/* * Zed Attack Proxy (ZAP) and its related class files. * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * * Copyright 2012 psiinon@gmail.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.zaproxy.zap.extension.autoupdate; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Desktop; import java.awt.EventQueue; import java.awt.GridBagLayout; import java.awt.HeadlessException; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.io.File; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.PatternSyntaxException; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.RowSorter; import javax.swing.SortOrder; import javax.swing.UIManager; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.RowFilter; import javax.swing.border.TitledBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import org.apache.log4j.Logger; import org.jdesktop.swingx.JXTable; import org.jdesktop.swingx.decorator.AbstractHighlighter; import org.jdesktop.swingx.decorator.ComponentAdapter; import org.jdesktop.swingx.decorator.CompoundHighlighter; import org.jdesktop.swingx.decorator.HighlightPredicate; import org.jdesktop.swingx.decorator.IconHighlighter; import org.jdesktop.swingx.renderer.DefaultTableRenderer; import org.jdesktop.swingx.renderer.IconAware; import org.jdesktop.swingx.renderer.IconValues; import org.jdesktop.swingx.renderer.MappedValue; import org.jdesktop.swingx.renderer.StringValues; import org.parosproxy.paros.Constant; import org.parosproxy.paros.extension.Extension; import org.parosproxy.paros.model.Model; import org.parosproxy.paros.view.AbstractFrame; import org.parosproxy.paros.view.View; import org.zaproxy.zap.control.AddOn; import org.zaproxy.zap.control.AddOnCollection; import org.zaproxy.zap.utils.DesktopUtils; import org.zaproxy.zap.utils.FontUtils; import org.zaproxy.zap.view.LayoutHelper; public class ManageAddOnsDialog extends AbstractFrame implements CheckForUpdateCallback { protected enum State {IDLE, DOWNLOADING_ZAP, DOWNLOADED_ZAP, DOWNLOADING_UPDATES, DOWNLOADED_UPDATES} static final Icon ICON_ADD_ON_ISSUES = new ImageIcon( InstalledAddOnsTableModel.class.getResource("/resource/icon/16/050.png")); static final Icon ICON_ADD_ON_EXTENSION_ISSUES = new ImageIcon( InstalledAddOnsTableModel.class.getResource("/resource/icon/fugue/information-white.png")); private static final Logger logger = Logger.getLogger(ManageAddOnsDialog.class); private static final long serialVersionUID = 1L; private JTabbedPane jTabbed = null; private JPanel topPanel = null; private JPanel installedPanel = null; private JPanel browsePanel = null; private JPanel corePanel = null; private JPanel installedAddOnsPanel = null; private JPanel installedAddOnsFilterPanel = null; private JPanel uninstalledAddOnsPanel = null; private JPanel uninstalledAddOnsFilterPanel = null; private JPanel retrievePanel = null; private JScrollPane marketPlaceScrollPane = null; private JButton addOnInfoButton = null; private JButton coreNotesButton = null; private JButton downloadZapButton = null; private JButton checkForUpdatesButton = null; private JButton updateButton = null; private JButton updateAllButton = null; private JButton uninstallButton = null; private JButton installButton = null; private JButton close1Button = null; private JButton close2Button = null; private JLabel downloadProgress = null; private JLabel updatesMessage = null; private JXTable installedAddOnsTable = null; private JXTable uninstalledAddOnsTable = null; //private ZapRelease latestRelease = null; private String currentVersion = null; private AddOnCollection latestInfo = null; private AddOnCollection prevInfo = null; private ExtensionAutoUpdate extension = null; private AddOnCollection installedAddOns; private final InstalledAddOnsTableModel installedAddOnsModel; private final UninstalledAddOnsTableModel uninstalledAddOnsModel; private State state = null; /** * @throws HeadlessException */ public ManageAddOnsDialog(ExtensionAutoUpdate ext, String currentVersion, AddOnCollection installedAddOns) throws HeadlessException { super(); this.extension = ext; this.currentVersion = currentVersion; this.installedAddOns = installedAddOns; installedAddOnsModel = new InstalledAddOnsTableModel(installedAddOns); uninstalledAddOnsModel = new UninstalledAddOnsTableModel(installedAddOns); initialize(); } /** * This method initializes this * */ private void initialize() { this.setTitle(Constant.messages.getString("cfu.manage.title")); //this.setContentPane(getJTabbed()); this.setContentPane(getTopPanel()); this.pack(); if (Model.getSingleton().getOptionsParam().getViewParam().getWmUiHandlingOption() == 0) { this.setSize(700, 500); } state = State.IDLE; // Handle escape key to close the dialog KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false); AbstractAction escapeAction = new AbstractAction() { private static final long serialVersionUID = 3516424501887406165L; @Override public void actionPerformed(ActionEvent e) { dispatchEvent(new WindowEvent(ManageAddOnsDialog.this, WindowEvent.WINDOW_CLOSING)); } }; getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape, "ESCAPE"); getRootPane().getActionMap().put("ESCAPE",escapeAction); } private JPanel getTopPanel() { if (topPanel == null) { topPanel = new JPanel(); topPanel.setLayout(new GridBagLayout()); topPanel.add(getJTabbed(), LayoutHelper.getGBC(0, 0, 1, 1.0D, 1.0D)); topPanel.add(this.getUpdatesMessage(), LayoutHelper.getGBC(0, 2, 1, 1.0D)); } return topPanel; } private JTabbedPane getJTabbed() { if (jTabbed == null) { jTabbed = new JTabbedPane(); jTabbed.addTab(Constant.messages.getString("cfu.tab.installed"), this.getInstalledPanel()); jTabbed.addTab(Constant.messages.getString("cfu.tab.browse"), this.getBrowsePanel()); } return jTabbed; } protected void selectMarketplaceTab() { getJTabbed().setSelectedIndex(1); } private JPanel getInstalledPanel() { if (installedPanel == null) { installedPanel = new JPanel(); installedPanel.setLayout(new GridBagLayout()); installedPanel.add(getCorePanel(true), LayoutHelper.getGBC(0, 0, 1, 1.0D, 0.0D)); installedPanel.add(getInstalledAddOnsPanel(), LayoutHelper.getGBC(0, 1, 1, 1.0D, 1.0D)); } return installedPanel; } private JPanel getBrowsePanel() { if (browsePanel == null) { browsePanel = new JPanel(); browsePanel.setLayout(new GridBagLayout()); browsePanel.add(getUninstalledAddOnsPanel(), LayoutHelper.getGBC(0, 0, 1, 1.0D, 1.0D)); } return browsePanel; } private JPanel getCorePanel(boolean update) { if (corePanel == null) { corePanel = new JPanel(); corePanel.setLayout(new GridBagLayout()); corePanel.setBorder( BorderFactory.createTitledBorder( null, Constant.messages.getString("cfu.label.zap.border"), TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, FontUtils.getFont(FontUtils.Size.standard), java.awt.Color.black)); if (latestInfo == null || this.latestInfo.getZapRelease() == null) { // Havnt checked for updatees yet corePanel.add(new JLabel(this.currentVersion), LayoutHelper.getGBC(0, 0, 1, 0.0D)); corePanel.add(new JLabel(""), LayoutHelper.getGBC(1, 0, 1, 1.0D)); corePanel.add(this.getCheckForUpdatesButton(), LayoutHelper.getGBC(2, 0, 1, 0.0D)); } else if (this.latestInfo.getZapRelease().isNewerThan(this.currentVersion)) { corePanel.add(new JLabel(Constant.messages.getString("cfu.check.zap.newer")), LayoutHelper.getGBC(0, 0, 1, 0.0D)); corePanel.add(new JLabel(this.latestInfo.getZapRelease().getVersion()), LayoutHelper.getGBC(1, 0, 1, 0.1D)); corePanel.add(new JLabel(""), LayoutHelper.getGBC(2, 0, 1, 0.8D)); corePanel.add(this.getDownloadProgress(), LayoutHelper.getGBC(3, 0, 1, 0.2D)); corePanel.add(this.getCoreNotesButton(), LayoutHelper.getGBC(4, 0, 1, 0.0D)); corePanel.add(this.getDownloadZapButton(), LayoutHelper.getGBC(5, 0, 1, 0.0D)); } else { corePanel.add(new JLabel(this.currentVersion + " : " + Constant.messages.getString("cfu.check.zap.latest")), LayoutHelper.getGBC(0, 0, 1, 1.0D)); } } else if (update && latestInfo != null && this.latestInfo.getZapRelease() != null) { corePanel.removeAll(); if (this.latestInfo.getZapRelease().isNewerThan(this.currentVersion)) { corePanel.add(new JLabel(Constant.messages.getString("cfu.check.zap.newer")), LayoutHelper.getGBC(0, 0, 1, 0.0D)); corePanel.add(new JLabel(this.latestInfo.getZapRelease().getVersion()), LayoutHelper.getGBC(1, 0, 1, 0.1D)); corePanel.add(new JLabel(""), LayoutHelper.getGBC(2, 0, 1, 0.8D)); corePanel.add(this.getDownloadProgress(), LayoutHelper.getGBC(3, 0, 1, 0.2D)); corePanel.add(this.getCoreNotesButton(), LayoutHelper.getGBC(4, 0, 1, 0.0D)); corePanel.add(this.getDownloadZapButton(), LayoutHelper.getGBC(5, 0, 1, 0.0D)); } else { corePanel.add(new JLabel(this.currentVersion + " : " + Constant.messages.getString("cfu.check.zap.latest")), LayoutHelper.getGBC(0, 0, 1, 1.0D)); } installedPanel.validate(); } return corePanel; } private JPanel getInstalledAddOnsPanel() { if (installedAddOnsPanel == null) { installedAddOnsPanel = new JPanel(); installedAddOnsPanel.setLayout(new GridBagLayout()); installedAddOnsPanel.setBorder( BorderFactory.createTitledBorder( null, Constant.messages.getString("cfu.label.addons.border"), TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, FontUtils.getFont(FontUtils.Size.standard), java.awt.Color.black)); getInstalledAddOnsTable(); JScrollPane scrollPane = new JScrollPane(); scrollPane.setHorizontalScrollBarPolicy(javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setViewportView(getInstalledAddOnsTable()); installedAddOnsFilterPanel = createFilterPanel(getInstalledAddOnsTable()); int row = 0; installedAddOnsPanel.add(installedAddOnsFilterPanel, LayoutHelper.getGBC(0, row++, 5, 0.0D)); installedAddOnsPanel.add(scrollPane, LayoutHelper.getGBC(0, row++, 5, 1.0D, 1.0D)); installedAddOnsPanel.add(new JLabel(""), LayoutHelper.getGBC(0, row, 1, 1.0D)); installedAddOnsPanel.add(getUninstallButton(), LayoutHelper.getGBC(1, row, 1, 0.0D)); installedAddOnsPanel.add(getUpdateButton(), LayoutHelper.getGBC(2, row, 1, 0.0D)); installedAddOnsPanel.add(getUpdateAllButton(), LayoutHelper.getGBC(3, row, 1, 0.0D)); installedAddOnsPanel.add(getClose1Button(), LayoutHelper.getGBC(4, row, 1, 0.0D)); } return installedAddOnsPanel; } private JPanel getUninstalledAddOnsPanel() { if (uninstalledAddOnsPanel == null) { uninstalledAddOnsPanel = new JPanel(); uninstalledAddOnsPanel.setLayout(new GridBagLayout()); uninstalledAddOnsPanel.setBorder( BorderFactory.createTitledBorder( null, Constant.messages.getString("cfu.label.addons.border"), TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, FontUtils.getFont(FontUtils.Size.standard), java.awt.Color.black)); uninstalledAddOnsFilterPanel = createFilterPanel(getUninstalledAddOnsTable()); if (latestInfo == null) { // Not checked yet getUninstalledAddOnsTable(); // To initialise the table and model getMarketPlaceScrollPane().setViewportView(getRetrievePanel()); uninstalledAddOnsFilterPanel.setVisible(false); } else { getMarketPlaceScrollPane().setViewportView(getUninstalledAddOnsTable()); uninstalledAddOnsFilterPanel.setVisible(true); } int row = 0; uninstalledAddOnsPanel.add(uninstalledAddOnsFilterPanel, LayoutHelper.getGBC(0, row++, 4, 0.0D)); uninstalledAddOnsPanel.add(getMarketPlaceScrollPane(), LayoutHelper.getGBC(0, row++, 4, 1.0D, 1.0D)); uninstalledAddOnsPanel.add(new JLabel(""), LayoutHelper.getGBC(0, row, 1, 1.0D)); uninstalledAddOnsPanel.add(getInstallButton(), LayoutHelper.getGBC(1, row, 1, 0.0D)); uninstalledAddOnsPanel.add(getAddOnInfoButton(), LayoutHelper.getGBC(2, row, 1, 0.0D)); uninstalledAddOnsPanel.add(getClose2Button(), LayoutHelper.getGBC(3, row, 1, 0.0D)); } return uninstalledAddOnsPanel; } private static JPanel createFilterPanel(final JXTable table) { JPanel filterPanel = new JPanel(); filterPanel.setLayout(new GridBagLayout()); JLabel filterLabel = new JLabel(Constant.messages.getString("cfu.label.addons.filter")); final JTextField filterTextField = new JTextField(); filterLabel.setLabelFor(filterTextField); filterPanel.add(filterLabel, LayoutHelper.getGBC(0, 0, 1, 0.0D)); filterPanel.add(filterTextField, LayoutHelper.getGBC(1, 0, 1, 1.0D)); String tooltipText = Constant.messages.getString("cfu.label.addons.filter.tooltip"); filterLabel.setToolTipText(tooltipText); filterTextField.setToolTipText(tooltipText); // Set filter listener filterTextField.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { updateFilter(); } @Override public void removeUpdate(DocumentEvent e) { updateFilter(); } @Override public void changedUpdate(DocumentEvent e) { updateFilter(); } public void updateFilter() { String filterText = filterTextField.getText(); if (filterText.isEmpty()) { table.setRowFilter(null); filterTextField.setForeground(UIManager.getColor("TextField.foreground")); } else { try { table.setRowFilter(RowFilter.regexFilter("(?i)" + filterText)); filterTextField.setForeground(UIManager.getColor("TextField.foreground")); } catch (PatternSyntaxException e) { filterTextField.setForeground(Color.RED); } } } }); return filterPanel; } private JScrollPane getMarketPlaceScrollPane () { if (marketPlaceScrollPane == null) { marketPlaceScrollPane = new JScrollPane(); marketPlaceScrollPane.setHorizontalScrollBarPolicy(javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); } return marketPlaceScrollPane; } private JPanel getRetrievePanel() { if (retrievePanel == null) { retrievePanel = new JPanel(); retrievePanel.setLayout(new GridBagLayout()); JButton retrieveButton = new JButton(); retrieveButton.setText(Constant.messages.getString("cfu.button.checkForUpdates")); retrieveButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { checkForUpdates(); } }); retrievePanel.add(new JLabel(""), LayoutHelper.getGBC(0, 0, 1, 1.0D)); retrievePanel.add(retrieveButton, LayoutHelper.getGBC(1, 0, 1, 0.0D)); retrievePanel.add(new JLabel(""), LayoutHelper.getGBC(2, 0, 1, 1.0D)); retrievePanel.add(new JLabel(""), LayoutHelper.getGBC(0, 1, 3, 1.0D, 1.0D)); } return retrievePanel; } protected void setPreviousVersionInfo(AddOnCollection prevInfo) { this.prevInfo = prevInfo; } protected void setLatestVersionInfo(AddOnCollection latestInfo) { this.latestInfo = latestInfo; getCorePanel(true); if (latestInfo != null) { installedAddOnsModel.setAvailableAddOns(latestInfo); uninstalledAddOnsModel.setAddOnCollection(latestInfo); List<AddOn> addOnsNotInstalled = installedAddOnsModel.updateEntries(); uninstalledAddOnsModel.setAddOns(addOnsNotInstalled, prevInfo); } getMarketPlaceScrollPane().setViewportView(getUninstalledAddOnsTable()); uninstalledAddOnsFilterPanel.setVisible(true); } private JXTable getInstalledAddOnsTable () { if (installedAddOnsTable == null) { installedAddOnsTable = new JXTable(); installedAddOnsModel.addTableModelListener(new TableModelListener() { @Override public void tableChanged(TableModelEvent e) { getUpdateButton().setEnabled(installedAddOnsModel.canUpdateSelected()); getUpdateAllButton().setEnabled(installedAddOnsModel.getAllUpdates().size() > 0); getUninstallButton().setEnabled(installedAddOnsModel.canUninstallSelected()); }}); installedAddOnsTable.setModel(installedAddOnsModel); installedAddOnsTable.getColumnModel().getColumn(0).setMaxWidth(20);//icon installedAddOnsTable.getColumnExt(0).setSortable(false);//icon doesn't need to be sortable installedAddOnsTable.getColumnModel().getColumn(1).setPreferredWidth(200);//name installedAddOnsTable.getColumnModel().getColumn(2).setPreferredWidth(400);//description installedAddOnsTable.getColumnExt(2).setSortable(false);//description doesn't need to be sortable installedAddOnsTable.getColumnModel().getColumn(3).setPreferredWidth(60);//update installedAddOnsTable.getColumnExt(3).setSortable(false);//update doesn't need to be sortable installedAddOnsTable.getColumnModel().getColumn(4).setPreferredWidth(40); installedAddOnsTable.getColumnExt(4).setSortable(false);//checkbox doesn't need to be sortable //Default sort by name (column 1) List<RowSorter.SortKey> sortKeys = new ArrayList<RowSorter.SortKey>(1); sortKeys.add(new RowSorter.SortKey(1, SortOrder.ASCENDING)); installedAddOnsTable.getRowSorter().setSortKeys(sortKeys); DefaultAddOnToolTipHighlighter toolTipHighlighter = new DefaultAddOnToolTipHighlighter( AddOnsTableModel.COLUMN_ADD_ON_WRAPPER); for (int i = 1; i < installedAddOnsTable.getColumnCount(); i++) { installedAddOnsTable.getColumnExt(i).addHighlighter(toolTipHighlighter); } installedAddOnsTable.getColumnExt(0).setCellRenderer( new DefaultTableRenderer(new MappedValue(StringValues.EMPTY, IconValues.NONE), JLabel.CENTER)); installedAddOnsTable.getColumnExt(0).setHighlighters( new CompoundHighlighter( new WarningRunningIssuesHighlighter(AddOnsTableModel.COLUMN_ADD_ON_WRAPPER), new WarningRunningIssuesToolTipHighlighter(AddOnsTableModel.COLUMN_ADD_ON_WRAPPER))); installedAddOnsTable.getColumnExt(3).setHighlighters( new CompoundHighlighter( new WarningUpdateIssuesHighlighter(AddOnsTableModel.COLUMN_ADD_ON_WRAPPER), new WarningUpdateIssuesToolTipHighlighter(AddOnsTableModel.COLUMN_ADD_ON_WRAPPER))); installedAddOnsTable.getColumnExt(4).addHighlighter( new DisableSelectionHighlighter(AddOnsTableModel.COLUMN_ADD_ON_WRAPPER)); } return installedAddOnsTable; } private JXTable getUninstalledAddOnsTable () { if (uninstalledAddOnsTable == null) { uninstalledAddOnsTable = new JXTable(); uninstalledAddOnsModel.addTableModelListener(new TableModelListener() { @Override public void tableChanged(TableModelEvent e) { getInstallButton().setEnabled(uninstalledAddOnsModel.canIinstallSelected()); }}); uninstalledAddOnsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener(){ @Override public void valueChanged(ListSelectionEvent e) { getAddOnInfoButton().setEnabled(false); if (DesktopUtils.canOpenUrlInBrowser() && getUninstalledAddOnsTable ().getSelectedRowCount() == 1) { //convertRowIndexToModel in-case they sorted AddOnWrapper ao = uninstalledAddOnsModel.getAddOnWrapper(getUninstalledAddOnsTable().convertRowIndexToModel(getUninstalledAddOnsTable().getSelectedRow())); if (ao != null && ao.getAddOn().getInfo() != null) { getAddOnInfoButton().setEnabled(true); } } }}); uninstalledAddOnsTable.setModel(uninstalledAddOnsModel); uninstalledAddOnsTable.getColumnModel().getColumn(0).setMaxWidth(20);//Icon uninstalledAddOnsTable.getColumnExt(0).setSortable(false); //Icon doesn't need sorting uninstalledAddOnsTable.getColumnModel().getColumn(1).setPreferredWidth(50);//Status uninstalledAddOnsTable.getColumnModel().getColumn(2).setPreferredWidth(150);//Name uninstalledAddOnsTable.getColumnModel().getColumn(3).setPreferredWidth(300);//Description uninstalledAddOnsTable.getColumnExt(3).setSortable(false);//Description doesn't need sorting uninstalledAddOnsTable.getColumnModel().getColumn(4).setPreferredWidth(60);//Update (version number) uninstalledAddOnsTable.getColumnExt(4).setSortable(false);//Update doesn't need sorting uninstalledAddOnsTable.getColumnModel().getColumn(5).setPreferredWidth(40);//Checkbox uninstalledAddOnsTable.getColumnExt(5).setSortable(false);//Checkbox doesn't need sorting //Default sort by status (column 1) descending (Release, Beta, Alpha), and name (column 2) ascending List<RowSorter.SortKey> sortKeys = new ArrayList<RowSorter.SortKey>(2); sortKeys.add(new RowSorter.SortKey(1, SortOrder.DESCENDING)); sortKeys.add(new RowSorter.SortKey(2, SortOrder.ASCENDING)); uninstalledAddOnsTable.getRowSorter().setSortKeys(sortKeys); DefaultAddOnToolTipHighlighter toolTipHighlighter = new DefaultAddOnToolTipHighlighter( UninstalledAddOnsTableModel.COLUMN_ADD_ON_WRAPPER); for (int i = 1; i < uninstalledAddOnsTable.getColumnCount(); i++) { uninstalledAddOnsTable.getColumnExt(i).addHighlighter(toolTipHighlighter); } uninstalledAddOnsTable.getColumnExt(0).setCellRenderer( new DefaultTableRenderer(new MappedValue(StringValues.EMPTY, IconValues.NONE), JLabel.CENTER)); uninstalledAddOnsTable.getColumnExt(0).setHighlighters( new CompoundHighlighter( new WarningRunningIssuesHighlighter(AddOnsTableModel.COLUMN_ADD_ON_WRAPPER), new WarningRunningIssuesToolTipHighlighter(UninstalledAddOnsTableModel.COLUMN_ADD_ON_WRAPPER))); uninstalledAddOnsTable.getColumnExt(5).addHighlighter( new DisableSelectionHighlighter(UninstalledAddOnsTableModel.COLUMN_ADD_ON_WRAPPER)); } return uninstalledAddOnsTable; } private String addOnToHtml(AddOn ao) { StringBuilder sb = new StringBuilder(); sb.append("<html>"); sb.append("<table>"); sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.name")); sb.append("</i></td><td>"); sb.append(ao.getName()); sb.append("</td></tr>"); sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.status")); sb.append("</i></td><td>"); sb.append(Constant.messages.getString("cfu.status." + ao.getStatus().name())); sb.append("</td></tr>"); sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.id")); sb.append("</i></td><td>"); sb.append(ao.getId()); sb.append("</td></tr>"); sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.desc")); sb.append("</i></td><td>"); sb.append(ao.getDescription()); sb.append("</td></tr>"); sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.author")); sb.append("</i></td><td>"); sb.append(ao.getAuthor()); sb.append("</td></tr>"); sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.changes")); sb.append("</i></td><td>"); sb.append(ao.getChanges()); sb.append("</td></tr>"); sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.version")); sb.append("</i></td><td>"); sb.append(ao.getFileVersion()); sb.append("</td></tr>"); sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.notbefore")); sb.append("</i></td><td>"); sb.append(ao.getNotBeforeVersion()); sb.append("</td></tr>"); sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.notfrom")); sb.append("</i></td><td>"); sb.append(ao.getNotFromVersion()); sb.append("</td></tr>"); if (!ao.getIdsAddOnDependencies().isEmpty()) { sb.append("<tr><td><i>"); sb.append(Constant.messages.getString("cfu.table.header.dependencies")); sb.append("</i></td><td>"); for (String addOnId : ao.getIdsAddOnDependencies()) { AddOn dep = installedAddOns.getAddOn(addOnId); if (dep == null && latestInfo != null) { dep = latestInfo.getAddOn(addOnId); } if (dep != null) { sb.append(dep.getName()); } else { sb.append(addOnId); } sb.append("<br>"); } sb.append("</td></tr>"); } sb.append("</table>"); sb.append("</html>"); return sb.toString(); } private JLabel getUpdatesMessage() { if (this.updatesMessage == null) { this.updatesMessage = new JLabel(" "); } return this.updatesMessage; } private JButton getCoreNotesButton() { if (coreNotesButton == null) { coreNotesButton = new JButton(); coreNotesButton.setIcon(new ImageIcon(ManageAddOnsDialog.class.getResource("/resource/icon/16/022.png"))); // 'Text file' icon coreNotesButton.setToolTipText(Constant.messages.getString("cfu.button.zap.relnotes")); final ManageAddOnsDialog dialog = this; coreNotesButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { URL url = latestInfo.getZapRelease().getReleaseNotesUrl(); if (url != null && DesktopUtils.canOpenUrlInBrowser()) { if (DesktopUtils.openUrlInBrowser(url.toString())) { // It worked :) return; } } StringBuilder sb = new StringBuilder(); sb.append("<html>"); sb.append(MessageFormat.format( Constant.messages.getString("cfu.title.relnotes"), latestInfo.getZapRelease().getVersion())); // Reformat the notes into html - the leading and trailing whitespace does need to be removed for some reason String []strs = latestInfo.getZapRelease().getReleaseNotes().split("\n"); for (String s : strs) { sb.append(s.replace("<", "<").trim()); } sb.append("</html>"); View.getSingleton().showMessageDialog(dialog, sb.toString()); } }); } return coreNotesButton; } private JButton getClose1Button() { if (close1Button == null) { close1Button = new JButton(); close1Button.setText(Constant.messages.getString("all.button.close")); close1Button.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { ManageAddOnsDialog.this.dispose(); } }); } return close1Button; } private JButton getClose2Button() { if (close2Button == null) { close2Button = new JButton(); close2Button.setText(Constant.messages.getString("all.button.close")); close2Button.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { ManageAddOnsDialog.this.dispose(); } }); } return close2Button; } private JLabel getDownloadProgress() { if (downloadProgress == null) { downloadProgress = new JLabel(""); } return downloadProgress; } private JButton getCheckForUpdatesButton() { if (checkForUpdatesButton == null) { checkForUpdatesButton = new JButton(); checkForUpdatesButton.setText(Constant.messages.getString("cfu.button.checkForUpdates")); checkForUpdatesButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { checkForUpdates(); } }); } return checkForUpdatesButton; } protected void checkForUpdates() { this.setCursor(new Cursor(Cursor.WAIT_CURSOR)); extension.getLatestVersionInfo(this); this.setCursor(Cursor.getDefaultCursor()); } private JButton getDownloadZapButton() { if (downloadZapButton == null) { downloadZapButton = new JButton(); if (Constant.isKali()) { getDownloadZapButton().setText(Constant.messages.getString("cfu.button.zap.options")); } else { downloadZapButton.setText(Constant.messages.getString("cfu.button.zap.download")); } downloadZapButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { if (extension.downloadLatestRelease()) { setDownloadingZap(); } } }); } return downloadZapButton; } protected void setDownloadingZap() { downloadZapButton.setEnabled(false); getUpdateButton().setEnabled(false); // Makes things less complicated getUpdateAllButton().setEnabled(false); state = State.DOWNLOADING_ZAP; getUpdatesMessage().setText(Constant.messages.getString("cfu.check.zap.downloading")); } protected void setDownloadingUpdates() { if (EventQueue.isDispatchThread()) { this.getDownloadZapButton().setEnabled(false); // Makes things less complicated this.getUpdateButton().setEnabled(false); this.getUpdateAllButton().setEnabled(false); this.state = State.DOWNLOADING_UPDATES; this.getUpdatesMessage().setText(Constant.messages.getString("cfu.check.upd.downloading")); } else { EventQueue.invokeLater(new Runnable() { @Override public void run() { setDownloadingUpdates(); } }); } } /** * Notifies that the given {@code addOn} is being downloaded. * * @param addOn the add-on that is being downloaded * @since 2.4.0 */ public void notifyAddOnDownloading(AddOn addOn) { if (installedAddOnsModel.notifyAddOnDownloading(addOn)) { // It's an update... return; } uninstalledAddOnsModel.notifyAddOnDownloading(addOn); } /** * Notifies that the download of the add-on with the given {@code url} as failed. * <p> * The entry of the add-on is updated to report that the download failed. * * @param url the URL of the add-on that was being downloaded * @since 2.4.0 */ public void notifyAddOnDownloadFailed(String url) { if (installedAddOnsModel.notifyAddOnDownloadFailed(url)) { // It's an update... return; } uninstalledAddOnsModel.notifyAddOnDownloadFailed(url); } /** * Notifies that the given {@code addOn} was installed. The add-on is added to the table of installed add-ons, or if an * update, set it as updated, and, if available in marketplace, removed from the table of available add-ons. * * @param addOn the add-on that was installed * @since 2.4.0 */ public void notifyAddOnInstalled(final AddOn addOn) { if (EventQueue.isDispatchThread()) { if (latestInfo != null && latestInfo.getAddOn(addOn.getId()) != null) { uninstalledAddOnsModel.removeAddOn(addOn); } installedAddOnsModel.addOrRefreshAddOn(addOn); } else { EventQueue.invokeLater(new Runnable() { @Override public void run() { notifyAddOnInstalled(addOn); } }); } } /** * Notifies that the given {@code addOn} as not successfully uninstalled. Add-ons that were not successfully uninstalled are * not re-selectable. * * @param addOn the add-on that was not successfully uninstalled * @since 2.4.0 */ public void notifyAddOnFailedUninstallation(final AddOn addOn) { if (EventQueue.isDispatchThread()) { installedAddOnsModel.notifyAddOnFailedUninstallation(addOn); } else { EventQueue.invokeLater(new Runnable() { @Override public void run() { notifyAddOnFailedUninstallation(addOn); } }); } } /** * Notifies that the given {@code addOn} as uninstalled. The add-on is removed from the table of installed add-ons and, if * available in marketplace, added to the table of available add-ons. * * @param addOn the add-on that was uninstalled * @since 2.4.0 */ public void notifyAddOnUninstalled(final AddOn addOn) { if (EventQueue.isDispatchThread()) { installedAddOnsModel.removeAddOn(addOn); if (latestInfo != null) { AddOn availableAddOn = latestInfo.getAddOn(addOn.getId()); if (availableAddOn != null) { uninstalledAddOnsModel.addAddOn(latestInfo.getAddOn(addOn.getId())); } } } else { EventQueue.invokeLater(new Runnable() { @Override public void run() { notifyAddOnUninstalled(addOn); } }); } } private JButton getUpdateButton() { if (updateButton == null) { updateButton = new JButton(); updateButton.setText(Constant.messages.getString("cfu.button.addons.update")); updateButton.setEnabled(false); // Nothing will be selected initially updateButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { processUpdates(installedAddOnsModel.getSelectedUpdates()); } }); } return updateButton; } private JButton getUpdateAllButton() { if (updateAllButton == null) { updateAllButton = new JButton(); updateAllButton.setText(Constant.messages.getString("cfu.button.addons.updateAll")); updateAllButton.setEnabled(false); // Nothing will be selected initially updateAllButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { processUpdates(installedAddOnsModel.getAllUpdates()); } }); } return updateAllButton; } private void processUpdates(Set<AddOn> updatedAddOns) { if (updatedAddOns.isEmpty()) { return; } AddOnDependencyChecker calc = new AddOnDependencyChecker(installedAddOns, latestInfo); AddOnDependencyChecker.AddOnChangesResult result = calc.calculateUpdateChanges(updatedAddOns); if (!calc.confirmUpdateChanges(ManageAddOnsDialog.this, result)) { return; } extension.processAddOnChanges(ManageAddOnsDialog.this, result); } private JButton getUninstallButton() { if (uninstallButton == null) { uninstallButton = new JButton(); uninstallButton.setText(Constant.messages.getString("cfu.button.addons.uninstall")); uninstallButton.setEnabled(false); // Nothing will be selected initially uninstallButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { Set<AddOn> selectedAddOns = installedAddOnsModel.getSelectedAddOns(); if (selectedAddOns.isEmpty()) { return; } Set<AddOn> addOnsBeingDownloaded = installedAddOnsModel.getDownloadingAddOns(); addOnsBeingDownloaded.addAll(uninstalledAddOnsModel.getDownloadingAddOns()); AddOnDependencyChecker calc = new AddOnDependencyChecker(installedAddOns, latestInfo); AddOnDependencyChecker.UninstallationResult changes = calc.calculateUninstallChanges(selectedAddOns); if (!calc.confirmUninstallChanges(ManageAddOnsDialog.this, changes, addOnsBeingDownloaded)) { return; } Set<AddOn> addOns = changes.getUninstallations(); Set<Extension> extensions = changes.getExtensions(); if (!extension.warnUnsavedResourcesOrActiveActions(ManageAddOnsDialog.this, addOns, extensions, false)) { return; } extension.uninstallAddOnsWithView(ManageAddOnsDialog.this, addOns, false, new HashSet<AddOn>()); } }); } return uninstallButton; } private JButton getInstallButton() { if (installButton == null) { installButton = new JButton(); installButton.setText(Constant.messages.getString("cfu.button.addons.install")); installButton.setEnabled(false); // Nothing will be selected initially installButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { Set<AddOn> selectedAddOns = uninstalledAddOnsModel.getSelectedAddOns(); if (selectedAddOns.isEmpty()) { return; } AddOnDependencyChecker calc = new AddOnDependencyChecker(installedAddOns, latestInfo); AddOnDependencyChecker.AddOnChangesResult changes = calc.calculateInstallChanges(selectedAddOns); if (!calc.confirmInstallChanges(ManageAddOnsDialog.this,changes)) { return; } extension.processAddOnChanges(ManageAddOnsDialog.this, changes); } }); } return installButton; } private JButton getAddOnInfoButton() { if (addOnInfoButton == null) { addOnInfoButton = new JButton(); addOnInfoButton.setText(Constant.messages.getString("cfu.button.addons.info")); addOnInfoButton.setEnabled(false); // Nothing will be selected initially addOnInfoButton.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionEvent e) { if (getUninstalledAddOnsTable().getSelectedRow() >= 0) { //convertRowIndexToModel in-case they sorted AddOnWrapper ao = uninstalledAddOnsModel.getAddOnWrapper(getUninstalledAddOnsTable().convertRowIndexToModel(getUninstalledAddOnsTable().getSelectedRow())); if (ao != null && ao.getAddOn().getInfo() != null) { DesktopUtils.openUrlInBrowser(ao.getAddOn().getInfo().toString()); } } } }); } return addOnInfoButton; } public void showProgress() { if (this.state.equals(State.DOWNLOADING_UPDATES)) { // Updates installedAddOnsModel.updateDownloadsProgresses(extension); // New addons uninstalledAddOnsModel.updateDownloadsProgresses(extension); if (extension.getCurrentDownloadCount() == 0) { this.state = State.DOWNLOADED_UPDATES; this.getDownloadZapButton().setEnabled(true); this.getUpdatesMessage().setText(Constant.messages.getString("cfu.check.upd.downloaded")); } } else if (this.state.equals(State.DOWNLOADING_ZAP)) { try { int progress = extension.getDownloadProgressPercent(this.latestInfo.getZapRelease().getUrl()); if (progress > 0) { this.getDownloadProgress().setText(progress + "%"); if (progress >= 100) { this.zapDownloadComplete(); } } } catch (Exception e) { logger.debug("Error on " + this.latestInfo.getZapRelease().getUrl(), e); this.getDownloadProgress().setText(Constant.messages.getString("cfu.table.label.failed")); } } } private void zapDownloadComplete () { if (this.state.equals(State.DOWNLOADED_ZAP)) { // Prevent re-entry return; } this.state = State.DOWNLOADED_ZAP; File f = new File(Constant.FOLDER_LOCAL_PLUGIN, latestInfo.getZapRelease().getFileName()); if (Desktop.isDesktopSupported()) { extension.promptToLaunchReleaseAndClose(this.latestInfo.getZapRelease().getVersion(), f); } else { View.getSingleton().showWarningDialog(this, MessageFormat.format( Constant.messages.getString("cfu.warn.nolaunch"), this.latestInfo.getZapRelease().getVersion(), f.getAbsolutePath())); } // Let people download updates now this.getUpdateButton().setEnabled(true); this.getUpdateAllButton().setEnabled(true); this.getUpdatesMessage().setText(MessageFormat.format( Constant.messages.getString("cfu.check.zap.downloaded"), f.getAbsolutePath())); } @Override public void gotLatestData(AddOnCollection aoc) { // Callback logger.debug("gotLatestData(AddOnCollection " + aoc); if (aoc != null) { setLatestVersionInfo(aoc); } else { View.getSingleton().showWarningDialog(this, Constant.messages.getString("cfu.check.failed")); } } @Override public void insecureUrl(String url, Exception cause) { logger.error("Failed to get check for updates on " + url, cause); View.getSingleton().showWarningDialog(this, Constant.messages.getString("cfu.warn.badurl")); } private static class DisableSelectionHighlighter extends AbstractHighlighter { public DisableSelectionHighlighter(final int columnIndex) { setHighlightPredicate(new HighlightPredicate() { @Override public boolean isHighlighted(final Component renderer, final ComponentAdapter adapter) { AddOn.InstallationStatus status = ((AddOnWrapper) adapter.getValue(columnIndex)).getInstallationStatus(); return AddOn.InstallationStatus.UNINSTALLATION_FAILED == status || AddOn.InstallationStatus.SOFT_UNINSTALLATION_FAILED == status || AddOn.InstallationStatus.DOWNLOADING == status; } }); } @Override protected Component doHighlight(Component renderer, ComponentAdapter adapter) { renderer.setEnabled(false); return renderer; } } private static class WarningRunningIssuesHighlighter extends IconHighlighter { private final int columnIndex; public WarningRunningIssuesHighlighter(int columnIndex) { super(); this.columnIndex = columnIndex; setHighlightPredicate(new HighlightPredicate.EqualsHighlightPredicate(Boolean.TRUE)); } public Icon getIcon(ComponentAdapter adapter) { AddOnWrapper aow = (AddOnWrapper) adapter.getValue(columnIndex); if (aow.isAddOnRunningIssues()) { return ICON_ADD_ON_ISSUES; } return ICON_ADD_ON_EXTENSION_ISSUES; } @Override protected Component doHighlight(Component component, ComponentAdapter adapter) { if (component instanceof IconAware) { ((IconAware) component).setIcon(getIcon(adapter)); } else if (component instanceof JLabel) { ((JLabel) component).setIcon(getIcon(adapter)); } return component; } } private static class WarningUpdateIssuesHighlighter extends IconHighlighter { private final int columnIndex; public WarningUpdateIssuesHighlighter(int columnIndex) { super(); this.columnIndex = columnIndex; setHighlightPredicate(new HighlightPredicate() { @Override public boolean isHighlighted(final Component renderer, final ComponentAdapter adapter) { AddOnWrapper aow = (AddOnWrapper) adapter.getValue(WarningUpdateIssuesHighlighter.this.columnIndex); if (AddOnWrapper.Status.newVersion == aow.getStatus()) { return aow.hasUpdateIssues(); } return false; } }); } public Icon getIcon(ComponentAdapter adapter) { AddOnWrapper aow = (AddOnWrapper) adapter.getValue(columnIndex); if (aow.isAddOnUpdateIssues()) { return ICON_ADD_ON_ISSUES; } return ICON_ADD_ON_EXTENSION_ISSUES; } @Override protected Component doHighlight(Component component, ComponentAdapter adapter) { if (component instanceof IconAware) { ((IconAware) component).setIcon(getIcon(adapter)); } else if (component instanceof JLabel) { ((JLabel) component).setIcon(getIcon(adapter)); } return component; } } private class DefaultAddOnToolTipHighlighter extends AbstractHighlighter { private final int column; public DefaultAddOnToolTipHighlighter(int column) { this.column = column; } @Override protected Component doHighlight(Component component, ComponentAdapter adapter) { ((JComponent) component).setToolTipText(getToolTip((AddOnWrapper) adapter.getValue(column))); return component; } protected String getToolTip(AddOnWrapper aow) { if (AddOn.InstallationStatus.UNINSTALLATION_FAILED == aow.getInstallationStatus() || AddOn.InstallationStatus.SOFT_UNINSTALLATION_FAILED == aow.getInstallationStatus()) { return addOnToHtml(aow.getAddOn()); } AddOn addOn = (aow.getAddOnUpdate() != null) ? aow.getAddOnUpdate() : aow.getAddOn(); return addOnToHtml(addOn); } } private class WarningRunningIssuesToolTipHighlighter extends DefaultAddOnToolTipHighlighter { public WarningRunningIssuesToolTipHighlighter(int column) { super(column); } @Override protected String getToolTip(AddOnWrapper aow) { if (aow.hasRunningIssues()) { return aow.getRunningIssues(); } return super.getToolTip(aow); } } private class WarningUpdateIssuesToolTipHighlighter extends DefaultAddOnToolTipHighlighter { public WarningUpdateIssuesToolTipHighlighter(int column) { super(column); } @Override protected String getToolTip(AddOnWrapper aow) { if (aow.hasUpdateIssues()) { return aow.getUpdateIssues(); } return super.getToolTip(aow); } } }