#!/bin/sh
# $Id: nagios-notify 10428 2009-07-28 10:13:35Z glen $
#
# Template based Nagios notify script.
# Requires: awk, base64 (coreutils)
#
# Author: Elan Ruusamäe <glen@pld-linux.org>
# License: Same as Nagios (GPL v2)
#

templatedir=${NAGIOS_TEMPLATEDIR:-/etc/nagios/templates}
export NAGIOS_DATADIR='/usr/share/nagios'
prog="${0##*/}"

# Substutute Nagios $VAR$-s (which are exported to environment by Nagios) inside template.
template_subst() {
	local tmpl="$1"
	awk '
	# escape string for shell arg
	function escape_shell_arg(string,   q, qq, s) {
		q = sprintf("%c", 39);
		qq = "\"";
		s = q qq q qq q;
		gsub(q, s, string);
		return q string q
	}

	# base64 encode a file
	function base64(file,   cmd, out) {
		cmd = "base64 " file;
		out = ""
		while ((cmd | getline) > 0) {
			out = out $0;
		}
		close(cmd);
		return out;
	}

	# base64 encode a string
	function base64_string(data,   cmd, out) {
		# if i knew how to make two way pipes, i would do that here
		cmd = "echo -n " escape_shell_arg(data) " | base64";
		out = ""
		while ((cmd | getline) > 0) {
			out = out $0;
		}
		close(cmd);
		return out;
	}

	# encode email header as rfc2047 (using base64 encoding)
	function encode_mime_header(charset, data) {
		return "=?" charset "?b?" base64_string(data) "?="
	}

	# urlencode
	function urlencode(data,   hextab, i, n, res, c, lo, hi) {
		split("1 2 3 4 5 6 7 8 9 a b c d e f", hextab, " ")
		hextab[0] = 0
		for (i = 1; i <= 255; i++) {
			ord[sprintf("%c", i) ""] = i + 0
		}

		n = length(data)
		res = ""
		for (i = 1; i <= n; i++) {
		   	c = substr(data, i, 1);
			if (!match(c, /[A-Za-z0-9._-]/)) {
				lo = ord[c] % 16
				hi = int(ord[c] / 16);
				c = "%" hextab[hi] hextab[lo]
			}
			res = res c
		}

		return res
	}

	# trim whitespace at both sides
	function trim(s) {
		gsub(/(^ *| *$)/, "", s);
		return s;
	}

	# remove quotation marks or regexps if surrounded at both ends
	function unquote(s,   c1, c2) {
		c1 = substr(s, 1, 1);
		c2 = substr(s, length(s));
		if (c1 == c2 && (c1 == "\"" || c1 == "/")) {
			return substr(s, 2, length(s) - 2);
		}
		return s;
	}

	function eval_math(left, op, right) {
		if (op == "+") {
			return left + right;

		} else if (op == "-") {
			return left - right;

		} else if (op == "*") {
			return left * right;

		} else if (op == "/") {
			return left / right;

		}
	}

	# evaluate mathematical expression
	function math(expr,   op, left, right) {
		# find left expression, operator, right expression
		if (match(expr, "^.*("MATH_OPS")")) {
			left = trim(substr(expr, 1, RLENGTH));
			right = trim(substr(expr, RSTART + RLENGTH));
			# find op from end of left
			if (match(left, "("MATH_OPS")$")) {
				op = substr(left, RSTART, RLENGTH);
				left = substr(left, 1, RSTART - 1);
			}
		}

		return eval_math(int(left), op, int(right));
	}

	function eval_expr(left, op, right) {
		left = unquote(left);
		right = unquote(right);

		if (op == "==") {
			return left == right;

		} else if (op == "=~" || op == "~=") {
			return left ~ right;

		} else if (op == "!~") {
			return left !~ right;

		} else if (op == "!=") {
			return left != right;

		} else if (op == ">=") {
			return int(left) >= int(right);

		} else if (op == "<=") {
			return int(left) <= int(right);

		} else if (op == ">") {
			return int(left) > int(right);

		} else if (op == "<") {
			return int(left) < int(right);

		}
	}

	function process_expr(line,   cmd, left, op, right) {
		# proccess line. set: cmd, left, op, right
		if (match(line, /^#if +/)) {
			cmd = "if";
			line = substr(line, 1 + RLENGTH);

			# find left expression, operator, right expression
			if (match(line, "^.*("COND_OPS")")) {
				left = trim(substr(line, 1, RLENGTH));
				right = trim(substr(line, RSTART + RLENGTH));
				# find op from end of left
				if (match(left, "("COND_OPS")$")) {
					op = substr(left, RSTART, RLENGTH);
					left = substr(left, 1, RSTART - 1);
				}
			}
		} else if (match(line, /^#(else|endif) */)) {
			cmd = trim(substr(line, RSTART + 1, RLENGTH - 1));
			op = left = right = "";
		}

		# eval expression and change output control
		if (cmd == "if") {
			state_push(state_get() && eval_expr(left, op, right));
		} else if (cmd == "else") {
			state_set(!state_get());
		} else if (cmd == "endif") {
			state_pop();
		}
	}

	# functions for output control
	function state_push(flag) {
		_stack_depth++;
		_stack[_stack_depth] = flag;
	}
	function state_pop() {
		if (_stack_depth) {
			_stack_depth--;
		}
		return _stack[_stack_depth];
	}
	function state_set(flag) {
		_stack[_stack_depth] = flag;
	}
	function state_get() {
		return _stack[_stack_depth];
	}

	BEGIN {
		# import environ variables
		for (var in ENVIRON) {
			if (substr(var, 1, length("NAGIOS_")) == "NAGIOS_") {
				val = ENVIRON[var];
				var = substr(var, 1 + length("NAGIOS_"));
				# add to vars[] array
				vars[var] = val;
			}
		}

		# valid operands
		COND_OPS = "==|=~|~=|!~|!=|>=|<=|>|<";
		# valid variables in expression
		# - numbers
		# - strings in quotes
		# - regexps between //
		COND_VARS = "[0-9]+|\"[^\"]*\"|/[^/]+/";

		# valid mathematical operands we support
		MATH_OPS = "\+|-|\*|/"
		MATH_EXPR = "[0-9]+"

		# by default we have no condition set
		_stack_depth = 0;
		_stack[_stack_depth] = 1;
	}

	{
		# replace nagios macros
		for (var in vars) {
			val = vars[var];
			gsub("\$" var "\$", val);
		}

		if (match($0, "^#(if *("COND_VARS") *("COND_OPS") *("COND_VARS") *$|else|endif)")) {
			process_expr($0);
			next;
		}

		if (!state_get()) {
			next;
		}

		# $(base64:/path/to/file)
		while (match($0, /\$\(base64:([^)]+)\)/)) {
			pos = length("$(base64:")
			file = substr($0, RSTART + pos, RLENGTH - pos - 1);
			left = substr($0, 0, RSTART);
			right = substr($0, RSTART + RLENGTH);
			$0 = left base64(file) right;
		}

		# $(encode_mime_header:CHARSET,DATA)
		while (match($0, /\$\(encode_mime_header:([^,]*,[^)]+)\)/)) {
			pos = length("$(encode_mime_header:")
			params = substr($0, RSTART + pos, RLENGTH - pos - 1);
			left = substr($0, 0, RSTART);
			right = substr($0, RSTART + RLENGTH);
			charset = substr(params, 1, index(params, ",") - 1)
			data = substr(params, index(params, ",") + 1)
			$0 = left encode_mime_header(charset, data) right;
		}
		# $(urlencode:data)
		while (match($0, /\$\(urlencode:([^)]+)\)/)) {
			pos = length("$(urlencode:")
			data = substr($0, RSTART + pos, RLENGTH - pos - 1);
			left = substr($0, 0, RSTART);
			right = substr($0, RSTART + RLENGTH);
			$0 = left urlencode(data) right;
		}

		# $(math:expression)
		while (match($0, "\$\(math:("MATH_EXPR") *("MATH_OPS") *("MATH_EXPR")\)")) {
			pos = length("$(math:")
			expr = substr($0, RSTART + pos, RLENGTH - pos - 1);
			left = substr($0, 0, RSTART);
			right = substr($0, RSTART + RLENGTH);
			$0 = left math(expr) right;
		}

		# print out
		print;
	}
	' $tmpl
}


if [ "${NAGIOS_STATUSDATAFILE+set}" = set ]; then
	NAGIOS_STATUSDATAFILE=${NAGIOS_STATUSDATAFILE:-/var/lib/nagios/status.dat}
else
	echo >&2 "$prog: This program must be ran from Nagios."
	exit 1
fi

# extract Nagios version from status file
export NAGIOS_VERSION=$(awk -F= '/version=/ && !/_version/{print $2}' $NAGIOS_STATUSDATAFILE)

tmpl="$templatedir/$1.tmpl"
if [ ! -f "$tmpl" ]; then
	echo >&2 "$prog: template '$tmpl' can not be found!"
	exit 1
fi

# Save ENV to temp file for debugging
#debug=1
if [ "$debug" = 1 ]; then
	tmp=$(mktemp -t nagios-notify.env.XXXXXX)
	env | LC_ALL=C sort | awk -F= '/NAGIOS_/{printf("export %s=%c%s%c\n", $1, 39, $2, 39)}' > $tmp
fi

template_subst "$tmpl"
