/* Class to manage the Windows Management Instrumentation(WMI).
   It depends on disphelper library (http://disphelper.sourceforge.net)

   Copyright (c) 2006-2023 Pandora FMS.
   Written by Esteban Sanchez.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MAB02111-1307, USA.
*/

#include "pandora_wmi.h"
#include "../pandora_strutils.h"
#include <iostream>
#include <algorithm>
#include <cctype>
#include <sstream>
#include <ctime>
#include <winuser.h>
#include <stdio.h>  

#define INVENTORY_FIELD_SEPARATOR 

using namespace std;
using namespace Pandora_Wmi;

static LPWSTR
getWmiStr (LPCWSTR computer) {
	static WCHAR wmi_str[256];

	wcscpy (wmi_str, L"winmgmts:{impersonationLevel=impersonate}!\\\\");
	
	if (computer) {
		wcsncat (wmi_str, computer, 128);
	} else {
		wcscat (wmi_str, L".");
	}

	wcscat (wmi_str, L"\\root\\cimv2");
	
	return wmi_str;
}

/** 
 * Check if a process is running.
 * 
 * @param process_name Name of the process with extension.
 * 
 * @return Number of instances of the process running.
 */
int
Pandora_Wmi::isProcessRunning (string process_name) {
	CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	string        name;
	int           result = 0;
	string        query;

	query = "SELECT * FROM Win32_Process WHERE Name=\"" + process_name + "\"";
	
	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%T)",
				     query.c_str ()));
	
		FOR_EACH (quickfix, quickfixes, NULL) {
			result++;
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("isProcessRunning error. %s", errstr.c_str ());
	}

	return result;
}

/** 
 * Check if a Windows service is running.
 * 
 * @param service_name Internal name of the service to check.
 * 
 * @retval 1 The service is running
 * @retval 0 The service is stopped
 */
int
Pandora_Wmi::isServiceRunning (string service_name) {
	CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	string        query;
	char         *state;
	string        str_state;
	int           retval = -1;

	query = "SELECT * FROM Win32_Service WHERE Name = \"" + service_name + "\"";

	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%T)",
				     query.c_str ()));
	
		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%s", &state, quickfix,
				    L".State");
			if (retval == -1) {
				str_state = state;
				if (str_state == "Running") {
					retval = 1;
				}
				else {
					retval = 0;
				}
			}
			dhFreeString (state);
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("isServiceRunning error. %s", errstr.c_str ());
	}

	if (retval == -1) {
		pandoraDebug ("Service %s not found.", service_name.c_str ());
		return 0;
	}

	return retval;
}

/** 
 * Get the free space in a logical disk drive.
 * 
 * @param disk_id Disk drive letter (C: for example).
 * 
 * @return Free space amount in MB.
 *
 * @exception Pandora_Wmi_Exception Throwd if an error occured when reading
 *            from WMI database.
 */
unsigned long
Pandora_Wmi::getDiskFreeSpace (string disk_id) {
	CDhInitialize      init;
	CDispPtr           wmi_svc, quickfixes;
	double             free_space = 0;
	string             query;

	query = "SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID = \"" + disk_id + "\"";
	
	struct QFix {
		CDhStringA free_space; 	 
	};
	
	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%T)",
				     query.c_str ()));
	
		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%e", &free_space, quickfix,
				    L".FreeSpace");
			
			// 1048576 = 1024 * 1024
			return (unsigned long) (free_space / 1048576);
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("getDiskFreeSpace error. %s", errstr.c_str ());
	}

	throw Pandora_Wmi_Exception ();
}

/** 
 * Get the free space in a logical disk drive.
 * 
 * @param disk_id Disk drive letter (C: for example).
 * 
 * @return Free space percentage.
 *
 * @exception Pandora_Wmi_Exception Throwd if an error occured when reading
 *            from WMI database.
 */
double
Pandora_Wmi::getDiskFreeSpacePercent (string disk_id) {
	CDhInitialize      init;
	CDispPtr           wmi_svc, quickfixes;
	double             free_space = 0, size = 0;
	string             query;

	query = "SELECT Size, FreeSpace FROM Win32_LogicalDisk WHERE DeviceID = \"" + disk_id + "\"";

	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%T)",
				     query.c_str ()));

		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%e", &free_space, quickfix,
				    L".FreeSpace");
			dhGetValue (L"%e", &size, quickfix,
				    L".Size");

			if (size == 0) {
				return 0;
			}

			return (free_space * 100 / size);
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("getDiskFreeSpace error. %s", errstr.c_str ());
	}

	throw Pandora_Wmi_Exception ();
}

/** 
 * Get the CPU usage percentage in the last minutes.
 * 
 * @param cpu_id CPU identifier.
 * 
 * @return The usage percentage of the CPU.
 *
 * @exception Pandora_Wmi_Exception Throwed if an error occured when reading
 *            from WMI database.
 */
int
Pandora_Wmi::getCpuUsagePercentage (int cpu_id) {
	CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	string        query;
	long          load_percentage, total_load;
	int           total_cpus;
	std::ostringstream stm;

    // Select all CPUs
    if (cpu_id < 0) {
	    query = "SELECT * FROM Win32_Processor";
    // Select a single CPUs
    } else {
	    stm << cpu_id;
	    query = "SELECT * FROM Win32_Processor WHERE DeviceID = \"CPU" + stm.str () + "\"";
    }

	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%T)",
				     query.c_str ()));

        total_cpus = 0;
        total_load = 0;
		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%d", &load_percentage, quickfix,
				    L".LoadPercentage");
			// Sanity check
			if (load_percentage >= 0 && load_percentage <= 100) {
				total_cpus++;
				total_load += load_percentage;
			}
		} NEXT_THROW (quickfix);

		if (total_cpus == 0) {
            return 0;
        }

		return total_load / total_cpus;
	} catch (string errstr) {
		pandoraLog ("getCpuUsagePercentage error. %s", errstr.c_str ());
	}

	throw Pandora_Wmi_Exception ();
}

/** 
 * Get the amount of free memory in the system
 *
 * @return The amount of free memory in MB.
 * @exception Pandora_Wmi_Exception Throwd if an error occured when reading
 *            from WMI database.
 */
long
Pandora_Wmi::getFreememory () {
	CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	long          free_memory;

	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT * FROM Win32_PerfRawData_PerfOS_Memory "));
	
		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%d", &free_memory, quickfix,
				    L".AvailableMBytes");
		
			return free_memory;
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("getFreememory error. %s", errstr.c_str ());
	}

	throw Pandora_Wmi_Exception ();	
}

/** 
 * Get the percentage of free memory in the system
 *
 * @return The percentage of free memory.
 * @exception Pandora_Wmi_Exception Throwd if an error occured when reading
 *            from WMI database.
 */
long
Pandora_Wmi::getFreememoryPercent () {
	CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	double        free_memory, total_memory;

	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT FreePhysicalMemory, TotalVisibleMemorySize FROM Win32_OperatingSystem "));
	
		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%e", &free_memory, quickfix,
				    L".FreePhysicalMemory");

			dhGetValue (L"%e", &total_memory, quickfix,
				    L".TotalVisibleMemorySize");

			if (total_memory == 0) {
                return 0;
            }
            
            return (long) (free_memory * 100.0 / total_memory);
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("getFreememory error. %s", errstr.c_str ());
	}

	throw Pandora_Wmi_Exception ();	
}

/**
 * Get the name of the operating system.
 * 
 * @return The name of the operating system.
 */
string
Pandora_Wmi::getOSName () {
	CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	char         *name = NULL;
	string        ret;

	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT * FROM Win32_OperatingSystem "));
	
		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%s", &name, quickfix,
				    L".Caption");
			
			if (name != NULL) {
				// Remove the (R) character.
				for (int i = 0; i < strlen(name); i++) {
					if ((unsigned char)name[i] == 0xAE) {
						name[i] = ' ';
					}
				}
				ret = name;
				dhFreeString (name);
			}
		
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("getOSName error. %s", errstr.c_str ());
	}

	return ret;
}

/** 
 * Get the version of the operating system.
 * 
 * @return The version of the operaing system.
 */
string
Pandora_Wmi::getOSVersion () {
	CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	char         *version = NULL;
	string        ret;

	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT * FROM Win32_OperatingSystem "));
	
		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%s", &version, quickfix,
				    L".CSDVersion");
		
			if (version != NULL) {
				// Remove the (R) character.
				for (int i = 0; i < strlen(version); i++) {
					if ((unsigned char)version[i] == 0xAE) {
						version[i] = ' ';
					}
				}
				ret = version;
				dhFreeString (version);
			}
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("getOSVersion error. %s", errstr.c_str ());
	}

	return ret;
}

/** 
 * Get the build of the operating system.
 * 
 * @return The build of the operating system.
 */
string
Pandora_Wmi::getOSBuild () {
	CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	char         *build = NULL;
	string        ret;
	
	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT * FROM Win32_OperatingSystem "));

		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%s", &build, quickfix,
				    L".Version");
			
			ret = build;
			dhFreeString (build);
			
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("getOSBuild error. %s", errstr.c_str ());
	}
	
	return ret;
}

/** 
 * Get the system name of the operating system.
 * 
 * @return The system name of the operating system.
 */
string
Pandora_Wmi::getSystemName () {
	CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	char         *name = NULL;
	string        ret;
	
	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT * FROM Win32_OperatingSystem "));

		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%s", &name, quickfix,
				    L".CSName");
			
			ret = name;
			dhFreeString (name);
			
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("getSystemName error. %s", errstr.c_str ());
	}
	
	return ret;
}

/**
 * Runs a program in a new process.
 *
 * @param command Command to run, with parameters
 * @param flags Process creation flags
 */
bool
Pandora_Wmi::runProgram (string command, DWORD flags, BOOL user_session) {
	PROCESS_INFORMATION process_info;
	STARTUPINFO         startup_info;
	bool                success;
	char               *cmd;
	
	if (command == "")
		return false;
	
	ZeroMemory (&startup_info, sizeof (startup_info));
	startup_info.cb = sizeof (startup_info);
	ZeroMemory (&process_info, sizeof (process_info));
	
	if (user_session) {
		DWORD sessionId = WTSGetActiveConsoleSessionId();
		startup_info.cb = sizeof(STARTUPINFO);
		startup_info.hStdError = 0;
		startup_info.hStdInput = 0;
		startup_info.hStdOutput = 0;
		if (
			startup_info.hStdError != 0
			|| startup_info.hStdInput != 0
			|| startup_info.hStdOutput != 0
		) {
			startup_info.dwFlags |= STARTF_USESTDHANDLES;
		}

		HANDLE procHandle = GetCurrentProcess();
		HANDLE token, userToken;

		// Tray to open the process
		if (OpenProcessToken(procHandle, TOKEN_DUPLICATE, &token) == 0) {
			pandoraDebug ("Open Process Token fails with error %d.", GetLastError());
			return false;
		}

		// Duplicate token
		if (DuplicateTokenEx(token,
			MAXIMUM_ALLOWED,
			0,
			SecurityImpersonation,
			TokenPrimary,
			&userToken) == 0) {
			pandoraDebug ("Duplicate token fails with error %d.", GetLastError());
			return false;
		}

		// Set Token Information
		if (SetTokenInformation(userToken,
			(TOKEN_INFORMATION_CLASS)TokenSessionId,
			&sessionId,
			sizeof(sessionId)) == 0) {
			// Error 1314 will be thrown if agent is not running as service.
			if (GetLastError() != 1314) {
				pandoraDebug ("Set token information fails with error %d.", GetLastError());
				return false;
			}
		}

		LPSTR command_exec = (LPSTR)command.c_str();

		// Create Process As User
		// Changed inherit and command
		success = CreateProcessAsUser(
				userToken,
				0,
				command_exec,
				0,
				0,
				FALSE,
				flags,
				0,
				NULL,
				&startup_info,
				&process_info);
	} else {
		pandoraDebug ("Start process \"%s\".", command.c_str ());
		cmd = strdup (command.c_str ());
		success = CreateProcess (NULL, cmd, NULL, NULL, FALSE, flags,
					NULL, NULL, &startup_info, &process_info);
		pandoraFree (cmd);
	}
	
	if (success) {
		pandoraDebug ("The process \"%s\" was started.", command.c_str ());
		return true;
	}
	pandoraLog ("Could not start process \"%s\". Error %d", command.c_str (),
		    GetLastError());
	return false;
}

/**
 * Start a Windows service.
 *
 * @param service_name Service internal name to start.
 *
 * @retval true If the service started.
 * @retval false If the service could not start. A log message is created.
 */
bool
Pandora_Wmi::startService (string service_name) {
	SC_HANDLE manager, service;
	bool      success;
	
	manager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (manager == NULL) {
		pandoraLog ("Could not access to service \"%s\" to start.",
			    service_name.c_str ());
		return false;
	}
	
	service = OpenService (manager, service_name.c_str (), GENERIC_EXECUTE);
	if (service == NULL) {
		pandoraLog ("Could not access to service \"%s\" to start.",
			    service_name.c_str ());
		CloseServiceHandle (manager);
		return false;
	}
	
	success = StartService (service, 0, NULL);
	
	CloseServiceHandle (service);
	CloseServiceHandle (manager);
	
	if (! success) {
		pandoraLog ("Could not start service \"%s\". (Error %d)",
			    service_name.c_str (), GetLastError ());
	}
	
	return success;
}

/**
 * Stop a Windows service.
 *
 * @param service_name Service internal name to stop.
 *
 * @retval true If the service started.
 * @retval false If the service could not stop. A log message is created.
 */
bool
Pandora_Wmi::stopService (string service_name) {
	SC_HANDLE manager, service;
	bool      success;
	SERVICE_STATUS ssStatus; 

	manager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (manager == NULL) {
		pandoraLog ("Could not access to service \"%s\" to stop.",
			    service_name.c_str ());
		return false;
	}
	
	service = OpenService (manager, service_name.c_str (), SERVICE_STOP);
	if (service == NULL) {
		pandoraLog ("Could not access to service \"%s\" to stop.",
			    service_name.c_str ());
		CloseServiceHandle (manager);
		return false;
	}
	
	success = ControlService (service, SERVICE_CONTROL_STOP, &ssStatus);
	
	CloseServiceHandle (service);
	CloseServiceHandle (manager);
	
	if (! success) {
		pandoraLog ("Could not stop service \"%s\". (Error %d)",
			    service_name.c_str (), GetLastError ());
	}
	
	return success;
}

/**
 * Runs a generic WQL query.
 * 
 * @param wmi_query WQL query.
 * @param column Column to retrieve from the query result.
 * @param rows List where the query result will be placed.
 */
void
Pandora_Wmi::runWMIQuery (string wmi_query, string column, list<string> &rows) {
    CDhInitialize init;
	CDispPtr      wmi_svc, quickfixes;
	char         *value = NULL;
    wstring column_w(column.length(), L' ');
    wstring wmi_query_w(wmi_query.length(), L' ');

    // Copy string to wstring.
    std::copy(column.begin(), column.end(), column_w.begin());
    std::copy(wmi_query.begin(), wmi_query.end(), wmi_query_w.begin());

	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &quickfixes, wmi_svc,
				     L".ExecQuery(%S)",
				     wmi_query_w.c_str ()));
		FOR_EACH (quickfix, quickfixes, NULL) {
			dhGetValue (L"%s", &value, quickfix,
				    column_w.c_str ());
			if (value != NULL) {
		  	   rows.push_back (value);
		  	}
			dhFreeString (value);		
		} NEXT_THROW (quickfix);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
}



/**
 * Gets all the sofware installed 
 * 
 * @param rows List where the query result will be placed.
 * @return An int with the number of Results found
 */
int
Pandora_Wmi::getSoftware (list<string> &rows) {
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, software_list = NULL;
	char         *name  = NULL, *version = NULL;
    int          num_objects = 0;
	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		if (wmi_svc == NULL) {
           pandoraLog("Error getting wmi_svc\n");
        }

        dhCheck (dhGetValue (L"%o", &software_list, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT * FROM Win32_Product "));
        
		FOR_EACH (software_item, software_list, NULL) {
            num_objects++;
			dhGetValue (L"%s", &name, software_item,
				    L".Name");
   			if (name != NULL) {
		  	   rows.push_back (name);
            }
            dhFreeString (name);
			dhGetValue (L"%s", &version, software_item,
				    L".Version");            
			if (version != NULL) {
		  	   rows.push_back (version);
		  	}
			dhFreeString (version);		
		} NEXT_THROW (software_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
	return num_objects;
}

/**
 * Gets the information about the CDRom
 * 
 * @param rows List where the query result will be placed.
 * @return An int with the number of Results found
 */
int
Pandora_Wmi::getCdRomInfo (list<string> &rows) {
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, cd_info = NULL;
	char         *name  = NULL, *description = NULL, *drive = NULL;
	string        ret = "";
    int          num_objects = 0;
 	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		if (wmi_svc == NULL) {
           pandoraLog("Error getting wmi_svc\n");
        }

        dhCheck (dhGetValue (L"%o", &cd_info, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT  Name, Description, Drive FROM Win32_CDROMDrive "));
        
		FOR_EACH (cd_info_item, cd_info, NULL) {
            num_objects++;
			dhGetValue (L"%s", &name, cd_info_item,
				    L".Name");
   			if (name != NULL) {
               ret +=  name;		  	 
            }
            ret += inventory_field_separator;
            dhFreeString(name);
			dhGetValue (L"%s", &description, cd_info_item,
				    L".Description");
			if (description != NULL) {
               ret += " ";
		  	   ret += description;   		  	  
		  	}
            ret += inventory_field_separator;
            dhFreeString (description);		
			dhGetValue (L"%s", &drive, cd_info_item,
				    L".Drive");
   			if (drive != NULL) {
               ret += " (";
               ret += drive;
               ret += ")"; 
            }
            rows.push_back(ret);
            ret.clear();
            dhFreeString(drive);
		} NEXT_THROW (cd_info_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
	return num_objects;
}


/**
 * Gets the information about the Video Card
 * 
 * @param rows List where the query result will be placed.
 * @return An int with the number of Results found
 */
int
Pandora_Wmi::getVideoInfo (list<string> &rows){
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, video_info = NULL;
	char         *caption  = NULL, *adapter_RAM = NULL, *video_processor = NULL;
	string        ret = "";
    int          num_objects = 0;
 	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		if (wmi_svc == NULL) {
           pandoraLog("Error getting wmi_svc\n");
        }

        dhCheck (dhGetValue (L"%o", &video_info, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT Caption, AdapterRAM, VideoProcessor FROM Win32_VideoController "));
        
		FOR_EACH (video_info_item, video_info, NULL) {
            num_objects++;
			dhGetValue (L"%s", &caption, video_info_item,
				    L".Caption");
   			if (caption != NULL) {
               ret +=  caption;		  	 
            }
            ret += inventory_field_separator;
            dhFreeString(caption);
			dhGetValue (L"%s", &adapter_RAM, video_info_item,
				    L".AdapterRAM");
			if (adapter_RAM != NULL) {
               double ram_in_mb = atof(adapter_RAM) / 1048576; 
               ostringstream converter;
               converter << ram_in_mb;
		  	   ret += " " + converter.str() + " MBytes";
		  	}
            ret += inventory_field_separator;
            dhFreeString (adapter_RAM);		
			dhGetValue (L"%s", &video_processor, video_info_item,
				    L".VideoProcessor");
   			if (video_processor != NULL) {
               ret += video_processor; 
            }
            rows.push_back(ret);
            ret.clear();
            dhFreeString(video_processor);
		} NEXT_THROW (video_info_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
	return num_objects;
}


/**
 * Gets the information about the Hard Drives
 * 
 * @param rows List where the query result will be placed.
 * @return An int with the number of Results found
 */
int
Pandora_Wmi::getHDsInfo (list<string> &rows) {
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, hd_info = NULL;
	char         *model  = NULL, *system_name = NULL;
    //long          size = 0;
    char * size = NULL;
	string        ret = "";
    int          num_objects = 0;
 	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		if (wmi_svc == NULL) {
           pandoraLog("Error getting wmi_svc\n");
        }

        dhCheck (dhGetValue (L"%o", &hd_info, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT Model, Size, SystemName FROM Win32_DiskDrive "));
        
		FOR_EACH (hd_info_item, hd_info, NULL) {
            num_objects++;
			dhGetValue (L"%s", &model, hd_info_item,
				    L".Model");
   			if (model != NULL) {
               ret +=  model;		  	 
            }
            ret += inventory_field_separator;
            dhFreeString(model);
			dhGetValue (L"%s", &size, hd_info_item,
				    L".Size");
			if (size != NULL) {
               double fsize = atof(size) / 1073741824; 
               ostringstream converter;
               converter << fsize;
		  	   ret += converter.str() + " GBs";   		  	  
		  	}

            ret += inventory_field_separator;
            dhFreeString (size);		
			dhGetValue (L"%s", &system_name, hd_info_item,
				    L".SystemName");
   			if (system_name != NULL) {
               ret += " (";
               ret += system_name;
               ret += ")"; 
            }
            rows.push_back(ret);
            ret.clear();
            dhFreeString(system_name);
		} NEXT_THROW (hd_info_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
	return num_objects;
}

/**
 * Gets the information about the CPUs
 * 
 * @param rows List where the query result will be placed.
 * @return An int with the number of Results found
 */
int
Pandora_Wmi::getCPUsInfo (list<string> &rows){
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, cpu_info = NULL;
	char         *name  = NULL, *speed = NULL, *description = NULL;
	// Note speed is an uint32 but it works ok as char *
	string        ret = "", mhz =" MHz";
    int          num_objects = 0;
 	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		if (wmi_svc == NULL) {
           pandoraLog("Error getting wmi_svc\n");
        }

        dhCheck (dhGetValue (L"%o", &cpu_info, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT Name, MaxClockSpeed, Description FROM Win32_Processor "));
        
		FOR_EACH (cpu_info_item, cpu_info, NULL) {
            num_objects++;
			dhGetValue (L"%s", &name, cpu_info_item,
				    L".Name");
   			if (name != NULL) {
               ret +=  name;		  	 
            }
            ret += inventory_field_separator;
            dhFreeString(name);
			dhGetValue (L"%s", &speed, cpu_info_item,
				    L".MaxClockSpeed");
			if (speed != NULL) {
		  	   ret += speed + mhz;   		  	  
		  	}
            ret += inventory_field_separator;
            dhFreeString (speed);		
			dhGetValue (L"%s", &description, cpu_info_item,
				    L".Description");
   			if (description != NULL) {
               ret += description; 
            }
            rows.push_back(ret);
            ret.clear();
            dhFreeString(description);
		} NEXT_THROW (cpu_info_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
	return num_objects;
}
/**
 * Gets a string with the IPs from an array of string of IPs 
 * 
 * This is a helper to extract the IPs from the result of the WMI query at 
 * getNICsInfo, that returns a **string with the IPs so this function gets this
 * parameter and retunrs a string with all  the IPs separated by ' , '
 * @param ip_array array of Strings of IPs as returned by the query on getNICsInfo
 * @return A string with the IPs separated by ' , ' 
 */
string 
getIPs(VARIANT *ip_array){
    UINT i; 
    VARIANT *pvArray; 
    string ret = "";
    if (V_VT(ip_array) == (VT_ARRAY | VT_VARIANT)) {
       if (FAILED(SafeArrayAccessData(V_ARRAY(ip_array), (void **) &pvArray))) {
          ret += "";
       }
       int num_ips  = V_ARRAY(ip_array)->rgsabound[0].cElements;
       for (i = 0;i < num_ips;i++) { 
           if (V_VT(&pvArray[i]) == VT_BSTR) { 
	   	     if (i > 0) {
		   	   ret += " , ";
	   	     }
             LPSTR szStringA;                     
             ret +=  Pandora_Strutils::strUnicodeToAnsi( V_BSTR(&pvArray[i]));             
           } 
       } 
       SafeArrayUnaccessData(V_ARRAY(ip_array));                                  
    }
	return ret;
}
/**
 * Gets the information about the Network Adapters
 * 
 * @param rows List where the query result will be placed. each row has the
 * 		  Caption, MACAddress and IPAddress separated by ; 
 * @return An int with the number of Results found
 */
int
Pandora_Wmi::getNICsInfo (list<string> &rows) {
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, nic_info = NULL;
    VARIANT ip_addresses;
	char         *caption  = NULL, *mac_address = NULL;
	string        ret = "";
    int          num_objects = 0;
 	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		if (wmi_svc == NULL) {
           pandoraLog("Error getting wmi_svc\n");
        }

        dhCheck (dhGetValue (L"%o", &nic_info, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT Caption, MACAddress, IPAddress FROM Win32_NetworkAdapterConfiguration "));
        
		FOR_EACH (nic_info_item, nic_info, NULL) {
            num_objects++;
			dhGetValue (L"%s", &caption, nic_info_item,
				    L".Caption");
   			if (caption != NULL) {
               ret +=  caption;		  	 
            }
            dhFreeString(caption);
            ret += inventory_field_separator;
			dhGetValue (L"%s", &mac_address, nic_info_item,
				    L".MACAddress");
			if (mac_address != NULL) {
		  	   ret += mac_address;   		  	  
		  	}            
            dhFreeString (mac_address);		
            ret += inventory_field_separator;
		    dhGetValue (L"%v", &ip_addresses, nic_info_item,
				    L".IPAddress");
		    if (&ip_addresses != NULL)
		    {
               ret += getIPs(&ip_addresses);
            }
            VariantClear(&ip_addresses);
            rows.push_back(ret);
            ret.clear();
		} NEXT_THROW (nic_info_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
	return num_objects;
}

/**
 * Gets the information about the Patch Information
 * 
 * @param rows List where the query result will be placed. each row has the
 * 		  HotFixID, Description and FixComments separated by ;
 * @return An int with the number of Results found
 */
int
Pandora_Wmi::getPatchInfo (list<string> &rows) {
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, patch_info = NULL;
	char         *hot_fix_id  = NULL, *description = NULL, *comments = NULL, *service_pack = NULL;
	string        ret = "";
    int          num_objects = 0;
 	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		if (wmi_svc == NULL) {
           pandoraLog("Error getting wmi_svc\n");
        }

        dhCheck (dhGetValue (L"%o", &patch_info, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT HotFixID, Description, FixComments, ServicePackInEffect FROM Win32_QuickFixEngineering "));
        
		FOR_EACH (patch_info_item, patch_info, NULL) {
            num_objects++;
			dhGetValue (L"%s", &hot_fix_id, patch_info_item,
				    L".HotFixID");
   			if (hot_fix_id != NULL) {
               ret +=  hot_fix_id;		  	 
            }
            ret += inventory_field_separator;
            dhFreeString(hot_fix_id);
			dhGetValue (L"%s", &description, patch_info_item,
				    L".Description");
			if (description != NULL) {
		  	   ret += description;   		  	  
		  	}
            ret += inventory_field_separator;
            dhFreeString (description);		
			dhGetValue (L"%s", &comments, patch_info_item,
				    L".FixComments");
   			if (comments != NULL) {
               ret += comments; 
            }
            ret += inventory_field_separator;
            dhFreeString (comments);		
			dhGetValue (L"%s", &service_pack, patch_info_item,
				    L".ServicePackInEffect");
   			if (service_pack != NULL) {
               ret += service_pack; 
            }
            dhFreeString(service_pack);
            rows.push_back(ret);
            ret.clear();
		} NEXT_THROW (patch_info_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
	return num_objects;
}

/**
 * Gets the information about the RAM
 * 
 * @param rows List where the query result will be placed. each row has the
 * 		   Tag, Capacity and Name  separated by ;
 * @return An int with the number of Results found
 */
int
Pandora_Wmi::getRAMInfo (list<string> &rows) {
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, ram_info = NULL;
	char         *tag  = NULL, *name = NULL, *capacity = NULL;
	string        ret = "";
    int          num_objects = 0;
 	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		if (wmi_svc == NULL) {
           pandoraLog("Error getting wmi_svc\n");
        }

        dhCheck (dhGetValue (L"%o", &ram_info, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT Tag, Capacity, Name FROM Win32_PhysicalMemory "));
        
		FOR_EACH (ram_info_item, ram_info, NULL) {
            num_objects++;
			dhGetValue (L"%s", &tag, ram_info_item,
				    L".Tag");
   			if (tag != NULL) {
               ret +=  tag;		  	 
            }
            ret += inventory_field_separator;
            dhFreeString(tag);
			dhGetValue (L"%s", &capacity, ram_info_item,
				    L".Capacity");
			if (capacity != NULL) {
               double fcapacity = atof(capacity) / 1048576; 
               ostringstream converter;
               converter << fcapacity;
		  	   ret += converter.str() + " MBs";   		  	  
		  	}
  		  	else {
                 pandoraDebug("Capacity unknown\n");
            }
            ret += inventory_field_separator;
            dhFreeString (capacity);		
			dhGetValue (L"%s", &name, ram_info_item,
				    L".Name");
   			if (name != NULL) {             
               ret += name;
            }
            rows.push_back(ret);
            ret.clear();
            dhFreeString(name);
		} NEXT_THROW (ram_info_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
	return num_objects;
}

/**
 * Get a list of running system services
 * 
 * @param rows Result list.
 * @return Result list length.
 */
int
Pandora_Wmi::getServices (list<string> &rows) {
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, services = NULL;
	char         *name  = NULL, *path_name = NULL, *state = NULL;
	string        ret = "";
    int          num_objects = 0;

 	try {
		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
        dhCheck (dhGetValue (L"%o", &services, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT Name, PathName, State FROM Win32_Service"));
        
		FOR_EACH (service, services, NULL) {
            num_objects++;
			dhGetValue (L"%s", &name, service, L".Name");
   			if (name != NULL) {
               ret += name;
            }
            ret += inventory_field_separator;
            dhFreeString(name);
			dhGetValue (L"%s", &path_name, service, L".PathName");
			if (path_name != NULL) {
		  	   ret += path_name;
		  	}
            ret += inventory_field_separator;
            dhFreeString (path_name);		
			dhGetValue (L"%s", &state, service, L".State");
   			if (state != NULL) {
               ret += state;
            }
            dhFreeString(state);
            rows.push_back(ret);
            ret.clear();
		} NEXT_THROW (service_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}
	return num_objects;
}

/** 
 * Get the system address
 * 
 * @return The system address
 */
string
Pandora_Wmi::getSystemAddress () {
    CDhInitialize init;
	CDispPtr      wmi_svc =  NULL, nic_info = NULL;
    VARIANT ip_addresses;
	char         *caption  = NULL, *mac_address = NULL;
	string        str_addr, ret = "";

 	try {

		dhCheck (dhGetObject (getWmiStr (L"."), NULL, &wmi_svc));
		dhCheck (dhGetValue (L"%o", &nic_info, wmi_svc,
				     L".ExecQuery(%S)",
				     L"SELECT IPAddress FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True"));

		FOR_EACH (nic_info_item, nic_info, NULL) {
		    dhGetValue (L"%v", &ip_addresses, nic_info_item,
				    L".IPAddress");
			
		    if (&ip_addresses != NULL && ret == "")
		    {
				str_addr = getIPs(&ip_addresses);
				if (str_addr != "0.0.0.0") {
					ret = str_addr;
				}
            }			
		} NEXT_THROW (nic_info_item);
	} catch (string errstr) {
		pandoraLog ("runWMIQuery error. %s", errstr.c_str ());
	}

	return ret;
}