File: 1.04.8a/server/base/freenats.inc.php (View as HTML)

  1: <?php // freenats.inc.php
  2: /* -------------------------------------------------------------
  3: This file is part of FreeNATS
  4: 
  5: FreeNATS is (C) Copyright 2008-2009 PurplePixie Systems
  6: 
  7: FreeNATS is free software: you can redistribute it and/or modify
  8: it under the terms of the GNU General Public License as published by
  9: the Free Software Foundation, either version 3 of the License, or
 10: (at your option) any later version.
 11: 
 12: FreeNATS is distributed in the hope that it will be useful,
 13: but WITHOUT ANY WARRANTY; without even the implied warranty of
 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15: GNU General Public License for more details.
 16: 
 17: You should have received a copy of the GNU General Public License
 18: along with FreeNATS.  If not, see www.gnu.org/licenses
 19: 
 20: For more information see www.purplepixie.org/freenats
 21: -------------------------------------------------------------- */
 22: 
 23: class TFreeNATS
 24: {
 25: var $init=false;
 26: var $DB;
 27: var $Cfg;
 28: var $Tests;
 29: var $Version="1.04.8";
 30: var $Release="a";
 31: var $EventHandlers=array();
 32: 
 33: function TFreeNATS()
 34: 	{
 35: 	$this->Tests=new TNATS_Tests(); // need this available during the include phase pre-start
 36: 	}
 37: 
 38: function Start()
 39: 	{
 40: 	global $BaseDir,$fnSkipSiteIncludes;
 41: 	if ( (!isset($BaseDir)) || ($BaseDir=="") ) $BaseDir="./";
 42: 	$this->DB=new TNATS_DB();
 43: 	$this->Cfg=new TNATS_Cfg();
 44: 	$this->DB->Connect();
 45: 	$this->Cfg->Load($this->DB);
 46: 	// Site Includes
 47: 	// Tests
 48: 	if ( (!isset($fnSkipSiteIncludes)) || ($fnSkipSiteIncludes === false) )
 49: 		{
 50: 	
 51: 		if ($this->Cfg->Get("site.include.tests",0)==1)
 52: 			{
 53: 			foreach(glob($BaseDir."site/tests/*.php") as $phpfile)
 54: 				{
 55: 				$this->Event("Including ".$phpfile,10,"NATS","Start");
 56: 				include_once($phpfile);
 57: 				}
 58: 			}
 59: 		// Events
 60: 		if ($this->Cfg->Get("site.include.events",0)==1)
 61: 			{
 62: 			foreach(glob($BaseDir."site/events/*.php") as $phpfile)
 63: 				{
 64: 				$this->Event("Including ".$phpfile,10,"NATS","Start");
 65: 				include_once($phpfile);
 66: 				}
 67: 			}
 68: 		}
 69: 		
 70: 	$this->init=true;
 71: 	}
 72: 	
 73: function Stop()
 74: 	{
 75: 	$t=$this->Cfg->Get("freenats.tracker","");
 76: 	if ( ($t>0) && ($t!="") )
 77: 		{
 78: 		$day=date("Ymd");
 79: 		if ($this->Cfg->Get("freenats.tracker.last","")!=$day)
 80: 			{
 81: 			$this->Cfg->Set("freenats.tracker.last",$day);
 82: 			$this->PhoneHome();
 83: 			}
 84: 		}
 85: 		
 86: 	$this->DB->Disconnect();
 87: 	$this->init=false;
 88: 	}	
 89: 	
 90: function Event($logevent,$loglevel=1,$modid="NONE",$catid="NONE")
 91: 	{
 92: 	global $NATS_Session;
 93: 	if ((isset($NATS_Session))&&($NATS_Session->auth)) $username=$NATS_Session->username;
 94: 	else $username="";
 95: 	$l=$this->Cfg->Get("log.level");
 96: 	//echo "** $l **\n";
 97: 	if ( $l=="" ) $l=10; // debug logging if no variable
 98: 	if ( $l < $loglevel ) return false;
 99: 	if (strlen($logevent)>249) $logevent=substr($logevent,0,245)."...";
100: 	$q="INSERT INTO fnlog(postedx,modid,catid,loglevel,logevent,username) VALUES(".time().",";
101: 	$q.="\"".ss($modid)."\",\"".ss($catid)."\",".ss($loglevel).",\"".ss($logevent)."\",\"".ss($username)."\")";
102: 	//echo $q;
103: 	$this->DB->Query($q,false);
104: 	}
105: 
106: function AlertAction($nodeid,$alertlevel,$change,$alerttext)
107: 	{
108: 	//echo "Called for node: ".$nodeid."\n";
109: 	if ($change==0) return false; // no change
110: 	if (trim($alerttext)=="") return false; // no text so a bogus event i.e. -1 to 0
111: 	//echo $nodeid.":".$alertlevel.":".$change.":".$alerttext."\n";
112: 	// get all the alertactions for this node id
113: 	$q="SELECT aaid FROM fnnalink WHERE nodeid=\"".ss($nodeid)."\"";
114: 	$r=$this->DB->Query($q);
115: 	while ($arow=$this->DB->Fetch_Array($r))
116: 		{
117: 		// get details for this alert action
118: 		$aq="SELECT * FROM fnalertaction WHERE aaid=".$arow['aaid']." LIMIT 0,1";
119: 		
120: 		$ar=$this->DB->Query($aq);
121: 		$aa=$this->DB->Fetch_Array($ar);
122: 		$this->DB->Free($ar);
123: 		
124: 		// UGGGGGGGG continue!!
125: 		// if the type is blank or disabled skip
126: 		if ( ($aa['atype']=="") || ($aa['atype']=="Disabled") ) continue;
127: 		// if warnings aren't set and it is a warning skip
128: 		if ( ($aa['awarnings']==0) && ($alertlevel==1) ) continue;
129: 		// if decreases aren't set and it is a decrease skip
130: 		if ( ($aa['adecrease']==0) && ($change<1) ) continue;
131: 		// if has a schedule and it dictates not to run now then skip
132: 		if (($aa['scheduleid']!=0)&&(!run_x_in_schedule(time(),$aa['scheduleid']))) continue;
133: 		
134: 		// made it this far
135: 		if ($aa['mdata']!="") $ndata=$aa['mdata']."\n".$nodeid.": ".$alerttext;
136: 		else $ndata=$nodeid.": ".$alerttext;
137: 		$uq="UPDATE fnalertaction SET mdata=\"".ss($ndata)."\" WHERE aaid=".$arow['aaid'];
138: 		//echo $uq."\n";
139: 		$this->DB->Query($uq);
140: 		}
141: 	}
142: 	
143: function ActionFlush()
144: 	{
145: 	global $allowed,$BaseDir; // allowed chars from screen in YA BODGE
146: 	$q="SELECT * FROM fnalertaction WHERE mdata!=\"\"";
147: 	$r=$this->DB->Query($q);
148: 	while ($row=$this->DB->Fetch_Array($r))
149: 		{
150: 			
151: 			$doalert=true;
152: 			
153: 		// clear mdata right at the start to get around duplicate emails whilst processing
154: 			$q="UPDATE fnalertaction SET mdata=\"\" WHERE aaid=".$row['aaid'];
155: 			$this->DB->Query($q);
156: 			
157: 			if ($this->DB->Affected_Rows()<=0) // already flushed or failed to flush
158: 				{
159: 				$doalert=false;
160: 				$this->Event("Alert Action Already Flushed - Skipping",8,"Flush","Action");
161: 				}
162: 			
163: 		// alert counter
164: 		$td=date("Ymd");
165: 		if ($td!=$row['ctrdate']) // new day or no flush record
166: 			{
167: 			$q="UPDATE fnalertaction SET ctrdate=\"".$td."\",ctrtoday=1 WHERE aaid=".$row['aaid'];
168: 			$this->DB->Query($q);
169: 			}
170: 		else
171: 			{
172: 			
173: 				if ( ($row['ctrlimit']==0) || ($row['ctrlimit']>$row['ctrtoday']) ) // no limit or below
174: 					{
175: 					$q="UPDATE fnalertaction SET ctrtoday=ctrtoday+1 WHERE aaid=".$row['aaid'];
176: 					$this->DB->Query($q);
177: 					}
178: 				else // at or over limit
179: 					{
180: 					$this->Event("Alert Action Limit Reached - Skipping",2,"Flush","Action");
181: 					$doalert=false;
182: 					}
183: 			
184: 			}
185: 			
186: 			
187: 		if ($row['atype']=="email")
188: 			{
189: 			if ($row['esubject']==0) $sub="";
190: 			else if ($row['esubject']==1) $sub=$this->Cfg->Get("alert.subject.short","FreeNATS Alert");
191: 			else $sub=$this->Cfg->Get("alert.subject.long","** FreeNATS Alert **");
192: 			$body="";
193: 			if ($row['etype']==0) $body=$row['mdata'];
194: 			else 
195: 				{
196: 				$body=$this->Cfg->Get("alert.body.header","FreeNATS Alert,");
197: 				$body.="\r\n\r\n".$row['mdata']."\r\n\r\n";
198: 				$body.=$this->Cfg->Get("alert.body.footer","");
199: 				$body.="\r\n-- FreeNATS @ ".nicedt(time());
200: 				}
201: 			//$tolist=preg_split("[\n\r]",$row['etolist']);
202: 			$tolist=array();
203: 			$f=0;
204: 			$tolist[0]="";
205: 			for ($a=0; $a<strlen($row['etolist']); $a++)
206: 				{
207: 				$chr=$row['etolist'][$a];
208: 				//echo $chr;
209: 				if (strpos($allowed,$chr)===false) // special char
210: 					{
211: 					$f++;
212: 					$tolist[$f]="";
213: 					}
214: 				else
215: 					{
216: 					$tolist[$f].=$chr;
217: 					}
218: 				}
219: 			
220: 			foreach($tolist as $toaddr)
221: 				{
222: 				$toaddr=nices($toaddr);
223: 				if ($toaddr!="")
224: 					{
225: 					$smtpserver=$this->Cfg->Get("mail.smtpserver","");
226: 					if ($smtpserver=="")
227: 						{
228: 						// mail() method - local delivery
229: 						$header="From: ".$row['efrom']."\r\n";
230: 						if ($doalert)
231: 							{
232: 							mail($toaddr,$sub,$body,$header);
233: 							$this->Event("Sent alert email to ".$toaddr,4,"Flush","Email");
234: 							}		
235: 						}
236: 					else // use phpmailer direct SMTP delivery
237: 						{
238: 						include_once($BaseDir."phpmailer/class.phpmailer.php");
239: 						$fromname=$this->Cfg->Get("mail.fromname","");
240: 						if ($fromname=="") $fromname="FreeNATS";
241: 						$smtpusername=$this->Cfg->Get("mail.smtpusername",""); // removed .
242: 						if ($smtpusername!="") $smtpauth=true;
243: 						else $smtpauth=false;
244: 						$smtppassword=$this->Cfg->Get("mail.smtppassword",""); // removed .
245: 						$smtphostname=$this->Cfg->Get("mail.smtphostname",""); // removed .
246: 						$mail=new PHPMailer();
247: 						$mail->IsSMTP();
248: 						$mail->Host=$smtpserver;
249: 						$mail->From=$row['efrom'];
250: 						$mail->FromName=$fromname;
251: 						$mail->AddAddress($toaddr);
252: 						$mail->Subject=$sub;
253: 						$mail->Body=$body;
254: 						$mail->WordWrap=50;
255: 						if ($smtphostname!="") $mail->Hostname=$smtphostname;
256: 						if ($smtpauth)
257: 							{
258: 							$mail->SMTPAuth=true;
259: 							$mail->Username=$smtpusername;
260: 							$mail->Password=$smtppassword;
261: 							}
262: 						if (!$mail->Send())
263: 							{ // failed
264: 							$this->Event("phpMailer to ".$toaddr." failed",1,"Flush","Email");
265: 							$this->Event("phpMailer Error: ".$mail->ErrorInfo,1,"Flush","Email");
266: 							}
267: 						else
268: 							{
269: 							$this->Event("phpMailer Sent Email To ".$toaddr,4,"Flush","Email");
270: 							}
271: 						}
272: 						
273: 					}
274: 				}
275: 				
276: 				
277: 				
278: 			}
279: 		else if ($row['atype']=="url")
280: 			{
281: 			// url send
282: 			if ($row['etype']==0) $body=$row['mdata'];
283: 			else $body="FreeNATS Alert,\r\n".$row['mdata']."\r\n--FreeNATS @ ".nicedt(time());
284: 			
285: 			$body=urlencode($body);
286: 			$tolist=array();
287: 			$f=0;
288: 			$tolist[0]="";
289: 			for ($a=0; $a<strlen($row['etolist']); $a++)
290: 				{
291: 				$chr=$row['etolist'][$a];
292: 				//echo $chr;
293: 				if (strpos($allowed,$chr)===false) // special char
294: 					{
295: 					$f++;
296: 					$tolist[$f]="";
297: 					}
298: 				else
299: 					{
300: 					$tolist[$f].=$chr;
301: 					}
302: 				}
303: 			
304: 			foreach($tolist as $tourl)
305: 				{
306: 				if ($doalert)
307: 					{
308: 					$url=$tourl.$body;
309: 					$fp=@fopen($url,"r");
310: 					if ($fp>0) fclose($fp);
311: 					else $this->Event("URL Alert Failed ".$url,1,"Flush","URL");
312: 					$this->Event("URL Alert ".$url,4,"Flush","URL");
313: 					}
314: 				}
315: 			
316: 			
317: 			}
318: 		else if ($row['atype']=="mqueue")
319: 			{
320: 			// message queue
321: 			$eventdata=array("aaid"=>$row['aaid'],"name"=>$row['aname'],"data"=>$row['mdata']);
322: 			$result=$this->EventHandler("alert_action",$eventdata);
323: 			if ($result===false) // put the data back into the queue
324: 				{
325: 				$q="UPDATE fnalertaction SET mdata=\"".$row['mdata']."\"+mdata WHERE aaid=".$row['aaid'];
326: 				$this->DB->Query($q);
327: 				if ($this->DB->Affected_Rows()<=0)
328: 					$this->Event("Persist MDATA Failed for AAID ".$row['aaid'],2,"Flush","MQueue");
329: 				}
330: 			else $this->Event("Queue Cleared for AAID ".$row['aaid']." by Handler",4,"Flush","MQueue");
331: 			}
332: 			
333: 		}
334: 	}	
335: 
336: function GetAlerts()
337: 	{
338: 	$q="SELECT nodeid,alertlevel FROM fnalert WHERE closedx=0";
339: 	$r=$this->DB->Query($q);
340: 	$c=0;
341: 	$al=array();
342: 	while ($row=$this->DB->Fetch_Array($r))
343: 		{
344: 		$al[$c]['nodeid']=$row['nodeid'];
345: 		$al[$c]['alertlevel']=$row['alertlevel'];
346: 		$c++;
347: 		}
348: 	if ($c>0) return $al;
349: 	else return false;
350: 	}
351: 	
352: function SetAlerts($nodeid,$alertlevel,$alerts="")
353: 	{
354: 	if ($alerts=="") $alerts=array();
355: 	// get current alert level
356: 	$q="SELECT alertlevel,nodealert FROM fnnode WHERE nodeid=\"".ss($nodeid)."\"";
357: 	$r=$this->DB->Query($q);
358: 	$row=$this->DB->Fetch_Array($r);
359: 	$this->DB->Free($r);
360: 	$cal=$row['alertlevel'];
361: 	
362: 	$eventdata=array("nodeid"=>$nodeid,"alertlevel"=>$alertlevel,
363: 		"oldalertlevel"=>$cal);
364: 	$this->EventHandler("set_alerts",$eventdata);
365: 		
366: 	if ($alertlevel!=$cal)
367: 		{
368: 		// update table
369: 		$q="UPDATE fnnode SET alertlevel=".ss($alertlevel)." WHERE nodeid=\"".ss($nodeid)."\"";
370: 		$this->DB->Query($q);
371: 		}
372: 		
373: 	// do not continue if node alert isn't set
374: 	if ($row['nodealert']!=1) return 0;
375: 	// or if untested
376: 	if ($alertlevel<0) return 0;
377: 		
378: 	// ALERTS
379: 	// is there an existing alert for this node
380: 	$q="SELECT alertid,alertlevel FROM fnalert WHERE nodeid=\"".ss($nodeid)."\" AND closedx=0";
381: 	$r=$this->DB->Query($q);
382: 	if ($row=$this->DB->Fetch_Array($r))
383: 		{ // yes there is
384: 		// if new alert level is 0 let's close it
385: 		if ($alertlevel==0)
386: 			{
387: 			$alertid=$row['alertid'];
388: 			$q="UPDATE fnalert SET closedx=".time()." WHERE alertid=".$row['alertid'];
389: 			$this->DB->Query($q);
390: 			$closetext=$this->Cfg->Get("site.text.closed","Alert Closed");
391: 			if (is_array($alerts)) $alerts[]=$closetext;
392: 			else
393: 				{
394: 				$alerts=array($alerts); // add as first element to new array
395: 				$alerts[]=$closetext;
396: 				}
397: 			$eventdata=array("nodeid"=>$nodeid);
398: 			$this->EventHandler("alert_close",$eventdata);
399: 			}
400: 		else
401: 			{
402: 			$alertid=$row['alertid'];
403: 			// otherwise update the alert to the new value (was: regardless, now just if not a 0)
404: 			$q="UPDATE fnalert SET alertlevel=".ss($alertlevel)." WHERE alertid=".$alertid;
405: 			$this->DB->Query($q);
406: 			}
407: 		}
408: 	else
409: 		{ // no there's not
410: 		$cal=0; // the cal (current alert level) goes to zero if it's a new alert so alert_actions fire ok
411: 		if ($alertlevel>0) // only if an actual alert
412: 			{
413: 			$q="INSERT INTO fnalert(nodeid,alertlevel,openedx) VALUES(";
414: 			$q.="\"".ss($nodeid)."\",".ss($alertlevel).",".time().")";
415: 			$this->DB->Query($q);
416: 			$alertid=$this->DB->Insert_Id();
417: 			$eventdata=array("nodeid"=>$nodeid);
418: 			$this->EventHandler("alert_open",$eventdata);
419: 			}
420: 		}
421: 	// ALERT LOG with $alertid
422: 	$t=time();
423: 	$at="";
424: 	if (is_array($alerts))
425: 		{
426: 		foreach($alerts as $alert)
427: 			{
428: 			if (isset($alertid)) // misses on manual runs methinx
429: 				{
430: 				if ($at!="") $at.=", ";
431: 				$at.=$alert;
432: 				//echo $at."\n";
433: 				$iq="INSERT INTO fnalertlog(alertid,postedx,logentry) VALUES(";
434: 				$iq.=$alertid.",".$t.",\"".ss($alert)."\")";
435: 				//echo $iq;
436: 				$this->DB->Query($iq);
437: 				}
438: 			}
439: 		}
440: 		
441: 	$this->AlertAction($nodeid,$alertlevel,$alertlevel-$cal,$at);
442: 	
443: 		
444: 		
445: 	}
446: 
447: function NodeAlertLevel($nodeid)
448: 	{
449: 	$q="SELECT alertlevel FROM fnnode WHERE nodeid=\"".ss($nodeid)."\"";
450: 	$r=$this->DB->Query($q);
451: 	if ($row=$this->DB->Fetch_Array($r)) return $row['alertlevel'];
452: 	else return -1;
453: 	}	
454: 
455: function GroupAlertLevel($groupid)
456: 	{
457: 	$lvl=-1;
458: 	$q="SELECT nodeid FROM fngrouplink WHERE groupid=\"".ss($groupid)."\"";
459: 	$r=$this->DB->Query($q);
460: 	while ($row=$this->DB->Fetch_Array($r))
461: 		{
462: 		$nl=$this->NodeAlertLevel($row['nodeid']);
463: 		if ($nl>$lvl) $lvl=$nl;
464: 		}
465: 	$this->DB->Free($r);
466: 	return $lvl;
467: 	}
468: 	
469: function PhoneHome($mode=0,$type="ping") // 0 - php, 1 - html, 2 - data
470: {
471: if ($mode<2)
472: 	{
473: 	$qs="?type=".$type."&data=v=".$this->Version;
474: 	if (isset($_SERVER['REMOTE_ADDR']))
475: 		$qs.=",ip=".$_SERVER['REMOTE_ADDR'];
476: 	$ploc="http://www.purplepixie.org/freenats/report/";
477: 	if ($mode==1) $ploc.="ping.html";
478: 	else $ploc.="ping.php";
479: 	
480: 	$ploc.=$qs;
481: 	
482: 	$lp=@fopen($ploc,"r");
483: 	if ($lp>0) @fclose($lp);
484: 	}
485: else
486: 	{
487: 	// data post -- !!
488: 	}
489: }
490: 
491: function GetNode($nodeid)
492: 	{
493: 	$return_row=false;
494: 	$q="SELECT * FROM fnnode WHERE nodeid=\"".ss($nodeid)."\" LIMIT 0,1";
495: 	$r=$this->DB->Query($q);
496: 	if ($row=$this->DB->Fetch_Array($r))
497: 		$return_row=true;
498: 		
499: 	$this->DB->Free($r);
500: 	if ($return_row) // found a valid
501: 		{
502: 		if ($row['nodename']!="") $row['name']=$row['nodename']; // make a "nice" name for it
503: 		else $row['name']=$row['nodeid'];
504: 		
505: 		$row['alerttext']=oText($row['alertlevel']); // textual alert status
506: 		
507: 		$row['lastrundt']=nicedt($row['lastrunx']); // text date-time last run
508: 		$row['lastrunago']=dtago($row['lastrunx'],false); // last run ago
509: 		
510: 		// protection
511: 		$row['nskey']="";
512: 		
513: 		return $row;
514: 		}
515: 	else
516: 		return false; // or failed
517: 	}
518: 	
519: function SetNode($nodeid,$data)
520: 	{
521: 	$q="UPDATE fnnode SET ";
522: 	$first=true;
523: 	foreach($data as $key => $val)
524: 		{
525: 		if ($first) $first=false;
526: 		else $q.=",";
527: 		$q.=ss($key)."=\"".ss($val)."\"";
528: 		}
529: 	$q.=" WHERE nodeid=\"".ss($nodeid)."\"";
530: 	$this->DB->Query($q);
531: 	if ($this->DB->Affected_Rows()>0) return true;
532: 	
533: 	if ($this->DB->Error()) // query failed
534: 		{
535: 		$errstr1="Query Failed: ".$q;
536: 		$errstr2="Query Failed: ".$this->DB->Error_String();
537: 		$this->Event($errstr1,2,"Node","Set");
538: 		$this->Event($errstr1,2,"Node","Set");
539: 		return false;
540: 		}
541: 	return true; // query succeeded but nothing was updated
542: 	}
543: 	
544: function EnableNode($nodeid,$enabled=true)
545: 	{
546: 	if ($enabled) $ne=1;
547: 	else $ne=0;
548: 	$data=array("nodeenabled"=>$ne);
549: 	return $this->SetNode($nodeid,$data);
550: 	}
551: 	
552: function DisableNode($nodeid)
553: 	{
554: 	return $this->EnableNode($nodeid,false);
555: 	}
556: 	
557: function SetNodeSchedule($nodeid,$scheduleid)
558: 	{
559: 	$data=array("scheduleid"=>$scheduleid);
560: 	return $this->SetNode($nodeid,$data);
561: 	}
562: 	
563: function GetGroup($groupid)
564: 	{
565: 	$q="SELECT * FROM fngroup WHERE groupid=".ss($groupid)." LIMIT 0,1";
566: 	$r=$this->DB->Query($q);
567: 	if (!$row=$this->DB->Fetch_Array($r)) return false;
568: 	
569: 	$this->DB->Free($r);
570: 	$row['alertlevel']=$this->GroupAlertLevel($groupid);
571: 	$row['alerttext']=oText($row['alertlevel']);
572: 	return $row;
573: 	}
574: 	
575: function GetTest($testid,$protect=false)
576: 	{
577: 	if ($testid=="") return false;
578: 	$class=$testid[0];
579: 	if (is_numeric($class))
580: 		{
581: 		// test ID will stay the same
582: 		$class="L";
583: 		$anytestid=$testid;
584: 		}
585: 	else
586: 		{
587: 		//$testid=substr($testid,1); // as it will here also so direct use to graphs can be made
588: 		$anytestid=substr($testid,1); // the classless version
589: 		}
590: 		
591: 	$q="";
592: 	switch($class)
593: 		{
594: 		case "L": // local tests 
595: 			$q="SELECT * FROM fnlocaltest WHERE localtestid=".ss($anytestid)." LIMIT 0,1";
596: 			break;
597: 		case "N": // node-side test
598: 			$q="SELECT * FROM fnnstest WHERE nstestid=".ss($anytestid)." LIMIT 0,1";
599: 			break;
600: 		default:
601: 			return false; // can't lookup this class
602: 		}
603: 		
604: 	if ($q=="") return false;
605: 	
606: 	$r=$this->DB->Query($q);
607: 	
608: 	if (!$row=$this->DB->Fetch_Array($r)) return false;
609: 	
610: 	$row['class']=$class;
611: 	$row['testid']=$testid;
612: 	$row['anytestid']=$anytestid;
613: 	$row['alerttext']=oText($row['alertlevel']);
614: 	$row['lastrundt']=nicedt($row['lastrunx']);
615: 	$row['lastrunago']=dtago($row['lastrunx'],false);
616: 	
617: 	if  ($row['testname']!="")  $row['name']=$row['testname'];
618: 	else
619: 			{
620: 			if ($class=="L")
621: 				{
622: 				$row['name']=lText($row['testtype']); // TODO OTHER TESTS
623: 				if ($row['testparam']!="") $row['name'].=" (".$row['testparam'].")";
624: 				}
625: 			else if ($class=="N")
626: 				{
627: 				if ($row['testdesc']!="") $row['name']=$row['testdesc'];
628: 				else $row['name']=$row['testtype'];
629: 				}
630: 			}
631: 			
632: 	if ($protect&&($class=="L")) // module test protection
633: 		{
634: 		if ($this->Tests->Exists($row['testtype'])) // in the module register
635: 			{
636: 			$this->Tests->Tests[$row['testtype']]->Create();
637: 			$this->Tests->Tests[$row['testtype']]->instance->ProtectOutput($row);
638: 			}
639: 		}
640: 	
641: 	$this->DB->Free($r);
642: 	
643: 	return $row;
644: 	}
645: 
646: 	
647: function DeleteTest($testid)
648: 	{
649: 	if ($testid=="") return false;
650: 	$class=$testid[0];
651: 	if (is_numeric($class))
652: 		{
653: 		// test ID will stay the same
654: 		$class="L";
655: 		$anytestid=$testid;
656: 		}
657: 	else
658: 		{
659: 		$anytestid=substr($testid,1); // the classless version
660: 		}
661: 		
662: 	$q="";
663: 	switch($class)
664: 		{
665: 		case "L": // local tests 
666: 			$q="DELETE FROM fnlocaltest WHERE localtestid=".ss($anytestid);
667: 			break;
668: 		case "N": // node-side test
669: 			$q="DELETE FROM fnnstest WHERE nstestid=".ss($anytestid);
670: 			break;
671: 		default:
672: 			return false; // can't lookup this class
673: 		}
674: 		
675: 	if ($q=="") return false;
676: 	
677: 	$this->DB->Query($q);
678: 	$tests=$this->DB->Affected_Rows();
679: 	
680: 	$rq="DELETE FROM fnrecord WHERE testid=\"".ss($testid)."\"";
681: 	$this->DB->Query($rq);
682: 	$records=$this->DB->Affected_Rows();
683: 	
684: 	$eq="DELETE FROM fneval WHERE testid=\"".ss($testid)."\"";
685: 	$this->DB->Query($eq);
686: 	$eval=$this->DB->Affected_Rows();
687: 	
688: 	$s="Deleted test ".$testid." (".$tests." tests, ".$records." records, ".$eval." evaluators)";
689: 	$this->Event($s,6,"Test","Delete");
690: 	}
691: 	
692: 	
693: 	
694: function InvalidateTest($testid,$rightnow=false)
695: 	{
696: 	$class=$testid[0];
697: 	if (is_numeric($class)) $class="L";
698: 	else $testid=substr($testid,1);
699: 	if ($rightnow)
700: 		{
701: 		$nextx=time();
702: 		$q="UPDATE ";
703: 		if ($class=="L") $q.="fnlocaltest";
704: 		// other ones here
705: 		
706: 		$q.=" SET nextrunx=".$nextx." WHERE ";
707: 		
708: 		if ($class=="L") $q.="localtestid=".$testid;
709: 		// other ones here
710: 		
711: 		$this->DB->Query($q);
712: 		return true;
713: 		}
714: 	// otherwise use it's interval
715: 	$q="SELECT testinterval FROM ";
716: 	
717: 	if ($class=="L") $q.="fnlocaltest WHERE localtestid=";
718: 	// other ones here
719: 	
720: 	$q.=$testid;
721: 	$r=$this->DB->Query($q);
722: 	if ($row=$this->DB->Fetch_Array($r))
723: 		{
724: 		$this->DB->Free($r);
725: 		$nextx=next_run_x($row['testinterval']);
726: 		$q="UPDATE ";
727: 		if ($class=="L") $q.="fnlocaltest";
728: 		// other ones here
729: 		
730: 		$q.=" SET nextrunx=".$nextx." WHERE ";
731: 		
732: 		if ($class=="L") $q.="localtestid=".$testid;
733: 		// other ones here
734: 		
735: 		$this->DB->Query($q);
736: 		return true;
737: 		}
738: 	return false;
739: 	}		
740: 	
741: function InvalidateNode($nodeid,$rightnow=false,$testsaswell=false)
742: 	{
743: 	if ($rightnow)
744: 		{
745: 		$nextx=time();
746: 		$q="UPDATE fnnode SET nextrunx=".$nextx." WHERE nodeid=\"".ss($nodeid)."\"";
747: 		$this->DB->Query($q);
748: 		if ($testsaswell)
749: 			{
750: 			$q="UPDATE fnlocaltest SET nextrunx=".$nextx." WHERE nodeid=\"".ss($nodeid)."\"";
751: 			$this->DB->Query($q);
752: 			}
753: 		return true;
754: 		}
755: 	// otherwise set to it's interval
756: 	$q="SELECT testinterval FROM fnnode WHERE nodeid=\"".ss($nodeid)."\"";
757: 	$r=$this->DB->Query($q);
758: 	if ($row=$this->DB->Fetch_Array($r))
759: 		{
760: 		$nextx=next_run_x($row['testinterval']);
761: 		$uq="UPDATE fnnode SET nextrunx=".$nextx." WHERE nodeid=\"".ss($nodeid)."\"";
762: 		$this->DB->Query($uq);
763: 		$this->DB->Free($r);
764: 		if ($testsaswell)
765: 			{
766: 			$uq="UPDATE fnlocaltest SET nextrunx=".$nextx." WHERE nodeid=\"".ss($nodeid)."\"";
767: 			$this->DB->Query($uq);
768: 			}
769: 		return true;
770: 		}
771: 	return false;
772: 	}
773: 	
774: 	
775: function NodeSide_Pull($nodeid)
776: 	{
777: 	$eventdata=array("nodeid"=>$nodeid,"success"=>false);
778: 	$q="SELECT nsenabled,nspullenabled,nsurl,nskey,nsinterval FROM fnnode WHERE nodeid=\"".ss($nodeid)."\" LIMIT 0,1";
779: 	$r=$this->DB->Query($q);
780: 	if (!$row=$this->DB->Fetch_Array($r)) return false;
781: 	
782: 	$this->DB->Free($r);
783: 	
784: 	$url=$row['nsurl'];
785: 	if ($row['nskey']!="") $url.="?nodekey=".$row['nskey'];
786: 	//echo $url."\n";
787: 	$this->Event("NodeSide_Pull Started for ".$nodeid,10,"Node","Pull");
788: 	
789: 	$xmlobj=new TNodeXML();
790: 	
791: 	$fp=@fopen($url,"r");
792: 	if ($fp<=0)
793: 		{
794: 		$this->Event("Pull Failed URL ".$url,1,"Node","Pull");
795: 		$this->EventHandler("nodeside_pull",$eventdata);
796: 		return false;
797: 		}
798: 	$xml="";
799: 	while (!feof($fp))
800: 		{
801: 		$xml.=fgets($fp,4096);
802: 		}
803: 	if ($xml=="")
804: 		{
805: 		$this->EventHandler("nodeside_pull",$eventdata);
806: 		return false;
807: 		}
808: 	
809: 	//echo $xml;
810: 	
811: 	$result=$xmlobj->Parse($xml);
812: 	
813: 	if ($xmlobj->Error()!="")
814: 		{
815: 		$this->Event("NodeXML Error: ".$xmlobj->Error(),1,"Node","Pull");
816: 		$this->EventHandler("nodeside_pull",$eventdata);
817: 		return false;
818: 		}
819: 	$this->Event("NodeSide_Pull Fetched ".$xmlobj->Tests." tests for ".$nodeid,10,"Node","Pull");
820: 	// Now just to actually process it...
821: 	$eventdata['success']=true;
822: 	$this->EventHandler("nodeside_pull",$eventdata);
823: 	$this->NodeSide_Process($nodeid,$xmlobj);
824: 	return true;
825: 	}	
826: 	
827: function NodeSide_Process($nodeid,&$xmlobj)
828: 	{ // nodeid + takes a TNodeXML Object
829: 	$alvl=0;
830: 	$this->Event("NodeSide_Process for ".$nodeid,10,"Node","Pull");
831: 	$q="SELECT * FROM fnnstest WHERE nodeid=\"".ss($nodeid)."\"";
832: 	$r=$this->DB->Query($q);
833: 	$tests=array();
834: 	while ($row=$this->DB->Fetch_Array($r))
835: 		{
836: 		$tests[$row['testtype']]=$row;
837: 		if (isset($xmlobj->Catalogue[$row['testtype']]))
838: 			{ // this test is in the DB and catalogue
839: 			$tests[$row['testtype']]['incat']=true;
840: 			if ($row['testenabled']==1) // it is enabled - so we test it
841: 				{
842: 				if ($row['simpleeval']==1) $level=$xmlobj->Catalogue[$row['testtype']]['ALERTLEVEL']; // use provided level
843: 				else $level=nats_eval("N".$row['nstestid'],$xmlobj->Catalogue[$row['testtype']]['VALUE']);
844: 				$dbs="Nodeside ".$row['testtype']." on ".$row['nodeid']." = ".$level;
845: 				if ($level==0) $debuglev=8;
846: 				else if ($level>0) $debuglev=5;
847: 				else $debuglev=2;
848: 				$this->Event($dbs,$debuglev,"Node","Process");
849: 				
850: 				if ($level>$alvl) $alvl=$level;
851: 				
852: 				if ($row['testrecord']==1) // record it
853: 					{
854: 					$testvalue=$xmlobj->Catalogue[$row['testtype']]['VALUE'];
855: 					$testvalue=str_replace(",",".",$testvalue);
856: 					if (!is_numeric($testvalue)) $testvalue=0;
857: 					$iq="INSERT INTO fnrecord(testid,alertlevel,recordx,nodeid,testvalue) VALUES(";
858: 					$iq.="\"N".$row['nstestid']."\",".$level.",".time().",\"".$row['nodeid']."\",".$testvalue.")";
859: 					$this->DB->Query($iq);
860: 					if ($this->DB->Affected_Rows()<=0)
861: 						$this->Event("Nodeside ".$row['testtype']." Failed to Record",1,"Node","Process");
862: 					}
863: 					
864: 				// We don't do any alerting here - the tester will do that for us!
865: 				$uq="UPDATE fnnstest SET lastrunx=".time().",lastvalue=\"".ss($xmlobj->Catalogue[$row['testtype']]['VALUE'])."\",alertlevel=".$level." ";
866: 				$uq.="WHERE nstestid=".$row['nstestid'];
867: 				$this->DB->Query($uq);
868: 				if ($this->DB->Affected_Rows()<=0)
869: 						$this->Event("Nodeside ".$row['testtype']." Failed to Update or Same Values",5,"Node","Process");
870: 				
871: 				}
872: 					
873: 			// check to see if the desc has changed
874: 			if ($row['testdesc']!=$xmlobj->Catalogue[$row['testtype']]['DESC'])
875: 				{
876: 				$duq="UPDATE fnnstest SET testdesc=\"".ss($xmlobj->Catalogue[$row['testtype']]['DESC'])."\" WHERE nstestid=".$row['nstestid'];
877: 				$this->DB->Query($duq);
878: 				}
879: 				
880: 			}
881: 		else
882: 			{
883: 			// test in the DB but NOT in the catalogue
884: 			//$xmlobj->Catalogue[$row['testtype']]['incat']=false;
885: 			if ($row['testenabled']==1)
886: 				{ // enabled so shown in lists etc
887: 				// Update it to show failed status
888: 				$this->Event("No nodeside data for test N".$row['nstestid'],3,"Node","Process");
889: 				$uq="UPDATE fnnstest SET alertlevel=2,lastvalue=-1 WHERE nstestid=".$row['nstestid'];
890: 				$this->DB->Query($uq);
891: 				$alvl=2;
892: 				if ($row['testrecord']==1) // record it
893: 					{
894: 					$testvalue=-1;
895: 					$iq="INSERT INTO fnrecord(testid,alertlevel,recordx,nodeid,testvalue) VALUES(";
896: 					$iq.="\"N".$row['nstestid']."\",2,".time().",\"".$row['nodeid']."\",".$testvalue.")";
897: 					$this->DB->Query($iq);
898: 					if ($this->DB->Affected_Rows()<=0)
899: 						$this->Event("Nodeside ".$row['testtype']." Failed to Record",1,"Node","Process");
900: 					}
901: 				}
902: 			else // not enabled so simply delete
903: 				{
904: 				$this->DeleteTest("N".$row['nstestid']);
905: 				}
906: 			}
907: 		}
908: 	$this->DB->Free($r);
909: 	
910: 	
911: 	// and finally we look for new tests i.e. in the cat but not in the DB
912: 	foreach($xmlobj->Catalogue as $val)
913: 		{
914: 		$key=$val['NAME'];
915: 		if (!isset($tests[$key])) // not in the DB
916: 			{
917: 			$q="INSERT INTO fnnstest(nodeid,testtype,testdesc,lastvalue,lastrunx,alertlevel) ";
918: 			$q.="VALUES(\"".ss($nodeid)."\",\"".$key."\",\"".ss($val['DESC'])."\",\"".ss($val['VALUE'])."\",".time().",".ss($val['ALERTLEVEL']).")";
919: 			//echo $q."<br>";
920: 			$this->DB->Query($q);
921: 			}
922: 		}
923: 		
924: 	$eventdata=array("nodeid"=>$nodeid,"alertlevel"=>$alvl);
925: 	$this->EventHandler("nodeside_process",$eventdata);
926: 	
927: 	
928: 	}
929: 	
930: 	
931: function AddEventHandler($event,$function)
932: {
933: 	if (!isset($this->EventHandlers[$event])) $this->EventHandlers[$event]=array();
934: 	$this->EventHandlers[$event][]=$function;
935: }
936: 
937: function EventHandler($event,$data)
938: {
939: 	$res=false;
940: 	if ( isset($data) && is_array($data) ) $data['event']=$event;
941: 	
942: 	if (isset($this->EventHandlers[$event])) // handler(s) exist
943: 	{
944: 	for($a=0; $a<count($this->EventHandlers[$event]); $a++)
945: 		{
946: 		if (function_exists($this->EventHandlers[$event][$a]))
947: 			{
948: 			$this->Event("Event ".$event." -> ".$this->EventHandlers[$event][$a],6,"Event","Handler");
949: 			if($this->EventHandlers[$event][$a]($data)) $res=true; // persist true only
950: 			}
951: 		else
952: 			{
953: 			$t="Illegal Handler ".$this->EventHandlers[$event][$a]." for ".$event;
954: 			$this->Event($t,2,"Event","Handler");
955: 			//return false;
956: 			}
957: 		}
958: 	}
959: 	else return $res;
960: }
961: 	
962: }
963: ?>
964: