package ch.rakudave.jnetmap.plugins;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import net.xeoh.plugins.base.annotations.PluginImplementation;
import ch.rakudave.jnetmap.controller.Scheduler;
import ch.rakudave.jnetmap.model.Map;
import ch.rakudave.jnetmap.model.IF.PhysicalIF;
import ch.rakudave.jnetmap.model.device.Device;
import ch.rakudave.jnetmap.model.device.DeviceEvent;
import ch.rakudave.jnetmap.model.device.DeviceEvent.Type;
import ch.rakudave.jnetmap.util.DeviceEventFilter;
import ch.rakudave.jnetmap.util.IO;
import ch.rakudave.jnetmap.util.Icons;
import ch.rakudave.jnetmap.util.Lang;
import ch.rakudave.jnetmap.util.SwingHelper;
import ch.rakudave.jnetmap.util.logging.Logger;
import ch.rakudave.jnetmap.view.preferences.PreferencePanel;
@PluginImplementation
public class ScriptNotifier implements Notifier {
private String name = Lang.getNoHTML("menu.file.new"), command = "", args = "";
private DeviceEventFilter filter = new DeviceEventFilter();
@Override
public void statusChanged(final DeviceEvent e, final Map m) {
boolean match = (!command.isEmpty()) && filter.matches(e);
Logger.debug("Attempting to execute script, filtered: " + !match);
if (match) {
Scheduler.execute(new Runnable() {
@Override
public void run() {
Process process = null;
try {
String[] commandAndArgs = splitArgs(command, fillArgs(args, e, m));
Logger.trace("executing script: "+Arrays.toString(commandAndArgs));
process = Runtime.getRuntime().exec(commandAndArgs);
process.waitFor();
} catch (Exception ex) {
Logger.warn("Unable to execute script", ex);
} finally {
if (process != null) process.destroy();
}
}
});
}
}
private String fillArgs(String arguments, DeviceEvent e, Map m) {
String args = arguments.replaceAll("%e", e.getType().toString());
args = args.replaceAll("%mn", m.getFileName());
args = args.replaceAll("%mp", m.getFilePath());
Device d;
if (Type.STATUS_CHANGED.equals(e.getType())) {
d = (Device) e.getSource();
} else {
PhysicalIF nif = (PhysicalIF) e.getSubject();
d = nif.getDevice();
args = args.replaceAll("%is", nif.getStatus().toString());
args = args.replaceAll("%in", nif.getName());
args = args.replaceAll("%ia", nif.getAddress().getHostAddress());
args = args.replaceAll("%iu", nif.getSubnet().getInfo().getBroadcastAddress());
args = args.replaceAll("%ig", nif.getGateway().getHostAddress());
args = args.replaceAll("%im", nif.getMacAddress());
}
args = args.replaceAll("%ds", d.getStatus().toString());
args = args.replaceAll("%dn", d.getName());
args = args.replaceAll("%dl", d.getLocation());
args = args.replaceAll("%di", new File(IO.userDir, "cisco/"+d.getType().toString().toLowerCase()+".png").getAbsolutePath());
return args;
}
/**
* @author Jan Goyvaerts
*/
private String[] splitArgs(String command, String args) {
List<String> matchList = new ArrayList<String>();
matchList.add(command);
Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
Matcher regexMatcher = regex.matcher(args);
while (regexMatcher.find()) {
if (regexMatcher.group(1) != null) {
// Add double-quoted string without the quotes
matchList.add(regexMatcher.group(1));
} else if (regexMatcher.group(2) != null) {
// Add single-quoted string without the quotes
matchList.add(regexMatcher.group(2));
} else {
// Add unquoted word
matchList.add(regexMatcher.group());
}
}
String[] out = new String[matchList.size()];
for (int i = 0; i < out.length; i++) {
out[i] = matchList.get(i);
}
return out;
}
@SuppressWarnings("serial")
@Override
public void showPropertiesWindow(final Frame owner, boolean isSetup) {
final JDialog d = new JDialog(owner, getPluginName() + " - " + name, ModalityType.DOCUMENT_MODAL);
d.setLayout(new BorderLayout(5, 5));
final JTextField nameField = new JTextField(name);
JPanel centerWrapper = new JPanel();
centerWrapper.setLayout(new BoxLayout(centerWrapper, BoxLayout.PAGE_AXIS));
JPanel scriptWrapper = new JPanel(new GridLayout(0, 1, 2, 2));
scriptWrapper.setBorder(BorderFactory.createTitledBorder("Script"));
JPanel scriptSelector = new JPanel(new BorderLayout());
final JTextField scriptPath = new JTextField(command);
JButton browse = new JButton(new AbstractAction(Lang.get("browse")) {
@Override
public void actionPerformed(ActionEvent e) {
scriptPath.setText(SwingHelper.openDialog(owner, null).getAbsolutePath());
}
});
JLabel l = new JLabel("File: ");
l.setPreferredSize(new Dimension(50, 30));
scriptSelector.add(l, BorderLayout.WEST);
scriptSelector.add(scriptPath, BorderLayout.CENTER);
scriptSelector.add(browse, BorderLayout.EAST);
JPanel argsWrapper = new JPanel(new BorderLayout());
final JTextField argsField = new JTextField();
l = new JLabel("Args: ");
l.setPreferredSize(new Dimension(50, 30));
argsWrapper.add(l, BorderLayout.WEST);
argsWrapper.add(argsField, BorderLayout.CENTER);
scriptWrapper.add(scriptSelector);
scriptWrapper.add(argsWrapper);
JTextPane legend = new JTextPane();
legend.setFocusable(false);
legend.setText(createLegend());
JScrollPane sp = new JScrollPane(legend, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
sp.setPreferredSize(new Dimension(460, 200));
sp.setBorder(BorderFactory.createTitledBorder("Args-"+Lang.getNoHTML("preferences.legend")));
centerWrapper.add(scriptWrapper);
centerWrapper.add(sp);
centerWrapper.add(filter.settingsPanel());
JPanel bottomRow = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 5));
JButton cancel = new JButton(Lang.get("action.cancel"), Icons.get("cancel"));
cancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
d.dispose();
}
});
JButton ok = new JButton(Lang.get("action.ok"), Icons.get("ok"));
ok.setPreferredSize(cancel.getPreferredSize());
ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
command = scriptPath.getText();
args = argsField.getText();
name = nameField.getText();
d.dispose();
}
});
if (!isSetup)
bottomRow.add(cancel);
bottomRow.add(ok);
d.add(nameField, BorderLayout.NORTH);
d.add(centerWrapper, BorderLayout.CENTER);
d.add(bottomRow, BorderLayout.SOUTH);
d.pack();
SwingHelper.centerTo(owner, d);
d.setVisible(true);
}
private String createLegend() {
StringBuffer sb = new StringBuffer();
sb.append("%e = ").append(Lang.getNoHTML("event.type")).append(" [STATUS_CHANGED | INTERFACE_STATUS_CHANGED]\n");
sb.append(Lang.getNoHTML("map")).append(":\n");
sb.append(" %mn = ").append(Lang.getNoHTML("map.name")).append("\n");
sb.append(" %mp = ").append(Lang.getNoHTML("menu.file")).append("\n");
sb.append(Lang.getNoHTML("device")).append(":").append("\n");
sb.append(" %ds = ").append(Lang.getNoHTML("message.status")).append("\n");
sb.append(" %dn = ").append(Lang.getNoHTML("device.name")).append("\n");
sb.append(" %dl = ").append(Lang.getNoHTML("device.location")).append("\n");
sb.append(" %di = ").append("Icon").append("\n");
sb.append(Lang.getNoHTML("interface")).append(": (if INTERFACE_STATUS_UP)\n");
sb.append(" %is = ").append(Lang.getNoHTML("message.status")).append("\n");
sb.append(" %in = ").append(Lang.getNoHTML("interface.name")).append("\n");
sb.append(" %ia = ").append(Lang.getNoHTML("interface.address")).append("\n");
sb.append(" %iu = ").append(Lang.getNoHTML("interface.subnet")).append("\n");
sb.append(" %ig = ").append(Lang.getNoHTML("interface.gateway")).append("\n");
sb.append(" %im = ").append(Lang.getNoHTML("interface.mac"));
return sb.toString();
}
@Override
public String getName() {
return name;
}
@Override
public String getPluginName() {
return "Script Notifier";
}
@Override
public String getAuthor() {
return "rakudave";
}
@Override
public String getDescription() {
return "Calls a script when a device/interface changes its status";
}
@Override
public Icon getIcon() {
return Icons.get("properties");
}
@Override
public boolean hasSettings() {
return false;
}
@Override
public PreferencePanel getSettingsPanel() {
return null;
}
@Override
public Notifier create() {
return new ScriptNotifier();
}
}