Macross expansion refactoring
authorzev <zev@netxms.org>
Fri, 20 Oct 2017 16:29:41 +0000 (19:29 +0300)
committerzev <zev@netxms.org>
Fri, 20 Oct 2017 16:43:27 +0000 (19:43 +0300)
42 files changed:
include/netxms-version.h
include/nms_cscp.h
src/client/java/netxms-client/src/main/java/org/netxms/client/NXCSession.java
src/client/java/netxms-client/src/main/java/org/netxms/client/ProtocolVersion.java
src/client/java/netxms-client/src/main/java/org/netxms/client/objecttools/ObjectContextBase.java [new file with mode: 0644]
src/client/java/netxms-client/src/test/java/org/netxms/client/ExpansionTest.java [new file with mode: 0644]
src/client/java/netxms-client/src/test/java/org/netxms/client/TestConstants.java
src/java/netxms-eclipse/Core/src/org/netxms/ui/eclipse/objects/ObjectContext.java
src/java/netxms-eclipse/FileManager/src/org/netxms/ui/eclipse/filemanager/views/AgentFileViewer.java
src/java/netxms-eclipse/ObjectTools/src/org/netxms/ui/eclipse/objecttools/api/ObjectToolExecutor.java
src/java/netxms-eclipse/ObjectTools/src/org/netxms/ui/eclipse/objecttools/views/AgentActionResults.java
src/java/netxms-eclipse/ObjectTools/src/org/netxms/ui/eclipse/objecttools/views/ServerScriptResults.java
src/java/netxms-eclipse/PerfView/src/org/netxms/ui/eclipse/perfview/TemplateGraphDynamicMenu.java
src/java/netxms-eclipse/PerfView/src/org/netxms/ui/eclipse/perfview/api/GraphTemplateCache.java
src/libnetxms/nxcp.cpp
src/libnxjava/java/base/netxms-base/src/main/java/org/netxms/base/NXCPCodes.java
src/server/core/actions.cpp
src/server/core/alarm.cpp
src/server/core/epp.cpp
src/server/core/events.cpp
src/server/core/netobj.cpp
src/server/core/node.cpp
src/server/core/nxslext.cpp
src/server/core/objtools.cpp
src/server/core/sensor.cpp
src/server/core/session.cpp
src/server/include/nms_actions.h
src/server/include/nms_alarm.h
src/server/include/nms_core.h
src/server/include/nms_events.h
src/server/include/nms_objects.h
src/server/include/nxsrvapi.h
src/server/libnxsrv/agent.cpp
src/server/tools/nxaction/nxaction.cpp
src/smsdrv/nxagent/main.cpp
webui/webapp/Core/src/org/netxms/ui/eclipse/objects/ObjectContext.java
webui/webapp/FileManager/src/org/netxms/ui/eclipse/filemanager/views/AgentFileViewer.java
webui/webapp/ObjectTools/src/org/netxms/ui/eclipse/objecttools/api/ObjectToolExecutor.java
webui/webapp/ObjectTools/src/org/netxms/ui/eclipse/objecttools/views/AgentActionResults.java
webui/webapp/ObjectTools/src/org/netxms/ui/eclipse/objecttools/views/ServerScriptResults.java
webui/webapp/PerfView/src/org/netxms/ui/eclipse/perfview/TemplateGraphDynamicMenu.java
webui/webapp/PerfView/src/org/netxms/ui/eclipse/perfview/api/GraphTemplateCache.java

index 254c999..8ecca58 100644 (file)
@@ -43,7 +43,7 @@
 /**
  * Current client-server protocol versions
  */
-#define CLIENT_PROTOCOL_VERSION_BASE      50    /* Base protocol version - must match for all clients */
+#define CLIENT_PROTOCOL_VERSION_BASE      51    /* Base protocol version - must match for all clients */
 #define CLIENT_PROTOCOL_VERSION_ALARMS    3     /* Alarm management API */
 #define CLIENT_PROTOCOL_VERSION_PUSH      1     /* Data push API */
 #define CLIENT_PROTOCOL_VERSION_TRAP      1     /* Event (trap) sending API */
index 2bc72c0..dbe3549 100644 (file)
@@ -589,6 +589,8 @@ typedef struct
 #define CMD_RESTART                       0x0162
 #define CMD_REGISTER_LORAWAN_SENSOR       0x0163
 #define CMD_UNREGISTER_LORAWAN_SENSOR     0x0164
+#define CMD_EXPAND_MACROS                 0x0165
+#define CMD_EXECUTE_ACTION_WITH_EXPANSION 0x0166
 
 #define CMD_RS_LIST_REPORTS            0x1100
 #define CMD_RS_GET_REPORT              0x1101
@@ -1206,6 +1208,9 @@ typedef struct
 #define VID_DCI_NAME                ((UINT32)600)
 #define VID_STATE_FLAGS             ((UINT32)601)
 #define VID_CAPABILITIES            ((UINT32)602)
+#define VID_IN_FIELD_COUNT          ((UINT32)603)
+#define VID_STRING_COUNT            ((UINT32)604)
+#define VID_EXPAND_STRING           ((UINT32)605)
 
 // Base variabe for single threshold in message
 #define VID_THRESHOLD_BASE          ((UINT32)0x00800000)
@@ -1404,6 +1409,11 @@ typedef struct
 
 #define VID_CALLBACK_BASE           ((UINT32)0x10000000)
 
+//base value for macro expansion
+#define VID_EXP_STRING_BASE         ((UINT32)0x10000000)
+#define VID_IN_FIELD_BASE           ((UINT32)0x20000000)
+
+
 #define VID_ZMQ_SUBSCRIPTION_BASE   ((UINT32)0x10000000)
 
 #ifdef __cplusplus
index f0abb4a..7598789 100644 (file)
@@ -153,6 +153,7 @@ import org.netxms.client.objects.TemplateRoot;
 import org.netxms.client.objects.UnknownObject;
 import org.netxms.client.objects.VPNConnector;
 import org.netxms.client.objects.Zone;
+import org.netxms.client.objecttools.ObjectContextBase;
 import org.netxms.client.objecttools.ObjectTool;
 import org.netxms.client.objecttools.ObjectToolDetails;
 import org.netxms.client.packages.PackageDeploymentListener;
@@ -5413,6 +5414,108 @@ public class NXCSession
     * Execute action on remote agent
     *
     * @param nodeId Node object ID
+    * @param alarmId Alarm ID (used for macross expansion)
+    * @param action Action with all argunets, that will be expanded and splited on server side
+    * @param inputValues Input values provided by user for expansion
+    * @return Expanded action 
+    * @throws IOException  if socket I/O error occurs
+    * @throws NXCException if NetXMS server returns an error or operation was timed out
+    */
+   public String executeActionWithExpansion(long nodeId, long alarmId, String action, final Map<String, String> inputValues) throws IOException, NXCException
+   {
+      return executeActionWithExpansion(nodeId, alarmId, action, false, inputValues, null, null);
+   }
+
+   /**
+    * Execute action on remote agent
+    *
+    * @param nodeId Node object ID
+    * @param alarmId Alarm ID (used for macross expansion)
+    * @param action Action with all argunets, that will be expanded and splited on server side
+    * @param inputValues Input values provided by user for expansion
+    * @param receiveOutput true if action's output has to be read
+    * @param listener listener for action's output or null
+    * @param writer writer for action's output or null
+    * @return Expanded action 
+    * @throws IOException  if socket I/O error occurs
+    * @throws NXCException if NetXMS server returns an error or operation was timed out
+    */
+   public String executeActionWithExpansion(long nodeId, long alarmId, String action, boolean receiveOutput, final Map<String, String> inputValues, final TextOutputListener listener, final Writer writer) throws IOException, NXCException
+   {
+      NXCPMessage msg = newMessage(NXCPCodes.CMD_EXECUTE_ACTION);
+      msg.setFieldInt32(NXCPCodes.VID_OBJECT_ID, (int) nodeId);
+      msg.setField(NXCPCodes.VID_EXPAND_STRING,  true);
+      msg.setField(NXCPCodes.VID_ACTION_NAME,  action);
+      msg.setField(NXCPCodes.VID_RECEIVE_OUTPUT, receiveOutput);
+      msg.setFieldInt32(NXCPCodes.VID_ALARM_ID, (int) alarmId);
+      
+
+      if(inputValues != null)
+      {
+         msg.setFieldInt32(NXCPCodes.VID_IN_FIELD_COUNT, inputValues.size());
+         long varId = NXCPCodes.VID_IN_FIELD_BASE;
+         for(Entry<String, String> e : inputValues.entrySet())
+         {
+            msg.setField(varId++, e.getKey());
+            msg.setField(varId++, e.getValue());
+         }
+      }
+      else
+         msg.setFieldInt16(NXCPCodes.VID_IN_FIELD_COUNT, 0);
+      
+      MessageHandler handler = receiveOutput ? new MessageHandler() {
+         @Override
+         public boolean processMessage(NXCPMessage m)
+         {
+            String text = m.getFieldAsString(NXCPCodes.VID_MESSAGE);
+            if (text != null)
+            {
+               if (listener != null)
+                  listener.messageReceived(text);
+               if (writer != null)
+               {
+                  try
+                  {
+                     writer.write(text);
+                  }
+                  catch(IOException e)
+                  {
+                  }
+               }
+            }
+            if (m.isEndOfSequence())
+               setComplete();
+            return true;
+         }
+      } : null;
+      if (receiveOutput)
+         addMessageSubscription(NXCPCodes.CMD_COMMAND_OUTPUT, msg.getMessageId(), handler);
+      
+      sendMessage(msg);
+      NXCPMessage result = waitForRCC(msg.getMessageId());
+
+      if (receiveOutput)
+      {
+         synchronized(handler)
+         {
+            try
+            {
+               handler.wait();
+            }
+            catch(InterruptedException e)
+            {
+            }
+         }
+         if (handler.isTimeout())
+            throw new NXCException(RCC.TIMEOUT);
+      }    
+      return result.getFieldAsString(NXCPCodes.VID_ACTION_NAME);
+   }
+
+   /**
+    * Execute action on remote agent
+    *
+    * @param nodeId Node object ID
     * @param action Action name
     * @param args Action arguments
     * @throws IOException  if socket I/O error occurs
@@ -5930,9 +6033,27 @@ public class NXCSession
     */
    public void executeLibraryScript(long nodeId, String script, Map<String, String> inputFields, final TextOutputListener listener) throws IOException, NXCException
    {
+      executeLibraryScript(nodeId, 0, script, inputFields, listener);
+   }
+   
+   /**
+    * Execute library script on object. Script name interpreted as command line with server-side macro substitution. Map inputValues
+    * can be used to pass data for %() macros.
+    * 
+    * @param nodeId node ID to execute script on
+    * @param alarmId alarm ID to use for expansion
+    * @param script script name and parameters
+    * @param inputFields input values map for %() macro substitution (can be null)
+    * @param listener script output listener
+    * @throws IOException if socket I/O error occurs
+    * @throws NXCException if NetXMS server returns an error or operation was timed out
+    */
+   public void executeLibraryScript(long nodeId, long alarmId, String script, Map<String, String> inputFields, final TextOutputListener listener) throws IOException, NXCException
+   {
       NXCPMessage msg = newMessage(NXCPCodes.CMD_EXECUTE_LIBRARY_SCRIPT);
       msg.setFieldInt32(NXCPCodes.VID_OBJECT_ID, (int)nodeId);
       msg.setField(NXCPCodes.VID_SCRIPT, script);
+      msg.setFieldInt32(NXCPCodes.VID_ALARM_ID, (int)alarmId);
       msg.setField(NXCPCodes.VID_RECEIVE_OUTPUT, listener != null);
       if (inputFields != null)
       {
@@ -8080,15 +8201,46 @@ public class NXCSession
     */
    public AgentFileData downloadFileFromAgent(long nodeId, String remoteFileName, long maxFileSize, boolean follow, ProgressListener listener) throws IOException, NXCException
    {
+      return downloadFileFromAgent(nodeId, remoteFileName, maxFileSize, follow, null, 0, listener);
+   }
+   
+   /**
+    * Download file from remote host via agent.
+    *
+    * @param nodeId node object ID
+    * @param remoteFileName fully qualified file name on remote system
+    * @param maxFileSize maximum download size, 0 == UNLIMITED
+    * @param follow if set to true, server will send file updates as they appear (like for tail -f command)
+    * @param inputValues input values map for %() macro substitution (can be null)
+    * @param alarmId alarm ID used for macro expansion
+    * @param listener The ProgressListener to set
+    * @return agent file handle which contains server assigned ID and handle for local file
+    * @throws IOException  if socket or file I/O error occurs
+    * @throws NXCException if NetXMS server returns an error or operation was timed out
+    */
+   public AgentFileData downloadFileFromAgent(long nodeId, String remoteFileName, long maxFileSize, boolean follow, Map<String, String> inputValues, long alarmId, ProgressListener listener) throws IOException, NXCException
+   {
       final NXCPMessage msg = newMessage(NXCPCodes.CMD_GET_AGENT_FILE);
       msg.setFieldInt32(NXCPCodes.VID_OBJECT_ID, (int) nodeId);
       msg.setField(NXCPCodes.VID_FILE_NAME, remoteFileName);
       msg.setFieldInt32(NXCPCodes.VID_FILE_SIZE_LIMIT, (int)maxFileSize);
       msg.setFieldInt16(NXCPCodes.VID_FILE_FOLLOW, follow ? 1 : 0);
+      msg.setFieldInt32(NXCPCodes.VID_ALARM_ID, (int)alarmId);
+      if(inputValues != null)
+      {
+         msg.setFieldInt32(NXCPCodes.VID_IN_FIELD_COUNT, inputValues.size());
+         long varId = NXCPCodes.VID_IN_FIELD_BASE;
+         for(Entry<String, String> e : inputValues.entrySet())
+         {
+            msg.setField(varId++, e.getKey());
+            msg.setField(varId++, e.getValue());
+         }
+      }
       sendMessage(msg);
 
       final NXCPMessage response = waitForRCC(msg.getMessageId()); // first confirmation - server job started
       final String id = response.getFieldAsString(NXCPCodes.VID_NAME);
+      remoteFileName = response.getFieldAsString(NXCPCodes.VID_FILE_NAME);
       if (listener != null)
       {
          final long fileSize = waitForRCC(msg.getMessageId()).getFieldAsInt64(NXCPCodes.VID_FILE_SIZE);
@@ -9861,4 +10013,90 @@ public class NXCSession
       sendMessage(msg);
       waitForRCC(msg.getMessageId());
    }
+
+   /**
+    * Substitute macross in may strings and one context
+    * 
+    * @param context expansion context alarm and node
+    * @param textsToExpand texts to be expanded
+    * @param inputValues input values provided by used used for %() expansion
+    * @return same count and order of strings already expanded
+    * @throws IOException if socket I/O error occurs
+    * @throws NXCException if NetXMS server returns an error or operation was timed out
+    */
+   public List<String> substitureMacross(ObjectContextBase context, List<String> textsToExpand, Map<String, String> inputValues) throws IOException, NXCException
+   {
+      final NXCPMessage msg = newMessage(NXCPCodes.CMD_EXPAND_MACROS);
+      long varId;
+      if(inputValues != null)
+      {
+         msg.setFieldInt32(NXCPCodes.VID_IN_FIELD_COUNT, inputValues.size());
+         varId = NXCPCodes.VID_IN_FIELD_BASE;
+         for(Entry<String, String> e : inputValues.entrySet())
+         {
+            msg.setField(varId++, e.getKey());
+            msg.setField(varId++, e.getValue());
+         }
+      }
+      msg.setFieldInt32(NXCPCodes.VID_STRING_COUNT, textsToExpand.size());
+      varId = NXCPCodes.VID_EXP_STRING_BASE;
+      for(String s : textsToExpand)
+      {
+         context.fillMessage(msg, varId, s);
+         varId+=5;
+      }
+
+      List<String> result = new ArrayList<String>();
+      sendMessage(msg);
+      NXCPMessage response = waitForRCC(msg.getMessageId());    
+      varId = NXCPCodes.VID_EXP_STRING_BASE;
+      for(int i = 0; i < textsToExpand.size(); i++)
+      {
+         result.add(response.getFieldAsString(varId++));
+      }    
+      return result;
+   }
+   
+   /**
+    * Substitute macross in many constexts for one string
+    * 
+    * @param context expansion contexts alarm and node
+    * @param textToExpand text to be expanded
+    * @param inputValues input values provided by used used for %() expansion
+    * @return same count and order of strings already expanded
+    * @throws IOException if socket I/O error occurs
+    * @throws NXCException if NetXMS server returns an error or operation was timed out
+    */
+   public List<String> substitureMacross(ObjectContextBase context[], String textToExpand, Map<String, String> inputValues) throws IOException, NXCException
+   {
+      final NXCPMessage msg = newMessage(NXCPCodes.CMD_EXPAND_MACROS);
+      long varId;
+      if(inputValues != null)
+      {
+         msg.setFieldInt32(NXCPCodes.VID_IN_FIELD_COUNT, inputValues.size());
+         varId = NXCPCodes.VID_IN_FIELD_BASE;
+         for(Entry<String, String> e : inputValues.entrySet())
+         {
+            msg.setField(varId++, e.getKey());
+            msg.setField(varId++, e.getValue());
+         }
+      }
+      msg.setFieldInt32(NXCPCodes.VID_STRING_COUNT, context.length);
+      varId = NXCPCodes.VID_EXP_STRING_BASE;
+      for(ObjectContextBase c : context)
+      {
+         c.fillMessage(msg, varId, textToExpand);
+         varId+=5;
+      }
+
+      List<String> result = new ArrayList<String>();
+      sendMessage(msg);
+      NXCPMessage response = waitForRCC(msg.getMessageId());    
+      varId = NXCPCodes.VID_EXP_STRING_BASE;
+      for(int i = 0; i < context.length; i++)
+      {
+         result.add(response.getFieldAsString(varId++));
+      }    
+      return result;
+   }
 }
index 4f2847a..dc793c7 100644 (file)
@@ -28,7 +28,7 @@ import org.netxms.base.NXCPMessage;
 public final class ProtocolVersion
 {
    // Versions
-   public static final int BASE = 50;
+   public static final int BASE = 51;
    public static final int ALARMS = 3;
    public static final int PUSH = 1;
    public static final int TRAP = 1;
diff --git a/src/client/java/netxms-client/src/main/java/org/netxms/client/objecttools/ObjectContextBase.java b/src/client/java/netxms-client/src/main/java/org/netxms/client/objecttools/ObjectContextBase.java
new file mode 100644 (file)
index 0000000..c9225be
--- /dev/null
@@ -0,0 +1,58 @@
+package org.netxms.client.objecttools;
+
+import org.netxms.base.NXCPMessage;
+import org.netxms.client.events.Alarm;
+import org.netxms.client.objects.AbstractNode;
+
+/**
+ * Class to hold information about selected node
+ */
+public class ObjectContextBase
+{
+   public AbstractNode object;
+   public Alarm alarm;
+   
+   public ObjectContextBase(AbstractNode object, Alarm alarm)
+   {
+      this.object = object;
+      this.alarm = alarm;
+   }
+
+   /* (non-Javadoc)
+    * @see java.lang.Object#hashCode()
+    */
+   @Override
+   public int hashCode()
+   {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((alarm == null) ? 0 : alarm.hashCode());
+      result = prime * result + ((object == null) ? 0 : object.hashCode());
+      return result;
+   }
+
+   /* (non-Javadoc)
+    * @see java.lang.Object#equals(java.lang.Object)
+    */
+   @Override
+   public boolean equals(Object obj)
+   {
+      if (this == obj)
+         return true;
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      ObjectContextBase other = (ObjectContextBase)obj;
+      if ((other.object == null) || (this.object == null))
+         return (other.object == null) && (this.object == null);
+      return other.object.getObjectId() == this.object.getObjectId();
+   }
+
+   public void fillMessage(NXCPMessage msg, long varId, String textToExpand)
+   {
+      msg.setField(varId++, textToExpand);
+      msg.setFieldInt32(varId++, object != null ? (int)object.getObjectId() : 0);
+      msg.setFieldInt32(varId++, alarm != null ? (int)alarm.getId() : 0);     
+   }
+}
\ No newline at end of file
diff --git a/src/client/java/netxms-client/src/test/java/org/netxms/client/ExpansionTest.java b/src/client/java/netxms-client/src/test/java/org/netxms/client/ExpansionTest.java
new file mode 100644 (file)
index 0000000..dd9c4b4
--- /dev/null
@@ -0,0 +1,38 @@
+package org.netxms.client;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.netxms.client.events.Alarm;
+import org.netxms.client.objects.AbstractNode;
+import org.netxms.client.objecttools.ObjectContextBase;
+
+public class ExpansionTest extends AbstractSessionTest
+{
+   public void testNodeAndAlarmExpanssion() throws Exception
+   {
+      final NXCSession session = connect();      
+      session.syncObjects();
+      
+      final AbstractNode object = (AbstractNode)session.findObjectById(TestConstants.NODE_ID);
+      final Map<Long, Alarm> alarms = session.getAlarms();
+      final Map<String, String> inputValues = new HashMap<String, String>();
+      final Alarm alarm = alarms.values().iterator().next();
+      inputValues.put("Key1", "Value1");
+      inputValues.put("Key2", "Value2");
+      final List<String> stringsToExpand = new ArrayList<String>();
+      
+      stringsToExpand.add("%%%a%A");
+      stringsToExpand.add("%g%I");
+      stringsToExpand.add("%K%n%U");
+      stringsToExpand.add("%(Key1)%(Key2)");
+      final List<String> expandedStrings = session.substitureMacross(new ObjectContextBase(object, alarm), stringsToExpand, inputValues);
+      
+      assertEquals("%" + object.getPrimaryIP().getHostAddress().toString()+alarm.getMessage(), expandedStrings.get(0));
+      assertEquals(object.getGuid().toString()+object.getObjectId(), expandedStrings.get(1));
+      assertEquals(alarm.getKey()+object.getObjectName()+session.getUserName(), expandedStrings.get(2));
+
+      session.disconnect();
+   }
+}
index 32b03c1..893bbbc 100644 (file)
@@ -28,7 +28,7 @@ public class TestConstants
    public static String loginName = "admin";
    public static String password = "";
    //Other constants
-   public static int NODE_ID = 142;
+   public static int NODE_ID = 2594;
    public static int LOCAL_NODE_ID = 142;
    public static long SUBNET_ID = 796;
    public static String FILE_NAME = "/var/adm/messages";
index faf3e88..a034765 100644 (file)
@@ -1,67 +1,22 @@
 package org.netxms.ui.eclipse.objects;
 
 import java.util.Map;
-import org.netxms.base.NXCommon;
 import org.netxms.client.events.Alarm;
 import org.netxms.client.objects.AbstractNode;
+import org.netxms.client.objecttools.ObjectContextBase;
 import org.netxms.ui.eclipse.console.Messages;
-import org.netxms.ui.eclipse.console.resources.StatusDisplayInfo;
-import org.netxms.ui.eclipse.shared.ConsoleSharedData;
 
 /**
  * Class to hold information about selected node
  */
-public class ObjectContext
-{
-   public AbstractNode object;
-   public Alarm alarm;
-   
+public class ObjectContext extends ObjectContextBase
+{   
    public ObjectContext(AbstractNode object, Alarm alarm)
    {
-      this.object = object;
-      this.alarm = alarm;
+      super(object, alarm);
    }
 
-   /* (non-Javadoc)
-    * @see java.lang.Object#hashCode()
-    */
-   @Override
-   public int hashCode()
-   {
-      final int prime = 31;
-      int result = 1;
-      result = prime * result + ((alarm == null) ? 0 : alarm.hashCode());
-      result = prime * result + ((object == null) ? 0 : object.hashCode());
-      return result;
-   }
-
-   /* (non-Javadoc)
-    * @see java.lang.Object#equals(java.lang.Object)
-    */
-   @Override
-   public boolean equals(Object obj)
-   {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      ObjectContext other = (ObjectContext)obj;
-      if ((other.object == null) || (this.object == null))
-         return (other.object == null) && (this.object == null);
-      return other.object.getObjectId() == this.object.getObjectId();
-   }
-   
-   /**
-    * Substitute macros in string
-    * 
-    * @param s
-    * @param node
-    * @param inputValues 
-    * @return
-    */
-   public String substituteMacros(String s, Map<String, String> inputValues)
+   public String substituteMacrosForMultiNodes(String s, Map<String, String> inputValues)
    {
       StringBuilder sb = new StringBuilder();
       
@@ -77,57 +32,19 @@ public class ObjectContext
             switch(src[i])
             {
                case 'a':
-                  sb.append((object != null) ? object.getPrimaryIP().getHostAddress() : Messages.get().ObjectContext_MultipleNodes);
-                  break;
-               case 'A':   // alarm message
-                  if (alarm != null)
-                     sb.append(alarm.getMessage());
-                  break;
-               case 'c':
-                  if (alarm != null)
-                     sb.append(alarm.getSourceEventCode());
+                  sb.append(Messages.get().ObjectContext_MultipleNodes);
                   break;
                case 'g':
-                  sb.append((object != null) ? object.getGuid().toString() : Messages.get().ObjectContext_MultipleNodes);
+                  sb.append(Messages.get().ObjectContext_MultipleNodes);
                   break;
                case 'i':
-                  sb.append((object != null) ? String.format("0x%08X", object.getObjectId()) : Messages.get().ObjectContext_MultipleNodes); //$NON-NLS-1$
+                  sb.append(Messages.get().ObjectContext_MultipleNodes); 
                   break;
                case 'I':
-                  sb.append((object != null) ? Long.toString(object.getObjectId()) : Messages.get().ObjectContext_MultipleNodes);
-                  break;
-               case 'm':   // alarm message
-                  if (alarm != null)
-                     sb.append(alarm.getMessage());
+                  sb.append(Messages.get().ObjectContext_MultipleNodes);
                   break;
                case 'n':
-                  sb.append((object != null) ? object.getObjectName() : Messages.get().ObjectContext_MultipleNodes);
-                  break;
-               case 'N':
-                  if (alarm != null)
-                     sb.append(ConsoleSharedData.getSession().getEventName(alarm.getSourceEventCode()));
-                  break;
-               case 's':
-                  if (alarm != null)
-                     sb.append(alarm.getCurrentSeverity());
-                  break;
-               case 'S':
-                  if (alarm != null)
-                     sb.append(StatusDisplayInfo.getStatusText(alarm.getCurrentSeverity()));
-                  break;
-               case 'U':
-                  sb.append(ConsoleSharedData.getSession().getUserName());
-                  break;
-               case 'v':
-                  sb.append(NXCommon.VERSION);
-                  break;
-               case 'y':   // alarm state
-                  if (alarm != null)
-                     sb.append(alarm.getState());
-                  break;
-               case 'Y':   // alarm ID
-                  if (alarm != null)
-                     sb.append(alarm.getId());
+                  sb.append(Messages.get().ObjectContext_MultipleNodes);
                   break;
                case '%':
                   sb.append('%');
@@ -174,4 +91,14 @@ public class ObjectContext
       
       return sb.toString();
    }
+   
+   /**
+    * Returns alarm id or 0 if alarm is not set
+    * 
+    * @return Context alarm id or 0 if alarm is not set
+    */
+   public long getAlarmId()
+   {
+      return alarm != null ? alarm.getId() : 0;
+   }
 }
\ No newline at end of file
index 9e25a56..865e938 100644 (file)
@@ -71,6 +71,7 @@ public class AgentFileViewer extends ViewPart
    private Action actionCopy;
    private Action actionSelectAll;
    private Action actionFind;
+   private AbstractObject object;
        
        /* (non-Javadoc)
         * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
@@ -86,7 +87,7 @@ public class AgentFileViewer extends ViewPart
                        throw new PartInitException("Internal error"); //$NON-NLS-1$
                
                nodeId = Long.parseLong(parts[0]);
-               AbstractObject object = session.findObjectById(nodeId);
+               object = session.findObjectById(nodeId);
                if ((object == null) || (object.getObjectClass() != AbstractObject.OBJECT_NODE))
                        throw new PartInitException(Messages.get().FileViewer_InvalidObjectID);
                
@@ -350,6 +351,17 @@ public class AgentFileViewer extends ViewPart
           {
              view.viewer.startTracking(nodeId, file.getId(), file.getRemoteName());
           }
+          view.updatePartName(file.getRemoteName()); //$NON-NLS-1$
           return true;
        }
+
+       /**
+        * Updates part view name after expanded name received
+        * @param remoteName
+        */
+   private void updatePartName(String remoteName)
+   {
+      remoteFileName = remoteName;
+      setPartName(object.getObjectName() + ": " + remoteFileName); //$NON-NLS-1$      
+   }
 }
index 402382d..8189ab3 100644 (file)
@@ -59,7 +59,7 @@ import org.netxms.ui.eclipse.tools.MessageDialogHelper;
  * Executor for object tool
  */
 public final class ObjectToolExecutor
-{
+{   
    /**
     * Private constructor to forbid instantiation 
     */
@@ -134,39 +134,64 @@ public final class ObjectToolExecutor
       {
          inputValues = new HashMap<String, String>(0);
       }
-      
-      if ((tool.getFlags() & ObjectTool.ASK_CONFIRMATION) != 0)
-      {
-         String message = tool.getConfirmationText();
-         if (nodes.size() == 1)
-         {
-            ObjectContext node = nodes.iterator().next();
-            message = node.substituteMacros(message, new HashMap<String, String>(0));
-         }
-         else
-         {
-            message = new ObjectContext(null, null).substituteMacros(message, new HashMap<String, String>(0));
-         }
-         if (!MessageDialogHelper.openQuestion(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), 
-               Messages.get().ObjectToolsDynamicMenu_ConfirmExec, message))
-            return;
-      }
-      
-      // Check if password validation needed
-      boolean validationNeeded = false;
-      for(int i = 0; i < fields.length; i++)
-         if (fields[i].getOptions().validatePassword)
-         {
-            validationNeeded = true;
-            break;
-         }
-      
-      if (validationNeeded)
-      {
-         final NXCSession session = ConsoleSharedData.getSession();
-         new ConsoleJob(Messages.get().ObjectToolExecutor_JobName, null, Activator.PLUGIN_ID, null) {
-            @Override
-            protected void runInternal(IProgressMonitor monitor) throws Exception
+      final NXCSession session = ConsoleSharedData.getSession();
+      new ConsoleJob(Messages.get().ObjectToolExecutor_JobName, null, Activator.PLUGIN_ID, null) {
+         @Override
+         protected void runInternal(IProgressMonitor monitor) throws Exception
+         {      
+            List<String> expandedText = null;
+            
+            if ((tool.getFlags() & ObjectTool.ASK_CONFIRMATION) != 0)
+            {
+               String message = tool.getConfirmationText();
+               if (nodes.size() == 1)
+               {
+                  //Expand message and action for 1 node, otherwise expansion occurs after confirmation
+                  List<String> textToExpand = new ArrayList<String>();
+                  textToExpand.add(tool.getConfirmationText());
+                  if(tool.getToolType() == ObjectTool.TYPE_URL || tool.getToolType() == ObjectTool.TYPE_LOCAL_COMMAND)
+                  {
+                     textToExpand.add(tool.getData());
+                  }
+                  ObjectContext node = nodes.iterator().next();
+                  expandedText = session.substitureMacross(node, textToExpand, inputValues);
+                  
+                  message = expandedText.remove(0);                  
+               }
+               else
+               {
+                  ObjectContext node = nodes.iterator().next();
+                  message = node.substituteMacrosForMultiNodes(message, inputValues);
+               }
+               
+               ConfirmationRunnable runnable = new ConfirmationRunnable(message);
+               getDisplay().syncExec(runnable);
+               if(!runnable.isConfirmed())
+                  return;
+
+               if(tool.getToolType() == ObjectTool.TYPE_URL || tool.getToolType() == ObjectTool.TYPE_LOCAL_COMMAND)
+               {
+                  expandedText = session.substitureMacross(nodes.toArray(new ObjectContext[nodes.size()]), tool.getData(), inputValues);
+               }
+            }
+            else
+            {
+               if(tool.getToolType() == ObjectTool.TYPE_URL || tool.getToolType() == ObjectTool.TYPE_LOCAL_COMMAND)
+               {
+                  expandedText = session.substitureMacross(nodes.toArray(new ObjectContext[nodes.size()]), tool.getData(), inputValues);
+               }
+            }
+            
+            // Check if password validation needed
+            boolean validationNeeded = false;
+            for(int i = 0; i < fields.length; i++)
+               if (fields[i].getOptions().validatePassword)
+               {
+                  validationNeeded = true;
+                  break;
+               }
+            
+            if (validationNeeded)
             {
                for(int i = 0; i < fields.length; i++)
                {
@@ -188,29 +213,67 @@ public final class ObjectToolExecutor
                      }
                   }
                }
-               
-               runInUIThread(new Runnable() {
-                  @Override
-                  public void run()
-                  {
-                     for(ObjectContext n : nodes)
-                        executeOnNode(n, tool, inputValues);
-                  }
-               });
             }
             
+            int i = 0;
+            for(final ObjectContext n : nodes)
+            {
+               if(tool.getToolType() == ObjectTool.TYPE_URL || tool.getToolType() == ObjectTool.TYPE_LOCAL_COMMAND)
+               {
+                  final String tmp = expandedText.get(i++);
+                  getDisplay().syncExec(new Runnable() {
+                     
+                     @Override
+                     public void run()
+                     {
+                        executeOnNode(n, tool, inputValues, tmp);
+                     }
+                  });
+               }
+               else
+               {
+                  getDisplay().syncExec(new Runnable() {
+                     
+                     @Override
+                     public void run()
+                     {
+                        executeOnNode(n, tool, inputValues, null);
+                     }
+                  });                  
+               }
+            }
+         }
+         
+         @Override
+         protected String getErrorMessage()
+         {
+            return Messages.get().ObjectToolExecutor_PasswordValidationFailed;
+         }
+         
+         class ConfirmationRunnable implements Runnable
+         {
+            private boolean confirmed;
+            private String message;
+
+            public ConfirmationRunnable(String message)
+            {
+               this.message = message;
+            }
+
             @Override
-            protected String getErrorMessage()
+            public void run()
             {
-               return Messages.get().ObjectToolExecutor_PasswordValidationFailed;
+               confirmed = MessageDialogHelper.openQuestion(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), 
+                     Messages.get().ObjectToolsDynamicMenu_ConfirmExec, message);
             }
-         }.start();
-      }
-      else
-      {
-         for(ObjectContext n : nodes)
-            executeOnNode(n, tool, inputValues);
-      }
+            
+            boolean isConfirmed()
+            {
+               return confirmed;
+            }
+         }         
+         
+      }.start();
    }
    
    /**
@@ -234,7 +297,7 @@ public final class ObjectToolExecutor
     * @param tool
     * @param inputValues 
     */
-   private static void executeOnNode(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues)
+   private static void executeOnNode(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues, String expandedValue)
    {
       switch(tool.getToolType())
       {
@@ -248,7 +311,7 @@ public final class ObjectToolExecutor
             executeInternalTool(node, tool);
             break;
          case ObjectTool.TYPE_LOCAL_COMMAND:
-            executeLocalCommand(node, tool, inputValues);
+            executeLocalCommand(node, tool, inputValues, expandedValue);
             break;
          case ObjectTool.TYPE_SERVER_COMMAND:
             executeServerCommand(node, tool, inputValues);
@@ -261,7 +324,7 @@ public final class ObjectToolExecutor
             executeTableTool(node, tool);
             break;
          case ObjectTool.TYPE_URL:
-            openURL(node, tool, inputValues);
+            openURL(node, tool, inputValues, expandedValue);
             break;
       }
    }
@@ -289,99 +352,13 @@ public final class ObjectToolExecutor
    }
    
    /**
-    * Split command line into tokens
-    *  
-    * @param input
-    * @return
-    */
-   private static String[] splitCommandLine(String input)
-   {
-      char[] in = input.toCharArray();
-      List<String> args = new ArrayList<String>();
-      
-      StringBuilder sb = new StringBuilder();
-      int state = 0;
-      for(char c : in)
-      {
-         switch(state)
-         {
-            case 0: // normal
-               if (Character.isSpaceChar(c))
-               {
-                  args.add(sb.toString());
-                  sb = new StringBuilder();
-                  state = 3;
-               }
-               else if (c == '"')
-               {
-                  state = 1;
-               }
-               else if (c == '\'')
-               {
-                  state = 2;
-               }
-               else
-               {
-                  sb.append(c);
-               }
-               break;
-            case 1: // double quoted string
-               if (c == '"')
-               {
-                  state = 0;
-               }
-               else
-               {
-                  sb.append(c);
-               }
-               break;
-            case 2: // single quoted string
-               if (c == '\'')
-               {
-                  state = 0;
-               }
-               else
-               {
-                  sb.append(c);
-               }
-               break;
-            case 3: // skip
-               if (!Character.isSpaceChar(c))
-               {
-                  if (c == '"')
-                  {
-                     state = 1;
-                  }
-                  else if (c == '\'')
-                  {
-                     state = 2;
-                  }
-                  else
-                  {
-                     sb.append(c);
-                     state = 0;
-                  }
-               }
-               break;
-         }
-      }
-      if (state != 3)
-         args.add(sb.toString());
-      
-      return args.toArray(new String[args.size()]);
-   }
-
-   /**
     * @param node
     * @param tool
     * @param inputValues 
     */
-   private static void executeAgentAction(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues)
+   private static void executeAgentAction(final ObjectContext node, final ObjectTool tool, final Map<String, String> inputValues)
    {
       final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
-      String[] parts = splitCommandLine(node.substituteMacros(tool.getData(), inputValues));
-      final String action = parts[0];
-      final String[] args = Arrays.copyOfRange(parts, 1, parts.length);
       
       if ((tool.getFlags() & ObjectTool.GENERATES_OUTPUT) == 0)
       {      
@@ -395,7 +372,7 @@ public final class ObjectToolExecutor
             @Override
             protected void runInternal(IProgressMonitor monitor) throws Exception
             {
-               session.executeAction(node.object.getObjectId(), action, args);
+               final String action = session.executeActionWithExpansion(node.object.getObjectId(), node.getAlarmId(), tool.getData(), inputValues);
                runInUIThread(new Runnable() {
                   @Override
                   public void run()
@@ -413,7 +390,7 @@ public final class ObjectToolExecutor
          try
          {
             AgentActionResults view = (AgentActionResults)window.getActivePage().showView(AgentActionResults.ID, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
-            view.executeAction(action, args);
+            view.executeAction(tool.getData(), node.getAlarmId(), inputValues);
          }
          catch(Exception e)
          {
@@ -487,7 +464,7 @@ public final class ObjectToolExecutor
             @Override
             protected void runInternal(IProgressMonitor monitor) throws Exception
             {
-               session.executeLibraryScript(node.object.getObjectId(), tool.getData(), inputValues, null);
+               session.executeLibraryScript(node.object.getObjectId(), node.getAlarmId(), tool.getData(), inputValues, null);
                runInUIThread(new Runnable() {
                   @Override
                   public void run()
@@ -511,7 +488,7 @@ public final class ObjectToolExecutor
          try
          {
             ServerScriptResults view = (ServerScriptResults)window.getActivePage().showView(ServerScriptResults.ID, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
-            view.executeScript(tool.getData(), inputValues);
+            view.executeScript(tool.getData(), node.getAlarmId(), inputValues);
          }
          catch(Exception e)
          {
@@ -527,10 +504,8 @@ public final class ObjectToolExecutor
     * @param tool
     * @param inputValues 
     */
-   private static void executeLocalCommand(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues)
-   {
-      String command = node.substituteMacros(tool.getData(), inputValues);
-      
+   private static void executeLocalCommand(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues, String command)
+   {      
       if ((tool.getFlags() & ObjectTool.GENERATES_OUTPUT) == 0)
       {
          final String os = Platform.getOS();
@@ -574,12 +549,12 @@ public final class ObjectToolExecutor
     * @param tool
     * @param inputValues 
     */
-   private static void executeFileDownload(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues)
+   private static void executeFileDownload(final ObjectContext node, final ObjectTool tool, final Map<String, String> inputValues)
    {
       final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
       String[] parameters = tool.getData().split("\u007F"); //$NON-NLS-1$
       
-      final String fileName = node.substituteMacros(parameters[0], inputValues);
+      final String fileName = parameters[0];
       final int maxFileSize = Integer.parseInt(parameters[1]);
       final boolean follow = parameters[2].equals("true") ? true : false; //$NON-NLS-1$
       
@@ -593,7 +568,7 @@ public final class ObjectToolExecutor
          @Override
          protected void runInternal(final IProgressMonitor monitor) throws Exception
          {
-            final AgentFileData file = session.downloadFileFromAgent(node.object.getObjectId(), fileName, maxFileSize, follow, new ProgressListener() {
+            final AgentFileData file = session.downloadFileFromAgent(node.object.getObjectId(), fileName, maxFileSize, follow, inputValues, node.getAlarmId(), new ProgressListener() {
                @Override
                public void setTotalWorkAmount(long workTotal)
                {
@@ -649,9 +624,8 @@ public final class ObjectToolExecutor
     * @param tool
     * @param inputValues 
     */
-   private static void openURL(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues)
+   private static void openURL(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues, String url)
    {
-      final String url = node.substituteMacros(tool.getData(), inputValues);
       
       final String sid = Long.toString(node.object.getObjectId()) + "&" + Long.toString(tool.getId()); //$NON-NLS-1$
       final IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
index fc5ab5a..201e32b 100644 (file)
@@ -19,6 +19,7 @@
 package org.netxms.ui.eclipse.objecttools.views;
 
 import java.io.IOException;
+import java.util.Map;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IMenuManager;
@@ -41,9 +42,10 @@ public class AgentActionResults extends AbstractCommandResults implements TextOu
    public static final String ID = "org.netxms.ui.eclipse.objecttools.views.AgentActionResults"; //$NON-NLS-1$
 
    private IOConsoleOutputStream out;
-   private String lastAction = null;
-   private String[] lastArgs = null;
    private Action actionRestart;
+   private String executionString;
+   private long alarmId;
+   private Map<String, String> inputValues;
    
    /**
     * Create actions
@@ -56,7 +58,7 @@ public class AgentActionResults extends AbstractCommandResults implements TextOu
          @Override
          public void run()
          {
-            executeAction(lastAction, lastArgs);
+            executeAction(executionString, alarmId, inputValues);
          }
       };
       actionRestart.setEnabled(false);
@@ -101,13 +103,14 @@ public class AgentActionResults extends AbstractCommandResults implements TextOu
    /**
     * @param action
     */
-   public void executeAction(final String action, final String[] args)
+   public void executeAction(final String executionString, final long alarmId, final Map<String, String> inputValues)
    {
       actionRestart.setEnabled(false);
       final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
       out = console.newOutputStream();
-      lastAction = action;
-      lastArgs = args;
+      this.alarmId = alarmId;
+      this.executionString = executionString;
+      this.inputValues = inputValues;
       ConsoleJob job = new ConsoleJob(String.format(Messages.get().ObjectToolsDynamicMenu_ExecuteOnNode, session.getObjectName(nodeId)), null, Activator.PLUGIN_ID, null) {
          @Override
          protected String getErrorMessage()
@@ -120,7 +123,7 @@ public class AgentActionResults extends AbstractCommandResults implements TextOu
          {
             try
             {
-               session.executeAction(nodeId, action, args, true, AgentActionResults.this, null);
+               session.executeActionWithExpansion(nodeId, alarmId, executionString, true, inputValues, AgentActionResults.this, null);
                out.write(Messages.get().LocalCommandResults_Terminated);
             }
             finally
index cc71b5e..4f5e8a7 100644 (file)
@@ -45,6 +45,7 @@ public class ServerScriptResults extends AbstractCommandResults implements TextO
    private String lastScript = null;
    private Action actionRestart;
    private Map<String, String> lastInputValues = null;
+   private long alarmId;
    
    /**
     * Create actions
@@ -57,7 +58,7 @@ public class ServerScriptResults extends AbstractCommandResults implements TextO
          @Override
          public void run()
          {
-            executeScript(lastScript, lastInputValues);
+            executeScript(lastScript, alarmId, lastInputValues);
          }
       };
       actionRestart.setEnabled(false);
@@ -101,14 +102,16 @@ public class ServerScriptResults extends AbstractCommandResults implements TextO
    
    /**
     * @param script
+    * @param alarmId 
     * @param inputValues 
     */
-   public void executeScript(final String script, final Map<String, String> inputValues)
+   public void executeScript(final String script, final long alarmId, final Map<String, String> inputValues)
    {
       actionRestart.setEnabled(false);
       final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
       out = console.newOutputStream();
       lastScript = script;
+      this.alarmId = alarmId;
       lastInputValues = inputValues;
       ConsoleJob job = new ConsoleJob(String.format(Messages.get().ObjectToolsDynamicMenu_ExecuteOnNode, session.getObjectName(nodeId)), null, Activator.PLUGIN_ID, null) {
          @Override
@@ -122,7 +125,7 @@ public class ServerScriptResults extends AbstractCommandResults implements TextO
          {
             try
             {
-               session.executeLibraryScript(nodeId, script, inputValues, ServerScriptResults.this);
+               session.executeLibraryScript(nodeId, alarmId, script, inputValues, ServerScriptResults.this);
             }
             finally
             {
index 1f26d35..041c43d 100644 (file)
@@ -109,6 +109,7 @@ public class TemplateGraphDynamicMenu extends ContributionItem implements IWorkb
                public void widgetSelected(SelectionEvent e)
                {
                   final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
+                  final GraphSettings gs = (GraphSettings)item.getData();
                   ConsoleJob job = new ConsoleJob("Get last values of " + node.getObjectName() , null, Activator.PLUGIN_ID, null) {
                      @Override
                      protected String getErrorMessage()
@@ -121,13 +122,7 @@ public class TemplateGraphDynamicMenu extends ContributionItem implements IWorkb
                      {
                         
                         final DciValue[] data = session.getLastValues(node.getObjectId());
-                        runInUIThread(new Runnable() {
-                           @Override
-                           public void run()
-                           {
-                              GraphTemplateCache.execute(node, (GraphSettings)item.getData(), data);
-                           }
-                        });
+                        GraphTemplateCache.execute(node, gs, data, getDisplay());
                      }
                   };
                   job.setUser(false);
index f36f31f..8533698 100644 (file)
@@ -1,16 +1,20 @@
 package org.netxms.ui.eclipse.perfview.api;
 
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.regex.Pattern;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.PlatformUI;
+import org.netxms.client.NXCException;
 import org.netxms.client.NXCSession;
 import org.netxms.client.SessionListener;
 import org.netxms.client.SessionNotification;
@@ -22,6 +26,7 @@ import org.netxms.ui.eclipse.objects.ObjectContext;
 import org.netxms.ui.eclipse.perfview.Activator;
 import org.netxms.ui.eclipse.perfview.Messages;
 import org.netxms.ui.eclipse.perfview.views.HistoricalGraphView;
+import org.netxms.ui.eclipse.shared.ConsoleSharedData;
 import org.netxms.ui.eclipse.tools.MessageDialogHelper;
 
 public class GraphTemplateCache
@@ -149,14 +154,16 @@ public class GraphTemplateCache
       return graphs;
    }
 
-   public static void execute(final AbstractNode node, final GraphSettings data, final DciValue[] values)
+   public static void execute(final AbstractNode node, final GraphSettings data, final DciValue[] values, final Display disp) throws IOException, NXCException
    {
-      ObjectContext ctx = new ObjectContext(node, null);
-      String name = ctx.substituteMacros(data.getTitle(), null);
-      GraphSettings result = new GraphSettings(data, name);
+      final NXCSession session = ConsoleSharedData.getSession();
+      List<String> textsToExpand = new ArrayList<String>();
+      textsToExpand.add(data.getTitle());
+      String name = session.substitureMacross(new ObjectContext(node, null), textsToExpand, new HashMap<String, String>()).get(0); 
+      final GraphSettings result = new GraphSettings(data, name);
       
       ChartDciConfig[] conf = result.getDciList();
-      HashSet<ChartDciConfig> newList = new HashSet<ChartDciConfig>();
+      final HashSet<ChartDciConfig> newList = new HashSet<ChartDciConfig>();
       int foundByDescription = -1;
       int foundDCICount = 0;
       //parse config and compare name as regexp and then compare description
@@ -193,13 +200,27 @@ public class GraphTemplateCache
       }
       if(foundDCICount > 0)
       {
-         result.setDciList(newList.toArray(new ChartDciConfig[newList.size()]));
-         showPredefinedGraph(result, node.getObjectId());
+         disp.syncExec(new Runnable() {
+            
+            @Override
+            public void run()
+            {
+               result.setDciList(newList.toArray(new ChartDciConfig[newList.size()]));
+               showPredefinedGraph(result, node.getObjectId());               
+            }
+         });
       }
       else
       {
-         MessageDialogHelper.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), "Graph creation from template error", "None of template graphs DCI were found on a node.");
-      }
+         disp.syncExec(new Runnable() {
+            
+            @Override
+            public void run()
+            {  
+               MessageDialogHelper.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), "Graph creation from template error", "None of template graphs DCI were found on a node.");
+            }
+         });
+       }
    }
    
    /**
index 11b093b..5454f47 100644 (file)
@@ -392,10 +392,13 @@ TCHAR LIBNETXMS_EXPORTABLE *NXCPMessageCodeName(WORD code, TCHAR *pszBuffer)
       _T("CMD_CLOSE_CHANNEL"),
       _T("CMD_CREATE_OBJECT_ACCESS_SNAPSHOT"),
       _T("CMD_UNBIND_AGENT_TUNNEL"),
-      _T("CMD_RESTART")
+      _T("CMD_RESTART"),
+      _T("CMD_REGISTER_LORAWAN_SENSOR"),
+      _T("CMD_UNREGISTER_LORAWAN_SENSOR"),
+      _T("CMD_EXPAND_MACROS")
    };
 
-   if ((code >= CMD_LOGIN) && (code <= CMD_RESTART))
+   if ((code >= CMD_LOGIN) && (code <= CMD_EXPAND_MACROS))
    {
       _tcscpy(pszBuffer, pszMsgNames[code - CMD_LOGIN]);
    }
index 48f0d24..9c19e9a 100644 (file)
@@ -373,6 +373,7 @@ public class NXCPCodes
    public static final int CMD_CLOSE_CHANNEL = 0x015F;
    public static final int CMD_CREATE_OBJECT_ACCESS_SNAPSHOT = 0x0160;
    public static final int CMD_UNBIND_AGENT_TUNNEL = 0x0161;
+   public static final int CMD_EXPAND_MACROS = 0x0165;
 
        // CMD_RS_ - Reporting Server related codes
        public static final int CMD_RS_LIST_REPORTS = 0x1100;
@@ -989,6 +990,9 @@ public class NXCPCodes
    public static final long VID_DCI_NAME = 600;
    public static final long VID_STATE_FLAGS = 601;
    public static final long VID_CAPABILITIES = 602;
+   public static final long VID_IN_FIELD_COUNT = 603;
+   public static final long VID_STRING_COUNT = 604;
+   public static final long VID_EXPAND_STRING = 605;
 
        public static final long VID_ACL_USER_BASE = 0x00001000L;
        public static final long VID_ACL_USER_LAST = 0x00001FFFL;
@@ -1079,5 +1083,7 @@ public class NXCPCodes
    public static final long VID_LOC_LIST_BASE = 0x10000000L;
    public static final long VID_SCHEDULE_LIST_BASE = 0x10000000L;
    public static final long VID_CALLBACK_BASE= 0x10000000L;
+   public static final long VID_EXP_STRING_BASE= 0x10000000L;
+   public static final long VID_IN_FIELD_BASE= 0x20000000L;
    public static final long VID_ZMQ_SUBSCRIPTION_BASE= 0x10000000L;
 }
index 134b280..b9a4a3b 100644 (file)
@@ -217,8 +217,8 @@ static BOOL ExecuteRemoteAction(TCHAR *pszTarget, TCHAR *pszAction)
       }
    }
 
+   StringList list;
        pTmp = _tcsdup(pszAction);
-       pCmd[0] = pTmp;
        nLen = (int)_tcslen(pTmp);
        for(i = 0, nState = 0, nCount = 1; (i < nLen) && (nCount < 127); i++)
        {
@@ -230,7 +230,7 @@ static BOOL ExecuteRemoteAction(TCHAR *pszTarget, TCHAR *pszAction)
                                        pTmp[i] = 0;
                                        if (pTmp[i + 1] != 0)
                                        {
-                                               pCmd[nCount++] = pTmp + i + 1;
+                                          list.add(pTmp + i + 1);
                                        }
                                }
                                break;
@@ -250,9 +250,8 @@ static BOOL ExecuteRemoteAction(TCHAR *pszTarget, TCHAR *pszAction)
                                break;
                }
        }
-       pCmd[nCount] = NULL;
 
-   dwError = pConn->execAction(pCmd[0], nCount - 1, &pCmd[1]);
+   dwError = pConn->execAction(pTmp, list);
    pConn->disconnect();
    pConn->decRefCount();
    free(pTmp);
@@ -389,7 +388,7 @@ static BOOL ExecuteActionScript(const TCHAR *scriptName, Event *event)
 /**
  * Execute action on specific event
  */
-BOOL ExecuteAction(UINT32 dwActionId, Event *pEvent, const TCHAR *alarmMsg, const TCHAR *alarmKey)
+BOOL ExecuteAction(UINT32 dwActionId, Event *pEvent, const Alarm *alarm)
 {
    static const TCHAR *actionType[] = { _T("EXEC"), _T("REMOTE"), _T("SEND EMAIL"), _T("SEND SMS"), _T("FORWARD EVENT"), _T("NXSL SCRIPT"), _T("XMPP MESSAGE") };
 
@@ -413,10 +412,10 @@ BOOL ExecuteAction(UINT32 dwActionId, Event *pEvent, const TCHAR *alarmMsg, cons
 
          TCHAR *pszExpandedData, *pszExpandedSubject, *pszExpandedRcpt, *curr, *next;
 
-         pszExpandedData = pEvent->expandText(CHECK_NULL_EX(pAction->pszData), alarmMsg, alarmKey);
+         pszExpandedData = pEvent->expandText(CHECK_NULL_EX(pAction->pszData), alarm);
          StrStrip(pszExpandedData);
 
-         pszExpandedRcpt = pEvent->expandText(pAction->szRcptAddr, alarmMsg, alarmKey);
+         pszExpandedRcpt = pEvent->expandText(pAction->szRcptAddr, alarm);
          StrStrip(pszExpandedRcpt);
 
          switch(pAction->iType)
@@ -436,7 +435,7 @@ BOOL ExecuteAction(UINT32 dwActionId, Event *pEvent, const TCHAR *alarmMsg, cons
                if (pszExpandedRcpt[0] != 0)
                {
                   DbgPrintf(3, _T("*actions* Sending mail to %s: \"%s\""), pszExpandedRcpt, pszExpandedData);
-                  pszExpandedSubject = pEvent->expandText(pAction->szEmailSubject, alarmMsg, alarmKey);
+                  pszExpandedSubject = pEvent->expandText(pAction->szEmailSubject, alarm);
                                           curr = pszExpandedRcpt;
                                           do
                                           {
index 71694fa..909cdf3 100644 (file)
@@ -530,7 +530,7 @@ void Alarm::updateFromEvent(Event *event, int state, int severity, UINT32 timeou
 /**
  * Create new alarm
  */
-void NXCORE_EXPORTABLE CreateNewAlarm(TCHAR *message, TCHAR *key, int state, int severity, UINT32 timeout,
+UINT32 NXCORE_EXPORTABLE CreateNewAlarm(TCHAR *message, TCHAR *key, int state, int severity, UINT32 timeout,
                                                                                   UINT32 timeoutEvent, Event *event, UINT32 ackTimeout, IntegerArray<UINT32> *alarmCategoryList, bool openHelpdeskIssue)
 {
    UINT32 alarmId = 0;
@@ -618,6 +618,7 @@ void NXCORE_EXPORTABLE CreateNewAlarm(TCHAR *message, TCHAR *key, int state, int
 
        free(pszExpMsg);
    free(pszExpKey);
+   return alarmId;
 }
 
 /**
@@ -1703,20 +1704,7 @@ int F_FindAlarmById(int argc, NXSL_Value **argv, NXSL_Value **result, NXSL_VM *v
       return NXSL_ERR_NOT_INTEGER;
 
    UINT32 alarmId = argv[0]->getValueAsUInt32();
-   Alarm *alarm = NULL;
-
-   MutexLock(m_mutex);
-   for(int i = 0; i < m_alarmList->size(); i++)
-   {
-      Alarm *a = m_alarmList->get(i);
-      if (a->getAlarmId() == alarmId)
-      {
-         alarm = new Alarm(a, false);
-         break;
-      }
-   }
-   MutexUnlock(m_mutex);
-
+   Alarm *alarm = FindAlarmById(alarmId);
    *result = (alarm != NULL) ? new NXSL_Value(new NXSL_Object(&g_nxslAlarmClass, alarm)) : new NXSL_Value();
    return 0;
 }
@@ -1797,3 +1785,24 @@ void ShutdownAlarmManager()
    MutexDestroy(m_mutex);
    delete m_alarmList;
 }
+
+/**
+ * Get alarm by id
+ */
+Alarm NXCORE_EXPORTABLE *FindAlarmById(UINT32 alarmId)
+{
+   if(alarmId == 0)
+      return NULL;
+   Alarm *alarm = NULL;
+   MutexLock(m_mutex);
+   for(int i = 0; i < m_alarmList->size(); i++)
+   {
+      if (m_alarmList->get(i)->getAlarmId() == alarmId)
+      {
+         alarm = new Alarm(m_alarmList->get(i), false);
+         break;
+      }
+   }
+   MutexUnlock(m_mutex);
+   return alarm;
+}
index ba1b5bc..8384f50 100644 (file)
@@ -529,18 +529,16 @@ bool EPRule::processEvent(Event *pEvent)
                        DbgPrintf(6, _T("Event ") UINT64_FMT _T(" match EPP rule %d"), pEvent->getId(), (int)m_id);
 
          // Generate alarm if requested
+         UINT32 alarmId = 0;
          if (m_dwFlags & RF_GENERATE_ALARM)
-            generateAlarm(pEvent);
+            alarmId = generateAlarm(pEvent);
 
          // Event matched, perform actions
          if (m_dwNumActions > 0)
          {
-            TCHAR *alarmMessage = pEvent->expandText(m_szAlarmMessage);
-            TCHAR *alarmKey = pEvent->expandText(m_szAlarmKey);
+            Alarm *alarm = FindAlarmById(alarmId);
             for(UINT32 i = 0; i < m_dwNumActions; i++)
-               ExecuteAction(m_pdwActionList[i], pEvent, alarmMessage, alarmKey);
-            free(alarmMessage);
-            free(alarmKey);
+               ExecuteAction(m_pdwActionList[i], pEvent, alarm);
          }
 
                        // Update persistent storage if needed
@@ -563,23 +561,25 @@ bool EPRule::processEvent(Event *pEvent)
 /**
  * Generate alarm from event
  */
-void EPRule::generateAlarm(Event *pEvent)
+UINT32 EPRule::generateAlarm(Event *pEvent)
 {
+   UINT32 alarmId = 0;
    // Terminate alarms with key == our ack_key
        if ((m_iAlarmSeverity == SEVERITY_RESOLVE) || (m_iAlarmSeverity == SEVERITY_TERMINATE))
        {
                TCHAR *pszAckKey = pEvent->expandText(m_szAlarmKey);
                if (pszAckKey[0] != 0)
-                       ResolveAlarmByKey(pszAckKey, (m_dwFlags & RF_TERMINATE_BY_REGEXP) ? true : false, m_iAlarmSeverity == SEVERITY_TERMINATE, pEvent);
+                  ResolveAlarmByKey(pszAckKey, (m_dwFlags & RF_TERMINATE_BY_REGEXP) ? true : false, m_iAlarmSeverity == SEVERITY_TERMINATE, pEvent);
                free(pszAckKey);
        }
        else    // Generate new alarm
        {
-               CreateNewAlarm(m_szAlarmMessage, m_szAlarmKey, ALARM_STATE_OUTSTANDING,
+          alarmId = CreateNewAlarm(m_szAlarmMessage, m_szAlarmKey, ALARM_STATE_OUTSTANDING,
                      (m_iAlarmSeverity == SEVERITY_FROM_EVENT) ? pEvent->getSeverity() : m_iAlarmSeverity,
                      m_dwAlarmTimeout, m_dwAlarmTimeoutEvent, pEvent, 0, m_alarmCategoryList,
                      ((m_dwFlags & RF_CREATE_TICKET) != 0) ? true : false);
        }
+       return alarmId;
 }
 
 /**
index 3c0d3e4..b76f5d3 100644 (file)
@@ -232,7 +232,7 @@ EventGroup::EventGroup(DB_RESULT result, int row, IntegerArray<UINT32> *memberCa
  */
 EventGroup::~EventGroup()
 {
-   free(m_eventCodeList);
+   delete m_eventCodeList;
 }
 
 /**
@@ -575,395 +575,17 @@ void Event::expandMessageText()
 /**
  * Substitute % macros in given text with actual values
  */
-TCHAR *Event::expandText(const TCHAR *textTemplate, const TCHAR *alarmMsg, const TCHAR *alarmKey)
+TCHAR *Event::expandText(const TCHAR *textTemplate, const Alarm *alarm)
 {
-       return Event::expandText(this, m_sourceId, textTemplate, alarmMsg, alarmKey);
-}
-
-/**
- * Substitute % macros in given text with actual values
- */
-TCHAR *Event::expandText(Event *event, UINT32 sourceObject, const TCHAR *textTemplate, const TCHAR *alarmMsg, const TCHAR *alarmKey)
-{
-   const TCHAR *pCurr;
-   UINT32 dwPos, dwSize, dwParam;
-       UINT32 sourceId = (event != NULL) ? event->getSourceId() : sourceObject;
-   NetObj *pObject;
-   struct tm *lt;
-#if HAVE_LOCALTIME_R
-   struct tm tmbuffer;
-#endif
-   TCHAR *pText, szBuffer[4], scriptName[256];
-       int i;
-
-       DbgPrintf(8, _T("Event::expandText(event=%p sourceObject=%d template='%s' alarmMsg='%s' alarmKey='%s')"),
-                 event, (int)sourceObject, CHECK_NULL(textTemplate), CHECK_NULL(alarmMsg), CHECK_NULL(alarmKey));
-
-   pObject = FindObjectById(sourceId);
-   if (pObject == NULL)
-   {
-      pObject = FindObjectById(g_dwMgmtNode);
-               if (pObject == NULL)
-                       pObject = g_pEntireNet;
-   }
-   dwSize = (UINT32)_tcslen(textTemplate) + 1;
-   pText = (TCHAR *)malloc(dwSize * sizeof(TCHAR));
-   for(pCurr = textTemplate, dwPos = 0; *pCurr != 0; pCurr++)
+       NetObj *obj = FindObjectById(m_sourceId);
+   if (obj == NULL)
    {
-      switch(*pCurr)
-      {
-         case '%':   // Metacharacter
-            pCurr++;
-            if (*pCurr == 0)
-            {
-               pCurr--;
-               break;   // Abnormal loop termination
-            }
-            switch(*pCurr)
-            {
-               case '%':
-                  pText[dwPos++] = '%';
-                  break;
-               case 'a':   // IP address of event source
-                  dwSize += 64;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  GetObjectIpAddress(pObject).toString(&pText[dwPos]);
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'A':   // Associated alarm message
-                  if (alarmMsg != NULL)
-                  {
-                     dwSize += (UINT32)_tcslen(alarmMsg);
-                         pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                     _tcscpy(&pText[dwPos], alarmMsg);
-                     dwPos += (UINT32)_tcslen(alarmMsg);
-                  }
-                  break;
-               case 'c':   // Event code
-                  dwSize += 16;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                               _sntprintf(&pText[dwPos], 16, _T("%u"), (unsigned int)((event != NULL) ? event->m_code : 0));
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'g':   // Source object's GUID
-                  dwSize += 36;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  pObject->getGuid().toString(&pText[dwPos]);
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'i':   // Source object identifier in form 0xhhhhhhhh
-                  dwSize += 10;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  _sntprintf(&pText[dwPos], 11, _T("0x%08X"), sourceId);
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'I':   // Source object identifier in decimal form
-                  dwSize += 10;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  _sntprintf(&pText[dwPos], 11, _T("%u"), (unsigned int)sourceId);
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'K':   // Associated alarm key
-                  if (alarmKey != NULL)
-                  {
-                     dwSize += (UINT32)_tcslen(alarmKey);
-                         pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                     _tcscpy(&pText[dwPos], alarmKey);
-                     dwPos += (UINT32)_tcslen(alarmKey);
-                  }
-                  break;
-               case 'm':
-                  if ((event != NULL) && (event->m_messageText != NULL))
-                  {
-                     dwSize += (UINT32)_tcslen(event->m_messageText);
-                         pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                     _tcscpy(&pText[dwPos], event->m_messageText);
-                     dwPos += (UINT32)_tcslen(event->m_messageText);
-                  }
-                  break;
-               case 'M':       // Custom message (usually set by matching script in EPP)
-                  if ((event != NULL) && (event->m_customMessage != NULL))
-                  {
-                     dwSize += (UINT32)_tcslen(event->m_customMessage);
-                         pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                     _tcscpy(&pText[dwPos], event->m_customMessage);
-                     dwPos += (UINT32)_tcslen(event->m_customMessage);
-                  }
-                  break;
-               case 'n':   // Name of event source
-                  dwSize += (UINT32)_tcslen(pObject->getName());
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  _tcscpy(&pText[dwPos], pObject->getName());
-                  dwPos += (UINT32)_tcslen(pObject->getName());
-                  break;
-               case 'N':   // Event name
-                                               if (event != NULL)
-                                               {
-                                                       dwSize += (UINT32)_tcslen(event->m_name);
-                                                       pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                                       _tcscpy(&pText[dwPos], event->m_name);
-                                                       dwPos += (UINT32)_tcslen(event->m_name);
-                                               }
-                  break;
-               case 's':   // Severity code
-                                               if (event != NULL)
-                                               {
-                                                       dwSize += 3;
-                                                       pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                                       _sntprintf(&pText[dwPos], 4, _T("%d"), (int)event->m_severity);
-                                                       dwPos = (UINT32)_tcslen(pText);
-                                               }
-                  break;
-               case 'S':   // Severity text
-                                               if (event != NULL)
-                                               {
-                     const TCHAR *statusText = GetStatusAsText(event->m_severity, false);
-                                                       dwSize += (UINT32)_tcslen(statusText);
-                                                       pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                                       _tcscpy(&pText[dwPos], statusText);
-                                                       dwPos += (UINT32)_tcslen(statusText);
-                                               }
-                  break;
-               case 't':   // Event's timestamp
-                  dwSize += 32;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                               if (event != NULL)
-                                               {
-#if HAVE_LOCALTIME_R
-                     lt = localtime_r(&event->m_timeStamp, &tmbuffer);
-#else
-                                                       lt = localtime(&event->m_timeStamp);
-#endif
-                                               }
-                                               else
-                                               {
-                                                       time_t t = time(NULL);
-#if HAVE_LOCALTIME_R
-                     lt = localtime_r(&t, &tmbuffer);
-#else
-                                                       lt = localtime(&t);
-#endif
-                                               }
-                  _tcsftime(&pText[dwPos], 32, _T("%d-%b-%Y %H:%M:%S"), lt);
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'T':   // Event's timestamp as number of seconds since epoch
-                  dwSize += 16;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                               _sntprintf(&pText[dwPos], 16, _T("%lu"), (unsigned long)((event != NULL) ? event->m_timeStamp : time(NULL)));
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'u':       // User tag
-                  if ((event != NULL) && (event->m_userTag != NULL))
-                  {
-                     dwSize += (UINT32)_tcslen(event->m_userTag);
-                         pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                     _tcscpy(&pText[dwPos], event->m_userTag);
-                     dwPos += (UINT32)_tcslen(event->m_userTag);
-                  }
-                  break;
-               case 'v':   // NetXMS server version
-                  dwSize += (UINT32)_tcslen(NETXMS_VERSION_STRING);
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  _tcscpy(&pText[dwPos], NETXMS_VERSION_STRING);
-                  dwPos += (UINT32)_tcslen(NETXMS_VERSION_STRING);
-                  break;
-               case '0':
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case '5':
-               case '6':
-               case '7':
-               case '8':
-               case '9':
-                                               if (event != NULL)
-                                               {
-                                                       szBuffer[0] = *pCurr;
-                                                       if (isdigit(*(pCurr + 1)))
-                                                       {
-                                                               pCurr++;
-                                                               szBuffer[1] = *pCurr;
-                                                               szBuffer[2] = 0;
-                                                       }
-                                                       else
-                                                       {
-                                                               szBuffer[1] = 0;
-                                                       }
-                                                       dwParam = _tcstoul(szBuffer, NULL, 10);
-                                                       if ((dwParam > 0) && (dwParam <= (UINT32)event->m_parameters.size()))
-                                                       {
-                                                               const TCHAR *value = (TCHAR *)event->m_parameters.get(dwParam - 1);
-                                                               if (value == NULL)
-                                                                       value = _T("");
-                                                               dwSize += (UINT32)_tcslen(value);
-                                                               pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                                               _tcscpy(&pText[dwPos], value);
-                                                               dwPos += (UINT32)_tcslen(value);
-                                                       }
-                                               }
-                  break;
-                                       case '[':       // Script
-                                               for(i = 0, pCurr++; (*pCurr != ']') && (*pCurr != 0) && (i < 255); pCurr++)
-                                               {
-                                                       scriptName[i++] = *pCurr;
-                                               }
-                                               if (*pCurr == 0)        // no terminating ]
-                                               {
-                                                       pCurr--;
-                                               }
-                                               else
-                                               {
-                                                       scriptName[i] = 0;
-
-                                                       // Entry point can be given in form script/entry_point
-                                                       TCHAR *entryPoint = _tcschr(scriptName, _T('/'));
-                                                       if (entryPoint != NULL)
-                                                       {
-                                                               *entryPoint = 0;
-                                                               entryPoint++;
-                                                               StrStrip(entryPoint);
-                                                       }
-                                                       StrStrip(scriptName);
-
-                                                       NXSL_VM *vm = CreateServerScriptVM(scriptName);
-                                                       if (vm != NULL)
-                                                       {
-                                                               if (pObject->getObjectClass() == OBJECT_NODE)
-                                                                       vm->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, pObject)));
-                                                               vm->setGlobalVariable(_T("$event"), (event != NULL) ? new NXSL_Value(new NXSL_Object(&g_nxslEventClass, event)) : new NXSL_Value);
-                                                               if (alarmMsg != NULL)
-                                                                       vm->setGlobalVariable(_T("$alarmMessage"), new NXSL_Value(alarmMsg));
-                                                               if (alarmKey != NULL)
-                                                                       vm->setGlobalVariable(_T("$alarmKey"), new NXSL_Value(alarmKey));
-
-                                                               if (vm->run(0, NULL, NULL, NULL, NULL, entryPoint))
-                                                               {
-                                                                       NXSL_Value *result = vm->getResult();
-                                                                       if (result != NULL)
-                                                                       {
-                                                                               const TCHAR *temp = result->getValueAsCString();
-                                                                               if (temp != NULL)
-                                                                               {
-                                                                                       dwSize += (UINT32)_tcslen(temp);
-                                                                                       pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                                                                       _tcscpy(&pText[dwPos], temp);
-                                                                                       dwPos += (UINT32)_tcslen(temp);
-                                                                                       DbgPrintf(4, _T("Event::ExpandText(%d, \"%s\"): Script %s executed successfully"),
-                                                                                               (int)((event != NULL) ? event->m_code : 0), textTemplate, scriptName);
-                                                                               }
-                                                                       }
-                                                               }
-                                                               else
-                                                               {
-                                                                       DbgPrintf(4, _T("Event::ExpandText(%d, \"%s\"): Script %s execution error: %s"),
-                                                                                                (int)((event != NULL) ? event->m_code : 0), textTemplate, scriptName, vm->getErrorText());
-                                                                       PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", scriptName, vm->getErrorText(), 0);
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               DbgPrintf(4, _T("Event::ExpandText(%d, \"%s\"): Cannot find script %s"),
-                                                                       (int)((event != NULL) ? event->m_code : 0), textTemplate, scriptName);
-                                                       }
-                                               }
-                                               break;
-                                       case '{':       // Custom attribute
-                                               for(i = 0, pCurr++; (*pCurr != '}') && (*pCurr != 0) && (i < 255); pCurr++)
-                                               {
-                                                       scriptName[i++] = *pCurr;
-                                               }
-                                               if (*pCurr == 0)        // no terminating }
-                                               {
-                                                       pCurr--;
-                                               }
-                                               else
-                                               {
-                                                       scriptName[i] = 0;
-                                                       StrStrip(scriptName);
-                                                       TCHAR *temp = pObject->getCustomAttributeCopy(scriptName);
-                                                       if (temp != NULL)
-                                                       {
-                                                               dwSize += (UINT32)_tcslen(temp);
-                                                               pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                                               _tcscpy(&pText[dwPos], temp);
-                                                               dwPos += (UINT32)_tcslen(temp);
-                                                               free(temp);
-                                                       }
-                                               }
-                                               break;
-                                       case '(':       // input field - scan for closing ) for compatibility
-                                               for(i = 0, pCurr++; (*pCurr != ')') && (*pCurr != 0) && (i < 255); pCurr++)
-                                               {
-                                               }
-                                               if (*pCurr == 0)        // no terminating }
-                                               {
-                                                       pCurr--;
-                                               }
-                                               break;
-                                       case '<':       // Named parameter
-                                               if (event != NULL)
-                                               {
-                                                       for(i = 0, pCurr++; (*pCurr != '>') && (*pCurr != 0) && (i < 255); pCurr++)
-                                                       {
-                                                               scriptName[i++] = *pCurr;
-                                                       }
-                                                       if (*pCurr == 0)        // no terminating >
-                                                       {
-                                                               pCurr--;
-                                                       }
-                                                       else
-                                                       {
-                                                               scriptName[i] = 0;
-                                                               StrStrip(scriptName);
-                                                               int index = event->m_parameterNames.indexOfIgnoreCase(scriptName);
-                                                               if (index != -1)
-                                                               {
-                                                                       const TCHAR *temp = (TCHAR *)event->m_parameters.get(index);
-                                                                       if (temp != NULL)
-                                                                       {
-                                                                               dwSize += (UINT32)_tcslen(temp);
-                                                                               pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                                                               _tcscpy(&pText[dwPos], temp);
-                                                                               dwPos += (UINT32)_tcslen(temp);
-                                                                       }
-                                                               }
-                                                       }
-                                               }
-                                               break;
-               default:    // All other characters are invalid, ignore
-                  break;
-            }
-            break;
-         case '\\':  // Escape character
-            pCurr++;
-            if (*pCurr == 0)
-            {
-               pCurr--;
-               break;   // Abnormal loop termination
-            }
-            switch(*pCurr)
-            {
-               case 't':
-                  pText[dwPos++] = '\t';
-                  break;
-               case 'n':
-                  pText[dwPos++] = 0x0D;
-                  pText[dwPos++] = 0x0A;
-                  break;
-               default:
-                  pText[dwPos++] = *pCurr;
-                  break;
-            }
-            break;
-         default:
-            pText[dwPos++] = *pCurr;
-            break;
-      }
+     obj = FindObjectById(g_dwMgmtNode);
+     if (obj == NULL)
+        obj = g_pEntireNet;
    }
-   pText[dwPos] = 0;
-   return pText;
+
+       return obj->expandText(textTemplate, alarm, NULL, NULL, NULL);
 }
 
 /**
index 97a9178..b690d90 100644 (file)
@@ -2423,3 +2423,412 @@ json_t *NetObj::toJson()
 
    return root;
 }
+
+TCHAR *NetObj::expandText(const TCHAR *textTemplate, const Alarm *alarm, const Event *event, const TCHAR *userName, const StringMap *inputFields)
+{
+   const TCHAR *pCurr;
+   UINT32 dwPos, dwSize, dwParam;
+   struct tm *lt;
+#if HAVE_LOCALTIME_R
+   struct tm tmbuffer;
+#endif
+   TCHAR *pText, szBuffer[4], scriptName[256];
+   int i;
+
+   DbgPrintf(8, _T("NetObj::expandText(sourceObject=%d template='%s' alarm='%d' event='%ld')"),
+             m_id, CHECK_NULL(textTemplate), alarm == NULL ? -1 : alarm->getAlarmId() , event == NULL ? -1 : event->getId());
+
+
+   dwSize = (UINT32)_tcslen(textTemplate) + 1;
+   pText = (TCHAR *)malloc(dwSize * sizeof(TCHAR));
+   for(pCurr = textTemplate, dwPos = 0; *pCurr != 0; pCurr++)
+   {
+      switch(*pCurr)
+      {
+         case '%':   // Metacharacter
+            pCurr++;
+            if (*pCurr == 0)
+            {
+               pCurr--;
+               break;   // Abnormal loop termination
+            }
+            switch(*pCurr)
+            {
+               case '%':
+                  pText[dwPos++] = '%';
+                  break;
+               case 'a':   // IP address of event source
+                  dwSize += 64;
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  GetObjectIpAddress(this).toString(&pText[dwPos]);
+                  dwPos = (UINT32)_tcslen(pText);
+                  break;
+               case 'A':   // Associated alarm message
+                  if (alarm != NULL)
+                  {
+                     dwSize += (UINT32)_tcslen(alarm->getMessage());
+                     pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                     _tcscpy(&pText[dwPos], alarm->getMessage());
+                     dwPos += (UINT32)_tcslen(alarm->getMessage());
+                  }
+                  break;
+               case 'c':   // Event code
+                  dwSize += 16;
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  _sntprintf(&pText[dwPos], 16, _T("%u"), (unsigned int)((event != NULL) ? event->getCode() : 0));
+                  dwPos = (UINT32)_tcslen(pText);
+                  break;
+               case 'g':   // Source object's GUID
+                  dwSize += 36;
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  getGuid().toString(&pText[dwPos]);
+                  dwPos = (UINT32)_tcslen(pText);
+                  break;
+               case 'i':   // Source object identifier in form 0xhhhhhhhh
+                  dwSize += 10;
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  _sntprintf(&pText[dwPos], 11, _T("0x%08X"), m_id);
+                  dwPos = (UINT32)_tcslen(pText);
+                  break;
+               case 'I':   // Source object identifier in decimal form
+                  dwSize += 10;
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  _sntprintf(&pText[dwPos], 11, _T("%u"), (unsigned int)m_id);
+                  dwPos = (UINT32)_tcslen(pText);
+                  break;
+               case 'K':   // Associated alarm key
+                  if (alarm != NULL)
+                  {
+                     dwSize += (UINT32)_tcslen(alarm->getKey());
+                     pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                     _tcscpy(&pText[dwPos], alarm->getKey());
+                     dwPos += (UINT32)_tcslen(alarm->getKey());
+                  }
+                  break;
+               case 'm':
+                  if ((event != NULL) && (event->getMessage() != NULL))
+                  {
+                     dwSize += (UINT32)_tcslen(event->getMessage());
+                     pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                     _tcscpy(&pText[dwPos], event->getMessage());
+                     dwPos += (UINT32)_tcslen(event->getMessage());
+                  }
+                  break;
+               case 'M':   // Custom message (usually set by matching script in EPP)
+                  if ((event != NULL) && (event->getCustomMessage() != NULL))
+                  {
+                     dwSize += (UINT32)_tcslen(event->getCustomMessage());
+                     pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                     _tcscpy(&pText[dwPos], event->getCustomMessage());
+                     dwPos += (UINT32)_tcslen(event->getCustomMessage());
+                  }
+                  break;
+               case 'n':   // Name of event source
+                  dwSize += (UINT32)_tcslen(getName());
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  _tcscpy(&pText[dwPos], getName());
+                  dwPos += (UINT32)_tcslen(getName());
+                  break;
+               case 'N':   // Event name
+                  if (event != NULL)
+                  {
+                     dwSize += (UINT32)_tcslen(event->getName());
+                     pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                     _tcscpy(&pText[dwPos], event->getName());
+                     dwPos += (UINT32)_tcslen(event->getName());
+                  }
+                  break;
+               case 's':   // Severity code
+                  if (event != NULL)
+                  {
+                     dwSize += 3;
+                     pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                     _sntprintf(&pText[dwPos], 4, _T("%d"), (int)event->getSeverity());
+                     dwPos = (UINT32)_tcslen(pText);
+                  }
+                  break;
+               case 'S':   // Severity text
+                  if (event != NULL)
+                  {
+                     const TCHAR *statusText = GetStatusAsText(event->getSeverity(), false);
+                     dwSize += (UINT32)_tcslen(statusText);
+                     pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                     _tcscpy(&pText[dwPos], statusText);
+                     dwPos += (UINT32)_tcslen(statusText);
+                  }
+                  break;
+               case 't':   // Event's timestamp
+                  dwSize += 32;
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  time_t t;
+                  if(event != NULL)
+                  {
+                     t = event->getTimeStamp();
+                  }
+                  else
+                  {
+                     t = time(NULL);
+                  }
+#if HAVE_LOCALTIME_R
+                  lt = localtime_r(&t, &tmbuffer);
+#else
+                  lt = localtime(&t);
+#endif
+                  _tcsftime(&pText[dwPos], 32, _T("%d-%b-%Y %H:%M:%S"), lt);
+                  dwPos = (UINT32)_tcslen(pText);
+                  break;
+               case 'T':   // Event's timestamp as number of seconds since epoch
+                  dwSize += 16;
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  _sntprintf(&pText[dwPos], 16, _T("%lu"), (unsigned long)((event != NULL) ? event->getTimeStamp() : time(NULL)));
+                  dwPos = (UINT32)_tcslen(pText);
+                  break;
+               case 'u':   // User tag
+                  if ((event != NULL) && (event->getUserTag() != NULL))
+                  {
+                     dwSize += (UINT32)_tcslen(event->getUserTag());
+                     pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                     _tcscpy(&pText[dwPos], event->getUserTag());
+                     dwPos += (UINT32)_tcslen(event->getUserTag());
+                  }
+                  break;
+               case 'U':   // User name
+                  dwSize += (UINT32)_tcslen(userName);
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  _tcscpy(&pText[dwPos], userName);
+                  dwPos += (UINT32)_tcslen(userName);
+                  break;
+               case 'v':   // NetXMS server version
+                  dwSize += (UINT32)_tcslen(NETXMS_VERSION_STRING);
+                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                  _tcscpy(&pText[dwPos], NETXMS_VERSION_STRING);
+                  dwPos += (UINT32)_tcslen(NETXMS_VERSION_STRING);
+                  break;
+               case 'y': // alarm state
+                  if (alarm != NULL)
+                  {
+                     dwSize += 3;
+                     pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                     _sntprintf(&pText[dwPos], 4, _T("%d"), (int)alarm->getState());
+                     dwPos = (UINT32)_tcslen(pText);
+                  }
+                  break;
+               case 'Y': // alarm ID
+                  break;
+               case '0':
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                  if (event != NULL)
+                  {
+                     szBuffer[0] = *pCurr;
+                     if (isdigit(*(pCurr + 1)))
+                     {
+                        pCurr++;
+                        szBuffer[1] = *pCurr;
+                        szBuffer[2] = 0;
+                     }
+                     else
+                     {
+                        szBuffer[1] = 0;
+                     }
+                     dwParam = _tcstoul(szBuffer, NULL, 10);
+                     const Array *params = event->getParameterList();
+                     if ((dwParam > 0) && (dwParam <= (UINT32)params->size()))
+                     {
+                        const TCHAR *value = (TCHAR *)params->get(dwParam - 1);
+                        if (value == NULL)
+                           value = _T("");
+                        dwSize += (UINT32)_tcslen(value);
+                        pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                        _tcscpy(&pText[dwPos], value);
+                        dwPos += (UINT32)_tcslen(value);
+                     }
+                  }
+                  break;
+               case '[':   // Script
+                  for(i = 0, pCurr++; (*pCurr != ']') && (*pCurr != 0) && (i < 255); pCurr++)
+                  {
+                     scriptName[i++] = *pCurr;
+                  }
+                  if (*pCurr == 0)  // no terminating ]
+                  {
+                     pCurr--;
+                  }
+                  else
+                  {
+                     scriptName[i] = 0;
+
+                     // Entry point can be given in form script/entry_point
+                     TCHAR *entryPoint = _tcschr(scriptName, _T('/'));
+                     if (entryPoint != NULL)
+                     {
+                        *entryPoint = 0;
+                        entryPoint++;
+                        StrStrip(entryPoint);
+                     }
+                     StrStrip(scriptName);
+
+                     NXSL_VM *vm = CreateServerScriptVM(scriptName);
+                     if (vm != NULL)
+                     {
+                        vm->setGlobalVariable(_T("$object"), createNXSLObject());
+                        if (getObjectClass() == OBJECT_NODE)
+                           vm->setGlobalVariable(_T("$node"), createNXSLObject());
+                        if (event != NULL)
+                           vm->setGlobalVariable(_T("$event"), (event != NULL) ? new NXSL_Value(new NXSL_Object(&g_nxslEventClass, const_cast<Event *>(event))) : new NXSL_Value);
+                        if (alarm != NULL)
+                        {
+                           vm->setGlobalVariable(_T("$alarm"), (event != NULL) ? new NXSL_Value(new NXSL_Object(&g_nxslAlarmClass, const_cast<Alarm *>(alarm))) : new NXSL_Value);
+                           vm->setGlobalVariable(_T("$alarmMessage"), new NXSL_Value(alarm->getMessage()));
+                           vm->setGlobalVariable(_T("$alarmKey"), new NXSL_Value(alarm->getKey()));
+                        }
+
+                        if (vm->run(0, NULL, NULL, NULL, NULL, entryPoint))
+                        {
+                           NXSL_Value *result = vm->getResult();
+                           if (result != NULL)
+                           {
+                              const TCHAR *temp = result->getValueAsCString();
+                              if (temp != NULL)
+                              {
+                                 dwSize += (UINT32)_tcslen(temp);
+                                 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                                 _tcscpy(&pText[dwPos], temp);
+                                 dwPos += (UINT32)_tcslen(temp);
+                                 DbgPrintf(4, _T("NetObj::ExpandText(%d, \"%s\"): Script %s executed successfully"),
+                                    (int)((event != NULL) ? event->getCode() : 0), textTemplate, scriptName);
+                              }
+                           }
+                        }
+                        else
+                        {
+                           DbgPrintf(4, _T("NetObj::ExpandText(%d, \"%s\"): Script %s execution error: %s"),
+                                     (int)((event != NULL) ? event->getCode() : 0), textTemplate, scriptName, vm->getErrorText());
+                           PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", scriptName, vm->getErrorText(), 0);
+                        }
+                     }
+                     else
+                     {
+                        DbgPrintf(4, _T("NetObj::ExpandText(%d, \"%s\"): Cannot find script %s"),
+                           (int)((event != NULL) ? event->getCode() : 0), textTemplate, scriptName);
+                     }
+                  }
+                  break;
+               case '{':   // Custom attribute
+                  for(i = 0, pCurr++; (*pCurr != '}') && (*pCurr != 0) && (i < 255); pCurr++)
+                  {
+                     scriptName[i++] = *pCurr;
+                  }
+                  if (*pCurr == 0)  // no terminating }
+                  {
+                     pCurr--;
+                  }
+                  else
+                  {
+                     scriptName[i] = 0;
+                     StrStrip(scriptName);
+                     const TCHAR *temp = getCustomAttribute(scriptName);
+                     if (temp != NULL)
+                     {
+                        dwSize += (UINT32)_tcslen(temp);
+                        pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                        _tcscpy(&pText[dwPos], temp);
+                        dwPos += (UINT32)_tcslen(temp);
+                        free(temp);
+                     }
+                  }
+                  break;
+               case '(':   // Input field
+                  for(i = 0, pCurr++; (*pCurr != ')') && (*pCurr != 0) && (i < 255); pCurr++)
+                  {
+                     scriptName[i++] = *pCurr;
+                  }
+                  if (*pCurr == 0)  // no terminating )
+                  {
+                     pCurr--;
+                  }
+                  else if (inputFields != NULL)
+                  {
+                     scriptName[i] = 0;
+                     StrStrip(scriptName);
+                     const TCHAR *temp = inputFields->get(scriptName);
+                     if (temp != NULL)
+                     {
+                        dwSize += (UINT32)_tcslen(temp);
+                        pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                        _tcscpy(&pText[dwPos], temp);
+                        dwPos += (UINT32)_tcslen(temp);
+                     }
+                  }
+                  break;
+               case '<':   // Named parameter
+                  if (event != NULL)
+                  {
+                     for(i = 0, pCurr++; (*pCurr != '>') && (*pCurr != 0) && (i < 255); pCurr++)
+                     {
+                        scriptName[i++] = *pCurr;
+                     }
+                     if (*pCurr == 0)  // no terminating >
+                     {
+                        pCurr--;
+                     }
+                     else
+                     {
+                        scriptName[i] = 0;
+                        StrStrip(scriptName);
+                        const StringList *names = event->getParameterNames();
+                        int index = names->indexOfIgnoreCase(scriptName);
+                        if (index != -1)
+                        {
+                           const TCHAR *temp = (TCHAR *)names->get(index);
+                           if (temp != NULL)
+                           {
+                              dwSize += (UINT32)_tcslen(temp);
+                              pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
+                              _tcscpy(&pText[dwPos], temp);
+                              dwPos += (UINT32)_tcslen(temp);
+                           }
+                        }
+                     }
+                  }
+                  break;
+               default:    // All other characters are invalid, ignore
+                  break;
+            }
+            break;
+         case '\\':  // Escape character
+            pCurr++;
+            if (*pCurr == 0)
+            {
+               pCurr--;
+               break;   // Abnormal loop termination
+            }
+            switch(*pCurr)
+            {
+               case 't':
+                  pText[dwPos++] = '\t';
+                  break;
+               case 'n':
+                  pText[dwPos++] = 0x0D;
+                  pText[dwPos++] = 0x0A;
+                  break;
+               default:
+                  pText[dwPos++] = *pCurr;
+                  break;
+            }
+            break;
+         default:
+            pText[dwPos++] = *pCurr;
+            break;
+      }
+   }
+   pText[dwPos] = 0;
+   return pText;
+}
index d9edb8c..a07d3fc 100644 (file)
@@ -7120,216 +7120,6 @@ VlanList *Node::getVlans()
 }
 
 /**
- * Substitute % macros in given text with actual values
- */
-TCHAR *Node::expandText(const TCHAR *textTemplate, StringMap *inputFields, const TCHAR *userName)
-{
-   const TCHAR *pCurr;
-   UINT32 dwPos, dwSize;
-   TCHAR *pText, scriptName[256];
-   int i;
-
-   dwSize = (UINT32)_tcslen(textTemplate) + 1;
-   pText = (TCHAR *)malloc(dwSize * sizeof(TCHAR));
-   for(pCurr = textTemplate, dwPos = 0; *pCurr != 0; pCurr++)
-   {
-      switch(*pCurr)
-      {
-         case '%':   // Metacharacter
-            pCurr++;
-            if (*pCurr == 0)
-            {
-               pCurr--;
-               break;   // Abnormal loop termination
-            }
-            switch(*pCurr)
-            {
-               case '%':
-                  pText[dwPos++] = '%';
-                  break;
-               case 'a':   // IP address of the node
-                  dwSize += 48;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  m_ipAddress.toString(&pText[dwPos]);
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'g':   // node's GUID
-                  dwSize += 36;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  m_guid.toString(&pText[dwPos]);
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'i':   // Node identifier
-                  dwSize += 10;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  _sntprintf(&pText[dwPos], 11, _T("0x%08X"), m_id);
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'I':   // Node identifier in decimal form
-                  dwSize += 10;
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  _sntprintf(&pText[dwPos], 11, _T("%u"), m_id);
-                  dwPos = (UINT32)_tcslen(pText);
-                  break;
-               case 'n':   // Name of the node
-                  dwSize += (UINT32)_tcslen(m_name);
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  _tcscpy(&pText[dwPos], m_name);
-                  dwPos += (UINT32)_tcslen(m_name);
-                  break;
-               case 'U':   // User name
-                  dwSize += (UINT32)_tcslen(userName);
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  _tcscpy(&pText[dwPos], userName);
-                  dwPos += (UINT32)_tcslen(userName);
-                  break;
-               case 'v':   // NetXMS server version
-                  dwSize += (UINT32)_tcslen(NETXMS_VERSION_STRING);
-                  pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                  _tcscpy(&pText[dwPos], NETXMS_VERSION_STRING);
-                  dwPos += (UINT32)_tcslen(NETXMS_VERSION_STRING);
-                  break;
-               case '[':   // Script
-                  for(i = 0, pCurr++; (*pCurr != ']') && (*pCurr != 0) && (i < 255); pCurr++)
-                  {
-                     scriptName[i++] = *pCurr;
-                  }
-                  if (*pCurr == 0)  // no terminating ]
-                  {
-                     pCurr--;
-                  }
-                  else
-                  {
-                     scriptName[i] = 0;
-                     StrStrip(scriptName);
-
-                     NXSL_VM *vm = CreateServerScriptVM(scriptName);
-                     if (vm != NULL)
-                     {
-                        vm->setGlobalVariable(_T("$node"), new NXSL_Value(new NXSL_Object(&g_nxslNodeClass, this)));
-
-                        if (vm->run())
-                        {
-                           NXSL_Value *result = vm->getResult();
-                           if (result != NULL)
-                           {
-                              const TCHAR *temp = result->getValueAsCString();
-                              if (temp != NULL)
-                              {
-                                 dwSize += (UINT32)_tcslen(temp);
-                                 pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                                 _tcscpy(&pText[dwPos], temp);
-                                 dwPos += (UINT32)_tcslen(temp);
-                                 DbgPrintf(4, _T("Node::expandText(\"%s\"): Script %s executed successfully"),
-                                           textTemplate, scriptName);
-                              }
-                           }
-                        }
-                        else
-                        {
-                           DbgPrintf(4, _T("Node::expandText(\"%s\"): Script %s execution error: %s"),
-                                     textTemplate, scriptName, vm->getErrorText());
-                           PostEvent(EVENT_SCRIPT_ERROR, g_dwMgmtNode, "ssd", scriptName, vm->getErrorText(), 0);
-                        }
-                        delete vm;
-                     }
-                     else
-                     {
-                        DbgPrintf(4, _T("Node::expandText(\"%s\"): Cannot find script %s"), textTemplate, scriptName);
-                     }
-                  }
-                  break;
-               case '{':   // Custom attribute
-                  for(i = 0, pCurr++; (*pCurr != '}') && (*pCurr != 0) && (i < 255); pCurr++)
-                  {
-                     scriptName[i++] = *pCurr;
-                  }
-                  if (*pCurr == 0)  // no terminating }
-                  {
-                     pCurr--;
-                  }
-                  else
-                  {
-                     scriptName[i] = 0;
-                     StrStrip(scriptName);
-                     TCHAR *temp = getCustomAttributeCopy(scriptName);
-                     if (temp != NULL)
-                     {
-                        dwSize += (UINT32)_tcslen(temp);
-                        pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                        _tcscpy(&pText[dwPos], temp);
-                        dwPos += (UINT32)_tcslen(temp);
-                        free(temp);
-                     }
-                  }
-                  break;
-               case '(':   // Input field
-                  for(i = 0, pCurr++; (*pCurr != ')') && (*pCurr != 0) && (i < 255); pCurr++)
-                  {
-                     scriptName[i++] = *pCurr;
-                  }
-                  if (*pCurr == 0)  // no terminating }
-                  {
-                     pCurr--;
-                  }
-                  else if (inputFields != NULL)
-                  {
-                     scriptName[i] = 0;
-                     StrStrip(scriptName);
-                     const TCHAR *temp = inputFields->get(scriptName);
-                     if (temp != NULL)
-                     {
-                        dwSize += (UINT32)_tcslen(temp);
-                        pText = (TCHAR *)realloc(pText, dwSize * sizeof(TCHAR));
-                        _tcscpy(&pText[dwPos], temp);
-                        dwPos += (UINT32)_tcslen(temp);
-                     }
-                  }
-                  break;
-               case '<':   // named attribute - scan until closing > for compatibility
-                  for(i = 0, pCurr++; (*pCurr != '>') && (*pCurr != 0) && (i < 255); pCurr++)
-                  {
-                  }
-                  if (*pCurr == 0)  // no terminating }
-                  {
-                     pCurr--;
-                  }
-                  break;
-               default:    // All other characters are invalid, ignore
-                  break;
-            }
-            break;
-         case '\\':  // Escape character
-            pCurr++;
-            if (*pCurr == 0)
-            {
-               pCurr--;
-               break;   // Abnormal loop termination
-            }
-            switch(*pCurr)
-            {
-               case 't':
-                  pText[dwPos++] = '\t';
-                  break;
-               case 'n':
-                  pText[dwPos++] = 0x0D;
-                  pText[dwPos++] = 0x0A;
-                  break;
-               default:
-                  pText[dwPos++] = *pCurr;
-                  break;
-            }
-            break;
-         default:
-            pText[dwPos++] = *pCurr;
-            break;
-      }
-   }
-   pText[dwPos] = 0;
-   return pText;
-}
-
-/**
  * Check and update last agent trap ID
  */
 bool Node::checkAgentTrapId(UINT64 trapId)
index eb003dc..d838c8f 100644 (file)
@@ -1218,10 +1218,10 @@ static int F_AgentExecuteAction(int argc, NXSL_Value **argv, NXSL_Value **ppResu
    AgentConnection *conn = node->createAgentConnection();
    if (conn != NULL)
    {
-      const TCHAR *args[128];
+      StringList list;
       for(int i = 2; (i < argc) && (i < 128); i++)
-         args[i - 2] = argv[i]->getValueAsCString();
-      UINT32 rcc = conn->execAction(argv[1]->getValueAsCString(), argc - 2, args, false, NULL, NULL);
+         list.add(argv[i]->getValueAsCString());
+      UINT32 rcc = conn->execAction(argv[1]->getValueAsCString(), list, false, NULL, NULL);
       *ppResult = new NXSL_Value((rcc == ERR_SUCCESS) ? 1 : 0);
       conn->decRefCount();
       nxlog_debug(5, _T("NXSL: F_AgentExecuteAction: action %s on node %s [%d]: RCC=%d"), argv[1]->getValueAsCString(), node->getName(), node->getId(), rcc);
@@ -1272,11 +1272,11 @@ static int F_AgentExecuteActionWithOutput(int argc, NXSL_Value **argv, NXSL_Valu
    AgentConnection *conn = node->createAgentConnection();
    if (conn != NULL)
    {
-      const TCHAR *args[128];
+      StringList list;
       for(int i = 2; (i < argc) && (i < 128); i++)
-         args[i - 2] = argv[i]->getValueAsCString();
+         list.add(argv[i]->getValueAsCString());
       String output;
-      UINT32 rcc = conn->execAction(argv[1]->getValueAsCString(), argc - 2, args, true, ActionOutputHandler, &output);
+      UINT32 rcc = conn->execAction(argv[1]->getValueAsCString(), list, true, ActionOutputHandler, &output);
       *ppResult = (rcc == ERR_SUCCESS) ? new NXSL_Value(output) : new NXSL_Value();
       conn->decRefCount();
       nxlog_debug(5, _T("NXSL: F_AgentExecuteActionWithOutput: action %s on node %s [%d]: RCC=%d"), argv[1]->getValueAsCString(), node->getName(), node->getId(), rcc);
index 93950f3..63c6762 100644 (file)
@@ -1533,7 +1533,7 @@ ServerCommandExec::ServerCommandExec(NXCPMessage *request, ClientSession *sessio
          inputFields = NULL;
 
       TCHAR *cmd = request->getFieldAsString(VID_COMMAND);
-      m_cmd = ((Node *)object)->expandText(cmd, inputFields, session->getLoginName());
+      m_cmd = object->expandText(cmd, NULL, NULL, session->getLoginName(), inputFields);
       free(cmd);
       delete inputFields;
    }
index 4381f6a..e785083 100644 (file)
@@ -319,7 +319,7 @@ bool Sensor::saveToDatabase(DB_HANDLE hdb)
 
    // Clear modifications flag and unlock object
        if (success)
-               m_modified = 0;
+           m_isModified = false;
    unlockProperties();
 
    return success;
index 3a09802..858c10d 100644 (file)
@@ -1349,6 +1349,9 @@ void ClientSession::processingThread()
          case CMD_GET_PREDICTED_DATA:
             CALL_IN_NEW_THREAD(getPredictedData, pMsg);
             break;
+         case CMD_EXPAND_MACROS:
+            expandMacros(pMsg);
+            break;
 #ifdef WITH_ZMQ
          case CMD_ZMQ_SUBSCRIBE_EVENT:
             zmqManageSubscription(pMsg, zmq::EVENT, true);
@@ -7579,7 +7582,8 @@ void ClientSession::updateAgentConfig(NXCPMessage *pRequest)
                if ((pRequest->getFieldAsUInt16(VID_APPLY_FLAG) != 0) &&
                    (dwResult == ERR_SUCCESS))
                {
-                  dwResult = pConn->execAction(_T("Agent.Restart"), 0, NULL);
+                  StringList list;
+                  dwResult = pConn->execAction(_T("Agent.Restart"), list);
                }
 
                switch(dwResult)
@@ -7675,60 +7679,160 @@ static void ActionExecuteCallback(ActionCallbackEvent e, const TCHAR *text, void
 }
 
 /**
+ *  Splits command line
+ */
+static StringList *SplitCommandLine(TCHAR *command)
+{
+   StringList *listOfStrings = new StringList();
+   String tmp;
+   int state = 0;
+   int size = _tcslen(command);
+   for(int i = 0; i < size; i++)
+   {
+      TCHAR c = command[i];
+      switch(state)
+      {
+         case 0:
+            if (c == _T(' '))
+            {
+               listOfStrings->add(tmp);
+               tmp = _T("");
+               state = 3;
+            }
+            else if (c == _T('"'))
+            {
+               state = 1;
+            }
+            else if (c == _T('\''))
+            {
+               state = 2;
+            }
+            else
+            {
+               tmp.append(c);
+            }
+            break;
+         case 1: // double quoted string
+            if (c == _T('"'))
+            {
+               state = 0;
+            }
+            else
+            {
+               tmp.append(c);
+            }
+            break;
+         case 2: // single quoted string
+            if (c == '\'')
+            {
+               state = 0;
+            }
+            else
+            {
+               tmp.append(c);
+            }
+            break;
+         case 3: // skip
+            if (c != _T(' '))
+            {
+               if (c == _T('"'))
+               {
+                  state = 1;
+               }
+               else if (c == '\'')
+               {
+                  state = 2;
+               }
+               else
+               {
+                  tmp.append(c);
+                  state = 0;
+               }
+            }
+            break;
+      }
+   }
+   if (state != 3)
+      listOfStrings->add(tmp);
+
+   return listOfStrings;
+}
+
+/**
  * Execute action on agent
  */
-void ClientSession::executeAction(NXCPMessage *pRequest)
+void ClientSession::executeAction(NXCPMessage *request)
 {
    NXCPMessage msg;
 
    // Prepare response message
    msg.setCode(CMD_REQUEST_COMPLETED);
-   msg.setId(pRequest->getId());
+   msg.setId(request->getId());
 
    // Get object id and check prerequisites
-   NetObj *object = FindObjectById(pRequest->getFieldAsUInt32(VID_OBJECT_ID));
+   NetObj *object = FindObjectById(request->getFieldAsUInt32(VID_OBJECT_ID));
    if (object != NULL)
    {
       if (object->getObjectClass() == OBJECT_NODE)
       {
          TCHAR action[MAX_PARAM_NAME];
-         pRequest->getFieldAsString(VID_ACTION_NAME, action, MAX_PARAM_NAME);
+         request->getFieldAsString(VID_ACTION_NAME, action, MAX_PARAM_NAME);
 
          if (object->checkAccessRights(m_dwUserId, OBJECT_ACCESS_CONTROL))
          {
             AgentConnection *pConn = ((Node *)object)->createAgentConnection();
             if (pConn != NULL)
             {
-               TCHAR *argv[64];
-               int argc = pRequest->getFieldAsInt16(VID_NUM_ARGS);
-               if (argc > 64)
+               StringList *list = NULL;
+               if(request->getFieldAsBoolean(VID_EXPAND_STRING))
                {
-                  debugPrintf(4, _T("executeAction: too many arguments (%d)"), argc);
-                  argc = 64;
+                  StringMap strMap;
+                  strMap.loadMessage(request, VID_IN_FIELD_COUNT, VID_IN_FIELD_BASE);
+                  Alarm *alarm = FindAlarmById(request->getFieldAsUInt32(VID_ALARM_ID));
+                  if(alarm != NULL && !object->checkAccessRights(m_dwUserId, OBJECT_ACCESS_READ_ALARMS) && !alarm->checkCategoryAccess(this))
+                  {
+                     msg.setField(VID_RCC, RCC_ACCESS_DENIED);
+                     sendMessage(&msg);
+                     delete alarm;
+                     return;
+                  }
+                  TCHAR *result = object->expandText(action, alarm, NULL, m_loginName, &strMap);
+                  list = SplitCommandLine(result);
+                  _tcsncpy(action, list->get(0), MAX_PARAM_NAME);
+                  list->remove(0);
+                  delete alarm;
+                  free(result);
+               }
+               else
+               {
+                  int argc = request->getFieldAsInt16(VID_NUM_ARGS);
+                  if (argc > 64)
+                  {
+                     debugPrintf(4, _T("executeAction: too many arguments (%d)"), argc);
+                     argc = 64;
+                  }
+
+                  list = new StringList();
+                  UINT32 fieldId = VID_ACTION_ARG_BASE;
+                  for(int i = 0; i < argc; i++)
+                     list->addPreallocated(request->getFieldAsString(fieldId++));
                }
-               UINT32 fieldId = VID_ACTION_ARG_BASE;
-               for(int i = 0; i < argc; i++)
-                  argv[i] = pRequest->getFieldAsString(fieldId++);
 
                UINT32 rcc;
-               bool withOutput = pRequest->getFieldAsBoolean(VID_RECEIVE_OUTPUT);
+               bool withOutput = request->getFieldAsBoolean(VID_RECEIVE_OUTPUT);
                if (withOutput)
                {
-                  ActionExecutionData data(this, pRequest->getId());
-                  rcc = pConn->execAction(action, argc, argv, true, ActionExecuteCallback, &data);
+                  ActionExecutionData data(this, request->getId());
+                  rcc = pConn->execAction(action, *list, true, ActionExecuteCallback, &data);
                }
                else
                {
-                  rcc = pConn->execAction(action, argc, argv);
+                  rcc = pConn->execAction(action, *list);
                }
                debugPrintf(4, _T("executeAction: rcc=%d"), rcc);
 
                String args;
-               for(int i = 0; i < argc; i++)
-               {
-                  args.appendFormattedString(_T("%s, "), argv[i]);
-                  free(argv[i]);
-               }
+               args.appendPreallocated(list->join(_T(", ")));
                args.shrink(2);
 
                switch(rcc)
@@ -7752,6 +7856,7 @@ void ClientSession::executeAction(NXCPMessage *pRequest)
                      break;
                }
                pConn->decRefCount();
+               delete list;
             }
             else
             {
@@ -10325,10 +10430,24 @@ void ClientSession::getAgentFile(NXCPMessage *request)
                        if (object->getObjectClass() == OBJECT_NODE)
                        {
                                request->getFieldAsString(VID_FILE_NAME, remoteFile, MAX_PATH);
+                          StringMap strMap;
+                          strMap.loadMessage(request, VID_IN_FIELD_COUNT, VID_IN_FIELD_BASE);
+            Alarm *alarm = FindAlarmById(request->getFieldAsUInt32(VID_ALARM_ID));
+            if(alarm != NULL && !object->checkAccessRights(m_dwUserId, OBJECT_ACCESS_READ_ALARMS) && !alarm->checkCategoryAccess(this))
+            {
+               msg.setField(VID_RCC, RCC_ACCESS_DENIED);
+               sendMessage(&msg);
+               delete alarm;
+               return;
+            }
+                               TCHAR *result = object->expandText(remoteFile, alarm, NULL, m_loginName, &strMap);
             bool follow = request->getFieldAsBoolean(VID_FILE_FOLLOW);
-                               FileDownloadJob *job = new FileDownloadJob((Node *)object, remoteFile,
+                               FileDownloadJob *job = new FileDownloadJob((Node *)object, result,
                                         request->getFieldAsUInt32(VID_FILE_SIZE_LIMIT), follow, this, request->getId());
                                msg.setField(VID_NAME, job->getLocalFileName());
+                               msg.setField(VID_FILE_NAME, result);
+                               free(result);
+                               delete alarm;
                                if (AddJob(job))
                                {
                    msg.setField(VID_RCC, RCC_SUCCESS);
@@ -10687,8 +10806,18 @@ void ClientSession::executeLibraryScript(NXCPMessage *request)
                      inputFields = NULL;
                   }
 
-                  TCHAR *expScript = ((Node *)object)->expandText(script, inputFields, m_loginName);
+                  Alarm *alarm = FindAlarmById(request->getFieldAsUInt32(VID_ALARM_ID));
+                  if(alarm != NULL && !object->checkAccessRights(m_dwUserId, OBJECT_ACCESS_READ_ALARMS) && !alarm->checkCategoryAccess(this))
+                  {
+                     msg.setField(VID_RCC, RCC_ACCESS_DENIED);
+                     sendMessage(&msg);
+                     delete alarm;
+                     delete inputFields;
+                     return;
+                  }
+                  TCHAR *expScript = object->expandText(script, alarm, NULL, m_loginName, inputFields);
                   script = expScript;
+                  delete alarm;
                   delete inputFields;
                }
 
@@ -13807,6 +13936,50 @@ void ClientSession::unbindAgentTunnel(NXCPMessage *request)
    sendMessage(&msg);
 }
 
+void ClientSession::expandMacros(NXCPMessage *request)
+{
+   NXCPMessage msg(CMD_REQUEST_COMPLETED, request->getId());
+
+   StringMap strMap;
+   strMap.loadMessage(request, VID_IN_FIELD_COUNT, VID_IN_FIELD_BASE);
+
+   int fieldCount = request->getFieldAsUInt32(VID_STRING_COUNT);
+   UINT32 inFieldId = VID_EXP_STRING_BASE;
+   UINT32 outFieldId = VID_EXP_STRING_BASE;
+   for(int i = 0; i < fieldCount; i++, inFieldId=inFieldId+2, outFieldId++)
+   {
+      TCHAR *textToExpand = request->getFieldAsString(inFieldId++, NULL, 0);
+      NetObj *obj = FindObjectById(request->getFieldAsUInt32(inFieldId++));
+      if(obj == NULL)
+      {
+         msg.setField(VID_RCC, RCC_INVALID_OBJECT_ID);
+         sendMessage(&msg);
+         return;
+      }
+      obj->incRefCount();
+      Alarm *alarm = FindAlarmById(request->getFieldAsUInt32(inFieldId++));
+      if(!obj->checkAccessRights(m_dwUserId, OBJECT_ACCESS_READ) || (alarm != NULL &&
+            !obj->checkAccessRights(m_dwUserId, OBJECT_ACCESS_READ_ALARMS) && !alarm->checkCategoryAccess(this)))
+      {
+         msg.setField(VID_RCC, RCC_ACCESS_DENIED);
+         sendMessage(&msg);
+         delete alarm;
+         return;
+      }
+
+      TCHAR *result = obj->expandText(textToExpand, alarm, NULL, m_loginName, &strMap);
+      msg.setField(outFieldId, result);
+      debugPrintf(7, _T("ClientSession::expandMacros(): String for expansion: '%s', result: '%s'"), textToExpand);
+      free(textToExpand);
+      free(result);
+      obj->decRefCount();
+      delete alarm;
+   }
+
+   msg.setField(VID_RCC, RCC_SUCCESS);
+   sendMessage(&msg);
+}
+
 #ifdef WITH_ZMQ
 /**
  * Manage subscription for ZMQ forwarder
index 63ca9e9..6c25ad2 100644 (file)
@@ -30,7 +30,7 @@
 
 BOOL InitActions();
 void CleanupActions();
-BOOL ExecuteAction(UINT32 dwActionId, Event *pEvent, const TCHAR *alarmMsg, const TCHAR *alarmKey);
+BOOL ExecuteAction(UINT32 dwActionId, Event *pEvent, const Alarm *alarm);
 UINT32 CreateNewAction(const TCHAR *pszName, UINT32 *pdwId);
 UINT32 DeleteActionFromDB(UINT32 dwActionId);
 UINT32 ModifyActionFromMessage(NXCPMessage *pMsg);
index 7d1b033..af236e2 100644 (file)
@@ -126,13 +126,14 @@ void DeleteAlarmEvents(DB_HANDLE hdb, UINT32 alarmId);
 
 UINT32 NXCORE_EXPORTABLE GetAlarm(UINT32 dwAlarmId, UINT32 userId, NXCPMessage *msg, ClientSession *session);
 ObjectArray<Alarm> NXCORE_EXPORTABLE *GetAlarms(UINT32 objectId = 0, bool recursive = false);
+Alarm NXCORE_EXPORTABLE *FindAlarmById(UINT32 alarmId);
 UINT32 NXCORE_EXPORTABLE GetAlarmEvents(UINT32 dwAlarmId, UINT32 userId, NXCPMessage *msg, ClientSession *session);
 NetObj NXCORE_EXPORTABLE *GetAlarmSourceObject(UINT32 dwAlarmId, bool alreadyLocked = false);
 NetObj NXCORE_EXPORTABLE *GetAlarmSourceObject(const TCHAR *hdref);
 int GetMostCriticalStatusForObject(UINT32 dwObjectId);
 void GetAlarmStats(NXCPMessage *pMsg);
 
-void NXCORE_EXPORTABLE CreateNewAlarm(TCHAR *message, TCHAR *key, int state, int severity, UINT32 timeout,
+UINT32 NXCORE_EXPORTABLE CreateNewAlarm(TCHAR *message, TCHAR *key, int state, int severity, UINT32 timeout,
                                                                                   UINT32 timeoutEvent, Event *event, UINT32 ackTimeout, IntegerArray<UINT32> *alarmCategoryList, bool openHelpdeskIssue);
 UINT32 NXCORE_EXPORTABLE AckAlarmById(UINT32 dwAlarmId, ClientSession *session, bool sticky, UINT32 acknowledgmentActionTime);
 UINT32 NXCORE_EXPORTABLE AckAlarmByHDRef(const TCHAR *hdref, ClientSession *session, bool sticky, UINT32 acknowledgmentActionTime);
index 41124ee..50b5581 100644 (file)
@@ -739,6 +739,7 @@ private:
    void unbindAgentTunnel(NXCPMessage *request);
    void getPredictionEngines(NXCPMessage *request);
    void getPredictedData(NXCPMessage *request);
+   void expandMacros(NXCPMessage *request);
 #ifdef WITH_ZMQ
    void zmqManageSubscription(NXCPMessage *request, zmq::SubscriptionType type, bool subscribe);
    void zmqListSubscriptions(NXCPMessage *request, zmq::SubscriptionType type);
index ee901b5..7d6618f 100644 (file)
@@ -156,6 +156,8 @@ public:
    const TCHAR *getMessage() const { return m_messageText; }
    const TCHAR *getUserTag() const { return m_userTag; }
    time_t getTimeStamp() const { return m_timeStamp; }
+   const Array *getParameterList() const { return &m_parameters; }
+   const StringList *getParameterNames() const { return &m_parameterNames; }
 
    void setSeverity(int severity) { m_severity = severity; }
 
@@ -165,8 +167,7 @@ public:
    void prepareMessage(NXCPMessage *msg) const;
 
    void expandMessageText();
-   TCHAR *expandText(const TCHAR *textTemplate, const TCHAR *alarmMsg = NULL, const TCHAR *alarmKey = NULL);
-   static TCHAR *expandText(Event *event, UINT32 sourceObject, const TCHAR *textTemplate, const TCHAR *alarmMsg, const TCHAR *alarmKey);
+   TCHAR *expandText(const TCHAR *textTemplate, const Alarm *alarm = NULL);
    void setMessage(const TCHAR *text) { free(m_messageText); m_messageText = _tcsdup_ex(text); }
    void setUserTag(const TCHAR *text) { free(m_userTag); m_userTag = _tcsdup_ex(text); }
 
@@ -228,7 +229,7 @@ private:
    bool matchSeverity(UINT32 dwSeverity);
    bool matchScript(Event *pEvent);
 
-   void generateAlarm(Event *pEvent);
+   UINT32 generateAlarm(Event *pEvent);
 
 public:
    EPRule(UINT32 id);
index 32a1dc5..058caf7 100644 (file)
@@ -700,6 +700,7 @@ public:
    const TCHAR *dbgGetChildList(TCHAR *szBuffer);
 
    static const TCHAR *getObjectClassName(int objectClass);
+   TCHAR *expandText(const TCHAR *textTemplate, const Alarm *alarm, const Event *event, const TCHAR *userName, const StringMap *inputFields);
 };
 
 /**
@@ -1835,8 +1836,6 @@ public:
 
    virtual json_t *toJson();
 
-       TCHAR *expandText(const TCHAR *textTemplate, StringMap *inputFields, const TCHAR *userName);
-
        Cluster *getMyCluster();
 
    const InetAddress& getIpAddress() const { return m_ipAddress; }
index 9532fee..d80e3ee 100644 (file)
@@ -572,7 +572,7 @@ public:
    UINT32 nop();
    UINT32 setServerCapabilities();
    UINT32 setServerId(UINT64 serverId);
-   UINT32 execAction(const TCHAR *action, int argc, const TCHAR * const *argv, bool withOutput = false,
+   UINT32 execAction(const TCHAR *action, const StringList &list, bool withOutput = false,
             void (* outputCallback)(ActionCallbackEvent, const TCHAR *, void *) = NULL, void *cbData = NULL);
    UINT32 uploadFile(const TCHAR *localFile, const TCHAR *destinationFile = NULL,
             void (* progressCallback)(INT64, void *) = NULL, void *cbArg = NULL,
index 67176f6..870ac01 100644 (file)
@@ -1400,7 +1400,7 @@ UINT32 AgentConnection::authenticate(BOOL bProxyData)
 /**
  * Execute action on agent
  */
-UINT32 AgentConnection::execAction(const TCHAR *action, int argc, const TCHAR * const *argv,
+UINT32 AgentConnection::execAction(const TCHAR *action, const StringList &list,
                                    bool withOutput, void (* outputCallback)(ActionCallbackEvent, const TCHAR *, void *), void *cbData)
 {
    NXCPMessage msg(m_nProtocolVersion);
@@ -1415,9 +1415,9 @@ UINT32 AgentConnection::execAction(const TCHAR *action, int argc, const TCHAR *
    msg.setId(dwRqId);
    msg.setField(VID_ACTION_NAME, action);
    msg.setField(VID_RECEIVE_OUTPUT, (UINT16)(withOutput ? 1 : 0));
-   msg.setField(VID_NUM_ARGS, (UINT32)argc);
-   for(i = 0; i < argc; i++)
-      msg.setField(VID_ACTION_ARG_BASE + i, argv[i]);
+   msg.setField(VID_NUM_ARGS, (UINT32)list.size());
+   for(i = 0; i < list.size(); i++)
+      msg.setField(VID_ACTION_ARG_BASE + i, list.get(i));
 
    if (sendMessage(&msg))
    {
index 0e7de99..96cd5d6 100644 (file)
@@ -270,14 +270,19 @@ int main(int argc, char *argv[])
                                        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, argv[optind + 1], -1, action, 256);
                                        action[255] = 0;
                                        
+                                       StringList list;
                                        int count = std::min(argc - optind - 2, 256);
                                        for(i = 0, k = optind + 2; i < count; i++, k++)
-                                               args[i] = WideStringFromMBString(argv[k]);
-               dwError = conn->execAction(action, count, args, showOutput, OutputCallback);
+                                               list.addPreallocated(WideStringFromMBString(argv[k]));
+               dwError = conn->execAction(action, list, showOutput, OutputCallback);
                                        for(i = 0; i < count; i++)
                                                free(args[i]);
 #else
-               dwError = conn->execAction(argv[optind + 1], argc - optind - 2, &argv[optind + 2], showOutput, OutputCallback);
+               StringList list;
+               int count = std::min(argc - optind - 2, 256);
+               for(i = 0, k = optind + 2; i < count; i++, k++)
+                  list.addPreallocated(argv[k]);
+               dwError = conn->execAction(argv[optind + 1], list, showOutput, OutputCallback);
 #endif
                if (dwError == ERR_SUCCESS)
                   _tprintf(_T("Action executed successfully\n"));
index 7cce917..36af229 100644 (file)
@@ -87,8 +87,10 @@ extern "C" bool EXPORT SMSDriverSend(const TCHAR *pszPhoneNumber, const TCHAR *p
                conn->setCommandTimeout(m_timeout);
       if (conn->connect())
       {
-                       const TCHAR *argv[2] = { pszPhoneNumber, pszText };
-                       UINT32 rcc = conn->execAction(_T("SMS.Send"), 2, argv);
+                       StringList list;
+                       list.add(pszPhoneNumber);
+         list.add(pszText);
+                       UINT32 rcc = conn->execAction(_T("SMS.Send"), list);
                        nxlog_debug(4, _T("nxagent.sms: agent action execution result: %d (%s)"), rcc, AgentErrorCodeToText(rcc));
          if (rcc == ERR_SUCCESS)
                                bSuccess = true;
index faf3e88..50a52e0 100644 (file)
@@ -4,6 +4,7 @@ import java.util.Map;
 import org.netxms.base.NXCommon;
 import org.netxms.client.events.Alarm;
 import org.netxms.client.objects.AbstractNode;
+import org.netxms.client.objecttools.ObjectContextBase;
 import org.netxms.ui.eclipse.console.Messages;
 import org.netxms.ui.eclipse.console.resources.StatusDisplayInfo;
 import org.netxms.ui.eclipse.shared.ConsoleSharedData;
@@ -11,57 +12,14 @@ import org.netxms.ui.eclipse.shared.ConsoleSharedData;
 /**
  * Class to hold information about selected node
  */
-public class ObjectContext
-{
-   public AbstractNode object;
-   public Alarm alarm;
-   
+public class ObjectContext extends ObjectContextBase
+{   
    public ObjectContext(AbstractNode object, Alarm alarm)
    {
-      this.object = object;
-      this.alarm = alarm;
+      super(object, alarm);
    }
 
-   /* (non-Javadoc)
-    * @see java.lang.Object#hashCode()
-    */
-   @Override
-   public int hashCode()
-   {
-      final int prime = 31;
-      int result = 1;
-      result = prime * result + ((alarm == null) ? 0 : alarm.hashCode());
-      result = prime * result + ((object == null) ? 0 : object.hashCode());
-      return result;
-   }
-
-   /* (non-Javadoc)
-    * @see java.lang.Object#equals(java.lang.Object)
-    */
-   @Override
-   public boolean equals(Object obj)
-   {
-      if (this == obj)
-         return true;
-      if (obj == null)
-         return false;
-      if (getClass() != obj.getClass())
-         return false;
-      ObjectContext other = (ObjectContext)obj;
-      if ((other.object == null) || (this.object == null))
-         return (other.object == null) && (this.object == null);
-      return other.object.getObjectId() == this.object.getObjectId();
-   }
-   
-   /**
-    * Substitute macros in string
-    * 
-    * @param s
-    * @param node
-    * @param inputValues 
-    * @return
-    */
-   public String substituteMacros(String s, Map<String, String> inputValues)
+   public String substituteMacrosForMultiNodes(String s, Map<String, String> inputValues)
    {
       StringBuilder sb = new StringBuilder();
       
@@ -77,57 +35,19 @@ public class ObjectContext
             switch(src[i])
             {
                case 'a':
-                  sb.append((object != null) ? object.getPrimaryIP().getHostAddress() : Messages.get().ObjectContext_MultipleNodes);
-                  break;
-               case 'A':   // alarm message
-                  if (alarm != null)
-                     sb.append(alarm.getMessage());
-                  break;
-               case 'c':
-                  if (alarm != null)
-                     sb.append(alarm.getSourceEventCode());
+                  sb.append(Messages.get().ObjectContext_MultipleNodes);
                   break;
                case 'g':
-                  sb.append((object != null) ? object.getGuid().toString() : Messages.get().ObjectContext_MultipleNodes);
+                  sb.append(Messages.get().ObjectContext_MultipleNodes);
                   break;
                case 'i':
-                  sb.append((object != null) ? String.format("0x%08X", object.getObjectId()) : Messages.get().ObjectContext_MultipleNodes); //$NON-NLS-1$
+                  sb.append(Messages.get().ObjectContext_MultipleNodes); 
                   break;
                case 'I':
-                  sb.append((object != null) ? Long.toString(object.getObjectId()) : Messages.get().ObjectContext_MultipleNodes);
-                  break;
-               case 'm':   // alarm message
-                  if (alarm != null)
-                     sb.append(alarm.getMessage());
+                  sb.append(Messages.get().ObjectContext_MultipleNodes);
                   break;
                case 'n':
-                  sb.append((object != null) ? object.getObjectName() : Messages.get().ObjectContext_MultipleNodes);
-                  break;
-               case 'N':
-                  if (alarm != null)
-                     sb.append(ConsoleSharedData.getSession().getEventName(alarm.getSourceEventCode()));
-                  break;
-               case 's':
-                  if (alarm != null)
-                     sb.append(alarm.getCurrentSeverity());
-                  break;
-               case 'S':
-                  if (alarm != null)
-                     sb.append(StatusDisplayInfo.getStatusText(alarm.getCurrentSeverity()));
-                  break;
-               case 'U':
-                  sb.append(ConsoleSharedData.getSession().getUserName());
-                  break;
-               case 'v':
-                  sb.append(NXCommon.VERSION);
-                  break;
-               case 'y':   // alarm state
-                  if (alarm != null)
-                     sb.append(alarm.getState());
-                  break;
-               case 'Y':   // alarm ID
-                  if (alarm != null)
-                     sb.append(alarm.getId());
+                  sb.append(Messages.get().ObjectContext_MultipleNodes);
                   break;
                case '%':
                   sb.append('%');
@@ -174,4 +94,14 @@ public class ObjectContext
       
       return sb.toString();
    }
-}
\ No newline at end of file
+   
+   /**
+    * Returns alarm id or 0 if alarm is not set
+    * 
+    * @return Context alarm id or 0 if alarm is not set
+    */
+   public long getAlarmId()
+   {
+      return alarm != null ? alarm.getId() : 0;
+   }
+}
index 9e25a56..865e938 100644 (file)
@@ -71,6 +71,7 @@ public class AgentFileViewer extends ViewPart
    private Action actionCopy;
    private Action actionSelectAll;
    private Action actionFind;
+   private AbstractObject object;
        
        /* (non-Javadoc)
         * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
@@ -86,7 +87,7 @@ public class AgentFileViewer extends ViewPart
                        throw new PartInitException("Internal error"); //$NON-NLS-1$
                
                nodeId = Long.parseLong(parts[0]);
-               AbstractObject object = session.findObjectById(nodeId);
+               object = session.findObjectById(nodeId);
                if ((object == null) || (object.getObjectClass() != AbstractObject.OBJECT_NODE))
                        throw new PartInitException(Messages.get().FileViewer_InvalidObjectID);
                
@@ -350,6 +351,17 @@ public class AgentFileViewer extends ViewPart
           {
              view.viewer.startTracking(nodeId, file.getId(), file.getRemoteName());
           }
+          view.updatePartName(file.getRemoteName()); //$NON-NLS-1$
           return true;
        }
+
+       /**
+        * Updates part view name after expanded name received
+        * @param remoteName
+        */
+   private void updatePartName(String remoteName)
+   {
+      remoteFileName = remoteName;
+      setPartName(object.getObjectName() + ": " + remoteFileName); //$NON-NLS-1$      
+   }
 }
index 7ca28ed..e535954 100644 (file)
@@ -57,7 +57,7 @@ import org.netxms.ui.eclipse.tools.MessageDialogHelper;
  * Executor for object tool
  */
 public final class ObjectToolExecutor
-{
+{   
    /**
     * Private constructor to forbid instantiation 
     */
@@ -132,39 +132,64 @@ public final class ObjectToolExecutor
       {
          inputValues = new HashMap<String, String>(0);
       }
-      
-      if ((tool.getFlags() & ObjectTool.ASK_CONFIRMATION) != 0)
-      {
-         String message = tool.getConfirmationText();
-         if (nodes.size() == 1)
-         {
-            ObjectContext node = nodes.iterator().next();
-            message = node.substituteMacros(message, new HashMap<String, String>(0));
-         }
-         else
-         {
-            message = new ObjectContext(null, null).substituteMacros(message, new HashMap<String, String>(0));
-         }
-         if (!MessageDialogHelper.openQuestion(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), 
-               Messages.get().ObjectToolsDynamicMenu_ConfirmExec, message))
-            return;
-      }
-      
-      // Check if password validation needed
-      boolean validationNeeded = false;
-      for(int i = 0; i < fields.length; i++)
-         if (fields[i].getOptions().validatePassword)
-         {
-            validationNeeded = true;
-            break;
-         }
-      
-      if (validationNeeded)
-      {
-         final NXCSession session = ConsoleSharedData.getSession();
-         new ConsoleJob(Messages.get().ObjectToolExecutor_JobName, null, Activator.PLUGIN_ID, null) {
-            @Override
-            protected void runInternal(IProgressMonitor monitor) throws Exception
+      final NXCSession session = ConsoleSharedData.getSession();
+      new ConsoleJob(Messages.get().ObjectToolExecutor_JobName, null, Activator.PLUGIN_ID, null) {
+         @Override
+         protected void runInternal(IProgressMonitor monitor) throws Exception
+         {      
+            List<String> expandedText = null;
+            
+            if ((tool.getFlags() & ObjectTool.ASK_CONFIRMATION) != 0)
+            {
+               String message = tool.getConfirmationText();
+               if (nodes.size() == 1)
+               {
+                  //Expand message and action for 1 node, otherwise expansion occurs after confirmation
+                  List<String> textToExpand = new ArrayList<String>();
+                  textToExpand.add(tool.getConfirmationText());
+                  if(tool.getToolType() == ObjectTool.TYPE_URL || tool.getToolType() == ObjectTool.TYPE_LOCAL_COMMAND)
+                  {
+                     textToExpand.add(tool.getData());
+                  }
+                  ObjectContext node = nodes.iterator().next();
+                  expandedText = session.substitureMacross(node, textToExpand, inputValues);
+                  
+                  message = expandedText.remove(0);                  
+               }
+               else
+               {
+                  ObjectContext node = nodes.iterator().next();
+                  message = node.substituteMacrosForMultiNodes(message, inputValues);
+               }
+               
+               ConfirmationRunnable runnable = new ConfirmationRunnable(message);
+               getDisplay().syncExec(runnable);
+               if(!runnable.isConfirmed())
+                  return;
+
+               if(tool.getToolType() == ObjectTool.TYPE_URL || tool.getToolType() == ObjectTool.TYPE_LOCAL_COMMAND)
+               {
+                  expandedText = session.substitureMacross(nodes.toArray(new ObjectContext[nodes.size()]), tool.getData(), inputValues);
+               }
+            }
+            else
+            {
+               if(tool.getToolType() == ObjectTool.TYPE_URL || tool.getToolType() == ObjectTool.TYPE_LOCAL_COMMAND)
+               {
+                  expandedText = session.substitureMacross(nodes.toArray(new ObjectContext[nodes.size()]), tool.getData(), inputValues);
+               }
+            }
+            
+            // Check if password validation needed
+            boolean validationNeeded = false;
+            for(int i = 0; i < fields.length; i++)
+               if (fields[i].getOptions().validatePassword)
+               {
+                  validationNeeded = true;
+                  break;
+               }
+            
+            if (validationNeeded)
             {
                for(int i = 0; i < fields.length; i++)
                {
@@ -186,29 +211,67 @@ public final class ObjectToolExecutor
                      }
                   }
                }
-               
-               runInUIThread(new Runnable() {
-                  @Override
-                  public void run()
-                  {
-                     for(ObjectContext n : nodes)
-                        executeOnNode(n, tool, inputValues);
-                  }
-               });
             }
             
+            int i = 0;
+            for(final ObjectContext n : nodes)
+            {
+               if(tool.getToolType() == ObjectTool.TYPE_URL || tool.getToolType() == ObjectTool.TYPE_LOCAL_COMMAND)
+               {
+                  final String tmp = expandedText.get(i++);
+                  getDisplay().syncExec(new Runnable() {
+                     
+                     @Override
+                     public void run()
+                     {
+                        executeOnNode(n, tool, inputValues, tmp);
+                     }
+                  });
+               }
+               else
+               {
+                  getDisplay().syncExec(new Runnable() {
+                     
+                     @Override
+                     public void run()
+                     {
+                        executeOnNode(n, tool, inputValues, null);
+                     }
+                  });                  
+               }
+            }
+         }
+         
+         @Override
+         protected String getErrorMessage()
+         {
+            return Messages.get().ObjectToolExecutor_PasswordValidationFailed;
+         }
+         
+         class ConfirmationRunnable implements Runnable
+         {
+            private boolean confirmed;
+            private String message;
+
+            public ConfirmationRunnable(String message)
+            {
+               this.message = message;
+            }
+
             @Override
-            protected String getErrorMessage()
+            public void run()
             {
-               return Messages.get().ObjectToolExecutor_PasswordValidationFailed;
+               confirmed = MessageDialogHelper.openQuestion(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), 
+                     Messages.get().ObjectToolsDynamicMenu_ConfirmExec, message);
             }
-         }.start();
-      }
-      else
-      {
-         for(ObjectContext n : nodes)
-            executeOnNode(n, tool, inputValues);
-      }
+            
+            boolean isConfirmed()
+            {
+               return confirmed;
+            }
+         }         
+         
+      }.start();
    }
    
    /**
@@ -232,7 +295,7 @@ public final class ObjectToolExecutor
     * @param tool
     * @param inputValues 
     */
-   private static void executeOnNode(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues)
+   private static void executeOnNode(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues, String expandedValue)
    {
       switch(tool.getToolType())
       {
@@ -256,7 +319,7 @@ public final class ObjectToolExecutor
             executeTableTool(node, tool);
             break;
          case ObjectTool.TYPE_URL:
-            openURL(node, tool, inputValues);
+            openURL(node, tool, inputValues, expandedValue);
             break;
       }
    }
@@ -284,99 +347,13 @@ public final class ObjectToolExecutor
    }
    
    /**
-    * Split command line into tokens
-    *  
-    * @param input
-    * @return
-    */
-   private static String[] splitCommandLine(String input)
-   {
-      char[] in = input.toCharArray();
-      List<String> args = new ArrayList<String>();
-      
-      StringBuilder sb = new StringBuilder();
-      int state = 0;
-      for(char c : in)
-      {
-         switch(state)
-         {
-            case 0: // normal
-               if (Character.isSpaceChar(c))
-               {
-                  args.add(sb.toString());
-                  sb = new StringBuilder();
-                  state = 3;
-               }
-               else if (c == '"')
-               {
-                  state = 1;
-               }
-               else if (c == '\'')
-               {
-                  state = 2;
-               }
-               else
-               {
-                  sb.append(c);
-               }
-               break;
-            case 1: // double quoted string
-               if (c == '"')
-               {
-                  state = 0;
-               }
-               else
-               {
-                  sb.append(c);
-               }
-               break;
-            case 2: // single quoted string
-               if (c == '\'')
-               {
-                  state = 0;
-               }
-               else
-               {
-                  sb.append(c);
-               }
-               break;
-            case 3: // skip
-               if (!Character.isSpaceChar(c))
-               {
-                  if (c == '"')
-                  {
-                     state = 1;
-                  }
-                  else if (c == '\'')
-                  {
-                     state = 2;
-                  }
-                  else
-                  {
-                     sb.append(c);
-                     state = 0;
-                  }
-               }
-               break;
-         }
-      }
-      if (state != 3)
-         args.add(sb.toString());
-      
-      return args.toArray(new String[args.size()]);
-   }
-
-   /**
     * @param node
     * @param tool
     * @param inputValues 
     */
-   private static void executeAgentAction(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues)
+   private static void executeAgentAction(final ObjectContext node, final ObjectTool tool, final Map<String, String> inputValues)
    {
       final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
-      String[] parts = splitCommandLine(node.substituteMacros(tool.getData(), inputValues));
-      final String action = parts[0];
-      final String[] args = Arrays.copyOfRange(parts, 1, parts.length);
       
       if ((tool.getFlags() & ObjectTool.GENERATES_OUTPUT) == 0)
       {      
@@ -390,7 +367,7 @@ public final class ObjectToolExecutor
             @Override
             protected void runInternal(IProgressMonitor monitor) throws Exception
             {
-               session.executeAction(node.object.getObjectId(), action, args);
+               final String action = session.executeActionWithExpansion(node.object.getObjectId(), node.getAlarmId(), tool.getData(), inputValues);
                runInUIThread(new Runnable() {
                   @Override
                   public void run()
@@ -408,7 +385,7 @@ public final class ObjectToolExecutor
          try
          {
             AgentActionResults view = (AgentActionResults)window.getActivePage().showView(AgentActionResults.ID, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
-            view.executeAction(action, args);
+            view.executeAction(tool.getData(), node.getAlarmId(), inputValues);
          }
          catch(Exception e)
          {
@@ -482,7 +459,7 @@ public final class ObjectToolExecutor
             @Override
             protected void runInternal(IProgressMonitor monitor) throws Exception
             {
-               session.executeLibraryScript(node.object.getObjectId(), tool.getData(), inputValues, null);
+               session.executeLibraryScript(node.object.getObjectId(), node.getAlarmId(), tool.getData(), inputValues, null);
                runInUIThread(new Runnable() {
                   @Override
                   public void run()
@@ -506,7 +483,7 @@ public final class ObjectToolExecutor
          try
          {
             ServerScriptResults view = (ServerScriptResults)window.getActivePage().showView(ServerScriptResults.ID, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
-            view.executeScript(tool.getData(), inputValues);
+            view.executeScript(tool.getData(), node.getAlarmId(), inputValues);
          }
          catch(Exception e)
          {
@@ -521,12 +498,12 @@ public final class ObjectToolExecutor
     * @param inputValues 
     * @param inputValues 
     */
-   private static void executeFileDownload(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues)
+   private static void executeFileDownload(final ObjectContext node, final ObjectTool tool, final Map<String, String> inputValues)
    {
       final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
       String[] parameters = tool.getData().split("\u007F"); //$NON-NLS-1$
       
-      final String fileName = node.substituteMacros(parameters[0], inputValues);
+      final String fileName = parameters[0];
       final int maxFileSize = Integer.parseInt(parameters[1]);
       final boolean follow = parameters[2].equals("true") ? true : false; //$NON-NLS-1$
       
@@ -540,7 +517,7 @@ public final class ObjectToolExecutor
          @Override
          protected void runInternal(final IProgressMonitor monitor) throws Exception
          {
-            final AgentFileData file = session.downloadFileFromAgent(node.object.getObjectId(), fileName, maxFileSize, follow, new ProgressListener() {
+            final AgentFileData file = session.downloadFileFromAgent(node.object.getObjectId(), fileName, maxFileSize, follow, inputValues, node.getAlarmId(), new ProgressListener() {
                @Override
                public void setTotalWorkAmount(long workTotal)
                {
@@ -596,9 +573,8 @@ public final class ObjectToolExecutor
     * @param tool
     * @param inputValues 
     */
-   private static void openURL(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues)
+   private static void openURL(final ObjectContext node, final ObjectTool tool, Map<String, String> inputValues, String url)
    {
-      final String url = node.substituteMacros(tool.getData(), inputValues);
       final UrlLauncher launcher = RWT.getClient().getService(UrlLauncher.class);
       launcher.openURL(url);
    }
index 5fb619b..f8b88b0 100644 (file)
@@ -19,6 +19,7 @@
 package org.netxms.ui.eclipse.objecttools.views;
 
 import java.io.IOException;
+import java.util.Map;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IMenuManager;
@@ -41,9 +42,10 @@ public class AgentActionResults extends AbstractCommandResults implements TextOu
    public static final String ID = "org.netxms.ui.eclipse.objecttools.views.AgentActionResults"; //$NON-NLS-1$
 
    private IOConsoleOutputStream out;
-   private String lastAction = null;
-   private String[] lastArgs = null;
    private Action actionRestart;
+   private String executionString;
+   private long alarmId;
+   private Map<String, String> inputValues;
    
    /**
     * Create actions
@@ -56,7 +58,7 @@ public class AgentActionResults extends AbstractCommandResults implements TextOu
          @Override
          public void run()
          {
-            executeAction(lastAction, lastArgs);
+            executeAction(executionString, alarmId, inputValues);
          }
       };
       actionRestart.setEnabled(false);
@@ -101,13 +103,14 @@ public class AgentActionResults extends AbstractCommandResults implements TextOu
    /**
     * @param action
     */
-   public void executeAction(final String action, final String[] args)
+   public void executeAction(final String executionString, final long alarmId, final Map<String, String> inputValues)
    {
       actionRestart.setEnabled(false);
       final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
       out = console.newOutputStream();
-      lastAction = action;
-      lastArgs = args;
+      this.alarmId = alarmId;
+      this.executionString = executionString;
+      this.inputValues = inputValues;
       ConsoleJob job = new ConsoleJob(String.format(Messages.get().ObjectToolsDynamicMenu_ExecuteOnNode, session.getObjectName(nodeId)), null, Activator.PLUGIN_ID, null) {
          @Override
          protected String getErrorMessage()
@@ -120,8 +123,8 @@ public class AgentActionResults extends AbstractCommandResults implements TextOu
          {
             try
             {
-               session.executeAction(nodeId, action, args, true, AgentActionResults.this, null);
-               out.write(Messages.get(getDisplay()).LocalCommandResults_Terminated);
+               session.executeActionWithExpansion(nodeId, alarmId, executionString, true, inputValues, AgentActionResults.this, null);
+               out.write(Messages.get().LocalCommandResults_Terminated);
             }
             finally
             {
index 20f9454..88c9cd6 100644 (file)
@@ -45,6 +45,7 @@ public class ServerScriptResults extends AbstractCommandResults implements TextO
    private String lastScript = null;
    private Action actionRestart;
    private Map<String, String> lastInputValues = null;
+   private long alarmId;
    
    /**
     * Create actions
@@ -57,7 +58,7 @@ public class ServerScriptResults extends AbstractCommandResults implements TextO
          @Override
          public void run()
          {
-            executeScript(lastScript, lastInputValues);
+            executeScript(lastScript, alarmId, lastInputValues);
          }
       };
       actionRestart.setEnabled(false);
@@ -101,14 +102,16 @@ public class ServerScriptResults extends AbstractCommandResults implements TextO
    
    /**
     * @param script
+    * @param alarmId 
     * @param inputValues 
     */
-   public void executeScript(final String script, final Map<String, String> inputValues)
+   public void executeScript(final String script, final long alarmId, final Map<String, String> inputValues)
    {
       actionRestart.setEnabled(false);
       final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
       out = console.newOutputStream();
       lastScript = script;
+      this.alarmId = alarmId;
       lastInputValues = inputValues;
       ConsoleJob job = new ConsoleJob(String.format(Messages.get().ObjectToolsDynamicMenu_ExecuteOnNode, session.getObjectName(nodeId)), null, Activator.PLUGIN_ID, null) {
          @Override
@@ -122,7 +125,7 @@ public class ServerScriptResults extends AbstractCommandResults implements TextO
          {
             try
             {
-               session.executeLibraryScript(nodeId, script, inputValues, ServerScriptResults.this);
+               session.executeLibraryScript(nodeId, alarmId, script, inputValues, ServerScriptResults.this);
             }
             finally
             {
index 1f26d35..041c43d 100644 (file)
@@ -109,6 +109,7 @@ public class TemplateGraphDynamicMenu extends ContributionItem implements IWorkb
                public void widgetSelected(SelectionEvent e)
                {
                   final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
+                  final GraphSettings gs = (GraphSettings)item.getData();
                   ConsoleJob job = new ConsoleJob("Get last values of " + node.getObjectName() , null, Activator.PLUGIN_ID, null) {
                      @Override
                      protected String getErrorMessage()
@@ -121,13 +122,7 @@ public class TemplateGraphDynamicMenu extends ContributionItem implements IWorkb
                      {
                         
                         final DciValue[] data = session.getLastValues(node.getObjectId());
-                        runInUIThread(new Runnable() {
-                           @Override
-                           public void run()
-                           {
-                              GraphTemplateCache.execute(node, (GraphSettings)item.getData(), data);
-                           }
-                        });
+                        GraphTemplateCache.execute(node, gs, data, getDisplay());
                      }
                   };
                   job.setUser(false);
index f36f31f..8533698 100644 (file)
@@ -1,16 +1,20 @@
 package org.netxms.ui.eclipse.perfview.api;
 
+import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.regex.Pattern;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.PlatformUI;
+import org.netxms.client.NXCException;
 import org.netxms.client.NXCSession;
 import org.netxms.client.SessionListener;
 import org.netxms.client.SessionNotification;
@@ -22,6 +26,7 @@ import org.netxms.ui.eclipse.objects.ObjectContext;
 import org.netxms.ui.eclipse.perfview.Activator;
 import org.netxms.ui.eclipse.perfview.Messages;
 import org.netxms.ui.eclipse.perfview.views.HistoricalGraphView;
+import org.netxms.ui.eclipse.shared.ConsoleSharedData;
 import org.netxms.ui.eclipse.tools.MessageDialogHelper;
 
 public class GraphTemplateCache
@@ -149,14 +154,16 @@ public class GraphTemplateCache
       return graphs;
    }
 
-   public static void execute(final AbstractNode node, final GraphSettings data, final DciValue[] values)
+   public static void execute(final AbstractNode node, final GraphSettings data, final DciValue[] values, final Display disp) throws IOException, NXCException
    {
-      ObjectContext ctx = new ObjectContext(node, null);
-      String name = ctx.substituteMacros(data.getTitle(), null);
-      GraphSettings result = new GraphSettings(data, name);
+      final NXCSession session = ConsoleSharedData.getSession();
+      List<String> textsToExpand = new ArrayList<String>();
+      textsToExpand.add(data.getTitle());
+      String name = session.substitureMacross(new ObjectContext(node, null), textsToExpand, new HashMap<String, String>()).get(0); 
+      final GraphSettings result = new GraphSettings(data, name);
       
       ChartDciConfig[] conf = result.getDciList();
-      HashSet<ChartDciConfig> newList = new HashSet<ChartDciConfig>();
+      final HashSet<ChartDciConfig> newList = new HashSet<ChartDciConfig>();
       int foundByDescription = -1;
       int foundDCICount = 0;
       //parse config and compare name as regexp and then compare description
@@ -193,13 +200,27 @@ public class GraphTemplateCache
       }
       if(foundDCICount > 0)
       {
-         result.setDciList(newList.toArray(new ChartDciConfig[newList.size()]));
-         showPredefinedGraph(result, node.getObjectId());
+         disp.syncExec(new Runnable() {
+            
+            @Override
+            public void run()
+            {
+               result.setDciList(newList.toArray(new ChartDciConfig[newList.size()]));
+               showPredefinedGraph(result, node.getObjectId());               
+            }
+         });
       }
       else
       {
-         MessageDialogHelper.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), "Graph creation from template error", "None of template graphs DCI were found on a node.");
-      }
+         disp.syncExec(new Runnable() {
+            
+            @Override
+            public void run()
+            {  
+               MessageDialogHelper.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), "Graph creation from template error", "None of template graphs DCI were found on a node.");
+            }
+         });
+       }
    }
    
    /**