void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy) { QList<QPair<QString, QString> > values = header.values(); const char *search = isProxy ? "proxy-authenticate" : "www-authenticate"; method = None; /* Fun from the HTTP 1.1 specs, that we currently ignore: User agents are advised to take special care in parsing the WWW- Authenticate field value as it might contain more than one challenge, or if more than one WWW-Authenticate header field is provided, the contents of a challenge itself can contain a comma-separated list of authentication parameters. */ QString headerVal; for (int i = 0; i < values.size(); ++i) { const QPair<QString, QString> ¤t = values.at(i); if (current.first.toLower() != QLatin1String(search)) continue; QString str = current.second; if (method < Basic && str.startsWith(QLatin1String("Basic"), Qt::CaseInsensitive)) { method = Basic; headerVal = str.mid(6); } else if (method < Ntlm && str.startsWith(QLatin1String("NTLM"), Qt::CaseInsensitive)) { method = Ntlm; headerVal = str.mid(5); } else if (method < DigestMd5 && str.startsWith(QLatin1String("Digest"), Qt::CaseInsensitive)) { method = DigestMd5; headerVal = str.mid(7); } } challenge = headerVal.trimmed().toLatin1(); QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge); switch(method) { case Basic: realm = QString::fromLatin1(options.value("realm")); if (user.isEmpty()) phase = Done; break; case Ntlm: // #### extract from header realm = QString(); break; case DigestMd5: { realm = QString::fromLatin1(options.value("realm")); if (options.value("stale").toLower() == "true") phase = Start; if (user.isEmpty()) phase = Done; break; } default: realm = QString(); challenge = QByteArray(); phase = Invalid; } }
QByteArray QAuthenticatorPrivate::digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path) { QHash<QByteArray,QByteArray> options = parseDigestAuthenticationChallenge(challenge); ++nonceCount; QByteArray nonceCountString = QByteArray::number(nonceCount, 16); while (nonceCountString.length() < 8) nonceCountString.prepend('0'); QByteArray nonce = options.value("nonce"); QByteArray opaque = options.value("opaque"); QByteArray qop = options.value("qop"); // qDebug() << "calculating digest: method=" << method << "path=" << path; QByteArray response = digestMd5ResponseHelper(options.value("algorithm"), user.toLatin1(), realm.toLatin1(), password.toLatin1(), nonce, nonceCountString, cnonce, qop, method, path, QByteArray()); QByteArray credentials; credentials += "username=\"" + user.toLatin1() + "\", "; credentials += "realm=\"" + realm.toLatin1() + "\", "; credentials += "nonce=\"" + nonce + "\", "; credentials += "uri=\"" + path + "\", "; if (!opaque.isEmpty()) credentials += "opaque=\"" + opaque + "\", "; credentials += "response=\"" + response + '\"'; if (!options.value("algorithm").isEmpty()) credentials += ", algorithm=" + options.value("algorithm"); if (!options.value("qop").isEmpty()) { credentials += ", qop=" + qop + ", "; credentials += "nc=" + nonceCountString + ", "; credentials += "cnonce=\"" + cnonce + '\"'; } return credentials; }
void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray> > &values, bool isProxy) { const char *search = isProxy ? "proxy-authenticate" : "www-authenticate"; method = None; /* Fun from the HTTP 1.1 specs, that we currently ignore: User agents are advised to take special care in parsing the WWW- Authenticate field value as it might contain more than one challenge, or if more than one WWW-Authenticate header field is provided, the contents of a challenge itself can contain a comma-separated list of authentication parameters. */ QByteArray headerVal; for (int i = 0; i < values.size(); ++i) { const QPair<QByteArray, QByteArray> ¤t = values.at(i); if (current.first.toLower() != search) continue; QByteArray str = current.second.toLower(); if (method < Basic && str.startsWith("basic")) { method = Basic; headerVal = current.second.mid(6); } else if (method < Ntlm && str.startsWith("ntlm")) { method = Ntlm; headerVal = current.second.mid(5); } else if (method < DigestMd5 && str.startsWith("digest")) { method = DigestMd5; headerVal = current.second.mid(7); } } // Reparse credentials since we know the method now updateCredentials(); challenge = headerVal.trimmed(); QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge); switch(method) { case Basic: this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm")); if (user.isEmpty() && password.isEmpty()) phase = Done; break; case Ntlm: // #### extract from header if (user.isEmpty() && password.isEmpty()) phase = Done; break; case DigestMd5: { this->options[QLatin1String("realm")] = realm = QString::fromLatin1(options.value("realm")); if (options.value("stale").toLower() == "true") phase = Start; if (user.isEmpty() && password.isEmpty()) phase = Done; break; } default: realm.clear(); challenge = QByteArray(); phase = Invalid; } }