rockbox/rbutil/rbutil.cpp

599 lines
18 KiB
C++
Raw Normal View History

//---------------------------------------------------------------------------
//
// 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, 0777) ) {
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 "<platform><space><path seperator>\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 "<crc (unused)><space><filename>\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;
}