downloadmanager.cpp
字号:
}
bool DownloadManager::HasItem(DownloadItem* item)
{
return (IndexOf(item) != kInvalidIndex);
}
// Internal functions
inline uint32 DownloadManager::CheckIndex(uint32 index)
{
// If we're dealing with a bogus index then set it to -1.
if(index >= CountItems())
{
index = kInvalidIndex;
}
return index;
}
DownloadItem* DownloadManager::GetNextQueuedItem()
{
int i, total = 0;
if (m_downloadIndex < 0 || m_downloadIndex >= (int)m_itemList.size())
m_downloadIndex = 0;
for(i = m_downloadIndex, total = 0; i < (int)m_itemList.size();
i = (i + 1) % m_itemList.size(), total++)
{
if (total >= (int)m_itemList.size())
return NULL;
if (m_itemList[i]->GetState() == kDownloadItemState_Queued)
{
m_downloadIndex++;
return m_itemList[i];
}
}
return NULL;
}
static int32 GetContentLengthFromHeader(const char* buffer)
{
int32 result = -1;
char* cp = strstr(buffer, "Content-Length:");
if(cp)
{
cp += strlen("Content-Length:") + 1;
result = atoi(cp);
}
return result;
}
const uint8 kHttpPort = 80;
const uint32 kMaxHostNameLen = 64;
static bool IsHTTPHeaderComplete(char* buffer, uint32 length)
{
bool result = false;
//if(length >= 4)
//{
//if( (buffer[0] == 'H' && buffer[1] == 'T'
// && buffer[2] == 'T' && buffer[3] == 'P'))
//{
//cout << "buffer is HTTP" << endl;
for(char* cp = buffer; cp < buffer + length; cp++)
{
if(!strncmp(cp, "\n\n", 2) || !strncmp(cp, "\r\n\r\n", 4))
{
result = true;
break;
}
}
//}
//}
return result;
}
Error DownloadManager::Download(DownloadItem* item)
{
Error result = kError_InvalidParam;
//*m_debug << "Downloading Item: " << item->SourceURL() << endl;
assert(item);
if(item)
{
char hostname[kMaxHostNameLen + 1];
char localname[kMaxHostNameLen + 1];
char proxyname[kMaxHostNameLen + 1];
unsigned short port;
struct sockaddr_in addr;
struct hostent host;
SOCKET s = -1;
char* file = NULL;
char* destPath = NULL;
bool useProxy;
destPath = new char[_MAX_PATH];
uint32 length = _MAX_PATH;
m_context->prefs->GetPrefString(kSaveMusicDirPref, destPath, &length);
strcat(destPath, DIR_MARKER_STR);
strcat(destPath, item->DestinationFile().c_str());
result = kError_ProtocolNotSupported;
// where should we connect to?
if(!strncasecmp(item->SourceURL().c_str(), "http://", 7))
{
int32 numFields;
uint32 length;
result = kError_NoErr;
m_context->prefs->GetUseProxyServer(&useProxy);
length = sizeof(proxyname);
m_context->prefs->GetProxyServerAddress(proxyname, &length);
if(useProxy)
{
numFields = sscanf(proxyname,
"http://%[^:/]:%hu", hostname, &port);
strcpy(proxyname, item->SourceURL().c_str());
file = proxyname;
}
else
{
numFields = sscanf(item->SourceURL().c_str(),
"http://%[^:/]:%hu", hostname, &port);
file = strchr(item->SourceURL().c_str() + 7, '/');
}
if(numFields < 1)
{
result = kError_InvalidURL;
}
if(numFields < 2)
{
port = kHttpPort;
}
}
if(item->GetState() == kDownloadItemState_Cancelled ||
item->GetState() == kDownloadItemState_Paused)
result = kError_UserCancel;
// get hostname
if(IsntError(result))
{
struct hostent* hostByName;
struct hostent hostByIP;
//*m_debug << "gethostbyname: " << hostname << endl;
hostByName = gethostbyname(hostname);
// On some stacks a numeric IP address
// will not parse with gethostbyname.
// If that didn't work try to convert it as a
// numeric address before giving up.
if(!hostByName)
{
static unsigned long ip;
static char *addr_ptr[2] = {(char*)&ip, NULL};
if((ip = inet_addr(hostname)) < 0)
result = kError_CantFindHost;
else
{
hostByIP.h_length = sizeof(uint32);
hostByIP.h_addrtype = AF_INET;
hostByIP.h_addr_list = (char**)&addr_ptr;
hostByName = &hostByIP;
}
}
if(IsntError(result))
{
memcpy(&host, hostByName, sizeof(struct hostent));
}
if(item->GetState() == kDownloadItemState_Cancelled ||
item->GetState() == kDownloadItemState_Paused)
result = kError_UserCancel;
}
// open socket
if(IsntError(result))
{
memset(&addr, 0x00, sizeof(struct sockaddr_in));
memcpy(&addr.sin_addr, host.h_addr, host.h_length);
addr.sin_family= host.h_addrtype;
addr.sin_port= htons(port);
//*m_debug << "socket" << endl;
s = socket(host.h_addrtype, SOCK_STREAM, 0);
if(s < 0)
result = kError_CantCreateSocket;
if(item->GetState() == kDownloadItemState_Cancelled ||
item->GetState() == kDownloadItemState_Paused)
result = kError_UserCancel;
}
// connect and send request
if(IsntError(result))
{
//*m_debug << "connect" << endl;
if(connect(s,(const struct sockaddr*)&addr, sizeof(struct sockaddr)))
result = kError_CannotBind;
if(item->GetState() == kDownloadItemState_Cancelled ||
item->GetState() == kDownloadItemState_Paused)
result = kError_UserCancel;
if(IsntError(result))
{
gethostname(localname, kMaxHostNameLen);
const char* kHTTPQuery = "GET %s HTTP/1.1\n"
"Host: %s\n"
"Accept: */*\n"
"User-Agent: FreeAmp/%s\n";
const char* kRange = "Range: %lu-\n"
"If-Range: %s\n";
const char* kCookie = "Cookie: %s\n";
// the magic 58 is enough for fixed length time in
// HTTP time format + 2 terabyte length range numbers.
// the 2 extra bytes on the end is an extra \n and 0x00 byte
char* query = new char[ strlen(kHTTPQuery) +
strlen(file) +
strlen(localname) +
strlen(FREEAMP_VERSION)+
(item->GetBytesReceived() ? (strlen(kRange) + 58): 0 ) +
(item->SourceCookie().size() ? (item->SourceCookie().size() + strlen(kCookie)): 0) +
2];
sprintf(query, kHTTPQuery, file, localname, FREEAMP_VERSION);
// do we need to request a range?
if(item->GetBytesReceived())
{
struct stat st;
if(-1 != stat(destPath, &st))
{
char* range = new char[strlen(kRange) + 58 + 1];
char time[32];
RFC822GMTTimeString(gmtime(&st.st_mtime), time);
sprintf(range, kRange, item->GetBytesReceived(), time);
strcat(query, range);
delete [] range;
}
else
{
item->SetBytesReceived(0);
}
}
if(item->SourceCookie().size())
{
char* cookie = new char[strlen(kCookie) + item->SourceCookie().size() + 1];
sprintf(cookie, kCookie, item->SourceCookie().c_str());
strcat(query, cookie);
delete [] cookie;
}
strcat(query, "\n");
//cout << query << endl;
int count;
//*m_debug << "send:" << endl << query;
count = send(s, query, strlen(query), 0);
if(count != (int)strlen(query))
{
result = kError_IOError;
}
if(item->GetState() == kDownloadItemState_Cancelled ||
item->GetState() == kDownloadItemState_Paused)
result = kError_UserCancel;
delete [] query;
}
}
// receive response
if(IsntError(result))
{
uint32 bufferSize = 2048;
char* buffer = NULL;
int count;
uint32 total = 0;
buffer = (char*)malloc(bufferSize);
result = kError_OutOfMemory;
if(buffer)
{
result = kError_NoErr;
do
{
if(total >= bufferSize - 1)
{
bufferSize *= 2;
buffer = (char*) realloc(buffer, bufferSize);
if(!buffer)
{
result = kError_OutOfMemory;
break;
}
}
count = recv(s, buffer + total, bufferSize - total - 1, 0);
if(count > 0)
total += count;
else
{
result = kError_IOError;
}
if(item->GetState() == kDownloadItemState_Cancelled ||
item->GetState() == kDownloadItemState_Paused)
result = kError_UserCancel;
}while(!IsHTTPHeaderComplete(buffer, total) && IsntError(result));
}
// parse header
if(IsntError(result))
{
uint32 returnCode = atoi(buffer+9);
buffer[total] = 0x00;
//cout << buffer << endl;
//cout << returnCode << endl;
switch(buffer[9])
{
// 1xx: Informational - Request received, continuing process
case '1':
{
// not sure what to do here... continue receiving???
}
// 2xx: Success - The action was successfully received,
// understood, and accepted
case '2':
{
result = kError_UnknownErr;
int32 fileSize = GetContentLengthFromHeader(buffer);
if(fileSize > 0)
item->SetTotalBytes(fileSize);
//cout << destPath << endl;
int openFlags = O_BINARY|O_CREAT|O_RDWR|O_APPEND;
if(returnCode != 206) // server oked partial download
{
item->SetBytesReceived(0);
openFlags |= O_TRUNC;
}
//*m_debug << "open file:" << destPath<< endl;
int fd = open(destPath, openFlags, S_IREAD | S_IWRITE);
if(fd >= 0)
{
result = kError_NoErr;
char* cp = strstr(buffer, "\n\n");
if(cp)
cp += 2;
else
{
cp = strstr(buffer, "\r\n\r\n");
if(cp)
cp += 4;
}
if(cp)
{
if(cp - buffer < (int)total)
{
write(fd, cp, total - (cp - buffer));
item->SetBytesReceived(total - (cp - buffer) + item->GetBytesReceived());
SendProgressMessage(item);
}
}
do
{
count = recv(s, buffer, bufferSize, 0);
if(count > 0)
{
write(fd, buffer, count);
item->SetBytesReceived(count + item->GetBytesReceived());
SendProgressMessage(item);
}
if(count < 0)
result = kError_IOError;
if(item->GetState() == kDownloadItemState_Cancelled ||
item->GetState() == kDownloadItemState_Paused)
result = kError_UserCancel;
}while(count > 0 && IsntError(result) && m_runDownloadThread &&
item->GetTotalBytes() > item->GetBytesReceived());
close(fd);
}
else
{
//*m_debug << "error opening file: " << endl;
switch(errno)
{
case EEXIST:
result = kError_FileExists;
break;
case EACCES:
result = kError_FileNoAccess;
break;
case ENOENT:
result = kError_FileNotFound;
break;
case EMFILE:
result = kError_FileNoHandles;
break;
case EINVAL:
result = kError_FileInvalidArg;
break;
}
}
break;
}
// 3xx: Redirection - Further action must be taken in order to
// complete the request
case '3':
{
char* cp = strstr(buffer, "Location:");
//int32 length;
if(cp)
{
cp += 9;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -