/* Defines a parent class for a Pandora module.

   Copyright (C) 2006 Artica ST.
   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, MA 02111-1307, USA.
*/

#include "pandora_module.h"
#include "../pandora_strutils.h"
#include "../pandora.h"

#include <iostream>
#include <sstream>

#define BUFSIZE 4096 

using namespace Pandora;
using namespace Pandora_Modules;
using namespace Pandora_Strutils;

/** 
 * Creates a Pandora_Module.
 *
 * Initializes all attributes. The default interval is set to 1 loop.
 * 
 * @param name Module name.
 */
Pandora_Module::Pandora_Module (string name) {
	this->module_name     = name;
	this->executions      = 0;
	this->module_interval = 1;
	this->module_timeout  = 15000;
	this->max             = 0;
	this->min             = 0;
	this->post_process    = "";
	this->has_limits      = false;
	this->has_min         = false;
	this->has_max         = false;
	this->async           = false;
	this->data_list       = NULL;
    this->inventory_list  = NULL;
    this->precondition_list  = NULL;
    this->condition_list  = NULL;
	this->cron            = NULL;
	this->min_critical    = "";
	this->max_critical    = "";
	this->min_warning     = "";
	this->max_warning     = "";
	this->disabled        = "";
	this->min_ff_event    = "";
	this->intensive_condition_list = NULL;
	this->intensive_interval = 1;
	this->timestamp       = 0;
	this->intensive_match = 0;
	this->unit            = "";
	this->module_group    = "";
	this->custom_id       = "";
	this->str_warning     = "";
	this->str_critical    = "";
	this->critical_instructions = "";
	this->warning_instructions = "";
	this->unknown_instructions = "";
	this->tags = "";
	this->critical_inverse = "";
	this->warning_inverse = "";
	this->quiet = "";
	this->module_ff_interval = "";
	this->module_alert_template = "";
	this->module_crontab = "";
}

/** 
 * Virtual destructor of Pandora_Module.
 *
 * Should be redefined by child classes.
 */
Pandora_Module::~Pandora_Module () {
	Condition *cond = NULL;
	Condition *precond = NULL;
	list<Condition *>::iterator iter;
	list<Condition *>::iterator iter_pre;

	/* Clean data lists */
	this->cleanDataList ();

	/* Clean precondition list */
	if (this->precondition_list != NULL && this->precondition_list->size () > 0) {
		iter_pre = this->precondition_list->begin ();
		for (iter_pre = this->precondition_list->begin ();
		     iter_pre != this->precondition_list->end ();
		     iter_pre++) {
			/* Free regular expressions */
			precond = *iter_pre;
			if (precond->string_value != "") {
				regfree (&(precond->regexp));
			}
			delete (*iter_pre);
		}
		delete (this->precondition_list);
		this->precondition_list = NULL;
	}
	
	/* Clean condition list */
	if (this->condition_list != NULL && this->condition_list->size () > 0) {
		iter = this->condition_list->begin ();
		for (iter = this->condition_list->begin ();
		     iter != this->condition_list->end ();
		     iter++) {
			/* Free regular expressions */
			cond = *iter;
			if (cond->string_value != "") {
				regfree (&(cond->regexp));
			}
			delete (*iter);
		}
		delete (this->condition_list);
		this->condition_list = NULL;
	}

	/* Clean intensive_condition list */
	if (this->intensive_condition_list != NULL && this->intensive_condition_list->size () > 0) {
		iter = this->intensive_condition_list->begin ();
		for (iter = this->intensive_condition_list->begin ();
		     iter != this->intensive_condition_list->end ();
		     iter++) {
			/* Free regular expressions */
			cond = *iter;
			if (cond->string_value != "") {
				regfree (&(cond->regexp));
			}
			delete (*iter);
		}
		delete (this->intensive_condition_list);
		this->intensive_condition_list = NULL;
	}
	
	/* Clean the module cron */
	if (this->cron != NULL) {
		delete (this->cron);
		this->cron = NULL;
	}
}


void
Pandora_Module::cleanDataList () {
	Pandora_Data                  *data;
	list<Pandora_Data *>::iterator iter;
	
	if (this->data_list) {
		if (this->data_list->size () > 0) {
			iter = this->data_list->begin ();
			for (iter = this->data_list->begin ();
			     iter != this->data_list->end ();
			     iter++) {
				data = *iter;
				delete data;
			}
		}
		delete this->data_list;
		this->data_list = NULL;
	}
	if (this->inventory_list) {
		if (this->inventory_list->size () > 0) {
			iter = this->inventory_list->begin ();
			for (iter = this->inventory_list->begin ();
			     iter != this->inventory_list->end ();
			     iter++) {
				data = *iter;
				delete data;
			}
		}
		delete this->inventory_list;
		this->inventory_list = NULL;
	}
}

/** 
 * Get the Module_Type from a string type.
 * 
 * @param type String type.
 * 
 * @return The Module_Type which represents the type.
 */
Module_Type
Pandora_Module::parseModuleTypeFromString (string type) {
	if (type == module_generic_data_str || type == "") {
		return TYPE_GENERIC_DATA;
	} else if (type == module_generic_data_inc_str) {
		return TYPE_GENERIC_DATA_INC;
	} else if (type == module_generic_data_string_str) {
		return TYPE_GENERIC_DATA_STRING;
	} else if (type == module_generic_proc_str) {
		return TYPE_GENERIC_PROC;
	} else if (type == module_async_data_str) {
		return TYPE_ASYNC_DATA;
	} else if (type == module_async_proc_str) {
		return TYPE_ASYNC_PROC;
	} else if (type == module_async_string_str) {
		return TYPE_ASYNC_STRING;
	} else if (type == module_log_str) {
		return TYPE_LOG;
	} else if (type == module_generic_data_inc_abs_str) {
		return TYPE_GENERIC_DATA_INC_ABS;
	} else {
		return TYPE_0;
	}
}

/** 
 * Get the Module_Kind from a string Kind.
 * 
 * @param kind String Kind.
 * 
 * @return The Module_Kind which represents the Kind.
 */
Module_Kind
Pandora_Module::parseModuleKindFromString (string kind) {
	if (kind == module_exec_str) {
		return MODULE_EXEC;
	} else if (kind == module_proc_str) {
		return MODULE_PROC;
	} else if (kind == module_service_str) {
		return MODULE_SERVICE;
	} else if (kind == module_freedisk_str) {
		return MODULE_FREEDISK;
	} else if (kind == module_freedisk_percent_str) {
		return MODULE_FREEDISK_PERCENT;
	} else if (kind == module_freememory_str) {
		return MODULE_FREEMEMORY;
	} else if (kind == module_freememory_percent_str) {
		return MODULE_FREEMEMORY_PERCENT;
	} else if (kind == module_cpuusage_str) {
		return MODULE_CPUUSAGE;
	} else if (kind == module_inventory_str) {
		return MODULE_INVENTORY;
	} else if (kind == module_logevent_str) {
		return MODULE_LOGEVENT;  
	} else if (kind == module_wmiquery_str) {
		return MODULE_WMIQUERY;               
	} else if (kind == module_perfcounter_str) {
		return MODULE_PERFCOUNTER;
	} else if (kind == module_tcpcheck_str) {
		return MODULE_TCPCHECK;
	} else if (kind == module_regexp_str) {
		return MODULE_REGEXP;
	} else if (kind == module_plugin_str) {
		return MODULE_PLUGIN;
	} else if (kind == module_ping_str) {
		return MODULE_PING;
	} else if (kind == module_snmpget_str) {
		return MODULE_SNMPGET;
	} else {
		return MODULE_0;
	}
}

/** 
 * Get the name of the module.
 * 
 * @return The name of the module.
 */
string
Pandora_Module::getName () const {
	return this->module_name;
}

/** 
 * Get the description of the module.
 * 
 * @return The module description.
 */
string
Pandora_Module::getDescription () const {
	return this->module_description;
}

/** 
 * Get the module type in a human readable string.
 * 
 * @return The module type..
 */
string
Pandora_Module::getTypeString () const {
	return this->module_type_str;
}

/** 
 * Get the module type in a integer value.
 * 
 * @return The module type in a integer value.
 */
Module_Type
Pandora_Module::getTypeInt () const {
	return this->module_type;
}

/** 
 * Get the kind of the module in a integer_value.
 * 
 * @return The module kind in a integer value.
 */
Module_Kind
Pandora_Module::getModuleKind () const {
	return this->module_kind;
}

/** 
 * Get the output of the module.
 * 
 * @return The module output in a string value.
 */
string
Pandora_Module::getLatestOutput () const {
	return this->latest_output;
}

/** 
 * Get the type of the module in a integer_value.
 * 
 * @return The module type in a integer value.
 */
Module_Type
Pandora_Module::getModuleType () const {
	return this->module_type;
}

/** 
 * Get the module output.
 *
 * After running the module, this function will return the output,
 * based on the module_type and the interval.
 *
 * @return The output in a string.
 *
 * @exception Output_Error Throwed if the module_type is not correct.
 * @exception Value_Error Throwed when the output is not in
 *            the interval range.
 */
string
Pandora_Module::getDataOutput (Pandora_Data *data) {
	double value;
	
	if (this->module_type == TYPE_GENERIC_DATA_STRING || 
        this->module_type == TYPE_ASYNC_STRING || this->module_type == TYPE_LOG) {
		return data->getValue ();
	}
	
	try {
		value = Pandora_Strutils::strtodouble (data->getValue ());
	} catch (Pandora_Strutils::Invalid_Conversion e) {
		pandoraLog ("Output error on module %s",
			    this->module_name.c_str ());
		throw Output_Error ();
	}
	
	if (this->has_limits) {
		if (value > this->max || value < this->min) {
			pandoraLog ("The returned value was not in the interval on module %s",
				    this->module_name.c_str ());
			throw Value_Error ();
		}
	}
	
	return trim (data->getValue ());
}

/** 
 * Export the module output to en environment variable.
 */
void
Pandora_Module::exportDataOutput () {
	Pandora_Data *pandora_data = NULL;
	string putenv_str, module_data;

	/* putenv expects a string of the form name=value */
	putenv_str = this->save + "=";
	
	/* No data */
	if ( (!this->has_output) || this->data_list == NULL) {
		putenv (putenv_str.c_str ());
		return;
	}

	/* Get the module data */
	pandora_data = data_list->front ();
	if (pandora_data == NULL) {
		putenv (putenv_str.c_str ());
		return;
	}
	module_data = pandora_data->getValue ();
	putenv_str += module_data;

	/* Save it as an environment variable */
	putenv (putenv_str.c_str ());
}

/** 
 * Set the output of the module.
 *
 * If the function is called more than once before calling getXML, the
 * output will be accumulated and added to a <datalist> tag.
 *
 * @param output Output to add.
 */
void
Pandora_Module::setOutput (string output) {
	Pandora_Data *data;

	if (this->data_list == NULL)
		this->data_list = new list<Pandora_Data *> ();
	data = new Pandora_Data (output, this->module_name);
	this->data_list->push_back (data);
	this->latest_output = output;
}


/** 
 * Set the output of the module.
 *
 * If the function is called more than once before calling getXML, the
 * output will be accumulated and added to a <datalist> tag.
 *
 * @param output Output to add.
 * @param system_time Timestamp. 
 */
void
Pandora_Module::setOutput (string output, SYSTEMTIME *system_time) {
	Pandora_Data *data;

	if (this->data_list == NULL)
		this->data_list = new list<Pandora_Data *> ();
	data = new Pandora_Data (output, system_time, this->module_name);
	this->data_list->push_back (data);
}

/** 
 * Set no output for the module.
 */
void
Pandora_Module::setNoOutput () {
	this->cleanDataList ();
	this->has_output = false;
}

/** 
 * Run the module and generates the output.
 *
 * It is used by the child classes to check the execution interval
 * value and increment the executions variable.
 * 
 * @exception Interval_Not_Fulfilled Throwed when the execution
 *            interval value indicates that the module doesn't have
 *            to execute.
 */
void
Pandora_Module::run () {
	/* Check the interval */
	if (this->executions % this->intensive_interval != 0) {
		pandoraDebug ("%s: Interval is not fulfilled", this->module_name.c_str ());
		this->executions++;
		has_output = false;
		throw Interval_Not_Fulfilled ();
	} 
	
	/* Increment the executions after check. This is done to execute the
	   first time */
	this->executions++;
	has_output = true;
}

/** 
 * Get the XML output of the value.
 *
 * The output is a element of the TinyXML library. A sample output of
 * a module is:
 * @verbatim
 <module>
   <name>Conexiones abiertas</name>
   <type>generic_data</type>
   <data>5</data>
   <description>Conexiones abiertas</description>
 </module>
   @endverbatim
 *
 * @return A pointer to the TiXmlElement if successful which has to be
 *         freed by the caller. NULL if the XML could not be created.
 */
string
Pandora_Module::getXml () {
	ostringstream module_interval, min, max;
 	string        module_xml, data_clean, interval_str;
	Pandora_Data *data;
	
	pandoraDebug ("%s getXML begin", module_name.c_str ());
	
	/* No data */
	if (!this->has_output || this->data_list == NULL) {
		return "";
	}
	
	/* Log module */
	if (this->module_type == TYPE_LOG) {
		module_xml = "<log_module>\n\t<source><![CDATA[";
		module_xml += this->module_name;
		module_xml += "]]></source>\n\t<data><![CDATA[";

		if (this->data_list && this->data_list->size () > 1) {
			list<Pandora_Data *>::iterator iter;
			
			iter = this->data_list->begin ();
			for (iter = this->data_list->begin ();
			     iter != this->data_list->end ();
			     iter++) {
				data = *iter;
				
				try {
					data_clean = strreplace (this->getDataOutput (data),
								 "%", "%%" );
				} catch (Module_Exception e) {
					continue;
				}
				
				module_xml += data_clean;
			}
		} else {
			data = data_list->front ();
			try {
				data_clean = strreplace (this->getDataOutput (data), "%", "%%" );
				module_xml += data_clean;

			} catch (Module_Exception e) {
			}
		}
		module_xml += "]]></data></log_module>";
		
		/* Clean up */
		this->cleanDataList ();

		pandoraDebug ("%s getXML end", module_name.c_str ());
		return module_xml;
	}

	/* Compose the module XML */
    module_xml = "<module>\n\t<name><![CDATA[";
    module_xml += this->module_name;
    module_xml += "]]></name>\n\t<type><![CDATA[";
    module_xml += this->module_type_str;
    module_xml += "]]></type>\n";
    
    /* Description */
    if (this->module_description != "") {
		module_xml += "\t<description><![CDATA[";
		module_xml += this->module_description;
		module_xml += "]]></description>\n";
	}
	
	/* Interval */
	module_interval << this->module_interval;
	module_xml += "\t<module_interval><![CDATA[";
	module_xml += module_interval.str ();
	module_xml += "]]></module_interval>\n";
	
	/* Min */
    if (this->has_min) {
		min << this->min;
		module_xml += "\t<min><![CDATA[";
		module_xml += min.str ();
		module_xml += "]]></min>\n";
	}
	
	/* Max */
	if (this->has_max) {
		max << this->max;
		module_xml += "\t<max><![CDATA[";
		module_xml += max.str ();
		module_xml += "]]></max>\n";
	}
	
	/* Post process */
	if (this->post_process != "") {
		module_xml += "\t<post_process><![CDATA[";
		module_xml += this->post_process;
		module_xml += "]]></post_process>\n";
	}

	/* Min critical */
	if (this->min_critical != "") {
		module_xml += "\t<min_critical><![CDATA[";
		module_xml += this->min_critical;
		module_xml += "]]></min_critical>\n";
	}

	/* Max critical */
	if (this->max_critical != "") {
		module_xml += "\t<max_critical><![CDATA[";
		module_xml += this->max_critical;
		module_xml += "]]></max_critical>\n";
	}

	/* Min warning */
	if (this->min_warning != "") {
		module_xml += "\t<min_warning><![CDATA[";
		module_xml += this->min_warning;
		module_xml += "]]></min_warning>\n";
	}

	/* Max warning */
	if (this->max_warning != "") {
		module_xml += "\t<max_warning><![CDATA[";
		module_xml += this->max_warning;
		module_xml += "]]></max_warning>\n";
	}

	/* Disabled */
	if (this->disabled != "") {
		module_xml += "\t<disabled><![CDATA[";
		module_xml += this->disabled;
		module_xml += "]]></disabled>\n";
	}

	/* Min ff event */
	if (this->min_ff_event != "") {
		module_xml += "\t<min_ff_event><![CDATA[";
		module_xml += this->min_ff_event;
		module_xml += "]]></min_ff_event>\n";
	}

	/* Unit */
	if (this->unit != "") {
		module_xml += "\t<unit><![CDATA[";
		module_xml += this->unit;
		module_xml += "]]></unit>\n";
	}
	
	/* Module group */
	if (this->module_group != "") {
		module_xml += "\t<module_group>";
		module_xml += this->module_group;
		module_xml += "</module_group>\n";
	}
	
	/* Custom ID */
	if (this->custom_id != "") {
		module_xml += "\t<custom_id>";
		module_xml += this->custom_id;
		module_xml += "</custom_id>\n";
	}
	
	/* Str warning */
	if (this->str_warning != "") {
		module_xml += "\t<str_warning>";
		module_xml += this->str_warning;
		module_xml += "</str_warning>\n";
	}
	
	/* Str critical */
	if (this->str_critical != "") {
		module_xml += "\t<str_critical>";
		module_xml += this->str_critical;
		module_xml += "</str_critical>\n";
	}
	
	/* Critical instructions */
	if (this->critical_instructions != "") {
		module_xml += "\t<critical_instructions>";
		module_xml += this->critical_instructions;
		module_xml += "</critical_instructions>\n";
	}
	
	/* Warning instructions */
	if (this->warning_instructions != "") {
		module_xml += "\t<warning_instructions>";
		module_xml += this->warning_instructions;
		module_xml += "</warning_instructions>\n";
	}
	
	/* Unknown instructions */
	if (this->unknown_instructions != "") {
		module_xml += "\t<unknown_instructions>";
		module_xml += this->unknown_instructions;
		module_xml += "</unknown_instructions>\n";
	}
	
	/* Tags */
	if (this->tags != "") {
		module_xml += "\t<tags>";
		module_xml += this->tags;
		module_xml += "</tags>\n";
	}
	
	/* Critical inverse */
	if (this->critical_inverse != "") {
		module_xml += "\t<critical_inverse>";
		module_xml += this->critical_inverse;
		module_xml += "</critical_inverse>\n";
	}
	
	/* Warning inverse */
	if (this->warning_inverse != "") {
		module_xml += "\t<warning_inverse>";
		module_xml += this->warning_inverse;
		module_xml += "</warning_inverse>\n";
	}
	
	/* Quiet */
	if (this->quiet != "") {
		module_xml += "\t<quiet>";
		module_xml += this->quiet;
		module_xml += "</quiet>\n";
	}
	
	/* Module FF interval */
	if (this->module_ff_interval != "") {
		module_xml += "\t<module_ff_interval>";
		module_xml += this->module_ff_interval;
		module_xml += "</module_ff_interval>\n";
	}
	
	/* Module Alert template */
	if (this->module_alert_template != "") {
		module_xml += "\t<alert_template>";
		module_xml += this->module_alert_template;
		module_xml += "</alert_template>\n";
	}
	
	/* Module Crontab */
	if (this->module_crontab != "") {
		module_xml += "\t<crontab>";
		module_xml += this->module_crontab;
		module_xml += "</crontab>\n";
	}

    /* Write module data */
	if (this->data_list && this->data_list->size () > 1) {
		list<Pandora_Data *>::iterator iter;

		module_xml += "\t<datalist>\n";
		
		iter = this->data_list->begin ();
		for (iter = this->data_list->begin ();
		     iter != this->data_list->end ();
		     iter++) {
			data = *iter;
			
			try {
				data_clean = strreplace (this->getDataOutput (data),
							 "%", "%%" );
			} catch (Module_Exception e) {
				continue;
			}
			
			module_xml += "\t\t<data>\n\t\t\t<value><![CDATA[";
			module_xml += data_clean;
			module_xml += "]]></value>\n\t\t\t<timestamp><![CDATA[";
			module_xml += data->getTimestamp ();
			module_xml += "]]></timestamp>\n\t\t</data>\n";
		}
		
		module_xml += "\t</datalist>\n";
	} else {
		data = data_list->front ();
		try {
			data_clean = strreplace (this->getDataOutput (data), "%", "%%" );
			module_xml += "\t<data><![CDATA[";
			module_xml += data_clean;
			module_xml += "]]></data>\n";
		} catch (Module_Exception e) {
		}
	}
		
	/* Close the module tag */
	module_xml += "</module>\n";
	
	/* Clean up */
	this->cleanDataList ();
	
	pandoraDebug ("%s getXML end", module_name.c_str ());
	return module_xml;
}

/** 
 * Set the max value the module can have.
 *
 * The range is closed, so the value is included.
 *
 * @param value Max value to set.
 */
void
Pandora_Module::setMax (int value) {
	this->has_limits = true;
	this->has_max = true;
	this->max        = value;
}

/** 
 * Set the min value the module can have.
 *
 * The range is closed, so the value is included.
 *
 * @param value Min value to set.
 */
void
Pandora_Module::setMin (int value) {
	this->has_limits = true;
	this->has_min = true;
	this->min        = value;
}

/** 
 * Set the post process value for the module.
 *
 * @param value Post process value to set.
 */
void
Pandora_Module::setPostProcess (string value) {
	this->post_process = value;
}

/** 
 * Set the min critical value for the module.
 *
 * @param value Min critical value to set.
 */
void
Pandora_Module::setMinCritical (string value) {
	this->min_critical = value;
}

/** 
 * Set the max critical value for the module.
 *
 * @param value Max critical value to set.
 */
void
Pandora_Module::setMaxCritical (string value) {
	this->max_critical = value;
}

/** 
 * Set the min warning value for the module.
 *
 * @param value Min warning value to set.
 */
void
Pandora_Module::setMinWarning (string value) {
	this->min_warning = value;
}

/** 
 * Set the max warning value for the module.
 *
 * @param value Max warning value to set.
 */
void
Pandora_Module::setMaxWarning (string value) {
	this->max_warning = value;
}

/** 
 * Set the disabled value for the module.
 *
 * @param value Disabled value to set.
 */
void
Pandora_Module::setDisabled (string value) {
	this->disabled = value;
}

/** 
 * Set the min ff event value for the module.
 *
 * @param value Min ff event value to set.
 */
void
Pandora_Module::setMinFFEvent (string value) {
	this->min_ff_event = value;
}

/** 
 * Set the unit value for the module.
 *
 * @param value unit value to set.
 */
void
Pandora_Module::setUnit (string value) {
	this->unit = value;
}

/** 
 * Set the module group for the module.
 *
 * @param value module group value to set.
 */
void
Pandora_Module::setModuleGroup (string value) {
	this->module_group = value;
}

/** 
 * Set the custom id for the module.
 *
 * @param value custom id value to set.
 */
void
Pandora_Module::setCustomId (string value) {
	this->custom_id = value;
}

/** 
 * Set the str warning for the module.
 *
 * @param value str warning value to set.
 */
void
Pandora_Module::setStrWarning (string value) {
	this->str_warning = value;
}

/** 
 * Set the str critical for the module.
 *
 * @param value str critical value to set.
 */
void
Pandora_Module::setStrCritical (string value) {
	this->str_critical = value;
}

/** 
 * Set the critical instructions for the module.
 *
 * @param value critical instructions value to set.
 */
void
Pandora_Module::setCriticalInstructions (string value) {
	this->critical_instructions = value;
}

/** 
 * Set the warning instructions for the module.
 *
 * @param value warning instructions value to set.
 */
void
Pandora_Module::setWarningInstructions (string value) {
	this->warning_instructions = value;
}

/** 
 * Set the unknown instructions for the module.
 *
 * @param value unknown instructions value to set.
 */
void
Pandora_Module::setUnknownInstructions (string value) {
	this->unknown_instructions = value;
}

/** 
 * Set the tags for the module.
 *
 * @param value tags value to set.
 */
void
Pandora_Module::setTags (string value) {
	this->tags = value;
}

/** 
 * Set the critical inverse for the module.
 *
 * @param value critical inverse value to set.
 */
void
Pandora_Module::setCriticalInverse (string value) {
	this->critical_inverse = value;
}

/** 
 * Set the warning inverse for the module.
 *
 * @param value warning inverse value to set.
 */
void
Pandora_Module::setWarningInverse (string value) {
	this->warning_inverse = value;
}

/** 
 * Set the quiet for the module.
 *
 * @param value quiet value to set.
 */
void
Pandora_Module::setQuiet (string value) {
	this->quiet = value;
}

/** 
 * Set the module FF interval for the module.
 *
 * @param value module FF interval value to set.
 */
void
Pandora_Module::setModuleFFInterval (string value) {
	this->module_ff_interval = value;
}

/** 
 * Set the module Alert template for the module.
 *
 * @param value module Alert template value to set.
 */
void
Pandora_Module::setModuleAlertTemplate (string value) {
	this->module_alert_template = value;
}

/** 
 * Set the module Crontab for the module.
 *
 * @param value module Crontab value to set.
 */
void
Pandora_Module::setModuleCrontab (string value) {
	this->module_crontab = value;
}

/** 
 * Set the async flag to the module.
 *
 * If a module is set to be async, it would try to works only when the
 * events happen. Note that not all the modules can work in async mode.
 *
 * @param async Flag to set.
 */
void
Pandora_Module::setAsync (bool async) {
	this->async = async;
}

/** 
 * Set the module type from a string type.
 * 
 * @param type String type.
 */
void
Pandora_Module::setType (string type) {
	this->module_type_str = type;
	this->module_type     = parseModuleTypeFromString (type);
}

/** 
 * Set the module kind from a string kind.
 * 
 * @param kind String kind.
 */
void
Pandora_Module::setKind (string kind) {
	this->module_kind_str = kind;
	this->module_kind     = parseModuleKindFromString (kind);
}

/** 
 * Set the interval execution.
 * 
 * @param interval Interval between executions.
 */
void
Pandora_Module::setInterval (int interval) {
	this->module_interval = interval;
}

/** 
 * Set the intensive interval.
 * 
 * @param intensive_interval Intensive interval.
 */
void
Pandora_Module::setIntensiveInterval (int intensive_interval) {
	this->intensive_interval = intensive_interval;
}

/** 
 * Set the execution timeout.
 * 
 * @param timeout Execution timeout.
 */
void
Pandora_Module::setTimeout (int timeout) {

	if (timeout < 0) {
		return;
	}
	
	/* WaitForSingleObject expects milliseconds */
	this->module_timeout = timeout * 1000;
}

/** 
 * Get the execution interval.
 * 
 * @return The execution interval.
 */
int
Pandora_Module::getInterval () {
	return this->module_interval;
}

/** 
 * Get the intensive interval.
 * 
 * @return The intensive interval.
 */
int
Pandora_Module::getIntensiveInterval () {
	return this->intensive_interval;
}

/** 
 * Get the execution timeout.
 * 
 * @return The execution timeout.
 */
int
Pandora_Module::getTimeout () {
	return this->module_timeout;
}

/** 
 * Set the module description.
 * 
 * @param description Description of the module.
 */
void
Pandora_Module::setDescription (string description) {
	this->module_description = description;
}

/** 
 * Set the name of the environment variable where the module data will be saved.
 * 
 * @param save Name of the environment variable.
 */
void
Pandora_Module::setSave (string save) {
	this->save = save;
}

/** 
 * Get the name of the environment variable where the module data will be saved.
 * 
 * @return The name of the environment variable.
 */
string
Pandora_Module::getSave () {
	return this->save;
}

/** 
 * Adds a new condition to a condition list.
 * 
 * @param condition Condition string.
 * @param condition_list Pointer to the condition list.
 */
void
Pandora_Module::addGenericCondition (string condition, list<Condition *> **condition_list) {
	Condition *cond;
	char operation[256], string_value[1024], command[1024];

	/* Create the condition list if it does not exist */
	if (*condition_list == NULL) {
		*condition_list = new list<Condition *> ();
	}

	/* Create the new condition */
	cond = new Condition;
	if (cond == NULL) {
		return;
	}
	cond->value_1 = 0;
	cond->value_2 = 0;

	/* Numeric comparison */
	if (sscanf (condition.c_str (), "%255s %lf %1023[^\n]s", operation, &(cond->value_1), command) == 3) {
		cond->operation = operation;
		cond->command = command;
		cond->command = command;
		cond->command = "cmd.exe /c \"" + cond->command + "\"";
		(*condition_list)->push_back (cond);		
	/* Regular expression */
	} else if (sscanf (condition.c_str (), "=~ %1023s %1023[^\n]s", string_value, command) == 2) {
		cond->operation = "=~";
		cond->string_value = string_value;
		cond->command = command;
		cond->command = "cmd.exe /c \"" + cond->command + "\"";
		if (regcomp (&(cond->regexp), string_value, REG_EXTENDED) != 0) {
			pandoraDebug ("Invalid regular expression %s", string_value);
			delete (cond);
			return;
		}
		(*condition_list)->push_back (cond);		
	/* Interval */
	} else if (sscanf (condition.c_str (), "(%lf , %lf) %1023[^\n]s", &(cond->value_1), &(cond->value_2), command) == 3) {
		cond->operation = "()";
		cond->command = command;
		cond->command = "cmd.exe /c \"" + cond->command + "\"";
		(*condition_list)->push_back (cond);
	} else {
		pandoraLog ("Invalid condition: %s", condition.c_str ());
		delete (cond);
		return;
	}

	return;
}

/** 
 * Adds a new module condition.
 * 
 * @param condition Condition string.
 */
void
Pandora_Module::addCondition (string condition) {
	addGenericCondition (condition, &(this->condition_list));
}

/** 
 * Adds a new module pre-condition.
 * 
 * @param condition Condition string.
 */
void
Pandora_Module::addPreCondition (string condition) {
	addGenericCondition (condition, &(this->precondition_list));
}

/** 
 * Adds a new module intensive condition.
 * 
 * @param condition Condition string.
 */
void
Pandora_Module::addIntensiveCondition (string condition) {
	Condition *cond;
	char operation[256], string_value[1024], command[1024];

	/* Create the condition list if it does not exist */
	if (this->intensive_condition_list == NULL) {
		this->intensive_condition_list = new list<Condition *> ();
	}

	/* Create the new condition */
	cond = new Condition;
	if (cond == NULL) {
		return;
	}
	cond->value_1 = 0;
	cond->value_2 = 0;

	/* Numeric comparison */
	if (sscanf (condition.c_str (), "%255s %lf", operation, &(cond->value_1)) == 2) {
		cond->operation = operation;
		(this->intensive_condition_list)->push_back (cond);		
	/* Regular expression */
	} else if (sscanf (condition.c_str (), "=~ %1023s", string_value) == 1) {
		cond->operation = "=~";
		cond->string_value = string_value;
		if (regcomp (&(cond->regexp), string_value, REG_EXTENDED) != 0) {
			pandoraDebug ("Invalid regular expression %s", string_value);
			delete (cond);
			return;
		}
		(this->intensive_condition_list)->push_back (cond);		
	/* Interval */
	} else if (sscanf (condition.c_str (), "(%lf , %lf)", &(cond->value_1), &(cond->value_2)) == 2) {
		cond->operation = "()";
		(this->intensive_condition_list)->push_back (cond);
	} else {
		pandoraDebug ("Invalid intensive condition: %s", condition.c_str ());
		delete (cond);
		return;
	}
}

/** 
 * Evaluates and executes module preconditions.
 */
int
Pandora_Module::evaluatePreconditions () {
	STARTUPINFO         si;
	PROCESS_INFORMATION pi;
	DWORD               retval, dwRet;
	SECURITY_ATTRIBUTES attributes;
	HANDLE              out, new_stdout, out_read, job;
	string              working_dir;
	Condition *precond = NULL;
	double double_output;
	list<Condition *>::iterator iter;
	unsigned char run;
	string output;
	
	if (this->precondition_list != NULL && this->precondition_list->size () > 0) {

		iter = this->precondition_list->begin ();
		for (iter = this->precondition_list->begin ();
		     iter != this->precondition_list->end ();
		     iter++) {
				
			precond = *iter;
			run = 0;	
			
	
			/* Set the bInheritHandle flag so pipe handles are inherited. */
			attributes.nLength = sizeof (SECURITY_ATTRIBUTES); 
			attributes.bInheritHandle = TRUE; 
			attributes.lpSecurityDescriptor = NULL; 

			/* Create a job to kill the child tree if it become zombie */
			/* CAUTION: In order to compile this, WINVER should be defined to 0x0500.
			This may need no change, since it was redefined by the 
			program, but if needed, the macro is defined 
			in <windef.h> */
			job = CreateJobObject (&attributes, this->module_name.c_str ());
			if (job == NULL) {
				pandoraLog ("evaluatePreconditions: CreateJobObject failed. Err: %d", GetLastError ());
				return 0;
			}

			/* Get the handle to the current STDOUT. */
			out = GetStdHandle (STD_OUTPUT_HANDLE); 

			if (! CreatePipe (&out_read, &new_stdout, &attributes, 0)) {
				pandoraLog ("evaluatePreconditions: CreatePipe failed. Err: %d", GetLastError ());
				return 0;
			}

			/* Ensure the read handle to the pipe for STDOUT is not inherited */
			SetHandleInformation (out_read, HANDLE_FLAG_INHERIT, 0);

			/* Set up members of the STARTUPINFO structure. */
			ZeroMemory (&si, sizeof (si));
			GetStartupInfo (&si);

			si.cb = sizeof (si);
			si.dwFlags     = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
			si.wShowWindow = SW_HIDE;
			si.hStdError   = new_stdout;
			si.hStdOutput  = new_stdout;

			/* Set up members of the PROCESS_INFORMATION structure. */
			ZeroMemory (&pi, sizeof (pi));
			pandoraDebug ("Executing pre-condition: %s", precond->command.c_str ());

			/* Set the working directory of the process. It's "utils" directory
			to find the GNU W32 tools */
			working_dir = getPandoraInstallDir () + "util\\";

			/* Create the child process. */
			if (! CreateProcess (NULL, (CHAR *) precond->command.c_str (), NULL,
			     NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW, NULL,
			     working_dir.c_str (), &si, &pi)) {
				pandoraLog ("evaluatePreconditions: %s CreateProcess failed. Err: %d",
			    this->module_name.c_str (), GetLastError ());
			} else {
				char          buffer[BUFSIZE + 1];
				unsigned long read, avail;
	
				if (! AssignProcessToJobObject (job, pi.hProcess)) {
					pandoraLog ("evaluatePreconditions: could not assign proccess to job (error %d)",
				    GetLastError ());
				}
				ResumeThread (pi.hThread);
	
				/*string output;*/
				output = "";
				int tickbase = GetTickCount();
				while ( (dwRet = WaitForSingleObject (pi.hProcess, 500)) != WAIT_ABANDONED ) {
					PeekNamedPipe (out_read, buffer, BUFSIZE, &read, &avail, NULL);
						if (avail > 0) {
							ReadFile (out_read, buffer, BUFSIZE, &read, NULL);
							buffer[read] = '\0';
							output += (char *) buffer;
					}
				
				try {
					double_output = Pandora_Strutils::strtodouble (output);
				} catch (Pandora_Strutils::Invalid_Conversion e) {
					double_output = 0;
				}
	
				if (dwRet == WAIT_OBJECT_0) { 
					break;
				} else if(this->getTimeout() < GetTickCount() - tickbase) {
					/* STILL_ACTIVE */
					TerminateProcess(pi.hThread, STILL_ACTIVE);
					pandoraLog ("evaluatePreconditions: %s timed out (retcode: %d)", this->module_name.c_str (), STILL_ACTIVE);
					break;
				}
			}
	
			GetExitCodeProcess (pi.hProcess, &retval);
	
			if (retval != 0) {
				if (! TerminateJobObject (job, 0)) {
					pandoraLog ("evaluatePreconditions: TerminateJobObject failed. (error %d)",
					GetLastError ());
				}
	            if (retval != STILL_ACTIVE) {
					pandoraLog ("evaluatePreconditions: %s did not executed well (retcode: %d)",
	                this->module_name.c_str (), retval);
	            }
				/* Close job, process and thread handles. */
				CloseHandle (job);
				CloseHandle (pi.hProcess);
				CloseHandle (pi.hThread);
				CloseHandle (new_stdout);
				CloseHandle (out_read);
				return 0;
			}
		
			/* Close job, process and thread handles. */
			CloseHandle (job);
			CloseHandle (pi.hProcess);
			CloseHandle (pi.hThread);
		}

			CloseHandle (new_stdout);
			CloseHandle (out_read);
		
			if (evaluateCondition (output, double_output, precond) == 0) {
				return 0;
			}
		}
	}
	
	return 1;
}

/** 
 * Evaluates and executes module conditions.
 */
void
Pandora_Module::evaluateConditions () {
	unsigned char run;
	double double_value;
	string string_value;
	Condition *cond = NULL;
	list<Condition *>::iterator iter;
	PROCESS_INFORMATION pi;
	STARTUPINFO         si;
	Pandora_Data *pandora_data = NULL;
	regex_t regex;

	/* No data */
	if ( (!this->has_output) || this->data_list == NULL) {
		return;
	}

	/* Get the module data */
	pandora_data = data_list->front ();

	/* Get the string value of the data */
	string_value = pandora_data->getValue ();

	/* Get the double value of the data */
	try {
		double_value = Pandora_Strutils::strtodouble (string_value);
	} catch (Pandora_Strutils::Invalid_Conversion e) {
		double_value = 0;
	}

	if (this->condition_list != NULL && this->condition_list->size () > 0) {
		iter = this->condition_list->begin ();
		for (iter = this->condition_list->begin ();
		     iter != this->condition_list->end ();
		     iter++) {
			cond = *iter;
			run = 0;
			
			if (evaluateCondition (string_value, double_value, cond) == 1) {
				/* Run the condition command */
				ZeroMemory (&si, sizeof (si));
				ZeroMemory (&pi, sizeof (pi));
				if (CreateProcess (NULL , (CHAR *)cond->command.c_str (), NULL, NULL, FALSE,
				    CREATE_NO_WINDOW, NULL, NULL, &si, &pi) == 0) {
				    return;
				}
				WaitForSingleObject(pi.hProcess, this->module_timeout);
				CloseHandle (pi.hProcess);
				CloseHandle (pi.hThread);
			}
		}
	}
}

/** 
 * Evaluates and executes intensive module conditions.
 */
int
Pandora_Module::evaluateIntensiveConditions () {
	double double_value;
	string string_value;
	Condition *cond = NULL;
	list<Condition *>::iterator iter;
	PROCESS_INFORMATION pi;
	STARTUPINFO         si;
	Pandora_Data *pandora_data = NULL;
	regex_t regex;

	/* Not an intensive module */
	if (this->intensive_condition_list == NULL || this->intensive_condition_list->size () <= 0) {
		return 1;
	}

	/* No data */
	if ( (!this->has_output) || this->data_list == NULL) {
		return 0;
	}

	/* Get the module data */
	pandora_data = data_list->front ();

	/* Get the string value of the data */
	string_value = pandora_data->getValue ();

	/* Get the double value of the data */
	try {
		double_value = Pandora_Strutils::strtodouble (string_value);
	} catch (Pandora_Strutils::Invalid_Conversion e) {
		double_value = 0;
	}

	iter = this->intensive_condition_list->begin ();
	for (iter = this->intensive_condition_list->begin ();
	     iter != this->intensive_condition_list->end ();
	     iter++) {
		cond = *iter;
		if (evaluateCondition (string_value, double_value, cond) == 0) {
			return 0;
		}
	}
	
	return 1;
}

/** 
 * Checks the module cron. Returns 1 if the module should run, 0 if not.
 * 
 * @return 1 if the module should run, 0 if not.
 */
int
Pandora_Module::checkCron (int module_interval, int agent_interval) {
	int i, time_params[5];
	time_t current_time, offset;
	struct tm *time_struct;
	Cron *cron = this->cron;

	// No cron
	if (cron == NULL) {
		return 1;
	}

	// Get current time
	current_time = time (NULL);

	// Check if the module was already executed
	if (current_time <= cron->utimestamp) {
		return 0;
	}

	// Break current time
	time_struct = localtime(&current_time);
	if (time_struct == NULL) {
		return 1;
	}

	time_params[0] = time_struct->tm_min;
	time_params[1] = time_struct->tm_hour;
	time_params[2] = time_struct->tm_mday;
	time_params[3] = time_struct->tm_mon;
	time_params[4] = time_struct->tm_wday;
	
	// Fix month (localtime retuns 0..11 and we need 1..12)
	time_params[3] += 1;
	
	// Check cron parameters	
	for (i = 0; i < 5; i++) {
		
		// Wildcard
		if (cron->params[i][0] < 0) {
			continue;
		}
		
		// Check if next execution will overflow the cron (only minutes overflow)
		// If overflow, execute the module
		bool overflow_cron = false;
		if (i == 0) {
			int start_cron_seconds = cron->params[i][0]*60;
			int current_exec_seconds = time_params[i]*60;
			int next_exec_seconds = time_params[i]*60 + module_interval*agent_interval;
			if (current_exec_seconds > start_cron_seconds && current_exec_seconds > next_exec_seconds) {
				start_cron_seconds += 3600;
			}
			if ((current_exec_seconds <= start_cron_seconds) && (start_cron_seconds <= next_exec_seconds)) {
				overflow_cron = true;
			}
		}
		
		// Check interval
		if (cron->params[i][0] <= cron->params[i][1]) {
			if ((time_params[i] < cron->params[i][0] || time_params[i] > cron->params[i][1]) && !overflow_cron) {
				return 0;
			}
		} else {
			if ((time_params[i] < cron->params[i][0] && time_params[i] > cron->params[i][1]) && !overflow_cron) {
				return 0;
			}
		}
	}

	// Do not check in the next minute, hour, day or month.
	offset = 0; 
	if (cron->interval >= 0) {
		offset = cron->interval;
	} else if(cron->params[0][0] >= 0) {
		// 1 minute
		offset = 60;
	} else if(cron->params[1][0] >= 0) {
		// 1 hour
		offset = 3600;
	} else if(cron->params[2][0] >=0 || cron->params[4][0] >= 0) {
		// 1 day
		offset = 86400;
	} else if(cron->params[3][0] >= 0) {
		// 31 days
		offset = 2678400;
	}

	cron->utimestamp = current_time + offset;
	return 1;
}

/** 
 * Sets the module cron from a string.
 * 
 * @return 1 if the module should run, 0 if not.
 */
void
Pandora_Module::setCron (string cron_string) {
	int i, value;
	char cron_params[5][256], bottom[256], top[256];
	
	/* Create the new cron if necessary */
	if (this->cron == NULL) {
		this->cron = new Cron ();
	}
	
	/* Parse the cron string */
	if (sscanf (cron_string.c_str (), "%255s %255s %255s %255s %255s", cron_params[0], cron_params[1], cron_params[2], cron_params[3], cron_params[4]) != 5) {
		pandoraDebug ("Invalid cron string: %s", cron_string.c_str ());
		return;
	}
	
	/* Fill the cron structure */
	this->cron->utimestamp = 0;
	this->cron->interval = -1;
	for (i = 0; i < 5; i++) {
		
		/* Wildcard */
		if (cron_params[i][0] == '*') {
			this->cron->params[i][0] = -1;

		/* Interval */
		} else if (sscanf (cron_params[i], "%255[^-]-%255s", bottom, top) == 2) {
			value = atoi (bottom);
			this->cron->params[i][0] = value;
			value = atoi (top);
			this->cron->params[i][1] = value;
		
		/* Single value */
		} else {
			value = atoi (cron_params[i]);
			this->cron->params[i][0] = value;
			this->cron->params[i][1] = value;
		}
	}	
}

/** 
 * Sets the interval of the module cron.
 * 
 * @param interval Module cron interval in seconds.
 */
void
Pandora_Module::setCronInterval (int interval) {
	if (this->cron == NULL) {
		this->cron = new Cron ();
	}
	
	this->cron->interval = interval;
}

/** 
 * Evaluate a single condition. Returns 1 if the condition matches, 0
 * otherwise.
 * 
 * @param string_value String value.
 * @param double_value Double value.
 * @param condition Pointer to the condition.
 */
int Pandora_Module::evaluateCondition (string string_value, double double_value, Condition *condition) {
	if ((condition->operation == ">" && double_value > condition->value_1) ||
	    (condition->operation == "<" && double_value < condition->value_1) ||
	    (condition->operation == "=" && double_value == condition->value_1) ||
	    (condition->operation == "!=" && double_value != condition->value_1) ||
	    (condition->operation == "=~" && regexec (&(condition->regexp), string_value.c_str(), 0, NULL, 0) == 0) ||
		(condition->operation == "()" && double_value > condition->value_1 && double_value < condition->value_2)) {
			return 1;
	}
	
	return 0;
}

/** 
 * Checks if a module has data.
 * 
 * @return 1 if the module has data, 0 otherwise.
 */	
int Pandora_Module::hasOutput () {
	if (this->has_output == 1) {
		return 1;
	}
	
	return 0;
}

/** 
 * Sets the module timestamp.
 * 
 * @param timestamp Module timestamp in seconds.
 */
void
Pandora_Module::setTimestamp (time_t timestamp) {
	this->timestamp = timestamp;
}

/** 
 * Gets the module timestamp.
 * 
 * @return Module timestamp in seconds.
 */
time_t
Pandora_Module::getTimestamp () {
	return this->timestamp;
}

/** 
 * Sets the value of intensive_match.
 * 
 * @param intensive_match 0 or 1.
 */
void
Pandora_Module::setIntensiveMatch (unsigned char intensive_match) {
	this->intensive_match = intensive_match;
}

/** 
 * Gets the value of intensive_match.
 * 
 * @return The value of intensive match.
 */
unsigned char
Pandora_Module::getIntensiveMatch () {
	return this->intensive_match;
}

bool
Pandora_Module::getAsync () {
	return this->async;
}