/**
 * Get proxy information from a proxy script.
 * @param dict : Dictionary to search through.
 * @param targetURLString : Target remote URL
 * @param logger : Log object
 * @return Collection of proxy information.
 */
ProxyInfoVec proxyInformationFromPac(CFDictionaryRef dict,
                                     const std::string &targetURLString,
                                     Logger &logger) {
  ProxyInfoVec proxyInfoVec;

  // is there a PAC enabled? If so, use it first.
  CFNumberRef pacEnabled;
  if ((pacEnabled = reinterpret_cast<CFNumberRef>(CFDictionaryGetValue(
           dict, kSCPropNetProxiesProxyAutoConfigEnable)))) {
    int enabled;
    if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) {
      // PAC is enabled
      CFStringRef cfPacLocation =
          reinterpret_cast<CFStringRef>(CFDictionaryGetValue(
              dict, kSCPropNetProxiesProxyAutoConfigURLString));
      CFDataRef pacData;
      CFURLRef pacURL =
          CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, nullptr);
      SInt32 errorCode;
      if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, pacURL,
                                                    &pacData, nullptr, nullptr,
                                                    &errorCode)) {
        logger.debug() << "Unable to get the PAC script at "
                       << toString(cfPacLocation) << "Error code: " << errorCode
                       << std::endl;
        return proxyInfoVec;
      }

      CFStringRef pacScript = CFStringCreateFromExternalRepresentation(
          kCFAllocatorDefault, pacData, kCFStringEncodingISOLatin1);

      CFURLRef targetURL = CFURLCreateWithBytes(
          kCFAllocatorDefault, (UInt8 *)targetURLString.c_str(),
          targetURLString.size(), kCFStringEncodingUTF8, nullptr);
      if (!targetURL) {
        logger.debug("Problem with Target URI for proxy script");
        return proxyInfoVec;
      }

      CFErrorRef pacError;
      CFArrayRef proxies = CFNetworkCopyProxiesForAutoConfigurationScript(
          pacScript, targetURL, &pacError);

      if (!proxies) {
        std::string pacLocation = toString(cfPacLocation);
        CFStringRef pacErrorDescription = CFErrorCopyDescription(pacError);
        logger.debug() << "Execution of PAC script at \"%s\" failed: %s"
                       << pacLocation << toString(pacErrorDescription)
                       << std::endl;
      }

      CFIndex size = CFArrayGetCount(proxies);
      for (CFIndex i = 0; i < size; ++i) {
        CFDictionaryRef proxy = reinterpret_cast<CFDictionaryRef>(
            CFArrayGetValueAtIndex(proxies, i));
        proxyInfoVec.push_back(proxyFromDictionary(proxy));
      }
    }
  }
  return proxyInfoVec;
}
QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
{
    QList<QNetworkProxy> result;

    // obtain a dictionary to the proxy settings:
    CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
    if (!dict) {
        qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL");
        return result;          // failed
    }

    if (isHostExcluded(dict, query.peerHostName())) {
        CFRelease(dict);
        return result;          // no proxy for this host
    }

    // is there a PAC enabled? If so, use it first.
    CFNumberRef pacEnabled;
    if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigEnable))) {
        int enabled;
        if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) {
            // PAC is enabled
            CFStringRef cfPacLocation = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString);

#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
            if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
                QCFType<CFDataRef> pacData;
                QCFType<CFURLRef> pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL);
                SInt32 errorCode;
                if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, pacUrl, &pacData, NULL, NULL, &errorCode)) {
                    QString pacLocation = QCFString::toQString(cfPacLocation);
                    qWarning("Unable to get the PAC script at \"%s\" (%s)", qPrintable(pacLocation), cfurlErrorDescription(errorCode));
                    return result;
                }

                QCFType<CFStringRef> pacScript = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, pacData, kCFStringEncodingISOLatin1);
                if (!pacScript) {
                    // This should never happen, but the documentation says it may return NULL if there was a problem creating the object.
                    QString pacLocation = QCFString::toQString(cfPacLocation);
                    qWarning("Unable to read the PAC script at \"%s\"", qPrintable(pacLocation));
                    return result;
                }

                QByteArray encodedURL = query.url().toEncoded(); // converted to UTF-8
                if (encodedURL.isEmpty()) {
                    return result; // Invalid URL, abort
                }

                QCFType<CFURLRef> targetURL = CFURLCreateWithBytes(kCFAllocatorDefault, (UInt8*)encodedURL.data(), encodedURL.size(), kCFStringEncodingUTF8, NULL);
                if (!targetURL) {
                    return result; // URL creation problem, abort
                }

                QCFType<CFErrorRef> pacError;
                QCFType<CFArrayRef> proxies = CFNetworkCopyProxiesForAutoConfigurationScript(pacScript, targetURL, &pacError);
                if (!proxies) {
                    QString pacLocation = QCFString::toQString(cfPacLocation);
                    QCFType<CFStringRef> pacErrorDescription = CFErrorCopyDescription(pacError);
                    qWarning("Execution of PAC script at \"%s\" failed: %s", qPrintable(pacLocation), qPrintable(QCFString::toQString(pacErrorDescription)));
                    return result;
                }

                CFIndex size = CFArrayGetCount(proxies);
                for (CFIndex i = 0; i < size; ++i) {
                    CFDictionaryRef proxy = (CFDictionaryRef)CFArrayGetValueAtIndex(proxies, i);
                    result << proxyFromDictionary(proxy);
                }
                return result;
            } else
#endif
            {
                QString pacLocation = QCFString::toQString(cfPacLocation);
                qWarning("Mac system proxy: PAC script at \"%s\" not handled", qPrintable(pacLocation));
            }
        }
    }

    // no PAC, decide which proxy we're looking for based on the query
    bool isHttps = false;
    QString protocol = query.protocolTag().toLower();

    // try the protocol-specific proxy
    QNetworkProxy protocolSpecificProxy;
    if (protocol == QLatin1String("ftp")) {
        protocolSpecificProxy =
            proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy,
                                kSCPropNetProxiesFTPEnable,
                                kSCPropNetProxiesFTPProxy,
                                kSCPropNetProxiesFTPPort);
    } else if (protocol == QLatin1String("http")) {
        protocolSpecificProxy =
            proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
                                kSCPropNetProxiesHTTPEnable,
                                kSCPropNetProxiesHTTPProxy,
                                kSCPropNetProxiesHTTPPort);
    } else if (protocol == QLatin1String("https")) {
        isHttps = true;
        protocolSpecificProxy =
            proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
                                kSCPropNetProxiesHTTPSEnable,
                                kSCPropNetProxiesHTTPSProxy,
                                kSCPropNetProxiesHTTPSPort);
    }
    if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy)
        result << protocolSpecificProxy;

    // let's add SOCKSv5 if present too
    QNetworkProxy socks5 = proxyFromDictionary(dict, QNetworkProxy::Socks5Proxy,
                                               kSCPropNetProxiesSOCKSEnable,
                                               kSCPropNetProxiesSOCKSProxy,
                                               kSCPropNetProxiesSOCKSPort);
    if (socks5.type() != QNetworkProxy::DefaultProxy)
        result << socks5;

    // let's add the HTTPS proxy if present (and if we haven't added
    // yet)
    if (!isHttps) {
        QNetworkProxy https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
                                                  kSCPropNetProxiesHTTPSEnable,
                                                  kSCPropNetProxiesHTTPSProxy,
                                                  kSCPropNetProxiesHTTPSPort);
        if (https.type() != QNetworkProxy::DefaultProxy && https != protocolSpecificProxy)
            result << https;
    }

    CFRelease(dict);
    return result;
}