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