#include "updatedlg.h" #include #include #include #include #include #include #include #include #include #include #include #include "devpakinstaller.h" #include "crc32.h" #include #include #include #include int idNet = wxNewId(); int idPopupInstall = wxNewId(); int idPopupDownload = wxNewId(); int idPopupDownloadAndInstall = wxNewId(); int idPopupUninstall = wxNewId(); BEGIN_EVENT_TABLE(UpdateDlg, wxDialog) EVT_UPDATE_UI(-1, UpdateDlg::OnUpdateUI) EVT_TREE_SEL_CHANGED(XRCID("tvCategories"), UpdateDlg::OnTreeSelChanged) EVT_LIST_ITEM_SELECTED(XRCID("lvFiles"), UpdateDlg::OnFileSelected) EVT_LIST_ITEM_DESELECTED(XRCID("lvFiles"), UpdateDlg::OnFileDeSelected) EVT_LIST_ITEM_RIGHT_CLICK(XRCID("lvFiles"), UpdateDlg::OnFileRightClick) EVT_MENU(idPopupDownload, UpdateDlg::OnDownload) EVT_MENU(idPopupDownloadAndInstall, UpdateDlg::OnDownloadAndInstall) EVT_MENU(idPopupInstall, UpdateDlg::OnInstall) EVT_MENU(idPopupUninstall, UpdateDlg::OnUninstall) EVT_COMBOBOX(XRCID("cmbServer"), UpdateDlg::OnServerChange) EVT_COMBOBOX(XRCID("cmbFilter"), UpdateDlg::OnFilterChange) EVT_CHECKBOX(XRCID("chkCache"), UpdateDlg::OnServerChange) EVT_CBNET_CONNECT(idNet, UpdateDlg::OnConnect) EVT_CBNET_DISCONNECT(idNet, UpdateDlg::OnDisConnect) EVT_CBNET_PROGRESS(idNet, UpdateDlg::OnProgress) EVT_CBNET_ABORTED(idNet, UpdateDlg::OnAborted) EVT_CBNET_START_DOWNLOAD(idNet, UpdateDlg::OnDownloadStarted) EVT_CBNET_END_DOWNLOAD(idNet, UpdateDlg::OnDownloadEnded) END_EVENT_TABLE() UpdateDlg::UpdateDlg(wxWindow* parent) : m_Recs(0), m_RecsCount(0), m_CurrFileSize(0), m_LastBlockSize(0), m_HasUpdated(false), m_FirstTimeCheck(true), m_Net(this, idNet, _T("http://devpaks.sourceforge.net/")) { //ctor wxXmlResource::Get()->LoadDialog(this, parent, _T("MainFrame")); CreateListColumns(); FillServers(); UpdateStatus(_("Ready"), 0); } UpdateDlg::~UpdateDlg() { //dtor delete[] m_Recs; m_RecsCount = 0; } void UpdateDlg::EndModal(int retCode) { if (!m_Net.IsConnected() || retCode != wxID_CANCEL) { wxDialog::EndModal(retCode); return; } if (m_Net.IsConnected()) m_Net.Abort(); } void UpdateDlg::CreateListColumns() { wxListCtrl* lst = XRCCTRL(*this, "lvFiles", wxListCtrl); lst->InsertColumn(0, _("Title")); lst->InsertColumn(1, _("Version")); lst->InsertColumn(2, _("Installed")); lst->InsertColumn(3, _("Size"), wxLIST_FORMAT_RIGHT); lst->SetColumnWidth(0, lst->GetSize().x - (64 * 3) - 2); // 1st column takes all remaining space lst->SetColumnWidth(1, 64); lst->SetColumnWidth(2, 64); lst->SetColumnWidth(3, 64); } void UpdateDlg::AddRecordToList(UpdateRec* rec) { if (!rec) return; wxListCtrl* lst = XRCCTRL(*this, "lvFiles", wxListCtrl); int idx = lst->GetItemCount(); lst->InsertItem(idx, rec->title); lst->SetItem(idx, 1, rec->version); lst->SetItem(idx, 2, rec->installed_version); lst->SetItem(idx, 3, rec->size); } void UpdateDlg::SetListColumnText(int idx, int col, const wxString& text) { wxListCtrl* lst = XRCCTRL(*this, "lvFiles", wxListCtrl); int index = idx == -1 ? lst->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) : idx; wxListItem it; it.m_itemId = index; it.m_col = col; it.m_mask = wxLIST_MASK_TEXT; it.m_text = text; lst->SetItem(it); } void UpdateDlg::UpdateStatus(const wxString& status, int curProgress, int maxProgress) { wxStaticText* lbl = XRCCTRL(*this, "lblStatus", wxStaticText); if (lbl->GetLabel() != status) lbl->SetLabel(status); if (curProgress != -1) XRCCTRL(*this, "gauProgress", wxGauge)->SetValue(curProgress); if (maxProgress != -1) XRCCTRL(*this, "gauProgress", wxGauge)->SetRange(maxProgress); } void UpdateDlg::EnableButtons(bool update, bool abort) { wxButton* btnCl = XRCCTRL(*this, "wxID_CANCEL", wxButton); btnCl->Enable(abort); // disable server list and cache checkbox while downloading XRCCTRL(*this, "cmbServer", wxComboBox)->Enable(!m_Net.IsConnected()); XRCCTRL(*this, "chkCache", wxCheckBox)->Enable(!m_Net.IsConnected()); wxYield(); } void UpdateDlg::FillGroups() { UpdateStatus(_("Parsing list of updates"), 0, m_RecsCount - 1); // get a list of unique group names wxArrayString groups; for (int i = 0; i < m_RecsCount; ++i) { for (unsigned int x = 0; x < m_Recs[i].groups.GetCount(); ++x) { if (m_Recs[i].groups[x].IsEmpty()) continue; if (groups.Index(m_Recs[i].groups[x]) == wxNOT_FOUND) { if (FilterRec(&m_Recs[i])) groups.Add(m_Recs[i].groups[x]); } } } // create the groups tree wxTreeCtrl* tree = XRCCTRL(*this, "tvCategories", wxTreeCtrl); tree->Freeze(); tree->DeleteAllItems(); wxTreeItemId root = tree->AddRoot(_("All categories")); for (unsigned int i = 0; i < groups.GetCount(); ++i) { tree->AppendItem(root, groups[i]); } tree->SortChildren(root); tree->Thaw(); tree->Expand(root); tree->SelectItem(root); // this calls the event UpdateStatus(_("Done parsing list of updates"), 0); } void UpdateDlg::FillFiles(const wxTreeItemId& id) { wxTreeCtrl* tree = XRCCTRL(*this, "tvCategories", wxTreeCtrl); wxListCtrl* lst = XRCCTRL(*this, "lvFiles", wxListCtrl); lst->Freeze(); lst->ClearAll(); CreateListColumns(); wxString group = id == tree->GetRootItem() ? _T("") : tree->GetItemText(id); // add files belonging to group int counter = 0; for (int i = 0; i < m_RecsCount; ++i) { if (group.IsEmpty() || (!m_Recs[i].groups.IsEmpty() && m_Recs[i].groups.Index(group) != wxNOT_FOUND)) { // filter if (FilterRec(&m_Recs[i])) { AddRecordToList(&m_Recs[i]); ++counter; } } } lst->Thaw(); // select first item lst->SetItemState(0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED); } void UpdateDlg::FillFileDetails(const wxListItem& id) { wxTextCtrl* txt = XRCCTRL(*this, "txtInfo", wxTextCtrl); txt->Clear(); UpdateRec* cur = GetRecFromListView(); if (!cur) { txt->Clear(); EnableButtons(); return; } txt->AppendText(_("Name: ") + cur->name + _T("\n")); // txt->AppendText(_("Server: ") + cur->remote_server + _T("\n")); // txt->AppendText(_("File: ") + cur->remote_file + _T("\n")); txt->AppendText(_("Version: ") + cur->version + _T("\n")); txt->AppendText(_("Size: ") + cur->size + _T("\n")); txt->AppendText(_("Date: ") + cur->date + _T("\n\n")); txt->AppendText(_("Description: \n")); txt->AppendText(cur->desc); txt->SetSelection(0, 0); txt->SetInsertionPoint(0); } void UpdateDlg::InternetUpdate(bool forceDownload) { UpdateStatus(_("Please wait...")); m_HasUpdated = false; m_Net.SetServer(GetCurrentServer()); EnableButtons(false); forceDownload = forceDownload || !XRCCTRL(*this, "chkCache", wxCheckBox)->GetValue(); bool forceDownloadMirrors = forceDownload || !wxFileExists(GetMirrorsFilename()); if (forceDownloadMirrors) { if (!m_Net.DownloadFile(_T("mirrors.cfg"), GetMirrorsFilename())) { UpdateStatus(_("Error downloading list of mirrors"), 0, 0); return; } else { FillServers(); m_Net.SetServer(GetCurrentServer()); // update server based on mirrors } } wxString config = GetConfFilename(); forceDownload = forceDownload || !wxFileExists(config); if (forceDownload && !m_Net.DownloadFile(_T("webupdate.conf"), config)) { UpdateStatus(_("Error downloading list of updates"), 0, 0); return; } else { IniParser ini; if (!ini.ParseFile(config)) { UpdateStatus(_("Failed to retrieve the list of updates"), 0, 0); return; } ini.Sort(); if (m_Recs) delete[] m_Recs; // remember to delete[] m_Recs when we 're done with it!!! // it's our responsibility once given to us m_Recs = ReadConf(ini, &m_RecsCount, GetCurrentServer(), GetPackagePath()); FillGroups(); } EnableButtons(); UpdateStatus(_("Ready"), 0, 0); m_HasUpdated = true; } void UpdateDlg::FillServers() { wxComboBox* cmb = XRCCTRL(*this, "cmbServer", wxComboBox); cmb->Clear(); m_Servers.Clear(); IniParser ini; ini.ParseFile(GetMirrorsFilename()); int group = ini.FindGroupByName(_T("WebUpdate mirrors")); for (int i = 0; group != -1 && i < ini.GetKeysCount(group); ++i) { cmb->Append(ini.GetKeyName(group, i)); m_Servers.Add(ini.GetKeyValue(group, i)); } if (cmb->GetCount() == 0) { cmb->Append(_("devpaks.org Community Devpaks")); m_Servers.Add(_T("http://devpaks.sourceforge.net/")); } cmb->SetSelection(0); } wxString UpdateDlg::GetConfFilename() { int server_hash = GetTextCRC32(GetCurrentServer().mb_str()); wxString config; config.Printf(_T("%s/webupdate-%x.conf"), wxGetHomeDir().c_str(), server_hash); return config; } wxString UpdateDlg::GetMirrorsFilename() { wxString config; config << wxGetHomeDir() << _T("/mirrors.cfg"); return config; } wxString UpdateDlg::GetCurrentServer() { return m_Servers[XRCCTRL(*this, "cmbServer", wxComboBox)->GetSelection()]; } wxString UpdateDlg::GetBasePath() { return g_MasterPath + wxFILE_SEP_PATH; } wxString UpdateDlg::GetPackagePath() { return GetBasePath() + _T("Packages") + wxFILE_SEP_PATH; } bool UpdateDlg::FilterRec(UpdateRec* rec) { if (!rec) return false; wxComboBox* cmb = XRCCTRL(*this, "cmbFilter", wxComboBox); switch (cmb->GetSelection()) { case 0: // All return true; case 1: // Installed return rec->installed; case 2: // installed with update available return rec->installed && rec->version != rec->installed_version; case 3: // downloaded but not installed return rec->downloaded && !rec->installed; case 4: // not installed return !rec->downloaded && !rec->installed; default: return false; } return false; // doesn't reach here } void UpdateDlg::ApplyFilter() { wxTreeCtrl* tree = XRCCTRL(*this, "tvCategories", wxTreeCtrl); FillGroups(); FillFiles(tree->GetSelection()); EnableButtons(); } UpdateRec* UpdateDlg::GetRecFromListView() { wxListCtrl* lst = XRCCTRL(*this, "lvFiles", wxListCtrl); int index = lst->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); if (index == -1) return 0; wxString title = lst->GetItemText(index); return FindRecByTitle(title, m_Recs, m_RecsCount); } void UpdateDlg::DownloadFile(bool dontInstall) { UpdateStatus(_("Please wait...")); UpdateRec* rec = GetRecFromListView(); if (!rec) { wxMessageBox(_("No file selected!"), _("Error"), wxICON_ERROR); UpdateStatus(_("Ready"), 0, 0); return; } if (rec->version == rec->installed_version) { if (wxMessageBox(_("You seem to have installed the latest version.\nAre you sure you want to proceed?"), _("Confirmation"), wxICON_QUESTION | wxYES_NO) == wxNO) return; } if (!CreateDirRecursively(GetPackagePath())) { wxMessageBox(_("Can't create directory ") + GetPackagePath(), _("Error"), wxICON_ERROR); return; } if (wxFileExists(GetPackagePath() + rec->local_file)) { if (wxMessageBox(_("This file already exists!\nAre you sure you want to download it again?"), _("Confirmation"), wxICON_QUESTION | wxYES_NO) == wxNO && rec->installable) { if (!dontInstall && wxMessageBox(_("Do you want to force-install it?"), _("Confirmation"), wxICON_QUESTION | wxYES_NO) == wxYES) InstallFile(); return; } } m_Net.SetServer(rec->remote_server); EnableButtons(false); if (!m_Net.DownloadFile(rec->remote_file, GetPackagePath() + rec->local_file)) { rec->downloaded = false; UpdateStatus(_("Error downloading file: ") + rec->remote_server + _T(" > ") + rec->remote_file, 0, 0); return; } else rec->downloaded = true; UpdateStatus(_("Ready"), 0, 0); EnableButtons(); } void UpdateDlg::InstallFile() { UpdateStatus(_("Please wait...")); UpdateRec* rec = GetRecFromListView(); if (!rec) { wxMessageBox(_("No file selected!"), _("Error"), wxICON_ERROR); UpdateStatus(_("Ready"), 0, 0); return; } wxYield(); if (rec->title == _T("WebUpdate Mirrors list")) { InstallMirrors(GetPackagePath() + rec->local_file); rec->installed = true; ApplyFilter(); UpdateStatus(_("Ready"), 0, 0); return; } else if (!rec->installable) { UpdateStatus(_("Ready"), 0, 0); return; } if (!CreateDirRecursively(GetPackagePath())) { UpdateStatus(_("Ready"), 0, 0); wxMessageBox(_("Can't create directory ") + GetPackagePath(), _("Error"), wxICON_ERROR); return; } wxArrayString files; DevPakInstaller inst; if (inst.Install(rec->name, GetPackagePath() + rec->local_file, GetBasePath(), &files)) { // wxFileName fname(GetPackagePath() + rec->local_file); // fname.SetExt("entry"); // fname.SetName(rec->title); // CreateEntryFile(rec, fname.GetFullPath(), files); CreateEntryFile(rec, GetPackagePath() + rec->entry, files); wxMessageBox(_("DevPak installed"), _("Message"), wxICON_INFORMATION); // refresh installed_version rec->installed = true; rec->installed_version = rec->version; SetListColumnText(-1, 2, rec->installed_version); } else { wxMessageBox(_("DevPak was not installed.\nStatus:\n") + inst.GetStatus(), _("Error"), wxICON_ERROR); } UpdateStatus(_("Ready"), 0, 0); } void UpdateDlg::InstallMirrors(const wxString& file) { wxLogNull ln; if (!wxCopyFile(file, GetMirrorsFilename(), true)) wxMessageBox(_("Can't install mirrors file: ") + file, _("Error"), wxICON_ERROR); else { wxRemoveFile(file); FillServers(); m_Net.SetServer(GetCurrentServer()); // update server based on mirrors wxMessageBox(_("Mirrors installed"), _("Information"), wxICON_INFORMATION); } } void UpdateDlg::UninstallFile() { UpdateStatus(_("Please wait...")); UpdateRec* rec = GetRecFromListView(); if (!rec) { wxMessageBox(_("No file selected!"), _("Error"), wxICON_ERROR); UpdateStatus(_("Ready"), 0, 0); return; } wxYield(); DevPakInstaller inst; if (inst.Uninstall(GetPackagePath() + rec->entry)) { wxMessageBox(_("DevPak uninstalled"), _("Message"), wxICON_INFORMATION); // refresh installed_version rec->installed_version.Clear(); rec->installed = false; SetListColumnText(-1, 2, rec->installed_version); } else { wxMessageBox(_("DevPak was not uninstalled.\nStatus:\n") + inst.GetStatus(), _("Error"), wxICON_ERROR); } } void UpdateDlg::CreateEntryFile(UpdateRec* rec, const wxString& filename, const wxArrayString& files) { wxString entry; entry << _T("[Setup]\n"); entry << _T("AppName=") << rec->name << '\n'; entry << _T("AppVersion=") << rec->version << '\n'; // entry << _T("Description=") << rec->name << '\n'; entry << _T('\n'); entry << _T("[Files]\n"); for (unsigned int i = 0; i < files.GetCount(); ++i) { entry << files[i] << _T('\n'); } wxFile f(filename, wxFile::write); if (f.IsOpened()) { f.Write(entry.c_str(), entry.Length()); } } void UpdateDlg::OnFileRightClick(wxListEvent& event) { // LOGSTREAM << "pt.x=" << event.GetPoint().x << ", pt.y=" << event.GetPoint().y << '\n'; UpdateRec* rec = GetRecFromListView(); if (!rec) return; wxMenu popup; popup.Append(idPopupDownloadAndInstall, _("Download && install")); popup.AppendSeparator(); popup.Append(idPopupDownload, _("Download")); popup.Append(idPopupInstall, _("Install")); popup.AppendSeparator(); popup.Append(idPopupUninstall, _("Uninstall")); bool canDl = !rec->downloaded || rec->version != rec->installed_version; bool canInst = rec->downloaded && (!rec->installed || rec->version != rec->installed_version); popup.Enable(idPopupDownload, canDl); popup.Enable(idPopupInstall, canInst); popup.Enable(idPopupDownloadAndInstall, canInst || canDl); popup.Enable(idPopupUninstall, rec->installed); wxListCtrl* lst = XRCCTRL(*this, "lvFiles", wxListCtrl); lst->PopupMenu(&popup, event.GetPoint()); } void UpdateDlg::OnFileDeSelected(wxListEvent& event) { wxListItem id; FillFileDetails(id); EnableButtons(); } void UpdateDlg::OnFileSelected(wxListEvent& event) { FillFileDetails(event.GetItem()); EnableButtons(); } void UpdateDlg::OnTreeSelChanged(wxTreeEvent& event) { FillFiles(event.GetItem()); EnableButtons(); } void UpdateDlg::OnDownload(wxCommandEvent& event) { DownloadFile(true); } void UpdateDlg::OnInstall(wxCommandEvent& event) { InstallFile(); } void UpdateDlg::OnUninstall(wxCommandEvent& event) { UninstallFile(); } void UpdateDlg::OnDownloadAndInstall(wxCommandEvent& event) { DownloadFile(); } void UpdateDlg::OnServerChange(wxCommandEvent& event) { InternetUpdate(); } void UpdateDlg::OnFilterChange(wxCommandEvent& event) { ApplyFilter(); } void UpdateDlg::OnConnect(wxCommandEvent& event) { XRCCTRL(*this, "wxID_CANCEL", wxButton)->SetLabel(_("Abort")); EnableButtons(); } void UpdateDlg::OnDisConnect(wxCommandEvent& event) { XRCCTRL(*this, "wxID_CANCEL", wxButton)->SetLabel(_("Close")); EnableButtons(); } void UpdateDlg::OnProgress(wxCommandEvent& event) { int prg = -1; if (m_CurrFileSize != 0) prg = event.GetInt() * 100 / m_CurrFileSize; UpdateStatus(_("Downloading: ") + event.GetString(), prg); wxStaticText* lbl = XRCCTRL(*this, "lblProgress", wxStaticText); wxString msg; msg.Printf(_("%s of %s"), GetSizeString(event.GetInt()).c_str(), GetSizeString(m_CurrFileSize).c_str()); lbl->SetLabel(msg); } void UpdateDlg::OnAborted(wxCommandEvent& event) { UpdateStatus(_("Download aborted: ") + event.GetString(), 0, 0); XRCCTRL(*this, "lblProgress", wxStaticText)->SetLabel(_T("")); m_LastBlockSize = 0; } void UpdateDlg::OnDownloadStarted(wxCommandEvent& event) { m_CurrFileSize = event.GetInt(); UpdateStatus(_("Download started: ") + event.GetString(), 0, 100); XRCCTRL(*this, "lblProgress", wxStaticText)->SetLabel(_T("")); m_LastBlockSize = 0; } void UpdateDlg::OnDownloadEnded(wxCommandEvent& event) { UpdateStatus(_("Download finished: ") + event.GetString()); XRCCTRL(*this, "lblProgress", wxStaticText)->SetLabel(_T("")); m_LastBlockSize = 0; if (m_HasUpdated && event.GetInt() == 0) { UpdateRec* rec = GetRecFromListView(); if (rec) { if (rec->bytes != m_CurrFileSize) wxMessageBox(_("File size mismatch for ") + event.GetString() + _("!\n\n" "This, usually, means one of three things:\n" "1) The reported size in the update list is wrong. The DevPak might still be valid.\n" "2) The file's location returned a web error-page. Invalid DevPak...\n" "3) The file is corrupt...\n\n" "You can try to install it anyway. If it is not a valid DevPak, the operation will fail."), _("Warning"), wxICON_WARNING); } if (rec && rec->installable && wxMessageBox(_("Do you want to install ") + event.GetString() + _(" now?"), _("Confirmation"), wxICON_QUESTION | wxYES_NO) == wxYES) InstallFile(); else if (rec && rec->title == _T("WebUpdate Mirrors list")) InstallMirrors(GetPackagePath() + rec->local_file); } m_CurrFileSize = 0; } void UpdateDlg::OnUpdateUI(wxUpdateUIEvent& event) { // hack to display the download message *after* the dialog has been shown... if (m_FirstTimeCheck) { m_FirstTimeCheck = false; // no more, just once wxString config = GetConfFilename(); if (wxFileExists(config)) InternetUpdate(); else { if (wxMessageBox(_("A list of updates needs to be downloaded.\nDo you want to do this now?"), _("Confirmation"), wxICON_QUESTION | wxYES_NO) == wxYES) InternetUpdate(true); } } }