www.pudn.com > MP3CORD.rar > downloadmanager.cpp
/*____________________________________________________________________________ FreeAmp - The Free MP3 Player Portions Copyright (C) 1999 EMusic.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: downloadmanager.cpp,v 1.21 2000/01/19 22:20:29 ijr Exp $ ____________________________________________________________________________*/ // The debugger can't handle symbols more than 255 characters long. // STL often creates symbols longer than that. // When symbols are longer than 255 characters, the warning is disabled. #ifdef WIN32 #pragma warning(disable:4786) #endif #include "config.h" #include#ifdef WIN32 #include #else #undef socklen_t #include #include #include #endif #if defined(unix) || defined(__BEOS__) #define SOCKET int #endif #if defined(unix) #include #define closesocket(x) close(x) #define O_BINARY 0 #endif #if !defined(WIN32) #include typedef ostrstream ostringstream; #else #include #endif #include #include #include #include #include #include #include #include using namespace std; #include "facontext.h" #include "errors.h" #include "downloadmanager.h" #include "registrar.h" #include "utility.h" #include "event.h" #include "eventdata.h" #include "musiccatalog.h" #include "debug.h" DownloadManager::DownloadManager(FAContext* context) { m_context = context; m_current = 0; m_downloadsPaused = true; m_downloadIndex = -1; Registrar registrar; registrar.SetSubDir("plugins"); registrar.SetSearchString("*.dlf"); registrar.InitializeRegistry(&m_formatRegistry, context->prefs); const RegistryItem* module = NULL; DownloadFormat* dlf = NULL; int32 i = 0; while((module = m_formatRegistry.GetItem(i++))) { dlf = (DownloadFormat*) module->InitFunction()(m_context); if(dlf) { DownloadFormatInfo dlfi; uint32 index = 0; // error != kError_NoMoreFormats while(IsntError(dlf->GetSupportedFormats(&dlfi, index++))) { dlfi.SetRef(dlf); m_formats.push_back(new DownloadFormatInfo(dlfi)); } } } LoadResumableDownloadItems(); char path[MAX_PATH]; uint32 length = sizeof(path); context->prefs->GetInstallDirectory(path, &length); strcat(path, "\\DownloadLog.txt"); m_runDownloadThread = true; m_downloadThread = Thread::CreateThread(); if(m_downloadThread) { m_downloadThread->Create(download_thread_function, this); } } DownloadManager::~DownloadManager() { uint32 index = 0; uint32 size = 0; DownloadItem* item = NULL; m_runDownloadThread = false; m_queueSemaphore.Signal(); m_quitMutex.Acquire(); SaveResumableDownloadItems(); size = m_itemList.size(); for(index = 0; index < size; index++) { item = m_itemList[index]; if(item) { if(item->GetState() == kDownloadItemState_Cancelled) { CleanUpDownload(item); } delete item; } } size = m_formats.size(); for(index = 0; index < size; index++) { delete m_formats[index]->GetRef(); delete m_formats[index]; } if(m_downloadThread) { delete m_downloadThread; } //delete m_debug; } // Functions for adding items to Download Manager // Adding an item implicitly queues it for // downloading. Error DownloadManager::AddItem(const char* url, const char* filename) { Error result = kError_InvalidParam; assert(url); if(url) { result = kError_OutOfMemory; if(!filename) { filename = strrchr(url, '/'); if(filename) filename++; else filename = url; } DownloadItem* item = new DownloadItem(url, filename); if(item) { item->SetNormalDownload(); result = AddItem(item); } } return result; } Error DownloadManager::AddItem(DownloadItem* item) { Error result = kError_InvalidParam; m_mutex.Acquire(); assert(item); if(item) { m_itemList.push_back(item); SendItemAddedMessage(item); QueueDownload(item); result = kError_NoErr; } m_mutex.Release(); return result; } Error DownloadManager::AddItems(vector * list) { Error result = kError_InvalidParam; m_mutex.Acquire(); assert(list); if(list) { m_itemList.insert( m_itemList.end(), list->begin(), list->end()); uint32 count = list->size(); for(uint32 i = 0; i < count; i++) { SendItemAddedMessage((*list)[i]); QueueDownload((*list)[i]); } result = kError_NoErr; } m_mutex.Release(); return result; } // Changes item state to queued if it is cancelled or error. // This will indicate to the download thread that it should // attempt to retrieve this item. Has no effect if the item's // state is Done, or Downloading. Error DownloadManager::QueueDownload(DownloadItem* item, bool bDownloadNow) { Error result = kError_InvalidParam; unsigned int index; assert(item); if(item) { if(item->GetState() != kDownloadItemState_Downloading && item->GetState() != kDownloadItemState_Done) { item->SetState(kDownloadItemState_Queued); SendStateChangedMessage(item); if (m_downloadsPaused && bDownloadNow) for(index = 0; index < m_itemList.size(); index++) if (m_itemList[index] == item) { m_downloadIndex = index; break; } m_queueSemaphore.Signal(); result = kError_NoErr; } } return result; } Error DownloadManager::QueueDownload(uint32 index) { return QueueDownload(ItemAt(index)); } // Changes item state to cancelled if it is queued or downloading. // If allowResume is true then data is retained for later download. // Has no effect if the item's state is Done, Cancelled, or Error. Error DownloadManager::CancelDownload(DownloadItem* item, bool allowResume) { Error result = kError_InvalidParam; assert(item); if(item) { if(item->GetState() == kDownloadItemState_Downloading || item->GetState() == kDownloadItemState_Queued || item->GetState() == kDownloadItemState_Paused) { if(!allowResume) { item->SetState(kDownloadItemState_Cancelled); } else { item->SetState(kDownloadItemState_Paused); } SendStateChangedMessage(item); result = kError_NoErr; } } return result; } Error DownloadManager::CancelDownload(uint32 index, bool allowResume) { return CancelDownload(ItemAt(index), allowResume); } // File Format support Error DownloadManager::GetSupportedDownloadFormats(DownloadFormatInfo* format, uint32 index) { Error result = kError_InvalidParam; assert(format); if(format) { result = kError_NoMoreFormats; uint32 numFormats = m_formats.size(); if(index < numFormats) { result = kError_NoErr; *format = *m_formats[index]; } } return result; } Error DownloadManager::ReadDownloadFile(char* url, vector * items) { Error result = kError_InvalidParam; assert(url); if(url) { // find a suitable plugin result = kError_FormatNotSupported; char* extension = strrchr(url, '.'); if(extension) { extension++; uint32 numFormats = m_formats.size(); for(uint32 index = 0; index < numFormats; index++) { DownloadFormatInfo* format = m_formats[index]; if(!strcmp(extension, format->GetExtension())) { bool addToInternalList = false; if(!items) { items = new vector ; addToInternalList = true; } result = format->GetRef()->ReadDownloadFile(url, items); if(addToInternalList) { AddItems(items); delete items; } break; } } } char path[255]; uint32 length = sizeof(path); URLToFilePath(url, path, &length); unlink(path); } return result; } // Utility Functions bool DownloadManager::IsEmpty() { bool result; m_mutex.Acquire(); result = m_itemList.empty(); m_mutex.Release(); return result; } uint32 DownloadManager::CountItems() { uint32 result; m_mutex.Acquire(); result = m_itemList.size(); m_mutex.Release(); return result; } DownloadItem* DownloadManager::ItemAt(uint32 index) { DownloadItem* result = NULL; m_mutex.Acquire(); index = CheckIndex(index); if(index != kInvalidIndex) { result = m_itemList[index]; } m_mutex.Release(); return result; } uint32 DownloadManager::IndexOf(DownloadItem* item) { return InternalIndexOf(&m_itemList, item); } uint32 DownloadManager::InternalIndexOf(vector * list, DownloadItem* item) { uint32 result = kInvalidIndex; uint32 index = 0; uint32 size = 0; assert(list); assert(item); if(list && item) { size = list->size(); for(index = 0; index < size; index++) { if(item == (*list)[index]) { result = index; break; } } } return result; } 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; if(*cp == 0x20) cp++; char *end; for(end = cp; end < buffer + total; end++) if(*end=='\r' || *end == '\n') break; *end = 0x00; if (305 == returnCode) // proxy { char* proxy = new char[strlen(cp) + strlen(item->SourceURL().c_str()) + 1]; sprintf(proxy, "%s%s", cp, item->SourceURL().c_str()); item->SetSourceURL(proxy); delete [] proxy; } else // redirect of some type { item->SetSourceURL(cp); } result = Download(item); } break; } // 4xx: Client Error - The request contains bad syntax or cannot // be fulfilled case '4': { //*m_debug << "HTTP Error: " << returnCode << endl; switch(returnCode) { case 400: result = kError_BadHTTPRequest; break; case 401: result = kError_AccessNotAuthorized; break; case 403: result = kError_DownloadDenied; break; case 404: result = kError_HTTPFileNotFound; break; case 416: // try to grab the whole thing... item->SetBytesReceived(0); result = Download(item); break; default: result = kError_UnknownErr; break; } break; } // 5xx: Server Error - The server failed to fulfill an apparently // valid request case '5': { result = kError_UnknownServerError; break; } } } // cleanup if(buffer) free(buffer); } // cleanup if(s > 0) closesocket(s); if(destPath) delete [] destPath; } return result; } void DownloadManager::CleanUpDownload(DownloadItem* item) { //cout << "Cleaning item: " << item->SourceURL() << endl; char path[_MAX_PATH]; uint32 length = sizeof(path); m_context->prefs->GetPrefString(kSaveMusicDirPref, path, &length); strcat(path, DIR_MARKER_STR); strcat(path, item->DestinationFile().c_str()); remove(path); item->SetBytesReceived(0); } Error DownloadManager::SubmitToDatabase(DownloadItem* item) { Error result = kError_InvalidParam; assert(item); if(item) { //cout << "Submitting item: " << item->SourceURL() << endl; char path[_MAX_PATH]; uint32 length = sizeof(path); m_context->prefs->GetPrefString(kSaveMusicDirPref, path, &length); strcat(path, DIR_MARKER_STR); strcat(path, item->DestinationFile().c_str()); uint32 urlLength = strlen(path) + 10; char *url = new char[urlLength]; if (IsntError(FilePathToURL(path, url, &urlLength))) { if (!item->IsNormalDownload()) m_context->catalog->WriteMetaDataToDatabase(url, item->GetMetaData()); m_context->catalog->AddSong(url); } delete [] url; } return result; } void DownloadManager::PauseDownloads(void) { m_downloadsPaused = true; } void DownloadManager::ResumeDownloads(void) { m_downloadsPaused = false; m_queueSemaphore.Signal(); } bool DownloadManager::IsPaused(void) { return m_downloadsPaused; } void DownloadManager::DownloadThreadFunction() { DownloadItem* item; Error result; m_quitMutex.Acquire(); while(m_runDownloadThread) { if (m_downloadsPaused) { m_queueSemaphore.Wait(); continue; } item = GetNextQueuedItem(); if(item) { item->SetState(kDownloadItemState_Downloading); SendStateChangedMessage(item); result = Download(item); if(IsntError(result)) { item->SetState(kDownloadItemState_Done); result = SubmitToDatabase(item); } else if(result == kError_UserCancel) { if(item->GetState() == kDownloadItemState_Cancelled) { CleanUpDownload(item); } } else { item->SetState(kDownloadItemState_Error); CleanUpDownload(item); } item->SetDownloadError(result); SendStateChangedMessage(item); } else { m_queueSemaphore.Wait(); } } m_quitMutex.Release(); } void DownloadManager::download_thread_function(void* arg) { DownloadManager* dlm = (DownloadManager*)arg; dlm->DownloadThreadFunction(); } // Messaging functions void DownloadManager::SendItemAddedMessage(DownloadItem* item) { m_context->target->AcceptEvent(new DownloadItemAddedEvent(item)); } void DownloadManager::SendItemRemovedMessage(DownloadItem* item) { m_context->target->AcceptEvent(new DownloadItemRemovedEvent(item)); } void DownloadManager::SendStateChangedMessage(DownloadItem* item) { m_context->target->AcceptEvent(new DownloadItemNewStateEvent(item)); } void DownloadManager::SendProgressMessage(DownloadItem* item) { m_context->target->AcceptEvent(new DownloadItemProgressEvent(item)); } void DownloadManager::SaveResumableDownloadItems() { char path[_MAX_PATH]; uint32 length = sizeof(path); m_context->prefs->GetPrefString(kDatabaseDirPref, path, &length); if(DoesDBDirExist(path)) { strcat(path, DIR_MARKER_STR); strcat(path, "ResumeDownloadDB"); Database database(path); if(database.Working()) { uint32 size = 0; DownloadItem* item = NULL; size = m_itemList.size(); for(uint32 index = 0; index < size; index++) { item = m_itemList[index]; if(item && (item->GetState() == kDownloadItemState_Paused || item->GetState() == kDownloadItemState_Queued)) { ostringstream ost; char num[256]; const char* kDatabaseDelimiter = " "; MetaData metadata = item->GetMetaData(); // write out the number of elements we have ost << 16 << kDatabaseDelimiter; // next record the length of each element ost << item->SourceURL().size() << kDatabaseDelimiter; ost << item->SourceCookie().size() << kDatabaseDelimiter; ost << item->DestinationFile().size() << kDatabaseDelimiter; ost << item->PlaylistName().size() << kDatabaseDelimiter; sprintf(num, "%ld", (long int)item->GetTotalBytes()); ost << strlen(num) << kDatabaseDelimiter; sprintf(num, "%ld", (long int)item->GetBytesReceived()); ost << strlen(num) << kDatabaseDelimiter; // metadata lengths ost << metadata.Artist().size() << kDatabaseDelimiter; ost << metadata.Album().size() << kDatabaseDelimiter; ost << metadata.Title().size() << kDatabaseDelimiter; ost << metadata.Comment().size() << kDatabaseDelimiter; ost << metadata.Genre().size() << kDatabaseDelimiter; sprintf(num, "%ld", (long int)metadata.Year()); ost << strlen(num) << kDatabaseDelimiter; sprintf(num, "%ld", (long int)metadata.Track()); ost << strlen(num) << kDatabaseDelimiter; sprintf(num, "%ld", (long int)metadata.Time()); ost << strlen(num) << kDatabaseDelimiter; sprintf(num, "%ld", (long int)metadata.Size()); ost << strlen(num) << kDatabaseDelimiter; sprintf(num, "%ld", (long int)item->GetState()); ost << strlen(num) << kDatabaseDelimiter; // now stuff all the data in there ost << item->SourceURL(); ost << item->SourceCookie(); ost << item->DestinationFile(); ost << item->PlaylistName(); ost << item->GetTotalBytes(); ost << item->GetBytesReceived(); ost << metadata.Artist(); ost << metadata.Album(); ost << metadata.Title(); ost << metadata.Comment(); ost << metadata.Genre(); ost << metadata.Year(); ost << metadata.Track(); ost << metadata.Time(); ost << metadata.Size(); sprintf(num, "%ld", (long int)item->GetState()); ost << num; ost << '\0'; sprintf(num, "%ld", (long int)index); #ifdef WIN32 database.Insert(num, (char*)ost.str().c_str()); #else database.Insert(num, (char*)ost.str()); #endif } } } } } void DownloadManager::LoadResumableDownloadItems() { char path[_MAX_PATH]; uint32 length = sizeof(path); m_context->prefs->GetPrefString(kDatabaseDirPref, path, &length); if(DoesDBDirExist(path)) { strcat(path, DIR_MARKER_STR); strcat(path, "ResumeDownloadDB"); Database database(path); if(database.Working()) { char *key = NULL; while ((key = database.NextKey(key))) { char* value = database.Value(key); if (!value) continue; uint32 numFields = 0; int offset = 0; sscanf(value,"%lu%n", (long unsigned int *)&numFields, &offset); uint32* fieldLength = new uint32[numFields]; for(uint32 i = 0; i < numFields; i++) { int temp; sscanf(value + offset, " %lu %n", (long unsigned int *)&fieldLength[i], &temp); if (i == numFields - 1) { char intholder[10]; sprintf(intholder, "%lu", (long unsigned int)fieldLength[i]); offset += strlen(intholder) + 1; } else offset += temp; } string data = value; data.erase(0, offset); delete value; value = NULL; DownloadItem* item = new DownloadItem(); MetaData metadata; uint32 count = 0; for(uint32 j = 0; j < numFields; j++) { switch(j) { case 0: item->SetSourceURL(data.substr(count, fieldLength[j]).c_str()); break; case 1: item->SetSourceCookie(data.substr(count, fieldLength[j]).c_str()); break; case 2: item->SetDestinationFile(data.substr(count, fieldLength[j]).c_str()); break; case 3: item->SetPlaylistName(data.substr(count, fieldLength[j]).c_str()); break; case 4: item->SetTotalBytes(atoi(data.substr(count, fieldLength[j]).c_str())); break; case 5: item->SetBytesReceived(atoi(data.substr(count, fieldLength[j]).c_str())); break; case 6: metadata.SetArtist(data.substr(count, fieldLength[j]).c_str()); break; case 7: metadata.SetAlbum(data.substr(count, fieldLength[j]).c_str()); break; case 8: metadata.SetTitle(data.substr(count, fieldLength[j]).c_str()); break; case 9: metadata.SetComment(data.substr(count, fieldLength[j]).c_str()); break; case 10: metadata.SetGenre(data.substr(count, fieldLength[j]).c_str()); break; case 11: metadata.SetYear(atoi(data.substr(count, fieldLength[j]).c_str())); break; case 12: metadata.SetTrack(atoi(data.substr(count, fieldLength[j]).c_str())); break; case 13: metadata.SetTime(atoi(data.substr(count, fieldLength[j]).c_str())); break; case 14: metadata.SetSize(atoi(data.substr(count, fieldLength[j]).c_str())); break; case 15: item->SetState((DownloadItemState)atoi(data.substr(count, fieldLength[j]).c_str())); break; default: break; } count += fieldLength[j]; } delete [] fieldLength; fieldLength = NULL; item->SetMetaData(&metadata); m_itemList.push_back(item); SendItemAddedMessage(item); } while ((key = database.NextKey(NULL))) { database.Remove(key); delete key; } } } } bool DownloadManager::DoesDBDirExist(char* path) { bool result = false; struct stat st; if(-1 != stat(path, &st)) { if(st.st_mode & S_IFDIR) result = true; } return result; }