📄 download.cpp.svn-base
字号:
AuthResponseHeader = _T("Proxy-Authorization");
return DoAuthentication(authChallenge, req, res, context);
}
BOOL CDownloader::DoAuthentication(const CString &authChallenge, CHttpRequest *&req, CHttpResponse *&res, LPVOID context) {
LOG0(5, "CDownloader::DoAuthentication()");
State = DOWNLOAD_STATE_AUTHENTICATING;
// scheme realm,auth-pars
int pos = authChallenge.Find(' ');
CString scheme = authChallenge.Left(pos);
// parse realm and parameters
while (pos < authChallenge.GetLength()) {
int eqPos = authChallenge.Find('=', pos);
if (eqPos == -1) {
Error = DOWNLOAD_ERROR_AUTHENTICATION_RESPONSE;
return FALSE;
}
CString name = authChallenge.Mid(pos, eqPos - pos);
name.TrimLeft();
int endPos = -1;
CString value;
if (authChallenge[eqPos + 1] == '"') {
endPos = authChallenge.Find('"', eqPos + 1);
if (endPos == -1) {
Error = DOWNLOAD_ERROR_AUTHENTICATION_RESPONSE;
return FALSE;
}
endPos = authChallenge.Find(',', endPos);
}
else {
endPos = authChallenge.Find(',', eqPos + 1);
}
// last item
if (endPos == -1)
endPos = authChallenge.GetLength();
value = authChallenge.Mid(eqPos + 1, endPos - eqPos - 1);
// strip "
value.TrimLeft('"');
value.TrimRight('"');
if (name.Compare(_T("realm")) == 0) Realm = value;
else if (name.Compare(_T("domain")) == 0) Domain = value;
else if (name.Compare(_T("nonce")) == 0) Nonce = value;
else if (name.Compare(_T("opaque")) == 0) Opaque = value;
else if (name.Compare(_T("stale")) == 0) Stale = value;
else if (name.Compare(_T("algorithm")) == 0) Algorithm = value;
else if (name.Compare(_T("qop")) == 0) Qop = value;
pos = endPos + 1;
}
if (scheme.CompareNoCase(_T("basic")) == 0) {
// Basic authentication
return DoBasicAuthentication(req, res, context);
}
else if (scheme.CompareNoCase(_T("digest")) == 0) {
// Digest authentication
return DoDigestAuthentication(req, res, context);
}
else {
Error = DOWNLOAD_ERROR_UNKNOWN_AUTH_SCHEME;
return FALSE;
}
}
///
void CDownloader::OnBeforeSendRequest(CHttpRequest *&req, LPVOID context) {
if (!ETag.IsEmpty()) req->SetHeader(_T("If-None-Match"), (LPCTSTR) ETag);
if (!LastModified.IsEmpty()) req->SetHeader(_T("If-Modified-Since"), (LPCTSTR) LastModified);
}
BOOL CDownloader::OnResponse(CHttpRequest *&req, CHttpResponse *&res, LPVOID context) {
LOG0(3, "CDownloader::OnResponse()");
int redirections = 0; // number of redirections
DWORD statusCode;
CString newAddress, headers;
CString strETag, strLastModified;
Updated = FALSE;
do {
statusCode = res->GetStatusCode();
LOG1(1, "StatusCode: %d", statusCode);
switch (statusCode) {
case HTTP_STATUS_OK:
case HTTP_STATUS_PARTIAL_CONTENT:
SaveHeaders(res);
if (res->GetHeader(_T("ETag"), strETag)) ETag = strETag;
if (res->GetHeader(_T("Last-Modified"), strLastModified)) LastModified = strLastModified;
Updated = TRUE;
return TRUE;
case HTTP_STATUS_NOT_MODIFIED: // 304
LOG0(3, "- NOT updated");
Updated = FALSE; // do not download the file (it is not changed)
return FALSE;
case HTTP_STATUS_MOVED: // 301
case HTTP_STATUS_REDIRECT: // 302
case HTTP_STATUS_REDIRECT_KEEP_VERB: // 307
case HTTP_STATUS_REDIRECT_METHOD: // 303
redirections++;
if (res->GetHeader(_T("Location"), newAddress)) {
CString object;
if (ParseURL(newAddress, ServiceType, ServerName, object, Port)) {
// absolute URL
URL = newAddress;
}
else {
// relative URL
object = newAddress;
if (ServiceType == INET_SERVICE_HTTPS) URL.Format(_T("https://%s%s"), ServerName, object);
else URL.Format(_T("http://%s%s"), ServerName, object);
}
delete res;
res = NULL;
delete req;
req = NULL;
HttpConnection.Close();
if (HttpConnection.Open(ServiceType, ServerName, Port)) {
req = HttpConnection.CreateRequest(object);
if (req != NULL) {
if (!Range.IsEmpty()) req->SetHeader(_T("Range"), Range);
req->AddHeaders(AdditionalHeaders);
HttpConnection.SendRequest(req);
res = HttpConnection.ReceiveResponse();
if (res != NULL) {
}
else {
Error = DOWNLOAD_ERROR_RESPONSE_ERROR;
return FALSE;
}
}
else {
Error = DOWNLOAD_ERROR_SENDING_REQUEST;
return FALSE;
}
}
else {
Error = DOWNLOAD_ERROR_CONNECTION_ERROR;
return FALSE;
}
}
else {
Error = DOWNLOAD_ERROR_NO_LOCATION_HEADER;
return FALSE;
}
break;
case HTTP_STATUS_DENIED:
// 401 Authorization required
return WWWAuthentication(req, res, context);
/* case 407:
// 407 Proxy Authentication required
// if (HttpConnection.Proxy->NeedAuth) {
// AuthSet = TRUE;
// UserName = HttpConnection.Proxy->UserName;
// Password = HttpConnection.Proxy->Password;
// ret = ProxyAuthentication(req, res, context);
// end = TRUE;
// }
// else {
// // proxy needs authentication
// end = TRUE;
// }
end = TRUE;
break;
*/
default:
Error = DOWNLOAD_ERROR_HTTP_ERROR;
HttpErrorNo = statusCode;
return FALSE;
}
} while (redirections < 6);
return FALSE;
}
void CDownloader::Terminate() {
LOG0(3, "CDownloader::Terminate()");
HttpConnection.Terminate();
SetEvent(HTerminate);
}
BOOL CDownloader::IsTerminated() {
return HttpConnection.IsTerminated();
}
//
// SaveHttpObject
//
BOOL CDownloader::SaveHttpObject(CString &url, const CString &strFileName, LPVOID context/* = NULL*/) {
LOG1(3, "CDownloader::SaveHttpObject('%S')", url);
HANDLE file = CreateFile(strFileName, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (file == INVALID_HANDLE_VALUE) {
// not exists
CreatePath(strFileName);
}
else {
// file exists => resume download
DWORD fileSize = GetFileSize(file, NULL);
CloseHandle(file);
Range.Format(_T("bytes=%d-"), fileSize);
}
Error = DOWNLOAD_ERROR_NONE;
URL = url;
ReplaceHTMLEntities(url); // there might be entities in the url
OnConnection(context);
CString objectName;
if (ParseURL(url, ServiceType, ServerName, objectName, Port)) {
State = DOWNLOAD_STATE_CONNECTING;
DWORD timeout = 3000; // we start with 3 seconds
for (int tries = 0; tries < 3; tries++) {
if (HttpConnection.Open(ServiceType, ServerName, Port)) {
CHttpRequest *req = HttpConnection.CreateRequest(objectName);
if (req != NULL) {
State = DOWNLOAD_STATE_SENDING_REQUEST;
if (!Range.IsEmpty()) req->SetHeader(_T("Range"), Range);
req->AddHeaders(AdditionalHeaders);
req->AddCookies(Cookies);
OnBeforeSendRequest(req, context);
HttpConnection.SendRequest(req, &Config.AdditionalHttpHeaders);
State = DOWNLOAD_STATE_RECEIVING_RESPONSE;
CHttpResponse *res = HttpConnection.ReceiveResponse();
if (res != NULL) {
if (OnResponse(req, res, context)) {
if (OnBeforeFileDownload(context)) {
State = DOWNLOAD_STATE_DATA_TRANSFER;
if (HttpConnection.GetFile(res, strFileName)) {
OnFileDownloaded(context);
}
else {
if (HttpConnection.GetSysError() == ERROR_DISK_FULL)
Error = DOWNLOAD_ERROR_DISK_FULL;
else
Error = DOWNLOAD_ERROR_GETTING_FILE;
}
}
}
delete res;
}
else
Error = DOWNLOAD_ERROR_RESPONSE_ERROR;
delete req;
}
else
Error = DOWNLOAD_ERROR_SENDING_REQUEST;
HttpConnection.Close();
return Error == DOWNLOAD_ERROR_NONE;
}
else {
// wait for a while
DWORD dwResult = WaitForSingleObject(HTerminate, timeout);
if (dwResult == WAIT_OBJECT_0)
break; // terminated
else
timeout *= 2; // timed out
}
}
Error = DOWNLOAD_ERROR_CONNECTION_ERROR;
}
else
Error = DOWNLOAD_ERROR_MALFORMED_URL;
return Error == DOWNLOAD_ERROR_NONE;
}
BOOL CDownloader::GetHttpObject(CString &url, CString &strBody, LPVOID context/* = NULL*/) {
TCHAR tempFileName[MAX_PATH];
GetTempFileName(Config.CacheLocation, L"rsr", 0, tempFileName);
BOOL ret = FALSE;
if (SaveHttpObject(url, tempFileName)) {
CString response;
HANDLE hFile = CreateFile(tempFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
// suppose the response is short, so that we can read it at once
DWORD read;
DWORD size = GetFileSize(hFile, NULL);
char *buffer = new char [size + 1];
ReadFile(hFile, buffer, size, &read, NULL);
buffer[size] = '\0';
CloseHandle(hFile);
strBody.Format(_T("%S"), buffer);
delete [] buffer;
ret = TRUE;
}
}
DeleteFile(tempFileName);
return ret;
}
BOOL CDownloader::Post(CString &url, const CString &strBody, CString &response, LPVOID context/* = NULL*/) {
LOG1(3, "CDownloader::Post('%S')", url);
Error = DOWNLOAD_ERROR_NONE;
URL = url;
ReplaceHTMLEntities(url); // there might be entities in the url
OnConnection(context);
CString objectName;
if (ParseURL(url, ServiceType, ServerName, objectName, Port)) {
State = DOWNLOAD_STATE_CONNECTING;
DWORD timeout = 3000; // we start with 3 seconds
for (int tries = 0; tries < 3; tries++) {
if (HttpConnection.Open(ServiceType, ServerName, Port)) {
CHttpRequest *req = HttpConnection.CreateRequest(objectName, HTTP_METHOD_POST);
if (req != NULL) {
// set the request body
req->SetBody(strBody);
// add necessary headers
req->SetHeader(_T("Content-Type"), _T("application/x-www-form-urlencoded"));
req->SetHeader(_T("Content-Length"), strBody.GetLength());
State = DOWNLOAD_STATE_SENDING_REQUEST;
req->AddHeaders(AdditionalHeaders);
req->AddCookies(Cookies);
OnBeforeSendRequest(req, context);
HttpConnection.SendRequest(req, &Config.AdditionalHttpHeaders);
State = DOWNLOAD_STATE_RECEIVING_RESPONSE;
CHttpResponse *res = HttpConnection.ReceiveResponse();
if (res != NULL) {
if (OnResponse(req, res, context)) {
if (OnBeforeFileDownload(context)) {
State = DOWNLOAD_STATE_DATA_TRANSFER;
TCHAR tempFileName[MAX_PATH];
GetTempFileName(Config.CacheLocation, L"rsr", 0, tempFileName);
if (HttpConnection.GetFile(res, tempFileName)) {
// ok, we get the response in the file -> read it
HANDLE hFile = CreateFile(tempFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
// suppose the response is short, so that we can read it at once
DWORD read;
DWORD size = GetFileSize(hFile, NULL);
char *buffer = new char [size + 1];
ReadFile(hFile, buffer, size, &read, NULL);
buffer[size] = '\0';
response.Format(_T("%S"), buffer);
CloseHandle(hFile);
}
OnFileDownloaded(context);
}
else {
if (HttpConnection.GetSysError() == ERROR_DISK_FULL)
Error = DOWNLOAD_ERROR_DISK_FULL;
else
Error = DOWNLOAD_ERROR_GETTING_FILE;
}
DeleteFile(tempFileName);
}
}
delete res;
}
else
Error = DOWNLOAD_ERROR_RESPONSE_ERROR;
delete req;
}
else
Error = DOWNLOAD_ERROR_SENDING_REQUEST;
HttpConnection.Close();
return Error == DOWNLOAD_ERROR_NONE;
}
else {
// wait for a while
DWORD dwResult = WaitForSingleObject(HTerminate, timeout);
if (dwResult == WAIT_OBJECT_0)
break; // terminated
else
timeout *= 2; // timed out
}
}
Error = DOWNLOAD_ERROR_CONNECTION_ERROR;
}
else
Error = DOWNLOAD_ERROR_MALFORMED_URL;
return Error == DOWNLOAD_ERROR_NONE;
}
void CDownloader::FreeAdditionalHeaders() {
while (!AdditionalHeaders.IsEmpty())
delete AdditionalHeaders.RemoveHead();
}
void CDownloader::SetCookie(const CString &cookie) {
Cookies.AddTail(cookie);
}
CString CDownloader::GetErrorMsg() {
CString errorMsg;
switch (Error) {
case DOWNLOAD_ERROR_GETTING_FILE: errorMsg.LoadString(IDS_ERROR_GETTING_FILE); break;
case DOWNLOAD_ERROR_RESPONSE_ERROR: errorMsg.LoadString(IDS_RESPONSE_ERROR); break;
case DOWNLOAD_ERROR_SENDING_REQUEST: errorMsg.LoadString(IDS_ERROR_SENDING_REQUEST); break;
case DOWNLOAD_ERROR_CONNECTION_ERROR: errorMsg.LoadString(IDS_ERROR_CONNECT); break;
case DOWNLOAD_ERROR_MALFORMED_URL: errorMsg.LoadString(IDS_MALFORMED_URL); break;
case DOWNLOAD_ERROR_AUTHENTICATION_RESPONSE: errorMsg.LoadString(IDS_INVALID_FEED_FILE); break;
case DOWNLOAD_ERROR_UNKNOWN_AUTH_SCHEME: errorMsg.LoadString(IDS_UNKNOWN_AUTH_SCHEME); break;
case DOWNLOAD_ERROR_NO_LOCATION_HEADER: errorMsg.LoadString(IDS_NO_LOCATION_HEADER); break;
case DOWNLOAD_ERROR_HTTP_ERROR: errorMsg.Format(IDS_HTTP_ERROR, HttpErrorNo); break;
case DOWNLOAD_ERROR_AUTHORIZATION_FAILED: errorMsg.LoadString(IDS_AUTHORIZATION_FAILED); break;
case DOWNLOAD_ERROR_AUTHENTICATION_ERROR: errorMsg.LoadString(IDS_AUTHENTICATION_ERROR); break;
case DOWNLOAD_ERROR_DISK_FULL: errorMsg.LoadString(IDS_DISK_FULL); break;
}
return errorMsg;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -