/****************************************************************************** * * * Copyright 2016 Subterranean Security * * * * 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 com.subterranean_security.crimson.viewer.ui.screen.controlpanels.client.controls; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Observable; import java.util.Observer; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ExecutionException; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.SwingWorker; import javax.swing.border.BevelBorder; import com.subterranean_security.crimson.core.Common; import com.subterranean_security.crimson.core.attribute.keys.AKeySimple; import com.subterranean_security.crimson.core.net.stream.StreamStore; import com.subterranean_security.crimson.core.net.stream.info.InfoMaster; import com.subterranean_security.crimson.core.proto.Misc.Outcome; import com.subterranean_security.crimson.core.proto.State.StateType; import com.subterranean_security.crimson.core.proto.Stream.InfoParam; import com.subterranean_security.crimson.core.util.DateUtil; import com.subterranean_security.crimson.sv.profile.ClientProfile; import com.subterranean_security.crimson.viewer.net.ViewerCommands; import com.subterranean_security.crimson.viewer.ui.UIUtil; import com.subterranean_security.crimson.viewer.ui.common.components.Console; import com.subterranean_security.crimson.viewer.ui.common.components.Console.LineType; import com.subterranean_security.crimson.viewer.ui.common.components.ProgressBarFactory; import com.subterranean_security.crimson.viewer.ui.common.components.StatusConsole; import com.subterranean_security.crimson.viewer.ui.common.panels.sl.epanel.EPanel; import com.subterranean_security.crimson.viewer.ui.screen.controlpanels.client.CPPanel; import com.subterranean_security.crimson.viewer.ui.screen.controlpanels.client.controls.ep.Confirmation; public class ControlsTab extends JPanel implements CPPanel, Observer { private static final long serialVersionUID = 1L; private EPanel ep; private ClientProfile profile; private Console console; private StatusConsole clientStatusConsole; private JMenuItem mntmShutdown; private JMenuItem mntmRestart; private JMenuItem mntmStandby; private JMenuItem mntmHibernate; private JMenuItem mntmLogoff; private JMenuItem mntmKillProcess; private JMenuItem mntmRestartProcess; private JMenuItem mntmUpdate; private JMenuItem mntmUninstall; private JMenuItem mntmRelocate; private JLabel statConsoleUptime; private JLabel statConsoleStatus; private JLabel statConsoleInstallDate; private JLabel statConsoleLocation; private JLabel statConsoleLastContact; public ControlsTab(EPanel ep, ClientProfile profile, Console console) { this.ep = ep; this.profile = profile; this.console = console; init(); initValues(); } public void setControlsEnabled(boolean e) { mntmShutdown.setEnabled(e); mntmRestart.setEnabled(e); mntmStandby.setEnabled(e); mntmHibernate.setEnabled(e); mntmLogoff.setEnabled(e); mntmKillProcess.setEnabled(e); mntmRestartProcess.setEnabled(e); mntmUpdate.setEnabled(e); mntmUninstall.setEnabled(e); mntmRelocate.setEnabled(e); } public void init() { setLayout(new BorderLayout(0, 0)); JPanel panel_3 = new JPanel(); add(panel_3, BorderLayout.CENTER); clientStatusConsole = new StatusConsole(); clientStatusConsole.setPreferredSize(new Dimension(450, 100)); panel_3.add(clientStatusConsole); clientStatusConsole.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null)); statConsoleUptime = clientStatusConsole.addRow("Uptime"); statConsoleStatus = clientStatusConsole.addRow("Status"); statConsoleInstallDate = clientStatusConsole.addRow("Install Date"); statConsoleLocation = clientStatusConsole.addRow("Location"); statConsoleLastContact = clientStatusConsole.addRow("Last Contact"); statConsoleInstallDate.setText(profile.get(AKeySimple.CLIENT_INSTALL_DATE)); statConsoleLocation.setText(profile.get(AKeySimple.CLIENT_BASE_PATH)); JProgressBar barUpdate = ProgressBarFactory.get(); barUpdate.setPreferredSize(new Dimension(100, 4)); JMenuBar menuBar = new JMenuBar(); add(menuBar, BorderLayout.NORTH); JMenu mnPower = new JMenu("Power"); mnPower.setIcon(UIUtil.getIcon("icons16/general/power_surge.png")); menuBar.add(mnPower); mntmShutdown = new JMenuItem("Shutdown"); mntmShutdown.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { new CommandWorker(StateType.SHUTDOWN, profile).execute(); } }); mntmShutdown.setFont(new Font("Dialog", Font.BOLD, 10)); mntmShutdown.setIcon(UIUtil.getIcon("icons16/general/lcd_tv_off.png")); mnPower.add(mntmShutdown); mntmRestart = new JMenuItem("Restart"); mntmRestart.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { new CommandWorker(StateType.RESTART, profile).execute(); } }); mntmRestart.setFont(new Font("Dialog", Font.BOLD, 10)); mntmRestart.setIcon(UIUtil.getIcon("icons16/general/arrow_redo.png")); mnPower.add(mntmRestart); mntmStandby = new JMenuItem("Standby"); mntmStandby.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { new CommandWorker(StateType.STANDBY, profile).execute(); } }); mntmStandby.setFont(new Font("Dialog", Font.BOLD, 10)); mntmStandby.setIcon(UIUtil.getIcon("icons16/general/lcd_tv_test.png")); mnPower.add(mntmStandby); mntmHibernate = new JMenuItem("Hibernate"); mntmHibernate.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { new CommandWorker(StateType.HIBERNATE, profile).execute(); } }); mntmHibernate.setFont(new Font("Dialog", Font.BOLD, 10)); mntmHibernate.setIcon(UIUtil.getIcon("icons16/general/wizard.png")); mnPower.add(mntmHibernate); mntmLogoff = new JMenuItem("Logoff"); mntmLogoff.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // new CommandWorker(StateType.SHUTDOWN, profile).execute(); } }); mntmLogoff.setFont(new Font("Dialog", Font.BOLD, 10)); mnPower.add(mntmLogoff); JMenu mnClient = new JMenu("Client"); mnClient.setIcon(UIUtil.getIcon("icons16/general/user.png")); menuBar.add(mnClient); mntmKillProcess = new JMenuItem("Kill Process"); mntmKillProcess.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { new CommandWorker(StateType.KILL, profile).execute(); } }); mntmKillProcess.setFont(new Font("Dialog", Font.BOLD, 10)); mntmKillProcess.setIcon(UIUtil.getIcon("icons16/general/delete.png")); mnClient.add(mntmKillProcess); mntmRestartProcess = new JMenuItem("Restart Process"); mntmRestartProcess.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { new CommandWorker(StateType.RESTART_PROCESS, profile).execute(); } }); mntmRestartProcess.setFont(new Font("Dialog", Font.BOLD, 10)); mntmRestartProcess.setIcon(UIUtil.getIcon("icons16/general/arrow_redo.png")); mnClient.add(mntmRestartProcess); mntmUpdate = new JMenuItem("Update"); mntmUpdate.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setControlsEnabled(false); barUpdate.setIndeterminate(true); stopStreaming(); statConsoleStatus.setText("UPDATING..."); console.addLine( "Updating client (" + profile.get(AKeySimple.CLIENT_VERSION) + " -> " + Common.version + ")", LineType.BLUE); new SwingWorker<Outcome, Void>() { @Override protected Outcome doInBackground() throws Exception { return ViewerCommands.updateClient(profile.getCvid()); } protected void done() { Outcome outcome = null; try { outcome = get(); } catch (InterruptedException | ExecutionException e) { outcome = Outcome.newBuilder().setResult(false).setComment(e.getMessage()).build(); } setControlsEnabled(true); barUpdate.setIndeterminate(false); if (!outcome.getResult()) { console.addLine("Update failed: " + outcome.getComment(), LineType.ORANGE); } else { console.addLine("Update succeeded", LineType.GREEN); statConsoleStatus.setText("RESTARTING CLIENT..."); barUpdate.setValue(100); new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { Thread.sleep(500); return null; } @Override protected void done() { barUpdate.setValue(0); } }.execute(); } }; }.execute(); } }); mntmUpdate.setFont(new Font("Dialog", Font.BOLD, 10)); mntmUpdate.setIcon(UIUtil.getIcon("icons16/general/update.png")); mnClient.add(mntmUpdate); mntmUninstall = new JMenuItem("Uninstall"); mntmUninstall.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setControlsEnabled(false); // confirm action with user c = new Confirmation(ep, ControlsTab.this, "The Crimson client will be uninstalled immediately."); ep.raise(c, 80); new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { synchronized (c) { try { c.wait(); } catch (InterruptedException e1) { } } return null; } protected void done() { if (!c.getResult()) { setControlsEnabled(true); return; } console.addLine("Sending uninstall signal to client: " + profile.get(AKeySimple.NET_HOSTNAME)); new SwingWorker<Outcome, Void>() { @Override protected Outcome doInBackground() throws Exception { return ViewerCommands.changeClientState(profile.getCvid(), StateType.UNINSTALL); } protected void done() { try { Outcome outcome = get(); if (!outcome.getResult()) { console.addLine("Uninstall error: " + outcome.getComment()); } } catch (InterruptedException | ExecutionException e) { console.addLine("Standby failed: " + e.getMessage(), LineType.ORANGE); } setControlsEnabled(true); }; }.execute(); } }.execute(); } }); mntmUninstall.setFont(new Font("Dialog", Font.BOLD, 10)); mntmUninstall.setIcon(UIUtil.getIcon("icons16/general/radioactivity.png")); mnClient.add(mntmUninstall); mntmRelocate = new JMenuItem("Relocate"); mntmRelocate.setFont(new Font("Dialog", Font.BOLD, 10)); mnClient.add(mntmRelocate); JMenu mnSound = new JMenu("Sound"); mnSound.setIcon(UIUtil.getIcon("icons16/general/sound.png")); menuBar.add(mnSound); JMenu mnDesktop = new JMenu("Desktop"); menuBar.add(mnDesktop); } class CommandWorker extends SwingWorker<Outcome, Void> { private ClientProfile profile; private StateType state; public CommandWorker(StateType state, ClientProfile cp) { this.state = state; this.profile = cp; setControlsEnabled(false); } @Override protected Outcome doInBackground() throws Exception { return ViewerCommands.changeClientState(profile.getCvid(), state); } protected void done() { try { Outcome outcome = get(); if (outcome.getResult()) { console.addLine("Saving state and shutting down in one second", LineType.GREEN); } else { console.addLine( "Shutdown error: " + (outcome.hasComment() ? outcome.getComment() : "Unknown error"), LineType.ORANGE); } } catch (InterruptedException | ExecutionException e) { console.addLine("Shutdown failed: " + e.getMessage(), LineType.ORANGE); } setControlsEnabled(true); }; } private void initValues() { refreshDates(); statConsoleStatus.setText(profile.get(AKeySimple.CLIENT_STATUS)); } private static DateFormat uptimeFormat = new SimpleDateFormat("EEE MMM dd kk:mm:ss z yyyy"); public void refreshDates() { try { statConsoleUptime.setText( DateUtil.timeBetween(new Date(), uptimeFormat.parse(profile.get(AKeySimple.OS_START_TIME)))); } catch (ParseException e) { statConsoleUptime.setText("N/A"); } statConsoleLastContact.setText(DateUtil.timeBetween(new Date(), profile.getLastUpdate())); } private Confirmation c; public void notifyConfirmation() { synchronized (c) { c.notifyAll(); } } @Override public void clientOffline() { setControlsEnabled(false); } @Override public void serverOffline() { clientOffline(); } @Override public void clientOnline() { setControlsEnabled(true); } @Override public void serverOnline() { // TODO check if client is online } private InfoMaster im; private Timer refreshTimer; class DateRefreshTask extends TimerTask { @Override public void run() { refreshDates(); } }; public void startStreaming() { if (im == null) { im = new InfoMaster(InfoParam.newBuilder().addKey(AKeySimple.CLIENT_STATUS.getFullID()).build(), profile.getCvid(), 1000); StreamStore.addStream(im); } } public void stopStreaming() { if (im != null) { StreamStore.removeStreamBySID(im.getStreamID()); im = null; } } public void startRefreshingDates() { if (refreshTimer == null) { refreshTimer = new Timer(); // TODO use a decreasing timer refreshTimer.schedule(new DateRefreshTask(), 0, 1000); } } public void stopRefreshingDates() { if (refreshTimer != null) { refreshTimer.cancel(); refreshTimer = null; } } @Override public void tabOpened() { profile.addObserver(this); startStreaming(); startRefreshingDates(); } @Override public void tabClosed() { profile.deleteObserver(this); stopStreaming(); stopRefreshingDates(); } @Override public void update(Observable arg0, Object arg1) { if (arg1.equals(AKeySimple.CLIENT_STATUS)) { statConsoleStatus.setText(profile.get(AKeySimple.CLIENT_STATUS)); } } }