1<?php
2/*
3 +-------------------------------------------------------------------------+
4 | Copyright (C) 2004-2021 The Cacti Group                                 |
5 |                                                                         |
6 | This program is free software; you can redistribute it and/or           |
7 | modify it under the terms of the GNU General Public License             |
8 | as published by the Free Software Foundation; either version 2          |
9 | of the License, or (at your option) any later version.                  |
10 |                                                                         |
11 | This program is distributed in the hope that it will be useful,         |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of          |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           |
14 | GNU General Public License for more details.                            |
15 +-------------------------------------------------------------------------+
16 | Cacti: The Complete RRDtool-based Graphing Solution                     |
17 +-------------------------------------------------------------------------+
18 | This code is designed, written, and maintained by the Cacti Group. See  |
19 | about.php and/or the AUTHORS file for specific developer information.   |
20 +-------------------------------------------------------------------------+
21 | http://www.cacti.net/                                                   |
22 +-------------------------------------------------------------------------+
23*/
24
25function do_hook($name) {
26	$data = func_get_args();
27	$data = api_plugin_hook($name, $data);
28	return $data;
29}
30
31function do_hook_function($name,$parm=NULL) {
32	return api_plugin_hook_function($name, $parm);
33}
34
35function api_user_realm_auth($filename = '') {
36	return api_plugin_user_realm_auth($filename);
37}
38
39/**
40 * This function executes a hook.
41 * @param string $name Name of hook to fire
42 * @return mixed $data
43 */
44function api_plugin_hook($name) {
45	global $config, $plugin_hooks, $plugins_integrated;
46
47	static $hook_cache = array();
48
49	$args = func_get_args();
50	$ret = '';
51
52	if (defined('IN_CACTI_INSTALL') || !db_table_exists('plugin_hooks')) {
53		return $args;
54	}
55
56	if (!isset($hook_cache[$name])) {
57		/* order the plugins by order */
58		$result = db_fetch_assoc_prepared('SELECT ph.name, ph.file, ph.function
59			FROM plugin_hooks AS ph
60			LEFT JOIN plugin_config AS pc
61			ON pc.directory = ph.name
62			WHERE ph.status = 1
63			AND hook = ?
64			ORDER BY pc.id ASC',
65			array($name),
66			true
67		);
68
69		$hook_cache[$name] = $result;
70	} else {
71		$result = $hook_cache[$name];
72	}
73
74	if (!empty($result)) {
75		foreach ($result as $hdata) {
76			if (!in_array($hdata['name'], $plugins_integrated)) {
77				if (file_exists($config['base_path'] . '/plugins/' . $hdata['name'] . '/' . $hdata['file'])) {
78					include_once($config['base_path'] . '/plugins/' . $hdata['name'] . '/' . $hdata['file']);
79				}
80				$function = $hdata['function'];
81				if (function_exists($function)) {
82					api_plugin_run_plugin_hook($name, $hdata['name'], $function, $args);
83				}
84			}
85		}
86	}
87
88	/* Variable-length argument lists have a slight problem when */
89	/* passing values by reference. Pity. This is a workaround.  */
90	return $args;
91}
92
93function api_plugin_hook_function($name, $parm = NULL) {
94	global $config, $plugin_hooks, $plugins_integrated;
95
96	static $hook_cache = array();
97
98	$ret = $parm;
99
100	if (defined('IN_CACTI_INSTALL') || !db_table_exists('plugin_hooks')) {
101		return $ret;
102	}
103
104	if (!isset($hook_cache[$name])) {
105		/* order the plugins by order */
106		$result = db_fetch_assoc_prepared('SELECT ph.name, ph.file, ph.function
107			FROM plugin_hooks AS ph
108			LEFT JOIN plugin_config AS pc
109			ON pc.directory = ph.name
110			WHERE ph.status = 1
111			AND hook = ?
112			ORDER BY pc.id ASC',
113			array($name),
114			true
115		);
116
117		$hook_cache[$name] = $result;
118	} else {
119		$result = $hook_cache[$name];
120	}
121
122	if (empty($ret)) {
123		$null_ret = true;
124	} else {
125		$null_ret = false;
126	}
127
128	if (!empty($result)) {
129		foreach ($result as $hdata) {
130			if (!in_array($hdata['name'], $plugins_integrated)) {
131				$p[] = $hdata['name'];
132
133				if (file_exists($config['base_path'] . '/plugins/' . $hdata['name'] . '/' . $hdata['file'])) {
134					include_once($config['base_path'] . '/plugins/' . $hdata['name'] . '/' . $hdata['file']);
135				}
136
137				$function = $hdata['function'];
138
139				if (function_exists($function)) {
140					if (is_array($ret)) {
141						$is_array = true;
142					} else {
143						$is_array = false;
144					}
145
146					$ret = api_plugin_run_plugin_hook_function($name, $hdata['name'], $function, $ret);
147
148					if (($is_array && !is_array($ret)) || ($ret == null && $null_ret === false)) {
149						if (cacti_sizeof($result) > 1) {
150							cacti_log(sprintf("WARNING: Plugin hook '%s' from Plugin '%s' must return the calling array or variable, and it is not doing so.  Please report this to the Plugin author.", $function, $hdata['name']), false);
151						}
152					}
153				}
154			}
155		}
156	}
157
158	/* Variable-length argument lists have a slight problem when */
159	/* passing values by reference. Pity. This is a workaround.  */
160	return $ret;
161}
162
163function api_plugin_run_plugin_hook($hook, $plugin, $function, $args) {
164	global $config, $menu;
165
166	if ($config['poller_id'] > 1) {
167		// Let's control the menu
168		$orig_menu = $menu;
169
170		$required_capabilities = array(
171			// Poller related
172			'poller_top'               => array('remote_collect'), // Poller Top
173			'poller_bottom'            => array('remote_collect'), // Poller execution, api_plugin_hook
174			'update_host_status'       => array('remote_collect'), // Processing poller output, api_plugin_hook
175			'poller_output'            => array('remote_collect'), // Poller output activities
176			'poller_command_args'      => array('remote_collect'), // Command line arguments
177			'cacti_stats_update'       => array('remote_collect'), // Updating statistics
178			'poller_finishing'         => array('remote_collect'), // Poller post processing
179			'poller_exiting'           => array('remote_collect'), // Poller exception handling
180
181			// GUI Related
182			'page_head'                => array('online_view', 'offline_view'), // Navigation, api_plugin_hook
183			'top_header_tabs'          => array('online_view', 'offline_view'), // Top Tabs, api_plugin_hook
184			'top_graph_header_tabs'    => array('online_view', 'offline_view'), // Top Tabs, api_plugin_hook
185			'graph_buttons'            => array('online_view', 'offline_view'), // Buttons by graphs, api_plugin_hook
186			'graphs_new_top_links'     => array('online_mgmt', 'offline_mgmt'), // Buttons by graphs, api_plugin_hook
187			'page_head'                => array('online_view', 'offline_view')  // Content, api_plugin_hook
188		);
189
190		$plugin_capabilities = api_plugin_remote_capabilities($plugin);
191
192		if ($plugin_capabilities === false) {
193			$function($args);
194		} elseif (api_plugin_hook_is_remote_collect($hook, $plugin, $required_capabilities)) {
195			if (api_plugin_status_run($hook, $required_capabilities, $plugin_capabilities)) {
196				$function($args);
197			}
198		} elseif (isset($required_capabilities[$hook])) {
199			if (api_plugin_status_run($hook, $required_capabilities, $plugin_capabilities)) {
200				$function($args);
201			}
202		} else {
203			$function($args);
204		}
205
206		// See if we need to restore the menu to original
207		if (($hook == 'config_arrays' || 'config_insert') && $config['connection'] == 'offline') {
208			if (!api_plugin_has_capability($plugin, 'offline_mgmt')) {
209				if ($orig_menu !== $menu) {
210					$menu = $orig_menu;
211				}
212			}
213		}
214	} else {
215		$function($args);
216	}
217
218	return $args;
219}
220
221function api_plugin_run_plugin_hook_function($hook, $plugin, $function, $ret) {
222	global $config;
223
224	if ($config['poller_id'] > 1) {
225		$required_capabilities = array(
226			// Poller related
227			'poller_output'            => array('remote_collect'), // Processing poller output, api_plugin_hook_function
228
229			// GUI Related
230			'top_header'               => array('online_view', 'offline_view'), // Top Tabs, api_plugin_hook_function
231			'top_graph_header'         => array('online_view', 'offline_view'), // Top Tabs, api_plugin_hook_function
232			'rrd_graph_graph_options'  => array('online_view', 'offline_view'), // Buttons by graphs, api_plugin_hook_function
233			'data_sources_table'       => array('online_mgmt', 'offline_mgmt'), // Buttons by graphs, api_plugin_hook_function
234
235			'device_action_array'      => array('online_mgmt', 'offline_mgmt'), // Actions Dropdown, api_plugin_hook_function
236			'data_source_action_array' => array('online_mgmt', 'offline_mgmt'), // Actions Dropdown, api_plugin_hook_function
237			'graphs_action_array'      => array('online_mgmt', 'offline_mgmt'), // Actions Dropdown, api_plugin_hook_function
238		);
239
240		$plugin_capabilities = api_plugin_remote_capabilities($plugin);
241
242		// we will run if capabilities are not set
243		if ($plugin_capabilities === false) {
244			$ret = $function($ret);
245		// run if hooks is remote_collect and we support it
246		} elseif (api_plugin_hook_is_remote_collect($hook, $plugin, $required_capabilities)) {
247			if (api_plugin_status_run($hook, $required_capabilities, $plugin_capabilities)) {
248				$ret = $function($ret);
249			}
250		// run if hooks is remote_collect and we support it
251		} elseif (isset($required_capabilities[$hook])) {
252			if (api_plugin_status_run($hook, $required_capabilities, $plugin_capabilities)) {
253				$ret = $function($ret);
254			}
255		} else {
256			$ret = $function($ret);
257		}
258	} else {
259		$ret = $function($ret);
260	}
261
262	return $ret;
263}
264
265function api_plugin_hook_is_remote_collect($hook, $plugin, $required_capabilities) {
266	if (isset($required_capabilities[$hook])) {
267		foreach($required_capabilities[$hook] as $capability) {
268			if (strpos($capability, 'remote_collect') !== false) {
269				return true;
270			}
271		}
272	}
273
274	return false;
275}
276
277function api_plugin_get_dependencies($plugin) {
278	global $config;
279
280	$file = $config['base_path'] . '/plugins/' . $plugin . '/INFO';
281
282	$returndeps = array();
283
284	if (file_exists($file)) {
285		$info = parse_ini_file($file, true);
286
287		if (isset($info['info']['requires']) && trim($info['info']['requires']) != '') {
288			$parts = explode(' ', trim($info['info']['requires']));
289
290			foreach ($parts as $p) {
291				$vparts = explode(':', $p);
292				if (isset($vparts[1])) {
293					$returndeps[$vparts[0]] = $vparts[1];
294				} else {
295					$returndeps[$p] = true;
296				}
297			}
298
299			return $returndeps;
300		}
301	}
302
303	return false;
304}
305
306function api_plugin_minimum_version($plugin, $version) {
307	if (strlen($version)) {
308		$plugin_version = db_fetch_cell_prepared('SELECT version
309			FROM plugin_config
310			WHERE directory = ?',
311			array($plugin));
312
313		$result = cacti_version_compare($version, $plugin_version, '<=');
314	} else {
315		$plugin_version = '<not read>';
316		$result = true;
317	}
318
319	return $result;
320}
321
322function api_plugin_installed($plugin) {
323	$plugin_data = db_fetch_row_prepared('SELECT directory, status
324		FROM plugin_config
325		WHERE directory = ?',
326		array($plugin));
327
328	if (cacti_sizeof($plugin_data)) {
329		if ($plugin_data['status'] >= 1) {
330			return true;
331		}
332	}
333
334	return false;
335}
336
337function api_plugin_remote_capabilities($plugin) {
338	global $config, $info_data;
339
340	if ($plugin == 'internal') {
341		return 'online_view:1 online_mgmt:1 offline_view:1 offline_mgmt:1 remote_collect:1';
342	}
343
344	$file = $config['base_path'] . '/plugins/' . $plugin . '/INFO';
345
346	if (!isset($info_data[$plugin])) {
347		if (file_exists($file)) {
348			$info = parse_ini_file($file, true);
349
350			if (cacti_sizeof($info)) {
351				$info_data[$plugin] = $info['info'];
352			}
353		}
354	}
355
356	if (isset($info_data[$plugin]) && isset($info_data[$plugin]['capabilities'])) {
357		return $info_data[$plugin]['capabilities'];
358	} else {
359		return 'online_view:0 online_mgmt:0 offline_view:0 offline_mgmt:0 remote_collect:0';
360	}
361
362	return false;
363}
364
365function api_plugin_has_capability($plugin, $capability) {
366	$capabilities = api_plugin_remote_capabilities($plugin);
367
368	if (strpos($capabilities, "$capability:1") !== false) {
369		return true;
370	} else {
371		return false;
372	}
373}
374
375function api_plugin_status_run($hook, $required_capabilities, $plugin_capabilities) {
376	global $config;
377
378	$status = $config['connection'];
379
380	if (!isset($required_capabilities[$hook])) {
381		return true;
382	}
383
384	foreach($required_capabilities[$hook] as $capability) {
385		if ($status == 'online' && strpos($capability, 'online') === false) {
386			continue;
387		} elseif (($status == 'offline' || $status == 'recovery') && strpos($capability, 'offline') === false) {
388			continue;
389		}
390
391		if (strpos($plugin_capabilities, "$capability:1") !== false) {
392			return true;
393		}
394
395		switch($capability) {
396			case 'offline_view': // if the plugin has mgmt, it's assumed to have view
397				if (strpos($plugin_capabilities, "offline_mgmt:1") !== false) {
398					return true;
399				}
400
401				break;
402			case 'online_view': // if the plugin has mgmt, it's assumed to have view
403				if (strpos($plugin_capabilities, "offline_mgmt:1") !== false) {
404					return true;
405				}
406
407				break;
408			default:
409				break;
410		}
411	}
412
413	return false;
414}
415
416function api_plugin_db_table_create($plugin, $table, $data) {
417	global $config;
418
419	include_once($config['library_path'] . '/database.php');
420
421	$result = db_fetch_assoc('SHOW TABLES');
422	$tables = array();
423	foreach($result as $index => $arr) {
424		foreach ($arr as $t) {
425			$tables[] = $t;
426		}
427	}
428
429	if (!in_array($table, $tables)) {
430		$c = 0;
431		$sql = 'CREATE TABLE `' . $table . "` (\n";
432		foreach ($data['columns'] as $column) {
433			if (isset($column['name'])) {
434				if ($c > 0) {
435					$sql .= ",\n";
436				}
437
438				$sql .= '`' . $column['name'] . '`';
439
440				if (isset($column['type'])) {
441					$sql .= ' ' . $column['type'];
442				}
443
444				if (isset($column['unsigned'])) {
445					$sql .= ' unsigned';
446				}
447
448				if (isset($column['NULL']) && $column['NULL'] == false) {
449					$sql .= ' NOT NULL';
450				}
451
452				if (isset($column['NULL']) && $column['NULL'] == true && !isset($column['default'])) {
453					$sql .= ' default NULL';
454				}
455
456				if (isset($column['default'])) {
457					if (strtolower($column['type']) == 'timestamp' && $column['default'] === 'CURRENT_TIMESTAMP') {
458						$sql .= ' default CURRENT_TIMESTAMP';
459					} else {
460						$sql .= ' default ' . (is_numeric($column['default']) ? $column['default'] : "'" . $column['default'] . "'");
461					}
462				}
463
464				if (isset($column['auto_increment'])) {
465					$sql .= ' auto_increment';
466				}
467
468				$c++;
469			}
470		}
471
472		if (isset($data['primary'])) {
473			$sql .= ",\n PRIMARY KEY (`" . $data['primary'] . '`)';
474		}
475
476		if (isset($data['keys']) && cacti_sizeof($data['keys'])) {
477			foreach ($data['keys'] as $key) {
478				if (isset($key['name'])) {
479					$sql .= ",\n INDEX `" . $key['name'] . '` (' . db_format_index_create($key['columns']) . ')';
480				}
481			}
482		}
483
484		if (isset($data['unique_keys'])) {
485			foreach ($data['unique_keys'] as $key) {
486				if (isset($key['name'])) {
487					$sql .= ",\n UNIQUE INDEX `" . $key['name'] . '` (' . db_format_index_create($key['columns']) . ')';
488				}
489			}
490		}
491
492		$sql .= ') ENGINE = ' . $data['type'];
493
494		if (isset($data['row_format']) && db_get_global_variable('innodb_file_format') == 'Barracuda') {
495			$sql .= ' ROW_FORMAT = ' . $data['row_format'];
496		}
497
498		if (isset($data['comment'])) {
499			$sql .= " COMMENT = '" . $data['comment'] . "'";
500		}
501
502		if (db_execute($sql)) {
503			db_execute_prepared("REPLACE INTO plugin_db_changes
504				(plugin, `table`, `column`, `method`)
505				VALUES (?, ?, '', 'create')",
506				array($plugin, $table));
507
508			if (isset($data['collate'])) {
509				db_execute("ALTER TABLE `$table` COLLATE = " . $data['collate']);
510			}
511
512			if (isset($data['charset'])) {
513				db_execute("ALTER TABLE `$table` CHARSET = " . $data['charset']);
514			}
515		}
516	}
517}
518
519function api_plugin_db_changes_remove($plugin) {
520	$tables = db_fetch_assoc_prepared("SELECT `table`
521		FROM plugin_db_changes
522		WHERE plugin = ?
523		AND method ='create'",
524		array($plugin), false);
525
526	if (cacti_count($tables)) {
527		foreach ($tables as $table) {
528			db_execute('DROP TABLE IF EXISTS `' . $table['table'] . '`;');
529		}
530
531		db_execute_prepared("DELETE FROM plugin_db_changes
532			WHERE plugin = ?
533			AND method ='create'",
534			array($plugin), false);
535	}
536
537	$columns = db_fetch_assoc_prepared("SELECT `table`, `column`
538		FROM plugin_db_changes
539		WHERE plugin = ?
540		AND method ='addcolumn'",
541		array($plugin), false);
542
543	if (cacti_count($columns)) {
544		foreach ($columns as $column) {
545			db_execute('ALTER TABLE `' . $column['table'] . '` DROP `' . $column['column'] . '`');
546		}
547
548		db_execute_prepared("DELETE FROM plugin_db_changes
549			WHERE plugin = ?
550			AND method = 'addcolumn'",
551			array($plugin), false);
552	}
553}
554
555function api_plugin_db_add_column ($plugin, $table, $column) {
556	global $config, $database_default;
557
558	// Example: api_plugin_db_add_column ('thold', 'plugin_config',
559	//	array('name' => 'test' . rand(1, 200), 'type' => 'varchar (255)', 'NULL' => false));
560
561	include_once($config['library_path'] . '/database.php');
562
563	$result = db_fetch_assoc('SHOW COLUMNS FROM `' . $table . '`');
564	$columns = array();
565	foreach($result as $index => $arr) {
566		foreach ($arr as $t) {
567			$columns[] = $t;
568		}
569	}
570
571	if (isset($column['name']) && !in_array($column['name'], $columns)) {
572		$sql = 'ALTER TABLE `' . $table . '` ADD `' . $column['name'] . '`';
573
574		if (isset($column['type'])) {
575			$sql .= ' ' . $column['type'];
576		}
577
578		if (isset($column['unsigned'])) {
579			$sql .= ' unsigned';
580		}
581
582		if (isset($column['NULL']) && $column['NULL'] == false) {
583			$sql .= ' NOT NULL';
584		}
585
586		if (isset($column['NULL']) && $column['NULL'] == true && !isset($column['default'])) {
587			$sql .= ' default NULL';
588		}
589
590		if (isset($column['default'])) {
591			if (strtolower($column['type']) == 'timestamp' && $column['default'] === 'CURRENT_TIMESTAMP') {
592				$sql .= ' default CURRENT_TIMESTAMP';
593			} else {
594				$sql .= ' default ' . (is_numeric($column['default']) ? $column['default'] : "'" . $column['default'] . "'");
595			}
596		}
597
598		if (isset($column['auto_increment'])) {
599			$sql .= ' auto_increment';
600		}
601
602		if (isset($column['after'])) {
603			$sql .= ' AFTER ' . $column['after'];
604		}
605
606		if (db_execute($sql)) {
607			db_execute_prepared("INSERT INTO plugin_db_changes
608				(plugin, `table`, `column`, `method`)
609				VALUES (?, ?, ?, 'addcolumn')",
610				array($plugin, $table, $column['name']));
611		}
612	}
613}
614
615function api_plugin_can_install($plugin, &$message) {
616	$dependencies = api_plugin_get_dependencies($plugin);
617	$message = '';
618	$proceed = true;
619
620	if (is_array($dependencies) && cacti_sizeof($dependencies)) {
621		foreach($dependencies as $dependency => $version) {
622			if (!api_plugin_minimum_version($dependency, $version)) {
623				$message .= __('%s Version %s or above is required for %s. ', ucwords($dependency), $version, ucwords($plugin));
624
625				$proceed = false;
626			} else if (!api_plugin_installed($dependency)) {
627				$message .= __('%s is required for %s, and it is not installed. ', ucwords($dependency), ucwords($plugin));
628
629				$proceed = false;
630			}
631		}
632	}
633
634	return $proceed;
635}
636
637function api_plugin_install($plugin) {
638	global $config;
639
640	if (!defined('IN_CACTI_INSTALL')) {
641		define('IN_CACTI_INSTALL', 1);
642	}
643
644	$dependencies = api_plugin_get_dependencies($plugin);
645
646	$proceed = api_plugin_can_install($plugin, $message);
647
648	if (!$proceed) {
649		$message .= '<br><br>' . __('Plugin cannot be installed.');
650
651		raise_message($message, MESSAGE_LEVEL_ERROR);
652
653		header('Location: plugins.php?header=false');
654
655		exit;
656	}
657
658	include_once($config['base_path'] . "/plugins/$plugin/setup.php");
659
660	$exists = db_fetch_assoc_prepared('SELECT id
661		FROM plugin_config
662		WHERE directory = ?',
663		array($plugin), false);
664
665	if (cacti_sizeof($exists)) {
666		db_execute_prepared('DELETE FROM plugin_config
667			WHERE directory = ?',
668			array($plugin));
669	}
670
671	$name = $author = $webpage = $version = '';
672	$function = 'plugin_' . $plugin . '_version';
673
674	if (function_exists($function)){
675		$info = $function();
676		$name = $info['longname'];
677
678		if (isset($info['homepage'])) {
679			$webpage = $info['homepage'];
680		} elseif (isset($info['webpage'])) {
681			$webpage = $info['webpage'];
682		} else {
683			$webpage = 'Not Stated';
684		}
685
686		$author  = $info['author'];
687		$version = $info['version'];
688	}
689
690	db_execute_prepared('INSERT INTO plugin_config
691		(directory, name, author, webpage, version)
692		VALUES (?, ?, ?, ?, ?)',
693		array($plugin, $name, $author, $webpage, $version));
694
695	$function = 'plugin_' . $plugin . '_install';
696	if (function_exists($function)){
697		$function();
698		$ready = api_plugin_check_config ($plugin);
699		if ($ready) {
700			// Set the plugin as "disabled" so it can go live
701			db_execute_prepared('UPDATE plugin_config
702				SET status = 4
703				WHERE directory = ?',
704				array($plugin));
705		} else {
706			// Set the plugin as "needs configuration"
707			db_execute_prepared('UPDATE plugin_config
708				SET status = 2
709				WHERE directory = ?',
710				array($plugin));
711		}
712	}
713}
714
715function api_plugin_uninstall_integrated() {
716	global $config, $plugin_hooks, $plugins_integrated;
717
718	foreach($plugins_integrated as $plugin) {
719		api_plugin_uninstall($plugin, false);
720	}
721}
722
723function api_plugin_uninstall($plugin, $tables = true) {
724	global $config;
725
726	if (file_exists($config['base_path'] . "/plugins/$plugin/setup.php")) {
727		include_once($config['base_path'] . "/plugins/$plugin/setup.php");
728
729		// Run the Plugin's Uninstall Function first
730		$function = 'plugin_' . $plugin . '_uninstall';
731
732		if (function_exists($function)) {
733			$function();
734		}
735	}
736
737	api_plugin_remove_hooks($plugin);
738	api_plugin_remove_realms($plugin);
739
740	db_execute_prepared('DELETE FROM plugin_config
741		WHERE directory = ?',
742		array($plugin));
743
744	if ($tables) {
745		api_plugin_db_changes_remove($plugin);
746	} else {
747		db_execute_prepared('DELETE FROM plugin_db_changes
748			WHERE plugin = ?',
749			array($plugin));
750	}
751}
752
753function api_plugin_check_config($plugin) {
754	global $config;
755
756	clearstatcache();
757
758	if (file_exists($config['base_path'] . "/plugins/$plugin/setup.php")) {
759		include_once($config['base_path'] . "/plugins/$plugin/setup.php");
760
761		$function = 'plugin_' . $plugin . '_check_config';
762
763		if (function_exists($function)) {
764			return $function();
765		}
766
767		return true;
768	}
769
770	return false;
771}
772
773function api_plugin_enable($plugin) {
774	$ready = api_plugin_check_config($plugin);
775
776	if ($ready) {
777		api_plugin_enable_hooks($plugin);
778
779		db_execute_prepared('UPDATE plugin_config
780			SET status = 1
781			WHERE directory = ?',
782			array($plugin));
783	}
784}
785
786function api_plugin_is_enabled($plugin) {
787	$status = db_fetch_cell_prepared('SELECT status
788		FROM plugin_config
789		WHERE directory = ?',
790		array($plugin), false);
791
792	if ($status == '1') {
793		return true;
794	}
795
796	return false;
797}
798
799function api_plugin_disable($plugin) {
800	api_plugin_disable_hooks($plugin);
801
802	db_execute_prepared('UPDATE plugin_config
803		SET status = 4
804		WHERE directory = ?',
805		array($plugin));
806}
807
808function api_plugin_disable_all($plugin) {
809	api_plugin_disable_hooks_all($plugin);
810
811	db_execute_prepared('UPDATE plugin_config
812		SET status = 4
813		WHERE directory = ?',
814		array($plugin));
815}
816
817function api_plugin_moveup($plugin) {
818	$id = db_fetch_cell_prepared('SELECT id
819		FROM plugin_config
820		WHERE directory = ?',
821		array($plugin));
822
823	if (!empty($id)) {
824		$temp_id = db_fetch_cell('SELECT MAX(id) FROM plugin_config')+1;
825
826		$prior_id = db_fetch_cell_prepared('SELECT MAX(id)
827			FROM plugin_config
828			WHERE id < ?',
829			array($id));
830
831		/* update the above plugin to the prior temp id */
832		db_execute_prepared('UPDATE plugin_config SET id = ? WHERE id = ?', array($temp_id, $prior_id));
833		db_execute_prepared('UPDATE plugin_config SET id = ? WHERE id = ?', array($prior_id, $id));
834		db_execute_prepared('UPDATE plugin_config SET id = ? WHERE id = ?', array($id, $temp_id));
835	}
836}
837
838function api_plugin_movedown($plugin) {
839	$id      = db_fetch_cell_prepared('SELECT id FROM plugin_config WHERE directory = ?', array($plugin));
840	$temp_id = db_fetch_cell('SELECT MAX(id) FROM plugin_config')+1;
841	$next_id = db_fetch_cell_prepared('SELECT MIN(id) FROM plugin_config WHERE id > ?', array($id));
842
843	/* update the above plugin to the prior temp id */
844	db_execute_prepared('UPDATE plugin_config SET id = ? WHERE id = ?', array($temp_id, $next_id));
845	db_execute_prepared('UPDATE plugin_config SET id = ? WHERE id = ?', array($next_id, $id));
846	db_execute_prepared('UPDATE plugin_config SET id = ? WHERE id = ?', array($id, $temp_id));
847}
848
849function api_plugin_register_hook($plugin, $hook, $function, $file, $enable = false) {
850	$status = 0;
851
852	$exists = db_fetch_cell_prepared('SELECT COUNT(*)
853		FROM plugin_hooks
854		WHERE name = ?
855		AND hook = ?',
856		array($plugin, $hook), false);
857
858	if (!$exists) {
859		// enable the hooks if they are system level hooks to enable configuration
860		$settings = array('config_settings', 'config_arrays', 'config_form');
861		$status   = (!in_array($hook, $settings) ? 0 : 1);
862
863		if ($enable) {
864			$status = 1;
865		}
866
867		db_execute_prepared('INSERT INTO plugin_hooks
868			(name, hook, `function`, file, status)
869			VALUES (?, ?, ?, ?, ?)',
870			array($plugin, $hook, $function, $file, $status));
871	} else {
872		if ($enable == true) {
873			$status = 1;
874		}
875
876		// enable the hook automatically if other hooks are already enabled
877		// for this plugin.
878		if (!$status) {
879			$exists = db_fetch_cell_prepared('SELECT COUNT(*)
880				FROM plugin_hooks
881				WHERE name = ?
882				AND status = 1',
883				array($plugin));
884
885			if ($exists > 0) {
886				$status = 1;
887			}
888		}
889
890		db_execute_prepared("UPDATE plugin_hooks
891			SET `function` = ?, `status` = ?,
892			`file` = ?
893			WHERE `name` = ?
894			AND `hook` = ?",
895			array($function, $status, $file, $plugin, $hook));
896	}
897}
898
899function api_plugin_remove_hooks($plugin) {
900	db_execute_prepared('DELETE FROM plugin_hooks
901		WHERE name = ?',
902		array($plugin));
903}
904
905function api_plugin_enable_hooks($plugin) {
906	db_execute_prepared('UPDATE plugin_hooks
907		SET status = 1
908		WHERE name = ?',
909		array($plugin));
910}
911
912function api_plugin_disable_hooks($plugin) {
913	db_execute_prepared("UPDATE plugin_hooks
914		SET status = 0
915		WHERE name = ?
916		AND hook != 'config_settings'
917		AND hook != 'config_arrays'
918		AND hook != 'config_form'",
919		array($plugin));
920}
921
922function api_plugin_disable_hooks_all($plugin) {
923	db_execute_prepared("UPDATE plugin_hooks
924		SET status = 0
925		WHERE name = ?",
926		array($plugin));
927}
928
929function api_plugin_register_realm($plugin, $file, $display, $admin = true) {
930	$files = explode(',', $file);
931
932	$i = 0;
933	$sql_where = '(';
934	foreach($files as $tfile) {
935		$sql_where .= ($sql_where != '(' ? ' OR ':'') .
936			' (file = "' . $tfile . '" OR file LIKE "' . $tfile . ',%" OR file LIKE "%,' . $tfile . ',%" OR file LIKE "%,' . $tfile . '")';
937	}
938	$sql_where .= ')';
939
940	$realm_ids = db_fetch_assoc_prepared("SELECT id
941		FROM plugin_realms
942		WHERE plugin = ?
943		AND $sql_where",
944		array($plugin));
945
946	if (cacti_sizeof($realm_ids) == 1) {
947		$realm_id = $realm_ids[0]['id'];
948	} elseif (cacti_sizeof($realm_ids) > 1) {
949		$realm_id = $realm_ids[0]['id'];
950		cacti_log('WARNING: Registering Realm for Plugin ' . $plugin . ' and Filenames ' . $file . ' is ambiguous.  Using first matching Realm.  Contact the plugin owner to resolve this issue.');
951
952		unset($realm_ids[0]);
953
954		foreach ($realm_ids as $id) {
955			$realm_info = db_fetch_row_prepared('SELECT *
956				FROM plugin_realms
957				WHERE id = ?',
958				array($id['id']));
959
960			if ($file == $realm_info['file']) {
961				db_execute_prepared('UPDATE IGNORE user_auth_realm
962					SET realm_id = ?
963					WHERE realm_id = ?',
964					array($realm_id+100, $realm_info['id']+100));
965
966				db_execute_prepared('UPDATE IGNORE user_auth_group_realm
967					SET realm_id = ?
968					WHERE realm_id = ?',
969					array($realm_id+100, $realm_info['id']+100));
970
971				db_execute_prepared('DELETE FROM plugin_realms
972					WHERE id = ?',
973					array($realm_info['id']));
974			} elseif (strpos($realm_info['file'], $file)) {
975				if (substr($realm_info['file'], 0, strlen($file)) == $file) {
976					$file = substr($file, strlen($file)-1);
977				} else {
978					$file = str_replace(',' . $file, '', $realm_info['file']);
979					$file = str_replace(',,', ',', $file);
980				}
981
982				db_execute_prepared('UPDATE plugin_realms
983					SET file = ?
984					WHERE id = ?',
985					array($file, $realm_info['id']));
986			}
987		}
988	} else {
989		$realm_id = false;
990	}
991
992	if ($realm_id === false) {
993		db_execute_prepared('REPLACE INTO plugin_realms
994			(plugin, file, display)
995			VALUES (?, ?, ?)',
996			array($plugin, $file, $display));
997
998		if ($admin) {
999			$realm_id = db_fetch_cell_prepared('SELECT id
1000				FROM plugin_realms
1001				WHERE plugin = ?
1002				AND file = ?',
1003				array($plugin, $file), false);
1004
1005			$realm_id = $realm_id + 100;
1006
1007			$user_ids[] = read_config_option('admin_user');
1008			if (isset($_SESSION['sess_user_id'])) {
1009				$user_ids[] = $_SESSION['sess_user_id'];
1010			}
1011
1012			if (cacti_sizeof($user_ids)) {
1013				foreach($user_ids as $user_id) {
1014					db_execute_prepared('REPLACE INTO user_auth_realm
1015						(user_id, realm_id)
1016						VALUES (?, ?)',
1017						array($user_id, $realm_id));
1018				}
1019			}
1020		}
1021	} else {
1022		db_execute_prepared('UPDATE plugin_realms
1023			SET display = ?,
1024			file = ?
1025			WHERE id = ?',
1026			array($display, $file, $realm_id));
1027	}
1028}
1029
1030function api_plugin_remove_realms($plugin) {
1031	$realms = db_fetch_assoc_prepared('SELECT id
1032		FROM plugin_realms
1033		WHERE plugin = ?',
1034		array($plugin), false);
1035
1036	foreach ($realms as $realm) {
1037		$id = $realm['id'] + 100;
1038		db_execute_prepared('DELETE FROM user_auth_realm
1039			WHERE realm_id = ?',
1040			array($id));
1041
1042		db_execute_prepared('DELETE FROM user_auth_group_realm
1043			WHERE realm_id = ?',
1044			array($id));
1045	}
1046
1047	db_execute_prepared('DELETE FROM plugin_realms
1048		WHERE plugin = ?',
1049		array($plugin));
1050}
1051
1052function api_plugin_load_realms() {
1053	global $user_auth_realms, $user_auth_realm_filenames;
1054
1055	$plugin_realms = db_fetch_assoc('SELECT *
1056		FROM plugin_realms
1057		ORDER BY plugin, display');
1058
1059	if (cacti_sizeof($plugin_realms)) {
1060		foreach ($plugin_realms as $plugin_realm) {
1061			$plugin_files = explode(',', $plugin_realm['file']);
1062
1063			foreach($plugin_files as $plugin_file) {
1064				$user_auth_realm_filenames[$plugin_file] = $plugin_realm['id'] + 100;
1065			}
1066
1067			$user_auth_realms[$plugin_realm['id'] + 100] = $plugin_realm['display'];
1068		}
1069	}
1070}
1071
1072function api_plugin_user_realm_auth($filename = '') {
1073	global $user_auth_realm_filenames;
1074	/* list all realms that this user has access to */
1075
1076	if ($filename != '' && isset($user_auth_realm_filenames[basename($filename)])) {
1077		if (is_realm_allowed($user_auth_realm_filenames[basename($filename)])) {
1078			return true;
1079		}
1080	}
1081
1082	return false;
1083}
1084
1085function plugin_config_arrays() {
1086	global $config, $menu;
1087
1088	if ($config['poller_id'] == 1 || $config['connection'] == 'online') {
1089		$menu[__('Configuration')]['plugins.php'] = __('Plugins');
1090	}
1091
1092	api_plugin_load_realms();
1093}
1094
1095function plugin_draw_navigation_text($nav) {
1096	$nav['plugins.php:'] = array('title' => __('Plugins'), 'mapping' => 'index.php:', 'url' => 'plugins.php', 'level' => '1');
1097
1098	return $nav;
1099}
1100
1101function plugin_is_compatible($plugin) {
1102	global $config;
1103
1104	$info = plugin_load_info_file($config['base_path'] . '/plugins/' . $plugin . '/INFO');
1105
1106	if ($info !== false) {
1107		if (!isset($info['compat']) || cacti_version_compare(CACTI_VERSION, $info['compat'], '<')) {
1108			return array('compat' => false, 'requires' => __('Requires: Cacti >= %s', $info['compat']));
1109		}
1110	} else {
1111		return array('compat' => false, 'requires' => __('Legacy Plugin'));
1112	}
1113
1114	return array('compat' => true, 'requires' => __('Requires: Cacti >= %s', $info['compat']));
1115}
1116
1117function plugin_load_info_defaults($file, $info, $defaults = array()) {
1118	$result = $info;
1119	$dir    = @basename(@dirname($file));
1120
1121	if (!is_array($defaults)) {
1122		$defaults = array();
1123	}
1124
1125	if (!is_array($result)) {
1126		$result = array();
1127	}
1128
1129	$info_fields = array(
1130		'name'         => ucfirst($dir),
1131		'requires'     => '',
1132		'longname'     => ucfirst($dir),
1133		'status'       => file_exists($file) ? 0 : -4,
1134		'version'      => __('Unknown'),
1135		'author'       => __('Unknown'),
1136		'homepage'     => isset($info['webpage']) ? $info['webpage'] : __('Not Stated'),
1137		'capabilities' => '',
1138		'directory'    => $dir,
1139	);
1140
1141	$info_fields = $info_fields + $defaults;
1142	foreach ($info_fields as $name => $value) {
1143		if (!array_key_exists($name, $result)) {
1144			$result[$name] = $value;
1145		}
1146	}
1147
1148	if (strstr($dir, ' ') !== false) {
1149		$result['status'] = -3;
1150	} elseif (strtolower($dir) != strtolower($result['name'])) {
1151		$result['status'] = -2;
1152	} elseif (!isset($result['compat']) || cacti_version_compare(CACTI_VERSION, $result['compat'], '<')) {
1153		$result['status'] = -1;
1154	}
1155
1156	return $result;
1157}
1158
1159function plugin_load_info_file($file) {
1160	$info = false;
1161	if (file_exists($file)) {
1162		if (is_readable($file)) {
1163			$info = parse_ini_file($file, true);
1164			if (cacti_sizeof($info) && array_key_exists('info', $info)) {
1165				$info = plugin_load_info_defaults($file, $info['info']);
1166			} else {
1167				cacti_log('WARNING: Loading plugin INFO file failed.  Parsing INI file failed.', false, 'WEBUI');
1168			}
1169		} else {
1170			cacti_log('WARNING: Loading plugin INFO file failed.  INFO file not readable.', false, 'WEBUI');
1171		}
1172	} else {
1173		cacti_log('WARNING: Loading plugin INFO file failed.  INFO file does not exist.', false, 'WEBUI');
1174	}
1175
1176	return $info;
1177}
1178
1179