//--------------------------------------------------------------------------- // // Name: rbutil.cpp // Author: Christi Scarborough // Created: 06-12-05 04:08 // //--------------------------------------------------------------------------- #include "rbutil.h" // This class allows us to return directories as well as files to // wxDir::Traverse class wxDirTraverserIncludeDirs : public wxDirTraverser { public: wxDirTraverserIncludeDirs(wxArrayString& files) : m_files(files) { } virtual wxDirTraverseResult OnFile(const wxString& filename) { m_files.Add(filename); return wxDIR_CONTINUE; } virtual wxDirTraverseResult OnDir(const wxString& dirname) { m_files.Add(dirname); return wxDIR_CONTINUE; } private: wxArrayString& m_files; }; wxDEFINE_SCOPED_PTR_TYPE(wxZipEntry); const wxChar* _rootmatch[] = { wxT("rockbox.*"), wxT("ajbrec.ajz"), wxT("archos.mod"), wxT(".scrobbler.*"), wxT("battery_bench.txt"), wxT("battery.dummy"), }; const wxArrayString* rootmatch = new wxArrayString( (size_t) (sizeof(_rootmatch) / sizeof(wxChar*)), _rootmatch); int DownloadURL(wxString src, wxString dest) { int input, errnum = 0, success = false; wxString buf, errstr; wxLogVerbose(_("=== begin DownloadURL(%s,%s)"), src.c_str(), dest.c_str()); buf.Printf(_("Fetching %s"), src.c_str()); wxProgressDialog* progress = new wxProgressDialog(_("Downloading"), buf, 100, NULL, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME | wxPD_CAN_ABORT); progress->Update(0); input = true; wxURL* in_http = new wxURL(src); if (in_http->GetError() == wxURL_NOERR) { wxFFileOutputStream* os = new wxFFileOutputStream(dest); input = false; if (os->IsOk()) { wxInputStream* is = in_http->GetInputStream(); input = true; if (is) { size_t filesize = is->GetSize(); input = true; if (is->IsOk()) { char buffer[FILE_BUFFER_SIZE + 1]; size_t current = 0; while (! is->Eof()) { is->Read(buffer, FILE_BUFFER_SIZE); input = true; if (is->LastRead() ) { os->Write(buffer, is->LastRead()); input = false; if (os->IsOk()) { current += os->LastWrite(); if (!progress->Update(current * 100 / filesize)) { errstr = _("Download aborted by user"); errnum = 1000; break; } } else { errnum = os->GetLastError(); errstr.Printf(_("Can't write to output stream (%s)"), stream_err_str(errnum).c_str() ); break; } } else { errnum = is->GetLastError(); if (errnum == wxSTREAM_EOF) { errnum = 0; break; } errstr.Printf(_("Can't read from input stream (%s)"), stream_err_str(errnum).c_str() ); } } os->Close(); if (! errnum) { errnum = os->GetLastError(); errstr.Printf(_("Can't close output file (%s)"), stream_err_str(errnum).c_str() ); input = false; } if (! errnum) success = true; } else { errnum = is->GetLastError(); errstr.Printf(_("Can't get input stream size (%s)"), stream_err_str(errnum).c_str() ); } } else { errnum = in_http->GetError(); errstr.Printf(_("Can't get input stream (%d)"), errnum); } delete is; } else { errnum = os->GetLastError(); errstr.Printf(_("Can't create output stream (%s)"), stream_err_str(errnum).c_str() ); } delete os; } else { errstr.Printf(_("Can't open URL %s (%d)"), src.c_str(), in_http->GetError() ); errnum = 100; } delete in_http; delete progress; if (!success) { if (errnum == 0) errnum = 999; if (input) { buf.Printf(_("%s reading\n%s"), errstr.c_str(), src.c_str()); ERR_DIALOG(buf, _("Download URL")); } else { buf.Printf(_("%s writing to download\n/%s"), errstr.c_str(), dest.c_str()); ERR_DIALOG(buf, _("Download URL")); } } wxLogVerbose(_("=== end DownloadURL")); return errnum; } int UnzipFile(wxString src, wxString destdir, bool isInstall) { wxZipEntryPtr entry; wxString in_str, progress_msg, buf, logfile = wxT(""); int errnum = 0, curfile = 0, totalfiles = 0; wxLogVerbose(_("===begin UnzipFile(%s,%s,%i)"), src.c_str(), destdir.c_str(), isInstall); wxFFileInputStream* in_file = new wxFFileInputStream(src); wxZipInputStream* in_zip = new wxZipInputStream(*in_file); if (in_file->Ok() ) { if (! in_zip->IsOk() ) { errnum = in_zip->GetLastError(); buf.Printf(_("Can't open ZIP stream %s for reading (%s)"), src.c_str(), stream_err_str(errnum).c_str() ); ERR_DIALOG(buf, _("Unzip File") ); delete in_zip; delete in_file; return true; } totalfiles = in_zip->GetTotalEntries(); if (! in_zip->IsOk() ) { errnum = in_zip->GetLastError(); buf.Printf(_("Error Getting total ZIP entries for %s (%s)"), src.c_str(), stream_err_str(errnum).c_str() ); ERR_DIALOG(buf, _("Unzip File") ); delete in_zip; delete in_file; return true; } } else { errnum = in_file->GetLastError(); buf.Printf(_("Can't open %s (%s)"), src.c_str(), stream_err_str(errnum).c_str() ); ERR_DIALOG(buf, _("Unzip File") ); delete in_zip; delete in_file; return true; } wxProgressDialog* progress = new wxProgressDialog(_("Unpacking archive"), _("Preparing to unpack the downloaded files to your audio" "device"), totalfiles, NULL, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME | wxPD_CAN_ABORT); progress->Update(0); while (! errnum && (entry.reset(in_zip->GetNextEntry()), entry.get() != NULL) ) { curfile++; wxString name = entry->GetName(); progress_msg.Printf(_("Unpacking %s"), name.c_str()); if (! progress->Update(curfile, progress_msg) ) { buf.Printf(_("Unpacking cancelled by user")); MESG_DIALOG(buf); errnum = 1000; break; } in_str.Printf(wxT("%s" PATH_SEP "%s"), destdir.c_str(), name.c_str()); buf = logfile; // We leave space for adding a future CRC check, if we ever // feel particularly enthunsiastic. logfile.Printf(wxT("0 %s\n%s"), name.c_str(), buf.c_str()); if (entry->IsDir() ) { wxDir* dirname = new wxDir(in_str); if (! dirname->Exists(in_str) ) { if (wxMkDir(in_str) ) { buf.Printf(_("Unable to create directory %s"), in_str.c_str() ); errnum = 100; delete dirname; break; } } delete dirname; continue; } wxFFileOutputStream* out = new wxFFileOutputStream(in_str); if (! out->IsOk() ) { buf.Printf(_("Can't open file %s for writing"), in_str.c_str() ); delete out; return 100; } in_zip->Read(*out); if (! out->IsOk()) { buf.Printf(_("Can't write to %s (%d)"), in_str.c_str(), errnum = out->GetLastError() ); } if (!in_zip->IsOk() && ! in_file->GetLastError() == wxSTREAM_EOF) { buf.Printf(_("Can't read from %s (%d)"), src.c_str(), errnum = in_file->GetLastError() ); } if (! out->Close() && errnum == 0) { buf.Printf(_("Unable to close %s (%d)"), in_str.c_str(), errnum = out->GetLastError() ); } delete out; } delete in_zip; delete in_file; delete progress; if (errnum) { ERR_DIALOG(buf, _("Unzip File")); } else if (isInstall) { // If this fails, we have no log. No biggie. buf = logfile; logfile.Printf(wxT("%s " PATH_SEP "\n%s"), gv->curplat.c_str(), buf.c_str()); buf.Printf(wxT("%s" PATH_SEP UNINSTALL_FILE), destdir.c_str()); wxFFileOutputStream* out = new wxFFileOutputStream(buf); out->Write(logfile, logfile.Len()); out->Close(); delete out; } wxLogVerbose(_("=== end UnzipFile")); return(errnum); } int Uninstall(const wxString dir, bool isFullUninstall) { wxString buf, uninst; bool gooddata = false; unsigned int i; bool errflag = false; wxLogVerbose(_("=== begin Uninstall(%s,%i)"), dir.c_str(), isFullUninstall); wxProgressDialog* progress = new wxProgressDialog(_("Uninstalling"), _("Reading uninstall data from jukebox"), 100, NULL, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME | wxPD_CAN_ABORT); progress->Update(0); if (! isFullUninstall) { buf.Printf(wxT("%s" PATH_SEP UNINSTALL_FILE), dir.c_str()); if ( wxFileExists(buf) ) { wxFFileInputStream* uninst_data = new wxFFileInputStream(buf); if (uninst_data->Ok() ) { wxStringOutputStream* out = new wxStringOutputStream(&uninst); uninst_data->Read(*out); if (uninst_data->GetLastError() == wxSTREAM_EOF && ! out->GetLastError() ) { gooddata = true; } delete out; } delete uninst_data; } if (! gooddata) { wxLogNull lognull; if ( wxMessageDialog(NULL, _("Rockbox Utility can't find any uninstall data on this " "jukebox.\n" "Would you like to attempt a full uninstall?\n" "(WARNING: A full uninstall removes all files in your Rockbox " "folder)"), _("Standard uninstall not possible"), wxICON_EXCLAMATION | wxYES_NO | wxNO_DEFAULT).ShowModal() == wxID_YES) { isFullUninstall = true; } else { MESG_DIALOG(_("Uninstall cancelled by user")); delete progress; return 1000; } } } if (isFullUninstall ) { buf.Printf(wxT("%s" PATH_SEP ".rockbox"), dir.c_str()); if (rm_rf(buf) ) { WARN_DIALOG(_("Unable to completely remove Rockbox directory"), _("Full uninstall") ); errflag = true; } wxDir* root = new wxDir(dir); wxArrayString* special = new wxArrayString(); // Search for files for deletion in the jukebox root for (i = 0; i < rootmatch->GetCount(); i++) { const wxString match = (*rootmatch)[i]; root->GetAllFiles(dir, special, match, wxDIR_FILES); } delete root; // Sort in reverse order so we get directories last special->Sort(true); for (i = 0; i < special->GetCount(); i++) { if (wxDirExists((*special)[i]) ) { // We don't check the return code since we don't want non // empty dirs disappearing. wxRmdir((*special)[i]); } else if (wxFileExists((*special)[i]) ) { if (! wxRemoveFile((*special)[i]) ) { buf.Printf(_("Can't delete %s"), (*special)[i].c_str()); WARN_DIALOG(buf.c_str(), _("Full uninstall")); errflag = true; } } // Otherwise there isn't anything there, so we don't have to worry. } delete special; } else { wxString instplat, this_path_sep; unsigned int totalfiles, rc; // First line is "\n" instplat = uninst.BeforeFirst(wxT(' ')); this_path_sep = uninst.AfterFirst(wxT(' ')).BeforeFirst(wxT('\n')); uninst = uninst.AfterFirst(wxT('\n')); totalfiles = uninst.Freq(wxT('\n')); i = 0; while ((buf = uninst.BeforeFirst(wxT('\n'))) != "" ) { // These lines are all "\n" buf = buf.AfterFirst(wxT(' ')); buf = buf.Format(wxT("%s" PATH_SEP "%s"), dir.c_str(), buf.c_str()); // So we can install under Linux and still uninstall under Win buf.Replace(this_path_sep, PATH_SEP); wxString* buf2 = new wxString; buf2->Format(_("Deleting %s"), buf.c_str()); if (! progress->Update(++i * 100 / totalfiles, *buf2) ) { WARN_DIALOG(_("Cancelled by user"), _("Normal Uninstall")); delete progress; return true; } if (wxDirExists(buf) ) { // If we're about to attempt to remove .rockbox. delete // install data first buf2->Printf(wxT("%s" PATH_SEP ".rockbox" PATH_SEP), dir.c_str() ); if ( buf.IsSameAs(buf2->c_str()) ) { buf2->Printf(wxT("%s" PATH_SEP UNINSTALL_FILE), dir.c_str()); wxRemoveFile(*buf2); } if ( rc = ! wxRmdir(buf) ) { buf = buf.Format(_("Can't remove directory %s"), buf.c_str()); errflag = true; WARN_DIALOG(buf.c_str(), _("Standard uninstall")); } } else if (wxFileExists(buf) ) { if ( rc = ! wxRemoveFile(buf) ) { buf = buf.Format(_("Can't delete file %s"), buf.c_str()); errflag = true; WARN_DIALOG(buf.c_str(), _("Standard uninstall")); } } else { errflag = true; buf = buf.Format(_("Can't find file or directory %s"), buf.c_str() ); WARN_DIALOG(buf.c_str(), _("Standard uninstall") ); } uninst = uninst.AfterFirst('\n'); } if (errflag) { ERR_DIALOG(_("Unable to remove some files"), _("Standard uninstall")) ; } } delete progress; wxLogVerbose(_("=== end Uninstall")); return errflag; } wxString stream_err_str(int errnum) { wxString out; switch (errnum) { case wxSTREAM_NO_ERROR: out = wxT("wxSTREAM_NO_ERROR"); break; case wxSTREAM_EOF: out = wxT("wxSTREAM_EOF"); break; case wxSTREAM_WRITE_ERROR: out = wxT("wxSTREAM_WRITE_ERROR"); break; case wxSTREAM_READ_ERROR: out = wxT("wxSTREAM_READ_ERROR"); break; default: out = wxT("UNKNOWN"); break; } return out; } bool rm_rf(wxString file) { wxLogVerbose(_("=== begin rm-rf(%s)"), file.c_str() ); wxString buf; wxArrayString selected; wxDirTraverserIncludeDirs wxdtid(selected); unsigned int rc = 0, i; bool errflag = false; if (wxFileExists(file) ) { rc = ! wxRemoveFile(file); } else if (wxDirExists(file) ) { wxDir* dir = new wxDir(file);; dir->Traverse(wxdtid); delete dir; // Sort into reverse alphabetical order for deletion in correct order // (directories after files) selected.Sort(true); selected.Add(file); wxProgressDialog* progress = new wxProgressDialog(_("Removing files"), _("Deleting files"), selected.GetCount(), NULL, wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME | wxPD_CAN_ABORT); for (i = 0; i < selected.GetCount(); i++) { wxLogVerbose(selected[i]); if (progress != NULL) { buf.Printf(_("Deleting %s"), selected[i].c_str() ); if (! progress->Update(i, buf)) { WARN_DIALOG(_("Cancelled by user"), _("Erase Files")); delete progress; return true; } } if (wxDirExists(selected[i]) ) { if (rc = ! wxRmdir(selected[i]) ) { buf.Printf(_("Can't remove directory %s"), selected[i].c_str()); errflag = true; WARN_DIALOG(buf.c_str(), _("Erase files")); } } else if (rc = ! wxRemoveFile(selected[i]) ) { buf.Printf(_("Error deleting file %s"), selected[i].c_str() ); errflag = true; WARN_DIALOG(buf.c_str(),_("Erase files")); } } delete progress; } else { buf.Printf(_("Can't find expected file %s"), file.c_str()); WARN_DIALOG(buf.c_str(), _("Erase files")); return true; } wxLogVerbose(_("=== end rm-rf")); return rc ? true : false; }