diff --git a/.htaccess b/.htaccess new file mode 100644 index 000000000..23367653a --- /dev/null +++ b/.htaccess @@ -0,0 +1,34 @@ + + +RewriteEngine on + +# change rewrite base if not in root +RewriteBase / + +# passthroughs +RewriteRule ^(api)($|/) - [L] +RewriteRule ^index.php$ - [L] +RewriteRule ^(css|js|site|test)/.*$ - [L] +RewriteRule site/login/captcha/captchashow.php - [L] + +# redirect errors +ErrorDocument 400 /error/400/ +ErrorDocument 401 /error/401/ +ErrorDocument 403 /error/403/ +ErrorDocument 404 /error/404/ +ErrorDocument 500 /error/500/ + +# IE login dashboard fix +RewriteRule ^login/dashboard/$ dashboard/ [R] +RewriteRule ^logout/dashboard/$ dashboard/ [R] +# search override +RewriteRule ^tools/search/(.*)/(.*)/(.*)/(.*)$ index.php?page=tools§ion=search&addresses=$1&subnets=$2&vlans=$3&ip=$4 [L] + +# Rewrites +RewriteRule ^(.*)/(.*)/(.*)/(.*)/(.*)/$ index.php?page=$1§ion=$2&subnetId=$3&sPage=$4&ipaddrid=$5 [L] +RewriteRule ^(.*)/(.*)/(.*)/(.*)/$ index.php?page=$1§ion=$2&subnetId=$3&sPage=$4 [L] +RewriteRule ^(.*)/(.*)/(.*)/$ index.php?page=$1§ion=$2&subnetId=$3 [L] +RewriteRule ^(.*)/(.*)/$ index.php?page=$1§ion=$2 [L] +RewriteRule ^(.*)/$ index.php?page=$1 [L] + + \ No newline at end of file diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100755 index 000000000..4549f985d --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1 @@ +http://phpipam.net/phpipam-installation-guide/ \ No newline at end of file diff --git a/README b/README new file mode 100755 index 000000000..178f4e34f --- /dev/null +++ b/README @@ -0,0 +1,76 @@ +Description +=========== +phpipam is an open-source web IP address management application. Its goal is to provide light and simple |P address management application. +It is ajax-based using jQuery libraries, it uses php scripts and javascript and some HTML5/CSS3 features, so some modern browser is preferred to be able to display javascript quickly and correctly... + +Features and tools: +- Section / Subnet separation +- Subnet nesting +- IPv4/IPv6 support +- Subnet ICMP/telnet scanning and automatic status updates +- Displays free range and number of clients +- Subnet statistics +- User management +- AD/LDAP/OpenLDAP authentication support +- E-Mail notification with IP details +- Import IP addresses from XLS / CSV file +- Export IP database to XLS file +- IPv4/IPv6 calculator +- Search IP database +- IP request module +- Custom IP address fields +- and much more... + +What it does not do: +- Updates DNS server + +License +======= +phpipam is released under the GPL v3 license, see misc/gpl-3.0.txt. + +Requirements +============ +- Apache2 web server with php and mod_rewrite support (set "AllowOverride all" in vhost config) +- Mysql server (5.1+) +- PHP version 5.2+ with following modules (On windows php 5.3 is required!) + + + mysqli : Adds support for the improved mySQL libraries + + + session : Adds persistent session support + + + gmp : Adds support for dev-libs/gmp (GNU MP library) -> to calculate IPv6 networks + + + ldap : Adds LDAP support (Lightweight Directory Access Protocol) + + + json : Adds supports for JSON data-interexchange format + + + SimpleXML : Adds SimpleXML support for parsin XML files + + + gettext : Add support for gettext translations +- PHP PEAR support (dev-php/pear) + +Install +======= +- http://phpipam.net/phpipam-installation-guide/ + +Update +======= +- see UPDATE file for details + +Demo page +============ +http://demo.phpipam.net + +Default user +============ +Admin / ipamadmin + +Changelog +========= +See misc/CHANGELOG + +Roadmap +========= +See misc/Roadmap + +Contact +======= +miha.petkovsek@gmail.com + +special thank also to Hosterdam team (http://www.hosterdam.com) for VPS server +that is used for development of phpIPAM and for demo site. + +And also to all users that filed a bug report / feature report and helped with feature testing! \ No newline at end of file diff --git a/UPDATE b/UPDATE new file mode 100755 index 000000000..f84465f51 --- /dev/null +++ b/UPDATE @@ -0,0 +1,50 @@ +# +# phpipam update instructions +# + +phpIPAM supports upgrading only to 2 older release, for example only version >= 0.9 can be upgraded to 1.1. + + +It is recommended that you backup the old phpipam database before you upgrade to new version (change username and pass to ones in config.php): + /usr/bin/mysqldump -u ipv6 -pipv6admin phpipam > /phpipam__migration_backup.db + +Backup phpipam files: + mv /phpipam /phpipam- + + +# +# !!! important !!! +# + +since version 0.7 phpipam uses mod_rewrite to handle url's. Please make sure you meet the following requirements: + 1.) Apache2 with mod_rewrite support + 2.) "AllowOverride all" set in vhost config for mod_rewrite to work + +In case your URL for phpipam is not root please set the following variables (example for /phpipam/): + 3.) Set BASE in config.php to /phpipam/ + 4.) RewriteBase /phpipam/ in .htaccess + + +# +# a) Automatic upgrade procedure +# + + 1.) Extract files form tar package: + tar -xvf phpipam-1.1.tar + 2.) Set database connection settings in config.php; + 3.) Open phpipam in browser and update database; + + + +# +# b) Manual upgrade procedure +# + 1.) Extract files form tar package: + tar -xvf phpipam-1.1.tar + 2.) Set database connection settings in config.php; + 3.) Manual database structure update + Update database structure by importing database scheme for your version. + If you have version 1.0 and want to upgrade to 1.1 use all update files form UPDATE-v1.0.sql on; + mysql -u root -p phpipam < db/UPDATE-v1.01.sql + mysql -u root -p phpipam < db/UPDATE-v1.02.sql + ... \ No newline at end of file diff --git a/api/.htaccess b/api/.htaccess new file mode 100644 index 000000000..b07049873 --- /dev/null +++ b/api/.htaccess @@ -0,0 +1,5 @@ +# controller rewrites +RewriteRule ^(.*)/(.*)/(.*)/(.*)/$ ?app_id=$1&controller=$2&id=$3&id2=$4 [L,QSA] +RewriteRule ^(.*)/(.*)/(.*)/$ ?app_id=$1&controller=$2&id=$3 [L,QSA] +RewriteRule ^(.*)/(.*)/$ ?app_id=$1&controller=$2 [L,QSA] +RewriteRule ^(.*)/$ ?app_id=$1 [L,QSA] \ No newline at end of file diff --git a/api/index.php b/api/index.php new file mode 100644 index 000000000..54f182dc5 --- /dev/null +++ b/api/index.php @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/api/v1/.htaccess b/api/v1/.htaccess new file mode 100755 index 000000000..7b76dc0e1 --- /dev/null +++ b/api/v1/.htaccess @@ -0,0 +1,6 @@ +# redirect errors +ErrorDocument 400 error/400/ +ErrorDocument 401 error/401/ +ErrorDocument 403 error/403/ +ErrorDocument 404 error/404/ +ErrorDocument 500 error/500/ \ No newline at end of file diff --git a/api/v1/README b/api/v1/README new file mode 100755 index 000000000..aa9bcc5a3 --- /dev/null +++ b/api/v1/README @@ -0,0 +1,159 @@ +phpIPAM API documentation v 0.1 +=============================== + +phpIPAM provides API server for providing data to clients. It is a webapp and can be called +via HTTP requests by providing appropriate GET parameters that define controller, actions and +additional parameters required for each controller, such as id, name, etc. + +Response is in JSON format with success true or false and provided error message or object. + +You can find examples of client API calls in folder api/_examples/. + +Client based request values must be encrypted with app_id and app_code. You first have to +enable API module in phpipam administration and create an app_id and app_code, that will be +used for external app with appropriate permissions. + + +Required php extensions for API server: + mcrypt + curl + + +Available controllers: +-------------------------------- + sections + subnets + addresses + vlans + vrfs + //users + //groups + +Available actions: +------------------ + read + create + update + delete + + +Output format for subnets and IP addresses +------------------------------------------ + You can manually specify output format for subnets and IP addresses: + + format=decimal returns in decimal form (default) + format=ip returns in IP address + + + + + +Per-controller options and examples +=================================== + += Sections +---------- + = read + all returns all sections + id returns section by id + name returns section by name + + example: ?controller=sections&action=read&id=1 + + = create + not available + + = update + not available + + = delete + id (mandatory) id of the section to be deleted + subnets will delete also belonging subnets (default = true) + addresses will delete also belonging ips (default = true) + + example: ?controller=sections&action=delete&id=1 + + += Subnets +--------- + = read + all returns all subnet + id returns subnet by id + sectionId returns all subnets in specified sectionId + format returns subnet in specified format + + example: ?controller=subnets&action=read&id=1 + + = create + not available + + = update + not available + + = delete + id (mandatory) subnet id + addresses will delete also belonging ips (default = true) + + example: ?controller=addresses&action=delete&id=1 + + += IP addresses +-------------- + = read + subnetId returns all ip addresses in subnet + id returns ip address details by id + format returns ip in specified format + + example: ?controller=addresses&action=read&id=1 + + = create + not available + + = update + not available + + = delete + id (mandatory) + + example: ?controller=addresses&action=delete&id=1 + + += Vlans +------- + = read + all returns all vlans + id returns vlan details by id + subnets return also ids of all attached subnets + + example: ?controller=vlans&action=read&id=1 + + = create + not available + + = update + not available + + = delete + id (mandatory) + + example: ?controller=vlans&action=delete&id=1 + += VRFs +------- + = read + all returns all vrfs + id returns vrf details by id + subnets return also ids of all attached subnets + + example: ?controller=vrfs&action=read&id=1 + + = create + not available + + = update + not available + + = delete + id (mandatory) + + example: ?controller=vrfs&action=delete&id=1 \ No newline at end of file diff --git a/api/v1/_examples/addresses/deleteAddress.php b/api/v1/_examples/addresses/deleteAddress.php new file mode 100755 index 000000000..3e9641314 --- /dev/null +++ b/api/v1/_examples/addresses/deleteAddress.php @@ -0,0 +1,35 @@ +sendRequest($req); + + print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print "Error: ".$e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/addresses/getAddressesById.php b/api/v1/_examples/addresses/getAddressesById.php
new file mode 100755
index 000000000..2e1c21d5b
--- /dev/null
+++ b/api/v1/_examples/addresses/getAddressesById.php
@@ -0,0 +1,38 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print "Error: ".$e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/addresses/getAllAddressesInSubnet.php b/api/v1/_examples/addresses/getAllAddressesInSubnet.php
new file mode 100755
index 000000000..f1fd9ed78
--- /dev/null
+++ b/api/v1/_examples/addresses/getAllAddressesInSubnet.php
@@ -0,0 +1,38 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print "Error: ".$e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/apiClient.php b/api/v1/_examples/apiClient.php
new file mode 100755
index 000000000..4c5aaaebc
--- /dev/null
+++ b/api/v1/_examples/apiClient.php
@@ -0,0 +1,71 @@
+ in which format to return data
+
+   //construct an ApiCaller object, taking an
+   //APP ID, APP KEY and API URL parameter
+   public function __construct($app_id, $app_key, $api_url, $api_resp="array")
+   {
+      $this->_app_id 	= $app_id;
+      $this->_app_key 	= $app_key;
+      $this->_api_url 	= $api_url;
+      $this->_api_resp 	= $api_resp;
+   }
+
+   //send the request to the API server
+   //also encrypts the request, then checks
+   //if the results are valid
+   public function sendRequest($request_params)
+   {
+      //encrypt the request parameters
+      $enc_request = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->_app_key, json_encode($request_params), MCRYPT_MODE_ECB));
+
+      //create the params array, which will
+      //be the POST parameters
+      $params = array();
+      $params['enc_request'] = $enc_request;
+      $params['app_id'] = $this->_app_id;
+
+      //initialize and setup the curl handler
+      $ch = curl_init();
+      curl_setopt($ch, CURLOPT_URL, $this->_api_url);
+      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+      curl_setopt($ch, CURLOPT_POST, count($params));
+      curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
+
+      //execute the request
+      $result = curl_exec($ch);
+
+      //json_decode the result
+      $result = @json_decode($result, true);
+
+      //check if we're able to json_decode the result correctly
+      if( $result == false || isset($result['success']) == false ) {
+         throw new Exception('Request was not correct');
+      }
+
+      //if there was an error in the request, throw an exception
+      if( $result['success'] == false ) {
+         throw new Exception($result['errormsg']);
+      }
+
+      //if everything went great, return the data
+      if($this->_api_resp == "json") {
+	      return json_encode($result['data']);
+	  } else {
+	      return ($result['data']);
+	  }
+   }
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/apiConfig.php b/api/v1/_examples/apiConfig.php
new file mode 100755
index 000000000..ed1d680fa
--- /dev/null
+++ b/api/v1/_examples/apiConfig.php
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/api/v1/_examples/sections/deleteSection.php b/api/v1/_examples/sections/deleteSection.php
new file mode 100755
index 000000000..71e6577d4
--- /dev/null
+++ b/api/v1/_examples/sections/deleteSection.php
@@ -0,0 +1,39 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print "Error: ".$e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/sections/getAllSections.php b/api/v1/_examples/sections/getAllSections.php
new file mode 100755
index 000000000..bcb0b3f3f
--- /dev/null
+++ b/api/v1/_examples/sections/getAllSections.php
@@ -0,0 +1,37 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print "Error: ".$e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/sections/getSectionById.php b/api/v1/_examples/sections/getSectionById.php
new file mode 100755
index 000000000..ef28e6e8e
--- /dev/null
+++ b/api/v1/_examples/sections/getSectionById.php
@@ -0,0 +1,39 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print "
";
+	print_r($e->getMessage());
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/subnets/deleteSubnet.php b/api/v1/_examples/subnets/deleteSubnet.php
new file mode 100755
index 000000000..2377babe1
--- /dev/null
+++ b/api/v1/_examples/subnets/deleteSubnet.php
@@ -0,0 +1,36 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print $e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/subnets/getAllSubnetsInSection.php b/api/v1/_examples/subnets/getAllSubnetsInSection.php
new file mode 100755
index 000000000..f6d113626
--- /dev/null
+++ b/api/v1/_examples/subnets/getAllSubnetsInSection.php
@@ -0,0 +1,38 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print "Error: ".$e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/subnets/getSubnetById.php b/api/v1/_examples/subnets/getSubnetById.php
new file mode 100755
index 000000000..226dbfab0
--- /dev/null
+++ b/api/v1/_examples/subnets/getSubnetById.php
@@ -0,0 +1,39 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print "Error: ".$e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/vlans/deleteSubnet.php b/api/v1/_examples/vlans/deleteSubnet.php
new file mode 100755
index 000000000..2377babe1
--- /dev/null
+++ b/api/v1/_examples/vlans/deleteSubnet.php
@@ -0,0 +1,36 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print $e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/_examples/vlans/getVlanById.php b/api/v1/_examples/vlans/getVlanById.php
new file mode 100755
index 000000000..a91880d92
--- /dev/null
+++ b/api/v1/_examples/vlans/getVlanById.php
@@ -0,0 +1,40 @@
+sendRequest($req);
+
+	print "
";
+	print_r($response);
+}
+catch( Exception $e ) {
+	//catch any exceptions and report the problem
+	print "Error: ".$e->getMessage();
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/controllers/Addresses.php b/api/v1/controllers/Addresses.php
new file mode 100755
index 000000000..b45b8f5eb
--- /dev/null
+++ b/api/v1/controllers/Addresses.php
@@ -0,0 +1,66 @@
+_params = $params;
+	}
+
+	/** 
+	* read addresses 
+	*/
+	public function readAddresses()
+	{
+		# init section class
+		$address = new Address ();
+		
+		# set method
+		if(isset($this->_params['format'])) { $address->format = $this->_params['format']; }
+		
+		# get all ips in subnet?
+		if($this->_params['subnetId'])	{ $address->subnetId = $this->_params['subnetId']; }
+		# get ip by Id
+		elseif($this->_params['id']) 	{ $address->id = $this->_params['id']; }
+		# false
+		else 							{  }
+		
+		# fetch results
+		$res = $address->readAddress(); 
+		# return result
+		return $res;
+	}
+
+
+	/** 
+	* delete addresses 
+	*/
+	public function deleteAddresses()
+	{
+		# init section class
+		$address = new Address ();
+		# set Id
+		if($this->_params['id']) 		{ $address->id = $this->_params['id']; }
+		
+		# fetch results
+		$res = $address->deleteAddress(); 
+		# return result
+		return $res;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/controllers/Sections.php b/api/v1/controllers/Sections.php
new file mode 100755
index 000000000..d30816974
--- /dev/null
+++ b/api/v1/controllers/Sections.php
@@ -0,0 +1,101 @@
+_params = $params;
+	}
+
+
+	/** 
+	* create new section 
+	*/
+	public function createSections()
+	{
+		/* not yet implemented */
+		throw new Exception('Action not yet implemented');	
+	}
+
+
+	/** 
+	* read sections 
+	*/
+	public function readSections()
+	{
+		//init section class
+		$section = new Section();
+		
+		//get all sections?
+		if($this->_params['all'])		{ $section->all	 = true; }
+		//get section by name
+		elseif($this->_params['name']) 	{ $section->name = $this->_params['name']; }
+		//get section by ID
+		else 							{ $section->id 	 = $this->_params['id'];	}
+		
+		//fetch results
+		$res = $section->readSection(); 
+		//return section(s) in array format
+		return $res;
+	}	
+	
+	
+	/** 
+	* update existing section 
+	*/
+	public function updateSections()
+	{
+		/* not yet implemented */
+		throw new Exception('Action not yet implemented');	
+	}	
+	
+	
+	/** 
+	* delete section 
+	*/
+	public function deleteSections()
+	{
+		//init section class
+		$section = new Section();
+		//required parameters
+		$section->id        	= $this->_params['id'];
+
+		//delete also IPs and subnets?
+		if(isset($this->_params['subnets'])) {
+			if($this->_params['subnets'] == false) 			{ $section->subnets = false; }
+			else											{ $section->subnets = true; }
+		} else {
+															{ $section->subnets = true; }
+		}		
+		//delete also addresses?
+		if($section->subnets == true) {
+			if(isset($this->_params['addresses'])) {
+				if($this->_params['addresses'] == false) 	{ $section->addresses = false; }
+				else										{ $section->addresses = true; }
+			} else {
+															{ $section->addresses = true; }
+			}				
+		}
+
+		//delete section
+		$res = $section->deleteSection(); 	
+		//return result
+		return $res;		
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/controllers/Subnets.php b/api/v1/controllers/Subnets.php
new file mode 100755
index 000000000..213a64338
--- /dev/null
+++ b/api/v1/controllers/Subnets.php
@@ -0,0 +1,102 @@
+_params = $params;
+		
+		//ip address format, can be decimal or ip
+		if(!$this->_params['format'])			  { $this->_params['format'] = "decimal"; }
+		//verify IP address format
+		if(!($this->_params['format']=="decimal" || $this->_params['format']== "ip")) {
+			throw new Exception('Invalid format');
+		}
+	}
+
+
+	/** 
+	* create subnet 
+	*/
+	public function createSubnets()
+	{
+		/* not yet implemented */
+		throw new Exception('Action not yet implemented');
+	}	
+	
+
+	/** 
+	* read subnets 
+	*/
+	public function readSubnets()
+	{
+		//init subnet class
+		$subnet = new Subnet();
+		
+		//set IP address format
+		$subnet->format = $this->_params['format'];
+		
+		//get all subnets
+		if($this->_params['all'])			{ $subnet->all = true; }
+		//get all subnets in subnet
+		elseif($this->_params['sectionId']) { $subnet->sectionId = $this->_params['sectionId']; }
+		//get subnet by ID
+		else 								{ $subnet->id = $this->_params['id'];	}
+		
+		//fetch results
+		$res = $subnet->readSubnet(); 
+		
+		//return subnet(s) in array format
+		return $res;
+	}	
+	
+	
+	/** 
+	* update existing subnet 
+	*/
+	public function updateSubnets()
+	{
+		/* not yet implemented */
+		throw new Exception('Action not yet implemented');
+	}	
+	
+	
+	/** 
+	* delete subnet 
+	*/
+	public function deleteSubnets()
+	{
+		//init subnet class
+		$subnet = new Subnet();
+		
+		//provide id
+		$subnet->id = $this->_params['id'];
+		//delete also IPs?
+		if(isset($this->_params['addresses'])) {
+			if($this->_params['addresses'] == false) { $subnet->addresses = false; }
+			else									 { $subnet->addresses = true; }
+		} else {
+													 { $subnet->addresses = true; }
+		}		
+		//fetch results
+		$res = $subnet->deleteSubnet(); 
+		
+		//return subnet(s) in array format
+		return $res;
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/controllers/Vlans.php b/api/v1/controllers/Vlans.php
new file mode 100755
index 000000000..605d67f2e
--- /dev/null
+++ b/api/v1/controllers/Vlans.php
@@ -0,0 +1,86 @@
+_params = $params;
+	}
+
+
+	/** 
+	* create vlan 
+	*/
+	public function createVlans()
+	{
+		/* not yet implemented */
+		throw new Exception('Action not yet implemented');
+	}	
+	
+
+	/** 
+	* read vlans 
+	*/
+	public function readVlans()
+	{
+		//init Vlan class
+		$vlan = new Vlan();
+		
+		//get also ids of belonging subnets?
+		if($this->_params['subnets'])		{ $vlan->subnets = true; }
+		
+		//get all vlans
+		if($this->_params['all'])			{ $vlan->all = true; }
+		//get vlan by ID
+		else 								{ $vlan->id = $this->_params['id'];	}
+		
+		//fetch results
+		$res = $vlan->readVlan(); 
+		
+		//return Vlan(s) in array format
+		return $res;
+	}	
+	
+	
+	/** 
+	* update existing Vlan 
+	*/
+	public function updateVlans()
+	{
+		/* not yet implemented */
+		throw new Exception('Action not yet implemented');
+	}	
+	
+	
+	/** 
+	* delete Vlan 
+	*/
+	public function deleteVlans()
+	{
+		//init Vlan class
+		$vlan = new Vlan();
+		
+		//provide id
+		$vlan->id = $this->_params['id'];
+
+		//fetch results
+		$res = $vlan->deleteVlan(); 
+		
+		//return Vlan(s) in array format
+		return $res;
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/controllers/Vrfs.php b/api/v1/controllers/Vrfs.php
new file mode 100755
index 000000000..3b9c6c56e
--- /dev/null
+++ b/api/v1/controllers/Vrfs.php
@@ -0,0 +1,86 @@
+_params = $params;
+	}
+
+
+	/** 
+	* create Vrf 
+	*/
+	public function createVrfs()
+	{
+		/* not yet implemented */
+		throw new Exception('Action not yet implemented');
+	}	
+	
+
+	/** 
+	* read Vrfs 
+	*/
+	public function readVrfs()
+	{
+		//init Vrf class
+		$vrf = new Vrf();
+		
+		//get also ids of belonging subnets?
+		if($this->_params['subnets'])		{ $vrf->subnets = true; }
+		
+		//get all Vrfs
+		if($this->_params['all'])			{ $vrf->all = true; }
+		//get Vrf by ID
+		else 								{ $vrf->id = $this->_params['id'];	}
+		
+		//fetch results
+		$res = $vrf->readVrf(); 
+		
+		//return Vrf(s) in array format
+		return $res;
+	}	
+	
+	
+	/** 
+	* update existing Vrf 
+	*/
+	public function updateVrfs()
+	{
+		/* not yet implemented */
+		throw new Exception('Action not yet implemented');
+	}	
+	
+	
+	/** 
+	* delete Vrf 
+	*/
+	public function deleteVrfs()
+	{
+		//init Vrf class
+		$vrf = new Vrf();
+		
+		//provide id
+		$vrf->id = $this->_params['id'];
+
+		//fetch results
+		$res = $vrf->deleteVrf(); 
+		
+		//return Vrf(s) in array format
+		return $res;
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/functions/dbfunctions.php b/api/v1/functions/dbfunctions.php
new file mode 100644
index 000000000..d26b07be8
--- /dev/null
+++ b/api/v1/functions/dbfunctions.php
@@ -0,0 +1,218 @@
+error:".$e->getMessage().""; }
+			return false;
+		}	
+
+		if(!isset($e))
+		$this->set_charset("utf8");
+		
+		
+		# change back reporting for exception throwing to scripts
+		//mysqli_report(MYSQLI_REPORT_ERROR);
+	} 
+
+	
+	/**
+	 * execute given query 
+	 *
+	 */
+	public function executeQuery( $query, $lastId = false ) 
+	{
+		# execute query
+		$result     = parent::query( $query );
+		$this->lastSqlId   = $this->insert_id;
+		
+		# if it failes throw new exception
+		if ( mysqli_error( $this ) ) {
+            throw new exception( mysqli_error( $this ), mysqli_errno( $this ) ); 
+      		}
+        else {
+        	# return lastId if requested
+        	if($lastId)	{ return $this->lastSqlId; }
+        	else 		{ return true; }
+        }
+	}
+	
+
+	/**
+	 * get only 1 row
+	 *
+	 */
+    function getRow ( $query ) 
+    {
+        /* get result */
+        if ($result = parent::query($query)) {     
+            $resp = $result->fetch_row();   
+        }
+        else {
+            throw new exception( mysqli_error( $this ), mysqli_errno( $this ) ); 
+        }
+        /* return result */
+        return $resp;   
+    }
+
+	
+	
+	/**
+	 * get array of results
+	 *
+	 * returns multi-dimensional array
+	 *     first dimension is number
+	 *     from second on the values
+	 * 
+	 * if nothing is provided use assocciative results
+	 *
+	 */
+	function getArray( $query , $assoc = true ) 
+	{	
+		/* execute query */
+		$result = parent::query($query);
+	
+	    /* if it failes throw new exception */
+		if(mysqli_error($this)) {
+      		throw new exception(mysqli_error($this), mysqli_errno($this)); 
+        }
+        
+		/** 
+		 * fetch array of all access responses 
+         * either assoc or num, based on input
+         *
+         */
+		if ($assoc == true) {
+            while($row = $result->fetch_array(MYSQLI_ASSOC)) {
+                $fields[] = $row;	
+            }
+		} 
+		else {
+            while($row = $result->fetch_array(MYSQLI_NUM)) {
+                $fields[] = $row;	
+            }
+        }
+        
+		/* return result array */
+		if(isset($fields)) {
+        	return($fields);
+        }
+        else {
+        	$fields = array();
+        	return $fields;
+        }
+	}
+
+
+	
+	/**
+	 * get array of multiple results
+	 *
+	 * returns multi-dimensional array
+	 *     first dimension is number
+	 *     from second on the values
+	 * 
+	 * if nothing is provided use assocciative results
+	 *
+	 */
+	function getMultipleResults( $query ) 
+	{
+        /* execute query */
+		$result = parent::multi_query($query);
+		
+		/**
+		 * get results to array
+		 * first save it, than get each row from result and store it to active[]
+		 */
+		do { 
+            $results = parent::store_result();
+			
+			/* save each to array (only first field) */
+			while ( $row = $results->fetch_row() ) {
+				$rows[] = $row[0];
+			}
+			$results->free();
+		}
+		while( parent::next_result() );
+		
+		/* return result array of rows */
+		return($rows);
+	}
+	
+	
+	/**
+	 * Execute multiple querries!
+	 *
+	 */
+	function executeMultipleQuerries( $query, $lastId = false ) 
+	{	
+        # execute querries 
+		//$result = parent::multi_query($query);
+		
+		if ($result = parent::multi_query($query)) {
+		    do {
+		        /* store first result set */
+		        if ($result = parent::store_result()) {
+		            $result->free();
+		        }
+		    } while (parent::next_result());
+		}
+		
+		# save lastid
+		$this->lastSqlId   = $this->insert_id;
+
+		# if it failes throw new exception
+		if ( mysqli_error( $this ) ) {
+            throw new exception( mysqli_error( $this ), mysqli_errno( $this ) ); 
+      	}
+        else {
+       		if($lastId)	{ return $this->lastSqlId; }
+        	else 		{ return true; }
+        }
+	}
+
+
+	/**
+	 * Select database
+	 *
+	 */
+	function selectDatabase( $database ) 
+	{	
+        /* execute querries */
+		$result = parent::select_db($database);
+
+		/* if it failes throw new exception */
+		if ( mysqli_error( $this ) ) {
+            throw new exception( mysqli_error( $this ), mysqli_errno( $this ) ); 
+      	}
+        else {
+            return true;
+        }	
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/api/v1/functions/functions-admin.php b/api/v1/functions/functions-admin.php
new file mode 100644
index 000000000..e76c8149e
--- /dev/null
+++ b/api/v1/functions/functions-admin.php
@@ -0,0 +1,2661 @@
+getArray( $query ); }
+        catch (Exception $e) {
+        	$error =  $e->getMessage();
+        	die("
"._('Error').": $error
"); + } + + # user already exists + if (sizeof($details) != 0) { $errors[] = _("User")." ".$userModDetails['username']." "._("already exists!"); } + } + # return errors + return($errors); +} + + +/** + * Delete user by ID + */ +function deleteUserById($id, $name = "") +{ + global $database; + + $query = 'delete from `users` where `id` = "'. $id .'";'; # set query, open db connection and fetch results */ + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # ok + if(!isset($error)) { + updateLogTable ('User '. $name .' deleted ok', 'User '. $name .' deleted ok', 1); # write success log + return true; + } + # problem + else { + print "
"._('Cannot delete user')."!
"._('Error').": $error
"; + updateLogTable ('Cannot delete user '. $name, 'Cannot delete user '. $name , 2); # write error log + return false; + } +} + + +/** + * Update user by ID - if id is empty add new user! + */ +function updateUserById ($userModDetails) { + + global $database; + + # replace special chars + $userModDetails['groups'] = mysqli_real_escape_string($database, $userModDetails['groups']); + + # set query - add or edit user + if (empty($userModDetails['userId'])) { + + # custom fields + $myFields = getCustomFields('users'); + $myFieldsInsert['query'] = ''; + $myFieldsInsert['values'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + # empty? + if(strlen($userModDetailsip[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", NULL"; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", '". $userModDetails[$myField['name']] . "'"; + } + } + } + + # reformat passChanged + if($userModDetails['domainUser']=="1") { $userModDetails['passChange'] = "No"; } //never for domain users + elseif(@$userModDetails['passChange']=="On") { $userModDetails['passChange'] = "Yes"; } //yes if requested + else { $userModDetails['passChange'] = "No"; } //no change needed + + $query = "insert into users "; + $query .= "(`username`, `password`, `role`, `real_name`, `email`, `domainUser`,`mailNotify`,`mailChangelog`,`groups`,`lang`,`passChange` $myFieldsInsert[query]) values "; + $query .= "('$userModDetails[username]', '$userModDetails[password1]', '$userModDetails[role]', '$userModDetails[real_name]', '$userModDetails[email]', '$userModDetails[domainUser]', '$userModDetails[mailNotify]','$userModDetails[mailChangelog]','$userModDetails[groups]','$userModDetails[lang]','$userModDetails[passChange]' $myFieldsInsert[values]);"; + } + else { + + # custom fields + $myFields = getCustomFields('users'); + $myFieldsInsert['query'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + if(strlen($userModDetails[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = NULL '; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = \''.$userModDetails[$myField['name']].'\' '; + } + } + } + + $query = "update users set "; + $query .= "`username` = '$userModDetails[username]', "; + if (strlen($userModDetails['password1']) != 0) { + $query .= "`password` = '$userModDetails[password1]', "; + } + $query .= "`role` = '$userModDetails[role]', `real_name`= '$userModDetails[real_name]', `email` = '$userModDetails[email]', `domainUser`= '$userModDetails[domainUser]',`mailNotify`= '$userModDetails[mailNotify]',`mailChangelog`= '$userModDetails[mailChangelog]', `lang`= '$userModDetails[lang]', `groups`='".$userModDetails['groups']."' "; + $query .= $myFieldsInsert['query']; + $query .= "where `id` = '$userModDetails[userId]';"; + } + + $log = prepareLogFromArray ($userModDetails); # prepare log + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # ok + if(!isset($error)) { + updateLogTable ('User '. $userModDetails['username'] .' updated ok', $log, 1); # write success log + return true; + } + # problem + else { + print "
"._("Cannot $userModDetails[action] user")."!
"._('Error').": $error
"; + updateLogTable ('Cannot modify user '. $userModDetails['username'], $log, 2); # write error log + return false; + } +} + + +/** + * User self-update + */ +function selfUpdateUser ($userModDetails) +{ + global $database; + + /* set query */ + $query = "update users set "; + if(strlen($userModDetails['password1']) != 0) { + $query .= "`password` = '$userModDetails[password1]',"; + } + $query .= "`real_name`= '$userModDetails[real_name]', `mailNotify`='$userModDetails[mailNotify]', `mailChangelog`='$userModDetails[mailChangelog]', `email` = '$userModDetails[email]', "; + $query .= "`lang`= '$userModDetails[lang]' "; + $query .= "where `id` = '$userModDetails[userId]';"; + + /* set log file */ + $log = prepareLogFromArray ($userModDetails); # prepare log + + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # ok + if(!isset($error)) { + updateLogTable ('User '. $userModDetails['real_name'] . ' selfupdate ok', $log, 1); # write success log + return true; + } + # problem + else { + print "
"._('Cannot update user')."!
"._('Error').": $error
"; + updateLogTable ('User '. $userModDetails['real_name'] . ' selfupdate failed', $log, 2); # write error log + return false; + } +} + + +/** + * User set dash widgets + */ +function setUserDashWidgets ($userId, $widgets) +{ + global $database; + + /* set query */ + $query = "update users set `widgets`= '$widgets' where `id` = '$userId';"; + + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # ok + if(!isset($error)) { + return true; + } + # problem + else { + print "
"._('Cannot update user')."!
"._('Error').": $error
"; + return false; + } +} + + + +/** + * Modify lang + */ +function modifyLang ($lang) +{ + global $database; + + /* set query based on action */ + if($lang['action'] == "add") { $query = "insert into `lang` (`l_code`,`l_name`) values ('$lang[l_code]','$lang[l_name]');"; } + elseif($lang['action'] == "edit") { $query = "update `lang` set `l_code`='$lang[l_code]',`l_name`='$lang[l_name]' where `l_id`='$lang[l_id]'; "; } + elseif($lang['action'] == "delete") { $query = "delete from `lang` where `l_id`='$lang[l_id]'; "; } + else { return 'false'; } + + /* execute */ + try { $details = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + return true; +} + + +/** + * Modify widget + */ +function modifyWidget ($w) +{ + global $database; + + /* set query based on action */ + if($w['action'] == "add") { $query = "insert into `widgets` (`wtitle`,`wdescription`,`wfile`,`whref`,`wadminonly`,`wactive`,`wsize`) values ('$w[wtitle]','$w[wdescription]','$w[wfile]','$w[whref]','$w[wadminonly]','$w[wactive]','$w[wsize]');"; } + elseif($w['action'] == "edit") { $query = "update `widgets` set `wtitle`='$w[wtitle]',`wdescription`='$w[wdescription]',`wfile`='$w[wfile]',`wadminonly`='$w[wadminonly]',`wactive`='$w[wactive]',`whref`='$w[whref]',`wsize`='$w[wsize]' where `wid`='$w[wid]'; "; } + elseif($w['action'] == "delete") { $query = "delete from `widgets` where `wid`='$w[wid]'; "; } + else { return 'false'; } + + /* execute */ + try { $details = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + return true; +} + + +/** + * Update user password on first login + */ +function update_user_password ($id, $password) +{ + global $database; + + # query + $query = "update `users` set `password`='$password', `passChange`='No' where `id` = $id;"; + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + return true; +} + + + + + + + + + + +/* @group functions ---------------- */ + + +/** + * get all groups + */ +function getAllGroups() +{ + global $database; + + /* execute query */ + $query = "select * from `userGroups` order by `g_name` asc;"; + + /* get groups */ + try { $groups = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + die("
"._('Error').": $error
"); + } + + /* return false if none, else list */ + if(sizeof($groups) == 0) { return false; } + else { return $groups; } +} + + +/** + * get all groups - array order by key + */ +function rekeyGroups($groups) +{ + foreach($groups as $k=>$g) { + $tkey = $g['g_id']; + + $out[$tkey]['g_id'] = $g['g_id']; + $out[$tkey]['g_name'] = $g['g_name']; + $out[$tkey]['g_desc'] = $g['g_desc']; + } + + return $out; +} + + +/** + * Group details by ID + */ +function getGroupById($id) +{ + # check if already in cache + if($vtmp = checkCache("group", $id)) { + return $vtmp; + } + # query + else { + global $database; + + /* execute query */ + $query = "select * from `userGroups` where `g_id`= '$id';"; + + /* get group */ + try { $group = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + die("
"._('Error').": $error
"); + } + + /* return false if none, else list */ + if(sizeof($group) == 0) { return false; } + else { writeCache("group", $id, $group[0]); return $group[0]; } + } +} + + +/** + * Parse all user groups + */ +function parseUserGroups($groups) +{ + if(sizeof($groups)>0) { + foreach($groups as $g) { + $tmp = getGroupById($g); + $out[$tmp['g_id']] = $tmp; + } + } + /* return array of groups */ + return $out; +} + + +/** + * Parse all user groups - get only Id's + */ +function parseUserGroupsIds($groups) +{ + if(sizeof($groups) >0) { + foreach($groups as $g) { + $tmp = getGroupById($g); + $out[$tmp['g_id']] = $tmp['g_id']; + } + } + /* return array of groups */ + return $out; +} + + + +/** + * Get users in group + */ +function getUsersInGroup($gid) +{ + # get all users + $users = getAllUsers(); + + # check if $gid in array + foreach($users as $u) { + $g = json_decode($u['groups'], true); + $g = parseUserGroups($g); + + if(sizeof($g)>0) { + foreach($g as $gr) { + if(in_array($gid, $gr)) { + $out[] = $u['id']; + } + } + } + } + # return + return $out; +} + + +/** + * Get users not in group + */ +function getUsersNotInGroup($gid) +{ + # get all users + $users = getAllUsers(); + + # check if $gid in array + foreach($users as $u) { + if($u['role'] != "Administrator") { + $g = json_decode($u['groups'], true); + if(!@in_array($gid, $g)) { $out[] = $u['id']; } + } + } + # return + return $out; +} + + +/** + * Function that returns all sections with selected group partitions + */ +function getSectionPermissionsByGroup ($gid, $name = true) +{ + # get all users + $sec = fetchSections(); + + # check if $gid in array + foreach($sec as $s) { + $p = json_decode($s['permissions'], true); + if(sizeof($p)>0) { + if($name) { + if(array_key_exists($gid, $p)) { $out[$s['name']] = $p[$gid]; } + } + else { + if(array_key_exists($gid, $p)) { $out[$s['id']] = $p[$gid]; } + } + } + # no permissions + else { + $out[$s['name']] = 0; + } + } + # return + return $out; +} + + + +/** + * Modify group + */ +function modifyGroup($g) +{ + global $database; + + # set query + if($g['action'] == "add") { $query = "insert into `userGroups` (`g_name`,`g_desc`) values ('$g[g_name]','$g[g_desc]'); "; } + else if($g['action'] == "edit") { $query = "update `userGroups` set `g_name`='$g[g_name]', `g_desc`='$g[g_desc]' where `g_id` = '$g[g_id]';"; } + else if($g['action'] == "delete") { $query = "delete from `userGroups` where `g_id` = '$g[g_id]';"; } + else { return false; } + + # execute + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # set log file + $log = prepareLogFromArray ($g); # prepare log + + # ok + if(!isset($error)) { + updateLogTable ("Group $g[action] success", $log, 0); # write success log + return true; + } + # problem + else { + print "
"._("Cannot $userModDetails[action] user")."!
"._('Error').": $error
"; + updateLogTable ("Group $g[action] error", $log, 2); # write error log + return false; + } + +} + + +/** + * Delete all users from group + */ +function deleteUsersFromGroup($gid) +{ + # get all users + $users = getAllUsers(); + + # check if $gid in array + foreach($users as $u) { + $g = json_decode($u['groups'], true); + $go = $g; + $g = parseUserGroups($g); + + if(sizeof($g)>0) { + foreach($g as $gr) { + if(in_array($gid, $gr)) { + unset($go[$gid]); + $ng = json_encode($go); + updateUserGroups($u['id'],$ng); + } + } + } + } + # return + return $out; + +} + + +/** + * Delete all users from group + */ +function deleteGroupFromSections($gid) +{ + # get all users + $sections = fetchSections(); + + # check if $gid in array + foreach($sections as $s) { + $g = json_decode($s['permissions'], true); + + if(sizeof($g)>0) { + if(array_key_exists($gid, $g)) { + unset($g[$gid]); + $ng = json_encode($g); + updateSectionGroups($s['id'],$ng); + } + } + } + # return + return $out; + +} + + + +/** + * Add user to group + */ +function addUserToGroup($gid, $uid) +{ + # get old groups + $user = getUserDetailsById($uid); + + # append new group + $g = json_decode($user['groups'], true); + $g[$gid] = $gid; + $g = json_encode($g); + + # update + if(!updateUserGroups($uid, $g)) { return false; } + else { return true; } +} + + +/** + * Remove user from group + */ +function removeUserFromGroup($gid, $uid) +{ + # get old groups + $user = getUserDetailsById($uid); + + # append new group + $g = json_decode($user['groups'], true); + unset($g[$gid]); + $g = json_encode($g); + + # update + if(!updateUserGroups($uid, $g)) { return false; } + else { return true; } +} + + +/** + * Update users's group + */ +function updateUserGroups($uid, $groups) +{ + global $database; + + # replace special chars + $groups = mysqli_real_escape_string($database, $groups); + + # set query + $query = "update `users` set `groups` = '$groups' where `id` = $uid; "; + + # update + try { $database->executeQuery($query); } + catch (Exception $e) { + print "
"._('Error').": $e
"; + return false; + } + + # ok + return true; +} + + +/** + * Update section permissions + */ +function updateSectionGroups($sid, $groups) +{ + global $database; + + # replace special chars + $groups = mysqli_real_escape_string($database, $groups); + + # set query + $query = "update `sections` set `permissions` = '$groups' where `id` = $sid; "; + + # update + try { $database->executeQuery($query); } + catch (Exception $e) { + print "
"._('Error').": $e
"; + return false; + } + + # ok + return true; +} + + + + + + + + + + +/* @subnet functions ---------------- */ + + +/** + * Add new subnet + */ +function modifySubnetDetails ($subnetDetails, $lastId = false, $api = false) +{ + global $database; + + /* escape vars to prevent SQL injection */ + $subnetDetails = filter_user_input ($subnetDetails, true, true); + + /* trim user input */ + $subnetDetails = trim_user_input ($subnetDetails); + + # set modify subnet details query + $query = setModifySubnetDetailsQuery ($subnetDetails, $api); + + $log = prepareLogFromArray ($subnetDetails); # prepare log + + /* save old if delete */ + if($subnetDetails['action']=="delete") { $dold = getSubnetDetailsById ($subnetDetails['subnetId']); } + elseif($subnetDetails['action']=="edit") { $old = getSubnetDetailsById ($subnetDetails['subnetId']); } + + # execute query + try { $updateId=$database->executeMultipleQuerries($query, true); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Subnet ('. $subnetDetails['description'] .') '. $subnetDetails['action'] .' failed', $log, 2); # write error log + print "
$error
"; + //save changelog + writeChangelog('subnet', $ip['action'], 'error', $old, $new); + return false; + } + + /* for changelog */ + if($subnetDetails['action']=="add") { + $subnetDetails['subnetId'] = $updateId; + writeChangelog('subnet', $subnetDetails['action'], 'success', array(), $subnetDetails); + } elseif ($subnetDetails['action']=="delete") { + $dold['subnetId'] = $dold['id']; + writeChangelog('subnet', $subnetDetails['action'], 'success', $dold, array()); + } else { + writeChangelog('subnet', $subnetDetails['action'], 'success', $old, $subnetDetails); + } + + // success + if($_POST['isFolder']==false) { + updateLogTable ('Subnet '.$subnetDetails['subnet'].' ('. $subnetDetails['description'] .') '. $subnetDetails['action'] .' ok', $log, 1); # write success log + } else { + updateLogTable ('Folder '.$subnetDetails['subnet'].' ('. $subnetDetails['description'] .') '. $subnetDetails['action'] .' ok', $log, 1); # write success log + } + // result + if(!$lastId) { return true; } + else { return $updateId; } +} + + +/** + * Add new subnet - set query + */ +function setModifySubnetDetailsQuery ($subnetDetails, $api) +{ + # add new subnet + if ($subnetDetails['action'] == "add") + { + # api? + if($api) { + $query = 'insert into subnets '. "\n"; + $query .= '(`subnet`, `mask`, `sectionId`, `description`, `vlanId`, `vrfId`, `masterSubnetId`, `allowRequests`, `showName`, `permissions`, `discoverSubnet`, `pingSubnet`) ' . "\n"; + $query .= 'values (' . "\n"; + $query .= ' "'. $subnetDetails['subnet'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['mask'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['sectionId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['description'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['vlanId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['vrfId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['masterSubnetId'] .'", ' . "\n"; + $query .= ''. isCheckbox($subnetDetails['allowRequests']) .','."\n"; + $query .= ''. isCheckbox($subnetDetails['showName']) .','."\n"; + $query .= ' "'. $subnetDetails['permissions'] .'", '."\n"; + $query .= ''. isCheckbox($subnetDetails['discoverSubnet']) .','."\n"; + $query .= ''. isCheckbox($subnetDetails['pingSubnet']) .''."\n"; + $query .= ' );'; + } else { + # remove netmask and calculate decimal values! + $subnetDetails['subnet_temp'] = explode("/", $subnetDetails['subnet']); + $subnetDetails['subnet'] = Transform2decimal ($subnetDetails['subnet_temp'][0]); + $subnetDetails['mask'] = $subnetDetails['subnet_temp'][1]; + + # custom fields + $myFields = getCustomFields('subnets'); + $myFieldsInsert['query'] = ''; + $myFieldsInsert['values'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + # empty? + if(strlen($subnetDetails[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", NULL"; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", '". $subnetDetails[$myField['name']] . "'"; + } + } + } + + $query = 'insert into subnets '. "\n"; + # is folder? + if($subnetDetails['isFolder']) { + $query .= '(`isFolder`,`subnet`, `mask`, `sectionId`, `description`, `vlanId`, `vrfId`, `masterSubnetId`, `allowRequests`, `showName`, `permissions`, `discoverSubnet`, `pingSubnet` '.$myFieldsInsert['query'].') ' . "\n"; + $query .= 'values (' . "\n"; + $query .= '1, ' . "\n"; + } + else { + $query .= '(`subnet`, `mask`, `sectionId`, `description`, `vlanId`, `vrfId`, `masterSubnetId`, `allowRequests`, `showName`, `permissions`, `discoverSubnet`, `pingSubnet` '.$myFieldsInsert['query'].') ' . "\n"; + $query .= 'values (' . "\n"; + } + $query .= ' "'. $subnetDetails['subnet'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['mask'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['sectionId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['description'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['vlanId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['vrfId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['masterSubnetId'] .'", ' . "\n"; + $query .= ''. isCheckbox($subnetDetails['allowRequests']) .','."\n"; + $query .= ''. isCheckbox($subnetDetails['showName']) .','."\n"; + $query .= ' "'. $subnetDetails['permissions'] .'", '."\n"; + $query .= ''. isCheckbox($subnetDetails['discoverSubnet']) .','."\n"; + $query .= ''. isCheckbox($subnetDetails['pingSubnet']) .''."\n"; + $query .= $myFieldsInsert['values']; + $query .= ' );'; + } + } + # Delete subnet + else if ($subnetDetails['action'] == "delete") + { + /* get ALL slave subnets, then remove all subnets and IP addresses */ + global $removeSlaves; + getAllSlaves ($subnetDetails['subnetId']); + $removeSlaves = array_unique($removeSlaves); + + $query = ""; + foreach($removeSlaves as $slave) { + $query .= 'delete from `subnets` where `id` = "'. $slave .'"; '."\n"; + $query .= 'delete from `ipaddresses` where `subnetId` = "'. $slave .'"; '."\n"; + } + } + # Edit subnet + else if ($subnetDetails['action'] == "edit") + { + + # custom fields + $myFields = getCustomFields('subnets'); + $myFieldsInsert['query'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + if(strlen($subnetDetails[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = NULL '; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = "'.$subnetDetails[$myField['name']].'" '; + } + } + } + + $query = 'update subnets set '. "\n"; + $query .= '`description` = "'. $subnetDetails['description'] .'", '. "\n"; + if($subnetDetails['sectionId'] != $subnetDetails['sectionIdNew']) { + $query .= '`sectionId` = "'. $subnetDetails['sectionIdNew'] .'", '. "\n"; + } + $query .= '`vlanId` = "'. $subnetDetails['vlanId'] .'", '. "\n"; + $query .= '`vrfId` = "'. $subnetDetails['vrfId'] .'", '. "\n"; + $query .= '`masterSubnetId` = "'. $subnetDetails['masterSubnetId'] .'", '. "\n"; + $query .= '`allowRequests` = "'. isCheckbox($subnetDetails['allowRequests']) .'", '. "\n"; + $query .= '`showName` = "'. isCheckbox($subnetDetails['showName']) .'", '. "\n"; + $query .= '`discoverSubnet` = "'. isCheckbox($subnetDetails['discoverSubnet']) .'", '. "\n"; + $query .= '`pingSubnet` = "'. isCheckbox($subnetDetails['pingSubnet']) .'" '. "\n"; + $query .= $myFieldsInsert['query']; + $query .= 'where id = "'. $subnetDetails['subnetId'] .'"; '."\n"; + + # if section changes + if($subnetDetails['sectionId'] != $subnetDetails['sectionIdNew']) { + # add querry to change slaves! + global $removeSlaves; + getAllSlaves ($subnetDetails['subnetId']); + $removeSlaves = array_unique($removeSlaves); + + foreach($removeSlaves as $slave) { + if($subnetDetails['subnetId'] != $slave) { + $query .= 'update `subnets` set `sectionId` = "'. $subnetDetails['sectionIdNew'] .'" where `id` = "'.$slave.'"; '."\n"; + } + } + } + + # if vrf changes + if($subnetDetails['vrfId'] != $subnetDetails['vrfIdOld']) { + # add querry to change vrfId! + global $removeSlaves; + getAllSlaves ($subnetDetails['subnetId']); + $removeSlaves = array_unique($removeSlaves); + + foreach($removeSlaves as $slave) { + $query .= 'update `subnets` set `vrfId` = "'. $subnetDetails['vrfId'] .'" where `id` = "'.$slave.'"; '."\n"; + } + } + } + # Something is not right! + else { + + } + # return query + return $query; +} + + +/** + * delete subnet - only single subnet, no child/slave hosts and IP addresses are removed!!!! Beware !!! + */ +function deleteSubnet ($subnetId) +{ + global $database; + + # set modify subnet details query + $query = "delete from `subnets` where `id` = '$subnetId';"; + + # execute query + if (!$database->executeQuery($query)) { + updateLogTable ('Subnet delete from split failed', "id:$subnetId", 2); # write error log + return false; + } + else { + updateLogTable ('Subnet deleted from split ok', "id: $subnetId", 0); # write success log + return true; + } +} + + +/** + * Resize subnet - change mask + */ +function modifySubnetMask ($subnetId, $mask) +{ + global $database; + + # set modify subnet details query + $query = "update `subnets` set `mask` = '$mask' where `id` = '$subnetId';"; + + $log = "subnetId: $subnetId\n New mask: $mask"; # prepare log + + # execute query + if (!$database->executeQuery($query)) { + updateLogTable ('Subnet resize failed', $log, 2); # write error log + return false; + } + else { + updateLogTable ('Subnet resized ok', $log, 1); # write success log + + /* changelog */ + writeChangelog('subnet', 'resize', 'success', array(), array("subnetId"=>$subnetId, "mask"=>$mask)); + + return true; + } +} + + +/** + * truncate subnet + */ +function truncateSubnet($subnetId) +{ + global $database; + + /* first update request */ + $query = 'delete from `ipaddresses` where `subnetId` = '. $subnetId .';'; + + /* execute */ + try { $database->executeQuery($query); } + catch (Exception $e) { + $error = $e->getMessage(); + die('
'.$error.'
'); + } + + /* changelog */ + writeChangelog('subnet', 'truncate', 'success', array(), array("Truncate"=>'Subnet truncated', "subnetId"=>$subnetId)); + + /* return true if locked */ + return true; +} + + +/** + * Print subnets structure + */ +function printAdminSubnets( $subnets, $actions = true, $vrf = "0" ) +{ + $html = array(); + + $rootId = 0; # root is 0 + + if(sizeof($subnets) > 0) { + foreach ( $subnets as $item ) { + $item = (array) $item; + $children[$item['masterSubnetId']][] = $item; + } + } + + /* get custom fields */ + $custom = getCustomFields('subnets'); + + global $settings; + /* set hidden fields */ + $ffields = json_decode($settings['hiddenCustomFields'], true); + if(is_array($ffields['subnets'])) { $ffields = $ffields['subnets']; } + else { $ffields = array(); } + + # loop will be false if the root has no children (i.e., an empty menu!) + $loop = !empty( $children[$rootId] ); + + # initializing $parent as the root + $parent = $rootId; + $parent_stack = array(); + + # display selected subnet as opened + if(isset($_GET['subnetId'])) { + if(!is_numeric($_GET['subnetId'])) { die('
'._("Invalid ID").'
'); } + $allParents = getAllParents ($_GET['subnetId']); + } + # return table content (tr and td's) + while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $rootId ) ) ) + { + # repeat + $repeat = str_repeat( " - ", ( count($parent_stack)) ); + # dashes + if(count($parent_stack) == 0) { $dash = ""; } + else { $dash = "-"; } + + if(count($parent_stack) == 0) { + $margin = "0px"; + $padding = "0px"; + } + else { + # padding + $padding = "10px"; + + # margin + $margin = (count($parent_stack) * 10) -10; + $margin = $margin *2; + $margin = $margin."px"; + } + + # count levels + $count = count( $parent_stack ) + 1; + + # get VLAN + $vlan = subnetGetVLANdetailsById($option['value']['vlanId']); + $vlan = $vlan['number']; + if(empty($vlan) || $vlan == "0") { $vlan = ""; } # no VLAN + + # description + if(strlen($option['value']['description']) == 0) { $description = "/"; } # no description + else { $description = $option['value']['description']; } # description + + # requests + if($option['value']['allowRequests'] == 1) { $requests = ""; } # requests enabled + else { $requests = ""; } # request disabled + + # hosts check + if($option['value']['pingSubnet'] == 1) { $pCheck = ""; } # ping check enabled + else { $pCheck = ""; } # ping check disabled + + #vrf + if($vrf == "1") { + # get VRF details + if(($option['value']['vrfId'] != "0") && ($option['value']['vrfId'] != "NULL") ) { + $vrfTmp = getVRFDetailsById ($option['value']['vrfId']); + $vrfText = $vrfTmp['name']; + } + else { + $vrfText = ""; + } + } + + # print table line + if(strlen($option['value']['subnet']) > 0) { + $html[] = ""; + # folder + if($option['value']['isFolder']==1) { + $html[] = " $description"; + $html[] = " $description"; + } + else { + if($count==1) { + $html[] = " ".transform2long($option['value']['subnet']) ."/".$option['value']['mask'].""; + $html[] = " $description"; + } + else { + # last? + if(!empty( $children[$option['value']['id']])) { + $html[] = " ".transform2long($option['value']['subnet']) ."/".$option['value']['mask'].""; + $html[] = " $description"; + } + else { + $html[] = " ".transform2long($option['value']['subnet']) ."/".$option['value']['mask'].""; + $html[] = " $description"; + } + } + } + $html[] = " $vlan"; + #vrf + if($vrf == "1") { + $html[] = " $vrfText"; + } + $html[] = " $requests"; + $html[] = " $pCheck"; + # custom + if(sizeof($custom)>0) { + foreach($custom as $field) { + if(!in_array($field['name'], $ffields)) { + $html[] = " ".$option['value'][$field['name']].""; + } + } + } + # actions + if($actions) { + $html[] = " "; + $html[] = "
"; + if($option['value']['isFolder']==1) { + $html[] = " "; + $html[] = " "; + $html[] = " "; + } else { + $html[] = " "; + $html[] = " "; + $html[] = " "; + } + $html[] = "
"; + $html[] = " "; + } + $html[] = ""; + } + + if ( $option === false ) { $parent = array_pop( $parent_stack ); } + # Has slave subnets + elseif ( !empty( $children[$option['value']['id']] ) ) { + array_push( $parent_stack, $option['value']['masterSubnetId'] ); + $parent = $option['value']['id']; + } + # Last items + else { } + } + return implode( "\n", $html ); +} + + +/** + * Update subnet permissions + */ +function updateSubnetPermissions ($subnet) +{ + global $database; + + # replace special chars + $subnet['permissions'] = mysqli_real_escape_string($database, $subnet['permissions']); + + # set querries for subnet and each slave + foreach($subnet['slaves'] as $slave) { + $query .= "update `subnets` set `permissions` = '$subnet[permissions]' where `id` = $slave;"; + + writeChangelog('subnet', 'perm_change', 'success', array(), array("permissions_change"=>"$subnet[permissions]", "subnetId"=>$slave)); + } + + # execute + try { $database->executeMultipleQuerries($query); } + catch (Exception $e) { + $error = $e->getMessage(); + print('
'._('Error').': '.$error.'
'); + return false; + } + + /* return true if passed */ + return true; +} + + + + + + + + + + + +/* @section functions ---------------- */ + + +/** + * Update section + */ +function UpdateSection ($update, $api = false) +{ + global $database; + + # replace special chars for permissions + $update['permissions'] = mysqli_real_escape_string($database, $update['permissions']); + $update['description'] = mysqli_real_escape_string($database, $update['description']); + $update['name'] = mysqli_real_escape_string($database, $update['name']); + + if (!$api && !$update['name']) { die('
'._('Name is mandatory').'!
'); } # section name is mandatory + + $query = setUpdateSectionQuery ($update); # set update section query + + $log = prepareLogFromArray ($update); # prepare log + + /* save old if delete */ + if($update['action']=="delete") { $dold = getSectionDetailsById ($update['id']); } + elseif($update['action']=="edit") { $old = getSectionDetailsById ($update['id']); } + + + # delete and edit requires multiquery + if ( ( $update['action'] == "delete") || ( $update['action'] == "edit") ) + { + # execute + try { $result = $database->executeMultipleQuerries($query, true); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Section ' . $update['action'] .' failed ('. $update['name']. ') - '.$error, $log, 2); # write error log + if(!$api) print ('
'.("Cannot $update[action] all entries").' - '.$error.'!
'); + return false; + } + # success + updateLogTable ('Section '. $update['name'] . ' ' . $update['action'] .' ok', $log, 1); # write success log + + /* for changelog */ + if ($update['action']=="delete") { + $dold['id'] = $update['id']; + writeChangelog('section', $update['action'], 'success', $dold, array()); + } else { + writeChangelog('section', $update['action'], 'success', $old, $update); + } + + return true; + } + # add is single querry + else + { + # execute + try { $result = $database->executeQuery($query, true); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Adding section '. $update['name'] .'failed - '.$error, $log, 2); # write error log + if(!$api) die('
'.('Cannot update database').'!
'.$error.'
'); + return false; + } + # success + updateLogTable ('Section '. $update['name'] .' added succesfully', $log, 1); # write success log + + /* for changelog */ + $update['id'] = $result; + writeChangelog('section', $update['action'], 'success', array(), $update); + + return true; + } +} + + +/** + * Set Query for update section + */ +function setUpdateSectionQuery ($update) +{ + # add section + if ($update['action'] == "add" || $update['action'] == "create") + { + $query = 'Insert into sections (`name`,`description`,`permissions`,`strictMode`,`subnetOrdering`,`showVLAN`,`showVRF`, `masterSection`) values ("'.$update['name'].'", "'.$update['description'].'", "'.$update['permissions'].'", "'.isCheckbox($update['strictMode']).'", "'.$update['subnetOrdering'].'", "'.isCheckbox($update['showVLAN']).'", "'.isCheckbox($update['showVRF']).'", "'.$update['masterSection'].'");'; + } + # edit section + else if ($update['action'] == "edit" || $update['action'] == "update") + { + $section_old = getSectionDetailsById ( $update['id'] ); # Get old section name for update + # Update section + $query = "update `sections` set `name` = '$update[name]', `description` = '$update[description]', `permissions` = '$update[permissions]', `strictMode`='".isCheckbox($update['strictMode'])."', `subnetOrdering`='$update[subnetOrdering]', `showVLAN`='".isCheckbox($update['showVLAN'])."', `showVRF`='".isCheckbox($update['showVRF'])."', `masterSection`='$update[masterSection]' where `id` = '$update[id]';"; + + # delegate permissions if set + if($update['delegate'] == 1) { + $query .= "update `subnets` set `permissions` = '$update[permissions]' where `sectionId` = '$update[id]';"; + } + } + # delete section + else if( $update['action'] == "delete" ) + { + /* we must delete many entries - section, all belonging subnets and ip addresses */ + $sectionId = $update['id']; + + # delete sections query + $query = "delete from `sections` where `id` = '$sectionId';"."\n"; + # delete belonging subnets + $query .= "delete from `subnets` where `sectionId` = '$sectionId';"."\n"; + # delete IP addresses query + $subnets = fetchSubnets ( $sectionId ); + + if (sizeof($subnets) != 0) { + foreach ($subnets as $subnet) { + $query .= "delete from `ipaddresses` where `subnetId` = '$subnet[id]';"."\n"; + } + } + + # if it has subsections delete all subsections and subnets/ip addresses + if(sizeof($subsections = getAllSubSections($sectionId))>0) { + foreach($subsections as $ss) { + $query .= "delete from `sections` where `id` = '$ss[id]';"."\n"; + $query .= "delete from `subnets` where `sectionId` = '$ss[id]';"."\n"; + $ssubnets = fetchSubnets ( $ss['id'] ); + if (sizeof($ssubnets) != 0) { + foreach ($ssubnets as $subnet) { + $query .= "delete from `ipaddresses` where `subnetId` = '$subnet[id]';"."\n"; + } + } + } + } + } + + /* return query */ + return $query; +} + + +/** + * Update section ordering + */ +function UpdateSectionOrder ($order) +{ + global $database; + + // set querries for each section + $query = ""; + foreach($order as $key=>$o) { + $query .= "update `sections` set `order` = $o where `id` = $key; \n"; + } + //log + $log = prepareLogFromArray ($order); + //execute multiple queries + try { $result = $database->executeMultipleQuerries($query); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Section reordering failed ('. $update['name']. ') - '.$error, $log, 2); # write error log + print ('
'._("Cannot reorder sections").' - '.$error.'!
'); + return false; + } + # success + updateLogTable ('Section reordering ok', $log, 1); # write success log + return true; +} + + +/** + * Parse section permissions + */ +function parseSectionPermissions($permissions) +{ + # save to array + $permissions = json_decode($permissions, true); + + if(sizeof($permissions)>0) { + foreach($permissions as $key=>$p) { + $tmp = getGroupById($key); + $out[$tmp['g_id']] = $p; + } + } + /* return array of groups */ + return $out; +} + + + + + + + + + +/* @device functions ---------------- */ + + +/** + * Update device details + */ +function updateDeviceDetails($device) +{ + global $database; + + /* set querry based on action */ + if($device['action'] == "add") { + + # custom fields + $myFields = getCustomFields('devices'); + $myFieldsInsert['query'] = ''; + $myFieldsInsert['values'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + # empty? + if(strlen($device[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", NULL"; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", '". $device[$myField['name']] . "'"; + } + } + } + + $query = 'insert into `devices` '. "\n"; + $query .= '(`hostname`,`ip_addr`, `type`, `vendor`,`model`,`version`,`description`,`sections` '.$myFieldsInsert['query'].') values '. "\n"; + $query .= '("'. $device['hostname'] .'", "'. $device['ip_addr'] .'", "'.$device['type'].'", "'. $device['vendor'] .'", '. "\n"; + $query .= ' "'. $device['model'] .'", "'. $device['version'] .'", "'. $device['description'] .'", "'. $device['sections'] .'" '. $myFieldsInsert['values'] .');'. "\n"; + } + else if($device['action'] == "edit") { + + # custom fields + $myFields = getCustomFields('devices'); + $myFieldsInsert['query'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + if(strlen($device[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = NULL '; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = "'.$device[$myField['name']].'" '; + } + } + } + + $query = 'update `devices` set '. "\n"; + $query .= '`hostname` = "'. $device['hostname'] .'", `ip_addr` = "'. $device['ip_addr'] .'", `type` = "'. $device['type'] .'", `vendor` = "'. $device['vendor'] .'", '. "\n"; + $query .= '`model` = "'. $device['model'] .'", `version` = "'. $device['version'] .'", `description` = "'. $device['description'] .'", '. "\n"; + $query .= '`sections` = "'. $device['sections'] .'" '. "\n"; + $query .= $myFieldsInsert['query']; + $query .= 'where `id` = "'. $device['switchId'] .'";'. "\n"; + } + else if($device['action'] == "delete") { + $query = 'delete from `devices` where id = "'. $device['switchId'] .'";'. "\n"; + } + + /* prepare log */ + $log = prepareLogFromArray ($device); + + /* execute */ + try { $res = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Device ' . $device['action'] .' failed ('. $device['hostname'] . ')'.$error, $log, 2); + return false; + } + + /* success */ + updateLogTable ('Device ' . $device['action'] .' success ('. $device['hostname'] . ')', $log, 0); + return true; +} + + +/** + * Update device details + */ +function updateDevicetypeDetails($device) +{ + global $database; + + /* set querry based on action */ + if($device['action'] == "add") { $query = "insert into `deviceTypes` (`tname`,`tdescription`) values ('$device[tname]', '$device[tdescription]');"; } + else if($device['action'] == "edit") { $query = "update `deviceTypes` set `tname` = '$device[tname]', `tdescription` = '$device[tdescription]' where `tid` = $device[tid]"; } + else if($device['action'] == "delete") { $query = "delete from `deviceTypes` where `tid` = $device[tid];"; } + + /* prepare log */ + $log = prepareLogFromArray ($device); + + /* execute */ + try { $res = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print $error; + updateLogTable ("Device $device[action] failed ($device[tname]) $error", $log, 2); + return false; + } + + /* if delete we need to null type in devices! */ + if($device['action'] == "delete") { + $query = "update `devices` set `type` = NULL where `type` = $device[tid];"; + try { $res = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print "
$e
";; + } + } + + /* success */ + updateLogTable ("Device $device[action] success ($device[tname])", $log, 0); + return true; +} + + +/** + * reformat sections for devices! + * sections are separated with ; + */ +function reformatDeviceSections ($sections) { + + if(sizeof($sections != 0)) { + + //first reformat + $temp = explode(";", $sections); + + foreach($temp as $section) { + //we have sectionId, so get its name + $out = getSectionDetailsById($section); + $out = $out['name']; + + //format output + $result[$out] = $section; + } + } + + //return result if it exists + if($result) { + return $result; + } + else { + return false; + } +} + + +/** + * get switch type + */ +function getDeviceTypes() +{ + global $database; + + $query = "select * from `deviceTypes`;"; + + /* execute */ + try { $devices = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* rekey */ + foreach($devices as $d) { + $devices2[$d['tid']] = _("$d[tname]"); + } + + /* return unique devices */ + return $devices2; +} + + +/** + * Transfor switch type + */ +function TransformDeviceType($type) +{ + global $database; + + $query = "select * from `deviceTypes` where `tid` = $type;"; + + /* execute */ + try { $devices = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + return $devices[0]['tname']; +} + + + + + + + + + + + + +/* @adLDAP functions ---------------- */ + +/** + * Get Domain settings for authentication + */ +if(!function_exists('getADSettings')) { +function getADSettings() +{ + global $database; + + /* Check connection */ + if ($database->connect_error) { + die('Connect Error (' . $database->connect_errno . '): '. $database->connect_error); + } + + /* first update request */ + $query = 'select * from `settingsDomain` limit 1;'; + $settings = $database->getArray($query); + + /* execute */ + try { $settings = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return settings */ + return($settings[0]); +} +} + + +/** + * Get Domain settings for authentication + */ +function updateADsettings($ad) +{ + global $database; + + /* Check connection */ + if ($database->connect_error) { + die('Connect Error (' . $database->connect_errno . '): '. $database->connect_error); + } + + /* if OpenLDAP then append BaseDN to account suffix */ + if($ad['type'] == "2") { $ad['account_suffix'] = ",".$ad['base_dn']; } + + /* set query and update */ + $query = 'update `settingsDomain` set '. "\n"; + $query .= '`domain_controllers` = "'. $ad['domain_controllers'] .'", `base_dn` = "'. $ad['base_dn'] .'", `account_suffix` = "'. $ad['account_suffix'] .'", '. "\n"; + $query .= '`use_ssl` = "'. $ad['use_ssl'] .'", `use_tls` = "'. $ad['use_tls'] .'", `ad_port` = "'. $ad['ad_port'] .'", `adminUsername`="'.$ad['adminUsername'].'", `adminPassword`="'.$ad['adminPassword'].'";'. "\n"; + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + # success + return true; +} + + + + + + + + +/* @VRF functions ---------------- */ + + +/** + * Update VRF details + */ +function updateVRFDetails($vrf) +{ + global $database; + + /* set querry based on action */ + if($vrf['action'] == "add") { + $query = 'insert into `vrf` '. "\n"; + $query .= '(`name`,`rd`,`description`) values '. "\n"; + $query .= '("'. $vrf['name'] .'", "'. $vrf['rd'] .'", "'. $vrf['description'] .'" ); '. "\n"; + } + else if($vrf['action'] == "edit") { + $query = 'update `vrf` set '. "\n"; + $query .= '`name` = "'. $vrf['name'] .'", `rd` = "'. $vrf['rd'] .'", `description` = "'. $vrf['description'] .'" '. "\n"; + $query .= 'where `vrfId` = "'. $vrf['vrfId'] .'";'. "\n"; + } + else if($vrf['action'] == "delete") { + $query = 'delete from `vrf` where `vrfId` = "'. $vrf['vrfId'] .'";'. "\n"; + } + + /* execute */ + try { $res = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + updateLogTable ('VRF ' . $vrf['action'] .' failed ('. $vrf['name'] . ')'.$error, $log, 2); + return false; + } + + # if delete also NULL all subnets! + if($vrf['action'] == 'delete') { + $query = "update `subnets` set `vrfId` = NULL where `vrfId` = '$vrf[vrfId]';"; + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ('
'.$error.'
'); + } + } + + /* prepare log */ + $log = prepareLogFromArray ($vrf); + + /* return details */ + updateLogTable ('VRF ' . $vrf['action'] .' success ('. $vrf['name'] . ')', $log, 0); + return true; +} + + + + + + + + + + +/* @VLAN functions ---------------- */ + + +/** + * Update VLAN details + */ +function updateVLANDetails($vlan, $lastId = false) +{ + global $database; + + /* set querry based on action */ + if($vlan['action'] == "add") { + + # custom fields + $myFields = getCustomFields('vlans'); + $myFieldsInsert['query'] = ''; + $myFieldsInsert['values'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + # empty? + if(strlen($vlan[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", NULL"; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", '". $vlan[$myField['name']] . "'"; + } + } + } + + $query = 'insert into `vlans` '. "\n"; + $query .= '(`name`,`number`,`description` '.$myFieldsInsert['query'].') values '. "\n"; + $query .= '("'. $vlan['name'] .'", "'. $vlan['number'] .'", "'. $vlan['description'] .'" '. $myFieldsInsert['values'] .' ); '. "\n"; + + } + else if($vlan['action'] == "edit") { + + # custom fields + $myFields = getCustomFields('vlans'); + $myFieldsInsert['query'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + if(strlen($vlan[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = NULL '; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = "'.$vlan[$myField['name']].'" '; + } + } + } + + $query = 'update `vlans` set '. "\n"; + $query .= '`name` = "'. $vlan['name'] .'", `number` = "'. $vlan['number'] .'", `description` = "'. $vlan['description'] .'" '. "\n"; + $query .= $myFieldsInsert['query']; + $query .= 'where `vlanId` = "'. $vlan['vlanId'] .'";'. "\n"; + } + else if($vlan['action'] == "delete") { + $query = 'delete from `vlans` where `vlanId` = "'. $vlan['vlanId'] .'";'. "\n"; + } + + /* execute */ + try { $res = $database->executeQuery( $query, true ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + updateLogTable ('VLAN ' . $vlan['action'] .' failed ('. $vlan['name'] . ')'.$error, $log, 2); + return false; + } + + # if delete also NULL all subnets! + if($vlan['action'] == 'delete') { + $query = "update `subnets` set `vlanId` = NULL where `vlanId` = '$vlan[vlanId]';"; + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ('
'.$error.'
'); + } + } + + /* prepare log */ + $log = prepareLogFromArray ($vlan); + + /* return success */ + updateLogTable ('VLAN ' . $vlan['action'] .' success ('. $vlan['name'] . ')', $log, 0); + + /* response */ + if($lastId) { return $res; } + else { return true; } +} + + + + + + + + + + +/* @other functions ---------------- */ + + +/** + * update site settings + */ +function updateSettings($settings) +{ + global $database; + + filter_user_input ($settings, true, true, false); + + /* first update request */ + $query = 'update `settings` set ' . "\n"; + $query .= '`siteTitle` = "'. $settings['siteTitle'] .'", ' . "\n"; + $query .= '`siteDomain` = "'. $settings['siteDomain'] .'", ' . "\n"; + $query .= '`siteURL` = "'. $settings['siteURL'] .'", ' . "\n"; + $query .= '`siteAdminName` = "'. $settings['siteAdminName'] .'", ' . "\n"; + $query .= '`siteAdminMail` = "'. $settings['siteAdminMail'] .'", ' . "\n"; + $query .= '`domainAuth` = "'. isCheckbox($settings['domainAuth']) .'", ' . "\n"; + $query .= '`enableIPrequests` = "'. isCheckbox($settings['enableIPrequests']) .'", ' . "\n"; + $query .= '`enableVRF` = "'. isCheckbox($settings['enableVRF']) .'", ' . "\n"; + $query .= '`donate` = "'. isCheckbox($settings['donate']) .'", ' . "\n"; + $query .= '`enableDNSresolving` = "'. isCheckbox($settings['enableDNSresolving']) .'", ' . "\n"; + $query .= '`dhcpCompress` = "'. isCheckbox($settings['dhcpCompress']) .'", ' . "\n"; + $query .= '`printLimit` = "'. $settings['printLimit'] .'", ' . "\n"; + $query .= '`visualLimit` = "'. $settings['visualLimit'] .'", ' . "\n"; + $query .= '`vlanDuplicate` = "'. isCheckbox($settings['vlanDuplicate']) .'", ' . "\n"; + $query .= '`vlanMax` = "'. $settings['vlanMax'] .'", ' . "\n"; + $query .= '`api` = "'. isCheckbox($settings['api']) .'", ' . "\n"; + $query .= '`enableChangelog` = "'. isCheckbox($settings['enableChangelog']) .'", ' . "\n"; + $query .= '`subnetOrdering` = "'. $settings['subnetOrdering'] .'", ' . "\n"; + $query .= '`pingStatus` = "'. $settings['pingStatus'] .'", ' . "\n"; + $query .= '`scanPingPath` = "'. $settings['scanPingPath'] .'", ' . "\n"; + $query .= '`scanMaxThreads` = "'. $settings['scanMaxThreads'] .'", ' . "\n"; + $query .= '`prettyLinks` = "'. $settings['prettyLinks'] .'", ' . "\n"; + $query .= '`inactivityTimeout` = "'. $settings['inactivityTimeout'] .'", ' . "\n"; + $query .= '`hideFreeRange` = "'. isCheckbox($settings['hideFreeRange']) .'", ' . "\n"; + $query .= '`defaultLang` = "'. $settings['defaultLang'] .'" ' . "\n"; + $query .= 'where id = 1;' . "\n"; + + /* set log file */ + foreach($settings as $key=>$setting) { + $log .= " ". $key . ": " . $setting . "
"; + } + + /* execute */ + try { + $database->executeQuery( $query ); + } + catch (Exception $e) { + $error = $e->getMessage(); + print '
'._('Update settings error').':
'. $error .'
'; + updateLogTable ('Failed to update settings', $log, 2); + return false; + } + + if(!isset($e)) { + updateLogTable ('Settings updated', $log, 1); + return true; + } +} + + +/** + * post-install updates + */ +function postauth_update($adminpass, $siteTitle, $siteURL) +{ + global $database; + + $query = "update `users` set `password`='$adminpass',`passChange`='No' where `username` = 'Admin';"; //to update admin pass + $query .= "update `settings` set `siteTitle`='$siteTitle',`siteURL`='$siteURL';"; + + /* execute */ + try { + $database->executeMultipleQuerries( $query ); + } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Failed to update settings', $log, 2); + return false; + } + return true; +} + + +/** + * update mail settings + */ +function updateMailSettings($settings) +{ + global $database; + + /* first update request */ + $query = 'update `settingsMail` set ' . "\n"; + $query .= '`mtype` = "'. $settings['mtype'] .'", ' . "\n"; + $query .= '`mserver` = "'. $settings['mserver'] .'", ' . "\n"; + $query .= '`mport` = "'. $settings['mport'] .'", ' . "\n"; + $query .= '`mauth` = "'. $settings['mauth'] .'", ' . "\n"; + $query .= '`msecure` = "'. $settings['msecure'] .'", ' . "\n"; + $query .= '`muser` = "'. $settings['muser'] .'", ' . "\n"; + $query .= '`mpass` = "'. $settings['mpass'] .'", ' . "\n"; + $query .= '`mAdminName` = "'. $settings['mAdminName'] .'", ' . "\n"; + $query .= '`mAdminMail` = "'. $settings['mAdminMail'] .'" ' . "\n"; + $query .= 'where id = 1;' . "\n"; + + /* set log file */ + foreach($settings as $key=>$setting) { + $log .= " ". $key . ": " . $setting . "
"; + } + + /* execute */ + try { + $database->executeQuery( $query ); + } + catch (Exception $e) { + $error = $e->getMessage(); + print '
'._('Update settings error').':
'. $error .'
'; + updateLogTable ('Failed to update settings', $log, 2); + return false; + } + + if(!isset($e)) { + updateLogTable ('Settings updated', $log, 1); + return true; + } +} + + +/** + * Verify checkboxes for saving config + */ +function isCheckbox($checkbox) +{ + if($checkbox == "") { $chkbox = "0"; } + else { $chkbox = $checkbox; } + + /* return 0 if not checkbos and same result if checkbox */ + return $chkbox; +} + + +/** + * Search and replace fields + */ +function searchAndReplace($query, $post) +{ + global $database; + + /* check how many records are in database */ + $query2 = 'select count(*) as count from `ipaddresses` where '. $post['field'] .' like "%'. $post['search'] .'%";'; + $count = $database->getArray($query2); + $count = $count[0]['count']; + + /* execute */ + try { + $database->executeQuery( $query ); + } + catch (Exception $e) { + $error = $e->getMessage(); + die('
'._('Error').': '. $error .'
'); + } + + if(!isset($e)) { + print '
'._('Replaced').' '. $count .' '._('items successfully').'!
'; + } +} + + +/** + * Write instructions + */ +function writeInstructions ($instructions) +{ + global $database; + + $instructions = $database->real_escape_string($instructions); //this hides code + + # execute query + $query = "update `instructions` set `instructions` = '". $instructions ."';"; + + /* update database */ + if ( !$database->executeQuery($query) ) { + updateLogTable ('Instructions update failed', $instructions, 2); + return false; + } + else { + updateLogTable ('Instructions update succeeded', $instructions, 1); + return true; + } +} + + +/** + * CSV import IP address + * + * provided input is CSV line! + */ +function importCSVline ($line, $subnetId) +{ + global $database; + + /* get subnet details by Id */ + $subnetDetails = getSubnetDetailsById ($subnetId); + $subnet = Transform2long($subnetDetails['subnet']) . "/" . $subnetDetails['mask']; + + /* verify! */ + $err = VerifyIpAddress( $line[0],$subnet ); + if($err) { return _('Wrong IP address').' - '.$err.' - '.$line[0]; } + + /* check for duplicates */ + if (checkDuplicate ($line[0], $subnetId)) { return _('IP address already exists').' - '.$line[0]; } + + /* get custom fields */ + $myFields = getCustomFields('ipaddresses'); + if(sizeof($myFields) > 0) { + $import['fieldName'] = ""; + $import['fieldValue'] = ""; + $m = 9; + foreach($myFields as $field) { + //escape chars + $line[$m] = mysqli_real_escape_string($database, $line[$m]); + + $import['fieldName'] .= ",`$field[name]`"; + $import['fieldValue'] .= ",'$line[$m]'"; + $m++; + } + } + + /* escape chars */ + foreach($line as $k=>$l) { + $line[$k] = mysqli_real_escape_string($database, $l); + } + + /* all ok, set query */ + $query = "insert into ipaddresses "; + $query .= "(`subnetId`, `ip_addr`, `state`, `description`, `dns_name`, `mac`, `owner`, `switch`, `port`, `note` $import[fieldName] ) "; + $query .= "values "; + $query .= "('$subnetId','".Transform2decimal( $line[0] )."', '$line[1]','$line[2]','$line[3]','$line[4]','$line[5]','$line[6]','$line[7]','$line[8]' $import[fieldValue]);"; + +/* + print "
";
+	print_r($line);
+	die('alert alert-danger');
+*/
+
+	/* set log details */
+	$log = prepareLogFromArray ($line);
+
+	/* execute */
+    try {
+    	$database->executeQuery( $query );
+    }
+    catch (Exception $e) {
+    	$error = $e->getMessage();
+	}
+
+	if(!isset($e)) {
+        updateLogTable ('CSV import of IP address '. $line[1] .' succeeded', $log, 0);
+		return true;
+	}
+	else {
+        updateLogTable ('CSV import of IP address '. $line[1] .' failed', $log, 2);
+        return $error;
+	}
+}
+
+
+
+
+
+
+
+
+
+/* @filter functions ---------------- */
+
+
+/**
+ * Get all fields in IP addresses
+ */
+function getIPaddrFields()
+{
+    global $database;
+
+    /* first update request */
+    $query    = 'describe `ipaddresses`;';
+
+    /* execute */
+    try { $fields = $database->getArray( $query ); }
+    catch (Exception $e) {
+        $error =  $e->getMessage();
+        print ("
"._('Error').": $error
"); + return false; + } + + /* return Field values only */ + foreach($fields as $field) { + $res[$field['Field']] = $field['Field']; + } + + return $res; +} + + +/** + * Get selected IP fields + */ +function getSelectedIPaddrFields() +{ + global $settings; + # we only need it if it is not already set! + if(!isset($settings)) { + + global $database; + + /* first update request */ + $query = 'select IPfilter from `settings`;'; + + /* execute */ + try { $fields = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + return $fields[0]['IPfilter']; + } + else { + return $settings['IPfilter']; + } + + return $settings['IPfilter']; +} + + +/** + * Set selected IP fields + */ +function updateSelectedIPaddrFields($fields) +{ + global $database; + + /* first update request */ + $query = 'update `settings` set `IPfilter` = "'. $fields .'";'; + + # execute query + if (!$database->executeQuery($query)) { + updateLogTable ('Failed to change IP field filter', $fields, 2); + return false; + } + else { + updateLogTable ('IP field filter change success', $fields, 1); + return true; + } +} + + + + + + + + + + +/* @custom fields */ + + +/** + * Get all custom fields + */ +function getCustomFields($table) +{ + global $database; + + /* first update request */ + $query = "show full columns from `$table`;"; + + /* execute */ + try { $fields = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return Field values only */ + foreach($fields as $field) { + $res[$field['Field']]['name'] = $field['Field']; + $res[$field['Field']]['type'] = $field['Type']; + $res[$field['Field']]['Comment'] = $field['Comment']; + $res[$field['Field']]['Null'] = $field['Null']; + $res[$field['Field']]['Default'] = $field['Default']; + } + + /* unset standard fields */ + if($table == "users") { + unset($res['id'], $res['username'], $res['password'], $res['groups'], $res['role'], $res['real_name'], $res['email'], $res['domainUser'], $res['lang']); + unset($res['editDate'],$res['widgets'],$res['favourite_subnets'],$res['mailNotify'],$res['mailChangelog'], $res['passChange']); + } + elseif($table == "devices") { + unset($res['id'], $res['hostname'], $res['ip_addr'], $res['type'], $res['vendor'], $res['model'], $res['version'], $res['description'], $res['sections'], $res['editDate']); + } + elseif($table == "subnets") { + unset($res['id'], $res['subnet'], $res['mask'], $res['sectionId'], $res['description'], $res['masterSubnetId']); + unset($res['vrfId'], $res['allowRequests'], $res['adminLock'], $res['vlanId'], $res['showName'],$res['permissions'],$res['editDate']); + unset($res['pingSubnet'], $res['isFolder'], $res['discoverSubnet']); + } + elseif($table == "ipaddresses") { + unset($res['id'], $res['subnetId'], $res['ip_addr'], $res['description'], $res['dns_name'], $res['switch']); + unset($res['port'], $res['mac'], $res['owner'], $res['state'], $res['note'], $res['lastSeen'], $res['excludePing'], $res['editDate']); + } + elseif($table == "vlans") { + unset($res['vlanId'], $res['name'], $res['number'], $res['description'],$res['editDate']); + } + + /* reset if empty */ + if(sizeof($res)==0) { $res = array(); } + + return $res; +} + + +/** + * Get all custom fields in number array + */ +function getCustomFieldsNumArr($table) +{ + $res = getCustomFields($table); + + /* reindex */ + foreach($res as $line) { + $out[] = $line['name']; + } + + return $out; +} + + +/** + * Update custom field + */ +function updateCustomField($field) +{ + global $database; + + /* escape vars */ + # set override + if($field['fieldType']!="set") { + $field = filter_user_input ($field, true, true); + } + + /* set db type values */ + if($field['fieldType']=="bool" || $field['fieldType']=="text" || $field['fieldType']=="date" || $field['fieldType']=="datetime") + { $field['ftype'] = "$field[fieldType]"; } + else { $field['ftype'] = "$field[fieldType]($field[fieldSize])"; } + + //default null + if(strlen($field['fieldDefault'])==0) { $field['fieldDefault'] = "NULL"; } + else { $field['fieldDefault'] = "'$field[fieldDefault]'"; } + + //character? + if($field['fieldType']=="varchar" || $field['fieldType']=="text" || $field['fieldType']=="set") { $charset = "CHARACTER SET utf8"; } + else { $charset = ""; } + + /* update request */ + if($field['action']=="delete") { $query = "ALTER TABLE `$field[table]` DROP `$field[name]`;"; } + else if ($field['action']=="edit"&&@$field['NULL']=="NO") { $query = "ALTER TABLE `$field[table]` CHANGE COLUMN `$field[oldname]` `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] NOT NULL COMMENT '$field[Comment]';"; } + else if ($field['action']=="edit") { $query = "ALTER TABLE `$field[table]` CHANGE COLUMN `$field[oldname]` `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] COMMENT '$field[Comment]';"; } + else if ($field['action']=="add"&&@$field['NULL']=="NO") { $query = "ALTER TABLE `$field[table]` ADD COLUMN `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] NOT NULL COMMENT '$field[Comment]';"; } + else if ($field['action']=="add") { $query = "ALTER TABLE `$field[table]` ADD COLUMN `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] NULL COMMENT '$field[Comment]';"; } + else { + return false; + } + + /* prepare log */ + $log = prepareLogFromArray ($field); + + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + updateLogTable ('Custom Field ' . $field['action'] .' failed ('. $field['name'] . ')', $log, 2); + return false; + } + + updateLogTable ('Custom Field ' . $field['action'] .' success ('. $field['name'] . ')', $log, 0); + return true; +} + + +/** + * reorder custom VLAN field + */ +function reorderCustomField($table, $next, $current) +{ + global $database; + + /* get field details */ + $old = getFullFieldData($table, $current); + + /* update request */ + if($old['Null']=="NO") { $query = 'ALTER TABLE `'.$table.'` MODIFY COLUMN `'. $current .'` '.$old['Type'].' NOT NULL COMMENT "'.$old['Comment'].'" AFTER `'. $next .'`;'; } + else { $query = 'ALTER TABLE `'.$table.'` MODIFY COLUMN `'. $current .'` '.$old['Type'].' DEFAULT NULL COMMENT "'.$old['Comment'].'" AFTER `'. $next .'`;'; } + + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + updateLogTable ('Custom Field reordering failed ('. $next .' was not put before '. $current .')', $log, 2); + return false; + } + + updateLogTable ('Custom Field reordering success ('. $next .' put before '. $current .')', $log, 0); + return true; +} + + +/** + * update filter for custom fields + */ +function save_filtered_custom_fields($table, $filtered) +{ + # prepare + if(is_null($filtered)) { $out = null; } + else { $out = $filtered; } + + # write + return write_custom_filter($table,$out); +} + + +/** + * save filtered fields + */ +function write_custom_filter($table, $out) +{ + $settings = getAllSettings(); + + if(strlen($settings['hiddenCustomFields'])>0) { $filterField = json_decode($settings['hiddenCustomFields'], true); } + else { $filterField = array(); } + + # set + if(is_null($out)) { unset($filterField[$table]); } + else { $filterField[$table]=$out; } + + # encode + $filterField = json_encode($filterField); + + # write + global $database; + $query = "update `settings` set `hiddenCustomFields`='$filterField';"; + + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + return true; +} + + + + + + + + + + +/* @api --------------------*/ + +/** + * Get all API keys + */ +function getAPIkeys() +{ + global $database; + + # set query + $query = 'select * from `api`;'; + # get result + try { $apis = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + return $apis; +} + + +/** + * Get API key by name + */ +function getAPIkeyByName($app_id, $reformat = false) +{ + global $database; + + # set query + $query = "select * from `api` where `app_id` = '$app_id';"; + # get result + try { $api = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + # reformat? + if($reformat) { + $out[$api[0]['app_id']] = $api[0]['app_code']; + return $out; + } + else { + return $api[0]; + } +} + + +/** + * Get API key by id + */ +function getAPIkeyById($id) +{ + global $database; + + # set query + $query = "select * from `api` where `id` = $id;"; + # get result + try { $api = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + return $api[0]; +} + + +/** + * Modify API details + */ +function modifyAPI($api) +{ + global $database; + + # set query based on action + if($api['action']=="add") { $query = "insert into `api` (`app_id`,`app_code`,`app_permissions`, `app_comment`) values ('$api[app_id]','$api[app_code]','$api[app_permissions]', '$api[app_comment]');"; } + elseif($api['action']=="edit") { $query = "update `api` set `app_id`='$api[app_id]',`app_code`='$api[app_code]',`app_permissions`='$api[app_permissions]', `app_comment`='$api[app_comment]' where `id`=$api[id] ; "; } + elseif($api['action']=="delete") { $query = "delete from `api` where `id` = $api[id];"; } + else { return false; } + + $log = prepareLogFromArray ($api); # prepare log + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('API update failed - '.$error, $log, 2); # write error log + return false; + } + + # success + updateLogTable ('API updated ok', $log, 1); # write success log + return true; +} + + + +/** + * Verify database + */ +function verifyDatabase() +{ + /* required tables */ + $reqTables = array("instructions", "ipaddresses", "logs", "requests", "sections", "settings", "settingsDomain", "subnets", "devices", "deviceTypes", "users", "vrf", "vlans", "widgets", "changelog", "userGroups", "lang", "api", "settingsMail"); + + /* required fields for each table */ + $fields['instructions'] = array("instructions"); + $fields['ipaddresses'] = array("subnetId", "ip_addr", "description", "dns_name", "mac", "owner", "switch", "port", "owner", "state", "note", "lastSeen", "excludePing"); + $fields['logs'] = array("severity", "date", "username", "ipaddr", "command", "details"); + $fields['requests'] = array("subnetId", "ip_addr", "description", "dns_name", "owner", "requester", "comment", "processed", "accepted", "adminComment"); + $fields['sections'] = array("name", "description", "permissions", "strictMode", "subnetOrdering", "order", "showVLAN", "showVRF", "masterSection"); + $fields['settings'] = array("siteTitle", "siteAdminName", "siteAdminMail", "siteDomain", "siteURL", "domainAuth", "enableIPrequests", "enableVRF", "enableDNSresolving", "version", "dbverified", "donate", "IPfilter", "printLimit", "visualLimit", "vlanDuplicate", "vlanMax", "subnetOrdering", "pingStatus", "defaultLang", "api", "editDate", "vcheckDate", "dhcpCompress", "enableChangelog", "scanPingPath", "scanMaxThreads", "prettyLinks", "hideFreeRange", "hiddenCustomFields", "inactivityTimeout"); + $fields['settingsDomain'] = array("account_suffix", "base_dn", "domain_controllers", "use_ssl", "use_tls", "ad_port", "adminUsername", "adminPassword"); + $fields['subnets'] = array("subnet", "mask", "sectionId", "description", "masterSubnetId", "vrfId", "allowRequests", "vlanId", "showName", "permissions", "pingSubnet", "discoverSubnet", "isFolder"); + $fields['devices'] = array("hostname", "ip_addr", "type", "vendor", "model", "version", "description", "sections"); + $fields['deviceTypes'] = array("tid", "tname", "tdescription"); + $fields['users'] = array("username", "password", "groups", "role", "real_name", "email", "domainUser", "lang", "widgets", "favourite_subnets", "mailNotify", "mailChangelog", "passChange"); + $fields['vrf'] = array("vrfId","name", "rd", "description"); + $fields['vlans'] = array("vlanId", "name", "number", "description"); + $fields['userGroups'] = array("g_id", "g_name", "g_desc"); + $fields['lang'] = array("l_id", "l_code", "l_name"); + $fields['api'] = array("app_id", "app_code", "app_permissions", "app_comment"); + $fields['changelog'] = array("cid", "ctype", "coid", "cuser", "caction", "cresult", "cdate", "cdiff"); + $fields['widgets'] = array("wid", "wtitle", "wdescription", "wfile", "wparams", "whref", "wsize", "wadminonly", "wactive"); + $fields['settingsMail'] = array("id", "mtype", "mauth", "mserver", "mport", "muser", "mpass", "mAdminName", "mAdminMail", "msecure"); + + /** + * check that each database exist - if it does check also fields + * 2 errors -> $tableError, $fieldError[table] = field + ****************************************************************/ + + foreach($reqTables as $table) { + + //check if table exists + if(!tableExists($table)) { + $error['tableError'][] = $table; + } + //check for each field + else { + foreach($fields[$table] as $field) { + //if it doesnt exist store error + if(!fieldExists($table, $field)) { + $error['fieldError'][$table] = $field; + } + } + } + } + + /* result */ + if(isset($error)) { + return $error; + } else { + return array(); + } +} + + +/** + * Update verified flag + */ +function updateDBverify() +{ + global $database; + + # set query based on action + $query = "update `settings` set `dbverified`='1'; "; + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { } + # return + return true; +} + + +/** + * get table fix + */ +function getTableFix($table) +{ + $res = fopen(dirname(__FILE__) . "/../db/SCHEMA.sql", "r"); + $file = fread($res, 100000); + + //go from delimiter on + $file = strstr($file, "DROP TABLE IF EXISTS `$table`;"); + $file = trim(strstr($file, "# Dump of table", true)); + + # check + if(strpos($file, "DROP TABLE IF EXISTS `$table`;") > 0 ) return false; + else return $file; +} + + +/** + * get field fix + */ +function getFieldFix($table, $field) +{ + $res = fopen(dirname(__FILE__) . "/../db/SCHEMA.sql", "r"); + $file = fread($res, 100000); + + //go from delimiter on + $file = strstr($file, "DROP TABLE IF EXISTS `$table`;"); + $file = trim(strstr($file, "# Dump of table", true)); + + //get proper line + $file = explode("\n", $file); + foreach($file as $k=>$l) { + if(strpos(trim($l), "$field`")==1) { + //get previous + $prev = trim($file[$k-1]); + $prev = explode("`", $prev); + $prev = "`$prev[1]`"; + + $res = trim($l, ","); + $res .= " after $prev;"; + + return $res; + } + } + + return false; +} + + +/** + * Fix table + */ +function fixTable($table) +{ + global $database; + + //get fix + $query = getTableFix($table); + + /* execute */ + try { $database->executeMultipleQuerries( $query ); } + catch (Exception $e) { + die("
".$e->getMessage()."
"); + } + # return + return true; +} + + +/** + * Fix field + */ +function fixField($table, $field) +{ + global $database; + + //get fix + $query = "alter table `$table` add "; + $query .= trim(getFieldFix($table, $field), ","); + $query .= ";"; + + /* execute */ + try { $database->executeMultipleQuerries( $query ); } + catch (Exception $e) { + die("
".$e->getMessage()."
"); + } + # return + return true; +} + + + + +?> \ No newline at end of file diff --git a/api/v1/functions/functions-common.php b/api/v1/functions/functions-common.php new file mode 100644 index 000000000..f5a4b657e --- /dev/null +++ b/api/v1/functions/functions-common.php @@ -0,0 +1,1924 @@ +0) { + foreach($get as $g) { + if(preg_match('/[^A-Za-z0-9_.#\\-$]/', $g)) { + # permit for search + if($get['section']!="search") { + header("Location:".create_link("error","406")); + } } } } +} + + +/** + * create URL + */ +function createURL () +{ + # reset url for base + if($_SERVER['SERVER_PORT'] == "443") { $url = "https://$_SERVER[HTTP_HOST]".BASE; } + // reverse proxy doing SSL offloading + elseif(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') { $url = "https://$_SERVER[SERVER_NAME]".BASE; } + elseif(isset($_SERVER['HTTP_X_SECURE_REQUEST']) && $_SERVER['HTTP_X_SECURE_REQUEST'] == 'true') { $url = "https://$_SERVER[SERVER_NAME]".BASE; } + // custom port + elseif($_SERVER['SERVER_PORT']!="80") { $url = "http://$_SERVER[HTTP_HOST]:$_SERVER[SERVER_PORT]".BASE; } + // normal http + else { $url = "http://$_SERVER[HTTP_HOST]".BASE; } + + //result + return $url; +} + + + + +/** + * protect against injections + * + * sql protects against SQL injections (mysql_escape_string) + * xss protects agains XSS injections (strip_tags) + * action sets permitted actions! + */ +function filter_user_input ($input, $sql = true, $xss = true, $actions = false) +{ + # XSS + if($xss) { + + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = strip_tags($v); } + } + else { + $input = strip_tags($input); + } + } + + # sql? + if($sql) { + global $database; + + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = $database->real_escape_string($v); } + } + else { + $input = $database->real_escape_string($input); + } + } + + # actions + if($actions) { + $permitted = array("add", "edit", "delete", "truncate", "split", "resize", "move"); + if(!in_array($input, $permitted)) { + die("
Invalid action!
"); + } + } + + + return $input; +} + + +/** + * Trim user input + */ +function trim_user_input($input) +{ + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = trim($v); } + } + else { + $input = $database->trim($input); + } + + return $input; +} + + + + + + +/* @user based functions ---------- */ + + + + +/** + * reset inactivity time + */ +function reset_inactivity_time() +{ + $_SESSION['lastactive'] = time(); +} + + +/** + * Functions to check if user is authenticated properly for ajax-loaded pages + * + */ +function isUserAuthenticated($die = true) +{ + /* open session and get username / pass */ + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + /* redirect if not authenticated */ + if (empty($_SESSION['ipamusername'])) { + # save requested page + $_SESSION['phpipamredirect'] = $_SERVER['HTTP_REFERER']; //here we need referrer + + $url = createURL (); + # die + if($die) { die(''); } + else { die("
"._('Error')."
"._('Please login first')."!
"._('Login').""); } + } + + /* close session */ + session_write_close(); +} + + +/** + * Functions to check if user is authenticated properly + * + * If not redirect to login! + */ +function isUserAuthenticatedNoAjax () +{ + /* open session and get username / pass */ + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + /* redirect if not authenticated */ + if (empty($_SESSION['ipamusername'])) { + # save requested page + $_SESSION['phpipamredirect'] = $_SERVER['SCRIPT_URI']; + + $url = createURL (); + # redirect + header("Location:".$url.create_link("login","timeout")); + } + else { + if($_GET['page']!="login" && $_GET['page']!="request_ip" && $_GET['page']!="upgrade" && $_GET['page']!="install") { + global $settings; + /* check inactivity time */ + if( strlen($settings['inactivityTimeout']>0) && (time()-$_SESSION['lastactive']) > $settings['inactivityTimeout']) { + # redirect + $url = createURL (); + header("Location:".$url.create_link("login","timeout")); + } + } + reset_inactivity_time(); + } + + /* close session */ + session_write_close(); +} + + +/** + * Check if user is admin + */ +function checkAdmin ($die = true) +{ + /* first get active username */ + if(!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + $ipamusername = $_SESSION['ipamusername']; + session_write_close(); + + /* set check query and get result */ + global $database; + + /* Check connection */ + if ($database->connect_error) { + if($_SERVER['SERVER_PORT'] == "443") { $url = "https://".$_SERVER['HTTP_HOST'].BASE; } + elseif($_SERVER['SERVER_PORT']!="80") { $url = "http://".$_SERVER['HTTP_HOST'].":".$_SERVER['SERVER_PORT'].BASE; } + else { $url = "http://".$_SERVER['HTTP_HOST'].BASE; } + # redirect + header("Location:".$url.create_link("login")); + } + + /* set query if database exists! */ + $query = 'select role from users where `username` = "'. $ipamusername .'";'; + + /* fetch role */ + try { $role = $database->getRow( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + die ("
"._('Error').": $error
"); + } + + + /* return true if admin, else false */ + if ($role[0] == "Administrator") { + return true; + } + else { + //die + if($die == true) { die('
'._('Administrator level privileges required').'!
'); } + //return false if called + else { return false; } + } + +} + + +/** + * Get active users username - from session! + */ +function getActiveUserDetails () +{ + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + + if(isset($_SESSION['ipamusername'])) { + return getUserDetailsByName ($_SESSION['ipamusername']); + } + session_write_close(); +} + + +/** + * Get all users + */ +function getAllUsers () +{ + global $database; + + /* set query, open db connection and fetch results */ + $query = 'select * from users order by `role` asc, `real_name` asc;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return results */ + return($details); +} + + +/** + * Get number of users + */ +function getNumberOfUsers () +{ + global $database; + /* set query, open db connection and fetch results */ + $query = 'select count(*) as count from users;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return results */ + return($details[0]['count']); +} + + +/** + * Get all admin users + */ +function getAllAdminUsers () +{ + global $database; + + /* check for possible errors because of cron */ + if(isset($database->error)) { + unset($database); + global $db; + $database = new database($db['host'], $db['user'], $db['pass'], $db['name'], NULL, false); + } + + /* set query, open db connection and fetch results */ + $query = 'select * from users where `role` = "Administrator" order by id desc;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return results */ + return($details); +} + + +/** + * Get user details by ID + */ +function getUserDetailsById ($id) +{ + # check if already in cache + if($user = checkCache("user", $id)) { + return $user; + } + # query + else { + global $database; + /* set query, open db connection and fetch results */ + $query = 'select * from users where id = "'. $id .'";'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + # save cache - id and name + writeCache("user", $id, $details[0]); + writeCache("user", $details[0]['username'], $details[0]); + + /* return results */ + return($details[0]); + } +} + + +/** + * Get user details by name + */ +function getUserDetailsByName ($username, $killsession = true) +{ + # check if already in cache + if($user = checkCache("user", $username)) { + return $user; + } + # query + else { + # for db upgrade! + if(strpos($_SERVER['SCRIPT_URI'], "databaseUpgrade.php")>0) { + global $db; + $database = new database($db['host'], $db['user'], $db['pass'], $db['name']); + } + else { + global $database; + } + /* set query, open db connection and fetch results */ + $query = 'select * from users where `username` = "'. $username .'";'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + # result must be more than 1! + if(!isset($details[0])) { + if($killsession) { + global $phpsessname; + if(strlen($phpsessname)>0) { session_name($phpsessname); } + session_start(); + session_destroy(); + return false; + } + } + else { + # save cache - id and name + writeCache("user", $details[0]['id'], $details[0]); + writeCache("user", $username, $details[0]); + + /* return results */ + return($details[0]); + } + + } +} + +/** + * Get user lang + */ +function getUserLang ($username) +{ + global $database; + /* set query, open db connection and fetch results */ + $query = 'select `lang`,`l_id`,`l_code`,`l_name` from `users` as `u`,`lang` as `l` where `l_id` = `lang` and `username` = "'.$username.'";;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return results */ + return($details[0]); +} + + +/** + * Get all lang + */ +function getLanguages () +{ + global $database; + /* set query, open db connection and fetch results */ + $query = 'select * from `lang`;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return results */ + return($details); +} + + +/** + * Get lang by id + */ +function getLangById ($id) +{ + # check cache + if($vtmp = checkCache("lang", $id)) { + return $vtmp; + } + else { + + global $database; + /* set query, open db connection and fetch results */ + $query = 'select * from `lang` where `l_id` = "'.$id.'";'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + # save cache + writeCache("lang", $id, $details[0]); + /* return results */ + return($details[0]); + } +} + + +/** + * Get all widgets + */ +function getAllWidgets($admin = false, $inactive = false) +{ + global $database; + + # inactive also - only for administration + if($inactive) { $query = "select * from `widgets`; "; + } + else { + # admin? + if($admin) { $query = "select * from `widgets` where `wactive` = 'yes'; "; } + else { $query = "select * from `widgets` where `wadminonly` = 'no' and `wactive` = 'yes'; "; } + } + + /* execute */ + try { $widgets = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* reindex */ + foreach($widgets as $w) { + $wout[$w['wfile']] = $w; + } + + /* return results */ + return $wout; +} + + +/** + * Get widget by id + */ +function getWidgetById($wid) +{ + global $database; + # query + $query = "select * from `widgets` where `wid` = '$wid'; "; + + /* execute */ + try { $widget = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return results */ + return $widget[0]; +} + + +/** + * Get widget by filename + */ +function getWidgetByFile($wfile) +{ + global $database; + # query + $query = "select * from `widgets` where `wfile` = '$wfile'; "; + + /* execute */ + try { $widget = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return results */ + return $widget[0]; +} + + +/** + * Verify widget + */ +function verifyWidget ($file) +{ + //verify that proper files exist + if(!file_exists("app/dashboard/widgets/$file.php")) { return false; } + else { return true; } +} + + +/** + * get user favourite subnets + */ +function getFavouriteSubnets() +{ + # get user details + $user = getActiveUserDetails(); + + # none + if(strlen($user['favourite_subnets'])==0) { + return false; + } + # ok + else { + //store to array + $favs = explode(";", $user['favourite_subnets']); + $favs = array_filter($favs); + //fetch details + $subnets = getUserFavouriteSubnets($favs); + + return $subnets; + } + +} + + +/** + * get user favourite subnets + */ +function getUserFavouriteSubnets($subnetIds) +{ + global $database; + + # get details for each id + foreach($subnetIds as $id) { + $query = "select `su`.`id` as `subnetId`,`se`.`id` as `sectionId`, `subnet`, `mask`,`su`.`description`,`se`.`description` as `section`, `vlanId`, `isFolder` + from `subnets` as `su`, `sections` as `se` where `su`.`id` = $id and `su`.`sectionId` = `se`.`id` limit 1;"; + + /* execute */ + try { $sdetails = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + # out array + $subnets[] = $sdetails[0]; + } + + //return result + return $subnets; +} + + +/** + * check if subnet is favourited + */ +function isSubnetFavourite($subnetId) +{ + # get user details + $user = getActiveUserDetails(); + + # none + if(strlen($user['favourite_subnets'])==0) { + return false; + } + # check + else { + //store to array + $favs = explode(";", $user['favourite_subnets']); + //check + if(in_array($subnetId, $favs)) { + return true; + } else { + return false; + } + } +} + + +/** + * edit favourite + */ +function editFavourite($post) +{ + global $database; + + # get user details and favourites + $user = getActiveUserDetails(); + # empty + $old = explode(";", $user['favourite_subnets']); + + # set query + if($post['action'] == "remove") { + $new = implode(";", array_diff($old, array($post['subnetId']))); + $query = "update `users` set `favourite_subnets` = '$new' where `id` = '$user[id]' limit 1;"; + } elseif($post['action'] == "add") { + if(!is_array($old)) { $old = array(); } + $new = implode(";",array_merge(array($post['subnetId']), $old)); + $query = "update `users` set `favourite_subnets` = '$new' where `id` = '$user[id]' limit 1;"; + } else { + return false; + } + + # execute + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + return true; +} + + + +/** + * Verify translation + */ +function verifyTranslation ($code) +{ + //verify that proper files exist + if(!file_exists("functions/locale/$code/LC_MESSAGES/phpipam.mo")) { return false; } + else { return true; } +} + + +/** + * Verify translation version + */ +function getTranslationVersion ($code) +{ + //check for version + $ver = shell_exec("grep 'Project-Id-Version:' ".dirname(__FILE__)."/locale/$code/LC_MESSAGES/phpipam.po"); + //parse + $ver = str_replace(array("Project-Id-Version:", " ", '"', "#",'\n', ":"), "", $ver); + //return version + return $ver; +} + + +/** + * Get full field data, including comments + */ +function getFullFieldData($table, $field) +{ + global $database; + + /* escape vars to prevent SQL injection */ + $table = filter_user_input ($table, true, true); + $field = filter_user_input ($field, true, true); + + /* set query, open db connection and fetch results */ + $query = "show full columns from `$table` where `Field` = '$field';"; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + //print ("
"._('Error').": $error
"); + return false; + } + + /* return results */ + return($details[0]); +} + + + + + + + + + + +/* @permission functions ---------- */ + +/** + * Check section permissions + */ +function checkSectionPermission ($sectionId) +{ + # open session and get username / pass + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + # redirect if not authenticated */ + if (empty($_SESSION['ipamusername'])) { return "0"; } + else { $username = $_SESSION['ipamusername']; } + + # get all user groups + global $userDetails; + if(!isset($userDetails)) { $user = getUserDetailsByName ($username); } + else { $user = $userDetails; } + $groups = json_decode($user['groups']); + + # if user is admin then return 3, otherwise check + if($user['role'] == "Administrator") { return "3"; } + + # get section permissions + $section = getSectionDetailsById($sectionId); + $sectionP = json_decode($section['permissions']); + + # default permission + $out = 0; + + # for each group check permissions, save highest to $out + if(sizeof($sectionP)>0) { + foreach($sectionP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + if(sizeof($groups)>0) { + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + } + # return permission level + return $out; +} + + +/** + * Check subnet permissions + */ +function checkSubnetPermission ($subnetId) +{ + # open session and get username / pass + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + # redirect if not authenticated */ + if (empty($_SESSION['ipamusername'])) { return "0"; } + else { $username = $_SESSION['ipamusername']; } + + # get all user groups + global $userDetails; + if(!isset($userDetails)) { $user = getUserDetailsByName ($username); } + else { $user = $userDetails; } + $groups = json_decode($user['groups']); + + # if user is admin then return 3, otherwise check + if($user['role'] == "Administrator") { return "3"; } + + # get subnet permissions + $subnet = getSubnetDetailsById($subnetId); + $subnetP = json_decode($subnet['permissions']); + + # get section permissions + $section = getSectionDetailsById($subnet['sectionId']); + $sectionP = json_decode($section['permissions']); + + # default permission + $out = 0; + + # for each group check permissions, save highest to $out + if(sizeof($sectionP) > 0) { + foreach($sectionP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + else { + $out = "0"; + } + + # if section permission == 0 then return 0 + if($out == "0") { + return "0"; + } + else { + $out = "0"; + # ok, user has section access, check also for any higher access from subnet + if(sizeof($subnetP) > 0) { + foreach($subnetP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + } + + # return result + return $out; +} + + + + + + + + +/* @general functions ---------- */ + + +/** + * Get all site settings + */ +function getAllSettings() +{ + global $settings; + # check if it already exists + if(isset($settings)) { + if(isset($settings[0])) { return $settings[0]; } + else { return $settings; } + } + else { + + global $db; + global $database; + + /* first check if table settings exists */ + $query = 'SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = "'. $db['name'] .'" AND table_name = "settings";'; + + /* execute */ + try { $count = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return true if it exists */ + if($count[0]['count'] == 1) { + + /* select database */ + $database->selectDatabase($db['name']); + + /* get settings */ + $query = 'select * from settings where id = 1'; + $settings = $database->getArray($query); + + /* return settings */ + return($settings[0]); + } + else { + return false; + } + + } +} + + +/** + * Get all mail settings + */ +function getAllMailSettings() +{ + global $db; # get variables from config file + global $database; + + /* first check if table settings exists */ + $query = 'SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = "'. $db['name'] .'" AND table_name = "settingsMail";'; + + /* execute */ + try { $count = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
"._('Error').": $error
"); + return false; + } + + /* return true if it exists */ + if($count[0]['count'] == 1) { + + /* select database */ + $database->selectDatabase($db['name']); + + /* first update request */ + $query = 'select * from `settingsMail` where id = 1'; + $settings = $database->getArray($query); + + /* return settings */ + return($settings[0]); + } + else { + return false; + } +} + + +/** + * validate email + */ +function checkEmail($email) { + if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/",$email)) { return false; } + else { return true; } +} + + +/** + * validate hostname + */ +function validateHostname($hostname) +{ + return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $hostname) //valid chars check + && preg_match("/^.{1,253}$/", $hostname) //overall length check + && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $hostname) ); //length of each label +} + + +/** + * Shorten text + */ +function ShortenText($text, $chars = 25) { + //count input text size + $startLen = strlen($text); + //cut onwanted chars + $text = substr($text,0,$chars); + //count output text size + $endLen = strlen($text); + + //append dots if it was cut + if($endLen != $startLen) { + $text = $text."..."; + } + + return $text; +} + + +/** + * Parse section/subnet permissions + */ +function parsePermissions($perm) +{ + switch($perm) { + case "0": $r = _("No access"); break; + case "1": $r = _("Read"); break; + case "2": $r = _("Read / Write"); break; + case "3": $r = _("Read / Write / Admin"); break; + default: $r = _("error"); + } + return $r; +} + + +/** + * secunds to hms + */ +function sec2hms($sec, $padHours = false) + { + // holds formatted string + $hms = ""; + + // get the number of hours + $hours = intval(intval($sec) / 3600); + + // add to $hms, with a leading 0 if asked for + $hms .= ($padHours) + ? str_pad($hours, 2, "0", STR_PAD_LEFT). ':' + : $hours. ':'; + + // get the seconds + $minutes = intval(($sec / 60) % 60); + + // then add to $hms (with a leading 0 if needed) + $hms .= str_pad($minutes, 2, "0", STR_PAD_LEFT). ':'; + + // seconds + $seconds = intval($sec % 60); + + // add to $hms, again with a leading 0 if needed + $hms .= str_pad($seconds, 2, "0", STR_PAD_LEFT); + + // return hms + return $hms; +} + + +/** + * get php exec path + */ +function getPHPExecutableFromPath() +{ + /* + not used anymore as it is not reliable, using PHP_BINDIR instead + */ + $paths = explode(PATH_SEPARATOR, getenv('PATH')); + foreach ($paths as $path) { + // we need this for XAMPP (Windows) + if (strstr($path, 'php.exe') && isset($_SERVER["WINDIR"]) && file_exists($path) && is_file($path)) { + return $path; + } + } + + //unix + $php_executable = PHP_BINDIR."/php"; + if (file_exists($php_executable) && is_file($php_executable)) { + return $php_executable; + } + + return FALSE; // not found +} + + + + + + + + + + +/* @menu builder */ + +/** + * Build the HTML menu + * + * based on http://pastebin.com/GAFvSew4 + */ +function get_menu_html( $subnets, $rootId = 0 ) +{ + $html = array(); + + foreach ( $subnets as $item ) + $children[$item['masterSubnetId']][] = $item; + + # loop will be false if the root has no children (i.e., an empty menu!) + $loop = !empty( $children[$rootId] ); + + # initializing $parent as the root + $parent = $rootId; + $parent_stack = array(); + + # verify subnetId + if(isset($_GET['subnetId'])) { + if(!is_numeric($_GET['subnetId'])) { die('
'._("Invalid ID").'
'); } + } + + # display selected subnet as opened + if(isset($_GET['subnetId'])) { $allParents = getAllParents ($_GET['subnetId']); } + else { $allParents = array(); } + + # Menu start + $html[] = '
    '; + + while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $rootId ) ) ) + { + # count levels + $count = count( $parent_stack ) + 1; + + # set opened or closed tag for displaying proper folders + if(in_array($option['value']['id'], $allParents)) { $open = "open"; $openf = "-open"; } + else { $open = "close"; $openf = ""; } + + # show also child's by default + if($option['value']['id']==$_GET['subnetId']) { + if(subnetContainsSlaves($_GET['subnetId'])) { $open = "open"; $openf = "-open"; } + else { $open = "close"; $openf = ""; } + } + + # override if cookie is set + if(isset($_COOKIE['expandfolders'])) { + if($_COOKIE['expandfolders'] == "1") { $open='open'; $openf = "-open"; } + } + + # for active class + if($_GET['page']=="subnets" && ($option['value']['id'] == $_GET['subnetId'])) { $active = "active"; $leafClass=""; } + else { $active = ""; $leafClass="icon-gray" ;} + + # override folder + if($option['value']['isFolder'] == 1 && ($option['value']['id'] == $_GET['subnetId'])) { $open = "open"; $openf = "-open"; $active = "active"; } + + # check for permissions if id is provided + if($option['value']['id'] != "") { + $sp = checkSubnetPermission ($option['value']['id']); + } + + if ( $option === false ) + { + $parent = array_pop( $parent_stack ); + + # HTML for menu item containing childrens (close) + $html[] = '
'; + $html[] = ''; + } + # Has children + elseif ( !empty( $children[$option['value']['id']] ) ) + { + # if user has access permission + if($sp != 0) { + # folder + if($option['value']['isFolder'] == 1) { + $html[] = '
  • '; + $html[] = ''.$option['value']['description'].''; + } + # print name + elseif($option['value']['showName'] == 1) { + $html[] = '
  • '; + $html[] = ''.$option['value']['description'].''; + } + # print subnet + else { + $html[] = '
  • '; + $html[] = ''.Transform2long($option['value']['subnet']).'/'.$option['value']['mask'].''; + } + + # print submenu + if($open == "open") { $html[] = '
  • \ No newline at end of file diff --git a/app/admin/languages/edit-result.php b/app/admin/languages/edit-result.php new file mode 100755 index 000000000..57b96eba0 --- /dev/null +++ b/app/admin/languages/edit-result.php @@ -0,0 +1,34 @@ +check_user_session(); + + +# verify that description is present if action != delete +if($_POST['action'] != "delete" && strlen($_POST['l_code']) < 2) { $Result->show("danger", _('Code must be at least 2 characters long'), true); } +if($_POST['action'] != "delete" && strlen($_POST['l_name']) < 2) { $Result->show("danger", _('Name must be at least 2 characters long'), true); } + +# create update array +$values = array("l_id"=>@$_POST['l_id'], + "l_code"=>$_POST['l_code'], + "l_name"=>$_POST['l_name'] + ); + +# update +if(!$Admin->object_modify("lang", $_POST['action'], "l_id", $values)) { $Result->show("danger", _("Language $_POST[action] error"), true); } +else { $Result->show("success", _("Language $_POST[action] success"), true); } +?> \ No newline at end of file diff --git a/app/admin/languages/edit.php b/app/admin/languages/edit.php new file mode 100755 index 000000000..31ddfe312 --- /dev/null +++ b/app/admin/languages/edit.php @@ -0,0 +1,72 @@ +check_user_session(); + +# get lang details +if($_POST['action']=="edit" || $_POST['action']=="delete") +$lang = (array) $Admin->fetch_object ("lang", "l_id", $_POST['langid']); + +# set title +if($_POST['action'] == "edit") { $title = 'Edit language'; } +elseif($_POST['action'] == "delete") { $title = 'Delete language'; } +else { $title = 'Add new language'; } +?> + + +
    + + +
    + +
    + + + + + + + + + + + + + + +
    >
    + > + + + +
    +
    + +
    + + + + + +
    +
    + + +
    + + +
    +
    diff --git a/app/admin/languages/index.php b/app/admin/languages/index.php new file mode 100755 index 000000000..14ecdb902 --- /dev/null +++ b/app/admin/languages/index.php @@ -0,0 +1,80 @@ +check_user_session(); + +# fetch all APIs +$languages = $Admin->fetch_all_objects("lang", "l_id"); +?> + + +

    +
    +"._('You can edit different language translations here')."

    "; ?> + + + + + + + + + + + "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # print + foreach($languages as $lang) { + //cast + $lang = (array) $lang; + + # verify validity + $valid = $Tools->verify_translation($lang['l_code']); + # check version + $tversion = $valid===true ? $Tools->get_translation_version ($lang['l_code']) : "NA"; + + # set valid text + if($valid) { $vPrint = ""._('Valid').""; } + else { $vPrint = ""._('Invalid').""; } + + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + } + } + ?> + + +
    "._('Language code').""._('Language name').""._('Validity').""._('Version')."
    $lang[l_code]$lang[l_name]$vPrint$tversion"; + print "
    "; + print " "; + print " "; + print "
    "; + print "
    + +
    +
    + :
    +
      +
    1. +
    2. +
    +
    diff --git a/app/admin/mail/edit.php b/app/admin/mail/edit.php new file mode 100755 index 000000000..4875f4e59 --- /dev/null +++ b/app/admin/mail/edit.php @@ -0,0 +1,35 @@ +check_user_session(); + +# set update query +$values = array("id"=>1, + "mtype"=>$_POST['mtype'], + "msecure"=>@$_POST['msecure'], + "mauth"=>@$_POST['mauth'], + "mserver"=>@$_POST['mserver'], + "mport"=>@$_POST['mport'], + "muser"=>@$_POST['muser'], + "mpass"=>@$_POST['mpass'], + "mAdminName"=>@$_POST['mAdminName'], + "mAdminMail"=>@$_POST['mAdminMail'] + ); + +# update +if(!$Admin->object_modify("settingsMail", "edit", "id", $values)) { $Result->show("danger", _('Cannot update settings').'!', true); } +else { $Result->show("success", _('Settings updated successfully')."!", true); } +?> \ No newline at end of file diff --git a/app/admin/mail/index.php b/app/admin/mail/index.php new file mode 100755 index 000000000..a1d63c7cf --- /dev/null +++ b/app/admin/mail/index.php @@ -0,0 +1,155 @@ +check_user_session(); + +# fetch mail settings +$mail_settings = $Admin->fetch_object("settingsMail", "id", 1); +?> + + +

    phpIPAM

    +
    + +
    + + + + + + + + + + + + + + + mtype=="localhost") print "style='display:none;'"; ?>> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + + +

    + + +
    + + + (25, 465 or 587)
    + + +
    + + +
    + + +
    + + +

    + + + +
    + + + +
    +
    + + +
    +
    +
    + + + +
    \ No newline at end of file diff --git a/app/admin/mail/test-mail.php b/app/admin/mail/test-mail.php new file mode 100755 index 000000000..9c61fce2e --- /dev/null +++ b/app/admin/mail/test-mail.php @@ -0,0 +1,56 @@ +check_user_session(); + +# fetch mailer settings +$mail_settings = $Admin->fetch_object("settingsMail", "id", 1); + + + +# initialize mailer +$phpipam_mail = new phpipam_mail($User->settings, $mail_settings); +//override settings +$phpipam_mail->override_settings($_POST); +//create object +$phpipam_mail->initialize_mailer(); + + +# set content +$content = $phpipam_mail->generate_message ("phpIPAM test HTML message"); +$content_plain = "phpIPAM test text message"; + + +# try to send +try { + $phpipam_mail->Php_mailer->setFrom($_POST['mAdminMail'], $_POST['mAdminName']); + $phpipam_mail->Php_mailer->addAddress($User->settings->siteAdminMail, $User->settings->siteAdminName); + $phpipam_mail->Php_mailer->Subject = 'phpIPAM localhost mail test'; + $phpipam_mail->Php_mailer->msgHTML($content); + $phpipam_mail->Php_mailer->AltBody = $content_plain; + //send + $phpipam_mail->Php_mailer->send(); +} catch (phpmailerException $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); +} catch (Exception $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); +} + +//if error not sent print ok +$Result->show("success alert-absolute", "Message sent to site admin (".$User->settings->siteAdminMail.")!", true); +?> \ No newline at end of file diff --git a/app/admin/replace-fields/index.php b/app/admin/replace-fields/index.php new file mode 100755 index 000000000..4613b56d2 --- /dev/null +++ b/app/admin/replace-fields/index.php @@ -0,0 +1,67 @@ +check_user_session(); +?> + +

    +

    + +
    + + + + + + + + + + + + + + + + + + + + + + +
    : + +
    + +
    + +
    + +
    +
    + + + +
    \ No newline at end of file diff --git a/app/admin/replace-fields/result.php b/app/admin/replace-fields/result.php new file mode 100755 index 000000000..9ad23ec0a --- /dev/null +++ b/app/admin/replace-fields/result.php @@ -0,0 +1,34 @@ +check_user_session(); + + +//verify post +if(empty($_POST['search'])) { $Result->show("danger", _('Please enter something in search field').'!', true); } +//if device verify that it exists +if($_POST['field'] == "switch") { + if(!$device1 = $Admin->fetch_object("devices", "hostname", $_POST['search'])) { $Result->show("danger alert-absolute", _('Switch').' "'. $_POST['search'] .'" '._('does not exist, first create switch under admin menu').'!', true); } + if(!$device2 = $Admin->fetch_object("devices", "hostname", $_POST['replace'])) { $Result->show("danger alert-absolute", _('Switch').' "'. $_POST['search'] .'" '._('does not exist, first create switch under admin menu').'!', true); } + + //replace posts + $_POST['search'] = $device1->id; + $_POST['replace'] = $device2->id; +} + +# update +$Admin->replace_fields ($_POST['field'], $_POST['search'], $_POST['replace']); +?> \ No newline at end of file diff --git a/app/admin/requests/edit-result.php b/app/admin/requests/edit-result.php new file mode 100755 index 000000000..376b86284 --- /dev/null +++ b/app/admin/requests/edit-result.php @@ -0,0 +1,149 @@ +check_user_session(); + + +# fetch custom fields +$custom = $Tools->fetch_custom_fields('ipaddresses'); +if(sizeof($custom) > 0) { + foreach($custom as $myField) { + if(isset($_POST[$myField['name']])) { $_POST[$myField['name']] = $_POST[$myField['name']];} + } +} + +# fetch subnet +$subnet = (array) $Admin->fetch_object("subnets", "id", $_POST['subnetId']); + +/* if action is reject set processed and accepted to 1 and 0 */ +if($_POST['action'] == "reject") { + //set reject values + $values = array("id"=>$_POST['requestId'], + "processed"=>1, + "accepted"=>0, + "adminComment"=>@$_POST['adminComment'] + ); + if(!$Admin->object_modify("requests", "edit", "id", $values)) { $Result->show("danger", _("Failed to reject IP request"), true); } + else { $Result->show("success", _("Request has beed rejected"), false); } +} +/* accept */ +else { + // fetch subnet + $subnet_temp = $Addresses->transform_to_dotted ($subnet['subnet'])."/".$subnet['mask']; + + //verify IP and subnet + $Addresses->verify_address( $Addresses->transform_address($_POST['ip_addr'], "dotted"), $subnet_temp, false, true); + + //check if already existing and die + if ($Addresses->address_exists($Addresses->transform_address($_POST['ip_addr'], "decimal"), $subnet['id'])) { $Result->show("danger", _('IP address already exists'), true); } + + //insert to ipaddresses table + $values = array("action"=>"add", + "ip_addr"=>$Addresses->transform_address($_POST['ip_addr'],"decimal"), + "subnetId"=>$_POST['subnetId'], + "description"=>@$_POST['description'], + "dns_name"=>@$_POST['dns_name'], + "mac"=>@$_POST['mac'], + "owner"=>@$_POST['owner'], + "state"=>@$_POST['state'], + "switch"=>@$_POST['switch'], + "port"=>@$_POST['port'], + "note"=>@$_POST['note'] + ); + if(!$Addresses->modify_address($values)) { $Result->show("danger", _("Failed to create IP address"), true); } + + //accept message + $values = array("id"=>$_POST['requestId'], + "processed"=>1, + "accepted"=>1, + "adminComment"=>$comment + ); + if(!$Admin->object_modify("requests", "edit", "id", $values)) { $Result->show("danger", _("Cannot confirm IP address"), true); } + else { $Result->show("success", _("IP request accepted/rejected"), false); } +} + + +/* send email, all is ok */ + + +# fetch mailer settings +$mail_settings = $Admin->fetch_object("settingsMail", "id", 1); + +# initialize mailer +require( dirname(__FILE__) . '/../../../functions/classes/class.Mail.php'); +$phpipam_mail = new phpipam_mail($User->settings, $mail_settings); +//create object +$phpipam_mail->initialize_mailer(); + + +# subject +if($_POST['action'] == "accept") { $subject = _("IP address request")." $_POST[ip_addr] "._("$_POST[action]ed"); } +else { $subject = _("IP address request $_POST[action]ed"); } + +# set HTML content +$content[] = ""; +$content[] = ""; +$content[] = '' . "\n"; +if($_POST['action'] == "accept") +$content[] = '' . "\n"; +$content[] = '' . "\n"; +$content[] = '' . "\n"; +$content[] = '' . "\n"; +$content[] = '' . "\n"; +$content[] = '' . "\n"; +$content[] = '' . "\n"; +$content[] = ""; +$content[] = "
    $subject
    • '._('Subnet').' '. $Addresses->transform_to_dotted($subnet['subnet'])."/".$subnet['mask'] .'
    • '._('assigned IP address').' '. $Addresses->transform_address($_POST['ip_addr'], "dotted") .'
    • '._('Description').' '. @$_POST['description'] .'
    • '._('Hostname').' '. @$_POST['dns_name'] .'
    • '._('Owner').' '. @$_POST['owner'] .'
    • '._('Requested from').' '. @$_POST['requester'] .'
    • '._('Comment (request)').' '. @$_POST['comment'] .'
    • '._('Admin accept/reject comment').' '. @$_POST['adminComment'] .'
    "._('Sent by user')." ".$User->user->real_name." at ".date('Y/m/d H:i')."
    "; + +# alt content +$content_plain[] = "$subject"."\r\n------------------------------"; +$content_plain[] = _("Subnet").": ".$Addresses->transform_to_dotted($subnet['subnet'])."/".$subnet['mask'] ; +if($_POST['action'] == "accept") +$content_plain[] = _("Assigned IP address").": ".$Addresses->transform_address($_POST['ip_addr'], "dotted"); +$content_plain[] = _("Description").": $_POST[description]"; +$content_plain[] = _("Hostname").": $_POST[dns_name]"; +$content_plain[] = _("Owner").": $_POST[owner]"; +$content_plain[] = _("Requested by").": $_POST[requester]"; +$content_plain[] = _("Comment (request)").": ".str_replace("
    ", "\r\n", $_POST['comment']).""; +$content_plain[] = _("Admin accept/reject comment").": ".str_replace("
    ", "\r\n", $_POST['adminComment']); + + +# get mail content +$content = $phpipam_mail->generate_message (implode("\n", $content)); +$content_plain = $phpipam_mail->generate_message_plain (implode("\r\n", $content_plain)); + + +# try to send +try { + $phpipam_mail->Php_mailer->setFrom($User->user->email, $User->user->real_name); + $phpipam_mail->Php_mailer->addAddress($_POST['requester']); + $phpipam_mail->Php_mailer->Subject = $subject; + $phpipam_mail->Php_mailer->msgHTML($content); + $phpipam_mail->Php_mailer->AltBody = $content_plain; + //send + $phpipam_mail->Php_mailer->send(); +} catch (phpmailerException $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); +} catch (Exception $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); +} + +//print ok +$Result->show("success", _("Sending mail for IP request succeeded"), true); + +?> \ No newline at end of file diff --git a/app/admin/requests/edit.php b/app/admin/requests/edit.php new file mode 100755 index 000000000..50f1ac092 --- /dev/null +++ b/app/admin/requests/edit.php @@ -0,0 +1,280 @@ +check_user_session(); + + +# fetch request +$request = (array) $Admin->fetch_object("requests", "id", $_POST['requestId']); + +//fail +if(@$request[0]===false) { $Result->show("danger", _("Request does not exist"), true, true); } + + +# set selected address fields array +$selected_ip_fields = explode(";", $User->settings->IPfilter); +# fetch custom fields +$custom_fields = $Tools->fetch_custom_fields('ipaddresses'); +?> + + +
    + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0) { + # count datepickers + $timeP = 0; + + # all my fields + foreach($custom_fields as $myField) { + # replace spaces with | + $myField['nameNew'] = str_replace(" ", "___", $myField['name']); + + # required + if($myField['Null']=="NO") { $required = "*"; } + else { $required = ""; } + + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + } + ?> + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    + +
    + +
    + +
    + +
    / + + + / + +
    + +
    '. $myField['name'] .' '.$required.''. "\n"; + + //set type + if(substr($myField['type'], 0,3) == "set") { + //parse values + $tmp = explode(",", str_replace(array("set(", ")", "'"), "", $myField['type'])); + //null + if($myField['Null']!="NO") { array_unshift($tmp, ""); } + + print ""; + } + //date and time picker + elseif($myField['type'] == "date" || $myField['type'] == "datetime") { + // just for first + if($timeP==0) { + print ''; + print ''; + print ''; + } + $timeP++; + + //set size + if($myField['type'] == "date") { $size = 10; $class='datepicker'; $format = "yyyy-MM-dd"; } + else { $size = 19; $class='datetimepicker'; $format = "yyyy-MM-dd"; } + + //field + if(!isset($details[$myField['name']])) { print ' '. "\n"; } + else { print ' '. "\n"; } + } + //boolean + elseif($myField['type'] == "tinyint(1)") { + print ""; + } + //text + elseif($myField['type'] == "text") { + print ' '. "\n"; + } + //default - input field + else { + print ' '. "\n"; + } + + print '

    "; ?>
    : + +
    +
    +
    + + +
    +
    + + + +
    + + +
    +
    \ No newline at end of file diff --git a/app/admin/requests/index.php b/app/admin/requests/index.php new file mode 100755 index 000000000..9041f32eb --- /dev/null +++ b/app/admin/requests/index.php @@ -0,0 +1,114 @@ +check_user_session(); + +# fetch all Active requests +$active_requests = $Admin->fetch_multiple_objects ("requests", "processed", 0, "id", false); +$inactive_requests = $Admin->fetch_multiple_objects ("requests", "processed", 1, "id", false); +?> + +

    +

    + +"._('No IP address requests available')."!"; } +else { +?> + + + + + + + + + + + + +$request) { + //cast + $request = (array) $request; + + //get subnet details + $subnet = (array) $Subnets->fetch_subnet (null, $request['subnetId']); + + //valid + if(sizeof($subnet)==0 || @$subnet[0]===false) { + unset($active_requests[$k]); + } + else { + print ''. "\n"; + print " "; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + } +?> + +
    '. $Subnets->transform_to_dotted($subnet['subnet']) .'/'. $subnet['mask'] .' ('. $subnet['description'] .')'. $request['dns_name'] .''. $request['description'] .''. $request['requester'] .''. $request['comment'] .'
    + + +

    +

    + + + + + + + + + + + + + + +$request) { + //cast + $request = (array) $request; + + //get subnet details + $subnet = (array) $Subnets->fetch_subnet (null, $request['subnetId']); + + //valid + if(sizeof($subnet)==0 || @$subnet[0]===false) { + unset($inactive_requests[$k]); + } + else { + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + } +?> + +
    '. $Subnets->transform_to_dotted($subnet['subnet']) .'/'. $subnet['mask'] .' ('. $subnet['description'] .')'. $request['dns_name'] .''. $request['description'] .''. $request['requester'] .''. $request['comment'] .''. $request['adminComment'] .''; + print $request['accepted']==1 ? "Yes" : "No"; + print '
    + + \ No newline at end of file diff --git a/app/admin/ripe-import/import-subnets.php b/app/admin/ripe-import/import-subnets.php new file mode 100755 index 000000000..d71e4a08c --- /dev/null +++ b/app/admin/ripe-import/import-subnets.php @@ -0,0 +1,96 @@ +check_user_session(); + + +//get size of subnets - $_POST/4 +$size = sizeof($_POST) / 4; + +//get unique keys for subnets because they are not sequential if deleted!!! +foreach($_POST as $key=>$line) { + if (strlen(strstr($key,"subnet"))>0) { + $allSubnets[] = $key; + } +} + +# format and verify each record +foreach($allSubnets as $subnet) { + //get sequential number + $m = str_replace("subnet-", "", $subnet); + + //reformat subnet + $_temp = explode("/", $_POST['subnet-' . $m]); + + //set subnet details for importing + $subnet_import['subnet'] = $Subnets->transform_to_decimal($_temp[0]); + $subnet_import['mask'] = $_temp[1]; + $subnet_import['sectionId'] = $_POST['section-' . $m]; + $subnet_import['description'] = $_POST['description-' . $m]; + $subnet_import['vlanId'] = $_POST['vlan-' . $m]; + $subnet_import['vrfId'] = $_POST['vrf-' . $m]; + $subnet_import['showName'] = $_POST['showName-' . $m]; + + //cidr + if(strlen($err=$Subnets->verify_cidr($Subnets->transform_to_dotted($subnet_import['subnet'])."/".$subnet_import['mask']))>5) { + $errors[] = $err; + } + //overlapping, only root ! + else if (strlen($err=$Subnets->verify_subnet_overlapping ($subnet_import['sectionId'], $Subnets->transform_to_dotted($subnet_import['subnet'])."/".$subnet_import['mask'], $subnet_import['vrfId']))>5) { + $errors[] = $err; + } + //set insert + else { + $subnets_to_insert[] = $subnet_import; + } +} + + +# print errors if they exist or success +if(isset($errors)) { + print '
    '._('Please fix the following errors before inserting').':
    '. "\n"; + foreach ($errors as $line) { + print $line.'
    '; + } + print '
    '; +} +else { + $errors_import_failed = 0; + + //insert if all other is ok! + foreach($subnets_to_insert as $subnet_import) { + //formulate insert query + $values = array("sectionId"=>$subnet_import['sectionId'], + "subnet"=>$subnet_import['subnet'], + "mask"=>$subnet_import['mask'], + "description"=>$subnet_import['description'], + "vlanId"=>$subnet_import['vlanId'], + "vrfId"=>$subnet_import['vrfId'], + "masterSubnetId"=>0, + "showName"=>$subnet_import['showName'] + ); + + if(!$Admin->object_modify("subnets", "add", "id", $values)) { + $Result->show("danger", _('Failed to import subnet').' '. $Subnets->transform_to_dotted($subnet_import['subnet'])."/".$subnet_import['mask'], false); + $errors_import_failed++; + } + } + //check if all is ok and print it! + if($errors_import_failed == 0) { $Result->show("success", _("Import successfull")."!", false); } +} +?> \ No newline at end of file diff --git a/app/admin/ripe-import/index.php b/app/admin/ripe-import/index.php new file mode 100755 index 000000000..a47afa784 --- /dev/null +++ b/app/admin/ripe-import/index.php @@ -0,0 +1,29 @@ +check_user_session(); +?> + +

    +

    + + +show("info alert-absolute", _('This script imports subnets from RIPE database for specific AS. Enter desired AS to search for subnets'), false); ?> + +
    +
    + +
    +
    + +
    +
    + + +
    +
    +
    \ No newline at end of file diff --git a/app/admin/ripe-import/ripe-telnet.php b/app/admin/ripe-import/ripe-telnet.php new file mode 100755 index 000000000..09dbd4185 --- /dev/null +++ b/app/admin/ripe-import/ripe-telnet.php @@ -0,0 +1,132 @@ +check_user_session(); + + +//strip AS if provided, to get just the number +if(substr($_POST['as'], 0,2)=="AS" || substr($_POST['as'], 0,2)=="as") { + $_POST['as'] = substr($_POST['as'], 2); +}; + + +# fetch subnets form ripe +$subnet = $Admin->ripe_fetch_subnets ($_POST['as']); + +# fetch all sections +$sections = $Admin->fetch_all_objects ("sections", "id"); +$vlans = $Admin->fetch_all_objects ("vlans", "vlanId"); +$vrfs = $Admin->fetch_all_objects ("vrf", "vrfId"); + +//none found +if(sizeof(@$subnet) == 0) { + print "
    "; + $Result->show("danger alert-absolute", _('No subnets found').'!', true); +} +else { + //form + print '
    '; + //table + print ''; + //headers + print ''; + print ' '; + print ' '; + + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + + print ""; + + //print found subnets + $m = 0; + foreach ($subnet as $route) { + # only not empty + if(strlen($route)>2) { + print ''. "\n"; + + //delete + print ''. "\n"; + //subnet + print ''. "\n"; + //section + print ''. "\n"; + //description + print ''. "\n"; + //VLAN + print ''. "\n"; + + print ''. "\n"; + } + $m++; + } + + //submit + print ''. "\n"; + print ''. "\n"; + print ''. "\n"; + + print '
    '._('I found the following routes belonging to AS').' '.$_POST['as'].':
    "._('Subnet').""._('select section').""._('Description').""._('VLAN').""._('VRF').""._('Show name')."
    '. "\n"; + print ' '. "\n"; + print ''. "\n"; + print ''. "\n"; + print ''. "\n"; + print ''. "\n"; + print ''. "\n"; + print ''. "\n"; + print ''. "\n"; + print ''. "\n"; + print ''. "\n"; + print '
    '. "\n"; + print ' '. "\n"; + print '
    '. "\n"; + print '
    '. "\n"; +} +?> +
    \ No newline at end of file diff --git a/app/admin/sections/edit-order-result.php b/app/admin/sections/edit-order-result.php new file mode 100755 index 000000000..5a595e4e8 --- /dev/null +++ b/app/admin/sections/edit-order-result.php @@ -0,0 +1,31 @@ +check_user_session(); + + +# create array of ordering +$otmp = explode(";", $_POST['position']); +foreach($otmp as $ot) { + $ptmp = explode(":", $ot); + $order[$ptmp[0]] = $ptmp[1]; +} + +#update +if(!$Sections->modify_section ("reorder", $order)) { $Result->show("danger", _("Section reordering failed"), true); } +else { $Result->show("success", _("Section reordering successful"), true); } +?> \ No newline at end of file diff --git a/app/admin/sections/edit-order.php b/app/admin/sections/edit-order.php new file mode 100755 index 000000000..09e50dff1 --- /dev/null +++ b/app/admin/sections/edit-order.php @@ -0,0 +1,69 @@ +check_user_session(); + +# fetch all sections +$sections = $Sections->fetch_all_sections(); +?> + + + + + + +
    + + +
    + + +

    + + +
      + id'> $s->name ( $s->description )"; + } + ?> +
    +
    + + +
    +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/app/admin/sections/edit-result.php b/app/admin/sections/edit-result.php new file mode 100755 index 000000000..f85b68579 --- /dev/null +++ b/app/admin/sections/edit-result.php @@ -0,0 +1,112 @@ +check_user_session(); + + + +# If confirm is not set print delete warning +if ($_POST['action']=="delete" && !isset($_POST['deleteconfirm'])) { + //for ajax to prevent reload + print "
    alert alert-danger
    "; + //result + print "
    "; + + //fetch all subsections + $subsections = $Sections->fetch_subsections ($_POST['id']); + + //print what will be deleted + if(sizeof($subsections)>0) { + $subnets = $Subnets->fetch_section_subnets($_POST['id']); //fetch all subnets in section + $num_subnets = sizeof($subnets); //number of subnets to be deleted + if(sizeof($subnets)>0) { + foreach($subnets as $s) { + $out[] = $s; + } + } + //fetch subsection subnets + foreach($subsections as $ss) { + $subsection_subnets = $Subnets->fetch_section_subnets($ss->id); //fetch all subnets in subsection + if(sizeof($subsection_subnets)>0) { + foreach($subsection_subnets as $sss) { + $out[] = $sss; + } + } + $num_subnets = $num_subnets + sizeof($subsection_subnets); + //count all addresses that will be deleted! + $ipcnt = $Addresses->count_addresses_in_multiple_subnets($out); + } + } + # no subsections + else { + $subnets = $Subnets->fetch_section_subnets ($_POST['id']); //fetch all subnets in section + $num_subnets = sizeof($subnets); + $ipcnt = $Addresses->count_addresses_in_multiple_subnets($subnets); + } + + # printout + print ""._("Warning").": "._("I will delete").":
      "; + print "
    • $num_subnets "._("subnets")."
    • "; + if($ipcnt>0) { + print "
    • $ipcnt "._("IP addresses")."
    • "; + } + print "
    "; + + print "
    "; + print _("Are you sure you want to delete above items?")." "; + print "
    "; + print " "._("Confirm").""; + print "
    "; + print "
    "; + print "
    "; +} +# ok, update section +else { + # set variables for update + $values = array("id"=>@$_POST['id'], + "name"=>@$_POST['name'], + "description"=>@$_POST['description'], + "strictMode"=>@$_POST['strictMode'], + "subnetOrdering"=>@$_POST['subnetOrdering'], + "showVLAN"=>@$_POST['showVLAN'], + "showVRF"=>@$_POST['showVRF'], + "masterSection"=>@$_POST['masterSection'], + ); + + # set permissions + foreach($_POST as $key=>$val) { + if(substr($key, 0,5) == "group") { + if($val != "0") { + $perm[substr($key,5)] = $val; + } + } + } + $values['permissions'] = isset($perm) ? json_encode($perm) : ""; + + # delegate to all subnets? + if(isset($_POST['delegate'])) { + if($_POST['delegate']==1) { $values['delegate']=1; } + else { $values['delegate']=0; } + } + + # execute update + if(!$Sections->modify_section ($_POST['action'], $values, @$_POST['id'])) { $Result->show("danger", _("Section $_POST[action] failed"), true); } + else { $Result->show("success", _("Section $_POST[action] successful"), true); } +} +?> \ No newline at end of file diff --git a/app/admin/sections/edit.php b/app/admin/sections/edit.php new file mode 100755 index 000000000..5962faa26 --- /dev/null +++ b/app/admin/sections/edit.php @@ -0,0 +1,232 @@ +check_user_session(); + +# fetch all sections for master section +$sections = $Sections->fetch_all_sections (); +# fetch groups +$groups = $Admin->fetch_all_objects("userGroups", "g_id"); +# fetch section +$section = (array) $Sections->fetch_section (null, @$_POST['sectionId']); +?> + + +
    + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 ? $Sections->parse_section_permissions($section['permissions']) : ""; + + # print for each group + $m=0; + + if($groups) { + foreach($groups as $g) { + //cast + $g = (array) $g; + # structure + print ""; + # title + if($m == 0) { print ""; } + else { print ""; } + + # name + print ""; + + # line + print ""; + + print ""; + + $m++; + } + } + else { + print ""; + print ""; + print ""; + print ""; + } + ?> + + + + + + + + + + + + +
    + placeholder=""> + + + +
    + placeholder=""> +
    + + +
    + + +
    + + +
    + + +
    + + +
    +
    +
    "._('Permissions')."$g[g_name]"; + print ""; + + print " na"; + if(@$permissions[$g['g_id']]==1) { print " ro"; } + else { print " ro"; } + if(@$permissions[$g['g_id']]==2) { print " rw"; } + else { print " rw"; } + if(@$permissions[$g['g_id']]==3) { print " rwa"; } + else { print " rwa"; } + print ""; + print "
    "._('Permissions')."
    "._('No groups available')."
    +
    +
    +
    + + +
    +
    +
    + + + '._('Warning').'!
    '._('Deleting Section will delete all belonging subnets and IP addresses').'!
    ' . "\n"; + } + ?> + + + + +
    +
    + + +
    + +
    +
    diff --git a/app/admin/sections/index.php b/app/admin/sections/index.php new file mode 100755 index 000000000..58539fae5 --- /dev/null +++ b/app/admin/sections/index.php @@ -0,0 +1,138 @@ +check_user_session(); + +# fetch all sections +$sections = $Sections->fetch_all_sections(); + +# Lets do some reordering to show slaves! +foreach($sections as $s) { + if($s->masterSection=="0") { + # it is master + $s->class = "master"; + $sectionssorted[] = $s; + # check for slaves + foreach($sections as $ss) { + if($ss->masterSection==$s->id) { + $ss->class = "slave"; + $sectionssorted[] = $ss; + } + } + } +} +# set new array +$sections_sorted = @$sectionssorted; +?> + +

    +
    + + +
    + + +
    + + + 0) { ?> + + + + + + + + + + + + + + +'. "\n"; + + print ' '. "\n"; + print ' '. "\n"; + //master Section + if($section['masterSection']!=0) { + # get section details + $ssec = $Admin->fetch_object("sections", "id", $section['masterSection']); + print " "; + } else { + print " "; + } + //strictMode + $mode = $section['strictMode']==0 ? _("No") : _("Yes"); + print ' '. "\n"; + //Show VLANs + print " "; + //Show VRFs + print " "; + //permissions + print ""; + + print ' '. "\n"; + + print ''. "\n";; +} +?> + +
    '. str_replace("_", " ", $section['name']).''. $section['description'] .'$ssec->name/'. $mode .'"; + print @$section['showVLAN']==1 ? _("Yes") : _("No"); + print " "; + print @$section['showVRF']==1 ? _("Yes") : _("No"); + print " "; + if(strlen($section['permissions'])>1 && !is_null($section['permissions'])) { + $permissions = $Sections->parse_section_permissions($section['permissions']); + # print for each if they exist + if(sizeof($permissions) > 0) { + foreach($permissions as $key=>$p) { + # get subnet name + $group = $Tools->fetch_object("userGroups", "g_id", $key); + # parse permissions + $perm = $Subnets->parse_permissions($p); + print $group->g_name." : ".$perm."
    "; + } + } + else { + print _("All groups: No access"); + } + } + else { + print _("All groups: No access"); + } + print "
    '. "\n"; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print '
    + + + +
    !
    + + + + +
    +
    +
      +
    • +
    • +
    • it will have same permission on subnets'); ?>
    • +
    • +
    • subnet permissions are set'); ?>
    • +
    +
    \ No newline at end of file diff --git a/app/admin/settings/index.php b/app/admin/settings/index.php new file mode 100755 index 000000000..e5d038249 --- /dev/null +++ b/app/admin/settings/index.php @@ -0,0 +1,415 @@ + + + + +check_user_session(); + +# fetch all languages +$languages = $Admin->fetch_all_objects("lang", "l_id"); + +# set settings +$settings = (array) $User->settings; +?> + + +

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + + +
    + +
    + +
    + + + ?
    +
      +
    • : ?page=administration&link2=settings
    • +
    • : /administration/settings/
    • +
    +
    + http://phpipam.net/prettified-links-with-mod_rewrite/ +
    + +
    + +
    + + + +

    + + + +
    + + + +

    + > + + +
    + > + + +
    + > + + +
    + > + + +
    + > + + +
    + > + + +

    + +
    + + + +
    + + + +
    + + + +
    + + + +

    + > + + +
    + + + +
    + + + +
    +
    +
    + +
    +
    \ No newline at end of file diff --git a/app/admin/settings/settings-save.php b/app/admin/settings/settings-save.php new file mode 100755 index 000000000..610c751f0 --- /dev/null +++ b/app/admin/settings/settings-save.php @@ -0,0 +1,72 @@ +check_user_session(); + + +//check for http/https +if ( (strpos($_POST['siteURL'],'http://') !== false) || (strpos($_POST['siteURL'],'https://') !== false) ) {} +else { $_POST['siteURL'] = "http://".$_POST['siteURL']; } + +//verify ping status fields +$_POST['pingStatus'] = str_replace(" ", "", $_POST['pingStatus']); //remove possible spaces +$_POST['pingStatus'] = str_replace(",", ";", $_POST['pingStatus']); //change possible , for ; +$statuses = explode(";", $_POST['pingStatus']); + +if(sizeof($statuses)!=2) { $Result->show("danger", _("Invalid ping status intervals"), true); } +if(!is_numeric($statuses[0]) || !is_numeric($statuses[1])) { $Result->show("danger", _("Invalid ping status intervals"), true); } + +//verify email +if(filter_var($_POST['siteAdminMail'], FILTER_VALIDATE_EMAIL) === false) { $Result->show("danger", _("Invalid email"), true); } + +//verify numbers +if(!is_numeric($_POST['vlanMax'])) { $Result->show("danger", _("Invalid value for Max VLAN number"), true); } + +# set update values +$values = array("id"=>1, + //site settings + "siteTitle"=>@$_POST['siteTitle'], + "siteDomain"=>@$_POST['siteDomain'], + "siteURL"=>@$_POST['siteURL'], + "prettyLinks"=>@$_POST['prettyLinks'], + "defaultLang"=>@$_POST['defaultLang'], + "inactivityTimeout"=>@$_POST['inactivityTimeout'], + //admin + "siteAdminName"=>@$_POST['siteAdminName'], + "siteAdminMail"=>@$_POST['siteAdminMail'], + //features + "api"=>$Admin->verify_checkbox(@$_POST['api']), + "enableIPrequests"=>$Admin->verify_checkbox(@$_POST['enableIPrequests']), + "enableVRF"=>$Admin->verify_checkbox(@$_POST['enableVRF']), + "enableDNSresolving"=>$Admin->verify_checkbox(@$_POST['enableDNSresolving']), + "vlanDuplicate"=>$Admin->verify_checkbox(@$_POST['vlanDuplicate']), + "vlanMax"=>@$_POST['vlanMax'], + "enableChangelog"=>$Admin->verify_checkbox(@$_POST['enableChangelog']), + "tempShare"=>$Admin->verify_checkbox(@$_POST['tempShare']), + //display + "donate"=>$Admin->verify_checkbox(@$_POST['donate']), + "visualLimit"=>@$_POST['visualLimit'], + "subnetOrdering"=>@$_POST['subnetOrdering'], + //ping + "scanPingType"=>@$_POST['scanPingType'], + "pingStatus"=>@$_POST['pingStatus'], + "scanPingPath"=>@$_POST['scanPingPath'], + "scanFPingPath"=>@$_POST['scanFPingPath'], + "scanMaxThreads"=>@$_POST['scanMaxThreads'] + ); +if(!$Admin->object_modify("settings", "edit", "id", $values)) { $Result->show("danger", _("Cannot update settings"), true); } +else { $Result->show("success", _("Settings updated successfully"), true); } +?> \ No newline at end of file diff --git a/app/admin/subnets/edit-folder-result.php b/app/admin/subnets/edit-folder-result.php new file mode 100755 index 000000000..bfdb78db5 --- /dev/null +++ b/app/admin/subnets/edit-folder-result.php @@ -0,0 +1,192 @@ +check_user_session(); + +# ID must be numeric +if($_POST['action']=="add") { + if(!is_numeric($_POST['sectionId'])) { $Result->show("danger", _("Invalid ID"), true); } +} else { + if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true); } +} + +# verify that user has permissions to add subnet +if($_POST['action']=="add") { + if($Sections->check_permission ($User->user, $_POST['sectionId']) != 3) { $Result->show("danger", _('You do not have permissions to add new subnet in this section')."!", true, true); } +} +# otherwise check subnet permission +else { + if($Subnets->check_permission ($User->user, $_POST['subnetId']) != 3) { $Result->show("danger", _('You do not have permissions to add edit/delete this subnet')."!", true, true); } +} + +# we need old values for mailing +if($_POST['action']=="edit" || $_POST['action']=="delete") { + $subnet_old_details = (array) $Subnets->fetch_subnet(null, $_POST['subnetId']); +} + +# get section details +$section = (array) $Sections->fetch_section(null, @$_POST['sectionId']); +# fetch custom fields +$custom = $Tools->fetch_custom_fields('subnets'); + +//custom +if(sizeof($custom) > 0) { + foreach($custom as $myField) { + # replace possible ___ back to spaces! + $myField['nameTest'] = str_replace(" ", "___", $myField['name']); + if(isset($_POST[$myField['nameTest']])) { $_POST[$myField['name']] = $_POST[$myField['nameTest']];} + } +} + +//remove subnet-specific fields +unset ($_POST['subnet'],$_POST['allowRequests'],$_POST['showName'],$_POST['pingSubnet'],$_POST['discoverSubnet']); +unset ($subnet_old_details['subnet'],$subnet_old_details['allowRequests'],$subnet_old_details['showName'],$subnet_old_details['pingSubnet'],$subnet_old_details['discoverSubnet']); + + +# Set permissions if adding new subnet +if($_POST['action']=="add") { + # root + if($_POST['masterSubnetId']==0) { + $_POST['permissions'] = $section['permissions']; + } + # nested - inherit parent permissions + else { + # get parent + $parent = $Subnets->fetch_subnet(null, $_POST['masterSubnetId']); + $_POST['permissions'] = $parent->permissions; + } +} + +//check for name length - 2 is minimum! +if(strlen($_POST['description'])<2 && $_POST['action']!="delete") { $Result->show("danger", _('Folder name must have at least 2 characters')."!", true); } +//custom fields +if(sizeof($custom) > 0) { + foreach($custom as $myField) { + //booleans can be only 0 and 1! + if($myField['type']=="tinyint(1)") { + if(@$_POST[$myField['name']]>1) { + $_POST[$myField['name']] = ""; + } + } + //not empty + if($myField['Null']=="NO" && strlen($_POST[$myField['name']])==0) { + $errors[] = "Field \"$myField[name]\" cannot be empty!"; + } + } +} + +# delete and not yet confirmed +if ($_POST['action']=="delete" && !isset($_POST['deleteconfirm'])) { + # for ajax to prevent reload + print "
    alert alert-danger
    "; + # result + print "
    "; + + # print what will be deleted + //fetch all slave subnets + $Subnets->fetch_subnet_slaves_recursive ($_POST['subnetId']); + $subcnt = sizeof($Subnets->slaves); + foreach($Subnets->slaves as $s) { + $slave_array[$s] = $s; + } + $ipcnt = $Addresses->count_addresses_in_multiple_subnets($slave_array); + + print ""._("Warning").": "._("I will delete").":
      "; + print "
    • $subcnt "._("subnets")."
    • "; + if($ipcnt>0) { + print "
    • $ipcnt "._("IP addresses")."
    • "; + } + print "
    "; + + print "
    "; + print _("Are you sure you want to delete above items?")." "; + print "
    "; + print " "._("Confirm").""; + print "
    "; + print "
    "; + print "
    "; +} +# execute +else { + + # create array of default update values + $values = array("id"=>@$_POST['subnetId'], + "isFolder"=>1, + "masterSubnetId"=>$_POST['masterSubnetId'], + "description"=>@$_POST['description'], + ); + # for new subnets we add permissions + if($_POST['action']=="add") { + $values['permissions']=$_POST['permissions']; + $values['sectionId']=$_POST['sectionId']; + } + else { + # if section change + if(@$_POST['sectionId'] != @$_POST['sectionIdNew']) { + $values['sectionId']=$_POST['sectionIdNew']; + } + } + # append custom fields + $custom = $Tools->fetch_custom_fields('subnets'); + if(sizeof($custom) > 0) { + foreach($custom as $myField) { + + //replace possible ___ back to spaces + $myField['nameTest'] = str_replace(" ", "___", $myField['name']); + if(isset($_POST[$myField['nameTest']])) { $_POST[$myField['name']] = $_POST[$myField['nameTest']];} + + //booleans can be only 0 and 1! + if($myField['type']=="tinyint(1)") { + if($_POST[$myField['name']]>1) { + $_POST[$myField['name']] = 0; + } + } + //not null! + if($myField['Null']=="NO" && strlen($_POST[$myField['name']])==0) { $Result->show("danger", $myField['name'].'" can not be empty!', true); } + + # save to update array + $values[$myField['name']] = $_POST[$myField['name']]; + } + } + + # execute + if(!$Admin->object_modify("subnets", $_POST['action'], "id", $values)) { $Result->show("danger", _('Error editing folder'), true); } + else { + # update also all slave subnets! + if(isset($values['sectionId'])&&$_POST['action']!="add") { + $Subnets->reset_subnet_slaves_recursive(); + $Subnets->fetch_subnet_slaves_recursive($_POST['subnetId']); + $Subnets->remove_subnet_slaves_master($_POST['subnetId']); + if(sizeof($Subnets->slaves)>0) { + foreach($Subnets->slaves as $slaveId) { + $Admin->object_modify ("subnets", "edit", "id", array("id"=>$slaveId, "sectionId"=>$_POST['sectionIdNew'])); + } + } + } + # edit success + if($_POST['action']=="delete") { $Result->show("success", _('Folder, IP addresses and all belonging subnets deleted successfully').'!', false); } + else { $Result->show("success", _("Folder $_POST[action] successfull").'!', true); } + + # send mail + # sendObjectUpdateMails("subnet", $_POST['action'], $subnet_old_details, $_POST); + } +} + +?> \ No newline at end of file diff --git a/app/admin/subnets/edit-folder.php b/app/admin/subnets/edit-folder.php new file mode 100755 index 000000000..558e43fd5 --- /dev/null +++ b/app/admin/subnets/edit-folder.php @@ -0,0 +1,247 @@ +check_user_session(); + + +# ID must be numeric +if($_POST['action']!="add") { + if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true, true); } +} + +# verify that user has permissions to add subnet +if($_POST['action'] == "add") { + if($Sections->check_permission ($User->user, $_POST['sectionId']) != 3) { $Result->show("danger", _('You do not have permissions to add new subnet in this section')."!", true, true); } +} +# otherwise check subnet permission +else { + if($Subnets->check_permission ($User->user, $_POST['subnetId']) != 3) { $Result->show("danger", _('You do not have permissions to add edit/delete this subnet')."!", true, true); } +} + + +# we are editing or deleting existing subnet, get old details +if ($_POST['action'] != "add") { + $folder_old_details = (array) $Subnets->fetch_subnet(null, $_POST['subnetId']); +} +# we are adding new folder - get folder details +else { + # for selecting master subnet if added from subnet details! + if(strlen($_POST['subnetId']) > 0) { + $subnet_old_temp = (array) $Subnets->fetch_subnet(null, $_POST['subnetId']); + $subnet_old_details['masterSubnetId'] = @$subnet_old_temp['id']; // same master subnet ID for nested + $subnet_old_details['vlanId'] = @$subnet_old_temp['vlanId']; // same default vlan for nested + $subnet_old_details['vrfId'] = @$subnet_old_temp['vrfId']; // same default vrf for nested + } +} + +# fetch custom fields +$custom_fields = $Tools->fetch_custom_fields('subnets'); +# fetch all sections +$sections = $Sections->fetch_all_sections(); + + +# set readonly flag +$readonly = $_POST['action']=="edit" || $_POST['action']=="delete" ? true : false; +?> + + + + +
    + + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0) { + # count datepickers + $timeP = 0; + + print ""; + print " "; + print ""; + foreach($custom_fields as $field) { + + # replace spaces + $field['nameNew'] = str_replace(" ", "___", $field['name']); + # retain newlines + $folder_old_details[$field['name']] = str_replace("\n", "\\n", @$folder_old_details[$field['name']]); + + # required + if($field['Null']=="NO") { $required = "*"; } + else { $required = ""; } + + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + } + + + # divider + print ""; + print " "; + print ""; + ?> + +
    + +
    + + + +
    + print_mastersubnet_dropdown_menu($_POST['sectionId'], @$folder_old_details['masterSubnetId']); ?> + !

    '. $field['name'] .' '.$required.''. "\n"; + + //set type + if(substr($field['type'], 0,3) == "set") { + //parse values + $tmp = explode(",", str_replace(array("set(", ")", "'"), "", $field['type'])); + //null + if($field['Null']!="NO") { array_unshift($tmp, ""); } + + print ""; + } + //date and time picker + elseif($field['type'] == "date" || $field['type'] == "datetime") { + // just for first + if($timeP==0) { + print ''; + print ''; + print ''; + } + $timeP++; + + //set size + if($field['type'] == "date") { $size = 10; $class='datepicker'; $format = "yyyy-MM-dd"; } + else { $size = 19; $class='datetimepicker'; $format = "yyyy-MM-dd"; } + + //field + if(!isset($folder_old_details[$field['name']])) { print ' '. "\n"; } + else { print ' '. "\n"; } + } + //boolean + elseif($field['type'] == "tinyint(1)") { + print ""; + } + //text + elseif($field['type'] == "text") { + print ' '. "\n"; + } + //default - input field + else { + print ' '. "\n"; + } + + print '

    +
    + + "._('Warning')."
    "._('Removing subnets will delete ALL underlaying subnets and belonging IP addresses')."!
    "; + } + ?> + + + + + + +
    +
    + + "._('Delete folder').""; + } + ?> + +
    + +
    +
    \ No newline at end of file diff --git a/app/admin/subnets/edit-result.php b/app/admin/subnets/edit-result.php new file mode 100755 index 000000000..257c5072d --- /dev/null +++ b/app/admin/subnets/edit-result.php @@ -0,0 +1,315 @@ +check_user_session(); + + +# ID must be numeric +if($_POST['action']=="add") { + if(!is_numeric($_POST['sectionId'])) { $Result->show("danger", _("Invalid ID"), true); } +} else { + if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true); } + if(!is_numeric($_POST['sectionId'])) { $Result->show("danger", _("Invalid ID"), true); } +} +# if show name than description must be set +if(@$_POST['showName']==1 && strlen($_POST['description'])==0) { $Result->show("danger", _("Please enter subnet description to show as name!"), true); } + +# verify that user has permissions to add subnet +if($_POST['action']=="add") { + if($Sections->check_permission ($User->user, $_POST['sectionId']) != 3) { $Result->show("danger", _('You do not have permissions to add new subnet in this section')."!", true, true); } +} +# otherwise check subnet permission +else { + if($Subnets->check_permission ($User->user, $_POST['subnetId']) != 3) { $Result->show("danger", _('You do not have permissions to add edit/delete this subnet')."!", true, true); } +} + +# we need old values for mailing +if($_POST['action']=="edit" || $_POST['action']=="delete") { + $subnet_old_details = (array) $Subnets->fetch_subnet(null, $_POST['subnetId']); +} + +# get mask and subnet +$_POST['mask']=trim(strstr($_POST['subnet'], "/"),"/"); +$_POST['subnet']=strstr($_POST['subnet'], "/",true); +$_POST['id']=$_POST['subnetId']; +//set cidr +$_POST['cidr'] = $_POST['subnet']."/".$_POST['mask']; + + +# get section details +$section = (array) $Sections->fetch_section(null, $_POST['sectionId']); +# fetch custom fields +$custom = $Tools->fetch_custom_fields('subnets'); + +# get master subnet details for folder overrides +if($_POST['masterSubnetId']!=0) { + $master_section = (array) $Subnets->fetch_subnet(null, $_POST['masterSubnetId']); + if($master_section['isFolder']==1) { $parent_is_folder = true; } + else { $parent_is_folder = false; } +} +else { $parent_is_folder = false; } + + +/** + * If request came from IP address subnet edit and + * action2 is Delete then change action + */ +if( (isset($_POST['action2'])) && ($_POST['action2']=="delete") ) { + $_POST['action'] = $_POST['action2']; +} + +/** + * If section changes then do checks! + */ +if ( ($_POST['sectionId'] != @$_POST['sectionIdNew']) && $_POST['action']=="edit" ) { + //reset masterId - we are putting it to root + $_POST['masterSubnetId'] = 0; + + //check for overlapping + if($section['strictMode']==1 && !$parent_is_folder) { + /* verify that no overlapping occurs if we are adding root subnet */ + $overlap=$Subnets->verify_subnet_overlapping ($_POST['sectionIdNew'], $_POST['cidr'], $_POST['vrfId']); + if($overlap!==false) { + $errors[] = $overlap; + } + } +} +/** + * Execute checks on add only and when root subnet is being added + */ +else if (($_POST['action']=="add") && ($_POST['masterSubnetId']==0)) { + //verify cidr + $cidr_check = $Subnets->verify_cidr_address($_POST['cidr']); + if(strlen($cidr_check)>5) { + $errors[] = $cidr_check; + } + //check for overlapping + if($section['strictMode']==1 && !$parent_is_folder) { + /* verify that no overlapping occurs if we are adding root subnet + only check for overlapping if vrf is empty or not exists! + */ + $overlap=$Subnets->verify_subnet_overlapping ($_POST['sectionId'], $_POST['cidr'], $_POST['vrfId']); + if($overlap!==false) { + $errors[] = $overlap; + } + } +} +/** + * Execute different checks on add only and when subnet is nested + */ +else if ($_POST['action']=="add") { + //verify cidr + $cidr_check = $Subnets->verify_cidr_address($_POST['cidr']); + if(strlen($cidr_check)>5) { + $errors[] = $cidr_check; + } + //disable checks for folders and if strict check enabled + if($section['strictMode']==1 && !$parent_is_folder ) { + + //verify that nested subnet is inside root subnet + if($_POST['masterSubnetId']!=0) { + if (!$Subnets->verify_subnet_nesting($_POST['masterSubnetId'], $_POST['cidr'])) { + $errors[] = _('Nested subnet not in root subnet!'); + } + } + + //nested? + if($_POST['masterSubnetId']!= 0) { + $overlap = $Subnets->verify_nested_subnet_overlapping($_POST['sectionId'], $_POST['cidr'], $_POST['vrfId'], $_POST['masterSubnetId']); + if($overlap!==false) { + $errors[] = $overlap; + } + } + //not nested + else { + $overlap = $Subnets->verify_subnet_overlapping($_POST['sectionId'], $_POST['cidr'], $_POST['vrfId']); + if($overlap!==false) { + $errors[] = $overlap; + } + } + } +} +/** + * Check if slave is under master + */ +else if ($_POST['action']=="edit") { + if($section['strictMode']==1 && !$parent_is_folder) { + /* verify that nested subnet is inside root subnet */ + if($_POST['masterSubnetId'] != 0) { + if (!$overlap = $Subnets->verify_subnet_nesting($_POST['masterSubnetId'], $_POST['cidr'])) { + $errors[] = _('Nested subnet not in root subnet!'); + } + } + } + /* for nesting - MasterId cannot be the same as subnetId! */ + if ( $_POST['masterSubnetId']==$_POST['subnetId'] ) { + $errors[] = _('Subnet cannot nest behind itself!'); + } +} +else {} + + +//custom fields +if(sizeof($custom) > 0) { + foreach($custom as $myField) { + //booleans can be only 0 and 1! + if($myField['type']=="tinyint(1)") { + if($_POST[$myField['name']]>1) { + $_POST[$myField['name']] = ""; + } + } + //not empty + if($myField['Null']=="NO" && strlen($_POST[$myField['name']])==0) { + $errors[] = "Field \"$myField[name]\" cannot be empty!"; + } + } +} + +# Set permissions if adding new subnet +if($_POST['action']=="add") { + # root + if($_POST['masterSubnetId']==0) { + $_POST['permissions'] = $section['permissions']; + } + # nested - inherit parent permissions + else { + # get parent + $parent = $Subnets->fetch_subnet(null, $_POST['masterSubnetId']); + $_POST['permissions'] = $parent->permissions; + } +} + + +/* If no errors are present execute request */ +if (sizeof(@$errors)>0) { + print '
    '._('Please fix following problems').':'; + foreach ($errors as $error) { print "
    ".$error; } + print '
    '; + die(); +} +/* delete confirmation */ +elseif ($_POST['action']=="delete" && !isset($_POST['deleteconfirm'])) { + # for ajax to prevent reload + print "
    alert alert-danger
    "; + # result + print "
    "; + + # print what will be deleted + //fetch all slave subnets + $Subnets->fetch_subnet_slaves_recursive ($_POST['subnetId']); + $subcnt = sizeof($Subnets->slaves); + foreach($Subnets->slaves as $s) { + $slave_array[$s] = $s; + } + $ipcnt = $Addresses->count_addresses_in_multiple_subnets($slave_array); + + print ""._("Warning").": "._("I will delete").":
      "; + print "
    • $subcnt "._("subnets")."
    • "; + if($ipcnt>0) { + print "
    • $ipcnt "._("IP addresses")."
    • "; + } + print "
    "; + + print "
    "; + print _("Are you sure you want to delete above items?")." "; + print "
    "; + print " "._("Confirm").""; + print "
    "; + print "
    "; + print "
    "; +} +/* execute */ +else { + + # create array of default update values + $values = array("id"=>@$_POST['subnetId'], + "isFolder"=>0, + "masterSubnetId"=>$_POST['masterSubnetId'], + "subnet"=>$Subnets->transform_to_decimal($_POST['subnet']), + "mask"=>$_POST['mask'], + "description"=>@$_POST['description'], + "vlanId"=>$_POST['vlanId'], + "allowRequests"=>$Admin->verify_checkbox(@$_POST['allowRequests']), + "showName"=>$Admin->verify_checkbox(@$_POST['showName']), + "discoverSubnet"=>$Admin->verify_checkbox(@$_POST['discoverSubnet']), + "pingSubnet"=>$Admin->verify_checkbox(@$_POST['pingSubnet']) + ); + # for new subnets we add permissions + if($_POST['action']=="add") { + $values['permissions']=$_POST['permissions']; + $values['sectionId']=$_POST['sectionId']; + } + else { + # if section change + if(@$_POST['sectionId'] != @$_POST['sectionIdNew']) { + $values['sectionId']=$_POST['sectionIdNew']; + } + # if vrf change + if(@$_POST['vrfId'] != @$_POST['vrfIdOld']) { + $values['vrfId']=$_POST['vrfId']; + } + } + # append custom fields + $custom = $Tools->fetch_custom_fields('subnets'); + if(sizeof($custom) > 0) { + foreach($custom as $myField) { + + //replace possible ___ back to spaces + $myField['nameTest'] = str_replace(" ", "___", $myField['name']); + if(isset($_POST[$myField['nameTest']])) { $_POST[$myField['name']] = $_POST[$myField['nameTest']];} + + //booleans can be only 0 and 1! + if($myField['type']=="tinyint(1)") { + if($_POST[$myField['name']]>1) { + $_POST[$myField['name']] = 0; + } + } + //not null! + if($myField['Null']=="NO" && strlen($_POST[$myField['name']])==0) { $Result->show("danger", $myField['name'].'" can not be empty!', true); } + + # save to update array + $values[$myField['name']] = $_POST[$myField['name']]; + } + } + + # execute + if(!$Admin->object_modify("subnets", $_POST['action'], "id", $values)) { $Result->show("danger", _('Error editing subnet'), true); } + else { + # update also all slave subnets! + if(isset($values['sectionId'])&&$_POST['action']!="add") { + $Subnets->reset_subnet_slaves_recursive(); + $Subnets->fetch_subnet_slaves_recursive($_POST['subnetId']); + $Subnets->remove_subnet_slaves_master($_POST['subnetId']); + if(sizeof($Subnets->slaves)>0) { + foreach($Subnets->slaves as $slaveId) { + $Admin->object_modify ("subnets", "edit", "id", array("id"=>$slaveId, "sectionId"=>$_POST['sectionIdNew'])); + } + } + } + + # edit success + if($_POST['action']=="delete") { $Result->show("success", _('Subnet, IP addresses and all belonging subnets deleted successfully').'!', false); } + else { $Result->show("success", _("Subnet $_POST[action] successfull").'!', true); } + + # send mail + # sendObjectUpdateMails("subnet", $_POST['action'], $subnet_old_details, $_POST); + } +} +?> \ No newline at end of file diff --git a/app/admin/subnets/edit-vlan-dropdown.php b/app/admin/subnets/edit-vlan-dropdown.php new file mode 100755 index 000000000..3cb2820be --- /dev/null +++ b/app/admin/subnets/edit-vlan-dropdown.php @@ -0,0 +1,71 @@ +check_user_session(); + +# fetch all permitted domains +$permitted_domains = $Sections->fetch_section_domains ($_POST['sectionId']); +# fetch all belonging vlans +$cnt = 0; +foreach($permitted_domains as $k=>$d) { + // fetch vlans and append + $vlans = $Tools->fetch_multiple_objects("vlans", "domainId", $d, "number"); + //fetch domain + $domain = $Tools->fetch_object("vlanDomains","id",$d); + //save to array + $out[$d]['domain'] = $domain; + $out[$d]['vlans'] = $vlans; + //count add + $cnt++; +} +//filter out empty +$permitted_domains = array_filter($out); +?> + + \ No newline at end of file diff --git a/app/admin/subnets/edit.php b/app/admin/subnets/edit.php new file mode 100755 index 000000000..663d285fe --- /dev/null +++ b/app/admin/subnets/edit.php @@ -0,0 +1,426 @@ +check_user_session(); + + +# verify that user has permissions to add subnet +if($_POST['action'] == "add") { + if($Sections->check_permission ($User->user, $_POST['sectionId']) != 3) { $Result->show("danger", _('You do not have permissions to add new subnet in this section')."!", true, true); } +} +# otherwise check subnet permission +else { + if($Subnets->check_permission ($User->user, $_POST['subnetId']) != 3) { $Result->show("danger", _('You do not have permissions to add edit/delete this subnet')."!", true, true); } +} + + +/** + * This script can be called from administration, subnet edit in IP details page and from IPCalc! + * + * From IP address list we must also provide delete button! + * + * From search we directly provide + * subnet / mask + * + */ + +# we are editing or deleting existing subnet, get old details +if ($_POST['action'] != "add") { + $subnet_old_details = (array) $Subnets->fetch_subnet(null, $_POST['subnetId']); +} +# we are adding new subnet +else { + # for selecting master subnet if added from subnet details! + if(strlen($_POST['subnetId']) > 0) { + $subnet_old_temp = (array) $Subnets->fetch_subnet(null, $_POST['subnetId']); + $subnet_old_details['masterSubnetId'] = @$subnet_old_temp['id']; // same master subnet ID for nested + $subnet_old_details['vlanId'] = @$subnet_old_temp['vlanId']; // same default vlan for nested + $subnet_old_details['vrfId'] = @$subnet_old_temp['vrfId']; // same default vrf for nested + } + # set master if it came from free space! + if(isset($_POST['freespaceMSID'])) { + $subnet_old_details['masterSubnetId'] = $_POST['freespaceMSID']; // dumb name, but it will do :) + } +} +# fetch custom fields +$custom_fields = $Tools->fetch_custom_fields('subnets'); +# fetch vrfs +$vrfs = $Tools->fetch_all_objects("vrf", "name"); +# check if it has slaves - if yes it cannot be splitted! +$slaves = $Subnets->has_slaves($_POST['subnetId']); +# fetch all sections +$sections = $Sections->fetch_all_sections(); + +# for vlan result on the fly +if(isset($_POST['vlanId'])) { + $subnet_old_details['vlanId'] = $_POST['vlanId']; +} + +# set readonly flag +$readonly = $_POST['action']=="edit" || $_POST['action']=="delete" ? true : false; +?> + + + + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + settings->enableVRF==1) { + print '' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print '' . "\n"; + + } + else { + print ''. "\n"; + } + + ?> + + + + + + + + + + + + + + + + + + settings->enableIPrequests==1) { + //checked + $checked = @$subnet_old_details['allowRequests']==1 ? "checked" : ""; + + print '' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print '' . "\n"; + + } + else { + print ''. "\n"; + } + + //show names instead of ip address + print '' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print '' . "\n"; + + + //check host status + $checked = @$subnet_old_details['pingSubnet']==1 ? "checked": ""; + print '' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ''; + + //Discover new hosts + $checked = @$subnet_old_details['discoverSubnet']==1 ? "checked": ""; + print '' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ''; + + //custom Subnet fields + if(sizeof($custom_fields) > 0) { + # count datepickers + $timeP = 0; + + print ""; + print " "; + print ""; + foreach($custom_fields as $field) { + + # replace spaces + $field['nameNew'] = str_replace(" ", "___", $field['name']); + # retain newlines + $subnet_old_details[$field['name']] = str_replace("\n", "\\n", @$subnet_old_details[$field['name']]); + + # required + $required = $field['Null']=="NO" ? "*" : ""; + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + } + + # divider + print ""; + print " "; + print ""; + ?> + +
    + transform_to_dotted($subnet_old_temp['subnet']).'/'.($subnet_old_temp['mask']+1);} //for nested + if (@$_POST['location'] == "ipcalc") { $cidr = $_POST['subnet'].'/'.$_POST['bitmask']; } //from ipcalc + if ($_POST['action'] != "add") { $cidr = $Subnets->transform_to_dotted($subnet_old_details['subnet']).'/'.$subnet_old_details['mask']; } //editing existing + ?> + > + + + + +
    + +
    + +
    + +
    + print_mastersubnet_dropdown_menu($_POST['sectionId'], @$subnet_old_details['masterSubnetId']); ?> + !
    '._('VRF').'' . "\n"; + print ' '. "\n"; + print ' '._('Add this subnet to VRF').'

    / +
    + + + +
    +

    '._('IP Requests').'' . "\n"; + print ' '. "\n"; + print ' '._('Allow or deny IP requests for this subnet').'
    '._('Show as name').'' . "\n"; + print ' '. "\n"; + + //hidden ones + ?> + + + + + + + + + + + ' . "\n"; + print ' '._('Show Subnet name instead of subnet IP address').'
    '._('Check hosts status').'' . "\n"; + print ' '. "\n"; + print ' '._('Ping hosts inside subnet to check avalibility').'
    '._('Discover new hosts').'' . "\n"; + print ' '. "\n"; + print ' '._('Discover new hosts in this subnet').'

    '. $field['name'] .' '.$required.''. "\n"; + + //set type + if(substr($field['type'], 0,3) == "set") { + //parse values + $tmp = explode(",", str_replace(array("set(", ")", "'"), "", $field['type'])); + //null + if($field['Null']!="NO") { array_unshift($tmp, ""); } + + print ""; + } + //date and time picker + elseif($field['type'] == "date" || $field['type'] == "datetime") { + // just for first + if($timeP==0) { + print ''; + print ''; + print ''; + } + $timeP++; + + //set size + if($field['type'] == "date") { $size = 10; $class='datepicker'; $format = "yyyy-MM-dd"; } + else { $size = 19; $class='datetimepicker'; $format = "yyyy-MM-dd"; } + + //field + if(!isset($subnet_old_details[$field['name']])) { print ' '. "\n"; } + else { print ' '. "\n"; } + } + //boolean + elseif($field['type'] == "tinyint(1)") { + print ""; + } + //text + elseif($field['type'] == "text") { + print ' '. "\n"; + } + //default - input field + else { + print ' '. "\n"; + } + + print '

    +
    + + "._('Warning')."
    "._('Removing subnets will delete ALL underlaying subnets and belonging IP addresses')."!
    "; + } + ?> + + + + + + +
    +
    + + "._('Delete subnet').""; + } + ?> + +
    + +
    + + +
    \ No newline at end of file diff --git a/app/admin/subnets/index.php b/app/admin/subnets/index.php new file mode 100755 index 000000000..f254959af --- /dev/null +++ b/app/admin/subnets/index.php @@ -0,0 +1,132 @@ +check_user_session(); + +# fetch custom fields +$custom_fields = $Tools->fetch_custom_fields('subnets'); + +# fetch all sections +$sections = $Sections->fetch_all_sections(); + +# set hidden fields +$hidden_custom_fields = json_decode($User->settings->hiddenCustomFields, true); +$hidden_custom_fields = is_array(@$hidden_custom_fields['subnets']) ? $hidden_custom_fields['subnets'] : array(); + + +# read cookie for showing all subnets +if(isset($_COOKIE['showSubnets'])) { + if($_COOKIE['showSubnets'] == 1) { + $display = ""; + $icon = "fa-compress"; + $iconchevron = "fa-angle-down"; + } + else { + $display = "display:none"; + $icon = "fa-expand"; + $iconchevron = "fa-angle-right"; + } +} +else { + $display = "display:none"; + $icon = "fa-expand"; + $iconchevron = "fa-angle-right"; +} + +# print all sections with delete / edit button +print '

    '._('Subnet management').'

    '; +print "
    "; + + + +/* Foreach section fetch subnets and print it! */ +if(sizeof($sections) > 0) { + + # expand / collapse button + print ""; + + # print table structure + print ""; + + $m = 0; //for subnet index + + # print titles and content + foreach($sections as $section) { + //cast + $section = (array) $section; + # set colcount + $colCount = $User->settings->enableVRF==1 ? 9 : 8; + + # just for count + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $field) { + if(!in_array($field['name'], $hidden_custom_fields)) { + $colCount++; + } + } + } + + # print name + print ""; + print ""; + print " "; + print ""; + print ""; + + # get all subnets in section + $section_subnets = $Subnets->fetch_section_subnets($section['id']); + + # collapsed div with details + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + if($User->settings->enableVRF == 1) { + print " "; + } + print " "; + print " "; + print " "; + print " "; + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $field) { + if(!in_array($field['name'], $hidden_custom_fields)) { + print " "; + } + } + } + print " "; + print ""; + + # add new link + print ""; + print " "; + print " "; + + # no subnets + if(sizeof($section_subnets) == 0) { + print ""; + } + else { + # subnets + $Subnets->print_subnets_tools($User->user, $section_subnets, $custom_fields); + } + print ""; + $m++; + } + + # end master table + print "
    "; + print "

    $section[name]

    "; + print "
    "._('Subnet').""._('Description').""._('VLAN').""._('Master Subnet')."
    "; + print " "; + print "
    "._('Section has no subnets')."!
    "; +} +?> \ No newline at end of file diff --git a/app/admin/subnets/permissions-show.php b/app/admin/subnets/permissions-show.php new file mode 100755 index 000000000..717822964 --- /dev/null +++ b/app/admin/subnets/permissions-show.php @@ -0,0 +1,104 @@ +check_user_session(); + + +# ID must be numeric +if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true, true); } + +# get all groups +$groups = $Admin->fetch_all_objects ("userGroups", "g_id"); +# get subnet details +$subnet = $Subnets->fetch_subnet(null, $_POST['subnetId']); +?> + + + +
    isFolder==1 ? _('Manage folder permissions') : _('Manage subnet permissions'); ?>
    + + +
    + isFolder==1) { print _('Manage permissions for folder')." $subnet->description"; } + else { print _('Manage permissions for subnet'); ?> transform_to_dotted($subnet->subnet)."/".$subnet->mask." ($subnet->description)"; } + ?> +
    + +
    + + + permissions)>1) { $permissons = $Sections->parse_section_permissions($subnet->permissions); } + else { $permissons = ""; } + + # print each group + if($groups) { + foreach($groups as $g) { + //cast + $g = (array) $g; + + print ""; + print " "; + print " "; + print ""; + } + } else { + print ""; + print " "; + print ""; + } + ?> + +
    $g[g_name]"; + + print ""; + + print " na"; + if(@$permissons[$g['g_id']]==1) { print " ro"; } + else { print " ro"; } + if(@$permissons[$g['g_id']]==2) { print " rw"; } + else { print " rw"; } + if(@$permissons[$g['g_id']]==3) { print " rwa"; } + else { print " rwa"; } + print ""; + + # hidden + print ""; + + print "
    "._('No groups available')."
    +
    + + has_slaves($_POST['subnetId'])) { $Result->show("info", _('Permissions for all nested subnets will be overridden')."!", false); } + ?> + +
    + + + +
    +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/app/admin/subnets/permissions-submit.php b/app/admin/subnets/permissions-submit.php new file mode 100755 index 000000000..a54c57c5b --- /dev/null +++ b/app/admin/subnets/permissions-submit.php @@ -0,0 +1,41 @@ +check_user_session(); + + + +# get posted permissions +foreach($_POST as $key=>$val) { + if(substr($key, 0,5) == "group") { + if($val != 0) { + $perm[substr($key,5)] = $val; + } + } +} + +# fetch all possible slaves + master +$Subnets->fetch_subnet_slaves_recursive($_POST['subnetId']); + +# set values and update +$permissions = isset($perm) ? array("permissions"=>json_encode($perm)) : array("permissions"=>""); + +if(!$Admin->object_modify("subnets", "edit-multiple", $Subnets->slaves, $permissions)) { $Result->show("danger", _("Failed to set subnet permissons")."!", true); } +else { $Result->show("success", _("Subnet permissions set")."!", true); } + +?> \ No newline at end of file diff --git a/app/admin/subnets/resize-save.php b/app/admin/subnets/resize-save.php new file mode 100755 index 000000000..7c150994b --- /dev/null +++ b/app/admin/subnets/resize-save.php @@ -0,0 +1,42 @@ +check_user_session(); + + +# ID must be numeric +if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true); } +# verify that user has write permissions for subnet +if($Subnets->check_permission ($User->user, $_POST['subnetId'])<3) { $Result->show("danger", _('You do not have permissions to resize subnet').'!', true); } + +# fetch old subnet details and set new +$subnet_old = (array) $Subnets->fetch_subnet (null, $_POST['subnetId']); + +# verify resizing +$Subnets->verify_subnet_resize ($subnet_old['subnet'], $_POST['newMask'], $subnet_old['id'], $subnet_old['vrfId'], $subnet_old['masterSubnetId'], $subnet_old['mask']); + +# set update values +$values = array("id"=>$_POST['subnetId'], + "mask"=>$subnet_new['mask'] + ); +if(!$Subnet->modify_subnet ("resize", $values)) { $Result->show("danger", _("Error resizing subnet")."!", true); } +else { $Result->show("success", _("Subnet resized successfully")."!", true); } + +?> \ No newline at end of file diff --git a/app/admin/subnets/resize.php b/app/admin/subnets/resize.php new file mode 100755 index 000000000..b14d9e279 --- /dev/null +++ b/app/admin/subnets/resize.php @@ -0,0 +1,80 @@ +check_user_session(); + + +# ID must be numeric +if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true, true); } +# verify that user has write permissions for subnet +if($Subnets->check_permission ($User->user, $_POST['subnetId'])<3) { $Result->show("danger", _('You do not have permissions to resize subnet').'!', true, true); } + +# fetch subnet details +$subnet = (array) $Subnets->fetch_subnet (null, $_POST['subnetId']); +?> + + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + +
    transform_to_dotted($subnet['subnet']) . " ($subnet[description])"; ?>
    + / + +
    +
    + + +
    + : +
      +
    • +
    • +
    +
    + +
    + + + +
    +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/app/admin/subnets/ripe-query.php b/app/admin/subnets/ripe-query.php new file mode 100755 index 000000000..137203846 --- /dev/null +++ b/app/admin/subnets/ripe-query.php @@ -0,0 +1,59 @@ +check_user_session(); + + +/* http://apps.db.ripe.net/whois/lookup/ripe/inetnum/212.58.224.0-212.58.255.255.html.xml */ +/* http://apps.db.ripe.net/whois/lookup/ripe/inet6num/2102:840::/32.xml */ + + +# identify address and set proper url +$type = $Subnets->identify_address($_POST['subnet']); + +if ($type == "IPv4") { $url = "http://apps.db.ripe.net/whois/lookup/ripe/inetnum/$_POST[subnet].xml"; } +else { $url = "http://apps.db.ripe.net/whois/lookup/ripe/inet6num/$_POST[subnet].xml"; } + +/* querry ripe db and parse result */ +$xml = @simplexml_load_file($url); + +/* fail */ +if (!$xml) { + /* save to json and return */ + header("Content-type: text/javascript"); + echo json_encode(array("Error"=>"Subnet not present in RIPE DB
    Error opening URL $url")); +} +else { + foreach($xml->objects->object[0]->attributes->children() as $m=>$subtag) { + $a = (string) $subtag->attributes()->name; + $b = (string) $subtag->attributes()->value; + + # replace - with _ + $a = str_replace("-", "_", $a); + + $out["$a"] .= $b.'\n'; + } + + # replace last newlines + foreach($out as $key=>$val) { + $out[$key] = rtrim($val, "\\n"); + } + + /* save to json and return */ + header("Content-type: text/javascript"); + echo json_encode($out); +} +?> \ No newline at end of file diff --git a/app/admin/subnets/split-save.php b/app/admin/subnets/split-save.php new file mode 100755 index 000000000..b8f9ef029 --- /dev/null +++ b/app/admin/subnets/split-save.php @@ -0,0 +1,38 @@ +check_user_session(); + + +# id must be numeric +if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true); } + +# get subnet details +$subnet_old = $Subnets->fetch_subnet (null, $_POST['subnetId']); + +# verify that user has write permissions for subnet +$subnetPerm = $Subnets->check_permission ($User->user, $subnet_old->id); +if($subnetPerm < 3) { $Result->show("danger", _('You do not have permissions to resize subnet').'!', true); } + +# verify +$Subnets->subnet_split ($subnet_old, $_POST['number'], $_POST['prefix'],$_POST['group'], $_POST['strict']); + +# all good +$Result->show("success", _("Subnet splitted ok")."!", true); + +?> \ No newline at end of file diff --git a/app/admin/subnets/split.php b/app/admin/subnets/split.php new file mode 100755 index 000000000..38b01a0f0 --- /dev/null +++ b/app/admin/subnets/split.php @@ -0,0 +1,144 @@ +check_user_session(); + + +# ID must be numeric +if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true, true); } + +# get subnet details +$subnet = $Subnets->fetch_subnet (null, $_POST['subnetId']); + +# verify that user has write permissions for subnet +$subnetPerm = $Subnets->check_permission ($User->user, $subnet->id); +if($subnetPerm < 3) { $Result->show("danger", _('You do not have permissions to resize subnet').'!', true, true); } + +# check if it has slaves - if yes it cannot be splitted! +if($Subnets->has_slaves($subnet->id)) { $Result->show("danger", _('Only subnets that have no nested subnets can be splitted')."!", true, true); } + +# calculate max mask +$max_new_mask = $Subnets->identify_address($Subnets->transform_to_dotted($subnet->subnet))=="IPv4" ? 32 : 128; + + +# die if too small +if($max_new_mask < $subnet->mask) { $Result->show("danger", _("Subnet too small to be splitted"), true, true); } + +$n = 2; # step +$m = 0; # array id + +//set mask options +for($mask=($subnet->mask+1); $mask<=$max_new_mask; $mask++) { + # set vars + $opts[$m]['mask'] = $mask; + $opts[$m]['number'] = $n; + $opts[$m]['max'] = $Subnets->get_max_hosts ($mask, $Subnets->identify_address($Subnets->transform_to_dotted($subnet->subnet))); + + # next + $m++; + $n = $n * 2; + + # max number = 16! + if($n > 256) { + $mask = 1000; + } +} +?> + + +
    + + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    transform_to_dotted($subnet->subnet) . "/$subnet->mask ($subnet->description)"; ?>
    + + +
    + +
    + +
    + "> +
    +
    + + +
    + +
      +
    • +
    • +
    • +
    +
    + +
    + + + +
    +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/app/admin/subnets/truncate-save.php b/app/admin/subnets/truncate-save.php new file mode 100755 index 000000000..f26be7cac --- /dev/null +++ b/app/admin/subnets/truncate-save.php @@ -0,0 +1,34 @@ +check_user_session(); + + +# id must be numeric +if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true, true); } + +# get subnet details +$subnet = $Subnets->fetch_subnet (null, $_POST['subnetId']); + +# verify that user has write permissions for subnet +$subnetPerm = $Subnets->check_permission ($User->user, $subnet->id); +if($subnetPerm < 3) { $Result->show("danger", _('You do not have permissions to resize subnet').'!', true, true); } + +# truncate network +if(!$Subnets->subnet_truncate($subnet->id)) { $Result->show("danger", _("Failed to truncate subnet"), true); } +else { $Result->show("success", _("Subnet truncated succesfully")."!", true); } +?> \ No newline at end of file diff --git a/app/admin/subnets/truncate.php b/app/admin/subnets/truncate.php new file mode 100755 index 000000000..c42bb4072 --- /dev/null +++ b/app/admin/subnets/truncate.php @@ -0,0 +1,66 @@ +check_user_session(); + + +# id must be numeric +if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true, true); } + +# get subnet details +$subnet = $Subnets->fetch_subnet (null, $_POST['subnetId']); + +# verify that user has write permissions for subnet +$subnetPerm = $Subnets->check_permission ($User->user, $subnet->id); +if($subnetPerm < 3) { $Result->show("danger", _('You do not have permissions to resize subnet').'!', true, true); } +?> + + +
    + + +
    + + + + + + + + + + + + +
    transform_to_dotted($subnet->subnet)."/$subnet->mask ($subnet->description)"; ?>
    count_subnet_addresses ($subnet->id); ?>
    + + +
    + +
    +
    + + +
    +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/app/admin/tags/edit-result.php b/app/admin/tags/edit-result.php new file mode 100644 index 000000000..3bb62c357 --- /dev/null +++ b/app/admin/tags/edit-result.php @@ -0,0 +1,52 @@ +check_user_session(); + +# fetch old values +if($_POST['action']=="delete") { + $old_tag = $Admin->fetch_object ("ipTags", "id", $_POST['id']); +} +else { + $old_tag = new StdClass (); +} + +/* checks */ +if($_POST['action']=="delete" && $old_tag->locked!="No") { $Result->show("danger", _("Cannot delete locked tag"), true); } +if($_POST['action']!="delete") { + if(strlen($_POST['type'])<3) { $Result->show("danger", _("Invalid tag name"), true); } + if(strlen($_POST['bgcolor'])<4) { $Result->show("danger", _("Invalid bg color"), true); } + if(strlen($_POST['fgcolor'])<4) { $Result->show("danger", _("Invalid fg color"), true); } +} + +# create array of values for modification +$values = array("id"=>@$_POST['id'], + "type"=>$_POST['type'], + "bgcolor"=>@$_POST['bgcolor'], + "fgcolor"=>@$_POST['fgcolor'], + "showtag"=>@$_POST['showtag'], + "compress"=>@$_POST['compress'] + ); + +# execute +if(!$Admin->object_modify("ipTags", $_POST['action'], "id", $values)) { $Result->show("danger", _("Tag $_POST[action] error"), true); } +else { $Result->show("success", _("Tag $_POST[action] success"), false); } + +# reset if delete to online +if($_POST['action']=="delete") { + $Admin->update_object_references ("ipaddresses", "state", $old_tag->id, 0); +} +?> \ No newline at end of file diff --git a/app/admin/tags/edit.php b/app/admin/tags/edit.php new file mode 100644 index 000000000..063234824 --- /dev/null +++ b/app/admin/tags/edit.php @@ -0,0 +1,118 @@ +check_user_session(); + +# ID must be numeric +if($_POST['action']!="add" && !is_numeric($_POST['id'])) { $Result->show("danger", _("Invalid ID"), true, true); } + +# fetch api for edit / add +if($_POST['action']!="add") { + # fetch api details + $tag = $Admin->fetch_object ("ipTags", "id", $_POST['id']); + # null ? + $tag===false ? $Result->show("danger", _("Invalid ID"), true, true) : null; +} +?> + + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + > + + +
    + +
    +
    + > +
    +
    +
    + > +
    +
    + +
    +
    + +
    + + +
    +
    + + +
    + +
    +
    diff --git a/app/admin/tags/index.php b/app/admin/tags/index.php new file mode 100644 index 000000000..5eeb4634a --- /dev/null +++ b/app/admin/tags/index.php @@ -0,0 +1,63 @@ +check_user_session(); + +# fetch all vrfs +$all_types = $Admin->fetch_all_objects("ipTags"); +?> + +

    +

    + + + + +'. "\n"; + +# headers +print ''. "\n"; +print ' '._('type').''. "\n"; +print ' '._('Show Tag').''. "\n"; +print ' '._('BG color').''. "\n"; +print ' '._('FG color').''. "\n"; +print ' '._('Compress range').''. "\n"; +print ' '._('Locked').''. "\n"; +print ' '. "\n"; +print ''. "\n"; + +# loop +foreach ($all_types as $type) { + //cast + $type = (array) $type; + + //format type + $showtag = $type['showtag']==1 ? "Yes" : "No"; + + //print details + print ''. "\n"; + print ' '. $type['type'] .''. "\n"; + print ' '.$showtag.''. "\n"; + print ' '. $type['bgcolor'] .''. "\n"; + print ' '. $type['fgcolor'] .''. "\n"; + print ' '. $type['compress'] .''. "\n"; + print ' '. $type['locked'] .''. "\n"; + + print " "; + print "
    "; + print " "; + print " "; + print "
    "; + print " "; + print ''. "\n"; +} +print ''. "\n"; +?> + + +
    \ No newline at end of file diff --git a/app/admin/users/ad-search-form.php b/app/admin/users/ad-search-form.php new file mode 100755 index 000000000..45afd8d09 --- /dev/null +++ b/app/admin/users/ad-search-form.php @@ -0,0 +1,77 @@ +check_user_session(); + +# fetch all available LDAP servers +$servers = $Admin->fetch_all_objects ("usersAuthMethod"); +foreach($servers as $k=>$s) { + if($s->type!="AD" && $s->type!="LDAP") { + unset($servers[$k]); + } +} + +# die if no servers +if(sizeof($servers)==0) { $Result->show("danger", _("No servers available"), true, true); } +?> + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + +
    Enter username
    Select server + +

    + +
    +
    + + +
    +
    + +
    +
    \ No newline at end of file diff --git a/app/admin/users/ad-search-result.php b/app/admin/users/ad-search-result.php new file mode 100755 index 000000000..5ada65823 --- /dev/null +++ b/app/admin/users/ad-search-result.php @@ -0,0 +1,97 @@ +check_user_session(); + +# fetch server +$server = $Admin->fetch_object("usersAuthMethod", "id", $_POST['server']); +$server!==false ? : $Result->show("danger", _("Invalid server ID"), true); + +//parse parameters +$params = json_decode($server->params); + +//no login parameters +if(strlen(@$params->adminUsername)==0 || strlen(@$params->adminPassword)==0) { $Result->show("danger", _("Missing credentials"), true); } +//at least 2 chars +if(strlen($_POST['dname'])<2) { $Result->show("danger", _('Please enter at least 2 characters'), true); } + + + +//open connection +try { + //AD + $adldap = new adLDAP(array( 'base_dn'=>$params->base_dn, 'account_suffix'=>$params->account_suffix, + 'domain_controllers'=>explode(";",$params->domain_controllers), 'use_ssl'=>$params->use_ssl, + 'use_tls'=> $params->use_tls, 'ad_port'=> $params->ad_port + )); + + //first check connection + if(@fsockopen($adldap->selected_controller, $params->ad_port, $errno, $errstr, 2)==false) { $Result->show("danger", "Cannot connect to controller $adldap->selected_controller
    $errstr ($errno)", true); } + + //try to login with higher credentials for search + $authUser = $adldap->authenticate($params->adminUsername, $params->adminPassword); + if ($authUser == false) { + $Result->show("danger", _("Invalid credentials"), true); + } + + // set OpenLDAP flag + if($server->type == "LDAP") { $adldap->setUseOpenLDAP(true); } + + //search for domain user! + $userinfo = $adldap->user()->info("$_POST[dname]*", array("*")); + + //echo $adldap->getLastError(); +} +catch (adLDAPException $e) { + $Result->show("danger", $e, true); +} + + +//check for found +if(!isset($userinfo['count'])) { + print "
    "; + print _('No users found')."!
    "; + print _('Possible reasons').":"; + print "
      "; + print "
    • "._('Username not existing')."
    • "; + print "
    • "._('Invalid baseDN setting for AD')."
    • "; + print "
    • "._('AD account does not have enough privileges for search')."
    • "; + print "
    "; +} else { + print _(" Following users were found").": ($userinfo[count]):
    "; + + print ""; + + unset($userinfo['count']); + if(sizeof(@$userinfo)>0 && isset($userinfo)) { + foreach($userinfo as $u) { + print ""; + print " "; + print " "; + print " "; + //actions + print " "; + print ""; + } + } + print "
    ".$u['displayname'][0]; + print "".$u['samaccountname'][0]."".$u['mail'][0].""; + print " "._('Select').""; + print "
    "; +} + +?> \ No newline at end of file diff --git a/app/admin/users/edit-notify.php b/app/admin/users/edit-notify.php new file mode 100755 index 000000000..add4c46bd --- /dev/null +++ b/app/admin/users/edit-notify.php @@ -0,0 +1,94 @@ +check_user_session(); + +# fetch mailer settings +$mail_settings = $Admin->fetch_object("settingsMail", "id", 1); + + +# initialize mailer +$phpipam_mail = new phpipam_mail($User->settings, $mail_settings); +$phpipam_mail->initialize_mailer(); + + +# set subject +if ($_POST['action'] == "Add") { $subject = _('New ipam account created'); } +else if ($_POST['action'] == "Edit") { $subject = _('User ipam account updated'); } +else { $subject = _('IPAM account details'); } + + +# set html content +$content[] = ""; +$content[] = ""; +$content[] = ''; +//we dont need pass for domain account +if($auth_method->type == "local") { +$content[] = ''; +if(strlen($_POST['password2']) != 0) { +$content[] = ''; +}} +else { +$content[] = ''; +$content[] = ''; +} +$content[] = ''; +$content[] = ''; +$content[] = ''; +$content[] = ""; +$content[] = "
    $subject
    • '._('Name').' '. $_POST['real_name'] .'
    • '._('Username').' '. $_POST['username'] .'
    • '._('Password').' '. $_POST['password2'] .'
    • '._('Username').' * '._('your domain username').' ('. $_POST['username'] .')
    • '._('Password').' * '._('your domain password').'
    • '._('Email').' '.$_POST['email'].'
    • '._('Role').' '. $_POST['role'] .'
    • '._('WebApp').' '. $User->settings->siteURL. '
    "._('Sent by user')." ".$User->user->real_name." at ".date('Y/m/d H:i')."
    "; + +# plain +$content_plain[] = "$subject"."\r\n------------------------------\r\n"; +$content_plain[] = _("Name").": $_POST[real_name]"; +# we dont need pass for domain account +if($auth_method->type == "local") { +$content_plain[] = _("Username").": $_POST[username]"; +if(strlen($_POST['password2']) != 0) { +$content_plain[] = _("Password").": $_POST[password2]"; +}} +else { +$content_plain[] = _("Username").": * your domain username($_POST[username]"; +$content_plain[] = _("Password").": * your domain password"; +} +$content_plain[] = _("Email").": $_POST[email]"; +$content_plain[] = _("Role").": $_POST[role]"; +$content_plain[] = _("WebApp").": ".$User->settings->siteURL; +$content_plain[] = "\r\n"._("Sent by user")." ".$User->user->real_name." at ".date('Y/m/d H:i'); + + +# set content +$content = $phpipam_mail->generate_message (implode("\r\n", $content)); +$content_plain = implode("\r\n",$content_plain); + + +# try to send +try { + $phpipam_mail->Php_mailer->setFrom($mail_settings->mAdminMail, $mail_settings->mAdminName); + $phpipam_mail->Php_mailer->addAddress($_POST['email'], $_POST['real_name']); + //add all admins to CC + foreach($Admin->fetch_multiple_objects ("users", "role", "Administrator") as $admin) { + $phpipam_mail->Php_mailer->AddCC($admin->email); + } + $phpipam_mail->Php_mailer->Subject = $subject; + $phpipam_mail->Php_mailer->msgHTML($content); + $phpipam_mail->Php_mailer->AltBody = $content_plain; + //send + $phpipam_mail->Php_mailer->send(); +} catch (phpmailerException $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); +} catch (Exception $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); +} + +//if error not sent print ok +$Result->show("success", _('Notification mail for new account sent').'!', true); + +?> \ No newline at end of file diff --git a/app/admin/users/edit-result.php b/app/admin/users/edit-result.php new file mode 100755 index 000000000..a025c4371 --- /dev/null +++ b/app/admin/users/edit-result.php @@ -0,0 +1,116 @@ +check_user_session(); + +# fetch auth method +$auth_method = $Admin->fetch_object ("usersAuthMethod", "id", $_POST['authMethod']); +$auth_method!==false ? : $Result->show("danger", _("Invalid authentication method"), true); + + +/* checks */ + +# ID must be numeric +if($_POST['action']=="edit"||$_POST['action']=="delete") { + if(!is_numeric($_POST['userId'])) { $Result->show("danger", _("Invalid ID"), true); } +} + +# if password changes check and hash passwords +if(strlen(@$_POST['password1'])>0 || (@$_POST['action']=="add" && $auth_method->type=="local")) { + //checks + if($_POST['password1']!=$_POST['password2']) { $Result->show("danger", _("Passwords do not match"), true); } + if(strlen($_POST['password1'])<8) { $Result->show("danger", _("Password must be at least 8 characters long!"), true); } + + //hash passowrd + $_POST['password1'] = $User->crypt_user_pass ($_POST['password1']); +} + +# general checks +if(strlen(@$_POST['real_name'])==0) { $Result->show("danger", _("Real name field is mandatory!"), true); } +# email format must be valid +if (!validate_email(@$_POST['email'])) { $Result->show("danger", _("Invalid email address!"), true); } + +# username must not already exist (if action is add) +if ($_POST['action']=="add") { + //username > 8 chars + if(strlen($_POST['username'])<6) { $Result->show("danger", _("Username must be at least 6 characters long!"), true); } + //check duplicate + if($Admin->fetch_object("users", "username", $_POST['username'])!==false) { + { $Result->show("danger", _("User")." ".$_POST['username']." "._("already exists!"), true); } + } +} +# admin user cannot be deleted +if($_POST['action']=="delete" && $_POST['username']=="admin") { $Result->show("danger", _("Admin user cannot be deleted"), true); } + +# custom fields check +$myFields = $Tools->fetch_custom_fields('users'); +if(sizeof($myFields) > 0) { + foreach($myFields as $myField) { + # replace possible ___ back to spaces! + $myField['nameTest'] = str_replace(" ", "___", $myField['name']); + + if(isset($_POST[$myField['nameTest']])) { $_POST[$myField['name']] = $_POST[$myField['nameTest']];} + + //booleans can be only 0 and 1! + if($myField['type']=="tinyint(1)") { + if($_POST[$myField['name']]>1) { + $$_POST[$myField['name']] = ""; + } + } + //not null! + if($myField['Null']=="NO" && strlen($_POST[$myField['name']])==0) { $Result->show("danger", '"'.$myField['name'].'" can not be empty!', true); } + } +} + + +/* update */ + +# formulate update values +$values = array("id"=>@$_POST['userId'], + "real_name"=>$_POST['real_name'], + "username"=>$_POST['username'], + "email"=>$_POST['email'], + "role"=>$_POST['role'], + "authMethod"=>$_POST['authMethod'], + "lang"=>$_POST['lang'], + "mailNotify"=>$_POST['mailNotify'], + "mailChangelog"=>$_POST['mailChangelog'] + ); +# update pass ? +if(strlen(@$_POST['password1'])>0 || (@$_POST['action']=="add" && $auth_method->type=="local")) { +$values['password'] = $_POST['password1']; +} +# set groups user belongs to +if($_POST['role']=="Administrator") { + $values['groups'] = null; +} else { + foreach($_POST as $key=>$post) { + if(substr($key, 0,5) == "group") { + $group[substr($key, 5)] = substr($key, 5); + } + } + $values['groups'] = json_encode(@$group); +} + +# execute +if(!$Admin->object_modify("users", $_POST['action'], "id", $values)) { $Result->show("danger", _("User $_POST[action] failed").'!', true); } +else { $Result->show("success", _("User $_POST[action] successfull").'!', false); } + +/* mail user */ +if($Admin->verify_checkbox(@$_POST['notifyUser'])==1) { include("edit-notify.php"); } + +?> \ No newline at end of file diff --git a/app/admin/users/edit.php b/app/admin/users/edit.php new file mode 100755 index 000000000..c23742593 --- /dev/null +++ b/app/admin/users/edit.php @@ -0,0 +1,356 @@ +check_user_session(); + + +# fetch custom fields +$custom = $Tools->fetch_custom_fields('users'); +# fetch all languages +$langs = $Admin->fetch_all_objects ("lang", "l_id"); +# fetch all auth types +$auth_types = $Admin->fetch_all_objects ("usersAuthMethod", "id"); +# fetch all groups +$groups = $Admin->fetch_all_objects ("userGroups", "g_id"); + + +# set header parameters and fetch user +if($_POST['action']!="add") { + $user = $Admin->fetch_object ("users", "id", $_POST['id']); + //false + if($user===false) { $Result->show("danger", _("Invalid ID"), true, true); } + else { + $user = (array) $user; + } +} +else { + $user = array(); + //set default lang + $user['lang']=$User->settings->defaultLang; +} +?> + + + + + +
    + + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0) { + print ''; + print ' '; + print ''; + + # count datepickers + $timeP = 0; + + # all my fields + foreach($custom as $myField) { + # replace spaces with | + $myField['nameNew'] = str_replace(" ", "___", $myField['name']); + + # required + if($myField['Null']=="NO") { $required = "*"; } + else { $required = ""; } + + print ''. "\n"; + print ' '. "\n"; + print ' "; + print ' '. "\n"; + print ''. "\n"; + } + } + ?> + + +
    > + + +
    + + + + + + + +
      +
    • +
    • +
    +
    + +

    (!)
    + + +

    + +
    >
    + + + +
    + + + +

    + groups_parse_ids($ugroups); + + foreach($groups as $g) { + # empty fix + if(sizeof($ugroups) > 0) { + if(in_array($g->g_id, $ugroups)) { print "$g->g_name
    "; } + else { print "$g->g_name
    "; } + } + else { + { print "$g->g_name
    "; } + } + } + } + else { + $Result->show("danger", _("No groups configured"), false); + } + + ?> +

    '. $myField['name'] .' '.$required.''. "\n"; + + //set type + if(substr($myField['type'], 0,3) == "set") { + //parse values + $tmp = explode(",", str_replace(array("set(", ")", "'"), "", $myField['type'])); + //null + if($myField['Null']!="NO") { array_unshift($tmp, ""); } + + print ""; + } + //date and time picker + elseif($myField['type'] == "date" || $myField['type'] == "datetime") { + // just for first + if($timeP==0) { + print ''; + print ''; + print ''; + } + $timeP++; + + //set size + if($myField['type'] == "date") { $size = 10; $class='datepicker'; $format = "yyyy-MM-dd"; } + else { $size = 19; $class='datetimepicker'; $format = "yyyy-MM-dd"; } + + //field + if(!isset($user[$myField['name']])) { print ' '. "\n"; } + else { print ' '. "\n"; } + } + //boolean + elseif($myField['type'] == "tinyint(1)") { + print ""; + } + //text + elseif($myField['type'] == "text") { + print ' '. "\n"; + } + //default - input field + else { + print ' '. "\n"; + } + + print " ".$myField['Comment']."
    +
    + +
    + + + +
    +
    + + +
    + + +
    +
    \ No newline at end of file diff --git a/app/admin/users/index.php b/app/admin/users/index.php new file mode 100755 index 000000000..92fe01f1b --- /dev/null +++ b/app/admin/users/index.php @@ -0,0 +1,13 @@ +check_user_session(); + +# print all or specific user? +if(isset($_GET['subnetId'])) { include("print-user.php"); } +else { include("print-all.php"); } +?> \ No newline at end of file diff --git a/app/admin/users/print-all.php b/app/admin/users/print-all.php new file mode 100644 index 000000000..4be74fafb --- /dev/null +++ b/app/admin/users/print-all.php @@ -0,0 +1,155 @@ +check_user_session(); + +# fetch all APIs +$users = $Admin->fetch_all_objects("users"); +# fetch custom fields +$custom = $Tools->fetch_custom_fields('users'); + +/* check customfields */ +$ffields = json_decode($User->settings->hiddenCustomFields, true); +$ffields = is_array(@$ffields['users']) ? $ffields['users'] : array(); +?> + + +

    +

    + + + + + + + + + + + + + + + + + + + 0) { + foreach($custom as $field) { + if(!in_array($field['name'], $ffields)) { + print ""; + } + } + } + ?> + + + +' . "\n"; + + # set icon based on normal user or admin + if($user['role'] == "Administrator") { print ' '. "\n"; } + else { print ' '. "\n"; } + + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + + # language + if(strlen($user['lang'])>0) { + # get lang name + $lname = $Admin->fetch_object("lang", "l_id", $user['lang']); + print ""; + } + else { + print ""; + } + + # check users auth method + $auth_method = $Admin->fetch_object("usersAuthMethod", "id", $user['authMethod']); + //false + print ""; + + # groups + if($user['role'] == "Administrator") { + print ' '. "\n"; + } + else { + $groups = json_decode($user['groups'], true); + $gr = $Admin->groups_parse($groups); + + print ' '. "\n"; + } + + # last login + print ""; + + # custom + if(sizeof($custom) > 0) { + foreach($custom as $field) { + if(!in_array($field['name'], $ffields)) { + print ""; + } + } + } + + # edit, delete + print " "; + + print '' . "\n"; +} +?> +
    $field[name]
    ' . $user['real_name'] . '' . $user['username'] . '' . $user['email'] . '' . $user['role'] . '$lname->l_nameEnglish (default)"; + if($auth_method===false) { print "No auth method"; } + else { print $auth_method->type." (".$auth_method->description.")"; } + print "'._('All groups').''; + if(sizeof($gr)>0) { + foreach($gr as $group) { + print $group['g_name']."
    "; + } + } + else { + print "No groups"; + } + print '
    "; + print strlen($user['lastLogin'])>0 ? $user['lastLogin'] : ""._("Never").""; + print ""; + //booleans + if($field['type']=="tinyint(1)") { + if($user[$field['name']] == "0") { print _("No"); } + elseif($user[$field['name']] == "1") { print _("Yes"); } + } + //text + elseif($field['type']=="text") { + if(strlen($user[$field['name']])>0) { print ""; } + else { print ""; } + } + else { + print $user[$field['name']]; + + } + print ""; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    + +
    +
      +
    • +
    • +
    +
    \ No newline at end of file diff --git a/app/admin/users/print-user.php b/app/admin/users/print-user.php new file mode 100644 index 000000000..0d6b5bbc1 --- /dev/null +++ b/app/admin/users/print-user.php @@ -0,0 +1,178 @@ +check_user_session(); + +# fetch user details +$user = $Admin->fetch_object("users", "id", $_GET['subnetId']); + +# invalid? +if($user===false) { $Result->show("danger", _("Invalid ID"), true); } + +# fetch user lang +$language = $Admin->fetch_object("lang", "l_id", $user->lang); +# check users auth method +$auth_details = $Admin->fetch_object("usersAuthMethod", "id", $user->authMethod); +# fetch custom fields +$custom_fields = $Tools->fetch_custom_fields('users'); +?> + + +

    +

    + + +" style="margin-bottom:10px;"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0) { + foreach($custom_fields as $key=>$field) { + $user->$key = str_replace("\n", "
    ",$user->$key); + print ""; + print " "; + print " "; + print ""; + } +} +?> + + +
    real_name; ?>
    username; ?>
    email; ?>
    l_name; ?>
    +
    + + +
    +


    role; ?>
    + No auth method"; } + else { print $auth_details->type." (".$auth_details->description.")"; } + ?> +
    lastLogin)>0 ? $user->lastLogin : ""._("Never").""; ?>
    lastActivity)>0 ? $user->lastActivity : ""._("Never").""; ?>
    + role == "Administrator") { + print _('All groups'); + } + else { + $groups = json_decode($user->groups, true); + $gr = $Admin->groups_parse($groups); + if(sizeof($gr)>0) { + foreach($gr as $group) { + print $group['g_name']."
    "; + } + } + else { + print "No groups"; + } + } + ?> +
    passChange; ?>


    compressOverride==1 ? _("Yes") : _("No") ?>
    hideFreeRange==1 ? _("Yes") : _("No") ?>
    printLimit; ?>


    role == "Administrator" ? _($user->mailNotify) : _("No"); ?>
    role == "Administrator" ? _($user->mailChangelog) : _("No"); ?>


    $key"; + //no length + if(strlen($user->$key)==0) { + print "/"; + } + //booleans + elseif($field['type']=="tinyint(1)") { + if($user->$key == "0") { print _("No"); } + elseif($user->$key == "1") { print _("Yes"); } + } + else { + print $user->$key; + } + print "
    \ No newline at end of file diff --git a/app/admin/verify-database/fix.php b/app/admin/verify-database/fix.php new file mode 100755 index 000000000..ccfa0a551 --- /dev/null +++ b/app/admin/verify-database/fix.php @@ -0,0 +1,41 @@ +check_user_session(); + +# admin user is required +$User->is_admin(true); + +/* verifications */ +if(!isset($_POST['tableid']) || strlen(@$_POST['tableid'])<1 ) { + $Result->show("danger", _("Wrong parameters"), true); +} +else { + //fix table + if($_POST['type'] == "table") { + $Tools->fix_table($_POST['tableid']); + $Result->show("success", _('Table fixed')); + } + //fix field + elseif($_POST['type'] == "field") { + $Tools->fix_field($_POST['tableid'], $_POST['fieldid']); + $Result->show("success", _('Field fixed')); + } + else { + $Result->show("danger", _("Wrong parameters"), true); + } +} +?> \ No newline at end of file diff --git a/app/admin/verify-database/index.php b/app/admin/verify-database/index.php new file mode 100755 index 000000000..147369a06 --- /dev/null +++ b/app/admin/verify-database/index.php @@ -0,0 +1,67 @@ +is_admin(true); + +# title +print "

    "._('Database structure verification').'


    '; + +# check for possible errors +if(sizeof($errors = $Tools->verify_database())>0) { + + //tables + if (isset($errors['tableError'])) { + print '
    '. "\n"; + print ''._('Missing table').'s:'. "\n"; + print '
      '. "\n"; + + foreach ($errors['tableError'] as $table) { + print '
    • '; + print $table." "; + //get fix + if(!$fix = $Tools->get_table_fix($table)) { + $Result->show("danger", _("Cannot get fix for table")." $table!", true); + } else { + print ""._("Fix table").""; + print ""; + } + print '
    • '. "\n"; + } + + print '
    '. "\n"; + print '
    '. "\n"; + } + + //fields + if (isset($errors['fieldError'])) { + print '
    '. "\n"; + print ''._('Missing fields').':'. "\n"; + print '
      '. "\n"; + + foreach ($errors['fieldError'] as $table=>$field) { + print '
    • '; + print 'Table `'. $table .'`: missing field `'. $field .'`;'; + //get fix + if(!$fix = $Tools->get_field_fix($table, $field)) { + $Result->show("danger", _("Cannot get fix for table field ")." `$table` `$field`!", true); + } else { + print ""._("Fix field").""; + print ""; + } + print '
    • '. "\n"; + } + + print '
    '. "\n"; + print '
    '. "\n"; + } + + +} +else { + $Result->show("success alert-absolute", _('All tables and fields are installed properly'), false); +} +?> \ No newline at end of file diff --git a/app/admin/version-check/index.php b/app/admin/version-check/index.php new file mode 100755 index 000000000..7e3433d87 --- /dev/null +++ b/app/admin/version-check/index.php @@ -0,0 +1,19 @@ +check_user_session(); + +# get latest version */ +if(!$version = $Tools->check_latest_phpipam_version()) { $Result->show("danger", _("Version check failed").'!', true); } + + +print "

    phpIPAM version check


    "; + +//print result +if($User->settings->version == $version) { $Result->show("success alert-absolute", _('Latest version').' ('. $User->settings->version .') '._('already installed').'!', false); } +else if ($User->settings->version > $version) { $Result->show("success alert-absolute", _('Development version').' ('. $User->settings->version .') '._('installed! Latest production version is').' '. $version, false);} +else { $Result->show("danger alert-absolute", _('New version of phpipam available').':
    '._('Installed version').': '.$User->settings->version."
    "._('Available version').': '. $version."

    "._('You can download new version').' '._('here').'.', false); } +?> \ No newline at end of file diff --git a/app/admin/vlans/edit-domain-result.php b/app/admin/vlans/edit-domain-result.php new file mode 100644 index 000000000..89e77be88 --- /dev/null +++ b/app/admin/vlans/edit-domain-result.php @@ -0,0 +1,61 @@ +check_user_session(); + + +# we cannot delete default domain +if(@$_POST['id']==1 && $_POST['action']=="delete") { $Result->show("danger", _("Default domain cannot be deleted"), true); } +// ID must be numeric +if($_POST['action']!="add" && !is_numeric($_POST['id'])) { $Result->show("danger", _("Invalid ID"), true); } +// Hostname must be present +if(@$_POST['name'] == "") { $Result->show("danger", _('Name is mandatory').'!', true); } + + +// set sections +if(@$_POST['id']!=1) { + foreach($_POST as $key=>$line) { + if (strlen(strstr($key,"section-"))>0) { + $key2 = str_replace("section-", "", $key); + $temp[] = $key2; + unset($_POST[$key]); + } + } + # glue sections together + $_POST['permissions'] = sizeof($temp)>0 ? implode(";", $temp) : null; +} +else { + $_POST['permissions'] = ""; +} + +# set update values +$values = array("id"=>@$_POST['id'], + "name"=>@$_POST['name'], + "description"=>@$_POST['description'], + "permissions"=>@$_POST['permissions'] + ); + +# update domain +if(!$Admin->object_modify("vlanDomains", $_POST['action'], "id", $values)) {} +else { $Result->show("success", _("Domain $_POST[action] successfull").'!', false); } + +# if delete move all vlans to default domain! +if($_POST['action']=="delete") { + $Admin->update_object_references ("vlans", "domainId", $_POST['id'], 1); +} + +?> \ No newline at end of file diff --git a/app/admin/vlans/edit-domain.php b/app/admin/vlans/edit-domain.php new file mode 100644 index 000000000..cf41b9eb0 --- /dev/null +++ b/app/admin/vlans/edit-domain.php @@ -0,0 +1,102 @@ +check_user_session(); + +# fetch vlan details +$l2_domain = $Admin->fetch_object ("vlanDomains", "id", @$_POST['id']); +$l2_domain = $l2_domain!==false ? (array) $l2_domain : array(); + +# set readonly flag +$readonly = $_POST['action']=="delete" ? "readonly" : ""; + +?> + + + + + +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    + > +
    + > + + +

    : + fetch_all_sections(); + # reformat domains sections to array + $domain_sections = explode(";", @$l2_domain['permissions']); + $domain_sections = is_array($domain_sections) ? $domain_sections : array(); + // loop + foreach($Sections->sections as $section) { + if(in_array($section->id, @$domain_sections) || @$l2_domain['id']=="1") { print '
    '. $section->name .'
    '. "\n"; } + else { print '
    '. $section->name .'
    '. "\n"; } + } + ?> +
    +
    + + show("warning", _('Warning').': '._('removing vlan domain will move all belonging vlans to default domain')."!", false); } + ?> +
    + + + +
    +
    + + +
    + + +
    +
    \ No newline at end of file diff --git a/app/admin/vlans/edit-result.php b/app/admin/vlans/edit-result.php new file mode 100755 index 000000000..4d839d613 --- /dev/null +++ b/app/admin/vlans/edit-result.php @@ -0,0 +1,71 @@ +check_user_session(); + +# fetch custom fields +$custom = $Tools->fetch_custom_fields('vlans'); + +//if it already exist die +if($User->settings->vlanDuplicate==0 && $_POST['action']=="add") { + $check_vlan = $Admin->fetch_multiple_objects ("vlans", "domainId", $_POST['domainId'], "vlanId"); + if($check_vlan!==false) { + foreach($check_vlan as $v) { + if($v->number == $_POST['number']) { + { $Result->show("danger", _("VLAN already exists"), true); } + } + } + } +} + +//if number too high +if($_POST['number']>$User->settings->vlanMax && $_POST['action']!="delete") { $Result->show("danger", _('Highest possible VLAN number is ').$settings['vlanMax'].'!', true); } +if($_POST['action']=="add") { + if($_POST['number']<0) { $Result->show("danger", _('VLAN number cannot be negative').'!', true); } + elseif(!is_numeric($_POST['number'])) { $Result->show("danger", _('Not number').'!', true); } +} +if(strlen($_POST['name'])==0) { $Result->show("danger", _('Name is required').'!', true); } + + +# formulate update query +$values = array("vlanId"=>@$_POST['vlanId'], + "number"=>$_POST['number'], + "name"=>$_POST['name'], + "description"=>@$_POST['description'], + "domainId"=>$_POST['domainId'] + ); +# append custom +if(sizeof($custom) > 0) { + foreach($custom as $myField) { + # replace possible ___ back to spaces! + $myField['nameTest'] = str_replace(" ", "___", $myField['name']); + if(isset($_POST[$myField['nameTest']])) { $values[$myField['name']] = @$_POST[$myField['nameTest']];} + } +} + + +# update +if(!$Admin->object_modify("vlans", $_POST['action'], "vlanId", $values)) { $Result->show("danger", _("Failed to $_POST[action] VLAN").'!', true); } +else { $Result->show("success", _("VLAN $_POST[action] successfull").'!', false); } + +# remove all references if delete +if($_POST['action']=="delete") { $Admin->remove_object_references ("subnets", "vlanId", $_POST['vlanId']); } + +# print value for on the fly +if($_POST['action']=="add") { print ''; } + +?> \ No newline at end of file diff --git a/app/admin/vlans/edit.php b/app/admin/vlans/edit.php new file mode 100755 index 000000000..1f4eb9fed --- /dev/null +++ b/app/admin/vlans/edit.php @@ -0,0 +1,204 @@ +check_user_session(); + +# fetch vlan details +$vlan = $Admin->fetch_object ("vlans", "vlanId", @$_POST['vlanId']); +$vlan = $vlan!==false ? (array) $vlan : array(); +# fetch custom fields +$custom = $Tools->fetch_custom_fields('vlans'); + +# set readonly flag +$readonly = $_POST['action']=="delete" ? "readonly" : ""; + +# set form name! +if(isset($_POST['fromSubnet'])) { $formId = "vlanManagementEditFromSubnet"; } +else { $formId = "vlanManagementEdit"; } + +# domain +if(!isset($_POST['domain'])) { $_POST['domain']=1; } + +# fetch l2 domain +if($_POST['action']=="add") { + $vlan_domain = $Admin->fetch_object("vlanDomains", "id", $_POST['domain']); +} else { + $vlan_domain = $Admin->fetch_object("vlanDomains", "id", $vlan['domainId']); +} +if($vlan_domain===false) { $Result->show("danger", _("Invalid ID"), true, true); } +?> + + + + + +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0) { + + print ''; + print ' '; + print ''; + + foreach($custom as $field) { + + # replace spaces + $field['nameNew'] = str_replace(" ", "___", $field['name']); + + # required + if($field['Null']=="NO") { $required = "*"; } + else { $required = ""; } + + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + } + ?> + +
    name." (".$vlan_domain->description.")"; ?>

    + > +
    + > +
    + > + + + +

    '. $field['name'] .' '.$required.''. "\n"; + + //set type + if(substr($field['type'], 0,3) == "set") { + //parse values + $tmp = explode(",", str_replace(array("set(", ")", "'"), "", $field['type'])); + //null + if($field['Null']!="NO") { array_unshift($tmp, ""); } + + print ""; + } + //date and time picker + elseif($field['type'] == "date" || $field['type'] == "datetime") { + // just for first + if($timeP==0) { + print ''; + print ''; + print ''; + } + $timeP++; + + //set size + if($field['type'] == "date") { $size = 10; $class='datepicker'; $format = "yyyy-MM-dd"; } + else { $size = 19; $class='datetimepicker'; $format = "yyyy-MM-dd"; } + + //field + if(!isset($vlan[$field['name']])) { print ' '. "\n"; } + else { print ' '. "\n"; } + } + //boolean + elseif($field['type'] == "tinyint(1)") { + print ""; + } + //text + elseif($field['type'] == "text") { + print ' '. "\n"; + } + //default - input field + else { + print ' '. "\n"; + } + + print '
    +
    + + show("warning", _('Warning').': '._('removing VLAN will also remove VLAN reference from belonging subnets')."!", false); } + ?> +
    + + + +
    +
    + + +
    + + +
    +
    \ No newline at end of file diff --git a/app/admin/vlans/index.php b/app/admin/vlans/index.php new file mode 100755 index 000000000..6b0dd90a0 --- /dev/null +++ b/app/admin/vlans/index.php @@ -0,0 +1,27 @@ +check_user_session(); + +# fetch all l2 domains +$vlan_domains = $Admin->fetch_all_objects("vlanDomains", "id"); + +# set default domain +if(sizeof($vlan_domains)==1) { $_GET['subnetId'] = 1; } +?> + + +

    +

    + +1 && !isset($_GET['subnetId'])) { include("print-domains.php"); } +# only 1 domain, print vlans +else { include("print-domain-vlans.php"); } + +?> diff --git a/app/admin/vlans/move-vlan-result.php b/app/admin/vlans/move-vlan-result.php new file mode 100644 index 000000000..925e65a56 --- /dev/null +++ b/app/admin/vlans/move-vlan-result.php @@ -0,0 +1,51 @@ +check_user_session(); + +// checks +if(!is_numeric($_POST['newDomainId'])) $Result->show("danger", _("Invalid ID"), true); +if(!is_numeric($_POST['vlanId'])) $Result->show("danger", _("Invalid ID"), true); + +// verify that new exists +$vlan_domain = $Admin->fetch_object("vlanDomains", "id", $_POST['newDomainId']); +if($vlan_domain===false) { $Result->show("danger", _("Invalid ID"), true); } + +//fetch vlan +$vlan = $Admin->fetch_object("vlans", "vlanId", $_POST['vlanId']); +if($vlan===false) { $Result->show("danger", _("Invalid ID"), true); } + +// check that it is not already set ! +if($User->settings->vlanDuplicate==0) { + $check_vlan = $Admin->fetch_multiple_objects ("vlans", "domainId", $vlan_domain->id, "vlanId"); + if($check_vlan!==false) { + foreach($check_vlan as $v) { + if($v->number == $vlan->number) { + { $Result->show("danger", _("VLAN already exists"), true); } + } + } + } +} + +# formulate update query +$values = array("vlanId"=>@$_POST['vlanId'], + "domainId"=>$vlan_domain->id + ); +# update +if(!$Admin->object_modify("vlans", "edit", "vlanId", $values)) { $Result->show("danger", _("Failed to move VLAN to new domain").'!', true); } +else { $Result->show("success", _("VLAN moved to new domain successfully").'!', false); } +?> \ No newline at end of file diff --git a/app/admin/vlans/move-vlan.php b/app/admin/vlans/move-vlan.php new file mode 100644 index 000000000..9906deeb1 --- /dev/null +++ b/app/admin/vlans/move-vlan.php @@ -0,0 +1,92 @@ +check_user_session(); + + +# fetch vlan details +$vlan = $Admin->fetch_object ("vlans", "vlanId", @$_POST['vlanId']); +if($vlan===false) { $Result->show("danger", _("Invalid ID"), true, true); } + +# fetch current domain +$vlan_domain = $Admin->fetch_object("vlanDomains", "id", $vlan->domainId); +if($vlan_domain===false) { $Result->show("danger", _("Invalid ID"), true, true); } + +# fetch all l2 domains +$vlan_domains = $Admin->fetch_all_objects("vlanDomains", "id"); +?> + + + + +
    + + +
    +
    + + + + + + + + + + + + + + + + +
    name." (".$vlan_domain->description.")"; ?>

    + + +
    +
    + + show("warning", _("No domains available!"), false); ?> + +
    + + +
    +
    + + 0) { ?> + + +
    + + +
    +
    \ No newline at end of file diff --git a/app/admin/vlans/print-domain-vlans.php b/app/admin/vlans/print-domain-vlans.php new file mode 100644 index 000000000..432854488 --- /dev/null +++ b/app/admin/vlans/print-domain-vlans.php @@ -0,0 +1,147 @@ +show("danger", _("Invalid ID"), true); } + +# fetch l2 domain +$vlan_domain = $Admin->fetch_object("vlanDomains", "id", $_GET['subnetId']); +if($vlan_domain===false) { $Result->show("danger", _("Invalid ID"), true); } +# fetch all VLANs in domain +$vlans = $Admin->fetch_multiple_objects("vlans", "domainId", $vlan_domain->id, "number"); + +# fetch custom fields +$custom = $Tools->fetch_custom_fields('vlans'); + +# set hidden fields +$hidden_fields = json_decode($User->settings->hiddenCustomFields, true); +$hidden_fields = is_array(@$hidden_fields['vlans']) ? $hidden_fields['vlans'] : array(); + +# set size of custom fields +$custom_size = sizeof($custom) - sizeof($hidden_fields); +?> + + + +
    + 1) { ?> + "> + + + + + +
    + +

    name; ?>

    +
    + +show("info alert-absolute", _('No VLANs configured')."!", false); } +else { +?> + + + + + + + 0) { + foreach($custom as $field) { + if(!in_array($field['name'], $hidden_fields)) { + print ""; + } + } + } + ?> + + + + + user->hideFreeRange!="1") { + if($m==0 && $vlan['number']!=1) { + print ""; + print ""; + print ""; + print ""; + } + # show free vlans - before vlan + if($m>0) { + if( (($vlans[$m]->number)-($vlans[$m-1]->number)-1) > 0 ) { + print ""; + print ""; + # only 1? + if( (($vlans[$m]->number)-($vlans[$m-1]->number)-1) ==1 ) { + print ""; + } else { + print ""; + } + print ""; + } + } + } + + //print details + print ''. "\n"; + + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + + if(sizeof($custom) > 0) { + foreach($custom as $field) { + if(!in_array($field['name'], $hidden_fields)) { + + print ""; + } + } + } + + print " "; + print ''. "\n"; + + # show free vlans - last + if($User->user->hideFreeRange!="1") { + if($m==(sizeof($vlans)-1)) { + if($User->settings->vlanMax>$vlan['number']) + print ""; + print ""; + print ""; + print ""; + } + } + + $m++; + + } +} +?> +
    "._('VLAN')."1 - ".($vlan['number']-1)." (".($vlan['number']-1)." "._('free').")
    "._('VLAN')." ".($vlan['number']-1)." (".(($vlans[$m]->number)-($vlans[$m-1]->number)-1)." "._('free').") "._('VLAN')." ".($vlans[$m-1]->number+1)." - ".($vlan['number']-1)." (".(($vlans[$m]->number)-($vlans[$m-1]->number)-1)." "._('free').")
    '. $vlan['name'] .''. $vlan['number'] .''. $vlan['description'] .'"; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    "._('VLAN')." ".($vlan['number']+1)." - ".$User->settings->vlanMax." (".(($User->settings->vlanMax)-($vlan['number']))." "._('free').")
    \ No newline at end of file diff --git a/app/admin/vlans/print-domains.php b/app/admin/vlans/print-domains.php new file mode 100644 index 000000000..580d5b731 --- /dev/null +++ b/app/admin/vlans/print-domains.php @@ -0,0 +1,59 @@ + +
    + +
    + + +

    +
    + + + + + + + + + + + +id==1) { + $sections = "All sections"; + } + elseif(strlen(@$domain->permissions==0)) { + $sections = "None"; + } + else { + //explode + unset($sec); + $sections_tmp = explode(";", $domain->permissions); + foreach($sections_tmp as $t) { + //fetch section + $tmp_section = $Sections->fetch_section(null, $t); + $sec[] = $tmp_section->name; + } + //implode + $sections = implode("
    ", $sec); + } + + // print + print ""; + print " "; + print " "; + print " "; + print " "; + // actions + print " "; + + print ""; +} +?> +
    $domain->name$domain->description$sectionsShow VLANs"; + print "
    "; + print " "; + print " "; + print "
    "; + print "
    \ No newline at end of file diff --git a/app/admin/vrfs/edit-result.php b/app/admin/vrfs/edit-result.php new file mode 100755 index 000000000..caa282adb --- /dev/null +++ b/app/admin/vrfs/edit-result.php @@ -0,0 +1,36 @@ +check_user_session(); + + +# Hostname must be present! +if($_POST['name'] == "") { $Result->show("danger", _("Name is mandatory"), true); } + + +# set update array +$values = array("vrfId"=>@$_POST['vrfId'], + "name"=>$_POST['name'], + "description"=>$_POST['description'] + ); +# update +if(!$Admin->object_modify("vrf", $_POST['action'], "vrfId", $values)) { $Result->show("danger", _("Failed to $_POST[action] VRF").'!', true); } +else { $Result->show("success", _("VRF $_POST[action] successfull").'!', false); } + + +# remove all references if delete +if($_POST['action']=="delete") { $Admin->remove_object_references ("subnets", "vrfId", $_POST['vrfId']); } +?> \ No newline at end of file diff --git a/app/admin/vrfs/edit.php b/app/admin/vrfs/edit.php new file mode 100755 index 000000000..0f42f1cd7 --- /dev/null +++ b/app/admin/vrfs/edit.php @@ -0,0 +1,86 @@ +check_user_session(); + +# get VRF +if($_POST['action']!="add") { + $vrf = $Admin->fetch_object ("vrf", "vrfId", $_POST['vrfId']); + $vrf!==false ? : $Result->show("danger", _("Invalid ID"), true, true); + $vrf = (array) $vrf; +} + +# disable edit on delete +$readonly = $_POST['action']=="delete" ? "readonly" : ""; +?> + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + > +
    + > +
    + '. "\n";} + ?> + + > +
    +
    + + show("warning", ""._('Warning').": "._("removing VRF will also remove VRF reference from belonging subnets!"), false);} + ?> +
    + + + +
    +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/app/admin/vrfs/index.php b/app/admin/vrfs/index.php new file mode 100755 index 000000000..bb26aab4c --- /dev/null +++ b/app/admin/vrfs/index.php @@ -0,0 +1,58 @@ +check_user_session(); + +# fetch all vrfs +$all_vrfs = $Admin->fetch_all_objects("vrf", "vrfId"); +?> + +

    +

    + + + + +show("danger", _("No VRFs configured")."!", true);} +else { + print ''. "\n"; + + # headers + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + + # loop + foreach ($all_vrfs as $vrf) { + //cast + $vrf = (array) $vrf; + + //print details + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print " "; + print ''. "\n"; + } + print '
    '._('Name').''._('RD').''._('Description').'
    '. $vrf['name'] .''. $vrf['rd'] .''. $vrf['description'] .'"; + print "
    "; + print " "; + print " "; + print "
    "; + print "
    '. "\n"; +} +?> + + +
    \ No newline at end of file diff --git a/app/admin/widgets/edit-result.php b/app/admin/widgets/edit-result.php new file mode 100755 index 000000000..8a94f12b8 --- /dev/null +++ b/app/admin/widgets/edit-result.php @@ -0,0 +1,45 @@ +check_user_session(); + + +# ID must be numeric */ +if($_POST['action']!="add") { + if(!is_numeric($_POST['wid'])) { $Result->show("danger", _("Invalid ID"), true); } +} +# Title and path must be present! +if($_POST['action']!="delete") { +if(strlen($_POST['wtitle'])==0 || strlen($_POST['wfile'])==0) { $Result->show("danger", _("Filename and title are mandatory")."!", true); } +} + +# Remove .php form wfile if it is present +$_POST['wfile'] = str_replace(".php","",trim(@$_POST['wfile'])); + +# set update values +$values = array("wid"=>@$_POST['wid'], + "wtitle"=>$_POST['wtitle'], + "wdescription"=>@$_POST['wdescription'], + "wfile"=>$_POST['wfile'], + "wadminonly"=>$_POST['wadminonly'], + "wactive"=>$_POST['wactive'], + "whref"=>$_POST['whref'], + "wsize"=>$_POST['wsize'] + ); +# update +if(!$Admin->object_modify("widgets", $_POST['action'], "wid", $values)) { $Result->show("danger", _("Widget $_POST[action] error")."!", true); } +else { $Result->show("success", _("Widget $_POST[action] success")."!", true); } +?> \ No newline at end of file diff --git a/app/admin/widgets/edit.php b/app/admin/widgets/edit.php new file mode 100755 index 000000000..c1a385951 --- /dev/null +++ b/app/admin/widgets/edit.php @@ -0,0 +1,124 @@ +check_user_session(); + +# fetch widget +if($_POST['action']!="add") { + $w = $Admin->fetch_object ("widgets", "wid", $_POST['wid']); + $w!==false ? : $Result->show("danger", _("Invalid ID"), true, true); + $w = (array) $w; +} +?> + + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    >
    + > + + + +
    >
    + +
    + +
    + +
    + +
    +
    + +
    + + + +
    +
    + + +
    + + +
    +
    diff --git a/app/admin/widgets/index.php b/app/admin/widgets/index.php new file mode 100755 index 000000000..686c97daa --- /dev/null +++ b/app/admin/widgets/index.php @@ -0,0 +1,82 @@ +check_user_session(); + +# fetch all widgets +$widgets = $Admin->fetch_all_objects("widgets", "wid"); +?> + + +

    +
    +"; +print _('You can manage widgets here').".
    "; +print "

    "; +?> + + + + + + + + + + + + "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # print + foreach($widgets as $w) { + # cast + $w = (array) $w; + + # verify validity + $valid = $Tools->verify_widget($w['wfile']); + + if($valid) { $vPrint = ""._('Valid').""; } + else { $vPrint = ""._('Invalid').""; } + + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + } + } + ?> + + +
    "._('Title').""._('Description').""._('File').""._('Admin').""._('Active').""._('Validity')."
    "._($w['wtitle']).""._($w['wdescription'])."$w[wfile].php"._($w['wadminonly']).""._($w['wactive'])."$vPrint"; + print "
    "; + print " "; + print " "; + print "
    "; + print "
    + +
    +
    + +
    diff --git a/app/dashboard/index.php b/app/dashboard/index.php new file mode 100755 index 000000000..4e631bdbd --- /dev/null +++ b/app/dashboard/index.php @@ -0,0 +1,169 @@ +check_user_session(); + +?> + + + + + + + + + + + + + + +
    + +
    + +fetch_widgets ($User->isadmin, false); +$widgets = (array) $widgets; + +# show user-selected widgets +$uwidgets = array_filter(explode(";",$User->user->widgets)); + +# split widgets to rows (chunks) +$currSize = 0; //to calculate size +$m=0; //to calculate chunk index + +foreach($uwidgets as $uk=>$uv) { + //get fetails + $wdet = (array) $widgets[$uv]; + if(strlen($wdet['wsize'])==0) { $wsize = 6; } + else { $wsize = $wdet['wsize']; } + + //calculate current size + $currSize = $currSize + $wsize; + + //ok, we have sizes, we need to split them into chunks of 12 + if($currSize > 12) { + $m++; //new index + $currSize = $wsize; //reset size + } + + //add to array + $uwidgetschunk[$m][] = $uv; +} + + +# print +print ""; + +if(sizeof($uwidgets)>1) { + + print '
    '; + + foreach($uwidgetschunk as $w) { + # print itams in a row + foreach($w as $c) { + + /* print items */ + $wdet = (array) $widgets[$c]; + if(array_key_exists($c, $widgets)) { + //reset size if not set + if(strlen($wdet['wsize'])==0) { $wdet['wsize'] = 6; } + + print "
    "; + print "
    "; + // href? + if($wdet['whref']=="yes") { print "

    "._($wdet['wtitle'])."

    "; } + else { print "

    "._($wdet['wtitle'])."

    "; } + print "
    "; + print "
    "._('Loading statistics')."
    "; + print "
    "; + print "
    "; + print "
    "; + + } + # invalid widget + else { + print "
    "; + print "
    "; + print "

    Invalid widget $c

    "; + print "
    "; + print "
    "; + } + + } + } + print "
    "; +} +# empty +else { + print "
    "._('No widgets selected')."!
    "._('Please select widgets to be displayed on dashboard on user menu page')."!
    "; +} +?> +
    \ No newline at end of file diff --git a/app/dashboard/widget-popup.php b/app/dashboard/widget-popup.php new file mode 100755 index 000000000..1db9039aa --- /dev/null +++ b/app/dashboard/widget-popup.php @@ -0,0 +1,56 @@ +check_user_session (false); + +# user widgets form database +$uwidgets = explode(";",$User->user->widgets); //selected +$uwidgets = array_filter((array) $uwidgets); + +# fetch all widgets +$widgets = $Tools->fetch_widgets ($User->isadmin, false); +$widgets = (array) $widgets; + +?> + + +
    + + +
    + "; + # print widghets that are not yet selected + $m = 0; + foreach($widgets as $k=>$w) { + if(!in_array($k, $uwidgets)) { + $wtmp = (array) $widgets[$k]; + # size fix + if(strlen($wtmp['wsize'])==0) { $wtmp['wsize']=6; } + print "
  • "; + print " "._($wtmp['wtitle']); + print "
    "._($wtmp['wdescription'])."
    "; + print "
  • "; + $m++; + } + } + print ""; + + # print empty + if($m==0) { $Result->show("info", _("All available widgets are already on dashboard"), false); } + ?> +
    + + +
    + +
    \ No newline at end of file diff --git a/app/dashboard/widgets/access_logs.php b/app/dashboard/widgets/access_logs.php new file mode 100755 index 000000000..b9d7b4a96 --- /dev/null +++ b/app/dashboard/widgets/access_logs.php @@ -0,0 +1,59 @@ +check_user_session (); + +# if direct request that redirect to tools page +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + header("Location: ".create_link("tools","logs")); +} + +# print last 5 access logs +$logs = $Tools->fetch_logs(5, NULL, NULL, NULL, 0, 0, 0); + +print ""; + +# headers +print ""; +print " "; +print " "; +print " "; +print " "; +print ""; + +# logs +foreach($logs as $log) { + # cast + $log = (array) $log; + # reformat severity + if($log['severity'] == 0) { $log['severityText'] = _("Info"); } + else if($log['severity'] == 1) { $log['severityText'] = _("Warn"); } + else if($log['severity'] == 2) { $log['severityText'] = _("Err"); } + + print ""; + print " "; + print " "; + print " "; + print " "; + + print ""; +} + +print "
    "._('Severity').""._('Command').""._('Date').""._('Username')."
    $log[severityText]$log[command]$log[date]$log[username]
    "; + +# print if none +if(sizeof($logs) == 0) { + print "
    "; + print "

    "._("No logs available")."

    "; + print "
    "; +} +?> \ No newline at end of file diff --git a/app/dashboard/widgets/changelog.php b/app/dashboard/widgets/changelog.php new file mode 100755 index 000000000..cfce0a56e --- /dev/null +++ b/app/dashboard/widgets/changelog.php @@ -0,0 +1,103 @@ +check_user_session (); + +# if direct request that redirect to tools page +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + header("Location: ".create_link("tools","changelog")); +} + +/* get logs */ +$clogs = $Tools->fetch_all_changelogs (false, "", 50); + + +if(sizeof($clogs)==0) { + print "
    "; + print "

    "._("No changelogs available")."

    "; + print ""._("No changelog entries are available").""; + print "
    "; +} +# print +else { + + # printout + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print ""; + + # logs + $pc = 0; //print count + foreach($clogs as $l) { + + # cast + $l = (array) $l; + + if($pc < 5) { + # permissions + if($l['ctype']=="subnet") { $permission = $Subnets->check_permission ($User->user, $l['tid']); } + elseif($l['ctype']=="ip_addr") { $permission = $Subnets->check_permission ($User->user, $l['subnetId']); } + elseif($l['ctype']=="section") { $permission = $Sections->check_permission ($User->user, $l['sectionId']); } + else { $permission = 0; } + + # if 0 die + if($permission > 0) { + # format diff + $l['cdiff'] = str_replace("\n", "
    ", $l['cdiff']); + + # format type + switch($l['ctype']) { + case "ip_addr": { $l['ctype'] = "IP address"; break; } + case "subnet": if($l['isFolder']==1) { $l['ctype'] = "Folder"; } + else { $l['ctype'] = "Subnet"; } + break; + + case "section": { $l['ctype'] = "Section"; break; } + } + + print ""; + print " "; + + # subnet, section or ip address + if($l['ctype']=="IP address") { + print " "; + } + elseif($l['ctype']=="Subnet") { + print " "; + } + elseif($l['ctype']=="Folder") { + print " "; + } + + print " "; + print " "; + print ""; + + $pc++; + } + } + } + + print "
    "._('User').""._('Object').""._('Date').""._('Change')."
    $l[real_name]".$Subnets->transform_address ($l['ip_addr'], "dotted")."".$Subnets->transform_address ($l['ip_addr'], "dotted")."/$l[mask]$l[sDescription]$l[cdate]$l[cdiff]
    "; +} +?> \ No newline at end of file diff --git a/app/dashboard/widgets/error_logs.php b/app/dashboard/widgets/error_logs.php new file mode 100755 index 000000000..0f4827a6a --- /dev/null +++ b/app/dashboard/widgets/error_logs.php @@ -0,0 +1,59 @@ +check_user_session (); + +# if direct request that redirect to tools page +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + header("Location: ".create_link("tools","logs")); +} + +# print last 5 access logs +$logs = $Tools->fetch_logs(5, NULL, NULL, NULL, 1, 2, 2); + +print ""; + +# headers +print ""; +print " "; +print " "; +print " "; +print " "; +print ""; + +# logs +foreach($logs as $log) { + # cast + $log = (array) $log; + # reformat severity + if($log['severity'] == 0) { $log['severityText'] = _("Info"); } + else if($log['severity'] == 1) { $log['severityText'] = _("Warn"); } + else if($log['severity'] == 2) { $log['severityText'] = _("Err"); } + + print ""; + print " "; + print " "; + print " "; + print " "; + + print ""; +} + +print "
    "._('Severity').""._('Command').""._('Date').""._('Username')."
    $log[severityText]$log[command]$log[date]$log[username]
    "; + +# print if none +if(sizeof($logs) == 0) { + print "
    "; + print "

    "._("No logs available")."

    "; + print "
    "; +} +?> \ No newline at end of file diff --git a/app/dashboard/widgets/favourite_subnets.php b/app/dashboard/widgets/favourite_subnets.php new file mode 100755 index 000000000..9c5dd1ff8 --- /dev/null +++ b/app/dashboard/widgets/favourite_subnets.php @@ -0,0 +1,104 @@ + + + +check_user_session (); + +# if direct request that redirect to tools page +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + header("Location: ".create_link("tools","favourites")); +} + +# fetch favourite subnets with details +$fsubnets = $User->fetch_favourite_subnets (); + +# print if none +if(!$fsubnets) { + print "
    "; + print "

    "._("No favourite subnets selected")."


    "; + print ""._("You can add subnets to favourites by clicking star icon in subnet details")."!
    "; + print "
    "; +} +else { + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # subnets + foreach($fsubnets as $f) { + + # must be either subnet or folder + if(sizeof($f)>0) { + + print ""; + + if($f['isFolder']==1) { + $master = true; + print " "; + } + else { + //master? + if($Subnets->has_slaves ($f['subnetId'])) { $master = true; print " "; } + else { $master = false; print " "; } + } + print " "; + print " "; + + # get vlan info + if(strlen($f['vlanId'])>0 && $f['vlanId']!=0) { + $vlan = $Tools->fetch_object("vlans", "vlanId", $f['vlanId']); + print " "; + } else { + print " "; + } + + # usage + if(!$master) { + $address_count = $Addresses->count_subnet_addresses ($f['subnetId']); + $subnet_usage = $Subnets->calculate_subnet_usage (gmp_strval($address_count), $f['mask'], $f['subnet']); + } + + # add address + if($master===true || $f['isFolder']==1 || $Subnets->reformat_number($subnet_usage['freehosts'])=="0") { $disabled = "disabled"; } + else { $disabled = ""; } + + # remove + print " "; + + print ""; + } + } + + print "
    "._('Object').""._('Description').""._('Section').""._('VLAN')."
    $f[description]".$Subnets->transform_to_dotted($f['subnet'])."/$f[mask]".$Subnets->transform_to_dotted($f['subnet'])."/$f[mask]$f[description]$f[section]$vlan->number/"; + print "
    "; + print " "; + print " "; + print "
    "; + print "
    "; +} +?> \ No newline at end of file diff --git a/app/dashboard/widgets/index.php b/app/dashboard/widgets/index.php new file mode 100644 index 000000000..91ac63555 --- /dev/null +++ b/app/dashboard/widgets/index.php @@ -0,0 +1,10 @@ +"; include_once('app/error.php'); print ""; } +else { include(dirname(__FILE__) . "/".$_GET['section'].".php"); } + +?> \ No newline at end of file diff --git a/app/dashboard/widgets/ipcalc-result.php b/app/dashboard/widgets/ipcalc-result.php new file mode 100644 index 000000000..ee3cf0dba --- /dev/null +++ b/app/dashboard/widgets/ipcalc-result.php @@ -0,0 +1,56 @@ +check_user_session(); + +# get requested IP addresses in CIDR format +$cidr = $_POST['cidr']; + +# verify input CIDR and die if errors +$errors = $Subnets->verify_cidr_address ($cidr, false); +$errors===true ? : $Result->show("danger", _('Invalid input').': '.$errors,true); + +# fetch all sections +$Sections->fetch_sections(); + +# calculate results +$calc_results = $Tools->calculate_ip_calc_results($cidr); +?> + +

    :

    + + + + + + $line) { + print ''; + print ' '; + print ' '; + print ''; + + $m++; + } + ?> +
    '._("$key").''. $line .'
    + \ No newline at end of file diff --git a/app/dashboard/widgets/ipcalc.php b/app/dashboard/widgets/ipcalc.php new file mode 100644 index 000000000..eb4324799 --- /dev/null +++ b/app/dashboard/widgets/ipcalc.php @@ -0,0 +1,61 @@ + + + + + +check_user_session (); + +# if direct request that redirect to tools page +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + header("Location: ".create_link("tools", "ip-calculator")); +} +?> + +
    + +
    +
    +
    +
    + + + + +
    +
    +
    +
    + + +
    + +
    + +
    \ No newline at end of file diff --git a/app/dashboard/widgets/requests.php b/app/dashboard/widgets/requests.php new file mode 100755 index 000000000..dede507b4 --- /dev/null +++ b/app/dashboard/widgets/requests.php @@ -0,0 +1,68 @@ +check_user_session (); + +# if direct request that redirect to tools page +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + header("Location: ".create_link("administration","manageRequests")); +} + +# fetch all requests +$requests = $Tools->requests_fetch (false); +?> + + + +"; + print ""._("No IP address requests available")."!
    "; + print ""; +} +# print +else { +?> + + + + + + + + + + + + +fetch_subnet ("id", $request['subnetId']); + + print ''. "\n"; + print " "; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } +?> + +
    '. $subnet->ip .'/'. $subnet->mask .' ('. $subnet->description .')'. $request['dns_name'] .''. $request['description'] .''. $request['requester'] .'
    + + \ No newline at end of file diff --git a/app/dashboard/widgets/statistics.php b/app/dashboard/widgets/statistics.php new file mode 100755 index 000000000..e4914a56c --- /dev/null +++ b/app/dashboard/widgets/statistics.php @@ -0,0 +1,56 @@ +check_user_session (); +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    numObjects ("sections"); ?>
    numObjects ("subnets");; ?>
    numObjects ("vlans");; ?>
    count_subnets ("IPv4"); ?>
    count_subnets ("IPv6"); ?>
    numObjects ("users");; ?>
    \ No newline at end of file diff --git a/app/dashboard/widgets/template.php b/app/dashboard/widgets/template.php new file mode 100755 index 000000000..38e4b7f78 --- /dev/null +++ b/app/dashboard/widgets/template.php @@ -0,0 +1,43 @@ +check_user_session (); + +# if direct request that redirect to tools page +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + header("Location: ".create_link("administration","logs")); +} + +/* You can check who requested this, to adjust parameters */ +if($_SERVER['HTTP_X_REQUESTED_WITH']=="XMLHttpRequest") { $dashboard = true; } +else { $dashboard = false; } + +?> + + + + + + \ No newline at end of file diff --git a/app/dashboard/widgets/tools.php b/app/dashboard/widgets/tools.php new file mode 100644 index 000000000..267fc9c04 --- /dev/null +++ b/app/dashboard/widgets/tools.php @@ -0,0 +1,100 @@ + + + + + +check_user_session (); + +# if direct request that redirect to tools page +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + header("Location: ".create_link("tools")); +} + +# set items +# Tools +$tools_menu['Tools'][] = array("show"=>true, "icon"=>"fa-search", "name"=>"Search", "href"=>"search", "description"=>"Search database Addresses, subnets and VLANs"); +$tools_menu['Tools'][] = array("show"=>true, "icon"=>"fa-calculator", "name"=>"IP calculator", "href"=>"ip-calculator","description"=>"IPv4v6 calculator for subnet calculations"); +# Subnets +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-sitemap", "name"=>"Subnets", "href"=>"subnets", "description"=>"Show all subnets"); +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-cloud", "name"=>"VLAN", "href"=>"vlan", "description"=>"Show VLANs and belonging subnets"); +if($User->settings->enableVRF == 1) +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-cloud", "name"=>"VRF", "href"=>"vrf", "description"=>"Show VRFs and belonging networks"); +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-desktop", "name"=>"Devices", "href"=>"devices", "description"=>"Show all configured devices"); +?> + +
    +
    + +$tool) { + # items + foreach($tool as $t) { + # remove unneeded + print "
    "; + print "
    "; + print "
    "; + print "
    "; + print "
    "._($t['name'])."
    "._($t['description'])."
    "; + print "
    "; + print "
    "; + print "
    "; + } + + # clear and break + print "
    "; +} +?> +
    +
    diff --git a/app/dashboard/widgets/top10_hosts_v4.php b/app/dashboard/widgets/top10_hosts_v4.php new file mode 100755 index 000000000..9fc9d5e30 --- /dev/null +++ b/app/dashboard/widgets/top10_hosts_v4.php @@ -0,0 +1,261 @@ +check_user_session (); + +# no errors! +//ini_set('display_errors', 0); + +# set size parameters +$height = 200; +$slimit = 10; //we dont need this, we will recalculate + + +# if direct request include plot JS +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + # get widget details + if(!$widget = $Tools->fetch_widget ("wfile", $_REQUEST['section'])) { $Result->show("danger", _("Invalid widget"), true); } + # reset size and limit + $height = 350; + $slimit = 20; + # include flot JS + print ''; + print ''; + print ''; + # and print title + print "
    "; + print "

    $widget[wtitle]


    "; + print "
    "; +} + +# get subnets statistic +$type = 'IPv4'; +$top_subnets = $Tools->fetch_top_subnets($type, 1000000, false); + + +/* detect duplicates */ +$unique = array(); +$numbering = array(); +$m = 0; +foreach($top_subnets as $subnet) { + # cast + $subnet = (array) $subnet; + # check if already in array + if(in_array($subnet['description'], $unique)) { + @$numbering[$subnet['description']]++; + $top_subnets[$m]->description = $subnet['description'].' #'.$numbering[$subnet['description']]; + } + $unique[] = $top_subnets[$m]->description; + $m++; +} + +# only print if some hosts exist +if(sizeof($top_subnets)>0) { +?> + + +"; + + print "
    "; + print "

    "._("No $type hosts configured")."

    "; + print ""._("Add some hosts to subnets to show graph of used hosts per subnet").""; + print "
    "; +} +?> + + +"; + + print "
    "; + print "

    "._("No $type hosts configured")."

    "; + print ""._("Add some hosts to subnets to show graph of used hosts per subnet").""; + print "
    "; + + #remove loading + ?> + + + +
    +

    +
    diff --git a/app/dashboard/widgets/top10_hosts_v6.php b/app/dashboard/widgets/top10_hosts_v6.php new file mode 100755 index 000000000..9c7e1bbc3 --- /dev/null +++ b/app/dashboard/widgets/top10_hosts_v6.php @@ -0,0 +1,259 @@ +check_user_session (); + +# no errors! +//ini_set('display_errors', 0); + +# set size parameters +$height = 200; +$slimit = 10; //we dont need this, we will recalculate + +# if direct request include plot JS +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + # get widget details + if(!$widget = $Tools->fetch_widget ("wfile", $_REQUEST['section'])) { $Result->show("danger", _("Invalid widget"), true); } + # reset size and limit + $height = 350; + $slimit = 20; + # include flot JS + print ''; + print ''; + print ''; + # and print title + print "
    "; + print "

    $widget[wtitle]


    "; + print "
    "; +} + +# get subnets statistic +$type = 'IPv6'; +$top_subnets = $Tools->fetch_top_subnets($type, 1000000, false); + +/* detect duplicates */ +$unique = array(); +$numbering = array(); +$m = 0; +foreach($top_subnets as $subnet) { + # cast + $subnet = (array) $subnet; + # check if already in array + if(in_array($subnet['description'], $unique)) { + $numbering[$subnet['description']]++; + $top_subnets[$m]->description = $subnet['description'].' #'.$numbering[$subnet['description']]; + } + $unique[] = $top_subnets[$m]->description; + $m++; +} + +# only print if some hosts exist +if(sizeof($top_subnets)>0) { +?> + + +"; + + print "
    "; + print "

    "._("No $type hosts configured")."

    "; + print ""._("Add some hosts to subnets to show graph of used hosts per subnet").""; + print "
    "; +} +?> + + +"; + + print "
    "; + print "

    "._("No $type hosts configured")."

    "; + print ""._("Add some hosts to subnets to show graph of used hosts per subnet").""; + print "
    "; + + #remove loading + ?> + + + +
    +

    +
    diff --git a/app/dashboard/widgets/top10_percentage.php b/app/dashboard/widgets/top10_percentage.php new file mode 100755 index 000000000..954f2349a --- /dev/null +++ b/app/dashboard/widgets/top10_percentage.php @@ -0,0 +1,262 @@ +check_user_session (); + +# set size parameters +$height = 200; +$slimit = 10; + +# if direct request include plot JS +if($_SERVER['HTTP_X_REQUESTED_WITH']!="XMLHttpRequest") { + # get widget details + if(!$widget = $Tools->fetch_widget ("wfile", $_REQUEST['section'])) { $Result->show("danger", _("Invalid widget"), true); } + # reset size and limit + $height = 350; + $slimit = 20; + # include flot JS + print ''; + print ''; + print ''; + # and print title + print "
    "; + print "

    $widget[wtitle]


    "; + print "
    "; +} + +$type = "IPv4"; + +# get subnets statistic +$top_subnets = $Tools->fetch_top_subnets($type, 1000000, true); + +/* detect duplicates */ +$unique = array(); +$numbering = array(); +$m = 0; +foreach($top_subnets as $subnet) { + # cast + $subnet = (array) $subnet; + # check if already in array + if(in_array($subnet['description'], $unique)) { + $numbering[$subnet['description']]++; + $top_subnets[$m]->description = $subnet['description'].' #'.$numbering[$subnet['description']]; + } + $unique[] = $top_subnets[$m]->description; + $m++; +} + +# set maximum for graph +$max = str_replace(",", ".", $top_subnets[0]->percentage); + + +# only print if some hosts exist +if(sizeof($top_subnets)>0) { +?> + + +"; + + print "
    "; + print "

    "._("No $type hosts configured")."

    "; + print ""._("Add some hosts to subnets to show graph of used hosts per subnet").""; + print "
    "; +} +?> + +"; + print "
    "; + print "

    "._("No $type hosts configured")."

    "; + print ""._("Add some hosts to subnets to calculate usage percentage").""; + print "
    "; + + #remove loading + ?> + + + + +
    +

    +
    diff --git a/app/error.php b/app/error.php new file mode 100755 index 000000000..6491cc96d --- /dev/null +++ b/app/error.php @@ -0,0 +1,69 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Switch Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Unordered Collection', + 426 => 'Upgrade Required', + 449 => 'Retry With', + 450 => 'Blocked by Windows Parental Controls', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 509 => 'Bandwidth Limit Exceeded', + 510 => 'Not Extended' +); +?> + +
    +
    +
    +


    +
    + : : +
    +
    +
    \ No newline at end of file diff --git a/app/folder/folder-details.php b/app/folder/folder-details.php new file mode 100755 index 000000000..9d716967a --- /dev/null +++ b/app/folder/folder-details.php @@ -0,0 +1,172 @@ +check_user_session(); + +# must be numeric +if(!is_numeric($_GET['subnetId'])) { $Result->show("danger", _("Invalid ID"), true); } +if(!is_numeric($_GET['section'])) { $Result->show("danger", _("Invalid ID"), true); } + +# save folder ID +$folderId = $_GET['subnetId']; + +# get custom subnet fields +$cfields = $Tools->fetch_custom_fields ('subnets'); + +# fetch subnet details! +$folder = (array) $Subnets->fetch_subnet ("id", $folderId); + +# die if empty or not folder +if(sizeof($folder) == 0) { $Result->show("danger", _("Folder does not exist"), true); } +if($folder['isFolder']!=1) { $Result->show("danger", _("Invalid ID"), true); } + +# get vlan +$vlan = $Tools->fetch_object("vlans", "vlanId", @$vlanId); + +# set rowspan +$rowSpan = 10 + sizeof($cfields); + +# get section permissions +$permission_section = $Sections->check_permission ($User->user, $folder['sectionId']); +# get subnet permissions +$permission = $Subnets->check_permission ($User->user, $folderId); + +# if 0 die +if($permission == 0) { $Result->show("danger", _('You do not have permission to access this folder'), true); } + +# verify that is it displayed in proper section, otherwise warn! +if($folder['sectionId']!=$_GET['section']) { + $sd = $Sections->fetch_section ("id", $folder['sectionId']); + { $Result->show("warning", "Folder is in section $sd->name!", false); } +} +?> + + +

    +
    + + + + + + + + + + + + + + + + + 0) { + foreach($cfields as $key=>$field) { + if(strlen($folder[$key]) > 0) { + print ""; + print " "; + print " "; + print ""; + } + } + } + + + # action button groups + print ""; + print " "; + print " "; + print ""; + + ?> + +
    + +
    parse_permissions($permission); ?>
    $key"; + # booleans + if($field['type']=="tinyint(1)") { + if($folder[$field['name']] == 0) { print _("No"); } + elseif($folder[$field['name']] == 1) { print _("Yes"); } + } + else { + print $folder[$field['name']]; + + } + print "
    "._('Actions').""; + + print "
    "; + + /* set values for permissions */ + if($permission == 1) { + $sp['editsubnet']= false; //edit subnet + $sp['editperm'] = false; //edit permissions + $sp['changelog'] = false; //changelog view + } + else if ($permission == 2) { + $sp['editsubnet']= false; //edit subnet + $sp['editperm'] = false; //edit permissions + $sp['changelog'] = true; //changelog view + } + else if ($permission == 3) { + $sp['editsubnet']= true; //edit subnet + $sp['editperm'] = true; //edit permissions + $sp['changelog'] = true; //changelog view + } + + + # edit / permissions / nested + print "
    "; + + //warning + if($permission == 1) + print " "; + + // edit subnet + if($sp['editsubnet']) + print ""; //edit subnet + else + print " "; //edit subnet + + //permissions + if($sp['editperm']) + print " "; //edit subnet + else + print " "; //edit subnet + + // add nested subnet + if($permission_section == 3) { + print " "; + print " "; //add new child subnet + } else { + print " "; + print " "; //add new child subnet + } + print "
    "; + + # favourites / changelog + print "
    "; + #favourite + if($User->is_folder_favourite ($folder['id'])) + print " "; + else + print " "; + # changelog + if($User->settings->enableChangelog==1) { + if($sp['changelog']) + print ""; + else + print ""; + } + print "
    "; + + + print "
    "; + + print "
    +
    \ No newline at end of file diff --git a/app/folder/folder-subnets.php b/app/folder/folder-subnets.php new file mode 100755 index 000000000..c16360b48 --- /dev/null +++ b/app/folder/folder-subnets.php @@ -0,0 +1,176 @@ + +check_user_session(); + +# must be numeric +if(!is_numeric($_GET['subnetId'])) { $Result->show("danger", _("Invalid ID"), true); } +if(!is_numeric($_GET['section'])) { $Result->show("danger", _("Invalid ID"), true); } + +# set master folder ID to check for slaves +$folderId = $_GET['subnetId']; + +# get section details +$section = $Sections->fetch_section ("id", $folder['sectionId']); + +# get all slaves +$slaves = $Subnets->fetch_subnet_slaves ($folderId); + +if($slaves) { + # sort slaves by folder / subnet + foreach($slaves as $s) { + if($s->isFolder==1) { $folders[] = $s; } + else { $subnets[] = $s; } + } + + # first print belonging folders + if(sizeof(@$folders)>0) { + # print title + print "

    "._("Folder")." $folder[description] "._('has')." ". sizeof($folders)." "._('directly nested folders').":


    "; + + # table + print ''. "\n"; + # headers + print ""; + print " "; + print " "; + print ""; + + # folders + $m=0; + foreach($folders as $f) { + $f = (array) $f; + # check permission + $permission = $Subnets->check_permission ($User->user, $f['id']); + if($permission > 0) { + print ""; + print " "; + print " "; + print ""; + $m++; + } + } + # no because of permissions + if($m==0) { + print ""; + print ""; + print ""; + } + + print "
    "._('Folder')."
    $f[description]
    "; + $Result->show("info", _("Folder has no subfolders")."!", false); + print "
    "; + } + # print subnets + if(sizeof(@$subnets)>0) { + # title + print "

    "._("Folder")." $folder[description] "._('has')." ".sizeof($subnets)." "._('directly nested subnets').":



    "; + + # print table + print ''. "\n"; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # print slave subnets + $m=0; + foreach ($subnets as $slave) { + # cast + $slave = (array) $slave; + # check permission + $permission = $Subnets->check_permission ($User->user, $slave['id']); + # allowed + if($permission > 0) { + # get VLAN details + $vlan = $Tools->fetch_object("vlans", "vlanId",$slave['vlanId']); + $vlan = (array) $vlan; + # reformat empty VLAN + if(sizeof($vlan)==1) { $vlan['number'] = "/"; } + + print ""; + print " "; + print " "; + print " "; + + # increase IP count + $ipCount = 0; + if(!$Subnets->has_slaves ($slave['id'])) { $ipCount = $Addresses->count_subnet_addresses ($slave['id']); } //ip count - no slaves + else { + # fix for subnet and broadcast free space calculation + $ipCount = 0; //initial count + $Subnets->reset_subnet_slaves_recursive (); + $slaves2 = $Subnets->fetch_subnet_slaves_recursive ($slave['id']); //fetch all slaves + foreach($Subnets->slaves as $s) { + $ipCount = $ipCount + $Addresses->count_subnet_addresses ($s['id']); + # subnet and broadcast add used + if($Subnets->get_ip_version ($s['subnet'])=="IPv4" && $s['mask']<31) { + $ipCount = $ipCount+2; + } + } + } + + # print usage + $calculate = $Subnets->calculate_subnet_usage ( (int) $ipCount, $slave['mask'], $slave['subnet'] ); + print ' '. "\n"; + print ' '; + + # allow requests + if($slave['allowRequests'] == 1) { print ''; } + else { print ''; } + + # edit buttons + if($permission == 3) { + print " "; + } + else { + print " "; + } + print '' . "\n"; + + $m++; //for count + } + } + # no because of permissions + if($m==0) { + print ""; + print ""; + print ""; + } + + print '
    "._('VLAN').""._('Subnet description').""._('Subnet')."
    ".$vlan['number']."$slave[description]$slave[ip]/$slave[mask]"; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    "; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    "; + print ""; + $Result->show("info", _("Folder has no belonging subnets")."!", false); + print "
    '. "\n"; + } +} +else { + print "
    "; + $Result->show("info", _("Folder has no subfolders or belonging subnets")."!", false); +} +?> \ No newline at end of file diff --git a/app/folder/index.php b/app/folder/index.php new file mode 100644 index 000000000..d8b3c5312 --- /dev/null +++ b/app/folder/index.php @@ -0,0 +1,11 @@ +"; +include_once("folder-details.php"); +print ""; + +# Subnets in Folder +print '
    '; +include_once('folder-subnets.php'); +print '
    '; +?> \ No newline at end of file diff --git a/app/footer.php b/app/footer.php new file mode 100755 index 000000000..3b5756cc6 --- /dev/null +++ b/app/footer.php @@ -0,0 +1,32 @@ +
    + + + + + + + + settings->donate == 0) { + +print ' '; + + } + } + ?> + + + +
    \ No newline at end of file diff --git a/app/install/index.php b/app/install/index.php new file mode 100755 index 000000000..67b32107b --- /dev/null +++ b/app/install/index.php @@ -0,0 +1,140 @@ +settings->prettyLinks = "No"; +} + +# if already installed than redirect ! +if($Install->check_db_connection(false) && $Install->check_table("vrf", false)) { + + # check if installation parts 2 and 3 are running, otherwise die! + $admin = $Tools->fetch_object ("users", "id", 1); + if($admin->password!='$6$rounds=3000$JQEE6dL9NpvjeFs4$RK5X3oa28.Uzt/h5VAfdrsvlVe.7HgQUYKMXTJUsud8dmWfPzZQPbRbk8xJn1Kyyt4.dWm4nJIYhAV2mbOZ3g.') { + header("Location: ".create_link("dashboard")); + die(); + } +} +# printout +?> + + + + + + + + + + + + + + + + + + + + + + phpipam installation + + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    jQuery error!
    +

    + Hide +
    + + +
    ...
    + + + + + + +
    +
    +
    + + + + + + + +
    + + +
    + + + + + + + + diff --git a/app/install/install-execute.php b/app/install/install-execute.php new file mode 100755 index 000000000..5c45ab261 --- /dev/null +++ b/app/install/install-execute.php @@ -0,0 +1,35 @@ +show("danger", _("Invalid request"), true); } + +# if already installed ignore! +if($Install->check_table ("widgets", false) && @$_POST['dropdb']!="on") { + # check for possible errors + if(sizeof($errors = $Tools->verify_database())>0) { } + else { $Result->show("danger", _("Database already installed"), true);} +} + +# get possible advanced options +$dropdb = @$_POST['dropdb']=="on" ? true : false; +$createdb = @$_POST['createdb']=="on" ? true : false; +$creategrants = @$_POST['creategrants']=="on" ? true : false; + +# try to install new database */ +if($Install->install_database ($_POST['mysqlrootuser'], $_POST['mysqlrootpass'], $dropdb, $createdb, $creategrants)) { + $Result->show("success alert-block", 'Database installed successfully! Continue', true); +} +?> \ No newline at end of file diff --git a/app/install/install_automatic.php b/app/install/install_automatic.php new file mode 100755 index 000000000..1f2c03591 --- /dev/null +++ b/app/install/install_automatic.php @@ -0,0 +1,93 @@ +
    +
    +

    Automatic database installation

    + +
    + +
    + Please provide required inputs in below form for automatic database installation, once finished click Install database. +
    + Before you install please fill in all settings in config.php file! +
    +
    + +
    +
    + + +
    MySQL username
    +
    + +
    + + +
    MySQL password
    +
    + +
    * User must have permissions to create new MySQL database
    +
    +
    + + +
    MySQL database location
    +
    + +
    +
    + + +
    MySQL database name
    +
    + +
    * change database details on config.php
    +
    + + +

    +
    + + + + + + + +
    + + +
    +
    + +
    +
    + + +
    +
    +
    \ No newline at end of file diff --git a/app/install/install_manual.php b/app/install/install_manual.php new file mode 100755 index 000000000..b17663cb1 --- /dev/null +++ b/app/install/install_manual.php @@ -0,0 +1,55 @@ +
    +
    +

    Manual database installation

    + +
    +
    + + + " class="btn btn-sm btn-default"> Back + +
    + For importing database file with mysqlimport tool please follow below instructions:
    +
      +
    1. Set variables for database connection in config.php
    2. +
    3. Open mysql connection, create database +
      mysql -u root -p
      +
    4. +
    5. + Copy below SQL queries and paste them to mysql +
    6. +
    7. Finished ! Now login with Admin/ipamadmin to webpage
      + Login +
    8. +
    +
    +
    + + +
    +
    +
    +
    +
    + +
    +
    +
    +
    \ No newline at end of file diff --git a/app/install/install_mysqlimport.php b/app/install/install_mysqlimport.php new file mode 100755 index 000000000..7ee5edaef --- /dev/null +++ b/app/install/install_mysqlimport.php @@ -0,0 +1,43 @@ +
    +
    +

    Installation with mysqlimport tool

    + +
    +
    + + + " class="btn btn-sm btn-default"> Back + +
    + For installation with mysqlimport please follow below steps:
    +
      +
    1. Set variables for database connection in config.php
    2. +
    3. Open mysql connection +
      mysql -u root -p
      +Enter password:
      +
    4. + +
    5. Create database +
      CREATE DATABASE ``;
      +exit
      +
    6. + +
    7. Import SQL file +
      mysql -u root -p < db/SCHEMA.sql
      +
    8. + +
    9. Set permissions for phpipam user +
      GRANT ALL on ``.* to @localhost identified by '';
      +
    10. + +
    11. Finished ! Now login with Admin/ipamadmin to webpage
      + Login +
    12. +
    +
    +
    + +
    +
    +
    +
    \ No newline at end of file diff --git a/app/install/invalid_install_type.php b/app/install/invalid_install_type.php new file mode 100755 index 000000000..17c7b5314 --- /dev/null +++ b/app/install/invalid_install_type.php @@ -0,0 +1,14 @@ +
    +
    +

    Invalid installation type

    + +
    +
    + show("danger", "This installation type does not exist. Please select valid installation method!", false); ?> + + " class="btn btn-sm btn-default"> Back + +
    +
    +
    +
    \ No newline at end of file diff --git a/app/install/postinstall_configure.php b/app/install/postinstall_configure.php new file mode 100755 index 000000000..1f9938f5c --- /dev/null +++ b/app/install/postinstall_configure.php @@ -0,0 +1,105 @@ +
    +
    +

    Postinstall configuration

    + +
    + +
    + Hi, almost set, lets just set some basic settings. You can change all settings under administration once logged in! +
    +
    + + verify_database (); + + /* print result */ + if( (isset($errors['tableError'])) || (isset($errors['fieldError'])) ) { + + print "
    "; + print "Some tables or fields are missing in database:
    "; + + //tables + if (isset($errors['tableError'])) { + print 'Missing tables:'. "\n"; + print '
      '. "\n"; + foreach ($errors['tableError'] as $table) { + print "
    • $table
    • "; + } + print '
    '. "\n"; + } + //fields + if (isset($errors['fieldError'])) { + print 'Missing fields:'. "\n"; + print '
      '. "\n"; + foreach ($errors['fieldError'] as $table=>$field) { + print '
    • '; + print 'Table `'. $table .'`: missing field `'. $field .'`;'; + print '
    • '. "\n"; + } + print '
    '. "\n"; + } + print "
    "; + } + # no db errors, lets configure ! + else { + ?> + + +
    +
    + + +
    Admin password
    +
    + +
    + + +
    +
    + +
    Set password for Admin user
    +
    +
    + +
    +
    +
    + + +
    Site title
    +
    + +
    +
    + + +
    Site URL
    +
    + +
    +
    + + +
    +
    +
    + + " > Back + +
    +
    +
    + + +
    +
    + +
    +
    + + +
    +
    +
    diff --git a/app/install/postinstall_submit.php b/app/install/postinstall_submit.php new file mode 100755 index 000000000..aa458b21b --- /dev/null +++ b/app/install/postinstall_submit.php @@ -0,0 +1,39 @@ +fetch_object ("users","username","Admin"); +if($admin->password!='$6$rounds=3000$JQEE6dL9NpvjeFs4$RK5X3oa28.Uzt/h5VAfdrsvlVe.7HgQUYKMXTJUsud8dmWfPzZQPbRbk8xJn1Kyyt4.dWm4nJIYhAV2mbOZ3g.') { + $Result->show("danger", "Not allowed!", true); +} +# update +else { + # check lenghts + if(strlen($_POST['password1'])<8) { $Result->show("danger", _("Invalid password"), true); } + if(strlen($_POST['password2'])<8) { $Result->show("danger", _("Invalid password"), true); } + + # check password match + if($_POST['password1']!=$_POST['password2']) { $Result->show("danger", _("Passwords do not match"), true); } + + # Crypt password + $_POST['password1'] = $User->crypt_user_pass ($_POST['password1']); + + # all good, update password! + $Install->postauth_update($_POST['password1'], $_POST['siteTitle'], $_POST['siteURL']); + # ok + { $Result->show("success", "Settings updated, installation complete!
    Proceed to login", false); } +} +?> \ No newline at end of file diff --git a/app/install/select_install_type.php b/app/install/select_install_type.php new file mode 100755 index 000000000..aca03bb2a --- /dev/null +++ b/app/install/select_install_type.php @@ -0,0 +1,47 @@ +
    +
    +

    Welcome to phpipam installation

    + +
    +
    + +
    + Hi, we are glad you decided to install phpipam. Before you can start using it you must create MySQL database for phpipam, you can do this three ways. +

    + Before you start installation please read INSTALL.txt file to make sure all requirements for installation are met! +

    + Please select preferred database installation type: +
    +
    + +
      + +
    1. + " class="btn btn-sm btn-default">Automatic database installation +
      +
      phpipam installer will create database for you automatically.
      +
      +
    2. + + +
    3. + " class="btn btn-sm btn-default">MySQL import instructions +
      +
      Install DB files with mysqlimport tool.
      +
      +
    4. + + +
    5. + " class="btn btn-sm btn-default">Manual database installation +
      +
      Install database manually with SQL queries.
      +
      +
    6. + + + +
    +
    +
    +
    \ No newline at end of file diff --git a/app/login/captcha/captcha.php b/app/login/captcha/captcha.php new file mode 100755 index 000000000..7276603c5 --- /dev/null +++ b/app/login/captcha/captcha.php @@ -0,0 +1,894 @@ + + * File: securimage.php
    + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version.

    + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details.

    + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

    + * + * Any modifications to the library should be indicated clearly in the source code + * to inform users that the changes are not a part of the original software.

    + * + * If you found this script useful, please take a quick moment to rate it.
    + * http://www.hotscripts.com/rate/49400.html Thanks. + * + * @link http://www.phpcaptcha.org Securimage PHP CAPTCHA + * @link http://www.phpcaptcha.org/latest.zip Download Latest Version + * @link http://www.phpcaptcha.org/Securimage_Docs/ Online Documentation + * @copyright 2007 Drew Phillips + * @author drew010 + * @version 1.0.3.1 (March 24, 2008) + * @package Securimage + * + */ + +/** + ChangeLog + + 1.0.3.1 + - Error reading from wordlist in some cases caused words to be cut off 1 letter short + + 1.0.3 + - Removed shadow_text from code which could cause an undefined property error due to removal from previous version + + 1.0.2 + - Audible CAPTCHA Code wav files + - Create codes from a word list instead of random strings + + 1.0 + - Added the ability to use a selected character set, rather than a-z0-9 only. + - Added the multi-color text option to use different colors for each letter. + - Switched to automatic session handling instead of using files for code storage + - Added GD Font support if ttf support is not available. Can use internal GD fonts or load new ones. + - Added the ability to set line thickness + - Added option for drawing arced lines over letters + - Added ability to choose image type for output + +*/ + +/** + * Output images in JPEG format + */ +define('SI_IMAGE_JPEG', 1); +/** + * Output images in PNG format + */ +define('SI_IMAGE_PNG', 2); +/** + * Output images in GIF format + * Must have GD >= 2.0.28! + */ +define('SI_IMAGE_GIF', 3); + +/** + * Securimage CAPTCHA Class. + * + * @package Securimage + * @subpackage classes + * + */ +class Securimage { + + /** + * The desired width of the CAPTCHA image. + * + * @var int + */ + var $image_width = 160; + + /** + * The desired width of the CAPTCHA image. + * + * @var int + */ + var $image_height = 27; + + /** + * The image format for output.
    + * Valid options: SI_IMAGE_PNG, SI_IMAGE_JPG, SI_IMAGE_GIF + * + * @var int + */ + var $image_type = SI_IMAGE_PNG; + + /** + * The length of the code to generate. + * + * @var int + */ + var $code_length = 5; + + /** + * The character set for individual characters in the image.
    + * Letters are converted to uppercase.
    + * The font must support the letters or there may be problematic substitutions. + * + * @var string + */ + var $charset = 'ABCDEFGHKLMNPRSTUVWYZ123456789'; + //var $charset = '0123456789'; + + /** + * Whether to use a GD font instead of a TTF font.
    + * TTF offers more support and options, but use this if your PHP doesn't support TTF.
    + * + * @var boolean + */ + var $use_gd_font = false; + + /** + * The path to the TTF font file to load. + * + * @var string + */ + var $ttf_file = "./elephant.ttf"; + + /** + * The font size.
    + * Depending on your version of GD, this should be specified as the pixel size (GD1) or point size (GD2)
    + * + * @var int + */ + var $font_size = 16; + + /** + * The minimum angle in degrees, with 0 degrees being left-to-right reading text.
    + * Higher values represent a counter-clockwise rotation.
    + * For example, a value of 90 would result in bottom-to-top reading text. + * + * @var int + */ + var $text_angle_minimum = -20; + + /** + * The minimum angle in degrees, with 0 degrees being left-to-right reading text.
    + * Higher values represent a counter-clockwise rotation.
    + * For example, a value of 90 would result in bottom-to-top reading text. + * + * @var int + */ + var $text_angle_maximum = 20; + + /** + * The X-Position on the image where letter drawing will begin.
    + * This value is in pixels from the left side of the image. + * + * @var int + */ + var $text_x_start = 8; + + /** + * Letters can be spaced apart at random distances.
    + * This is the minimum distance between two letters.
    + * This should be at least as wide as a font character.
    + * Small values can cause letters to be drawn over eachother.
    + * + * @var int + */ + var $text_minimum_distance = 30; + + /** + * Letters can be spaced apart at random distances.
    + * This is the maximum distance between two letters.
    + * This should be at least as wide as a font character.
    + * Small values can cause letters to be drawn over eachother.
    + * + * @var int + */ + var $text_maximum_distance = 33; + + /** + * The background color for the image.
    + * This should be specified in HTML hex format.
    + * Make sure to include the preceding # sign! + * + * @var string + */ + //var $image_bg_color = "#e3daed"; +/* var $image_bg_color = "#E0F8EC"; */ + var $image_bg_color = "#fffaf0"; + + /** + * The text color to use for drawing characters.
    + * This value is ignored if $use_multi_text is set to true.
    + * Make sure this contrasts well with the background color.
    + * Specify the color in HTML hex format with preceding # sign + * + * @see Securimage::$use_multi_text + * @var string + */ + var $text_color = "#ff0000"; + + /** + * Set to true to use multiple colors for each character. + * + * @see Securimage::$multi_text_color + * @var boolean + */ + var $use_multi_text = false; + + /** + * String of HTML hex colors to use.
    + * Separate each possible color with commas.
    + * Be sure to precede each value with the # sign. + * + * @var string + */ + var $multi_text_color = "#0a68dd,#f65c47,#8d32fd"; + + /** + * Set to true to make the characters appear transparent. + * + * @see Securimage::$text_transparency_percentage + * @var boolean + */ + var $use_transparent_text = true; + + /** + * The percentage of transparency, 0 to 100.
    + * A value of 0 is completely opaque, 100 is completely transparent (invisble) + * + * @see Securimage::$use_transparent_text + * @var int + */ + var $text_transparency_percentage = 40; + + + // Line options + /** + * Draw vertical and horizontal lines on the image. + * + * @see Securimage::$line_color + * @see Securimage::$line_distance + * @see Securimage::$line_thickness + * @see Securimage::$draw_lines_over_text + * @var boolean + */ + var $draw_lines = true; + + /** + * The color of the lines drawn on the image.
    + * Use HTML hex format with preceding # sign. + * + * @see Securimage::$draw_lines + * @var string + */ +/* var $line_color = "#80BFFF"; */ + var $line_color = "#ffa500"; + + /** + * How far apart to space the lines from eachother in pixels. + * + * @see Securimage::$draw_lines + * @var int + */ + var $line_distance = 5; + + /** + * How thick to draw the lines in pixels.
    + * 1-3 is ideal depending on distance + * + * @see Securimage::$draw_lines + * @see Securimage::$line_distance + * @var int + */ + var $line_thickness = 1; + + /** + * Set to true to draw angled lines on the image in addition to the horizontal and vertical lines. + * + * @see Securimage::$draw_lines + * @var boolean + */ + var $draw_angled_lines = false; + + /** + * Draw the lines over the text.
    + * If fales lines will be drawn before putting the text on the image.
    + * This can make the image hard for humans to read depending on the line thickness and distance. + * + * @var boolean + */ + var $draw_lines_over_text = false; + + /** + * For added security, it is a good idea to draw arced lines over the letters to make it harder for bots to segment the letters.
    + * Two arced lines will be drawn over the text on each side of the image.
    + * This is currently expirimental and may be off in certain configurations. + * + * @var boolean + */ + var $arc_linethrough = false; + + /** + * The colors or color of the arced lines.
    + * Use HTML hex notation with preceding # sign, and separate each value with a comma.
    + * This should be similar to your font color for single color images. + * + * @var string + */ + var $arc_line_colors = "#8080ff"; + + + //END USER CONFIGURATION + //There should be no need to edit below unless you really know what you are doing. + + /** + * The gd image resource. + * + * @access private + * @var resource + */ + var $im; + + /** + * The background image resource + * + * @access private + * @var resource + */ + var $bgimg; + + /** + * The code generated by the script + * + * @access private + * @var string + */ + var $code; + + /** + * The code that was entered by the user + * + * @access private + * @var string + */ + var $code_entered; + + /** + * Whether or not the correct code was entered + * + * @access private + * @var boolean + */ + var $correct_code; + + /** + * Class constructor.
    + * Because the class uses sessions, this will attempt to start a session if there is no previous one.
    + * If you do not start a session before calling the class, the constructor must be called before any + * output is sent to the browser. + * + * + * $securimage = new Securimage(); + * + * + */ + function Securimage() + { + if ( session_id() == '' ) { // no session has been started yet, which is needed for validation + require_once( dirname(__FILE__) . '/../../../config.php' ); + if(strlen($phpsessname)>0) { session_name($phpsessname); } + session_start(); + } + } + + /** + * Generate a code and output the image to the browser. + * + * + * show('bg.jpg'); + * ?> + * + * + * @param string $background_image The path to an image to use as the background for the CAPTCHA + */ + function show($background_image = "") + { + if($background_image != "" && is_readable($background_image)) { + $this->bgimg = $background_image; + } + + $this->doImage(); + } + + /** + * Validate the code entered by the user. + * + * + * $code = $_POST['code']; + * if ($securimage->check($code) == false) { + * die("Sorry, the code entered did not match."); + * } else { + * $valid = true; + * } + * + * @param string $code The code the user entered + * @return boolean true if the code was correct, false if not + */ + function check($code) + { + $this->code_entered = $code; + $this->validate(); + return $this->correct_code; + } + + /** + * Generate and output the image + * + * @access private + * + */ + function doImage() + { + if($this->use_transparent_text == true || $this->bgimg != "") { + $this->im = imagecreatetruecolor($this->image_width, $this->image_height); + $bgcolor = imagecolorallocate($this->im, hexdec(substr($this->image_bg_color, 1, 2)), hexdec(substr($this->image_bg_color, 3, 2)), hexdec(substr($this->image_bg_color, 5, 2))); + imagefilledrectangle($this->im, 0, 0, imagesx($this->im), imagesy($this->im), $bgcolor); + } else { //no transparency + $this->im = imagecreate($this->image_width, $this->image_height); + $bgcolor = imagecolorallocate($this->im, hexdec(substr($this->image_bg_color, 1, 2)), hexdec(substr($this->image_bg_color, 3, 2)), hexdec(substr($this->image_bg_color, 5, 2))); + } + + if($this->bgimg != "") { $this->setBackground(); } + + $this->createCode(); + + if (!$this->draw_lines_over_text && $this->draw_lines) $this->drawLines(); + + $this->drawWord(); + + if ($this->arc_linethrough == true) $this->arcLines(); + + if ($this->draw_lines_over_text && $this->draw_lines) $this->drawLines(); + + $this->output(); + + } + + /** + * Set the background of the CAPTCHA image + * + * @access private + * + */ + function setBackground() + { + $dat = @getimagesize($this->bgimg); + if($dat == false) { return; } + + switch($dat[2]) { + case 1: $newim = @imagecreatefromgif($this->bgimg); break; + case 2: $newim = @imagecreatefromjpeg($this->bgimg); break; + case 3: $newim = @imagecreatefrompng($this->bgimg); break; + case 15: $newim = @imagecreatefromwbmp($this->bgimg); break; + case 16: $newim = @imagecreatefromxbm($this->bgimg); break; + default: return; + } + + if(!$newim) return; + + imagecopy($this->im, $newim, 0, 0, 0, 0, $this->image_width, $this->image_height); + } + + /** + * Draw arced lines over the text + * + * @access private + * + */ + function arcLines() + { + $colors = explode(',', $this->arc_line_colors); + imagesetthickness($this->im, 3); + + $color = $colors[rand(0, sizeof($colors) - 1)]; + $linecolor = imagecolorallocate($this->im, hexdec(substr($color, 1, 2)), hexdec(substr($color, 3, 2)), hexdec(substr($color, 5, 2))); + + $xpos = $this->text_x_start + ($this->font_size * 2) + rand(-5, 5); + $width = $this->image_width / 2.66 + rand(3, 10); + $height = $this->font_size * 2.14 - rand(3, 10); + + if ( rand(0,100) % 2 == 0 ) { + $start = rand(0,66); + $ypos = $this->image_height / 2 - rand(5, 15); + $xpos += rand(5, 15); + } else { + $start = rand(180, 246); + $ypos = $this->image_height / 2 + rand(5, 15); + } + + $end = $start + rand(75, 110); + + imagearc($this->im, $xpos, $ypos, $width, $height, $start, $end, $linecolor); + + $color = $colors[rand(0, sizeof($colors) - 1)]; + $linecolor = imagecolorallocate($this->im, hexdec(substr($color, 1, 2)), hexdec(substr($color, 3, 2)), hexdec(substr($color, 5, 2))); + + if ( rand(1,75) % 2 == 0 ) { + $start = rand(45, 111); + $ypos = $this->image_height / 2 - rand(5, 15); + $xpos += rand(5, 15); + } else { + $start = rand(200, 250); + $ypos = $this->image_height / 2 + rand(5, 15); + } + + $end = $start + rand(75, 100); + + imagearc($this->im, $this->image_width * .75, $ypos, $width, $height, $start, $end, $linecolor); + } + + /** + * Draw lines on the image + * + * @access private + * + */ + function drawLines() + { + $linecolor = imagecolorallocate($this->im, hexdec(substr($this->line_color, 1, 2)), hexdec(substr($this->line_color, 3, 2)), hexdec(substr($this->line_color, 5, 2))); + imagesetthickness($this->im, $this->line_thickness); + + //vertical lines + for($x = 1; $x < $this->image_width; $x += $this->line_distance) { + imageline($this->im, $x, 0, $x, $this->image_height, $linecolor); + } + + //horizontal lines + for($y = 11; $y < $this->image_height; $y += $this->line_distance) { + imageline($this->im, 0, $y, $this->image_width, $y, $linecolor); + } + + if ($this->draw_angled_lines == true) { + for ($x = -($this->image_height); $x < $this->image_width; $x += $this->line_distance) { + imageline($this->im, $x, 0, $x + $this->image_height, $this->image_height, $linecolor); + } + + for ($x = $this->image_width + $this->image_height; $x > 0; $x -= $this->line_distance) { + imageline($this->im, $x, 0, $x - $this->image_height, $this->image_height, $linecolor); + } + } + } + + /** + * Draw the CAPTCHA code over the image + * + * @access private + * + */ + function drawWord() + { + if ($this->use_gd_font == true) { + if (!is_int($this->gd_font_file)) { //is a file name + $font = @imageloadfont($this->gd_font_file); + if ($font == false) { + trigger_error("Failed to load GD Font file {$this->gd_font_file} ", E_USER_WARNING); + return; + } + } else { //gd font identifier + $font = $this->gd_font_file; + } + + $color = imagecolorallocate($this->im, hexdec(substr($this->text_color, 1, 2)), hexdec(substr($this->text_color, 3, 2)), hexdec(substr($this->text_color, 5, 2))); + imagestring($this->im, $font, $this->text_x_start, ($this->image_height / 2) - ($this->gd_font_size / 2), $this->code, $color); + + } else { //ttf font + if($this->use_transparent_text == true) { + $alpha = intval($this->text_transparency_percentage / 100 * 127); + $font_color = imagecolorallocatealpha($this->im, hexdec(substr($this->text_color, 1, 2)), hexdec(substr($this->text_color, 3, 2)), hexdec(substr($this->text_color, 5, 2)), $alpha); + } else { //no transparency + $font_color = imagecolorallocate($this->im, hexdec(substr($this->text_color, 1, 2)), hexdec(substr($this->text_color, 3, 2)), hexdec(substr($this->text_color, 5, 2))); + } + + $x = $this->text_x_start; + $strlen = strlen($this->code); + $y_min = ($this->image_height / 2) + ($this->font_size / 2) - 2; + $y_max = ($this->image_height / 2) + ($this->font_size / 2) + 2; + $colors = explode(',', $this->multi_text_color); + + for($i = 0; $i < $strlen; ++$i) { + $angle = rand($this->text_angle_minimum, $this->text_angle_maximum); + $y = rand($y_min, $y_max); + if ($this->use_multi_text == true) { + $idx = rand(0, sizeof($colors) - 1); + $r = substr($colors[$idx], 1, 2); + $g = substr($colors[$idx], 3, 2); + $b = substr($colors[$idx], 5, 2); + if($this->use_transparent_text == true) { + $font_color = imagecolorallocatealpha($this->im, "0x$r", "0x$g", "0x$b", $alpha); + } else { + $font_color = imagecolorallocate($this->im, "0x$r", "0x$g", "0x$b"); + } + } + @imagettftext($this->im, $this->font_size, $angle, $x, $y, $font_color, $this->ttf_file, $this->code{$i}); + + $x += rand($this->text_minimum_distance, $this->text_maximum_distance); + } //for loop + } //else ttf font + } //function + + /** + * Create a code and save to the session + * + * @since 1.0.1 + * + */ + function createCode() + { + $this->code = false; + + if ($this->use_wordlist && is_readable($this->wordlist_file)) { + $this->code = $this->readCodeFromFile(); + } + + if ($this->code == false) { + $this->code = $this->generateCode($this->code_length); + } + + $this->saveData(); + } + + /** + * Generate a code + * + * @access private + * @param int $len The code length + * @return string + */ + function generateCode($len) + { + $code = ''; + + for($i = 1, $cslen = strlen($this->charset); $i <= $len; ++$i) { + $code .= strtoupper( $this->charset{rand(0, $cslen - 1)} ); + } + return $code; + } + + /** + * Reads a word list file to get a code + * + * @access private + * @since 1.0.2 + * @return mixed false on failure, a word on success + */ + function readCodeFromFile() + { + $fp = @fopen($this->wordlist_file, 'rb'); + if (!$fp) return false; + + $fsize = filesize($this->wordlist_file); + if ($fsize < 32) return false; // too small of a list to be effective + + if ($fsize < 128) { + $max = $fsize; // still pretty small but changes the range of seeking + } else { + $max = 128; + } + + fseek($fp, rand(0, $fsize - $max), SEEK_SET); + $data = fread($fp, 128); // read a random 128 bytes from file + fclose($fp); + $data = preg_replace("/\r?\n/", "\n", $data); + + $start = strpos($data, "\n", rand(0, 100)) + 1; // random start position + $end = strpos($data, "\n", $start); // find end of word + + return strtolower(substr($data, $start, $end - $start)); // return substring in 128 bytes + } + + /** + * Output image to the browser + * + * @access private + * + */ + function output() + { + header("Expires: Sun, 1 Jan 2000 12:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT"); + header("Cache-Control: no-store, no-cache, must-revalidate"); + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); + + switch($this->image_type) + { + case SI_IMAGE_JPEG: + header("Content-Type: image/jpeg"); + imagejpeg($this->im, null, 90); + break; + + case SI_IMAGE_GIF: + header("Content-Type: image/gif"); + imagegif($this->im); + break; + + default: + header("Content-Type: image/png"); + imagepng($this->im); + break; + } + + imagedestroy($this->im); + } + + /** + * Get WAV file data of the spoken code.
    + * This is appropriate for output to the browser as audio/x-wav + * + * @since 1.0.1 + * @return string WAV data + * + */ + function getAudibleCode() + { + $letters = array(); + $code = $this->getCode(); + + if ($code == '') { + $this->createCode(); + $code = $this->getCode(); + } + + for($i = 0; $i < strlen($code); ++$i) { + $letters[] = $code{$i}; + } + + return $this->generateWAV($letters); + } + + /** + * Save the code in the session + * + * @access private + * + */ + function saveData() + { + $_SESSION['securimage_code_value'] = strtolower($this->code); + } + + /** + * Validate the code to the user code + * + * @access private + * + */ + function validate() + { + if ( isset($_SESSION['securimage_code_value']) && !empty($_SESSION['securimage_code_value']) ) { + if ( $_SESSION['securimage_code_value'] == strtolower(trim($this->code_entered)) ) { + $this->correct_code = true; + $_SESSION['securimage_code_value'] = ''; + } else { + $this->correct_code = false; + } + } else { + $this->correct_code = false; + } + } + + /** + * Get the captcha code + * + * @since 1.0.1 + * @return string + */ + function getCode() + { + if (isset($_SESSION['securimage_code_value']) && !empty($_SESSION['securimage_code_value'])) { + return $_SESSION['securimage_code_value']; + } else { + return ''; + } + } + + /** + * Check if the user entered code was correct + * + * @access private + * @return boolean + */ + function checkCode() + { + return $this->correct_code; + } + + /** + * Generate a wav file by concatenating individual files + * @since 1.0.1 + * @access private + * @param array $letters Array of letters to build a file from + * @return string WAV file data + */ + function generateWAV($letters) + { + $first = true; // use first file to write the header... + $data_len = 0; + $files = array(); + $out_data = ''; + + foreach ($letters as $letter) { + $filename = $this->audio_path . strtoupper($letter) . '.wav'; + + $fp = fopen($filename, 'rb'); + + $file = array(); + + $data = fread($fp, filesize($filename)); // read file in + + $header = substr($data, 0, 36); + $body = substr($data, 44); + + + $data = unpack('NChunkID/VChunkSize/NFormat/NSubChunk1ID/VSubChunk1Size/vAudioFormat/vNumChannels/VSampleRate/VByteRate/vBlockAlign/vBitsPerSample', $header); + + $file['sub_chunk1_id'] = $data['SubChunk1ID']; + $file['bits_per_sample'] = $data['BitsPerSample']; + $file['channels'] = $data['NumChannels']; + $file['format'] = $data['AudioFormat']; + $file['sample_rate'] = $data['SampleRate']; + $file['size'] = $data['ChunkSize'] + 8; + $file['data'] = $body; + + if ( ($p = strpos($file['data'], 'LIST')) !== false) { + // If the LIST data is not at the end of the file, this will probably break your sound file + $info = substr($file['data'], $p + 4, 8); + $data = unpack('Vlength/Vjunk', $info); + $file['data'] = substr($file['data'], 0, $p); + $file['size'] = $file['size'] - (strlen($file['data']) - $p); + } + + $files[] = $file; + $data = null; + $header = null; + $body = null; + + $data_len += strlen($file['data']); + + fclose($fp); + } + + $out_data = ''; + for($i = 0; $i < sizeof($files); ++$i) { + if ($i == 0) { // output header + $out_data .= pack('C4VC8', ord('R'), ord('I'), ord('F'), ord('F'), $data_len + 36, ord('W'), ord('A'), ord('V'), ord('E'), ord('f'), ord('m'), ord('t'), ord(' ')); + + $out_data .= pack('VvvVVvv', + 16, + $files[$i]['format'], + $files[$i]['channels'], + $files[$i]['sample_rate'], + $files[$i]['sample_rate'] * (($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8), + ($files[$i]['bits_per_sample'] * $files[$i]['channels']) / 8, + $files[$i]['bits_per_sample'] ); + + $out_data .= pack('C4', ord('d'), ord('a'), ord('t'), ord('a')); + + $out_data .= pack('V', $data_len); + } + + $out_data .= $files[$i]['data']; + } + + return $out_data; + } +} /* class Securimage */ + +?> diff --git a/app/login/captcha/captchashow.php b/app/login/captcha/captchashow.php new file mode 100755 index 000000000..cb54c03cc --- /dev/null +++ b/app/login/captcha/captchashow.php @@ -0,0 +1,6 @@ +show(); +?> diff --git a/app/login/captcha/elephant.ttf b/app/login/captcha/elephant.ttf new file mode 100755 index 000000000..024b076e5 Binary files /dev/null and b/app/login/captcha/elephant.ttf differ diff --git a/app/login/index.php b/app/login/index.php new file mode 100755 index 000000000..495e6bacc --- /dev/null +++ b/app/login/index.php @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + <?php print $User->settings->siteTitle; ?> + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    jQuery error!
    +

    + Hide +
    + + +
    + + + + + +
    ...
    + + + + + +
    +
    + + settings->defaultLang) && !is_null($User->settings->defaultLang) ) { + # get language + $lang = $User->get_default_lang(); + + putenv("LC_ALL=".$lang->l_code); + setlocale(LC_ALL, $lang->l_code); // set language + bindtextdomain("phpipam", "./functions/locale"); // Specify location of translation tables + textdomain("phpipam"); // Choose domain + } + ?> + + "; include_once('app/error.php'); print "
    "; } + ?> + + +
    + authenticated == true ) { + # print result + if($_GET['section']=="timeout") { $Result->show("success", _('You session has timed out')); } + else { $Result->show("success", _('You have logged out')); } + + # write log + write_log( "User logged out", "User $User->username has logged out", 0, $User->username ); + + # destroy session + $User->destroy_session(); + } + ?> +
    + +
    +
    + + + + + +
    + + +
    + + + + + + + + + + \ No newline at end of file diff --git a/app/login/login_check.php b/app/login/login_check.php new file mode 100755 index 000000000..a252e876e --- /dev/null +++ b/app/login/login_check.php @@ -0,0 +1,60 @@ +0 ) { + $Result->show("danger", _("Invalid characters in username"), true); + } + + # check failed table + $cnt = $User->block_check_ip (); + + # check for failed logins and captcha + if($User->blocklimit > $cnt) { + // all good + } + # count set, captcha required + elseif(!isset($_POST['captcha'])) { + write_log( "Login IP blocked", "Login from IP address $ip was blocked because of 5 minute block after 5 failed attempts", 1); + $Result->show("danger", _('You have been blocked for 5 minutes due to authentication failures'), true); + } + # captcha check + else { + # check captcha + if($_POST['captcha']!=$_SESSION['securimage_code_value']) { + $Result->show("danger", _("Invalid security code"), true); + } + } + + # all good, try to authentucate user + $User->authenticate ($_POST['ipamusername'], $_POST['ipampassword']); +} +# Username / pass not provided +else { + $Result->show("danger", _('Please enter your username and password'), true); +} + +?> diff --git a/app/login/login_form.php b/app/login/login_form.php new file mode 100755 index 000000000..d115a736c --- /dev/null +++ b/app/login/login_form.php @@ -0,0 +1,68 @@ +
    + +
    +
    + + +
    + +
    + + +
    +
    + +
    + + +
    +
    + + "; + } + ?> +
    + + block_check_ip (); + if($cnt>4) { + ?> + +
    +
    + +
    +
    + +
    + + +
    +
    + +
    + +
    + +
    + +settings->enableIPrequests == 1) { +?> + + + +
    \ No newline at end of file diff --git a/app/login/request_ip_form.php b/app/login/request_ip_form.php new file mode 100755 index 000000000..bf927ae5a --- /dev/null +++ b/app/login/request_ip_form.php @@ -0,0 +1,123 @@ +
    +
    + +
    + + + + + + + +requests_fetch_available_subnets (); + +# die if no subnets are available for requests! +if($subnets===false) { ?> + + + +
    + +
    + + + + + + + + * + + + + + + + + + + + + + + + + + + + + +settings->IPfilter); + +# owner if set +if(in_array('owner', $setFields)) { + print ''. "\n"; + print ''._('Owner').''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + print ''. "\n"; +} +?> + + + + + * + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + + + + +
    \ No newline at end of file diff --git a/app/login/request_ip_result.php b/app/login/request_ip_result.php new file mode 100755 index 000000000..49d0da5d4 --- /dev/null +++ b/app/login/request_ip_result.php @@ -0,0 +1,43 @@ +fetch_settings(); + +# requests must be enabled! +if($settings['enableIPrequests']==1) { + # verify email + if(!validate_email($_POST['requester']) ) { $Result->show("danger", _('Please provide valid email address').'! ('._('requester').': '.$_POST['requester'].')', true); } + + # formulate insert values + $values = array("subnetId"=>$_POST['subnetId'], + "ip_addr"=>@$_POST['ip_addr'], + "description"=>@$_POST['description'], + "dns_name"=>@$_POST['dns_name'], + "owner"=>$_POST['owner'], + "requester"=>$_POST['requester'], + "comment"=>@$_POST['comment'], + "processed"=>0 + ); + if(!$Admin->object_modify("requests", "add", "id", $values)) { $Result->show("danger", _('Error submitting new IP address request'), true); } + else { + { $Result->show("success", _('Request submitted successfully')); } + # send mail + //if(!sendIPReqEmail($_POST)) { $Result->show("danger", _('Sending mail for new IP request failed'), true); } + //else { $Result->show("success", _('Sending mail for IP request succeeded'), true); } + } +} +else { $Result->show("danger", _('IP requests disabled'), true); } + +?> \ No newline at end of file diff --git a/app/sections/index.php b/app/sections/index.php new file mode 100755 index 000000000..21955b1cf --- /dev/null +++ b/app/sections/index.php @@ -0,0 +1,307 @@ + + + +check_user_session(); + +# fetch all sections +$sections = $Sections->fetch_all_sections (); + +# check for requests +$requests = $Tools->requests_fetch(true); + +# get admin and tools menu items +require( dirname(__FILE__) . '/../tools/tools-menu-config.php' ); +require( dirname(__FILE__) . '/../admin/admin-menu-config.php' ); + +?> + + + \ No newline at end of file diff --git a/app/sections/section-changelog.php b/app/sections/section-changelog.php new file mode 100755 index 000000000..534add016 --- /dev/null +++ b/app/sections/section-changelog.php @@ -0,0 +1,56 @@ +is_admin (true); + +# get clog entries +$clogs = $Tools->fetch_changlog_entries("section", $_GET['sPage']); + +# header +print "

    "._('Section')." - "._('Changelog')."


    "; + +# back +print " "._('Back to section').""; + + +# empty +if(sizeof($clogs)==0) { + print "
    "; + print "

    "._("No changelogs available")."

    "; + print ""._("No changelog entries are available for this section").""; + print "
    "; +} +# result +else { + # printout + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # logs + foreach($clogs as $l) { + # cast + $l = (array) $l; + # format diff + $l['cdiff'] = str_replace("\n", "
    ", $l['cdiff']); + + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + } + + print "
    "._('User').""._('Action').""._('Result').""._('Date').""._('Change')."
    $l[real_name]"._("$l[caction]").""._("$l[cresult]")."$l[cdate]$l[cdiff]
    "; +} +?> \ No newline at end of file diff --git a/app/sections/section-subnets.php b/app/sections/section-subnets.php new file mode 100755 index 000000000..2a1e62607 --- /dev/null +++ b/app/sections/section-subnets.php @@ -0,0 +1,142 @@ +check_user_session (); + +# must be numeric +if(!is_numeric($_GET['section'])) { $Result->show("danger", _("Invalid ID"), true); } + +# set custom fields +$custom = $Tools->fetch_custom_fields ("subnets"); + +# set hidden fields +$hidden_fields = json_decode($User->settings->hiddenCustomFields, true); +$hidden_fields = is_array($hidden_fields['subnets']) ? $hidden_fields['subnets'] : array(); + +# title +print "

    "._('Available subnets')."

    "; + +# check permission +$permission = $Sections->check_permission ($User->user, $_GET['section']); + +# permitted +if($permission != 0) { + + # print table structure + print ""; + + # set colcount + if($User->settings->enableVRF == 1) { $colCount = 8; } + else { $colCount = 7; } + + # get Available subnets in section + $subnets = $Subnets->fetch_section_subnets($_GET['section']); + + # remove custom fields if all empty! */ + foreach($custom as $field) { + $sizeMyFields[$field['name']] = 0; // default value + # check against each IP address + foreach($subnets as $subn) { + if(strlen($subn->$field['name']) > 0) { + $sizeMyFields[$field['name']]++; // +1 + } + } + # unset if value == 0 + if($sizeMyFields[$field['name']] == 0) { + unset($custom[$field['name']]); + } + else { + $colCount++; // colspan + } + } + + # collapsed div with details + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + if($User->settings->enableVRF == 1) { + print " "; + } + print " "; + print " "; + print " "; + print " "; + if(sizeof($custom) > 0) { + foreach($custom as $field) { + if(!in_array($field['name'], $hidden_fields)) { + print " "; + } + } + } + print " "; + print ""; + + # add new link + print ""; + print " "; + print " "; + + # no subnets + if(sizeof($subnets) == 0) { + print ""; + + # check Available subnets for subsection + $subsections = $Sections->fetch_subsections($_GET['section']); + } + else { + # subnets + $Subnets->print_subnets_tools($User->user, $subnets, $custom); + } + + # subsection subnets + if(sizeof($subsections)>0) { + + # set colspan + $colspan = 8 + sizeof($custom); + if($User->settings->enableVRF == 1) { $colspan++; } + + # subnets + foreach($subsections as $ss) { + # case + $ss = (array) $ss; + $slavesubnets = $Subnets->fetch_section_subnets($ss['id']); + + if(sizeof($slavesubnets)>0) { + # headers + print ""; + print " "; + print ""; + + # subnets + $Subnets->print_subnets_tools($User->user, $slavesubnets, $custom_fields); + } + else { + print ""; + print " "; + print ""; + + print ""; + print " "; + print ""; + } + } + } + + print ""; + $m++; + + # end master table + print "
    "._('Subnet').""._('Description').""._('VLAN').""._('Master Subnet')."
    "; + print " "; + print "
    "._('Section has no subnets')."!
    "._('Available subnets in subsection')." $ss[name]:
    "._('Available subnets in subsection')." $ss[name]:
    "._('Section has no subnets')."!
    "; +} +else { + print "
    "._("You do not have permission to access this network")."!
    "; +} +?> \ No newline at end of file diff --git a/app/sections/user-menu.php b/app/sections/user-menu.php new file mode 100755 index 000000000..f13e6f4d3 --- /dev/null +++ b/app/sections/user-menu.php @@ -0,0 +1,41 @@ + + +
    + +
    +
    + ' type='text' value=''> +
    + + + +
    + +
    + + > + > + > +
    + + + authenticated) { ?> + ">, user->real_name; ?>
    + user->role); ?>
    + + + "> + +
    \ No newline at end of file diff --git a/app/subnets/addresses/address-changelog.php b/app/subnets/addresses/address-changelog.php new file mode 100644 index 000000000..2cbfc3c0e --- /dev/null +++ b/app/subnets/addresses/address-changelog.php @@ -0,0 +1,58 @@ +check_user_session(); + +# get clog entries for current subnet +$clogs = $Tools->fetch_changlog_entries("ip_addr", $address['id']); + +# permissions +$permission = $Subnets->check_permission ($User->user, $_GET['subnetId']); +if($permission == 0) { $Result->show("danger", _('You do not have permission to access this network'), true); } + +# header +print "

    "._('Changelog')."


    "; + +# empty +if(sizeof($clogs)==0) { + print "
    "; + print "

    "._("No changelogs available")."

    "; + print ""._("No changelog entries are available for this host").""; + print "
    "; +} +# result +else { + # printout + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # logs + foreach($clogs as $l) { + $l = (array) $l; + # format diff + $l['cdiff'] = str_replace("\n", "
    ", $l['cdiff']); + + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + } + print "
    "._('User').""._('Action').""._('Result').""._('Date').""._('Change')."
    $l[real_name]"._("$l[caction]").""._("$l[cresult]")."$l[cdate]$l[cdiff]
    "; +} +?> \ No newline at end of file diff --git a/app/subnets/addresses/address-details.php b/app/subnets/addresses/address-details.php new file mode 100644 index 000000000..4d9451737 --- /dev/null +++ b/app/subnets/addresses/address-details.php @@ -0,0 +1,331 @@ +check_user_session(); + +# checks +if(!is_numeric($_GET['subnetId'])) { $Result->show("danger", _("Invalid ID"), true); } +if(!is_numeric($_GET['section'])) { $Result->show("danger", _("Invalid ID"), true); } +if(!is_numeric($_GET['ipaddrid'])) { $Result->show("danger", _("Invalid ID"), true); } + +# get IP a nd subnet details +$address = (array) $Addresses-> fetch_address(null, $_GET['ipaddrid']); +$subnet = (array) $Subnets->fetch_subnet(null, $address['subnetId']); + +# fetch all custom fields +$custom_fields = $Tools->fetch_custom_fields ('ipaddresses'); +# set hidden custom fields +$hidden_cfields = json_decode($User->settings->hiddenCustomFields, true); +$hidden_cfields = is_array($hidden_cfields['ipaddresses']) ? $hidden_cfields['ipaddresses'] : array(); + +# set selected address fields array +$selected_ip_fields = $User->settings->IPfilter; +$selected_ip_fields = explode(";", $selected_ip_fields); //format to array +$selected_ip_fields_size = in_array('state', $selected_ip_fields) ? (sizeof($selected_ip_fields)-1) : sizeof($selected_ip_fields); //set size of selected fields +if($selected_ip_fields_size==1 && strlen($selected_ip_fields[0])==0) { $selected_ip_fields_size = 0; } //fix for 0 + + +# set ping statuses +$statuses = explode(";", $User->settings->pingStatus); + +# permissions +$subnet_permission = $Subnets->check_permission($User->user, $subnet['id']); +$section_permission = $Sections->check_permission ($User->user, $subnet['sectionId']); + +# checks +if(sizeof($subnet)==0) { $Result->show("danger", _('Subnet does not exist'), true); } //subnet doesnt exist +if($subnet_permission == 0) { $Result->show("danger", _('You do not have permission to access this network'), true); } //not allowed to access + + # resolve dns name +$DNS = new DNS ($Database); +$resolve = $DNS->resolve_address((object) $address); + +# reformat empty fields +$address = $Addresses->reformat_empty_array_fields($address, "/"); + +#header +print "

    "._('IP address details')."


    "; + +# back +print " "._('Back to subnet').""; + +# check if it exists, otherwise print error +if(sizeof($address)>1) { + + # table - details + print ""; + + # ip + print ""; + print " "; + print " "; + print ""; + + # description + print ""; + print " "; + print " "; + print ""; + + # hierarchy + print ""; + print " "; + print " "; + print ""; + + # subnet + print ""; + print " "; + print " "; + print ""; + + # state + print ""; + print " "; + print " "; + print ""; + + # hostname + print ""; + print " "; + print " "; + print ""; + + # mac + if(in_array('owner', $selected_ip_fields)) { + print ""; + print " "; + print " "; + print ""; + } + + # mac + if(in_array('mac', $selected_ip_fields)) { + print ""; + print " "; + print " "; + print ""; + } + + # note + if(in_array('note', $selected_ip_fields)) { + print ""; + print " "; + print " "; + print ""; + } + + # switch + if(in_array('switch', $selected_ip_fields)) { + print ""; + print " "; + if(strlen($address['switch'])>0) { + # get device + $device = (array) $Tools->fetch_device(null, $address['switch']); + $device = $Addresses->reformat_empty_array_fields($device, ""); + print " "; + } else { + print " "; + } + print ""; + } + + # port + if(in_array('port', $selected_ip_fields)) { + print ""; + print " "; + print " "; + print ""; + } + + # last edited + print ""; + print " "; + if(strlen($address['editDate'])>1) { + print " "; + } else { + print " "; + } + print ""; + + + # avalibility + print ""; + print " "; + print ""; + print ""; + + # calculate + $tDiff = time() - strtotime($address['lastSeen']); + if($address['excludePing']==1) { $seen_status = ""; $seen_text = ""; } + elseif($tDiff < $statuses[0]) { $seen_status = "success"; $seen_text = _("Device is alive")."
    "._("Last seen").": ".$address['lastSeen']; } + elseif($tDiff < $statuses[1]) { $seen_status = "warning"; $seen_text = _("Device warning")."
    "._("Last seen").": ".$address['lastSeen']; } + elseif($tDiff > $statuses[1]) { $seen_status = "error"; $seen_text = _("Device is offline")."
    "._("Last seen").": ".$address['lastSeen'];} + elseif($address['lastSeen'] == "0000-00-00 00:00:00") { $seen_status = "neutral"; $seen_text = _("Device is offline")."
    "._("Last seen").": "._("Never");} + else { $seen_status = "neutral"; $seen_text = _("Device status unknown");} + + print " "; + print " "; + print ""; + + # custom subnet fields + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $key=>$field) { + if(strlen($address[$key])>0) { + $address[$key] = str_replace(array("\n", "\r\n"), "
    ",$address[$key]); + print ""; + print " "; + print " "; + print ""; + } + } + } + + # check for temporary shares! + if($User->settings->tempShare==1) { + foreach(json_decode($User->settings->tempAccess) as $s) { + if($s->type=="ipaddresses" && $s->id==$address['id']) { + if(time()<$s->validity) { + $active_shares[] = $s; + } + else { + $expired_shares[] = $s; + } + } + } + if(sizeof(@$active_shares)>0) { + # divider + print ""; + print " "; + print ""; + # print + print ""; + print ""; + print ""; + } + if(sizeof(@$expired_shares)>0) { + # divider + print ""; + print " "; + print " "; + print ""; + # print + print ""; + print ""; + print ""; + } + } + + + # actions + print ""; + print " "; + print ""; + print ""; + print " "; + + print ""; + + print ""; + + + print "
    "._('IP address')."$address[ip]
    "._('Description')."$address[description]
    "._('Hierarchy').""; + print_breadcrumbs ($Sections, $Subnets, $_GET, $Addresses); + print "
    "._('Subnet')."$subnet[ip]/$subnet[mask] ($subnet[description])
    "._('IP status').""; + + if ($address['state'] == "0") { $stateClass = _("Offline"); } + else if ($address['state'] == "2") { $stateClass = _("Reserved"); } + else if ($address['state'] == "3") { $stateClass = _("DHCP"); } + else { $stateClass = _("Online"); } + + print $Addresses->address_type_index_to_type ($address['state']); + print $Addresses->address_type_format_tag ($address['state']); + + print "
    "._('Hostname')."$address[dns_name]
    "._('Owner')."$address[owner]
    "._('MAC address')."$address[mac]
    "._('Note')."$address[note]
    "._('Device')."".@$device['hostname']." ".@$device['description']."$address[switch]
    "._('Port')."$address[port]
    "._('Last edited')."$address[editDate]"._('Never')."

    "._('Avalibility')."
    "; + print "$seen_text"; + + print "
    $key"; + #booleans + if($field['type']=="tinyint(1)") { + if($address[$key] == 0) { print _("No"); } + elseif($address[$key] == 1) { print _("Yes"); } + } + else { + print $address[$key]; + } + print "

    "._("Active shares").":"; + $m=1; + foreach($active_shares as $s) { + print " Share $m ("._("Expires")." ".date("Y-m-d H:i:s", $s->validity).")
    "; + $m++; + } + print "
    "; + print "

    "._("Expired shares").":"; + $m=1; + foreach($expired_shares as $s) { + print " Share $m ("._("Expired")." ".date("Y-m-d H:i:s", $s->validity).")
    "; + $m++; + } + print "
    "; + print "

    "._('Actions').""; + print "
    "; + print "
    "; + # write permitted + if( $subnet_permission > 1) { + if(@$address['class']=="range-dhcp") + { + print " "; + print " "; + print " "; + print " "; + print " "; + } + else + { + print " "; + print " "; + print " "; + print " "; + print " "; + //share + if($User->settings->tempShare==1) { + print " "; + } + } + } + # write not permitted + else { + if(@$address['class']=="range-dhcp") + { + print " "; + print " "; + print " "; + print " "; + print " "; + } + else + { + print " "; + print " "; + print " "; + print " "; + print " "; + } + } + + print "
    "; + print "
    "; + print "
    "; + + # changelog + include("address-changelog.php"); +} +# not exisitng +else { + $Result->show("danger", _("IP address not existing in database")."!", true); +} +?> \ No newline at end of file diff --git a/app/subnets/addresses/address-modify-check.php b/app/subnets/addresses/address-modify-check.php new file mode 100644 index 000000000..9bdc1ef18 --- /dev/null +++ b/app/subnets/addresses/address-modify-check.php @@ -0,0 +1,243 @@ +check_user_session(); + +# validate action +$Tools->validate_action ($_POST['action']); +$action = $_POST['action']; +//reset delete action form visual visual +if(isset($_POST['action-visual'])) { + if(@$_POST['action-visual'] == "delete") { $action = "delete"; } +} + +# save $_POST to $address +$address = $_POST; + +# required fields +isset($address['action']) ?: $Result->show("danger", _("Missing required fields"), true); +isset($address['subnet']) ?: $Result->show("danger", _("Missing required fields"), true); +isset($address['subnetId']) ?: $Result->show("danger", _("Missing required fields"), true); +isset($address['id']) ?: $Result->show("danger", _("Missing required fields"), true); +isset($address['state']) ?: $Result->show("danger", _("Missing required fields"), true); + + +# set and check permissions +$subnet_permission = $Subnets->check_permission($User->user, $address['subnetId']); +$subnet_permission > 1 ?: $Result->show("danger", _('Cannot edit IP address'), true); + +# fetch subnet +$subnet = (array) $Subnets->fetch_subnet(null, $address['subnetId']); +sizeof($subnet)>0 ?: $Result->show("danger", _("Invalid subnet"), true); + +# replace empty fields with nulls +$address = $Addresses->reformat_empty_array_fields ($address, null); + +# custom fields and checks +$custom_fields = $Tools->fetch_custom_fields ('ipaddresses'); +if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $field) { + # replace possible ___ back to spaces! + $field['nameTest'] = str_replace(" ", "___", $field['name']); + + if(isset($address[$field['nameTest']])) { $address[$field['name']] = $address[$field['nameTest']];} + # booleans can be only 0 and 1 + if($field['type']=="tinyint(1)") { + if($address[$field['name']]>1) { + $address[$field['name']] = ""; + } + } + # null custom fields not permitted + if($field['Null']=="NO" && strlen($address[$field['name']])==0) { + $Result->show("danger", $field['name']._(" can not be empty!"), true); + } + } +} + +# we need old address details for mailing or if we are editing address +if($action=="edit" || $action=="delete" || $action=="move") { + $address_old = (array) $Addresses->fetch_address(null, $address['id']); +} + +# set excludePing value +$address['excludePing'] = @$address['excludePing']==1 ? 1 : 0; + +# no strict checks flag - for range networks and /31, /32 +$not_strict = @$address['nostrict']=="yes" ? true : false; + + + +# are we adding/editing range? +if (strlen(strstr($address['ip_addr'],"-")) > 0) { + + # set flag for updating + $address['type'] = "series"; + + # remove possible spaces + $address['ip_addr'] = str_replace(" ", "", $address['ip_addr']); + + # get start and stop of range + $range = explode("-", $address['ip_addr']); + $address['start'] = $range[0]; + $address['stop'] = $range[1]; + + # verify both IP addresses + $Addresses->verify_address( $address['start'], "$subnet[ip]/$subnet[mask]", $not_strict ); + $Addresses->verify_address( $address['stop'] , "$subnet[ip]/$subnet[mask]", $not_strict ); + + # go from start to stop and insert / update / delete IPs + $start = $Subnets->transform_to_decimal($address['start']); + $stop = $Subnets->transform_to_decimal($address['stop']); + + # start cannot be higher than stop! + if($start>$stop) { $Result->show("danger", _("Invalid address range")."!", true); } + + # we can manage only 255 IP's at once! + if(gmp_strval(gmp_sub($stop,$start)) > 255) { $Result->show("danger", _("Only 255 IP addresses at once")."!", true); } + + # set limits + $m = gmp_strval($start); + $n = gmp_strval(gmp_add($stop,1)); + + # check if delete is confirmed + if ($action=="delete" && !isset($address['deleteconfirm'])) { + $range = str_replace("-", " - ", $address['ip_addr']); + # for ajax to prevent reload + print "
    alert alert-danger
    "; + # result + print "
    "; + print ""._("Warning").": "._("Are you sure you want to delete IP address range")."?"; + print "
    $range
    "; + print "
    "; + print " "._("Confirm").""; + print "
    "; + print "
    "; + print "
    "; + } + # ok, edit + else { + # for each IP in range modify + while (gmp_cmp($m, $n) != 0) { + + # reset IP address field + $address['ip_addr'] = $m; + + # modify action - if delete ok, dynamically reset add / edit -> if IP already exists set edit + if($action != "delete") { + $action = $Addresses->address_exists ($m, $address['subnetId'])==true ? "edit" : "add"; + } + # if it fails set error log + if (!$Addresses->modify_address($address)) { + $errors[] = _('Cannot').' '. $action. ' '._('IP address').' '. $Addresses->transform_to_dotted($m); + } + # next IP + $m = gmp_strval(gmp_add($m,1)); + } + + # print errors if they exist + if(isset($errors)) { + $log = array_to_log ($errors); + $Result->show("danger", $log, false); + write_log( "IP address modification", "'Error $action range $address[start] - $address[stop]
    $log", 2, $User->username); + } + else { + # reset IP for mailing + $address['ip_addr'] = $address['start'] .' - '. $address['stop']; + + /* @mail functions ------------------- */ + include_once('../../../functions/functions-mail.php'); + //sendObjectUpdateMails("ip", $action, array(), $address, true); + + $Result->show("success", _("Range")." $address[start] - $address[stop] "._($action)." "._("successfull")."!", false); + write_log( "IP address modification", "Range $address[start] - $address[stop] $action successfull!", 0, $User->username); + } + } +} +/* no range, single IP address */ +else { + + # unique hostname requested? + if(isset($address['unique'])) { + if($address['unique'] == 1 && strlen($address['dns_name'])>0) { + # check if unique + if(!$Addresses->is_hostname_unique($address['dns_name'])) { $Result->show("danger", _('Hostname is not unique')."!", true); } + } + } + + # reset subnet if move + if($action == "move") { + $subnet = (array) $Subnets->fetch_subnet(null, $address['newSubnet']); + $address['ip_addr'] = $address_old['ip']; + } + # verify address + $verify = $Addresses->verify_address( $address['ip_addr'], "$subnet[ip]/$subnet[mask]", $not_strict ); + + # if errors are present print them, else execute query! + if($verify) { $Result->show("danger", _('Error').": $verify ($address[ip_addr])", true); } + else { + # set update type for update to single + $address['type'] = "single"; + + # check for duplicate entryon adding new address + if ($action == "add") { + if ($Addresses->address_exists ($address['ip_addr'], $address['subnetId'])) { $Result->show("danger", _('IP address')." $address[ip_addr] "._('already existing in selected network').'!', true); } + } + + # check for duplicate entry on edit! + if ($action == "edit") { # if IP is the same than it can already exist! + if($Addresses->transform_address($address['ip_addr'],"decimal") != $address['ip_addr_old']) { + if ($Addresses->address_exists ($address['ip_addr'], $address['subnetId'])) { $Result->show("danger", _('IP address')." $address[ip_addr] "._('already existing in selected network').'!', true); } + } + } + # move checks + if($action == "move") { + # check if not already used in new subnet + if ($Addresses->address_exists ($address['ip_addr'], $address['newSubnet'])) { $Result->show("danger", _('IP address')." $address[ip_addr] "._('already existing in selected network').'!', true); } + } + + # for delete actions check if delete was confirmed + if ($action=="delete" && !isset($address['deleteconfirm'])) { + # for ajax to prevent reload + print "
    alert alert-danger
    "; + # result + print "
    "; + print ""._("Warning").": "._("Are you sure you want to delete IP address")."?"; + print "
    "; + print "
    "; + print " "._("Confirm").""; + print "
    "; + print "
    "; + print "
    "; + } + # ok, execute + else { + //fail + if (!$Addresses->modify_address($address)) { + $Result->show("danger", _('Error inserting IP address')."!", false); + write_log( "IP address modification", "Error $action IP address $address[ip_addr]
    SubnetId: $address[subnetId]", 2, $User->username ); + } + //success, save log file and send email + else { + $Result->show("success", _("IP $action successful"),false); + write_log( "IP address modification", "$action of IP address $address[ip_addr] succesfull!
    SubnetId: $address[subnetId]", 0, $User->username ); + } + } + } +} +?> \ No newline at end of file diff --git a/app/subnets/addresses/address-modify.php b/app/subnets/addresses/address-modify.php new file mode 100644 index 000000000..66a4ad6a8 --- /dev/null +++ b/app/subnets/addresses/address-modify.php @@ -0,0 +1,452 @@ +check_user_session(); + +# validate action +$Tools->validate_action ($_POST['action']); + +# validate post +is_numeric($_POST['subnetId']) ?: $Result->show("danger", _("Invalid ID"), true, true); +is_numeric($_POST['id']) || strlen($_POST['id'])==0 ?: $Result->show("danger", _("Invalid ID"), true, true); + +# get posted values +$subnetId= $_POST['subnetId']; +$action = $_POST['action']; +$id = $_POST['id']; + +# fetch subnet +$subnet = (array) $Subnets->fetch_subnet(null, $subnetId); +sizeof($subnet)>0 ?: $Result->show("danger", _("Invalid subnet"), true, true); + +# set and check permissions +$subnet_permission = $Subnets->check_permission($User->user, $subnet['id']); +$subnet_permission > 1 ?: $Result->show("danger", _('Cannot edit IP address details').'!
    '._('You do not have write access for this network'), true, true); + +# set selected address fields array +$selected_ip_fields = $User->settings->IPfilter; +$selected_ip_fields = explode(";", $selected_ip_fields); //format to array + +# get all custom fields +$custom_fields = $Tools->fetch_custom_fields ('ipaddresses'); + + +# if action is not add then fetch current details, otherwise fetch first available IP address +if ($action == "all-add") { + $address['ip_addr'] = $Subnets->transform_address($id, "dotted"); +} +else if ($action == "add") { + # get first available IP address + $first = $Addresses->get_first_available_address ($subnetId, $Subnets); + $first = !$first ? "" : $Subnets->transform_address($first, "dotted"); + + $address['ip_addr'] = $first; +} +else { + $address = (array) $Addresses->fetch_address(null, $id); +} + + +# Set action and button text +if ($action == "add") { $btnName = _("add"); $act = "add"; } +else if ($action == "all-add") { $btnName = _("add"); $act = "add"; } +else if ($action == "edit") { $btnName = _("edit"); $act = "edit"; } +else if ($action == "all-edit") { $btnName = _("edit"); $act = "edit"; } +else if ($action == "delete") { $btnName = _("delete"); $act = "delete"; } +else { $btnName = ""; } + +# set delete flag +if($act=="delete") { $delete = "readonly='readonly'"; } +else { $delete = ""; } + +?> + + + + + + + +
    + + +
    + + +
    + + + + + + + + + + + + '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + ?> + + + + + + + + + '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + ?> + + '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + ?> + + '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + # Port + if(in_array('port', $selected_ip_fields)) { + + if(!isset($address['port'])) {$address['port'] = "";} + + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + ?> + + '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + ?> + + addresses_types_fetch(); + # default type + if(!is_numeric(@$address['state'])) { $address['state'] = 2; } + + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + ?> + + '; + print ''; + print ''; + print ''; + } + ?> + + + + + + + + + + + + 0) { + # count datepickers + $timeP = 0; + + # all my fields + foreach($custom_fields as $myField) { + # replace spaces with | + $myField['nameNew'] = str_replace(" ", "___", $myField['name']); + + # required + if($myField['Null']=="NO") { $required = "*"; } + else { $required = ""; } + + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ''. "\n"; + } + } + ?> + + + + + + + + + + + + identify_address ($subnet['subnet']); + + if($subnet['mask'] < 31 && ($action=='add' || substr($action, 0,4)=="all-") && $type == "IPv4" ) { ?> + + + + + + + + + + + + + + + + +
    * +
    + 0) print "-".$Subnets->transform_address($_POST['stopIP'],"dotted"); ?>" placeholder=""> + + + +
    + + + + "> + + + + + + +
    '._('Hostname').''. "\n"; + print '
    '; + print ' '. "\n"; + print ' '."\n"; + print " "; + print " "; + print "
    "; + print '
    + + placeholder=""> +
    '._('MAC address').''. "\n"; + print ' '. "\n"; + print '
    '._('Owner').''. "\n"; + print ' '. "\n"; + print '
    '._('Device').''. "\n"; + + print ''. "\n"; + print '
    '._('Port').''. "\n"; + print ' '. "\n"; + print '
    '._('Note').''. "\n"; + print ' '. "\n"; + print '
    '._('Tag').''. "\n"; + print ' '. "\n"; + print '
    '._("Ping exclude").''; + print "
    "; + print ' '. _('Exclude from ping status checks'); + print "
    "; + print '
    + > +

    '. $myField['name'] .' '.$required.''. "\n"; + + //set type + if(substr($myField['type'], 0,3) == "set") { + //parse values + $tmp = explode(",", str_replace(array("set(", ")", "'"), "", $myField['type'])); + //null + if($myField['Null']!="NO") { array_unshift($tmp, ""); } + + print ""; + } + //date and time picker + elseif($myField['type'] == "date" || $myField['type'] == "datetime") { + // just for first + if($timeP==0) { + print ''; + print ''; + print ''; + } + $timeP++; + + //set size + if($myField['type'] == "date") { $size = 10; $class='datepicker'; $format = "yyyy-MM-dd"; } + else { $size = 19; $class='datetimepicker'; $format = "yyyy-MM-dd"; } + + //field + if(!isset($address[$myField['name']])) { print ' '. "\n"; } + else { print ' '. "\n"; } + } + //boolean + elseif($myField['type'] == "tinyint(1)") { + print ""; + } + //text + elseif($myField['type'] == "text") { + print ' '. "\n"; + } + //default - input field + else { + print ' '. "\n"; + } + + print '

    +
    + > +
    +
    +
    + +
    +
    +
    + +
    +
    +
    + + + + +
    + + +
    +
    + + "._('Delete IP').""; + } + ?> + +
    + + +
    +
    diff --git a/app/subnets/addresses/address-resolve.php b/app/subnets/addresses/address-resolve.php new file mode 100755 index 000000000..5f16ef9e5 --- /dev/null +++ b/app/subnets/addresses/address-resolve.php @@ -0,0 +1,28 @@ +check_user_session(); + +# create object +$address = new StdClass(); +$address->ip_addr = $_POST['ipaddress']; +$address->dns_name = null; + +# resolve +$hostname = $DNS->resolve_address ($address, true); + +# print result +print $hostname['name']; +?> \ No newline at end of file diff --git a/app/subnets/addresses/export-field-select.php b/app/subnets/addresses/export-field-select.php new file mode 100644 index 000000000..d3c44dd19 --- /dev/null +++ b/app/subnets/addresses/export-field-select.php @@ -0,0 +1,124 @@ +check_user_session(); + +# set and check permissions +$subnet_permission = $Subnets->check_permission($User->user, $_POST['subnetId']); +$subnet_permission > 0 ? : $Result->show("danger", _('You do not have access to this network'), true); + +?> + + +
    + + + +
    + +'; + +# table +print " "; + +# IP addr - mandatory +print " "; +print " "; +print " "; +print " "; + +# state +print " "; +print " "; +print " "; +print " "; + +# description - mandatory +print " "; +print " "; +print " "; +print " "; + +# hostname - mandatory +print " "; +print " "; +print " "; +print " "; + +# mac +print " "; +print " "; +print " "; +print " "; + +# owner +print " "; +print " "; +print " "; +print " "; + +# switch +print " "; +print " "; +print " "; +print " "; + +# port +print " "; +print " "; +print " "; +print " "; + +# note +print " "; +print " "; +print " "; +print " "; + + +# get all custom fields +$custom_fields = $Tools->fetch_custom_fields ('ipaddresses'); +if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $myField) { + + //change spaces to ___ + $myField['nameTemp'] = str_replace(" ", "___", $myField['name']); + + print " "; + print " "; + print " "; + print " "; + } +} + +print '
    "._('IP address')."
    "._('IP state')."
    "._('Description')."
    "._('Hostname')."
    "._('MAC address')."
    "._('Owner')."
    "._('Switch')."
    "._('Port')."
    "._('Note')."
    $myField[name]
    '; +print ''; + +?> + +
    + + +
    +
    + + +
    +
    \ No newline at end of file diff --git a/app/subnets/addresses/export-subnet.php b/app/subnets/addresses/export-subnet.php new file mode 100755 index 000000000..0ec668c86 --- /dev/null +++ b/app/subnets/addresses/export-subnet.php @@ -0,0 +1,230 @@ +check_user_session(); + +# we dont need any errors! +//ini_set('display_errors', 0); + +# fetch subnet details +$subnet = (array) $Subnets->fetch_subnet (null, $_GET['subnetId']); +# fetch all IP addresses in subnet +$addresses = $Addresses->fetch_subnet_addresses ($_GET['subnetId'], "ip_addr", "asc"); +# get all custom fields +$custom_fields = $Tools->fetch_custom_fields ('ipaddresses'); + + +# Create a workbook +$filename = "phpipam_subnet_export.xls"; +$workbook = new Spreadsheet_Excel_Writer(); + +//formatting headers +$format_header =& $workbook->addFormat(); +$format_header->setBold(); +$format_header->setColor('black'); +$format_header->setSize(12); + +//format vlan +$format_vlan =& $workbook->addFormat(); +$format_vlan->setColor('black'); +$format_vlan->setSize(11); + + +//formatting titles +$format_title =& $workbook->addFormat(); +$format_title->setColor('black'); +$format_title->setFgColor(22); //light gray +$format_title->setBottom(1); +$format_title->setTop(1); +$format_title->setAlign('left'); + +//formatting content - borders around IP addresses +$format_right =& $workbook->addFormat(); +$format_right->setRight(1); +$format_left =& $workbook->addFormat(); +$format_left->setLeft(1); +$format_top =& $workbook->addFormat(); +$format_top->setTop(1); + + +// Create a worksheet +//$worksheet =& $workbook->addWorksheet($subnet['description']); +$worksheet_name = $subnet['description']; +$worksheet_name = (strlen($worksheet_name) > 30) ? substr($worksheet_name,0,27).'...' : $worksheet_name; +$worksheet =& $workbook->addWorksheet($worksheet_name); + +$lineCount = 0; + +# Write title - subnet details +$worksheet->write($lineCount, $rowCount, $subnet['description'], $format_header ); +$lineCount++; +$worksheet->write($lineCount, $rowCount, $Subnets->transform_address($subnet['subnet'],"dotted") . "/" .$subnet['mask'], $format_header ); +$lineCount++; + +# write VLAN details +$vlan = (array) $Tools->fetch_object("vlans", "vlanId", $subnet['vlanId']); +if($vlan!=false) { + $vlan_text = strlen($vlan['name'])>0 ? "vlan: $vlan[number] - $vlan[name]" : "vlan: $vlan[number]"; + + $worksheet->write($lineCount, $rowCount, $vlan_text, $format_vlan ); + $lineCount++; +} +$lineCount++; + +//set row count +$rowCount = 0; + +//write headers +if( (isset($_GET['ip_addr'])) && ($_GET['ip_addr'] == "on") ) { + $worksheet->write($lineCount, $rowCount, _('ip address') ,$format_title); + $rowCount++; +} +if( (isset($_GET['state'])) && ($_GET['state'] == "on") ) { + $worksheet->write($lineCount, $rowCount, _('ip state') ,$format_title); + $rowCount++; +} +if( (isset($_GET['description'])) && ($_GET['description'] == "on") ) { + $worksheet->write($lineCount, $rowCount, _('description') ,$format_title); + $rowCount++; +} +if( (isset($_GET['dns_name'])) && ($_GET['dns_name'] == "on") ) { + $worksheet->write($lineCount, $rowCount, _('hostname') ,$format_title); + $rowCount++; +} +if( (isset($_GET['mac'])) && ($_GET['mac'] == "on") ) { + $worksheet->write($lineCount, $rowCount, _('mac') ,$format_title); + $rowCount++; +} +if( (isset($_GET['owner'])) && ($_GET['owner'] == "on") ) { + $worksheet->write($lineCount, $rowCount, _('owner') ,$format_title); + $rowCount++; +} +if( (isset($_GET['switch'])) && ($_GET['switch'] == "on") ) { + $worksheet->write($lineCount, $rowCount, _('device') ,$format_title); + $rowCount++; +} +if( (isset($_GET['port'])) && ($_GET['port'] == "on") ) { + $worksheet->write($lineCount, $rowCount, _('port') ,$format_title); + $rowCount++; +} +if( (isset($_GET['note'])) && ($_GET['note'] == "on") ) { + $worksheet->write($lineCount, $rowCount, _('note') ,$format_title); + $rowCount++; +} + +//custom +if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $myField) { + //set temp name - replace space with three ___ + $myField['nameTemp'] = str_replace(" ", "___", $myField['name']); + + if( (isset($_GET[$myField['nameTemp']])) && ($_GET[$myField['nameTemp']] == "on") ) { + $worksheet->write($lineCount, $rowCount, $myField['name'] ,$format_title); + $rowCount++; + } + } +} + + +$lineCount++; + + +//we need to reformat state! +$ip_types = $Addresses->addresses_types_fetch(); +//fetch devices and reorder +$devices = $Tools->fetch_devices (); +foreach($devices as $d) { + $devices_indexed[$d->id] = $d; +} +//write all IP addresses +foreach ($addresses as $ip) { + $ip = (array) $ip; + + //reset row count + $rowCount = 0; + + //change switch ID to name + $ip['switch'] = is_null($ip['switch'])||strlen($ip['switch'])==0 ? "" : $devices_indexed[$ip['switch']]->hostname; + + if( (isset($_GET['ip_addr'])) && ($_GET['ip_addr'] == "on") ) { + $worksheet->write($lineCount, $rowCount, $Subnets->transform_address($ip['ip_addr'],"dotted"), $format_left); + $rowCount++; + } + if( (isset($_GET['state'])) && ($_GET['state'] == "on") ) { + if($ip_types[$ip['state']]['showtag']==1) { + $worksheet->write($lineCount, $rowCount, $ip_types[$ip['state']]['type']); + } + $rowCount++; + } + if( (isset($_GET['description'])) && ($_GET['description'] == "on") ) { + $worksheet->write($lineCount, $rowCount, $ip['description']); + $rowCount++; + } + if( (isset($_GET['dns_name'])) && ($_GET['dns_name'] == "on") ) { + $worksheet->write($lineCount, $rowCount, $ip['dns_name']); + $rowCount++; + } + if( (isset($_GET['mac'])) && ($_GET['mac'] == "on") ) { + $worksheet->write($lineCount, $rowCount, $ip['mac']); + $rowCount++; + } + if( (isset($_GET['owner'])) && ($_GET['owner'] == "on") ) { + $worksheet->write($lineCount, $rowCount, $ip['owner']); + $rowCount++; + } + if( (isset($_GET['switch'])) && ($_GET['switch'] == "on") ) { + $worksheet->write($lineCount, $rowCount, $ip['switch']); + $rowCount++; + } + if( (isset($_GET['port'])) && ($_GET['port'] == "on") ) { + $worksheet->write($lineCount, $rowCount, $ip['port']); + $rowCount++; + } + if( (isset($_GET['note'])) && ($_GET['note'] == "on") ) { + $worksheet->write($lineCount, $rowCount, $ip['note']); + $rowCount++; + } + + //custom + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $myField) { + //set temp name - replace space with three ___ + $myField['nameTemp'] = str_replace(" ", "___", $myField['name']); + + if( (isset($_GET[$myField['nameTemp']])) && ($_GET[$myField['nameTemp']] == "on") ) { + $worksheet->write($lineCount, $rowCount, $ip[$myField['name']]); + $rowCount++; + } + } + } + + $lineCount++; +} + + +//new line +$lineCount++; + +// sending HTTP headers +$workbook->send($filename); + +// Let's send the file +$workbook->close(); + +?> \ No newline at end of file diff --git a/app/subnets/addresses/index.php b/app/subnets/addresses/index.php new file mode 100644 index 000000000..8387fb7a9 --- /dev/null +++ b/app/subnets/addresses/index.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/app/subnets/addresses/mail-notify-check.php b/app/subnets/addresses/mail-notify-check.php new file mode 100644 index 000000000..7426d4b22 --- /dev/null +++ b/app/subnets/addresses/mail-notify-check.php @@ -0,0 +1,80 @@ +check_user_session(); + +# verify each recipient +foreach (explode(",", $_POST['recipients']) as $rec) { + if(!filter_var(trim($rec), FILTER_VALIDATE_EMAIL)) { + $Result->show("danger", _("Invalid email address")." - ".$rec, true); + } +} +# strip html tags +$_POST = $Tools->strip_input_tags($_POST); + + + +# fetch mailer settings +$mail_settings = $Tools->fetch_object("settingsMail", "id", 1); + +# initialize mailer +$phpipam_mail = new phpipam_mail($User->settings, $mail_settings); +$phpipam_mail->initialize_mailer(); + + +// set subject +$subject = $_POST['subject']; + +// set html content +$content[] = ""; +$content[] = ""; +foreach(explode("\r\n", $_POST['content']) as $c) { +$content[] = ""; +} +$content[] = ""; +//set al content +$content_plain[] = "$subject"."\r\n------------------------------\r\n"; +$content_plain[] = str_replace("·", "\t - ", $_POST['content']); +$content_plain[] = "\r\n\r\n"._("Sent by user")." ".$User->user->real_name." at ".date('Y/m/d H:i'); +$content[] = "
    $subject
    $c
    Sent by user ".$User->user->real_name." at ".date('Y/m/d H:i')."
    "; + +// set alt content +$content = $phpipam_mail->generate_message (implode("\r\n", $content)); +$content_plain = implode("\r\n",$content_plain); + + +# try to send +try { + $phpipam_mail->Php_mailer->setFrom($mail_settings->mAdminMail, $mail_settings->mAdminName); + foreach(explode(",", $_POST['recipients']) as $r) { + $phpipam_mail->Php_mailer->addAddress(trim($r)); + } + $phpipam_mail->Php_mailer->Subject = $subject; + $phpipam_mail->Php_mailer->msgHTML($content); + $phpipam_mail->Php_mailer->AltBody = $content_plain; + //send + $phpipam_mail->Php_mailer->send(); +} catch (phpmailerException $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); +} catch (Exception $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); +} + +# all good +$Result->show("success", _('Sending mail succeeded')."!" , true); +?> \ No newline at end of file diff --git a/app/subnets/addresses/mail-notify.php b/app/subnets/addresses/mail-notify.php new file mode 100644 index 000000000..c567cccc7 --- /dev/null +++ b/app/subnets/addresses/mail-notify.php @@ -0,0 +1,134 @@ +check_user_session(); + +# id must be numeric +is_numeric($_POST['id']) || strlen($_POST['id'])==0 ?: $Result->show("danger", _("Invalid ID"), true); + +# get IP address id +$id = $_POST['id']; + +# fetch address, subnet and vlan +$address = (array) $Addresses->fetch_address (null, $id); +$subnet = (array) $Subnets->fetch_subnet (null, $address['subnetId']); +$vlan = (array) $Tools->fetch_object("vlans", "vlanId", @$address['vlanId']); + +# get all custom fields +$custom_fields = $Tools->fetch_custom_fields ('ipaddresses'); + + +# checks +sizeof($address)>0 ?: $Result->show("danger", _("Invalid ID"), true); +sizeof($subnet)>0 ?: $Result->show("danger", _("Invalid subnet"), true); + + +# set title +$title = _('IP address details').' :: ' . $address['ip']; + + +# address + $content[] = "• "._('IP address').": \t $address[ip]/$subnet[mask]"; +# description +empty($address['description']) ? : $content[] = "• "._('Description').":\t $address[description]"; +# hostname +empty($address['dns_name']) ? : $content[] = "• "._('Hostname').": \t $address[dns_name]"; +# subnet desc +$s_descrip = empty($address['description']) ? "" : " (" . $subnet['description']. ")"; +# subnet + $content[] = "• "._('Subnet').": \t $subnet[ip]/$subnet[mask] $s_descrip"; +# gateway +$gateway = $Subnets->find_gateway($subnet['id']); +if($gateway !==false) + $content[] = "• "._('Gateway').": \t". $Subnets->transform_to_dotted($gateway->ip_addr); +# VLAN +empty($address['vlan']) ? : $content[] = "• "._('VLAN').": \t\t $address[vlan]"; +# Switch +if(!empty($address['switch'])) { + # get device by id + $device = (array) $Tools->fetch_device(null, $address['switch']); + !sizeof($device)>1 ? : $content[] = "• "._('Device').": \t\t $device[hostname]"; +} +# port +empty($address['port']) ? : $content[] = "• "._('Port').": \t $address[port]"; +# mac +empty($address['mac']) ? : $content[] = "• "._('Mac address').": \t $address[mac]"; +# owner +empty($address['owner']) ? : $content[] = "• "._('Owners').": \t $address[owner]"; + +# custom +if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $custom_field) { + if(!empty($address[$custom_field['name']])) { + $content[] = "• ". _($custom_field['name']).":\t".$address[$custom_field['name']]; + } + } +} +?> + + + + +
    + + +
    + + +
    + + + + + + + + + + + + + + + + + + + +
    + + +
    + + +
    + +
    +
    +
    + + +
    +
    + + +
    + + +
    +
    \ No newline at end of file diff --git a/app/subnets/addresses/move-address.php b/app/subnets/addresses/move-address.php new file mode 100644 index 000000000..9f323f4e6 --- /dev/null +++ b/app/subnets/addresses/move-address.php @@ -0,0 +1,111 @@ +check_user_session(); + +# validate action +$Tools->validate_action ($_POST['action']); + +# validate post +is_numeric($_POST['subnetId']) ?: $Result->show("danger", _("Invalid ID"), true); +is_numeric($_POST['id']) || strlen($_POST['id'])==0 ?: $Result->show("danger", _("Invalid ID"), true); + +# fetch address and subnet +$address = (array) $Addresses->fetch_address(null, $_POST['id']); +$subnet = (array) $Subnets->fetch_subnet(null, $_POST['subnetId']); + +# fetch all slave subnets +$Subnets->fetch_subnet_slaves_recursive ($subnet['id']); +?> + + +
    + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + "> + + + + +
    0 ? $address['description'] : "/"; ?>
    0 ? $address['dns_name'] : "/"; ?>

    : + +
    +
    + +
    + + +
    + + + + +
    +
    \ No newline at end of file diff --git a/app/subnets/addresses/ping-address.php b/app/subnets/addresses/ping-address.php new file mode 100644 index 000000000..e64bc3665 --- /dev/null +++ b/app/subnets/addresses/ping-address.php @@ -0,0 +1,72 @@ +settings); + +# verify that user is logged in +$User->check_user_session(); + +# validate post +is_numeric($_POST['subnetId']) ?: $Result->show("danger", _("Invalid ID"), true); +is_numeric($_POST['id']) || strlen($_POST['id'])==0 ?: $Result->show("danger", _("Invalid ID"), true); + +# set and check permissions +$subnet_permission = $Subnets->check_permission($User->user, $_POST['subnetId']); +$subnet_permission > 2 ?: $Result->show("danger", _('Cannot edit IP address details').'!
    '._('You do not have write access for this network'), true, true); + +# fetch address +$address = (array) $Addresses->fetch_address(null, $_POST['id']); + + +# try to ping it +$pingRes = $Ping->ping_address($address['ip']); + +# update last seen if success +if($pingRes==0) { @$Ping->ping_update_lastseen($address['id']); } +?> + + +
    + + +
    + show("success", _("IP address")." $address[ip] "._("is alive"), false); } + # offline + elseif ($pingRes==1 || $pingRes==2) { $Result->show("danger", _("IP address")." $address[ip] "._("is not alive"), false); } + # error + else { + # fetch error code + $ecode = $Ping->ping_exit_explain($pingRes); + { $Result->show("danger", _("Error").": $ecode ($pingRes)", false); } + } + + # hr + print "
    "; + { $Result->show("muted pull-right", "(".$Ping->settings->scanPingType.")", false); } + # additional notes + if(isset($Ping->rtt)) { $Result->show("muted pull-right", $Ping->rtt." ms", false); } + ?> +
    + + + \ No newline at end of file diff --git a/app/subnets/addresses/print-address-table.php b/app/subnets/addresses/print-address-table.php new file mode 100644 index 000000000..1d25fe0dc --- /dev/null +++ b/app/subnets/addresses/print-address-table.php @@ -0,0 +1,465 @@ + + +check_user_session(); + + # set sorting + $sort['direction'] = 'asc'; + $sort['field'] = 'ip_addr'; + $sort['directionNext'] = "desc"; +} +# AJAX loaded - if header sorting +else { + # use required functions + require('../../../functions/functions.php'); + # database object + $Database = new Database_PDO; + # initialize objects + $Result = new Result; + $User = new User ($Database); + $Sections = new Sections ($Database); + $Subnets = new Subnets ($Database); + $Tools = new Tools ($Database); + $Addresses = new Addresses ($Database); + + # verify that user is logged in + $User->check_user_session(); + + # set sorting + $tmp = explode("|", $_POST['direction']); + + $sort['field'] = $tmp[0]; + $sort['direction'] = $tmp[1]; + + if($sort['direction'] == "asc") { $sort['directionNext'] = "desc"; } + else { $sort['directionNext'] = "asc"; } + + # subnet-related variables + $subnet = (array) $Subnets->fetch_subnet(null, $_POST['subnetId']); //subnet details + $subnet_detailed = $Subnets->get_network_boundaries ($subnet['subnet'], $subnet['mask']); //set network boundaries + $slaves = $Subnets->has_slaves ($subnet['id']) ? true : false; //check if subnet has slaves and set slaves flag true/false + + # fetch all addresses - sorted + if($slaves) { + $addresses = $Addresses->fetch_subnet_addresses_recursive ($subnet['id'], false, $sort['field'], $sort['direction']); + $slave_subnets = (array) $Subnets->fetch_subnet_slaves ($subnet['id']); + } else { + $addresses = $Addresses->fetch_subnet_addresses ($subnet['id'], $sort['field'], $sort['direction']); + } + + # set permissions + $subnet_permission = $Subnets->check_permission($User->user, $subnet['id']); +} + + +# We need DNS object +$DNS = new DNS ($Database, $User->settings); + + +/* verifications */ +# checks +if(sizeof($subnet)==0) { $Result->show("danger", _('Subnet does not exist'), true); } //subnet doesnt exist +if($subnet_permission == 0) { $Result->show("danger", _('You do not have permission to access this network'), true); } //not allowed to access +if(!is_numeric($_REQUEST['subnetId'])) { $Result->show("danger", _('Invalid ID'), true); } //subnet id must be numeric + +/* selected and hidden fields */ + +# reset custom fields to ip addresses +$custom_fields = $Tools->fetch_custom_fields ('ipaddresses'); +# set hidden custom fields +$hidden_cfields = json_decode($User->settings->hiddenCustomFields, true); +$hidden_cfields = is_array($hidden_cfields['ipaddresses']) ? $hidden_cfields['ipaddresses'] : array(); + +# set selected address fields array +$selected_ip_fields = $User->settings->IPfilter; +$selected_ip_fields = explode(";", $selected_ip_fields); //format to array +$selected_ip_fields_size = in_array('state', $selected_ip_fields) ? (sizeof($selected_ip_fields)-1) : sizeof($selected_ip_fields); //set size of selected fields +if($selected_ip_fields_size==1 && strlen($selected_ip_fields[0])==0) { $selected_ip_fields_size = 0; } //fix for 0 + + +/* Addresses and fields manupulations */ + +# save for visual display ! +$addresses_visual = $addresses; +# new compress functions +$Addresses->addresses_types_fetch(); +foreach($Addresses->address_types as $t) { + if($t['compress']=="Yes" && $User->user->compressOverride!="Uncompress") { + if(sizeof($addresses)>0) { + $addresses = $Addresses->compress_address_ranges ($addresses, $t['id']); + } + } +} + +# set colspan for output +$colspan['empty'] = $selected_ip_fields_size + sizeof($custom_fields) +4; //empty colspan +$colspan['unused'] = $selected_ip_fields_size + sizeof($custom_fields) +3; //unused colspan +$colspan['dhcp'] = $selected_ip_fields_size + sizeof($custom_fields); //dhcp colspan + +# remove custom fields if all are empty! +foreach($custom_fields as $field) { + $sizeMyFields[$field['name']] = 0; // default value + # check against each IP address + if($addresses!==false) { + foreach($addresses as $ip) { + $ip = (array) $ip; + if(strlen($ip[$field['name']]) > 0) { + $sizeMyFields[$field['name']]++; // +1 + } + } + # unset if value == 0 + if($sizeMyFields[$field['name']] == 0) { + unset($custom_fields[$field['name']]); + + $colspan['empty']--; + $colspan['unused']--; //unused span -1 + $colspan['dhcp']--; //dhcp span -1 + } + } +} + +/* output variables */ + +# set page limit for pagination +$page_limit = $User->user->printLimit; +if($page_limit == 0) { $page_limit = "100000000"; } +else if(empty($page_limit)) { $page_limit = "124"; } +# times to repeat body +$page_repeats = ceil(sizeof($addresses)/$page_limit); + +# set page number from post +$maxPages = round(sizeof($addresses)/$page_limit,0); // set max number of pages +if(@$_REQUEST['sPage']>$page_repeats || !isset($_REQUEST['sPage'])) { $_REQUEST['sPage'] = 1; } // reset to 1 if number too big +elseif(!is_numeric($_REQUEST['sPage'])) { $_REQUEST['sPage'] = str_replace("page", "", $_REQUEST['sPage']); } // remove p from page + +# set ping statuses for warning and offline +$statuses = explode(";", $User->settings->pingStatus); +?> + + +

    +"._('Orphaned IP addresses for subnet')." $subnet[description] (".sizeof($addresses)." orphaned)
    "._('This happens if subnet contained IP addresses when new child subnet was created')."'

    "; } +else { print _("IP addresses belonging to ALL nested subnets"); } +# print page # if present +if(sizeof($addresses) > $page_limit) + { print " ("._('Page')." $_REQUEST[sPage]/$page_repeats)"; } +?> +

    + + +$page_limit) { $Addresses->print_pagination ($_REQUEST['sPage'], $page_repeats); } +?> + + + + + + + + + + "; } + else { $icon = " "; } + + # IP address - mandatory + print ""; + # hostname - mandatory + print ""; + # Description - mandatory + print ""; + # MAC address + if(in_array('mac', $selected_ip_fields)) { print ""; } + # note + if(in_array('note', $selected_ip_fields)) { print ""; } + # switch + if(in_array('switch', $selected_ip_fields)) { print ""; } + # port + if(in_array('port', $selected_ip_fields)) { print ""; } + # owner + if(in_array('owner', $selected_ip_fields)) { print ""; } + + # custom fields + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $myField) { + if(!in_array($myField['name'], $hidden_cfields)) { + print ""; + } + } + } + ?> + + + + + + + +user->hideFreeRange!=1) { + $unused = $Addresses->find_unused_addresses($Subnets->transform_to_decimal($subnet_detailed['network']), $Subnets->transform_to_decimal($subnet_detailed['broadcast']), $subnet['mask'], $empty=true ); + print ''. "\n"; + } +} +# print IP address +else { + # break into arrays + $addresses_chunk = (array_chunk($addresses, $page_limit, true)); + + $c = 1; //count for print for pages - $c++ per page + $n = 0; //count for IP addresses - $n++ per IP address + $g = 0; //count for compress consecutive class + + foreach($addresses_chunk as $address_chunk) { + + # show current page + if($c == $_REQUEST['sPage']) { $show = true; } + else { $show = false; } + + foreach($address_chunk as $dummy) + { + # display? + if($show) { + + # + # first check for gaps from network to first host + # + + # check gap between network address and first IP address + if ( $n == 0 ) { $unused = $Addresses->find_unused_addresses ( $Subnets->transform_to_decimal($subnet_detailed['network']), $addresses[$n]->ip_addr, $subnet['mask']); } + # check unused space between IP addresses + else { + // compressed and dhcp? + if($addresses[$n-1]->class=="compressed-range") { $unused = $Addresses->find_unused_addresses ( $addresses[$n-1]->stopIP, $addresses[$n]->ip_addr, $subnet['mask'] ); } + //uncompressed + else { $unused = $Addresses->find_unused_addresses ( $addresses[$n-1]->ip_addr, $addresses[$n]->ip_addr, $subnet['mask'] ); } + } + + # if there is some result for unused print it - if sort == ip_addr + if($User->user->hideFreeRange!=1) { + if ( $unused && ($sort['field'] == 'ip_addr') && $sort['direction'] == "asc" ) { + print ""; + print " "; + print " "; + print ""; + } + } + + + # + # print IP address + # + + # ip - range + if(@$addresses[$n]->class=="compressed-range") + { + print ""; + print " "; + print " "; + print " "; + if($colspan['dhcp']!=0) + print " "; + // tr ends after! + + } + # ip - normal + else + { + print ""; + + # status icon + if($subnet['pingSubnet']=="1") { + //calculate + $tDiff = time() - strtotime($addresses[$n]->lastSeen); + if($addresses[$n]->excludePing=="1" ) { $hStatus = "padded"; $hTooltip = ""; } + elseif($tDiff < $statuses[0]) { $hStatus = "success"; $hTooltip = "rel='tooltip' data-container='body' data-html='true' data-placement='left' title='"._("Device is alive")."
    "._("Last seen").": ".$addresses[$n]->lastSeen."'"; } + elseif($tDiff < $statuses[1]) { $hStatus = "warning"; $hTooltip = "rel='tooltip' data-container='body' data-html='true' data-placement='left' title='"._("Device warning")."
    "._("Last seen").": ".$addresses[$n]->lastSeen."'"; } + elseif($tDiff < 2592000) { $hStatus = "error"; $hTooltip = "rel='tooltip' data-container='body' data-html='true' data-placement='left' title='"._("Device is offline")."
    "._("Last seen").": ".$addresses[$n]->lastSeen."'";} + elseif($addresses[$n]->lastSeen == "0000-00-00 00:00:00") { $hStatus = "neutral"; $hTooltip = "rel='tooltip' data-container='body' data-html='true' data-placement='left' title='"._("Device is offline")."
    "._("Last seen").": "._("Never")."'";} + else { $hStatus = "neutral"; $hTooltip = "rel='tooltip' data-container='body' data-html='true' data-placement='left' title='"._("Device status unknown")."'";} + } + else { + $hStatus = "hidden"; + $hTooltip = ""; + } + + // gateway + $gw = $addresses[$n]->is_gateway==1 ? "gateway" : ""; + + print " "; + + # resolve dns name + $resolve = $DNS->resolve_address($addresses[$n]); print ""; + + # print description - mandatory + print ""; + # Print mac address icon! + if(in_array('mac', $selected_ip_fields)) { + if(!empty($addresses[$n]->mac)) { print ""; } + else { print ""; } + } + + + # print info button for hover + if(in_array('note', $selected_ip_fields)) { + if(!empty($addresses[$n]->note)) { print ""; } + else { print ""; } + } + + # print device + if(in_array('switch', $selected_ip_fields)) { + # get device details + $device = (array) $Tools->fetch_device(null, $addresses[$n]->switch); + print ""; + } + + # print port + if(in_array('port', $selected_ip_fields)) { print ""; } + + # print owner + if(in_array('owner', $selected_ip_fields)) { print ""; } + + # print custom fields + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $myField) { + if(!in_array($myField['name'], $hidden_cfields)) { + print ""; + } + } + } + } + + # print action links if user can edit + print ""; + + print ''. "\n"; + + /* if last one return ip address and broadcast IP + ****************************************************/ + if ( $n == $m ) + { + if($User->user->hideFreeRange!=1) { + # compressed? + if(isset($addresses[$n]->stopIP)) { $unused = $Addresses->find_unused_addresses ( $addresses[$n]->stopIP, $Subnets->transform_to_decimal($subnet_detailed['broadcast']), $subnet['mask'] ); } + else { $unused = $Addresses->find_unused_addresses ( $addresses[$n]->ip_addr, $Subnets->transform_to_decimal($subnet_detailed['broadcast']), $subnet['mask'] ); } + + if ( $unused ) { + print ""; + print " "; + print " "; + print ""; + } + } + } + } + /* next IP address for free check */ + $n++; + } + $c++; + } +} +?> + +
    "._('IP address')." "; if($sort['field'] == "ip_addr") print $icon; print ""._('Hostname')." "; if($sort['field'] == "dns_name") print $icon; print ""._('Description')." "; if($sort['field'] == "description") print $icon; print "
    '.$unused['ip'].' (' .$Subnets->reformat_number($unused['hosts']).')
    $unused[ip] ($unused[hosts])
    "; + # status icon + if($subnet['pingSubnet']=="1") { + print " "; + } + print $Subnets->transform_to_dotted( $addresses[$n]->ip_addr).' - '.$Subnets->transform_to_dotted( $addresses[$n]->stopIP)." (".$addresses[$n]->numHosts.")"; + print $Addresses->address_type_format_tag($addresses[$n]->state); + print " ".$Addresses->address_type_index_to_type($addresses[$n]->state)." ("._("range").")".$addresses[$n]->description."
    id)."'>".$Subnets->transform_to_dotted( $addresses[$n]->ip_addr); + if($addresses[$n]->is_gateway==1) { print " "; } + if(in_array('state', $selected_ip_fields)) { print $Addresses->address_type_format_tag($addresses[$n]->state); } + print "$resolve[name]".$addresses[$n]->description."mac."'>"; + print "
    "; + # orphaned + if(@$orphaned && $subnet_permission > 1) { + print " "; + print " "; + } + # write permitted + elseif( $subnet_permission > 1) { + if(@$addresses[$n]->class=="compressed-range") + { + print " "; + print " "; + print " "; + print " "; + print " "; + } + else + { + print " "; + print " "; + print " "; + print " "; + print " "; + } + } + # write not permitted + else { + if($addresses[$n]->class=="compressed-range") + { + print " "; + print " "; + print " "; + print " "; + print " "; + } + else + { + print " "; + print " "; + print " "; + print " "; + print " "; + } + } + print "
    "; + print "
    $unused[ip] ($unused[hosts])
    + + +$page_limit) { + $Addresses->print_pagination ($_REQUEST['sPage'], $page_repeats); +} +?> \ No newline at end of file diff --git a/app/subnets/import-subnet/import-file.php b/app/subnets/import-subnet/import-file.php new file mode 100755 index 000000000..d49b9f89d --- /dev/null +++ b/app/subnets/import-subnet/import-file.php @@ -0,0 +1,128 @@ +check_user_session(); + +# permissions +$permission = $Subnets->check_permission ($User->user, $_POST['subnetId']); + +# die if write not permitted +if($permission < 2) $Result->show("danger", _('You cannot write to this subnet'), true); +# check integer +is_numeric($_POST['subnetId']) ? : $Result->show("danger", _("Invalid subnet ID") ,true); + +# set filetype +$filetype = end(explode(".", $_POST['filetype'])); + +# get custom fields +$custom_address_fields = $Tools->fetch_custom_fields('ipaddresses'); + + +# CSV +if (strtolower($filetype) == "csv") { + /* get file to string */ + $outFile = file_get_contents('upload/import.csv') or die ($Result->show("danger", _('Cannot open upload/import.csv'), true)); + + /* format file */ + $outFile = str_replace( array("\r\n","\r") , "\n" , $outFile); //replace windows and Mac line break + $outFile = explode("\n", $outFile); +} +# XLS +elseif(strtolower($filetype) == "xls") { + # get excel object + require_once('../../../functions/php-excel-reader/excel_reader2.php'); //excel reader 2.21 + $data = new Spreadsheet_Excel_Reader('upload/import.xls', false); + + //get number of rows + $numRows = $data->rowcount(0); + $numRows++; + + //add custom fields + $numRows = $numRows + sizeof($custom_address_fields); + + //get all to array! + for($m=0; $m < $numRows; $m++) { + + //IP must be present! + if(filter_var($data->val($m,'A'), FILTER_VALIDATE_IP)) { + + $outFile[$m] = $data->val($m,'A').','.$data->val($m,'B').','.$data->val($m,'C').','.$data->val($m,'D').','; + $outFile[$m] .= $data->val($m,'E').','.$data->val($m,'F').','.$data->val($m,'G').','.$data->val($m,'H').','; + $outFile[$m] .= $data->val($m,'I'); + //add custom fields + if(sizeof($custom_address_fields) > 0) { + $currLett = "J"; + foreach($custom_address_fields as $field) { + $outFile[$m] .= ",".$data->val($m,$currLett++); + } + } + } + } +} +# die +else { + $Result->show('danger', _("Invalid file type"), true); +} + + +# import each value +foreach($outFile as $k=>$line) { + + // explode it to array for verifications + $lineArr = explode(",", $line); + + // array size must be at least 9 + if(sizeof($lineArr)<9) { + $errors[] = "Line $k is invalid"; + unset($outFile[$k]); //wrong line, unset! + } + // all good, reformat + else { + // reformat IP state + $lineArr[1] = $Addresses->address_type_type_to_index($lineArr[1]); + // reformat device from name to id + $devices = $Tools->fetch_devices (); + foreach($devices as $d) { + if($d->hostname==$lineArr[6]) { $lineArr[6] = $d->id; } + } + + // insert + $ret = $Addresses->import_address_from_csv ($lineArr, $_POST['subnetId']); + if(!is_bool($ret) && strlen($ret)>0) { $errors[] = $ret; $failed = true; } + elseif($ret===false) { $failed = true; } + } +} + +# print success if no errors +if(@$failed===true) { + # errors + if(sizeof($errors)>0) { + foreach($errors as $e) { + $Result->show("danger", _("Error").": "._($e), false); + } + } else { + "
    ".$Result->show("danger", _('Import failed'), false); + } +} +else { + $Result->show("success", _('Import successfull'), false); + + # erase file on success + unlink('upload/import.'.$filetype); +} +?> \ No newline at end of file diff --git a/app/subnets/import-subnet/import-template.php b/app/subnets/import-subnet/import-template.php new file mode 100755 index 000000000..8790cc11e --- /dev/null +++ b/app/subnets/import-subnet/import-template.php @@ -0,0 +1,56 @@ +check_user_session(); + + +// Create a workbook +$filename = "phpipam_template_". date("Y-m-d") .".xls"; +$workbook = new Spreadsheet_Excel_Writer(); + +//get all custom fields! +$custom_address_fields = $Tools->fetch_custom_fields('ipaddresses'); + +// Create a worksheet +$worksheet = $workbook->addWorksheet("template"); + +$lineCount = 1; + +// set headers +$worksheet->write($lineCount, 0, _('ip address')); +$worksheet->write($lineCount, 1, _('ip state')); +$worksheet->write($lineCount, 2, _('description')); +$worksheet->write($lineCount, 3, _('hostname')); +$worksheet->write($lineCount, 4, _('mac')); +$worksheet->write($lineCount, 5, _('owner')); +$worksheet->write($lineCount, 6, _('device')); +$worksheet->write($lineCount, 7, _('port')); +$worksheet->write($lineCount, 8, _('note')); +$fc = 9; +foreach($custom_address_fields as $k=>$f) { + $worksheet->write($lineCount, $fc, $k); + $fc++; +} + +// sending HTTP headers +$workbook->send($filename); + +// Let's send the file +$workbook->close(); + +?> \ No newline at end of file diff --git a/app/subnets/import-subnet/import-verify.php b/app/subnets/import-subnet/import-verify.php new file mode 100755 index 000000000..f2e6eb3d2 --- /dev/null +++ b/app/subnets/import-subnet/import-verify.php @@ -0,0 +1,36 @@ + \ No newline at end of file diff --git a/app/subnets/import-subnet/index.php b/app/subnets/import-subnet/index.php new file mode 100755 index 000000000..7dac60ed0 --- /dev/null +++ b/app/subnets/import-subnet/index.php @@ -0,0 +1,193 @@ +check_user_session(); + +# permissions +$permission = $Subnets->check_permission ($User->user, $_POST['subnetId']); + +# die if write not permitted +if($permission < 2) { $Result->show("danger", _('You cannot write to this subnet'), true); } + +# get custom fields +$custom_address_fields = $Tools->fetch_custom_fields('ipaddresses'); +?> + + +
    + + + +
    + + 0) { + $custFields = " | "; + foreach($custom_address_fields as $myField) { + $custFields .= "$myField[name] | "; + } + # remove last | + $custFields = substr($custFields, 0,-2); + } + ?> + + + ( ip | State | Description | hostname | MAC | Owner | Device | Port | Note '); ?> ) +
    + +

    + + + Download template + + +

    1.) :

    +
    + +
    +
    + + + : Browse +
    + + +
      + +
    + +
    + + + + + + + + + + + + +

    2.) :

    +
    + + + + + +
    + + +
    +
    + + +
    + +
    \ No newline at end of file diff --git a/app/subnets/import-subnet/print-file.php b/app/subnets/import-subnet/print-file.php new file mode 100755 index 000000000..77ec972c3 --- /dev/null +++ b/app/subnets/import-subnet/print-file.php @@ -0,0 +1,134 @@ +check_user_session(); + +# set filetype +$filetype = end(explode(".", $_POST['filetype'])); + +# get custom fields +$custom_address_fields = $Tools->fetch_custom_fields('ipaddresses'); + + +# CSV +if (strtolower($filetype) == "csv") { + /* get file to string */ + $outFile = file_get_contents('upload/import.csv') or die ($Result->show("danger", _('Cannot open upload/import.csv'), true)); + + /* format file */ + $outFile = str_replace( array("\r\n","\r") , "\n" , $outFile); //replace windows and Mac line break + $outFile = explode("\n", $outFile); +} +# XLS +elseif(strtolower($filetype) == "xls") { + # get excel object + require_once('../../../functions/php-excel-reader/excel_reader2.php'); //excel reader 2.21 + $data = new Spreadsheet_Excel_Reader('upload/import.xls', false); + + //get number of rows + $numRows = $data->rowcount(0); + $numRows++; + + //get all to array! + for($m=0; $m < $numRows; $m++) { + + //IP must be present! + if(filter_var($data->val($m,'A'), FILTER_VALIDATE_IP)) { + + $outFile[$m] = $data->val($m,'A').','.$data->val($m,'B').','.$data->val($m,'C').','.$data->val($m,'D').','; + $outFile[$m] .= $data->val($m,'E').','.$data->val($m,'F').','.$data->val($m,'G').','.$data->val($m,'H').','; + $outFile[$m] .= $data->val($m,'I'); + //add custom fields + if(sizeof($custom_address_fields) > 0) { + $currLett = "J"; + foreach($custom_address_fields as $field) { + $outFile[$m] .= ",".$data->val($m,$currLett++); + } + } + } + } +} +# die +else { + $Result->show('danger', _("Invalid file type"), true); +} + + +/* + * print table + *********************/ +print ''; + +// headers +print ''; +print ' '; +print ' '; +print ' '; +print ' '; +print ' '; +print ' '; +print ' '; +print ' '; +print ' '; +// Add custom fields +if(sizeof($custom_address_fields) > 0) { + foreach($custom_address_fields as $field) { + print " "; + } +} +print ''; + + +// values - $outFile is provided by showscripts +$errors = 0; +foreach($outFile as $line) { + + //put it to array + $field = explode(",", $line); + + //verify IP address + if(!filter_var($field[0], FILTER_VALIDATE_IP)) { $class = "danger"; $errors++; } + else { $class = ""; } + + //print + print ''; + foreach ($field as $value) { + if (!empty($field[0])) { //IP address must be present otherwise ignore field + print ''; + } + } + print ''; +} +print '
    '._('IP').''._('Status').''._('Description').''._('Hostname').''._('MAC').''._('Owner').''._('Switch').''._('Port').''._('Note').'$field[name]
    '. $value .'
    '; +?> + + +

    3.)

    +
    +0) { + print "
    "._("Errors marked with red will be ignored from importing")."!
    "; +} +?> +
    ? + + +
    + + +
    diff --git a/app/subnets/index.php b/app/subnets/index.php new file mode 100755 index 000000000..26568bf46 --- /dev/null +++ b/app/subnets/index.php @@ -0,0 +1,96 @@ +check_user_session(); + +# subnet id must be numeric +if(!is_numeric($_GET['subnetId'])) { $Result->show("danger", _('Invalid ID'), true); } + +# fetch subnet reated stuff +$custom_fields = $Tools->fetch_custom_fields ('subnets'); //custom fields +$subnet = (array) $Subnets->fetch_subnet(null, $_GET['subnetId']); //subnet details +if(sizeof($subnet)==0) { $Result->show("danger", _('Subnet does not exist'), true); } //die if empty +$subnet_detailed = $Subnets->get_network_boundaries ($subnet['subnet'], $subnet['mask']); //set network boundaries +$slaves = $Subnets->has_slaves ($subnet['id']) ? true : false; //check if subnet has slaves and set slaves flag true/false + +# permissions +$subnet_permission = $Subnets->check_permission($User->user, $subnet['id']); //subnet permission +$section_permission = $Sections->check_permission($User->user, $subnet['sectionId']); //section permission +if($subnet_permission == 0) { $Result->show("danger", _('You do not have permission to access this network'), true); } + +# fetch VLAN details +$vlan = (array) $Tools->fetch_object("vlans", "vlanId", $subnet['vlanId']); + +# fetch all addresses and calculate usage +if($slaves) { + $addresses = $Addresses->fetch_subnet_addresses_recursive ($subnet['id'], false); + $slave_subnets = (array) $Subnets->fetch_subnet_slaves ($subnet['id']); + $subnet_usage = $Subnets->calculate_subnet_usage (gmp_strval(sizeof($addresses)), $subnet['mask'], $subnet['subnet'] ); //Calculate free/used etc +} else { + $addresses = $Addresses->fetch_subnet_addresses ($subnet['id']); + $subnet_usage = $Subnets->calculate_subnet_usage (gmp_strval(sizeof($addresses)), $subnet['mask'], $subnet['subnet'] ); //Calculate free/used etc +} + +# verify that is it displayed in proper section, otherwise warn! +if($subnet['sectionId']!=$_GET['section']) { + $sd = (array) $Sections->fetch_section(null,$subnet['sectionId']); + $Result->show("warning", _("Subnet is in section")."$sd[name]!", false); +} +?> + + +
    + + +
    + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + identify_address($subnet['subnet']) == "IPv4") { + if($User->settings->visualLimit > 0) { + if($User->settings->visualLimit <= $subnet['mask'] && !$slaves) { + include('subnet-visual.php'); + } + } + } + ?> +
    + + +
    + fetch_subnet_addresses ($subnet['id']); + if(sizeof($addresses)>0) { + # set flag + $orphaned = true; + include('addresses/print-address-table.php'); + } + } + ?> +
    + +
    \ No newline at end of file diff --git a/app/subnets/scan/subnet-scan-execute.php b/app/subnets/scan/subnet-scan-execute.php new file mode 100755 index 000000000..046be88b7 --- /dev/null +++ b/app/subnets/scan/subnet-scan-execute.php @@ -0,0 +1,44 @@ +settings); +$DNS = new DNS ($Database, $User->settings); +$Result = new Result (); + +# verify that user is logged in +$User->check_user_session(); + +# subnet Id must be a integer +if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true); } + +# verify that user has write permissionss for subnet +if($Subnets->check_permission ($User->user, $_POST['subnetId']) != 3) { $Result->show("danger", _('You do not have permissions to modify hosts in this subnet')."!", true); } + +# fetch subnet details +$subnet = $Subnets->fetch_subnet (null, $_POST['subnetId']); +$subnet!==false ? : $Result->show("danger", _("Invalid ID"), true, true); + +# verify ping path +if(!file_exists($Scan->php_exec)) { $Result->show("danger", _("Invalid ping path"), true); } + + + +# invoke proper script! +if($_POST['type']=="scan-icmp") { include("subnet-scan-icmp.php"); } +elseif($_POST['type']=="scan-telnet") { include("subnet-scan-telnet.php"); } +elseif($_POST['type']=="update-icmp") { include("subnet-update-icmp.php"); } +else { $Result->show("danger", _("Invalid scan type"), true); } + +?> \ No newline at end of file diff --git a/app/subnets/scan/subnet-scan-icmp-result.php b/app/subnets/scan/subnet-scan-icmp-result.php new file mode 100755 index 000000000..b18212c2f --- /dev/null +++ b/app/subnets/scan/subnet-scan-icmp-result.php @@ -0,0 +1,64 @@ +check_user_session(); + + +# subnet Id must be a integer +if(!is_numeric($_POST['subnetId']) || $_POST['subnetId']==0) { $Result->show("danger", _("Invalid ID"), true); } +# verify that user has write permissionss for subnet +if($Subnets->check_permission ($User->user, $_POST['subnetId']) != 3) { $Result->show("danger", _('You do not have permissions to modify hosts in this subnet')."!", true, true); } + + +# ok, lets get results form post array! +foreach($_POST as $key=>$line) { + // IP address + if(substr($key, 0,2)=="ip") { $res[substr($key, 2)]['ip_addr'] = $line; } + // description + if(substr($key, 0,11)=="description") { $res[substr($key, 11)]['description'] = $line; } + // dns name + if(substr($key, 0,8)=="dns_name") { $res[substr($key, 8)]['dns_name'] = $line; } + //verify that it is not already in table! + if(substr($key, 0,2)=="ip") { + if($Addresses->address_exists ($line, $_POST['subnetId']) === true) { + $Result->show("danger", "IP address $line already exists!", true); + + } + } +} + +# insert entries +if(sizeof($res)>0) { + $errors = 0; + foreach($res as $r) { + # set insert values + $values = array("ip_addr"=>$Subnets->transform_to_decimal($r['ip_addr']), + "dns_name"=>$r['dns_name'], + "subnetId"=>$_POST['subnetId'], + "description"=>$r['description'], + "lastSeen"=>date("Y-m-d H:i:s") + ); + # insert + if(!$Admin->object_modify("ipaddresses", "add", "id", $values)) { $Result->show("danger", "Failed to import entry ".$r['ip_addr'], false); $errors++; } + } + + # success if no errors + if($errors==0) { $Result->show("success", _("Scan results added to database")."!", true); } +} +# error +else { $Result->show("danger", _("No entries available"), true); } +?> \ No newline at end of file diff --git a/app/subnets/scan/subnet-scan-icmp.php b/app/subnets/scan/subnet-scan-icmp.php new file mode 100644 index 000000000..0705d0478 --- /dev/null +++ b/app/subnets/scan/subnet-scan-icmp.php @@ -0,0 +1,89 @@ +php_exec." ".dirname(__FILE__) . '/../../../functions/scan/subnet-scan-icmp-execute.php'." 'discovery' ".$_POST['subnetId']; + +# save result to $output +exec($cmd, $output, $retval); + +# format result back to object +$script_result = json_decode($output[0]); + +//title +print "
    "._('Scan results').":

    "; + +# die if error +if($retval!=0) { $Result->show("danger", "Error executing scan! Error code - $retval", false); } +# error? +elseif($script_result->status===1) { $Result->show("danger", $script_result->error, false); } +# empty +elseif(!isset($script_result->values->alive)) { $Result->show("danger", _("No alive host found")."!", false); } +# ok +else { + + //form + print "
    "; + print ""; + + // titles + print ""; + print " "; + print " "; + print " "; + print " "; + print ""; + + // alive + $m=0; + foreach($script_result->values->alive as $ip) { + //resolve? + $address1 = array("ip_addr"=>$Subnets->transform_to_dotted($ip), "dns_name"=>""); + $hostname = $DNS->resolve_address ( (object) $address1, true); + + print ""; + //ip + print ""; + //description + print ""; + //hostname + print ""; + //remove button + print ""; + print ""; + + $m++; + } + + //result + print ""; + print " "; + print ""; + + //submit + print ""; + print " "; + print ""; + + print "
    "._("IP").""._("Description").""._("Hostname")."
    ".$Subnets->transform_to_dotted($ip).""; + print " "; + print " "; + print ""; + print " "; + print "
    "; + print "
    "; + print "
    "; + print " "._("Add discovered hosts").""; + print "
    "; + print "
    "; +} +//print scan method +print "
    Scan method: ".$Scan->settings->scanPingType.""; + +# show debug? +if($_POST['debug']==1) { print "
    "; print_r($output[0]); print "
    "; } + +?> \ No newline at end of file diff --git a/app/subnets/scan/subnet-scan-telnet-result.php b/app/subnets/scan/subnet-scan-telnet-result.php new file mode 100644 index 000000000..685eccba2 --- /dev/null +++ b/app/subnets/scan/subnet-scan-telnet-result.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/app/subnets/scan/subnet-scan-telnet.php b/app/subnets/scan/subnet-scan-telnet.php new file mode 100755 index 000000000..087034c9d --- /dev/null +++ b/app/subnets/scan/subnet-scan-telnet.php @@ -0,0 +1,103 @@ +show("danger", _('Please enter ports to scan').'!', true); } + +//verify ports +$pcheck = explode(";", str_replace(",",";",$_POST['port'])); +foreach($pcheck as $p) { + if(!is_numeric($p)) { + $Result->show("danger", _("Invalid port")." ($p)", true); + } +} +$_POST['port'] = str_replace(";",",",$_POST['port']); + + +# invoke CLI with threading support +$cmd = $Scan->php_exec." ".dirname(__FILE__) . "/../../../functions/scan/subnet-scan-telnet-execute.php $_POST[subnetId] '$_POST[port]'"; + +# save result to $output +exec($cmd, $output, $retval); + +# format result back to object +$script_result = json_decode($output[0]); + + + +//title +print "
    "._('Scan results').":

    "; + +# die if error +if($retval!=0) { $Result->show("danger", "Error executing scan! Error code - $retval", false); } +# error? +elseif($script_result->status===1) { $Result->show("danger", $script_result->error, false); } +# empty +elseif(!isset($script_result->values->alive)) { $Result->show("danger", _("No alive host found")."!", false); } +# ok +else { + print "
    "; + print ""; + + // titles + print ""; + print " "; + print " "; + print " "; + print " "; + print ""; + + // alive + $m=0; + foreach($script_result->values->alive as $ip=>$port) { + //resolve? + $address1 = array("ip_addr"=>$Subnets->transform_to_dotted($ip), "dns_name"=>""); + $hostname = $DNS->resolve_address ( (object) $address1, true); + + print ""; + //ip + print ""; + //description + print ""; + //hostname + print ""; + //remove button + print ""; + print ""; + + $m++; + } + + //result + print ""; + print " "; + print ""; + + //submit + print ""; + print " "; + print ""; + + print "
    "._("IP").""._("Description").""._("Hostname")."
    ".$Subnets->transform_to_dotted($ip).""; + print " "; + print " "; + print ""; + print " "; + print "
    "; + print "
    "; + print "
    "; + print " "._("Add discovered hosts").""; + print "
    "; + print "
    "; +} + +//print scan method +print "
    Scan method: telnet"; + +# show debug? +if($_POST['debug']==1) { print "
    "; print_r($output[0]); print "
    "; } + +?> \ No newline at end of file diff --git a/app/subnets/scan/subnet-scan.php b/app/subnets/scan/subnet-scan.php new file mode 100755 index 000000000..3eb25265e --- /dev/null +++ b/app/subnets/scan/subnet-scan.php @@ -0,0 +1,105 @@ +check_user_session(); + + +# ID must be numeric +if(!is_numeric($_POST['subnetId'])) { $Result->show("danger", _("Invalid ID"), true, true); } + +# verify that user has write permissionss for subnet +if($Subnets->check_permission ($User->user, $_POST['subnetId']) != 3) { $Result->show("danger", _('You do not have permissions to modify hosts in this subnet')."!", true, true); } + +# fetch subnet details +$subnet = $Subnets->fetch_subnet (null, $_POST['subnetId']); +$subnet!==false ? : $Result->show("danger", _("Invalid ID"), true, true); + +# IPv6 scanning is not supported +if ( $Subnets->identify_address($subnet->subnet) == "IPv6") { $Result->show("danger", _('IPv6 scanning is not supported').'!', true, true); } +?> + + + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    transform_to_dotted($subnet->subnet)."/$subnet->mask ($subnet->description)"; ?>
    + +
    + +
    + + +
    + ·
    + ·
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    +
    \ No newline at end of file diff --git a/app/subnets/scan/subnet-update-icmp.php b/app/subnets/scan/subnet-update-icmp.php new file mode 100755 index 000000000..72f9295e9 --- /dev/null +++ b/app/subnets/scan/subnet-update-icmp.php @@ -0,0 +1,110 @@ +php_exec." ".dirname(__FILE__) . '/../../../functions/scan/subnet-scan-icmp-execute.php'." 'update' ".$_POST['subnetId']; + +# save result to $output +exec($cmd, $output, $retval); + +# format result back to object +$script_result = json_decode($output[0]); + + +# recode to same array with statuses +$m=0; +if($script_result->status==0) { + //loop types (dead, alive, error) + if(sizeof($script_result->values)>0) { + foreach($script_result->values as $k=>$r) { + //loop addresses in type + foreach($r as $ip) { + # get details + $ipdet = (array) $Addresses->fetch_address_multiple_criteria ($ip, $_POST['subnetId']); + + # format output + $res[$ip]['ip_addr'] = $ip; + $res[$ip]['description'] = $ipdet['description']; + $res[$ip]['dns_name'] = $ipdet['dns_name']; + + //online + if($k=="alive") { + $res[$ip]['status'] = "Online"; + $res[$ip]['code']=0; + //update alive time + @$Scan->ping_update_lastseen($ipdet['id']); + } + //offline + elseif($k=="dead") { + $res[$ip]['status'] = "Offline"; + $res[$ip]['code']=1; + } + //excluded + elseif($k=="excluded") { + $res[$ip]['status'] = "Excluded form check"; + $res[$ip]['code']=100; + } + else { + $res[$ip]['status'] = "Error"; + $res[$ip]['code']=2; + } + $m++; + } + } + } +} +?> + + +
    :
    +
    + +show("danger", "Error executing scan! Error code - $retval", false); } +# error? +elseif($script_result->status===1) { $Result->show("danger", $script_result->error, false); } +# empty +elseif(!isset($script_result->values)) { $Result->show("info", _("Subnet is empty")."!", false); } +# ok +else { + # order by IP address + ksort($res); + + //table + print ""; + + //headers + print ""; + print " "; + print " "; + print " "; + print " "; + print ""; + + //loop + foreach($res as $r) { + //set class + if($r['code']==0) { $class='success'; } + elseif($r['code']==100) { $class='warning'; } + else { $class='danger'; } + + print ""; + print " "; + print " "; + print " "; + print " "; + + print ""; + } + print "
    "._('IP').""._('Description').""._('status').""._('hostname')."
    ".$Subnets->transform_to_dotted($r['ip_addr'])."".$r['description'].""._("$r[status]")."".$r['dns_name']."
    "; +} +//print scan method +print "
    Scan method: ".$Scan->settings->scanPingType.""; + +# show debug? +if($_POST['debug']==1) { print "
    "; print_r($output[0]); print "
    "; } +?> \ No newline at end of file diff --git a/app/subnets/subnet-changelog.php b/app/subnets/subnet-changelog.php new file mode 100755 index 000000000..c6c3fc12b --- /dev/null +++ b/app/subnets/subnet-changelog.php @@ -0,0 +1,170 @@ +check_user_session(); + +# get clog entries for current subnet +$clogs = $Tools->fetch_changlog_entries("subnet", $_GET['subnetId'], true); +# subnet changelog for all slave subnets +$clogsSlaves = $Tools->fetch_subnet_slaves_changlog_entries_recursive($_GET['subnetId']); +# changelog for each IP address, also in slave subnets +$clogsAddresses = $Tools->fetch_subnet_addresses_changelog_recursive($_GET['subnetId']); //se ne dela ! + +# get subnet details +$subnet = (array) $Subnets-> fetch_subnet("id",$_GET['subnetId']); + + +# permissions +$permission = $Subnets->check_permission ($User->user, $_GET['subnetId']); +if($permission == 0) { $Result->show("danger", _('You do not have permission to access this network'), true); } + +# header +print "

    "._('Subnet')." - "._('Changelog')."


    "; + +# back +if($subnet['isFolder']==1) { + print " "._('Back to subnet').""; +} else { + print " "._('Back to subnet').""; +} + + +/* current subnet changelog */ + +# empty +if(sizeof($clogs)==0) { + print "
    "; + print "

    "._("No changelogs available")."

    "; + print ""._("No changelog entries are available for this subnet").""; + print "
    "; +} +# result +else { + # printout + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # logs + foreach($clogs as $l) { + $l = (array) $l; + # format diff + $l['cdiff'] = str_replace("\n", "
    ", $l['cdiff']); + + print ""; + print " "; + # folder? + if($subnet['isFolder']==1) { print " "; } + else { print " "; } + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + } + + print "
    "._('User').""._('Subnet').""._('Description').""._('Action').""._('Result').""._('Date').""._('Change')."
    $l[real_name]$subnet[description]$subnet[ip]/$subnet[mask]$l[description]"._("$l[caction]").""._("$l[cresult]")."$l[cdate]$l[cdiff]
    "; +} + + +/* Subnet slaves changelog */ + +# empty +if($clogsSlaves) { + # header + print "

    "._('Slave subnets')." "._('Changelog')."


    "; + + # printout + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # logs + foreach($clogsSlaves as $l) { + $l = (array) $l; + # format diff + $l['cdiff'] = str_replace("\n", "
    ", $l['cdiff']); + + print ""; + print " "; + # folder? + if($l['isFolder']==1) { print " "; } + else { print " "; } + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + } + + print "
    "._('User').""._('Subnet').""._('Description').""._('Action').""._('Result').""._('Date').""._('Change')."
    $l[real_name]$l[description]".$Subnets->transform_to_dotted($l['subnet'])."/$l[mask]$l[description]"._("$l[caction]").""._("$l[cresult]")."$l[cdate]$l[cdiff]
    "; +} + + +/* IP changelog */ + +if($clogsAddresses) { + # header + print "

    "._('Underlying hosts')." "._('Changelog')."


    "; + + # printout + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # logs + foreach($clogsAddresses as $l) { + $l = (array) $l; + # format diff + $l['cdiff'] = str_replace("\n", "
    ", $l['cdiff']); + + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + } + + print "
    "._('User').""._('IP').""._('Action').""._('Result').""._('Date').""._('Change')."
    $l[real_name]".$Subnets->transform_to_dotted($l['ip_addr']).""._("$l[caction]").""._("$l[cresult]")."$l[cdate]$l[cdiff]
    "; +} + + +?> \ No newline at end of file diff --git a/app/subnets/subnet-details.php b/app/subnets/subnet-details.php new file mode 100755 index 000000000..d2e68ca95 --- /dev/null +++ b/app/subnets/subnet-details.php @@ -0,0 +1,318 @@ + + + +

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + find_gateway($subnet['id']); + if($gateway !==false) { ?> + + + + + + + + + + + + + settings->enableVRF==1) { + # get vrf details + $vrf = (array) $Tools->fetch_vrf(null,$subnet['vrfId']); + # set text + $vrfText = $vrf['name']; + if(!empty($vrf['description'])) { $vrfText .= " [$vrf[description]]";} + + print ""; + print " "; + print " "; + print ""; + } + + if(!$slaves) { + # divider + print ""; + print " "; + print " "; + print ""; + + # Are IP requests allowed? + if ($User->settings->enableIPrequests==1) { + print ""; + print " "; + if($subnet['allowRequests'] == 1) { print " "; } # yes + else { print " ";} # no + print ""; + } + # ping-check hosts inside subnet + print ""; + print " "; + if($subnet['pingSubnet'] == 1) { print " "; } # yes + else { print " ";} # no + print ""; + # scan subnet for new hosts * + print ""; + print " "; + if($subnet['discoverSubnet'] == 1) { print " "; } # yes + else { print " ";} # no + print ""; + } + ?> + + 0) { + foreach($custom_fields as $key=>$field) { + if(strlen($subnet[$key])>0) { + $subnet[$key] = str_replace(array("\n", "\r\n"), "
    ",$subnet[$key]); + $html_custom[] = ""; + $html_custom[] = " "; + $html_custom[] = " "; + $html_custom[] = ""; + } + } + + # any? + if(isset($html_custom)) { + # divider + print ""; + print " "; + print " "; + print ""; + + print implode("\n", $html_custom); + } + } + + # check for temporary shares! + if($User->settings->tempShare==1) { + foreach(json_decode($User->settings->tempAccess) as $s) { + if($s->type=="subnets" && $s->id==$subnet['id']) { + if(time()<$s->validity) { + $active_shares[] = $s; + } + else { + $expired_shares[] = $s; + } + } + } + if(sizeof(@$active_shares)>0) { + # divider + print ""; + print " "; + print " "; + print ""; + # print + print ""; + print ""; + print ""; + } + if(sizeof(@$expired_shares)>0) { + # divider + print ""; + print " "; + print " "; + print ""; + # print + print ""; + print ""; + print ""; + } + } + + print ""; + print ""; + print ""; + + # action button groups + print ""; + print " "; + print " "; + print ""; + ?> + +
    $subnet[ip]/$subnet[mask] ($subnet_detailed[netmask])"; ?>
    + +
    parse_permissions($subnet_permission); ?>
    + reformat_number ($subnet_usage['used']) .' | + '._('Free').': '. $Subnets->reformat_number ($subnet_usage['freehosts']) .' ('. $subnet_usage['freehosts_percent'] .'%) | + '._('Total').': '. $Subnets->reformat_number ($subnet_usage['maxhosts']); + ?> +
    transform_to_dotted($gateway->ip_addr);?>
    + /"; } //Display fix for emprt VLAN + print $vlan['number']; + + if(!empty($vlan['name'])) { print ' - '.$vlan['name']; } //Print name if provided + if(!empty($vlan['description'])) { print ' ['. $vlan['description'] .']'; } //Print description if provided + ?> +
    "._('VRF')."$vrfText

    "._('IP requests').""._('enabled').""._('disabled')."
    "._('Hosts check').""._('enabled').""._('disabled')."
    "._('Discover new hosts').""._('enabled').""._('disabled')."
    $key"; + #booleans + if($field['type']=="tinyint(1)") { + if($subnet[$key] == "0") { $html_custom[] = _("No"); } + elseif($subnet[$key] == "1") { $html_custom[] = _("Yes"); } + } + else { + $html_custom[] = $subnet[$key]; + } + $html_custom[] = "


    "._("Active subnet shares").":"; + $m=1; + foreach($active_shares as $s) { + print " Share $m ("._("Expires")." ".date("Y-m-d H:i:s", $s->validity).")
    "; + $m++; + } + print "
    "; + print "

    "._("Expired subnet shares").":"; + $m=1; + foreach($expired_shares as $s) { + print " Share $m ("._("Expired")." ".date("Y-m-d H:i:s", $s->validity).")
    "; + $m++; + } + print "
    "; + print "

    "._('Actions').""; + + print "
    "; + + # set values for permissions + if($subnet_permission == 1) { + $sp['editsubnet']= false; //edit subnet + $sp['editperm'] = false; //edit permissions + + $sp['addip'] = false; //add ip address + $sp['scan'] = false; //scan subnet + $sp['changelog'] = false; //changelog view + $sp['import'] = false; //import + } + else if ($subnet_permission == 2) { + $sp['editsubnet']= false; //edit subnet + $sp['editperm'] = false; //edit permissions + + $sp['addip'] = true; //add ip address + $sp['scan'] = true; //scan subnet + $sp['changelog'] = true; //changelog view + $sp['import'] = true; //import + } + else if ($subnet_permission == 3) { + $sp['editsubnet']= true; //edit subnet + $sp['editperm'] = true; //edit permissions + + $sp['addip'] = true; //add ip address + $sp['scan'] = true; //scan subnet + $sp['changelog'] = true; //changelog view + $sp['import'] = true; //import + } + + # edit / permissions / nested + print "
    "; + # warning + if($subnet_permission == 1) + print " "; + # edit subnet + if($sp['editsubnet']) + print " "; + else + print " "; + # permissions + if($sp['editperm']) + print " "; + else + print " "; + # add nested subnet + if($section_permission == 3) { + print " "; + } else { + print " "; + } + print "
    "; + + # favourites / changelog + print "
    "; + # favourite + if($User->is_subnet_favourite($subnet['id'])) + print " "; + else + print " "; + # changelog + if($User->settings->enableChangelog==1) { + if($sp['changelog']) + print " "; + else + print " "; + } + print "
    "; + + # add / requests / scan + if(!$slaves) { + print "
    "; + // if full prevent new + if($Subnets->reformat_number($subnet_usage['freehosts'])=="0" || !$sp['addip']) + print " "; + else + print " "; + //requests + if($subnet['allowRequests'] == 1 && $subnet_permission<3) { + print " "; + } + // subnet scan + if($sp['scan']) + print " "; + else + print " "; + print "
    "; + + # export / import + print "
    "; + //import + if($sp['import']) + print " "; + else + print " "; + //export + print " "; + //share + if($subnet_permission>1 && $User->settings->tempShare==1) { + print " "; + } + print "
    "; + } + + print "
    "; + print "
    \ No newline at end of file diff --git a/app/subnets/subnet-graph.php b/app/subnets/subnet-graph.php new file mode 100755 index 000000000..0577268a0 --- /dev/null +++ b/app/subnets/subnet-graph.php @@ -0,0 +1,92 @@ +calculate_subnet_usage_detailed( $subnet['subnet'], $subnet['mask'], $addresses); + +?> + + +

    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/app/subnets/subnet-slaves.php b/app/subnets/subnet-slaves.php new file mode 100755 index 000000000..e68cbf55a --- /dev/null +++ b/app/subnets/subnet-slaves.php @@ -0,0 +1,153 @@ + +$subnet[description] ($subnet[ip]/$subnet[mask]) "._('has')." ".sizeof($slave_subnets)." "._('directly nested subnets').":

    "; + +# print HTML table +print ''. "\n"; + +# headers +print ""; +print " "; +print " "; +print " "; +print " "; +print " "; +print " "; +print " "; +print ""; + +$m = 0; //slave index + +# loop +foreach ($slave_subnets as $slave_subnet) { + # cast to array + $slave_subnet = (array) $slave_subnet; + + # if first check for free space if permitted + if($User->user->hideFreeRange!=1) { + if($m == 0) { + # if master start != first slave start print free space + if($subnet['subnet'] != $slave_subnet['subnet']) { + # calculate diff between subnet and slave + $diff = (int) gmp_strval(gmp_sub($slave_subnet['subnet'], $subnet['subnet'])); + + print ""; + print " "; + print " "; + print " "; + print ""; + } } } + + + # get VLAN details + $slave_vlan = (array) $Tools->fetch_object("vlans", "vlanId", $slave_subnet['vlanId']); + if(!$slave_vlan) { $slave_vlan['number'] = "/"; } //reformat empty vlan + + print ""; + print " "; + print " "; + print " "; + + # calculate free / used / percentage + if(!$Subnets->has_slaves ($slave_subnet['id'])) { + $slave_addresses = (int) $Addresses->count_subnet_addresses ($slave_subnet['id']); + $calculate = $Subnets->calculate_subnet_usage( $slave_addresses, $slave_subnet['mask'], $slave_subnet['subnet']); + } else { + $calculate = $Subnets->calculate_subnet_usage_recursive( $slave_subnet['id'], $slave_subnet['subnet'], $slave_subnet['mask'], $Addresses); + } + + print ' '. "\n"; + print ' '; + + # allow requests + if($slave_subnet['allowRequests'] == 1) { print ''; } + else { print ''; } + + # edit + $slave_subnet_permission = $Subnets->check_permission($User->user, $subnet['id']); + if($slave_subnet_permission == 3) { + print " "; + } + else { + print " "; + } + + print '' . "\n"; + + + # check if some free space between this and next subnet + if($User->user->hideFreeRange!=1) { + if(isset($slave_subnets[$m+1])) { + + # set max host > (bcast) from current slave + $current_slave_details = $Subnets->get_network_boundaries ($slave_subnet['subnet'], $slave_subnet['mask']); + $current_slave_bcast = $Subnets->transform_to_decimal($current_slave_details['broadcast']); + # calculate next slave + $next_slave_subnet = $slave_subnets[$m+1]->subnet; + # calculate diff + $diff = (int) gmp_strval(gmp_sub($next_slave_subnet, $current_slave_bcast)); + # if diff print free space + if($diff>1) { + + print ""; + print " "; + print " "; + print " "; + print ""; + } } } + + # next - for free space check + $m++; + + # if last check for free space + if($User->user->hideFreeRange!=1) { + if($m == sizeof($slave_subnets)) { + + # top subnet limit + # + # $subnet -> master subnet + # $subnet_detailed -> master subnet detailed (bcast, ...) + # $slave_subnet -> last slave subnet + # $calculate -> last slave subnet calculations + + # set max host > (bcast) from master + $max_host = $Subnets->transform_to_decimal($subnet_detailed['broadcast']); + + # set max host of subnet + $slave_details = $Subnets->get_network_boundaries ($slave_subnets[$m-1]->subnet, $slave_subnets[$m-1]->mask); + $max_last_slave = $Subnets->transform_to_decimal($slave_details['broadcast']); + + # if slave stop < master stop print free space + if($max_host > $max_last_slave) { + print ""; + print " "; + print " "; + print " "; + print ""; + } } } + +} +print '
    "._('VLAN').""._('Subnet description').""._('Subnet')."
    "._('Free space')."$subnet[ip] - ". $Subnets->transform_to_dotted(gmp_strval(gmp_add($subnet['subnet'], gmp_sub($diff,1)))) ." ( ".$diff." )
    ".@$slave_vlan['number']."$slave_subnet[description]$slave_subnet[ip]/$slave_subnet[mask]"; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    "; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    "._('Free space')."".$Subnets->transform_to_dotted(gmp_strval(gmp_add($current_slave_bcast, 1))) ." - ".$Subnets->transform_to_dotted(gmp_strval(gmp_sub($next_slave_subnet, 1))) ." ( ".gmp_strval(gmp_sub($diff, 1))." )
    "._('Free space')."". $Subnets->transform_to_dotted(gmp_strval(gmp_add($max_last_slave,1))) ." - ". $Subnets->transform_to_dotted(gmp_strval($max_host)) ." ( ".gmp_strval(gmp_sub($max_host,$max_last_slave))." )
    '. "\n"; + +?> \ No newline at end of file diff --git a/app/subnets/subnet-visual.php b/app/subnets/subnet-visual.php new file mode 100755 index 000000000..fe03347e6 --- /dev/null +++ b/app/subnets/subnet-visual.php @@ -0,0 +1,68 @@ +

    "._('Visual subnet display')." to manage IP address')."!'>


    "; +print "
    "; + +# set limits - general +$start_visual = gmp_strval(gmp_and("0xffffffff", (int) $Subnets->transform_to_decimal($subnet_detailed['network']))); +$stop_visual = gmp_strval(gmp_and("0xffffffff", (int) $Subnets->transform_to_decimal($subnet_detailed['broadcast']))); + +# remove subnet and bcast if mask < 31 +if($subnet['mask'] > 30) {} +elseif ($section['strictMode']==1) { +$start_visual = gmp_strval(gmp_add($start_visual, 1)); +$stop_visual = gmp_strval(gmp_sub($stop_visual, 1)); +} + +# we need to reindex addresses to have ip address in decimal as key! +$visual_addresses = array(); +if($addresses_visual) { +foreach($addresses_visual as $a) { + $visual_addresses[$a->ip_addr] = (array) $a; +} +} + +# print +for($m=$start_visual; $m<=$stop_visual; $m=gmp_strval(gmp_add($m,1))) { + + # already exists + if (array_key_exists((string)$m, $visual_addresses)) { + + # fix for empty states - if state is disabled, set to active + if(strlen($visual_addresses[$m]['state'])==0) { $visual_addresses[$m]['state'] = 1; } + + # to edit + $class = $visual_addresses[$m]['state']; + $id = (int) $visual_addresses[$m]['id']; + $action = 'all-edit'; + + # tooltip + $title = $Subnets->transform_to_dotted($m); + if(strlen($visual_addresses[$m]['dns_name'])>0) { $title .= "
    ".$visual_addresses[$m]['dns_name']; } + if(strlen($visual_addresses[$m]['description'])>0) { $title .= "
    ".$visual_addresses[$m]['description']; } + + # set colors + $background = $Subnets->address_types[$visual_addresses[$m]['state']]['bgcolor']; + $foreground = $Subnets->address_types[$visual_addresses[$m]['state']]['fgcolor']; + } + else { + # print add new + $class = "unused"; + $id = $m; + $action = 'all-add'; + $title = ""; + + # set colors + $background = "#ffffff"; + $foreground = "#333333"; + } + + # print box + if($subnet_permission > 1) { print ".".substr(strrchr($Subnets->transform_to_dotted($m), "."), 1).""; } + else { print ".".substr(strrchr($Subnets->transform_to_dotted($m), "."), 1).""; } +} +print "
    "; +print "
    "; # clear float +?> \ No newline at end of file diff --git a/app/subnets/subnets-menu.php b/app/subnets/subnets-menu.php new file mode 100755 index 000000000..3be46d5c6 --- /dev/null +++ b/app/subnets/subnets-menu.php @@ -0,0 +1,138 @@ +check_user_session(); + +# ID must be numeric +if(!is_numeric($_GET['section'])) { $Result->show("danger",_('Invalid ID'), true); } + + +# Admin check, otherwise load requested subnets +if ($_GET['section'] == 'Administration') { + if (!$User->isadmin) { $Result->show("danger",_('Sorry, must be admin'), true); } + else { include('admin/admin-menu.php'); } +} +# load subnets +else { + # check for possible subsection + $subsections = $Sections->fetch_subsections ($_GET['section']); + + # permissions + foreach($subsections as $k=>$ss) { + $perm = $Sections->check_permission ($User->user, $ss->id); + # remove not permitted + if($perm==0 ) { unset($subsections[$k]); } + } + + # print belonging subsections if they exist + if(sizeof(@$subsections)>0) { + # title + print "

    "._('Belonging subsections')."


    "; + # table + print ""; + + foreach($subsections as $ss) { + print ""; + print " "; + print ""; + } + print "
    $ss->name
    "; + } + + + /* print Subnets */ + + # get section details + $section = (array) $Sections->fetch_section("id", $_GET['section']); + + # verify permissions + $section_permission = $Sections->check_permission ($User->user, $_GET['section']); + + # no access + if($section_permission == 0) { $Result->show("danger",_('You do not have access to this section'), true); } + + # invalid section id + if(sizeof($section) == 0) { $Result->show("danger",_('Section does not exist'), true); } + + # expand all folders? + if(isset($_COOKIE['expandfolders'])) { + if($_COOKIE['expandfolders'] == "1") { $iconClass='fa-compress'; $action = 'open';} + else { $iconClass='fa-expand'; $action = 'close'; } + } + else { $iconClass='fa-expand'; $action = 'close';} + + # Check if it has parent, and if so print back link + if($section['masterSection']!=0) { + # get details + $master_section = (array) $Sections->fetch_section ("id", $section['masterSection']); + + print ""; + } + + # header + print "

    "._('Available subnets')."

    "; + print "
    "; + + /* print subnets menu ---------- */ + print "
    "; + # print links + $section_subnets = (array) $Subnets->fetch_section_subnets($_GET['section']); + print $Subnets->print_subnets_menu($User->user, $section_subnets); + print "
    "; + + + /* print VLAN menu ---------- */ + if($section['showVLAN'] == 1) { + $vlans = $Sections->fetch_section_vlans($_GET['section']); + + # if some is present + if($vlans) { + print "
    "; + # title + print "

    "._('Available VLANs')."


    "; + # create and print menu + print $Subnets->print_vlan_menu($User->user, $vlans, $_GET['section']); + print "
    "; + } + } + + + /* print VRF menu ---------- */ + if($User->settings->enableVRF==1 && $section['showVRF']==1) { + $vrfs = $Sections->fetch_section_vrfs($_GET['section']); + + # if some is present + if($vrfs) { + print "
    "; + # title + print "

    "._('Available VRFs')."


    "; + # create and print menu + print $Subnets->print_vrf_menu($User->user, $vrfs, $_GET['section']); + print "
    "; + } + } +} + +# add new subnet if permitted +$section_permission = $Sections->check_permission ($User->user, $_GET['section']); +if($section_permission == 3) { + print "
    "; + if(isset($_GET['subnetId'])) { + print " "; + } + print " "._('Add new'); + print "
    "; + print " "; + print " "; + print "
    "; + print "
    "; + print "
    "; +} \ No newline at end of file diff --git a/app/temp_share/address.php b/app/temp_share/address.php new file mode 100644 index 000000000..b906e190c --- /dev/null +++ b/app/temp_share/address.php @@ -0,0 +1,217 @@ + fetch_address(null, $_GET['subnetId']); + $subnet = (array) $Subnets->fetch_subnet(null, $address['subnetId']); +} + +# fetch all custom fields +$custom_fields = $Tools->fetch_custom_fields ('ipaddresses'); + +# set selected address fields array +$selected_ip_fields = $settings->IPfilter; +$selected_ip_fields = explode(";", $selected_ip_fields); //format to array +$selected_ip_fields_size = in_array('state', $selected_ip_fields) ? (sizeof($selected_ip_fields)-1) : sizeof($selected_ip_fields); //set size of selected fields +if($selected_ip_fields_size==1 && strlen($selected_ip_fields[0])==0) { $selected_ip_fields_size = 0; } //fix for 0 + + +# set ping statuses +$statuses = explode(";", $settings->pingStatus); + +# checks +if(sizeof($subnet)==0) { $Result->show("danger", _('Subnet does not exist'), true); } //subnet doesnt exist + + # resolve dns name +$DNS = new DNS ($Database); +$resolve = $DNS->resolve_address((object) $address); + +# reformat empty fields +$address = $Addresses->reformat_empty_array_fields($address, "/"); + +#header +print "

    "._('IP address details')."


    "; + +# back +if(@$temp_objects[$_GET['section']]->type=="subnets") { +print " "._('Back to subnet').""; +} + +# check if it exists, otherwise print error +if(sizeof($address)>1) { + + # table - details + print ""; + + # ip + print ""; + print " "; + print " "; + print ""; + + # description + print ""; + print " "; + print " "; + print ""; + + # hierarchy + print ""; + print " "; + print " "; + print ""; + + # subnet + print ""; + print " "; + print " "; + print ""; + + # state + print ""; + print " "; + print " "; + print ""; + + # hostname + print ""; + print " "; + print " "; + print ""; + + # mac + if(in_array('owner', $selected_ip_fields)) { + print ""; + print " "; + print " "; + print ""; + } + + # mac + if(in_array('mac', $selected_ip_fields)) { + print ""; + print " "; + print " "; + print ""; + } + + # note + if(in_array('note', $selected_ip_fields)) { + print ""; + print " "; + print " "; + print ""; + } + + # switch + if(in_array('switch', $selected_ip_fields)) { + print ""; + print " "; + if(strlen($address['switch'])>0) { + # get device + $device = $Tools->fetch_device(null, $address['switch']); + if($device!==false) { + $device = (array) $device; + $device = $Addresses->reformat_empty_array_fields($device, ""); + print " "; + } + else { + print " "; + } + } else { + print " "; + } + print ""; + } + + # port + if(in_array('port', $selected_ip_fields)) { + print ""; + print " "; + print " "; + print ""; + } + + # last edited + print ""; + print " "; + if(strlen($address['editDate'])>1) { + print " "; + } else { + print " "; + } + print ""; + + + # avalibility + print ""; + print " "; + print ""; + print ""; + + # calculate + $tDiff = time() - strtotime($address['lastSeen']); + if($address['excludePing']==1) { $seen_status = ""; $seen_text = ""; } + elseif($tDiff < $statuses[0]) { $seen_status = "success"; $seen_text = _("Device is alive")."
    "._("Last seen").": ".$address['lastSeen']; } + elseif($tDiff < $statuses[1]) { $seen_status = "warning"; $seen_text = _("Device warning")."
    "._("Last seen").": ".$address['lastSeen']; } + elseif($tDiff > $statuses[1]) { $seen_status = "error"; $seen_text = _("Device is offline")."
    "._("Last seen").": ".$address['lastSeen'];} + elseif($address['lastSeen'] == "0000-00-00 00:00:00") { $seen_status = "neutral"; $seen_text = _("Device is offline")."
    "._("Last seen").": "._("Never");} + else { $seen_status = "neutral"; $seen_text = _("Device status unknown");} + + print " "; + print " "; + print ""; + + # custom fields + if(sizeof($custom_fields) > 0) { + print ""; + print " "; + print ""; + + foreach($custom_fields as $key=>$field) { + if(strlen($address[$key])>0) { + $address[$key] = str_replace(array("\n", "\r\n"), "
    ",$address[$key]); + print ""; + print " "; + print " "; + print ""; + } + } + } + + print ""; + print "
    "._('IP address')."$address[ip]
    "._('Description')."$address[description]
    "._('Hierarchy').""; + print_breadcrumbs($Sections, $Subnets, array("page"=>"subnets", "section"=>$subnet['sectionId'], "subnetId"=>$subnet['id'], "ipaddrid"=>$address['id']), $Addresses); + print "
    "._('Subnet')."$subnet[ip]/$subnet[mask] ($subnet[description])
    "._('IP status').""; + + if ($address['state'] == "0") { $stateClass = _("Offline"); } + else if ($address['state'] == "2") { $stateClass = _("Reserved"); } + else if ($address['state'] == "3") { $stateClass = _("DHCP"); } + else { $stateClass = _("Online"); } + + print $Addresses->address_type_index_to_type ($address['state']); + print $Addresses->address_type_format_tag ($address['state']); + + print "
    "._('Hostname')."$address[dns_name]
    "._('Owner')."$address[owner]
    "._('MAC address')."$address[mac]
    "._('Note')."$address[note]
    "._('Device')."".@$device['hostname']." ".@$device['description']."//
    "._('Port')."$address[port]
    "._('Last edited')."$address[editDate]"._('Never')."

    "._('Avalibility')."
    "; + print "$seen_text"; + + print "

    $key"; + #booleans + if($field['type']=="tinyint(1)") { + if($address[$key] == 0) { print _("No"); } + elseif($address[$key] == 1) { print _("Yes"); } + } + else { + print $address[$key]; + } + print "
    "; +} +# not exisitng +else { + $Result->show("danger", _("IP address not existing in database")."!", true); +} +?> \ No newline at end of file diff --git a/app/temp_share/index.php b/app/temp_share/index.php new file mode 100644 index 000000000..88779bc4a --- /dev/null +++ b/app/temp_share/index.php @@ -0,0 +1,155 @@ +fetch_object("settings", "id", 1); +?> + + + + + + + + + + + + + + + + + + + + + + <?php print $settings->siteTitle; ?> + + + + + + + + + + + + + + + + + + +
    + + +
    ...
    + + + + + +
    +
    + +
    +
    + +tempAccess); +# check +$temp_objects = sizeof($temp_objects)>0 ? (array) $temp_objects : array(); +# set width +$max_width = (@$temp_objects[$_GET['section']]->type=="ipaddresses" || isset($_GET['subnetId'])) ? "max-width:700px" : ""; +?> + + +
    +
    + + tempShare!=1) { $Result->show("danger", _("Temporary sharing disabled"), false); } + # none + elseif(sizeof($temp_objects)==0) { write_log( "Tempory share access", $_GET['section'], 2); $Result->show("danger", _("Invalid share key")."! Login", false); } + # try to fetch object + elseif(!array_key_exists($_GET['section'], $temp_objects)) { write_log( "Tempory share access", $_GET['section'], 2); $Result->show("danger", _("Invalid share key")."! Login", false); } + # ok, include script + else { + //check if expired + if(time()>$temp_objects[$_GET['section']]->validity) { write_log( "Tempory share access", $_GET['section'], 2); $Result->show("danger", _("Share expired")."!", false); } + else { + //log + write_log( "Tempory share access", $_GET['section'], 0); + + if($temp_objects[$_GET['section']]->type=="subnets") { + # address? + if(isset($_GET['subnetId'])) { include("address.php"); } + else { include("subnet.php"); } + } + else { + # set object + $object = $temp_objects[$_GET['section']]; + + // fetch address + $address = (array) $Addresses->fetch_address(null, $object->id); + // fetch subnet + $subnet = (array) $Subnets->fetch_subnet(null, $address['subnetId']); + + include("address.php"); + } + } + + # write validity + print "
    "; + $Result->show("info", "Notification
    "._("Share expires on ").date("Y-m-d H:i:s", $temp_objects[$_GET['section']]->validity), false); + } + ?> + +
    +
    + + + + + +
    + + +
    + + + + + + + + + + \ No newline at end of file diff --git a/app/temp_share/subnet-addresses.php b/app/temp_share/subnet-addresses.php new file mode 100644 index 000000000..280d31afc --- /dev/null +++ b/app/temp_share/subnet-addresses.php @@ -0,0 +1,268 @@ + + +settings); + +# reset custom fields to ip addresses +$custom_fields = $Tools->fetch_custom_fields ('ipaddresses'); +# set hidden custom fields +$hidden_cfields = json_decode($settings->hiddenCustomFields, true); +$hidden_cfields = is_array($hidden_cfields['ipaddresses']) ? $hidden_cfields['ipaddresses'] : array(); + +# set selected address fields array +$selected_ip_fields = $settings->IPfilter; +$selected_ip_fields = explode(";", $selected_ip_fields); //format to array +$selected_ip_fields_size = in_array('state', $selected_ip_fields) ? (sizeof($selected_ip_fields)-1) : sizeof($selected_ip_fields); //set size of selected fields +if($selected_ip_fields_size==1 && strlen($selected_ip_fields[0])==0) { $selected_ip_fields_size = 0; } //fix for 0 + + +/* Addresses and fields manupulations */ + +# save for visual display ! +$addresses_visual = $addresses; + +# set colspan for output +$colspan['empty'] = $selected_ip_fields_size + sizeof($custom_fields) +4; //empty colspan +$colspan['unused'] = $selected_ip_fields_size + sizeof($custom_fields) +3; //unused colspan +$colspan['dhcp'] = $selected_ip_fields_size + sizeof($custom_fields); //dhcp colspan + +# remove custom fields if all are empty! +foreach($custom_fields as $field) { + $sizeMyFields[$field['name']] = 0; // default value + # check against each IP address + if($addresses!==false) { + foreach($addresses as $ip) { + $ip = (array) $ip; + if(strlen($ip[$field['name']]) > 0) { + $sizeMyFields[$field['name']]++; // +1 + } + } + # unset if value == 0 + if($sizeMyFields[$field['name']] == 0) { + unset($custom_fields[$field['name']]); + + $colspan['empty']--; + $colspan['unused']--; //unused span -1 + $colspan['dhcp']--; //dhcp span -1 + } + } +} + +/* output variables */ + +# set page limit for pagination +$page_limit = 100000000; +# set ping statuses for warning and offline +$statuses = explode(";", $settings->pingStatus); +?> + + +

    + +

    + + + + + + + + + + "._('IP address').""; + # hostname - mandatory + print ""; + # Description - mandatory + print ""; + # MAC address + if(in_array('mac', $selected_ip_fields)) { print ""; } + # note + if(in_array('note', $selected_ip_fields)) { print ""; } + # switch + if(in_array('switch', $selected_ip_fields)) { print ""; } + # port + if(in_array('port', $selected_ip_fields)) { print ""; } + # owner + if(in_array('owner', $selected_ip_fields)) { print ""; } + + # custom fields + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $myField) { + if(!in_array($myField['name'], $hidden_cfields)) { + print ""; + } + } + } + ?> + + + + +find_unused_addresses($Subnets->transform_to_decimal($subnet_detailed['network']), $Subnets->transform_to_decimal($subnet_detailed['broadcast']), $subnet['mask'], $empty=true ); + print ''. "\n"; +} +# print IP address +else { + foreach($addresses as $dummy) { + # + # first check for gaps from network to first host + # + + # check gap between network address and first IP address + if ( $n == 0 ) { $unused = $Addresses->find_unused_addresses ( $Subnets->transform_to_decimal($subnet_detailed['network']), $addresses[$n]->ip_addr, $subnet['mask']); } + # check unused space between IP addresses + else { $unused = $Addresses->find_unused_addresses ( $addresses[$n-1]->ip_addr, $addresses[$n]->ip_addr, $subnet['mask'] ); } + + # if there is some result for unused print it - if sort == ip_addr + if ( $unused ) { + print ""; + print " "; + print " "; + print ""; + } + + # + # print IP address + # + + # ip - range + if($addresses[$n]->class=="range-dhcp") + { + print ""; + print " "; + print " "; + print " "; + if($colspan['dhcp']!=0) + print " "; + // tr ends after! + + } + # ip - normal + else + { + /* set class for reserved and offline - if set! */ + $stateClass = ""; + if(in_array('state', $selected_ip_fields)) { + if ($addresses[$n]->state == 0) { $stateClass = _("Offline"); } + else if ($addresses[$n]->state == 2) { $stateClass = _("Reserved"); } + else if ($addresses[$n]->state == 3) { $stateClass = _("DHCP"); } + } + + print ""; + + // gateway + $gw = $addresses[$n]->is_gateway==1 ? "gateway" : ""; + + print " "; + + # resolve dns name + $resolve = $DNS->resolve_address($addresses[$n]); print ""; + + # print description - mandatory + print ""; + # Print mac address icon! + if(in_array('mac', $selected_ip_fields)) { + if(!empty($addresses[$n]->mac)) { print ""; } + else { print ""; } + } + + + # print info button for hover + if(in_array('note', $selected_ip_fields)) { + if(!empty($addresses[$n]->note)) { print ""; } + else { print ""; } + } + + # print device + if(in_array('switch', $selected_ip_fields)) { + # get device details + $device = (array) $Tools->fetch_device(null, $addresses[$n]->switch); + print ""; + } + + # print port + if(in_array('port', $selected_ip_fields)) { print ""; } + + # print owner + if(in_array('owner', $selected_ip_fields)) { print ""; } + + # print custom fields + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $myField) { + if(!in_array($myField['name'], $hidden_cfields)) { + print ""; + } + } + } + } + print ''. "\n"; + + /* if last one return ip address and broadcast IP + ****************************************************/ + if ( $n == $m ) + { + # compressed? + if(isset($addresses[$n]->stopIP)) { $unused = $Addresses->find_unused_addresses ( $addresses[$n]->stopIP, $Subnets->transform_to_decimal($subnet_detailed['broadcast']), $subnet['mask'] ); } + else { $unused = $Addresses->find_unused_addresses ( $addresses[$n]->ip_addr, $Subnets->transform_to_decimal($subnet_detailed['broadcast']), $subnet['mask'] ); } + + if ( $unused ) { + print ""; + print " "; + print " "; + print ""; + } + } + /* next IP address for free check */ + $n++; + } +} +?> + +
    "._('Hostname').""._('Description')."
    '.$unused['ip'].' (' .$Subnets->reformat_number($unused['hosts']).')
    $unused[ip] ($unused[hosts])
    "; + # status icon + if($subnet['pingSubnet']=="1") { + print " "; + } + print $Subnets->transform_to_dotted( $addresses[$n]->ip_addr).' - '.$Subnets->transform_to_dotted( $addresses[$n]->stopIP)." (".$addresses[$n]->numHosts.")"; + print $Addresses->address_type_format_tag($addresses[$n]->state); + print " "._("DHCP range")."".$addresses[$n]->description."
    id)."'>".$Subnets->transform_to_dotted( $addresses[$n]->ip_addr).""; + if($addresses[$n]->is_gateway==1) { print " "; } + if(in_array('state', $selected_ip_fields)) { print $Addresses->address_type_format_tag($addresses[$n]->state); } + print "$resolve[name]".$addresses[$n]->description."mac."'>
    $unused[ip] ($unused[hosts])
    \ No newline at end of file diff --git a/app/temp_share/subnet-details.php b/app/temp_share/subnet-details.php new file mode 100644 index 000000000..714aeb09f --- /dev/null +++ b/app/temp_share/subnet-details.php @@ -0,0 +1,144 @@ + +

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + find_gateway($subnet['id']); + if($gateway !==false) { ?> + + + + + + + + + + + + + enableVRF==1) { + # get vrf details + $vrf = (array) $Tools->fetch_vrf(null,$subnet['vrfId']); + # set text + $vrfText = $vrf['name']; + if(!empty($vrf['description'])) { $vrfText .= " [$vrf[description]]";} + + print ""; + print " "; + print " "; + print ""; + } + + if(!$slaves) { + # divider + print ""; + print " "; + print " "; + print ""; + + # Are IP requests allowed? + if ($settings->enableIPrequests==1) { + print ""; + print " "; + if($subnet['allowRequests'] == 1) { print " "; } # yes + else { print " ";} # no + print ""; + } + # ping-check hosts inside subnet + print ""; + print " "; + if($subnet['pingSubnet'] == 1) { print " "; } # yes + else { print " ";} # no + print ""; + # scan subnet for new hosts * + print ""; + print " "; + if($subnet['discoverSubnet'] == 1) { print " "; } # yes + else { print " ";} # no + print ""; + } + ?> + + 0) { + foreach($custom_fields as $key=>$field) { + if(strlen($subnet[$key])>0) { + $subnet[$key] = str_replace(array("\n", "\r\n"), "
    ",$subnet[$key]); + $html_custom[] = ""; + $html_custom[] = " "; + $html_custom[] = " "; + $html_custom[] = ""; + } + } + + # any? + if(isset($html_custom)) { + # divider + print ""; + print " "; + print " "; + print ""; + + print implode("\n", $html_custom); + } + } + print ""; + print ""; + print ""; + + ?> + +
    $subnet[ip]/$subnet[mask] ($subnet_detailed[netmask])"; ?>
    + "subnets", "section"=>$subnet['sectionId'], "subnetId"=>$subnet['id'])); ?> +
    parse_permissions($subnet_permission); ?>
    + reformat_number ($subnet_usage['used']) .' | + '._('Free').': '. $Subnets->reformat_number ($subnet_usage['freehosts']) .' ('. $subnet_usage['freehosts_percent'] .'%) | + '._('Total').': '. $Subnets->reformat_number ($subnet_usage['maxhosts']); + ?> +
    transform_to_dotted($gateway->ip_addr);?>
    + /"; } //Display fix for emprt VLAN + print $vlan['number']; + + if(!empty($vlan['name'])) { print ' - '.$vlan['name']; } //Print name if provided + if(!empty($vlan['description'])) { print ' ['. $vlan['description'] .']'; } //Print description if provided + ?> +
    "._('VRF')."$vrfText

    "._('IP requests').""._('enabled').""._('disabled')."
    "._('Hosts check').""._('enabled').""._('disabled')."
    "._('Discover new hosts').""._('enabled').""._('disabled')."
    $key"; + #booleans + if($field['type']=="tinyint(1)") { + if($subnet[$key] == "0") { $html_custom[] = _("No"); } + elseif($subnet[$key] == "1") { $html_custom[] = _("Yes"); } + } + else { + $html_custom[] = $subnet[$key]; + } + $html_custom[] = "


    \ No newline at end of file diff --git a/app/temp_share/subnet-graph.php b/app/temp_share/subnet-graph.php new file mode 100644 index 000000000..0577268a0 --- /dev/null +++ b/app/temp_share/subnet-graph.php @@ -0,0 +1,92 @@ +calculate_subnet_usage_detailed( $subnet['subnet'], $subnet['mask'], $addresses); + +?> + + +

    +
    +
    + + + + + + + + \ No newline at end of file diff --git a/app/temp_share/subnet-visual.php b/app/temp_share/subnet-visual.php new file mode 100644 index 000000000..ba912e21f --- /dev/null +++ b/app/temp_share/subnet-visual.php @@ -0,0 +1,68 @@ +fetch_object("sections", "id", $subnet['sectionId']); + +print "

    "._('Visual subnet display')." to manage IP address')."!'>


    "; +print "
    "; + +# set limits - general +$start_visual = gmp_strval(gmp_and("0xffffffff", (int) $Subnets->transform_to_decimal($subnet_detailed['network']))); +$stop_visual = gmp_strval(gmp_and("0xffffffff", (int) $Subnets->transform_to_decimal($subnet_detailed['broadcast']))); + +# remove subnet and bcast if mask < 31 +if($subnet['mask'] > 30) {} +elseif ($section['strictMode']==1) { +$start_visual = gmp_strval(gmp_add($start_visual, 1)); +$stop_visual = gmp_strval(gmp_sub($stop_visual, 1)); +} + +# we need to reindex addresses to have ip address in decimal as key! +$visual_addresses = array(); +if($addresses_visual) { +foreach($addresses_visual as $a) { + $visual_addresses[$a->ip_addr] = (array) $a; +} +} + +# print +for($m=$start_visual; $m<=$stop_visual; $m=gmp_strval(gmp_add($m,1))) { + + # already exists + if (array_key_exists((string)$m, $visual_addresses)) { + + # fix for empty states - if state is disabled, set to active + if(strlen($visual_addresses[$m]['state'])==0) { $visual_addresses[$m]['state'] = 1; } + + # to edit + $class = $visual_addresses[$m]['state']; + $id = (int) $visual_addresses[$m]['id']; + + # tooltip + $title = $Subnets->transform_to_dotted($m); + if(strlen($visual_addresses[$m]['dns_name'])>0) { $title .= "
    ".$visual_addresses[$m]['dns_name']; } + if(strlen($visual_addresses[$m]['description'])>0) { $title .= "
    ".$visual_addresses[$m]['description']; } + + # set colors + $background = $Subnets->address_types[$visual_addresses[$m]['state']]['bgcolor']; + $foreground = $Subnets->address_types[$visual_addresses[$m]['state']]['fgcolor']; + } + else { + # print add new + $class = "unused"; + $id = $m; + $title = ""; + + # set colors + $background = "#ffffff"; + $foreground = "#333333"; + } + + # print box + print ".".substr(strrchr($Subnets->transform_to_dotted($m), "."), 1).""; +} +print "
    "; +print "
    "; # clear float +?> \ No newline at end of file diff --git a/app/temp_share/subnet.php b/app/temp_share/subnet.php new file mode 100644 index 000000000..2001fcfbf --- /dev/null +++ b/app/temp_share/subnet.php @@ -0,0 +1,91 @@ +fetch_object("subnets", "id", $temp_objects[$_GET['section']]->id); + +# subnet id must be numeric +if(!is_numeric($subnet->id)) { $Result->show("danger", _('Invalid ID'), true); } + +//cast +$subnet = (array) $subnet; + +# fetch subnet reated stuff +$custom_fields = $Tools->fetch_custom_fields ('subnets'); //custom fields +$subnet = (array) $Subnets->fetch_subnet(null, $subnet['id']); //subnet details +if(sizeof($subnet)==0) { $Result->show("danger", _('Subnet does not exist'), true); } //die if empty +$subnet_detailed = $Subnets->get_network_boundaries ($subnet['subnet'], $subnet['mask']); //set network boundaries +$slaves = $Subnets->has_slaves ($subnet['id']) ? true : false; //check if subnet has slaves and set slaves flag true/false + +# permissions +$subnet_permission = 1; //subnet permission +$section_permission = 1; //section permission +if($subnet_permission == 0) { $Result->show("danger", _('You do not have permission to access this network'), true); } + +# fetch VLAN details +$vlan = (array) $Tools->fetch_object("vlans", "vlanId", $subnet['vlanId']); + +# fetch all addresses and calculate usage +if($slaves) { + $addresses = $Addresses->fetch_subnet_addresses_recursive ($subnet['id'], false); + $slave_subnets = (array) $Subnets->fetch_subnet_slaves ($subnet['id']); + $subnet_usage = $Subnets->calculate_subnet_usage (gmp_strval(sizeof($addresses)), $subnet['mask'], $subnet['subnet'] ); //Calculate free/used etc +} else { + $addresses = $Addresses->fetch_subnet_addresses ($subnet['id']); + $subnet_usage = $Subnets->calculate_subnet_usage (gmp_strval(sizeof($addresses)), $subnet['mask'], $subnet['subnet'] ); //Calculate free/used etc +} +?> + + + +
    + + +
    + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + identify_address($subnet['subnet']) == "IPv4") { + if($settings->visualLimit > 0) { + if($settings->visualLimit <= $subnet['mask'] && !$slaves) { + include('subnet-visual.php'); + } + } + } + ?> +
    + + +
    + fetch_subnet_addresses ($subnet['id']); + if(sizeof($addresses)>0) { + # set flag + $orphaned = true; + include('addresses/print-address-table.php'); + } + } + ?> +
    + +
    \ No newline at end of file diff --git a/app/tools/changelog/changelog-print.php b/app/tools/changelog/changelog-print.php new file mode 100755 index 000000000..bb76ab0cd --- /dev/null +++ b/app/tools/changelog/changelog-print.php @@ -0,0 +1,102 @@ +check_user_session(); + +# change parameters - search string provided +if(isset($_GET['sPage'])) { + $_REQUEST['cfilter'] = $_REQUEST['subnetId']; + $_REQUEST['climit'] = $_REQUEST['sPage']; +} +elseif(isset($_GET['subnetId'])) { + $_REQUEST['climit'] = $_REQUEST['subnetId']; +} +else { + $_REQUEST['climit'] = 50; +} + +# get clog entries +if(!isset($_REQUEST['cfilter'])) { $clogs = $Tools->fetch_all_changelogs (false, "", $_REQUEST['climit']); } +else { $clogs = $Tools->fetch_all_changelogs (true, $_REQUEST['cfilter'], $_REQUEST['climit']); } + +# empty +if(sizeof($clogs)==0) { + print "
    "; + print "

    "._("No changelogs available")."

    "; + print ""._("No changelog entries are available").""; + print "
    "; +} +# result +else { + # if more that configured print it! + if(sizeof($clogs)==$_REQUEST['climit']) { $Result->show("warning alert-absolute", _("Output has been limited to last $_REQUEST[climit] lines")."!", false); } + + # printout + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # logs + foreach($clogs as $l) { + # cast + $l = (array) $l; + + # permissions + if($l['ctype']=="subnet") { $permission = $Subnets->check_permission ($User->user, $l['tid']); } + elseif($l['ctype']=="ip_addr") { $permission = $Subnets->check_permission ($User->user, $l['subnetId']); } + elseif($l['ctype']=="section") { $permission = $Sections->check_permission ($User->user, $l['sectionId']); } + else { $permission = 0; } + + # printout + if($permission > 0) { + # format diff + $l['cdiff'] = str_replace("\n", "
    ", $l['cdiff']); + + # format type + switch($l['ctype']) { + case "ip_addr": $l['ctype'] = "IP address"; break; + case "subnet": if($l['isFolder']==1) { $l['ctype'] = "Folder"; } + else { $l['ctype'] = "Subnet"; } + break; + + case "section": $l['ctype'] = "Section"; break; + } + + print ""; + print " "; + print " "; + + # subnet, section or ip address + if($l['ctype']=="IP address") { + print " "; + } + elseif($l['ctype']=="Subnet") { + print " "; + } + elseif($l['ctype']=="Folder") { + print " "; + } + + print " "; + print " "; + print " "; + print " "; + print ""; + } + } + print "
    "._('User').""._('Type').""._('Object').""._('Action').""._('Result').""._('Date').""._('Change')."
    $l[real_name]$l[ctype]".$Subnets->transform_address($l['ip_addr'],"dotted")."".$Subnets->transform_address($l['ip_addr'],"dotted")."/$l[mask]$l[sDescription]"._("$l[caction]").""._("$l[cresult]")."$l[cdate]$l[cdiff]
    "; +} +?> \ No newline at end of file diff --git a/app/tools/changelog/index.php b/app/tools/changelog/index.php new file mode 100755 index 000000000..39ee70d92 --- /dev/null +++ b/app/tools/changelog/index.php @@ -0,0 +1,65 @@ +check_user_session(); + +# header +print "

    "._('Changelog')."


    "; + +# if enabled +if($User->settings->enableChangelog == 1) { + # set default size + if(!isset($_REQUEST['subnetId'])) { $_REQUEST['climit'] = 50; } + else { $_REQUEST['climit'] = $_GET['subnetId']; } + + # change parameters - search string provided + if(isset($_GET['sPage'])) { + $_REQUEST['cfilter'] = $_REQUEST['subnetId']; + $_REQUEST['climit'] = $_REQUEST['sPage']; + } + elseif(isset($_GET['subnetId'])) { + $_REQUEST['climit'] = $_REQUEST['subnetId']; + } + else { + $_REQUEST['climit'] = 50; + } +?> + + +
    +
    + +
    + +
    + +
    + ' type='text' style='width:150px;'> + + '> + +
    + +
    +
    + + show("info",_("Change logging is disabled. You can enable it under administration")."!", false); +} +?> \ No newline at end of file diff --git a/app/tools/devices/devices-hosts.php b/app/tools/devices/devices-hosts.php new file mode 100755 index 000000000..923b31be0 --- /dev/null +++ b/app/tools/devices/devices-hosts.php @@ -0,0 +1,201 @@ +check_user_session(); + +# check +is_numeric($_GET['sPage']) ? : $Result->show("danger", _("Invalid ID"), true); + +# fetch device +$device = (array) $Tools->fetch_device (null,$_GET['sPage']); +# get custom fields +$custom_fields = $Tools->fetch_custom_fields('devices'); +# fetch all addresses on switch +$addresses = $Tools->fetch_device_addresses($device['id']); + +# print +if($_GET['sPage']!=0 && sizeof($device)>0) { + + # set type + $device_type = $Tools->fetch_device_type(null, $device['type']); + + # title + print "

    "._('Device details')."

    "; + print "
    "; + + # device details + print ""; + print ''; + print " '; + print " "; + print ""; + print " '; + print " "; + print ""; + print " '; + print " "; + print ""; + print " '; + print " "; + print ""; + print " '; + print " "; + print ""; + print " '; + print " "; + print ""; + print " '; + print " "; + print ""; + print " '; + print " "; + print ""; + print " '; + print " "; + print ""; + + print ""; + print " "; + print ""; + + if(sizeof($custom) > 0) { + foreach($custom_fields as $field) { + + # fix for boolean + if($field['type']=="tinyint(1)" || $field['type']=="boolean") { + if($device[$field['name']]=="0") { $device[$field['name']] = "false"; } + elseif($device[$field['name']]=="1") { $device[$field['name']] = "true"; } + else { $device[$field['name']] = ""; } + } + + print ""; + print ""; + print ""; + } + + print ""; + print " "; + print ""; + } + + print ""; + print " "; + + if($User->isadmin) { + print " "; + } + else { + print " "; + } + print ""; + + + print "
    ". _('Hostname').'$device[hostname]
    ". _('IP address').'$device[ip_addr]
    ". _('Description').'$device[description]
    ". _('Number of hosts').'".sizeof($addresses)."
    ". _('Type').'$device_type->tname
    ". _('Vendor').'$device[vendor]
    ". _('Model').'$device[model]
    ". _('SW version').'$device[version]
    ". _('Sections').':"; + if(strlen($device['hostname'])>0) { + $section_ids = explode(";", $device['sections']); + foreach($section_ids as $k=>$id) { + $section = $Sections->fetch_section(null, $id); + $section_print[$k] = "· ".$section->name; + $section_print[$k] .= strlen($section->description)>0 ? " ($section->description)" : ""; + } + print implode("
    ", $section_print); + } + print "

    $field[name]".$device[$field['name']].""; + print "

    "; + print "
    "; + print " "; + print " "; + print "
    "; + print "
    "; + print "
    "; + print " "; + print " "; + print "
    "; + print "
    "; +} + +# main table frame +print ""; + + if(empty($device['hostname'])) { + $device['hostname'] = _('Device not specified'); + $device['ip_addr'] = ""; + } + else { + $device['ip_addr'] = "($device[ip_addr])"; + } + + /* reformat if empty */ + if(empty($device['hostname'])) { $device['hostname'] = "Unspecified";} + + # print name + print ""; + print ""; + print " "; + print ""; + print ""; + + # collapsed div with details + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + if(sizeof($addresses) == 0) { + print ""; + print ' '. "\n"; + print ""; + } + + # IP addresses + foreach ($addresses as $ip) { + # cast + $ip = (array) $ip; + + # check permission + $subnet_permission = $Subnets->check_permission($User->user, $ip['subnetId']); + + if($subnet_permission>0) { + # get subnet and section details for belonging IP + $subnet = (array) $Subnets->fetch_subnet(null, $ip['subnetId']); + $section = (array) $Sections->fetch_section (null, $subnet['sectionId']); + + # print + print ""; + print " "; + print " "; + print " "; + print " "; + + # print info button for hover + print ""; + + print " "; + print " "; + print ""; + } + } + + print ""; + print ""; + +print "
    "; + print "

    $device[hostname] $device[ip_addr]

    "; + print "
    "._('IP address').""._('Port').""._('Subnet').""._('Description')."
    '.$Result->show('info', _('No hosts belonging to this device').'!', false).'
    ".$Subnets->transform_to_dotted($ip['ip_addr'])."$ip[port]$subnet[ip]/$subnet[mask] ($subnet[description])$ip[description]"; + if(!empty($ip['note'])) { + $ip['note'] = str_replace("\n", "
    ",$ip['note']); + print " "; + } + print "
    "; # end major table +?> \ No newline at end of file diff --git a/app/tools/devices/devices-print.php b/app/tools/devices/devices-print.php new file mode 100755 index 000000000..1fe1c49ff --- /dev/null +++ b/app/tools/devices/devices-print.php @@ -0,0 +1,196 @@ + + +check_user_session(); + + +# sorting +if(!isset($_POST['direction'])) { + $sort['direction'] = 'asc'; + $sort['field'] = 'hostname'; + + $sort['directionNext'] = "desc"; + + $_POST['direction'] = "hostname|asc"; + + # fields + $_POST['ffield'] = null; + $_POST['fval'] = null; +} +else { + //format posted values! + $tmp = explode("|", $_POST['direction']); + + $sort['field'] = $tmp[0]; + $sort['direction'] = $tmp[1]; + + if($sort['direction'] == "asc") { $sort['directionNext'] = "desc"; } + else { $sort['directionNext'] = "asc"; } +} + +# filter devices or fetch print all? +$filter = isset($_POST['ffield']) ? true : false; +$devices = $Tools->fetch_devices ($_POST['ffield'], $_POST['fval'], $sort['field'], $sort['direction']); +$device_types = $Tools->fetch_all_objects ("deviceTypes", "tid"); + +# get custom fields +$custom_fields = $Tools->fetch_custom_fields('devices'); +# get hidden fields */ +$hidden_fields = json_decode($User->settings->hiddenCustomFields, true); +$hidden_fields = is_array(@$hidden_fields['devices']) ? $hidden_fields['devices'] : array(); + + +# sort icons +if($sort['direction'] == 'asc') { $icon = " "; } +else { $icon = " "; } + +# title +print "

    "._('List of network devices')."

    "; +print "
    "; + +//filter +print "
    "; +print "
    "; + //select + $select = array("hostname"=>"Hostname", "ip_addr"=>"IP address", "description"=>"Description", "type"=>"Type", "vendor"=>"Vendor", "model"=>"Model", "version"=>"Version"); + foreach($custom_fields as $c) { + $select[$c['name']] = $c['name']; + } + + print " "; + + //field + print ""; + print ""; + print ""; + +print "
    "; +print "
    "; + + +# filter notification +if($filter) +$Result->show("warning", _('Filter applied:'). '$_POST[ffield] like *$_POST[fval]*', false); + + +print ''; + +#headers +print ''; +print " '; +print " '; +print " '; +print " '; +print " '; +print " '; +print " '; + +if(sizeof(@$custom) > 0) { + foreach($custom_fields as $field) { + if(!in_array($field['name'], $hidden_fields)) { + print ""; + $colspanCustom++; + } + } +} +print ' '; +print ''; + +if(sizeof(@$devices) == 0) { + $colspan = 8 + $colspanCustom; + print ""; + print " "; + print ""; +} +else { + foreach ($devices as $device) { + //cast + $device = (array) $device; + + //count items + $cnt = $Tools->count_device_addresses($device['id']); + + //print details + print ''. "\n"; + + print " '. "\n"; + print " '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + + //custom + if(sizeof(@$custom) > 0) { + foreach($custom_fields as $field) { + if(!in_array($field['name'], $hidden_fields)) { + print ""; + } + } + } + + print ' '; + print ''. "\n"; + + } + + # print for unspecified + print ''. "\n"; + + //$cnt = countIPaddressesBySwitchId(NULL); + $cnt = $Tools->count_device_addresses(0); + + + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + + //custom + if(sizeof(@$custom) > 0) { + foreach($custom_fields as $field) { + if(!in_array($field['name'], $hidden_fields)) { + print ""; + } + } + } + print ' '; + print ''. "\n"; +} + +print '
    "; ; if($sort['field'] == "hostname") print $icon; print _('Hostname').'"; ; if($sort['field'] == "ip_addr") print $icon; print _('IP address').'"; ; if($sort['field'] == "description") print $icon; print _('Description').'"._('Number of hosts').'
    ".$Result->show('info', _('No devices configured')."!", false)."
    ". $device['hostname'] .'". $device['ip_addr'] .''. $device['description'] .''. $cnt .' '._('Hosts').' '._('Show all hosts').'
    '._('Device not specified').''. $cnt .' '._('Hosts').' '._('Show all hosts').'
    '; +?> \ No newline at end of file diff --git a/app/tools/devices/index.php b/app/tools/devices/index.php new file mode 100755 index 000000000..ca4b6d06d --- /dev/null +++ b/app/tools/devices/index.php @@ -0,0 +1,30 @@ +check_user_session(); + +# print link to manage +print "
    "; + //back button + if(isset($_GET['sPage'])) { print " ". _('Back').""; } + //administer + elseif($User->isadmin) { print " ". _('Manage').""; } +print "
    "; + + +# print hosts or all devices +if(isset($_GET['subnetId'])) { + include('devices-hosts.php'); + +} else { + print "
    "; + include('devices-print.php'); + print "
    "; +} + +?> \ No newline at end of file diff --git a/app/tools/favourites/favourite-edit.php b/app/tools/favourites/favourite-edit.php new file mode 100755 index 000000000..002e0197c --- /dev/null +++ b/app/tools/favourites/favourite-edit.php @@ -0,0 +1,22 @@ +check_user_session(); + +# checks +is_numeric($_POST['subnetId']) ? : $Result->show("danger", _('Invalid ID'),false, true); + +# execute action +if(!$User->edit_favourite($_POST['action'], $_POST['subnetId'])) { $Result->show("danger", _('Error editing favourite'),false, true); } +else { print "success"; } +?> \ No newline at end of file diff --git a/app/tools/favourites/index.php b/app/tools/favourites/index.php new file mode 100755 index 000000000..15cc8f0a9 --- /dev/null +++ b/app/tools/favourites/index.php @@ -0,0 +1,93 @@ +check_user_session(); + +# fetch favourite subnets +$favourite_subnets = $User->fetch_favourite_subnets(); + +# title +print "

    "._('Favourite subnets')."

    "; +print "
    "; + +# print if none +if(sizeof($favourite_subnets) == 0 || !isset($favourite_subnets[0])) { + print "
    "; + print "

    "._("No favourite subnets selected")."


    "; + print ""._("You can add subnets to favourites by clicking star icon in subnet details")."!
    "; + print "
    "; +} +else { + print ""; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # logs + foreach($favourite_subnets as $f) { + # if subnet already removed (doesnt exist) dont print it! + if(sizeof($f)>0) { + print ""; + + if($f['isFolder']==1) { + $master = true; + print " "; + } + else { + //master? + if($Subnets->has_slaves ($f['subnetId'])) { $master = true; print " "; } + else { $master = false; print " "; } + } + + print " "; + print " "; + + # vlan + $vlan = $Tools->fetch_object("vlans", "vlanId", $f['vlanId']); + $vlan = $vlan===false ? "" : $vlan->number; + print " "; + + # usage + if($f['isFolder']==1) { + print ''; + } + elseif(!$master) { + $address_count = $Addresses->count_subnet_addresses ($f['subnetId']); + $subnet_usage = $Subnets->calculate_subnet_usage (gmp_strval($address_count), $f['mask'], $f['subnet']); + + print ' '; + } + else { + print ''. "\n"; + } + + # add address + if($master===true || $f['isFolder']==1 || $Subnets->reformat_number($subnet_usage['freehosts'])=="0") { $disabled = "disabled"; } + else { $disabled = ""; } + + # remove + print " "; + + print ""; + } + } + + print "
    "._('Object').""._('Description').""._('Section')."
    $f[description]".$Subnets->transform_to_dotted($f['subnet'])."/$f[mask]".$Subnets->transform_to_dotted($f['subnet'])."/$f[mask]$f[description]$f[section]"; + print "
    "; + print " "; + print " "; + print "
    "; + print "
    "; +} +?> \ No newline at end of file diff --git a/app/tools/index.php b/app/tools/index.php new file mode 100755 index 000000000..b4d662e57 --- /dev/null +++ b/app/tools/index.php @@ -0,0 +1,32 @@ +
    + +
    +
    + +$tool) { + + # headers + print "

    "._($k)."

    "; + print "
    "; + + # items + foreach($tool as $t) { + print "
    "; + print "
    "; + print "
    "; + print "
    "; + print "
    "._($t['name'])."
    "._($t['description'])."
    "; + print "
    "; + print "
    "; + print "
    "; + } + + # clear and break + print "
    "; +} +?> +
    +
    +
    \ No newline at end of file diff --git a/app/tools/instructions/index.php b/app/tools/instructions/index.php new file mode 100755 index 000000000..028f94547 --- /dev/null +++ b/app/tools/instructions/index.php @@ -0,0 +1,25 @@ +fetch_instructions(); +$instructions = $instructions->instructions; + +/* format line breaks */ +$instructions = stripslashes($instructions); //show html + +/* prevent ", "
    ", $instructions); + +?> + +

    +
    + +
    + +
    \ No newline at end of file diff --git a/app/tools/ip-calculator/index.php b/app/tools/ip-calculator/index.php new file mode 100755 index 000000000..4b1a7c5c8 --- /dev/null +++ b/app/tools/ip-calculator/index.php @@ -0,0 +1,43 @@ +check_user_session(); +?> + +

    +
    + + +
    + + + + + + + + + + + + + + + + + +
    / + + +
    +
    +
    + + +
    +
    +
    + + + +
    +
    \ No newline at end of file diff --git a/app/tools/ip-calculator/result.php b/app/tools/ip-calculator/result.php new file mode 100755 index 000000000..87d431805 --- /dev/null +++ b/app/tools/ip-calculator/result.php @@ -0,0 +1,80 @@ +check_user_session(); + +# get requested IP addresses in CIDR format +$cidr = $_POST['cidr']; + +# verify input CIDR and die if errors +$errors = $Subnets->verify_cidr_address ($cidr, false); +$errors===true ? : $Result->show("danger alert-absolute", _('Invalid input').': '.$errors,true); + +# fetch all sections +$Sections->fetch_sections(); + +# calculate results +$calc_results = $Tools->calculate_ip_calc_results($cidr); +?> + +
    +

    :

    + + + + + + $line) { + print ''; + print ' '; + print ' '; + print ''; + + $m++; + } + ?> + + + + + + + + + + + + +
    '._("$key").''. $line .'
    + +
    +
    \ No newline at end of file diff --git a/app/tools/logs/clear-logs.php b/app/tools/logs/clear-logs.php new file mode 100755 index 000000000..15629aad8 --- /dev/null +++ b/app/tools/logs/clear-logs.php @@ -0,0 +1,22 @@ +check_user_session(); + +# truncate logs table +if(!$Admin->truncate_table("logs")) { $Result->show("danger", _('Error clearing logs')."!", true); } +else { $Result->show("success", _('Logs cleared successfully')."!", true); } +?> \ No newline at end of file diff --git a/app/tools/logs/detail-popup.php b/app/tools/logs/detail-popup.php new file mode 100755 index 000000000..94dc8feff --- /dev/null +++ b/app/tools/logs/detail-popup.php @@ -0,0 +1,86 @@ +check_user_session(); + + +# fetch log +$log = $Admin->fetch_object("logs", "id", $_POST['id']); +if($log==false) { $Result->show("danger", _("Invalid ID"), true, true); } +else { $log = (array) $log; } + +if ($log['severity'] == 0) { + $log['severityText'] = _("Informational"); + $color = "success"; +} +else if ($log['severity'] == 1) { + $log['severityText'] = _("Notice"); + $color = "warning"; +} +else { + $log['severityText'] = _("Warning"); + $color = "error"; +} + +# get user details +$user = $Admin->fetch_object("users", "username", $log['username']); +$userprint = $user===false ? "" : $user->real_name."(".$user->username.")"; +?> + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + +
    + +
    diff --git a/app/tools/logs/export.php b/app/tools/logs/export.php new file mode 100755 index 000000000..a5677de31 --- /dev/null +++ b/app/tools/logs/export.php @@ -0,0 +1,114 @@ +check_user_session(); + + + +// Create a workbook +$filename = "phpipam_logs_export_". date("Y-m-d") .".xls"; +$workbook = new Spreadsheet_Excel_Writer(); + +//increase memory size +ini_set('memory_limit', '1024M'); + +//fetch sections, and for each section write new tab, inside tab write all values! +$logs = $Admin->fetch_all_objects ("logs", "id"); + +//formatting headers +$format_header =& $workbook->addFormat(); +$format_header->setBold(); +$format_header->setColor('white'); +$format_header->setFgColor('black'); + +//formatting titles +$format_title =& $workbook->addFormat(); +$format_title->setColor('black'); +$format_title->setFgColor(22); //light gray +$format_title->setBottom(2); +$format_title->setLeft(1); +$format_title->setRight(1); +$format_title->setTop(1); +$format_title->setAlign('left'); + +//formatting content - borders around IP addresses +$format_right =& $workbook->addFormat(); +$format_right->setRight(1); +$format_left =& $workbook->addFormat(); +$format_left->setLeft(1); +$format_top =& $workbook->addFormat(); +$format_top->setTop(1); + + + +// Create a worksheet +$worksheet =& $workbook->addWorksheet('phpipam logs'); + +$lineCount = 0; +//Write titles + +//write headers +$worksheet->write($lineCount, 0, _('id'),$format_title); +$worksheet->write($lineCount, 1, _('Severity'),$format_title); +$worksheet->write($lineCount, 2, _('Date'),$format_title); +$worksheet->write($lineCount, 3, _('Username'),$format_title); +$worksheet->write($lineCount, 4, _('Command'),$format_title); +$worksheet->write($lineCount, 5, _('Details'),$format_title); + +$lineCount++; + +foreach ($logs as $log) { + //cast + $log = (array) $log; + + //we need to reformat severity! + switch($log['severity']) { + case 0: $log['severity'] = _("Informational"); break; + case 1: $log['severity'] = _("Warning"); break; + case 2: $log['severity'] = _("Critical"); break; + } + + //remove breaks in details + $log['details'] = str_replace("
    ", "\n", $log['details']); + + $worksheet->write($lineCount, 0, $log['id'], $format_left); + $worksheet->write($lineCount, 1, $log['severity']); + $worksheet->write($lineCount, 2, $log['date']); + $worksheet->write($lineCount, 3, $log['username']); + $worksheet->write($lineCount, 4, $log['command']); + $worksheet->write($lineCount, 5, $log['details'], $format_right); + + $lineCount++; + +} + + +//top border line at bottom of IP addresses +$worksheet->write($lineCount, 0, "", $format_top); +$worksheet->write($lineCount, 1, "", $format_top); +$worksheet->write($lineCount, 2, "", $format_top); +$worksheet->write($lineCount, 3, "", $format_top); +$worksheet->write($lineCount, 4, "", $format_top); +$worksheet->write($lineCount, 5, "", $format_top); + + +// sending HTTP headers +$workbook->send($filename); + +// Let's send the file +$workbook->close(); +?> \ No newline at end of file diff --git a/app/tools/logs/index.php b/app/tools/logs/index.php new file mode 100755 index 000000000..53b658a90 --- /dev/null +++ b/app/tools/logs/index.php @@ -0,0 +1,43 @@ +check_user_session(); + +# admin class +$Admin = new Admin($Database); + +?> + +

    :

    +
    + + +
    + :: + :: + + + + + + + + + + +
    + + +
    +
    +
    + + + +
    + +
    \ No newline at end of file diff --git a/app/tools/logs/show-logs.php b/app/tools/logs/show-logs.php new file mode 100755 index 000000000..ed9b15415 --- /dev/null +++ b/app/tools/logs/show-logs.php @@ -0,0 +1,117 @@ + + + + + +check_user_session(); +} + +# if nothing is provided display all +if ( empty($_POST['Informational']) && empty($_POST['Notice']) && empty($_POST['Warning']) ) { + $_POST['Informational'] = _("Informational"); + $_POST['Notice'] = _("Notice"); + $_POST['Warning'] = _("Warning"); +} +?> + + + + + + + + + + + +log_fetch_highest_id(); +if(empty($_POST['lastId']) || ($_POST['lastId'] == "undefined")) { $_POST['lastId'] = $highestId; } + +//set empty direction +if(!isset($_POST['direction'])) { $_POST['direction'] = ""; } + +/* get requested logs */ +$logs = $Tools->fetch_logs($logCount, $_POST['direction'], $_POST['lastId'], $highestId, $_POST['InformationalQuery'], $_POST['NoticeQuery'], $_POST['WarningQuery']); + +$x = 0; +foreach ($logs as $log) { + //cast + $log = (array) $log; + + if($x < $logCount) { + + //set classes based on severity + if ($log['severity'] == 0) { + $log['severityText'] = _("Informational"); + $color = "success"; + } + else if ($log['severity'] == 1) { + $log['severityText'] = _("Notice"); + $color = "warning"; + } + else { + $log['severityText'] = _("Warning"); + $color = "danger"; + } + + if (in_array($log['severityText'], $_POST)) { + /* reformat details */ + $log['details'] = str_replace("\"", "'", $log['details']); + + print ''. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; + print ' '. "\n"; } + print ' '. "\n"; + print ''. "\n"; + } + } + $x++; +} +?> + +
    '. $log['date'] .''. $log['severity'] .''. $log['severityText'] .''. $log['username'] .''. $log['ipaddr'] .''. $log['command'] .''; + /* details */ + if(!empty($log['details'])) { print '
    + + +show("info", _('No logs available')."!", true); } ?> \ No newline at end of file diff --git a/app/tools/pass-change/form.php b/app/tools/pass-change/form.php new file mode 100755 index 000000000..c79a588fc --- /dev/null +++ b/app/tools/pass-change/form.php @@ -0,0 +1,52 @@ +check_user_session (); +?> + +
    +

    +
    + +
    + +
    +
    +
    + +
    +
    +

    +
    + +
    +
    + + +
    +
    + +
    + + +
    +
    + +
    + + +
    + +
    + +
    +
    + + +
    +
    +
    + +
    + + +
    \ No newline at end of file diff --git a/app/tools/pass-change/result.php b/app/tools/pass-change/result.php new file mode 100755 index 000000000..df9ef800b --- /dev/null +++ b/app/tools/pass-change/result.php @@ -0,0 +1,20 @@ +check_user_session (); + +# checks +if(strlen($_POST['ipampassword1'])<8) { $Result->show("danger", _("Invalid password"), true); } +if($_POST['ipampassword1']!=$_POST['ipampassword2']) { $Result->show("danger", _("Passwords do not match"), true); } + +# update pass +$User->update_user_pass($_POST['ipampassword1']); +?> \ No newline at end of file diff --git a/app/tools/request-ip/index.php b/app/tools/request-ip/index.php new file mode 100755 index 000000000..d67486f84 --- /dev/null +++ b/app/tools/request-ip/index.php @@ -0,0 +1,93 @@ +check_user_session(); +?> + + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    * + transform_to_dotted($Addresses->get_first_available_address ($_POST['subnetId'], $Subnets)); + # get subnet details + $subnet = (array) $Subnets->fetch_subnet(null, $_POST['subnetId']); + ?> + + + +
    + +
    * + +
    + +
    +
    + +
    + + +
    + + + +
    +
    diff --git a/app/tools/scanned-networks/index.php b/app/tools/scanned-networks/index.php new file mode 100644 index 000000000..664090647 --- /dev/null +++ b/app/tools/scanned-networks/index.php @@ -0,0 +1,65 @@ +check_user_session(); + +# fetch all scanned subnets +$subnets = $Subnets->fetch_scanned_subnets(); + +# title +print "

    "._('Scanned subnets summary')."

    "; +print "
    "; + +# table +print ""; + +print ""; +print " "; +print " "; +print " "; +print " "; +print " "; +print " "; +print ""; + +//loop +if($subnets!==false) { + foreach($subnets as $subnet) { + //fetch section + $section = $Sections->fetch_section(null, $subnet->sectionId); + //set hosts check + $status_check = $subnet->pingSubnet==1 ? "" : ""; + //set discovery + $discovery = $subnet->discoverSubnet==1 ? "" : ""; + + # print + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + + print " "; + print ""; + } +} +else { + print ""; + print " "; + print ""; +} +?> + +
    "._('Subnet').""._('Description').""._('Section').""._('Hosts check').""._('Discover')."
    ".$Subnets->transform_to_dotted($subnet->subnet)."/$subnet->mask$subnet->description$section->name ($section->description)$status_check$discovery"; + print "
    "; + print " "; + print " "; + print "
    "; + print "
    "; + $Result->show("info", _('No subnets'), false); + print "
    \ No newline at end of file diff --git a/app/tools/search/index.php b/app/tools/search/index.php new file mode 100755 index 000000000..1e283deb4 --- /dev/null +++ b/app/tools/search/index.php @@ -0,0 +1,53 @@ +check_user_session(); + +# get posted search term +if(@$_GET['ip']) { $searchTerm = $_GET['ip']; } +else { $searchTerm = ""; } + +?> + +

    +
    + + + + +
    + + + +
    + +
    \ No newline at end of file diff --git a/app/tools/search/search-results-export.php b/app/tools/search/search-results-export.php new file mode 100755 index 000000000..bcdd51064 --- /dev/null +++ b/app/tools/search/search-results-export.php @@ -0,0 +1,359 @@ +check_user_session(); + +# fetch search term +$search_term = $_REQUEST['search_term']; + +# check if mac address or ip address +if(strlen($search_term)==17) { + if(substr_count($search_term, ":") == 5) { $type = "mac"; } //count : -> must be 5 +} +else if(strlen($search_term) == 12) { + if( (substr_count($search_term, ":") == 0) && (substr_count($search_term, ".") == 0) ) { $type = "mac"; } //no dots or : -> mac without : +} +else { $type = $Addresses->identify_address( $search_term ); } # identify address type + +# reformat if IP address for search +if ($type == "IPv4") { $search_term_edited = $Tools->reformat_IPv4_for_search ($search_term); } //reformat the IPv4 address! +elseif($type == "IPv6") { $search_term_edited = $Tools->reformat_IPv6_for_search ($search_term); } //reformat the IPv4 address! + + +# get all custom fields +$custom_address_fields = $Tools->fetch_custom_fields ("ipaddresses"); +$custom_subnet_fields = $Tools->fetch_custom_fields ("subnets"); +$custom_vlan_fields = $Tools->fetch_custom_fields ("vlans"); + +# set selected address fields array +$selected_ip_fields = $User->settings->IPfilter; +$selected_ip_fields = explode(";", $selected_ip_fields); + +# set col size +$fieldSize = sizeof($selected_ip_fields); +$mySize = sizeof($custom_address_fields); +$colSpan = $fieldSize + $mySize + 3; + + +# search addresses +if(@$_REQUEST['addresses']=="on") { $result_addresses = $Tools->search_addresses($search_term, $search_term_edited['high'], $search_term_edited['low']); } +# search subnets +if(@$_REQUEST['subnets']=="on") { $result_subnets = $Tools->search_subnets($search_term, $search_term_edited['high'], $search_term_edited['low']); } +# search vlans +if(@$_REQUEST['vlans']=="on") { $result_vlans = $Tools->search_vlans($search_term); } + + +/* + * Write xls + *********************/ + +// Create a workbook +$filename = _("phpipam_search_export_"). $search_term .".xls"; +$workbook = new Spreadsheet_Excel_Writer(); + + +//formatting titles +$format_title =& $workbook->addFormat(); +$format_title->setColor('black'); +$format_title->setFgColor(22); //light gray +$format_title->setBottom(2); +$format_title->setAlign('left'); + + +$lineCount = 0; //for line change +$m = 0; //for section change + + + + +/* -- Create a worksheet for addresses -- */ +if(sizeof($result_addresses)>0) { + $worksheet =& $workbook->addWorksheet(_('Addresses')); + + //write headers + $x = 0; + + $worksheet->write($lineCount, $x, _('ip address') ,$format_title); $x++; + # state + if(in_array('state', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, _('state') ,$format_title); $x++; + } + # description, note + $worksheet->write($lineCount, $x, _('description') ,$format_title); $x++; + $worksheet->write($lineCount, $x, _('hostname') ,$format_title); $x++; + # switch + if(in_array('switch', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, _('device') ,$format_title); $x++; + } + # port + if(in_array('port', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, _('port') ,$format_title); $x++; + } + # owner + if(in_array('owner', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, _('owner') ,$format_title); $x++; + } + # mac + if(in_array('mac', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, _('mac') ,$format_title); $x++; + } + # note + if(in_array('note', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, _('note') ,$format_title); $x++; + } + //custom + if(sizeof($custom_address_fields) > 0) { + foreach($custom_address_fields as $myField) { + $worksheet->write($lineCount, $x, $myField['name'], $format_title); $x++; + } + } + + //new line + $lineCount++; + + //Write IP addresses + foreach ($result_addresses as $ip) { + //cast + $ip = (array) $ip; + + # check permission + $subnet_permission = $Subnets->check_permission($User->user, $ip['subnetId']); + if($subnet_permission > 0) { + + //get the Subnet details + $subnet = (array) $Subnets->fetch_subnet(null, $ip['subnetId']); + //get section + $section = (array) $Sections->fetch_section(null, $subnet['sectionId']); + //get VLAN for subnet + $vlan = (array) (array) $Tools->fetch_object("vlans", "vlanId", $subnet['vlanId']); + //format vlan + if(sizeof($vlan)>0) { + if(strlen($vlan['number']) > 0) { + $vlanText = " (vlan: " . $vlan['number']; + if(strlen($vlan['name']) > 0) { + $vlanText .= ' - '. $vlan['name'] . ')'; + } + else { + $vlanText .= ")"; + } + } + } else { + $vlanText = ""; + } + + //section change + if ($result_addresses[$m]->subnetId != $result_addresses[$m-1]->subnetId) { + + //new line + $lineCount++; + + //subnet details + $worksheet->write($lineCount, 0, $Subnets->transform_to_dotted($subnet['subnet']) . "/" .$subnet['mask'] . " - " . $subnet['description'] . $vlanText, $format_title ); + $worksheet->mergeCells($lineCount, 0, $lineCount, $colSpan-1); + + //new line + $lineCount++; + } + $m++; + + $x = 0; + $worksheet->write($lineCount, $x, $Subnets->transform_to_dotted($ip['ip_addr']), $format_left); $x++; + # state + if(in_array('state', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, _($Addresses->address_type_index_to_type ($ip['state'])) ); $x++; + } + $worksheet->write($lineCount, $x, $ip['description']); $x++; + $worksheet->write($lineCount, $x, $ip['dns_name']); $x++; + # switch + if(in_array('switch', $selected_ip_fields)) { + if(strlen($ip['switch'])>0 && $ip['switch']!=0) { + $device = (array) $Tools->fetch_device(null, $ip['switch']); + $ip['switch'] = $device!=0 ? $device['hostname'] : ""; + } + else { + $ip['switch'] = ""; + } + $worksheet->write($lineCount, $x, $ip['switch']); $x++; + } + # port + if(in_array('port', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, $ip['port']); $x++; + } + # owner + if(in_array('owner', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, $ip['owner']); $x++; + } + # mac + if(in_array('mac', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, $ip['mac']); $x++; + } + # note + if(in_array('note', $selected_ip_fields)) { + $worksheet->write($lineCount, $x, $ip['note']); $x++; + } + + #custom + if(sizeof($custom_address_fields) > 0) { + foreach($custom_address_fields as $myField) { + $worksheet->write($lineCount, $x, $ip[$myField['name']]); $x++; + } + } + + //new line + $lineCount++; + } + } +} + + + + +/* -- Create a worksheet for subnets -- */ +if(sizeof($result_subnets)>0) { + $lineCount = 0; + + $worksheet =& $workbook->addWorksheet(_('Subnets')); + + //write headers + $worksheet->write($lineCount, 0, _('Section') ,$format_title); + $worksheet->write($lineCount, 1, _('Subet') ,$format_title); + $worksheet->write($lineCount, 2, _('Mask') ,$format_title); + $worksheet->write($lineCount, 3, _('Description') ,$format_title); + $worksheet->write($lineCount, 4, _('Master subnet') ,$format_title); + $worksheet->write($lineCount, 5, _('VLAN') ,$format_title); + $worksheet->write($lineCount, 6, _('IP requests') ,$format_title); + $c=7; + if(sizeof($custom_subnet_fields) > 0) { + foreach($custom_subnet_fields as $field) { + $worksheet->write($lineCount, $c, $field['name'], $format_title); + $c++; + } + } + + //new line + $lineCount++; + + foreach($result_subnets as $line) { + //cast + $line = (array) $line; + + //get section details + $section = (array) $Sections->fetch_section (null, $line['sectionId']); + + //format master subnet + if($line['masterSubnetId'] == 0) { $line['masterSubnetId'] = "/"; } + else { + $line['masterSubnetId'] = (array) $Subnets->fetch_subnet (null, $line['masterSubnetId']); + # folder? + if($line['masterSubnetId']['isFolder']==1) { $line['masterSubnetId'] = $line['masterSubnetId']['description']; } + else { $line['masterSubnetId'] = $Subnets->transform_to_dotted($line['masterSubnetId']['subnet']) .'/'. $line['masterSubnetId']['mask']. "(".$line['masterSubnetId']['description'].")"; } + } + //allowRequests + if($line['allowRequests'] == 1) { $line['allowRequests'] = 'yes'; } + else { $line['allowRequests'] = ''; } + + //vlan + //get VLAN for subnet + $vlan = (array) $Tools->fetch_object("vlans", "vlanId", $line['vlanId']); + //format vlan + $line['vlanId'] = is_numeric($vlan['number']) ? $vlan['number'] : ""; + + //print subnet + $worksheet->write($lineCount, 0, $section['name']); + if($line['isFolder']==1) { + $worksheet->write($lineCount, 1, _('Folder')); + $worksheet->write($lineCount, 2, ""); + } + else { + $worksheet->write($lineCount, 1, $Subnets->transform_to_dotted($line['subnet'])); + $worksheet->write($lineCount, 2, $line['mask']); + } + $worksheet->write($lineCount, 3, $line['description']); + $worksheet->write($lineCount, 4, $line['masterSubnetId']); + $worksheet->write($lineCount, 5, $line['vlanId']); + $worksheet->write($lineCount, 6, $line['allowRequests']); + //custom + $c=7; + if(sizeof($custom_subnet_fields) > 0) { + foreach($custom_subnet_fields as $field) { + $worksheet->write($lineCount, $c, $line[$field['name']]); + $c++; + } + } + + //new line + $lineCount++; + } +} + + + +/* -- Create a worksheet for VLANs -- */ +if(sizeof($result_vlans)>0) { + $lineCount = 0; + + $worksheet =& $workbook->addWorksheet(_('VLAN search results')); + + //write headers + $worksheet->write($lineCount, 0, _('Name') ,$format_title); + $worksheet->write($lineCount, 1, _('Number') ,$format_title); + $worksheet->write($lineCount, 2, _('Description') ,$format_title); + $c=3; + if(sizeof($custom_vlan_fields) > 0) { + foreach($custom_vlan_fields as $field) { + $worksheet->write($lineCount, $c, $field['name'], $format_title); + $c++; + } + } + + //new line + $lineCount++; + + foreach($result_vlans as $line) { + //cast + $line = (array) $line; + + //print subnet + $worksheet->write($lineCount, 0, $line['name'], $format_left); + $worksheet->write($lineCount, 1, $line['number']); + $worksheet->write($lineCount, 2, $line['description']); + //custom + $c=3; + if(sizeof($custom_vlan_fields) > 0) { + foreach($custom_subnet_fields as $field) { + $worksheet->write($lineCount, $c, $line[$field['name']]); + $c++; + } + } + //new line + $lineCount++; + } +} + + + + +// sending HTTP headers +$workbook->send($filename); + +// Let's send the file +$workbook->close(); + +?> \ No newline at end of file diff --git a/app/tools/search/search-results.php b/app/tools/search/search-results.php new file mode 100755 index 000000000..f1abfb964 --- /dev/null +++ b/app/tools/search/search-results.php @@ -0,0 +1,448 @@ + + +check_user_session(); + +# change * to % for database wildchar +$search_term = str_replace("*", "%", $search_term); + +# check if mac address or ip address +if(strlen($search_term)==17) { + if(substr_count($search_term, ":") == 5) { $type = "mac"; } //count : -> must be 5 +} +else if(strlen($search_term) == 12) { + if( (substr_count($search_term, ":") == 0) && (substr_count($search_term, ".") == 0) ) { $type = "mac"; } //no dots or : -> mac without : +} +else { $type = $Addresses->identify_address( $search_term ); } # identify address type + +# reformat if IP address for search +if ($type == "IPv4") { $search_term_edited = $Tools->reformat_IPv4_for_search ($search_term); } //reformat the IPv4 address! +elseif($type == "IPv6") { $search_term_edited = $Tools->reformat_IPv6_for_search ($search_term); } //reformat the IPv4 address! + + +# get all custom fields +$custom_address_fields = $Tools->fetch_custom_fields ("ipaddresses"); +$custom_subnet_fields = $Tools->fetch_custom_fields ("subnets"); +$custom_vlan_fields = $Tools->fetch_custom_fields ("vlans"); + +# set hidden custom fields +$hidden_fields = json_decode($User->settings->hiddenCustomFields, true); + +$hidden_address_fields = is_array(@$hidden_fields['ipaddresses']) ? $hidden_fields['ipaddresses'] : array(); +$hidden_subnet_fields = is_array(@$hidden_fields['subnets']) ? $hidden_fields['subnets'] : array(); +$hidden_vlan_fields = is_array(@$hidden_fields['vlans']) ? $hidden_fields['vlans'] : array(); + +# set selected address fields array +$selected_ip_fields = $User->settings->IPfilter; +$selected_ip_fields = explode(";", $selected_ip_fields); + +# set col size +$fieldSize = sizeof($selected_ip_fields); +$mySize = sizeof($custom_address_fields); +$colSpan = $fieldSize + $mySize + 4; + + + +/** search **/ + +# search addresses +if(@$_REQUEST['addresses']=="on" && strlen($_REQUEST['ip'])>0) { $result_addresses = $Tools->search_addresses($search_term, $search_term_edited['high'], $search_term_edited['low']); } +# search subnets +if(@$_REQUEST['subnets']=="on" && strlen($_REQUEST['ip'])>0) { $result_subnets = $Tools->search_subnets($search_term, $search_term_edited['high'], $search_term_edited['low']); } +# search vlans +if(@$_REQUEST['vlans']=="on" && strlen($_REQUEST['ip'])>0) { $result_vlans = $Tools->search_vlans($search_term); } + + +# all are off? +if(!isset($_REQUEST['addresses']) && !isset($_REQUEST['subnets']) && !isset($_REQUEST['vlans']) ) { include("search-tips.php"); } +elseif(strlen($_REQUEST['ip'])==0) { include("search-tips.php"); } +else { +if(sizeof($result_subnets)!=0 || sizeof($result_addresses)!=0 || sizeof($result_vlans)!=0) { + # formulate for results + $export_input = "search_term=$search_term"; + sizeof($result_subnets)==0 ? : $export_input .= "&subnets=on"; + sizeof($result_addresses)==0 ? : $export_input .= "&addresses=on"; + sizeof($result_vlans)==0 ? : $export_input .= "&vlans=on"; + + print(''); +} +?> + + + + +
    +

    :

    +
    + + + + + + + + + + + + 0) { + foreach($custom_subnet_fields as $field) { + if(!in_array($field['name'], $hidden_subnet_fields)) { + print " "; + } + } + } + ?> + + + 0) { + # loop + foreach($result_subnets as $line) { + # cast + $line = (array) $line; + + # check permission + $subnet_permission = $Subnets->check_permission($User->user, $line['id']); + if($subnet_permission > 0) { + $m++; + + //get section details + $section = (array) $Sections->fetch_section(null, $line['sectionId']); + //get vlan number + $vlan = (array) $Tools->fetch_object("vlans", "vlanId", $line['vlanId']); + + //format requests + $line['allowRequests'] = $line['allowRequests']==1 ? "enabled" : "disabled"; + + //format master subnet + if($line['masterSubnetId'] == 0) { $master_text = "/"; } + else { + $master_subnet = (array) $Subnets->fetch_subnet (null, $line['masterSubnetId']); + # folder? + if($master_subnet['isFolder']==1) { $master_text = " $master_subnet[description]"; } + else { $master_text = "$master_subnet[ip]/$master_subnet[mask]"; } + } + + //tr + print ''. "\n"; + + //section + print ' '. "\n"; + //folder? + if($line['isFolder']==1) { + print ' '. "\n"; + } else { + print ' '. "\n"; + } + print ' ' . "\n"; + //master + print ' ' . "\n"; + //vlan + print ' ' . "\n"; + //requests + print ' ' . "\n"; + + # custom fields + if(sizeof($custom_subnet_fields) > 0) { + foreach($custom_subnet_fields as $field) { + if(!in_array($field['name'], $hidden_subnet_fields)) { + print " "; + } + } + } + + #locked for writing + if($subnet_permission > 1) { + if(@$master_subnet['isFolder']==1) { + print " '. "\n"; + } + } + } +print "
    '. $section['name'] . ' '.$line['description'].''. $Subnets->transform_to_dotted($line['subnet']) . '/'.$line['mask'].''. $line['description'] .''. $master_text .''. @$vlan['number'] .''. _($line['allowRequests']) .'"; + } + print '
    "; +# show text if no results +if($m==0) { $Result->show("info", _("No results"), false); } +} +?> + + + + + +
    +

    :

    +
    + + + + + + +'._('IP address').''. "\n"; + # description + print ''. "\n"; + print ''. "\n"; + # mac + if(in_array('mac', $selected_ip_fields)) { print ''. "\n"; $address_span++; } + # switch + if(in_array('switch', $selected_ip_fields)) { print ''. "\n"; $address_span++; } + # port + if(in_array('port', $selected_ip_fields)) { print ''. "\n"; $address_span++; } + # owner and note + if( (in_array('owner', $selected_ip_fields)) && (in_array('note', $selected_ip_fields)) ) { print ''. "\n"; $address_span=$address_span+2; } + else if (in_array('owner', $selected_ip_fields)) { print ''. "\n"; $address_span++; } + else if (in_array('note', $selected_ip_fields)) { print ''. "\n"; $address_span++; } + + # custom fields + if(sizeof($custom_address_fields) > 0) { + foreach($custom_address_fields as $field) { + if(!in_array($field['name'], $hidden_address_fields)) { print ""; $address_span++; } + } + } + + # actions + print ''; +?> + + + + 0) { + /* print content */ + foreach ($result_addresses as $line) { + # cast + $line = (array) $line; + + # check permission + $subnet_permission = $Subnets->check_permission($User->user, $line['subnetId']); + if($subnet_permission > 0) { + $n++; + + //get the Subnet details + $subnet = (array) $Subnets->fetch_subnet (null, $line['subnetId']); + //get section + $section = (array) $Sections->fetch_section (null, $subnet['sectionId']); + + //detect section change and print headers + if ($result_addresses[$m]->subnetId != @$result_addresses[$m-1]->subnetId) { + print '' . "\n"; + print ' ' . "\n"; + print ''; + } + $m++; + + //print table + print ''. "\n"; + //address + print ' ' . "\n"; + //description + print ' ' . "\n"; + //dns + print ' ' . "\n"; + //mac + if(in_array('mac', $selected_ip_fields)) { + print ' '. "\n"; + } + //device + if(in_array('switch', $selected_ip_fields)) { + if(strlen($line['switch'])>0 && $line['switch']!="0") { + # get switch + $switch = (array) $Tools->fetch_object("devices", "id", $line['switch']); + $line['switch'] = $switch['hostname']; + } + else { + $line['switch'] = "/"; + } + + print ' ' . "\n"; + } + //port + if(in_array('port', $selected_ip_fields)) { print ' ' . "\n"; } + //owner and note + if((in_array('owner', $selected_ip_fields)) && (in_array('note', $selected_ip_fields)) ) { + print ' ' . "\n"; + print ' '. "\n"; + } + //owner only + else if (in_array('owner', $selected_ip_fields)) { print ' ' . "\n"; } + //note only + else if (in_array('note', $selected_ip_fields)) { + print ''. "\n"; + } + //custom fields + if(sizeof($custom_address_fields) > 0) { + foreach($custom_address_fields as $field) { + if(!in_array($field['name'], $hidden_address_fields)) { print ''. "\n"; } + } + } + + # print action links if user can edit + print ""; + + print '' . "\n"; + } + } +} +?> +
    '._('Description').''._('Hostname').''._('Port').'
    '. $section['name'] . ' :: ' . $subnet['description'] .' ('. $Subnets->transform_to_dotted($subnet['subnet']) .'/'. $subnet['mask'] .')
    '. $Subnets->transform_to_dotted($line['ip_addr']).""; + //tag + if(in_array('state', $selected_ip_fields)) { print $Addresses->address_type_format_tag($line['state']); } + print ' '. shorten_text($line['description'], $chars = 50) .''. $line['dns_name'] .''. "\n"; + if(strlen($line['mac']) > 0) { + print ''. "\n"; + } + print ' '. $line['port'] .'' . "\n"; + if(!empty($line['note'])) { + $line['note'] = str_replace("\n", "
    ",$line['note']); + print ' ' . "\n"; + } + print '
    "; + print "
    "; + + if($subnet_permission > 1) { + print " "; + print " "; + print " "; + } + # unlocked + else { + print " "; + print " "; + print " "; + } + print "
    "; + print "
    +show("info", _("No results"), false); +} +} +?> + + + + +
    +

    :

    +
    + + + + + + + + + 0) { + foreach($custom_vlan_fields as $field) { + if(!in_array($field['name'], $hidden_vlan_fields)) { + print " "; + $mf++; + } + } + } + ?> + + + + + 0) { + # print vlans + foreach($result_vlans as $vlan) { + # cast + $vlan = (array) $vlan; + + print '' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + # custom fields + if(sizeof($custom_vlan_fields) > 0) { + foreach($custom_vlan_fields as $field) { + if(!in_array($field['name'], $hidden_vlan_fields)) { + print " "; + } + } + } + # for admins print link + print " "; + print ''. "\n"; + } +} +?> + +
    +show("info", _("No results"), false); +} +?> + + + + + +
    \ No newline at end of file diff --git a/app/tools/search/search-tips.php b/app/tools/search/search-tips.php new file mode 100755 index 000000000..25b84869f --- /dev/null +++ b/app/tools/search/search-tips.php @@ -0,0 +1,79 @@ + + +

    +
    + +
    +
      + + +
    • +
        +
      • +
      • +
      + +
    • +
      + + +
    • +
        +
      • +
      • +
      + +
    • +
      + + +
    • +
        +
      • +
      + +
    • +
      + + +
    • +
        +
      • +
      + +
    • +
      + + +
    • +
        +
      • +
      + +
    • +
      + + +
    • +
        +
      • +
      + +
    • +
      + + +
    • +
        +
      • +
      + +
    • + +
    +
    \ No newline at end of file diff --git a/app/tools/subnet-masks/index.php b/app/tools/subnet-masks/index.php new file mode 100644 index 000000000..934c8308d --- /dev/null +++ b/app/tools/subnet-masks/index.php @@ -0,0 +1,14 @@ +check_user_session(); + +// set popup +$popup = false; +// table +include('print-table.php'); +?> \ No newline at end of file diff --git a/app/tools/subnet-masks/popup.php b/app/tools/subnet-masks/popup.php new file mode 100644 index 000000000..c549ea301 --- /dev/null +++ b/app/tools/subnet-masks/popup.php @@ -0,0 +1,40 @@ +check_user_session(); +?> + + +
    + + +
    + +
    + + +
    +
    + +
    +
    + diff --git a/app/tools/subnet-masks/print-table.php b/app/tools/subnet-masks/print-table.php new file mode 100644 index 000000000..0471937f0 --- /dev/null +++ b/app/tools/subnet-masks/print-table.php @@ -0,0 +1,50 @@ +get_ipv4_masks (); +?> + + table table-striped table-top "> + + + + + + + + + + "._("Subnet bits").""; + print ""; + } + ?> + + + + + +"; + print " "; + print " "; + print " "; + print " "; + print " "; + if(!$popup) { + print " "; + print " "; + + } + print ""; +} +?> + + +
    "._("Host bits")."
    /$m->bitmask$m->netmask$m->binary$m->subnets$m->hosts$m->subnet_bits$m->host_bits
    \ No newline at end of file diff --git a/app/tools/subnets/index.php b/app/tools/subnets/index.php new file mode 100755 index 000000000..5a1200934 --- /dev/null +++ b/app/tools/subnets/index.php @@ -0,0 +1,94 @@ +check_user_session(); + +# get all sections +$sections = $Sections->fetch_all_sections(); + +# get custom fields +$custom_fields = $Tools->fetch_custom_fields('subnets'); + +# set hidden fields +$hidden_fields = json_decode($User->settings->hiddenCustomFields, true); +$hidden_fields = is_array($hidden_fields['subnets']) ? $hidden_fields['subnets'] : array(); + +# title +print "

    "._('Available subnets')."

    "; +print "
    "; + +# table +print ""; + +# print vlans in each section +foreach ($sections as $section) { + # cast + $section = (array) $section; + + # check permission + $permission = $Sections->check_permission ($User->user, $section['id']); + if($permission > 0) { + + # set colspan + $colSpan = 9 + (sizeof($custom_fields)); + + # section names + print ""; + print " "; + print " "; + print " "; + print ""; + + # body + print ""; + + # headers + print " "; + print " "; + print " "; + print " "; + if($User->settings->enableVRF == 1) { + print " "; + } + print " "; + print " "; + print " "; + print " "; + + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $field) { + # hidden? + if(!in_array($field['name'], $hidden_fields)) { + print " "; + } + } + } + # actions + print ""; + + print ""; + + # get all subnets in section + $subnets = $Subnets->fetch_section_subnets ($section['id']); + + # no subnets + if(sizeof($subnets) == 0) { + print ""; + } + else { + $Subnets->print_subnets_tools($User->user, $subnets, $custom_fields); + } + + print ''; + + } # end permission check +} +?> + +

    $section[name] [$section[description]]

    "._('Subnet').""._('Description').""._('VLAN').""._('VRF').""._('Master Subnet')."
    "; + $Result->show("info", _('Section has no subnets')."!", false); + print "
    \ No newline at end of file diff --git a/app/tools/temp-shares/delete-result.php b/app/tools/temp-shares/delete-result.php new file mode 100644 index 000000000..abe614a5d --- /dev/null +++ b/app/tools/temp-shares/delete-result.php @@ -0,0 +1,47 @@ +
    +
    +check_user_session(); + +/* checks */ +if($User->settings->tempShare!=1) { $Result->show("danger", _("Temporary sharing disabled"), true); } +if(strlen($_POST['code'])!=32) { $Result->show("danger", _("Invalid code"), true); } + +# remove object +$old_access = json_decode($User->settings->tempAccess, true); +//check that it exists +if(!isset($old_access[$_POST['code']])) { $Result->show("danger", _("Code does not exist"), true); } +//remove +unset($old_access[$_POST['code']]); + +//reset +$new_access = !is_array($old_access) ? "" : json_encode(array_filter($old_access)); + +# execute +if(!$Admin->object_modify("settings", "edit", "id", array("id"=>1,"tempAccess"=>$new_access))) { $Result->show("danger", _("Temporary share delete error"), true); } +else { $Result->show("success", _("Temporary share deleted"), false); } + +?> +
    + +
    +
    + +
    +
    \ No newline at end of file diff --git a/app/tools/temp-shares/edit-result.php b/app/tools/temp-shares/edit-result.php new file mode 100644 index 000000000..8419c4e89 --- /dev/null +++ b/app/tools/temp-shares/edit-result.php @@ -0,0 +1,133 @@ +check_user_session(); + +/* checks */ +if($User->settings->tempShare!=1) { $Result->show("danger", _("Temporary sharing disabled"), true); } +if($_POST['type']!="subnets"&&$_POST['type']!="ipaddresses") { $Result->show("danger", _("Invalid type"), true); } +if(!is_numeric($_POST['id'])) { $Result->show("danger", _("Invalid ID"), true); } +if(strlen($_POST['code'])!=32) { $Result->show("danger", _("Invalid code"), true); } +if($_POST['validity']show("danger", _("Invalid date"), true); } +if($_POST['validity']>date("Y-m-d H:i:s", strtotime("+ 7 days"))) { $Result->show("danger", _("1 week is max validity time"), true); } +# verify each recipient +if(strlen($_POST['email'])>0) { + foreach (explode(",", $_POST['email']) as $rec) { + if(!filter_var(trim($rec), FILTER_VALIDATE_EMAIL)) { $Result->show("danger", _("Invalid email address")." - ".$rec, true); } + } +} + +# fetch object +$object = $Admin->fetch_object ($_POST['type'], "id", $_POST['id']); + +if($_POST['type']=="subnets") { + $tmp[] = "Share type: subnet"; + $tmp[] = "\t".$Subnets->transform_to_dotted($object->subnet)."/$object->mask"; + $tmp[] = "\t".$object->description; +} +else { + $tmp[] = "Share type: IP address"; + $tmp[] = "\t".$Subnets->transform_to_dotted($object->ip_addr); + $tmp[] = "\t".$object->description; +} + +# set new access +$new_access[$_POST['code']] = array("id"=>$_POST['id'], + "type"=>$_POST['type'], + "code"=>$_POST['code'], + "validity"=>strtotime($_POST['validity']), + "userId"=>$User->user->id + ); + +# create array of values for modification +$old_access = json_decode($User->settings->tempAccess, true); +if(!is_array($old_access)) { + $old_access = array(); +} else { + //remove all expired + foreach($old_access as $k=>$a) { + if(time()>$a['validity']) { + unset($old_access[$k]); + } + } + //reset array + is_array($old_access) ? : $old_access = array(); +} +$new_access = json_encode(array_merge($old_access, array_filter($new_access))); + +# execute +if(!$Admin->object_modify("settings", "edit", "id", array("id"=>1,"tempAccess"=>$new_access))) { $Result->show("danger", _("Temporary share create error"), true); } +else { $Result->show("success", _("Temporary share created"), false); } + +# send mail +if(strlen($_POST['email'])>0) { + + # fetch mailer settings + $mail_settings = $Admin->fetch_object("settingsMail", "id", 1); + + # initialize mailer + $phpipam_mail = new phpipam_mail($User->settings, $mail_settings); + $phpipam_mail->initialize_mailer(); + + // generate url + $url = createURL().rtrim(BASE, "/").create_link("temp_share",$_POST['code']); + + // set subject + $subject = "New ipam share created"; + + // set html content + $content[] = ""; + $content[] = ""; + + $content[] = ""; + $content[] = ""; + $content[] = ""; + $content[] = ""; + //set al content + $content_plain[] = "$subject"."\r\n------------------------------\r\n"; + $content_plain[] = "Hi, new share was created on ".$User->settings->siteTitle.", available on following address:\r\n ".$url; + $content_plain[] = "\r\nDetails: \r\n".implode("\r\n", $tmp)."\r\n"; + $content_plain[] = "\r\n\r\n"._("Sent by user")." ".$User->user->real_name." at ".date('Y/m/d H:i'); + $content[] = "
    $subject
    Hi, new share was created on ".$User->settings->siteTitle.", available on following address:
    $url

    Details:
    ".implode("
    ", $tmp)."
    Sent by user ".$User->user->real_name." at ".date('Y/m/d H:i')."
    "; + + // set alt content + $content = $phpipam_mail->generate_message (implode("\r\n", $content)); + $content_plain = implode("\r\n",$content_plain); + + + # try to send + try { + $phpipam_mail->Php_mailer->setFrom($mail_settings->mAdminMail, $mail_settings->mAdminName); + foreach(explode(",", $_POST['email']) as $r) { + $phpipam_mail->Php_mailer->addAddress(trim($r)); + } + $phpipam_mail->Php_mailer->Subject = $subject; + $phpipam_mail->Php_mailer->msgHTML($content); + $phpipam_mail->Php_mailer->AltBody = $content_plain; + //send + $phpipam_mail->Php_mailer->send(); + } catch (phpmailerException $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); + } catch (Exception $e) { + $Result->show("danger", "Mailer Error: ".$e->errorMessage(), true); + } + + # all good + $Result->show("success", _('Sending mail succeeded')."!" , true); +} +?> \ No newline at end of file diff --git a/app/tools/temp-shares/edit.php b/app/tools/temp-shares/edit.php new file mode 100644 index 000000000..e1f4b145b --- /dev/null +++ b/app/tools/temp-shares/edit.php @@ -0,0 +1,124 @@ +check_user_session(); + +# checks +if($User->settings->tempShare!=1) { $Result->show("danger", _("Temporary sharing disabled"), true, true); } +if($_POST['type']!="subnets"&&$_POST['type']!="ipaddresses") { $Result->show("danger", _("Invalid type"), true, true); } +if(!is_numeric($_POST['id'])) { $Result->show("danger", _("Invalid ID"), true, true); } + + +//fetch object details +$object = $Tools->fetch_object ($_POST['type'], "id", $_POST['id']); + + +# set share details +$share = new StdClass; +//set details +if($_POST['type']=="subnets") { + $tmp[] = "Share type: subnet"; + $tmp[] = $Subnets->transform_to_dotted($object->subnet)."/$object->mask"; + $tmp[] = $object->description; +} +else { + $tmp[] = "Share type: IP address"; + $tmp[] = $Subnets->transform_to_dotted($object->ip_addr); + $tmp[] = $object->description; +} +$share->details = implode("
    ", $tmp); + +//set code and timeframe +@$share->code = md5(time()); +$share->validity = date("Y-m-d H:i:s", strtotime("+1 day")); + +# set url for printing +$url = createURL().rtrim(BASE, "/").create_link("temp_share",$share->code); + +?> + + + + + + + + +
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + "._('Share details')."
    "; + print "
    "; + print $share->details; + print "
    "; + print "URL: $url"; + print "
    "; + print "
    "; + ?> + + + + +
    + + +
    + + +
    +
    + +
    + + + +
    +
    + + +
    + +
    +
    diff --git a/app/tools/temp-shares/index.php b/app/tools/temp-shares/index.php new file mode 100644 index 000000000..5870431e3 --- /dev/null +++ b/app/tools/temp-shares/index.php @@ -0,0 +1,95 @@ +check_user_session(); + +# fetch all shares +$temp_shares = json_decode($User->settings->tempAccess); +?> + +

    +
    + +settings->tempShare!=1) { + $Result->show("warning alert-absolute", _("Temporary sharing disabled"), false); +} +# none +elseif(!is_object($temp_shares)) { + $Result->show("info alert-absolute", _("No temporary shares available"), false); +} +else { +?> + + + + + + + + + + + + +fetch_object ($s->type, "id", $s->id); + # fetch user + $user = $Tools->fetch_object ("users", "id", $s->userId); + + //type test + $s->type_text = $s->type=="subnets" ? "Subnet" : "IP address"; + + //set details + unset($tmp); + if($s->type=="subnets") { + $tmp[] = "Share type: subnet
    "."".$Subnets->transform_to_dotted($object->subnet)."/$object->mask"; + $tmp[] = $object->description; + } + else { + $tmp[] = "Share type: IP address
    ".$Subnets->transform_to_dotted($object->ip_addr); + $tmp[] = $object->description; + } + $s->details = implode("
    ", $tmp); + + //validity + $class = time()>$s->validity ? "alert-danger" : "alert-success"; + + //access logs + unset($logText); + $logs = $Tools->fetch_multiple_objects ("logs", "details", $s->code, "date", false); + if($logs===false) { $logText = ""._("No access").""; } + else { + foreach($logs as $l) { + $logText[] = $l->date." (IP $l->ipaddr)"; + } + $logText = implode("
    ", $logText); + } + + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + # remove + print " "; + + print ""; +} +?> + +
    $s->type_text$s->details$s->code".date("Y-m-d H:i:s", $s->validity)."$user->real_name ($user->username)$logText"; + print "
    "; + print " "; + print "
    "; + print "
    + \ No newline at end of file diff --git a/app/tools/tools-menu-config.php b/app/tools/tools-menu-config.php new file mode 100644 index 000000000..2ff157fb4 --- /dev/null +++ b/app/tools/tools-menu-config.php @@ -0,0 +1,37 @@ +true, "icon"=>"fa-search", "name"=>"Search", "href"=>"search", "description"=>"Search database Addresses, subnets and VLANs"); +$tools_menu['Tools'][] = array("show"=>true, "icon"=>"fa-calculator", "name"=>"IP calculator", "href"=>"ip-calculator","description"=>"IPv4v6 calculator for subnet calculations"); +if($User->settings->enableChangelog == 1) +$tools_menu['Tools'][] = array("show"=>true, "icon"=>"fa-clock-o", "name"=>"Changelog", "href"=>"changelog", "description"=>"Show changelog for all network objects"); +$tools_menu['Tools'][] = array("show"=>true, "icon"=>"fa-info", "name"=>"Instructions", "href"=>"instructions", "description"=>"Instructions for managing IP addresses"); +$tools_menu['Tools'][] = array("show"=>true, "icon"=>"fa-list", "name"=>"Log files", "href"=>"logs", "description"=>"Browse phpipam log files"); + +# Subnets +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-star", "name"=>"Favourite networks", "href"=>"favourites", "description"=>"Show favourite networks"); +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-sitemap", "name"=>"Subnets", "href"=>"subnets", "description"=>"Show all subnets"); +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-cloud", "name"=>"VLAN", "href"=>"vlan", "description"=>"Show VLANs and belonging subnets"); +if($User->settings->enableVRF == 1) +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-cloud", "name"=>"VRF", "href"=>"vrf", "description"=>"Show VRFs and belonging networks"); +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-desktop", "name"=>"Devices", "href"=>"devices", "description"=>"Show all configured devices"); +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-eye", "name"=>"Scanned networks", "href"=>"scanned-networks", "description"=>"List of subnets to be scanned for online hosts and detect new hosts"); +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-th-large", "name"=>"Subnet masks", "href"=>"subnet-masks", "description"=>"Table of all subnet masks with different representations"); +// temp shares +if($User->settings->tempShare==1) +$tools_menu['Subnets'][] = array("show"=>true, "icon"=>"fa-share-alt", "name"=>"Temporary shares", "href"=>"temp-shares", "description"=>"List of temporary shared objects"); + +# user menu +$tools_menu['User Menu'][] = array("show"=>true, "icon"=>"fa-user", "name"=>"My account", "href"=>"user-menu", "description"=>"Manage your account"); + +?> \ No newline at end of file diff --git a/app/tools/tools-menu.php b/app/tools/tools-menu.php new file mode 100755 index 000000000..599bc208b --- /dev/null +++ b/app/tools/tools-menu.php @@ -0,0 +1,33 @@ +check_user_session(); + +# print +foreach($tools_menu as $k=>$tool) { + print "
    "; + # header + print "
    "; + print "

    "._($k)."

    "; + print "
    "; + + # items + print "
      "; + foreach($tool as $t) { + # active? + $active = $_GET['section']==$t['href'] ? "active" : ""; + # print + print "
    • "; + print ""._($t['name']).""; + print "
    • "; + } + print "
    "; + + print "
    "; +} +?> \ No newline at end of file diff --git a/app/tools/user-menu/index.php b/app/tools/user-menu/index.php new file mode 100755 index 000000000..e2a5afc2e --- /dev/null +++ b/app/tools/user-menu/index.php @@ -0,0 +1,273 @@ + + + + +check_user_session(); + +# fetch all languages +$langs = $User->fetch_langs(); + +/* print hello */ +print "

    ".$User->user->real_name.", "._('here you can change your account details').":

    "; +print "

    "; + +?> + + + +
    + + + + + + + + + + + + + + + + +user->authMethod == 1) { +?> + + + + + + + + + + + + + + + + + + + + + + +user->role=="Administrator") { ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    () + +
    + +
    + +
    + +

    + user->compressOverride == "Uncompress") print 'checked'; ?>> + + +
    + user->hideFreeRange == 1) print 'checked'; ?>> + + +
    + + + +
    + +
    +
    + + + + + + + +

    +
    + + + + + + + +user->widgets); //selected +$user_widgets = array_filter($user_widgets); + +print "
      "; + +# get all widgets +if($User->user->role=="Administrator") { $widgets = $Tools->fetch_widgets(true, false); } +else { $widgets = $Tools->fetch_widgets(false, false); } + +# first selected widgets already in user database +if(sizeof($user_widgets)>0) { + foreach($user_widgets as $k) { + print "
    • ".$widgets[$k]->wtitle."
    • "; + } +} +# than others, based on admin or normal user +foreach($widgets as $k=>$w) { + if(!in_array($k, $user_widgets)) { + $wtmp = $widgets[$k]; + print "
    • ".$widgets[$k]->wtitle."
    • "; + } +} + +print "
    "; +?> + + + + + \ No newline at end of file diff --git a/app/tools/user-menu/user-edit.php b/app/tools/user-menu/user-edit.php new file mode 100755 index 000000000..46a50538b --- /dev/null +++ b/app/tools/user-menu/user-edit.php @@ -0,0 +1,36 @@ +check_user_session(); + +# verify email +if(!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) { $Result->show("danger alert-absolute", _('Email not valid!'), true); } + +# verify password if changed (not empty) +if (strlen($_POST['password1']) != 0) { + if ( (strlen($_POST['password1']) < 8) && (!empty($_POST['password1'])) ) { $Result->show("danger alert-absolute", _('Password must be at least 8 characters long!'), true); } + else if ($_POST['password1'] != $_POST['password2']) { $Result->show("danger alert-absolute", _('Passwords do not match!', true)); } +} + +# set override +$_POST['compressOverride'] = @$_POST['compressOverride']=="Uncompress" ? "Uncompress" : "default"; + +# Update user +if (!$User->self_update ($_POST)) { $Result->show("danger alert-absolute", _('Error updating user account!'), true); } +else { $Result->show("success alert-absolute", _('Account updated successfully'), false); } +?> \ No newline at end of file diff --git a/app/tools/user-menu/user-widgets-set.php b/app/tools/user-menu/user-widgets-set.php new file mode 100755 index 000000000..b37722088 --- /dev/null +++ b/app/tools/user-menu/user-widgets-set.php @@ -0,0 +1,20 @@ +self_update_widgets ($_POST['widgets'])) { $Result->show("danger", _('Error updating'),true); } +else { $Result->show("success", _('Widgets updated'),true); } +?> \ No newline at end of file diff --git a/app/tools/vlan/domain-vlans.php b/app/tools/vlan/domain-vlans.php new file mode 100755 index 000000000..57a13d45c --- /dev/null +++ b/app/tools/vlan/domain-vlans.php @@ -0,0 +1,198 @@ +check_user_session(); + +# fetch l2 domain +$vlan_domain = $Tools->fetch_object("vlanDomains", "id", $_GET['subnetId']); +if($vlan_domain===false) { $Result->show("danger", _("Invalid ID"), true); } + +# get all VLANs and subnet descriptions +$vlans = $Tools->fetch_vlans_and_subnets ($vlan_domain->id); + +# get custom VLAN fields +$custom_fields = $Tools->fetch_custom_fields('vlans'); + +# set hidden fields +$hidden_fields = json_decode($User->settings->hiddenCustomFields, true); +$hidden_fields = is_array(@$hidden_fields['vlans']) ? $hidden_fields['vlans'] : array(); + +# size of custom fields +$csize = sizeof($custom_fields) - sizeof($hidden_fields); + + +# set disabled for non-admins +$disabled = $User->admin==true ? "" : "hidden"; + + +# title +print "

    "._('Available VLANs in domain')." $vlan_domain->name

    "; +print "
    "; + +if(sizeof($vlan_domains)>1) { +print "
    "; +print " "._('L2 Domains').""; +print "
    "; +} + +# no VLANS? +if($vlans===false) { + $Result->show("info", _("No VLANS configured"), false); +} +else { + # table + print ""; + + # headers + print ""; + print '' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + print ' ' . "\n"; + if(sizeof(@$custom) > 0) { + foreach($custom as $field) { + if(!in_array($field['name'], $hidden_fields)) { + print " "; + } + } + } + print " "; + print ""; + print ""; + + print ""; + $m = 0; + foreach ($vlans as $vlan) { + + // show free vlans - start + if($User->user->hideFreeRange!=1) { + if($m==0 && $vlan->number!=1) { + print ""; + print ""; + print ""; + print ""; + } + # show free vlans - before vlan + if($m>0) { + if( (($vlans[$m][0]->number)-($vlans[$m-1][0]->number)-1) > 0 ) { + print ""; + print ""; + # only 1? + if( (($vlans[$m][0]->number)-($vlans[$m-1][0]->number)-1) ==1 ) { + print ""; + } else { + print ""; + } + print ""; + } + } + } + + //save first + $first = $vlan[0]; + + //unset if no permissions + foreach($vlan as $k=>$v) { + if ($v->subnetId!=null) { + $permission = $Subnets->check_permission ($User->user, $v->subnetId); + if($permission==0) { + unset($vlan[$k]); + } + } + } + + //if none + if(sizeof($vlan)==0) { + $first->subnetId = null; + $vlan[0] = $first; + } + + //subnets + if(sizeof($vlan)>0) { + foreach($vlan as $k=>$v) { + //first? + if($k==0) { + //set odd / even + $n = @$n==1 ? 0 : 1; + $class = $n==0 ? "odd" : "even"; + //start - VLAN details + print ""; + print " "; + print " "; + print " "; + } else { + print ""; + print ""; + print ""; + print ""; + } + //subnet? + if ($v->subnetId!=null) { + //section + $section = $Sections->fetch_section (null, $v->sectionId); + print " "; + print " "; + //custom fields + if(sizeof(@$custom) > 0) { + foreach($custom as $field) { + # hidden + if(!in_array($field['name'], $ffields)) { + print ""; + } + } + } + print ""; + } + // no subnets + else { + print " "; + print " "; + //custom + for($z=0; $z<$csize; $z++) { + print " "; + } + print ""; + } + } + } + + # show free vlans - last + if($User->user->hideFreeRange!=1) { + if($m==(sizeof($vlans)-1)) { + if($User->settings->vlanMax > $vlan[0]->number) { + print ""; + print ""; + print ""; + print ""; + } + } + } + # next index + $m++; + } + print ""; + + print '
    '._('Number').''._('Name').''._('Description').''._('Belonging subnets').''._('Section').'
    "._('VLAN')." 1 - ".($vlan[0]->number -1)." (".($vlan[0]->number -1)." "._('free').")
    "._('VLAN')." ".($vlan[0]->number -1)." (".(($vlans[$m][0]->number)-($vlans[$m-1][0]->number)-1)." "._('free').") "._('VLAN')." ".($vlans[$m-1][0]->number+1)." - ".($vlan[0]->number -1)." (".(($vlans[$m][0]->number)-($vlans[$m-1][0]->number)-1)." "._('free').")
    ".$vlan[0]->number."".$vlan[0]->name."".$vlan[0]->description."
    ". $Subnets->transform_to_dotted($v->subnet) ."/$v->mask$section->name
    ///
    "._('VLAN')." ".($vlan[0]->number+1)." - ".$User->settings->vlanMax." (".(($User->settings->vlanMax)-($vlan[0]->number))." "._('free').")
    '; +} +?> diff --git a/app/tools/vlan/domains.php b/app/tools/vlan/domains.php new file mode 100644 index 000000000..bfd4a6074 --- /dev/null +++ b/app/tools/vlan/domains.php @@ -0,0 +1,46 @@ +

    +
    + + + + + + + + + + +id==1) { + $sections = "All sections"; + } + elseif(strlen(@$domain->permissions==0)) { + $sections = "None"; + } + else { + //explode + unset($sec); + $sections_tmp = explode(";", $domain->permissions); + foreach($sections_tmp as $t) { + //fetch section + $tmp_section = $Sections->fetch_section(null, $t); + $sec[] = $tmp_section->name; + } + //implode + $sections = implode("
    ", $sec); + } + + // print + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + + print ""; +} +?> +
    $domain->name$domain->description$sectionsShow VLANs
    \ No newline at end of file diff --git a/app/tools/vlan/index.php b/app/tools/vlan/index.php new file mode 100755 index 000000000..4abbc67a6 --- /dev/null +++ b/app/tools/vlan/index.php @@ -0,0 +1,23 @@ +check_user_session(); + +# fetch all l2 domains +$vlan_domains = $Tools->fetch_all_objects("vlanDomains", "id"); + +# set default domain +if(sizeof($vlan_domains)==1) { $_GET['subnetId'] = 1; } + +# vlan requested +if(isset($_GET['sPage'])) { include("vlan-details.php"); } +# we have more domains +elseif(sizeof($vlan_domains)>1 && !isset($_GET['subnetId'])) { include("domains.php"); } +# only 1 domain, print vlans +else { include("domain-vlans.php"); } + +?> \ No newline at end of file diff --git a/app/tools/vlan/vlan-details.php b/app/tools/vlan/vlan-details.php new file mode 100755 index 000000000..40ba20593 --- /dev/null +++ b/app/tools/vlan/vlan-details.php @@ -0,0 +1,195 @@ +check_user_session(); + +# get VLAN details +$vlan = (array) $Tools->fetch_object("vlans", "vlanId", $_GET['sPage']); + +# fetch l2 domain +$vlan_domain = $Tools->fetch_object("vlanDomains", "id", $vlan['domainId']); +if($vlan_domain===false) { $Result->show("danger", _("Invalid ID"), true); } + +# not existing +if($vlan[0]===false) { $Result->show("danger", _('Invalid VLAN id'), true); } + +# get custom VLAN fields +$custom_fields = $Tools->fetch_custom_fields('vlans'); +?> + + + +

    +
    + +id)."' data-action='add' data-switchid='' style='margin-bottom:10px;'> ". _('Back').""; +?> + + + + + + + + + + + + + + + + + + + + 0) { + + print ""; + print " "; + print ""; + + foreach($custom_fields as $key=>$field) { + $vlan[$key] = str_replace("\n", "
    ",$vlan[$key]); + + # fix for boolean + if($field['type']=="tinyint(1)" || $field['type']=="boolean") { + if($vlan[$key]==0) { $vlan[$key] = "false"; } + elseif($vlan[$key]==1) { $vlan[$key] = "true"; } + else { $vlan[$key] = ""; } + } + + print ""; + print " "; + print " "; + print ""; + } + } + + print ""; + print " "; + print ""; + + /* action button groups */ + print ""; + print " "; + print " "; + print ""; + + ?> + +
    '. $vlan['number']; ?>
    + +
    name ?>

    $key$vlan[$key]

    "._('Actions').""; + + print "
    "; + print "
    "; + + # permissions + if($User->isadmin==true) { + print " "; + print " "; + } + + print "
    "; + print "
    "; + + print "
    +
    + +fetch_vlan_subnets($vlan['vlanId']); + +# subnet count +$scnt = 0 ; +# check each subnet +if($subnets!==false) { + foreach ($subnets as $subnet) { + # cast + $subnet = (array) $subnet; + # check permission + $permission = $Subnets->check_permission ($User->user, $subnet['id']); + # add to array if permitted + if($permission > 0) { + //add to cnt + $scnt++; + # fetch secton details + $section = (array) $Sections->fetch_section(null, $subnet['sectionId']); + + $html[] = ""; + $html[] = "".$Subnets->transform_to_dotted($subnet['subnet'])."/$subnet[mask]"; + $html[] = "$subnet[description]"; + + # section + $html[] = "".$section['name'].""; + + # host check + if($subnet['pingSubnet']==1) { $html[] = ''; } + else { $html[] = ''; } + + # allow requests + if($subnet['allowRequests'] == 1) { $html[] = ''; } + else { $html[] = ''; } + + # edit + if($permission == 3) { + $html[] = ""; + $html[] = "
    "; + $html[] = " "; + $html[] = " "; + $html[] = " "; + $html[] = "
    "; + $html[] = ""; + } + else { + $html[] = ""; + $html[] = "
    "; + $html[] = " "; + $html[] = " "; + $html[] = " "; + $html[] = "
    "; + $html[] = ""; + } + $html[] = '' . "\n"; + } + } +} + +# print if some are present +if($scnt==0) { + print "
    "; + print "

    "._('VLAN')." "._('has no belonging subnets')."


    "; +} +else { + # print title + print "
    "; + print "

    "._('VLAN')." "._('has')." ".sizeof($subnets)." "._('belonging subnets').":



    "; + + # print HTML tabl + print ''. "\n"; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # content + print implode("\n", $html); + + print '
    "._('Subnet').""._('Subnet description').""._('Section')."
    '. "\n"; +} + +?> \ No newline at end of file diff --git a/app/tools/vrf/index.php b/app/tools/vrf/index.php new file mode 100755 index 000000000..e15196ef9 --- /dev/null +++ b/app/tools/vrf/index.php @@ -0,0 +1,124 @@ +check_user_session(); + +# fetch all VRFs +$vrfs = $Tools->fetch_all_objects("vrf", "vrfId"); + + +# title +print "

    "._('Available VRFs and belonging subnets')."

    "; +print "
    "; +if($User->isadmin) { + print " ". _('Manage').""; +} + + +/* for each VRF check which subnet has it configured */ +if(!$vrfs) { + $Result->show("info", _('No VRFs configured'), false); +} +else { + # print table + print ""; + + # loop + foreach ($vrfs as $vrf) { + # cast + $vrf = (array) $vrf; + + # print table body + print ""; + + # vrf name and details + print ""; + print " "; + print ""; + + # fetch subnets in vrf + $subnets = $Subnets->fetch_vrf_subnets ($vrf['vrfId'], null); + + # headers + print " "; + print " "; + print " "; + + # subnets + if($subnets) { + foreach ($subnets as $subnet) { + # cast + $subnet = (array) $subnet; + + # check permission + $permission = $Subnets->check_permission ($User->user, $subnet['id']); + + # permission + if($permission > 0) { + + # check if it is master + $masterSubnet = ($subnet['masterSubnetId'] == 0)||(empty($subnet['masterSubnetId'])) ? true : false; + + print ""; + + # get VLAN details + $subnet['VLAN'] = $Tools->fetch_object("vlans", "vlanId", $subnet['vlanId']); + $subnet['VLAN'] = (empty($subnet['VLAN']) || !$subnet['VLAN']) ? "" : $subnet['VLAN']->number; + + # get section name + $section = (array) $Sections->fetch_section(null, $subnet['sectionId']); + + print " "; + print " "; + # folder? + if($subnet->isFolder==1) { + print " "; + } + else { + print " "; + } + + if($masterSubnet) { + print ' ' . "\n"; + } + else { + $master = (array) $Subnets->fetch_subnet (null, $subnet['masterSubnetId']); + # orphaned + if(strlen($master['subnet']) == 0) { print " ";} + # folder + elseif($master['isFolder']==1) { print " "; } + else { print " "; } + } + + # allow requests + if($subnet['allowRequests'] == 1) { print ''; } + else { print ''; } + + print '' . "\n"; + } + } + } + # no subnets! + else { + print ''. "\n"; + print ''. "\n"; + print ''. "\n"; + } + # end + print ''; + } +} +print "

    $vrf[name]

    "._('VLAN').""._('Description').""; + print " "._('Subnet').""; + print " "._('Master Subnet').""; + print "
    $subnet[VLAN]$subnet[description]$subnet[description]".$Subnets->transform_to_dotted($subnet['subnet'])."/$subnet[mask]/".$Result->show('warning', _('Master subnet does not exist')."!", false)." $master[description]".$Subnets->transform_to_dotted($master['subnet'])."/$master[mask] ($master[description])
    '; + $Result->show("info", _('No subnets belonging to this VRF')."!", false); + print '
    "; + +?> \ No newline at end of file diff --git a/app/upgrade/index.php b/app/upgrade/index.php new file mode 100755 index 000000000..9dba994b3 --- /dev/null +++ b/app/upgrade/index.php @@ -0,0 +1,217 @@ +check_user_session(); +?> + + + + + + + + + + + + + + + + + + + + + + <?php print $User->settings->siteTitle; ?> + + + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    jQuery error!
    +

    + Hide +
    + + +
    + + + + + +
    ...
    + + + + + +
    +
    + + +settings->version //installed version (from database) + * VERSION //file version + * LAST_POSSIBLE //last possible for upgrade + */ + + +# authenticated, but not admins +if (!$User->is_admin(false)) { + # version is ok + if ($User->settings->version == VERSION) { + header("Location: ".create_link("login")); + } + # upgrade needed + else { + $title = 'phpipam upgrade required'; + $content = '
    Database needs upgrade. Please contact site administrator ('.$User->settings->siteAdminName.')!
    '; + } +} +# admins that are authenticated +elseif($User->is_admin(false)) { + # version ok + if ($User->settings->version == VERSION) { + $title = "Database upgrade check"; + $content = "
    Database seems up to date and doesn't need to be upgraded!
    "; + $content .= ''; + } + # version too old + elseif ($User->settings->version < LAST_POSSIBLE) { + $title = "Database upgrade check"; + $content = "
    Your phpIPAM version is too old to be upgraded, at least version ".LAST_POSSIBLE." is required for upgrade.
    "; + } + # upgrade needed + elseif ($User->settings->version < VERSION) { + $title = "phpipam database upgrade required"; + $title .= "
    Database needs to be upgraded to version v".VERSION.", it seems you are using phpipam version v".$User->settings->version."!
    "; + + // automatic + $content = "
    Automatic database upgrade

    "; + $content .= "
    "; + $content .= "
    Warning! Backup database first before attempting to upgrade it! You have been warned.
    "; + $content .= "Clicking on upgrade button will automatically update database to newest version!"; + $content .= "
    "; + $content .= "
    "; + $content .= "
    "; + + // manual + $content .= "
    Manual upgrade instructions

    "; + $content .= "
    "; + $content .= "Show instructions"; + $content .= ""; + $content .= "
    "; + } + # upgrade not needed, redirect to login + else { + header("Location: ".create_link("login")); + } +} +# default, smth is wrong +else { + header("Location: ".create_link("login")); +} + +?> + +
    +
    +

    + +
    +
    + +
    +
    +
    +
    + +
    +
    + + + + + +
    + + +
    + + + + + + + + +
    + + + + + \ No newline at end of file diff --git a/app/upgrade/upgrade-execute.php b/app/upgrade/upgrade-execute.php new file mode 100755 index 000000000..0dfd2ca83 --- /dev/null +++ b/app/upgrade/upgrade-execute.php @@ -0,0 +1,54 @@ +check_user_session(); + +# admin user is required +$User->is_admin(true); + +# try to upgrade database +if($Install->upgrade_database()) { $Result->show("success", _("Database upgraded successfully! Dashboard"), false); } + +# migrate settings +$User->migrate_domain_settings (); + +# check for possible errors +if(sizeof($errors = $Tools->verify_database())>0) { + $esize = sizeof($errors['tableError']) + sizeof($errors['fieldError']); + + print '
    '. "\n"; + + # print table errors + if (isset($errors['tableError'])) { + print ''._('Missing table').'s:'. "\n"; + print '
      '. "\n"; + foreach ($errors['tableError'] as $table) { + print '
    • '.$table.'
    • '. "\n"; + } + print '
    '. "\n"; + } + + # print field errors + if (isset($errors['fieldError'])) { + print ''._('Missing fields').':'. "\n"; + print '
      '. "\n"; + foreach ($errors['fieldError'] as $table=>$field) { + print '
    • Table `'. $table .'`: missing field `'. $field .'`;
    • '. "\n"; + } + print '
    '. "\n"; + } + print "
    "; +} +?> \ No newline at end of file diff --git a/app/vlan/index.php b/app/vlan/index.php new file mode 100644 index 000000000..43c41230e --- /dev/null +++ b/app/vlan/index.php @@ -0,0 +1,11 @@ +"; +include_once("vlan-details.php"); +print "
    "; + +# Subnets in VLAN +print '
    '; +include_once('vlan-subnets.php'); +print '
    '; +?> \ No newline at end of file diff --git a/app/vlan/vlan-details.php b/app/vlan/vlan-details.php new file mode 100755 index 000000000..bbac1219a --- /dev/null +++ b/app/vlan/vlan-details.php @@ -0,0 +1,75 @@ +check_user_session(); + +# get VLAN details +$vlan = $Tools->fetch_object("vlans", "vlanId", $_GET['subnetId']); +$vlan = (array) $vlan; + +# not existing +if(!$vlan) { $Result->show("danger", _('Invalid VLAN id'),true); } + +# get custom VLAN fields +$cfields = $Tools->fetch_custom_fields ('vlans'); +?> + + +

    +
    + + + + + + + + + + + + + + + + 0) { + foreach($cfields as $key=>$field) { + $vlan[$key] = str_replace("\n", "
    ",$vlan[$key]); + print ""; + print " "; + print " "; + print ""; + } + } + + # action button groups + print ""; + print " "; + print " "; + print ""; + + ?> + +
    '. $vlan['number']; ?>
    + +
    $key$vlan[$key]
    "._('Actions').""; + + print "
    "; + print "
    "; + + # permissions + if($User->is_admin (false)) { + print " "; + print " "; + } + + print "
    "; + print "
    "; + + print "
    +
    \ No newline at end of file diff --git a/app/vlan/vlan-subnets.php b/app/vlan/vlan-subnets.php new file mode 100755 index 000000000..f8f73171e --- /dev/null +++ b/app/vlan/vlan-subnets.php @@ -0,0 +1,118 @@ +check_user_session(); + +# fetch all subnets in VLAN in this section +$slaves = $Subnets->fetch_vlan_subnets ($_GET['subnetId'], $_GET['section']); + +# no subnets +if(!$slaves) { + print "
    "; + print "

    "._('VLAN')." $vlan->number (".$vlan->name.") "._('has no belonging subnets')."

    "; +} +else { + # cast + $vlan = (array) $vlan; + # print title + $slaveNum = sizeof($slaves); + print "

    "._('VLAN')." $vlan[number] (".$vlan['name'].") "._('has')." $slaveNum "._('belonging subnets').":



    "; + + # table + print ''. "\n"; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # print subnets + foreach ($slaves as $subnet) { + # cast + $subnet = (array) $subnet; + # check permission + $permission = $Subnets->check_permission ($User->user, $subnet['id']); + # allowed + $m=0; + if($permission > 0) { + + print ""; + print " "; + print " "; + + # host check + if($subnet['pingSubnet'] == 1) { print ''; } + else { print ''; } + + # increase IP count + $ipCount = 0; + if(!$Subnets->has_slaves ($subnet['id'])) { $ipCount = $Addresses->count_subnet_addresses ($subnet['id']); } //ip count - no slaves + else { + # fix for subnet and broadcast free space calculation + $ipCount = 0; //initial count + $Subnets->reset_subnet_slaves_recursive (); + $slaves2 = $Subnets->fetch_subnet_slaves_recursive ($subnet['id']); //fetch all slaves + foreach($Subnets->slaves as $s) { + $ipCount = $ipCount + $Addresses->count_subnet_addresses ($s['id']); + # subnet and broadcast add used + if($Subnets->get_ip_version ($s['subnet'])=="IPv4" && $s['mask']<31) { + $ipCount = $ipCount+2; + } + } + } + + # print usage + $calculate = $Subnets->calculate_subnet_usage ( (int) $ipCount, $subnet['mask'], $subnet['subnet'] ); + print ' '. "\n"; + print ' '; + + # allow requests + if($subnet['allowRequests'] == 1) { print ''; } + else { print ''; } + + # edit + if($permission == 3) { + print " "; + } + else { + print " "; + } + print '' . "\n"; + + $m++; + } + # no because of permissions + if($m==0) { + print ""; + print ""; + print ""; + } + } + print '
    "._('Subnet description').""._('Subnet')."
    $subnet[description]$subnet[ip]/$subnet[mask]"; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    "; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    "; + print ""; + $Result->show("info", _("VLAN has no belonging subnets")."!", false); + print "
    '. "\n"; +} +?> \ No newline at end of file diff --git a/app/vrf/index.php b/app/vrf/index.php new file mode 100644 index 000000000..af652838e --- /dev/null +++ b/app/vrf/index.php @@ -0,0 +1,11 @@ +"; +include_once("vrf-details.php"); +print "
    "; + +# Subnets in VRF +print '
    '; +include_once('vrf-subnets.php'); +print '
    '; +?> \ No newline at end of file diff --git a/app/vrf/vrf-details.php b/app/vrf/vrf-details.php new file mode 100755 index 000000000..48230515a --- /dev/null +++ b/app/vrf/vrf-details.php @@ -0,0 +1,63 @@ +check_user_session(); + +# get VLAN details +$vrf = $Tools->fetch_vrf ($method="vrfId", $_GET['subnetId']); + +# not existing +if(!$vrf) { $Result->show("danger", _('Invalid VRF id'), true); } +?> + + + + + +

    +
    + + + + + + + + + + + + + + + + "; + print " "; + print " "; + print ""; + ?> + +
    rd; ?>
    + name; ?> +
    description; ?>
    "._('Actions').""; + + print "
    "; + print "
    "; + + # permissions + if($User->is_admin (false)) { + print " "; + print " "; + } + + print "
    "; + print "
    "; + + print "
    +
    \ No newline at end of file diff --git a/app/vrf/vrf-subnets.php b/app/vrf/vrf-subnets.php new file mode 100755 index 000000000..a2e521a08 --- /dev/null +++ b/app/vrf/vrf-subnets.php @@ -0,0 +1,118 @@ +check_user_session(); + +# fetch all subnets in vrf in this section +$slaves = $Subnets->fetch_vrf_subnets ($_GET['subnetId'], $_GET['section']); + +# no subnets +if(!$slaves) { + print "
    "; + print "

    "._('VRF')." $vrf->number (".$vrf->description.") "._('has no belonging subnets')."

    "; +} +else { + # cast + $vrf = (array) $vrf; + # print title + $slaveNum = sizeof($slaves); + print "

    "._('VRF')." $vrf[number] (".$vrf['name'].") "._('has')." $slaveNum "._('belonging subnets').":



    "; + + # table + print ''. "\n"; + + # headers + print ""; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print " "; + print ""; + + # print subnets + foreach ($slaves as $subnet) { + # cast + $subnet = (array) $subnet; + # check permission + $permission = $Subnets->check_permission ($User->user, $subnet['id']); + # allowed + $m=0; + if($permission > 0) { + + print ""; + print " "; + print " "; + + # host check + if($subnet['pingSubnet'] == 1) { print ''; } + else { print ''; } + + # increase IP count + $ipCount = 0; + if(!$Subnets->has_slaves ($slave['id'])) { $ipCount = $Addresses->count_subnet_addresses ($subnet['id']); } //ip count - no slaves + else { + # fix for subnet and broadcast free space calculation + $ipCount = 0; //initial count + $Subnets->reset_subnet_slaves_recursive (); + $slaves2 = $Subnets->fetch_subnet_slaves_recursive ($subnet['id']); //fetch all slaves + foreach($Subnets->slaves as $s) { + $ipCount = $ipCount + $Addresses->count_subnet_addresses ($s['id']); + # subnet and broadcast add used + if($Subnets->get_ip_version ($s['subnet'])=="IPv4" && $s['mask']<31) { + $ipCount = $ipCount+2; + } + } + } + + # print usage + $calculate = $Subnets->calculate_subnet_usage ( (int) $ipCount, $subnet['mask'], $subnet['subnet'] ); + print ' '. "\n"; + print ' '; + + # allow requests + if($subnet['allowRequests'] == 1) { print ''; } + else { print ''; } + + # edit + if($permission == 3) { + print " "; + } + else { + print " "; + } + print '' . "\n"; + + $m++; + } + # no because of permissions + if($m==0) { + print ""; + print ""; + print ""; + } + } + print '
    "._('Subnet description').""._('Subnet')."
    $subnet[description]$subnet[ip]/$subnet[mask]"; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    "; + print "
    "; + print " "; + print " "; + print " "; + print "
    "; + print "
    "; + print ""; + $Result->show("info", _("VRF has no belonging subnets")."!", false); + print "
    '. "\n"; +} +?> \ No newline at end of file diff --git a/config.php b/config.php new file mode 100755 index 000000000..225d75ce6 --- /dev/null +++ b/config.php @@ -0,0 +1,36 @@ + diff --git a/css/bootstrap/bootstrap-colorpicker.min.css b/css/bootstrap/bootstrap-colorpicker.min.css new file mode 100644 index 000000000..fff7ec1dd --- /dev/null +++ b/css/bootstrap/bootstrap-colorpicker.min.css @@ -0,0 +1,9 @@ +/*! + * Bootstrap Colorpicker + * http://mjolnic.github.io/bootstrap-colorpicker/ + * + * Originally written by (c) 2012 Stefan Petre + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0.txt + * + */.colorpicker-saturation{float:left;width:100px;height:100px;cursor:crosshair;background-image:url("../images/bootstrap-colorpicker/saturation.png")}.colorpicker-saturation i{position:absolute;top:0;left:0;display:block;width:5px;height:5px;margin:-4px 0 0 -4px;border:1px solid #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-saturation i b{display:block;width:5px;height:5px;border:1px solid #fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-hue,.colorpicker-alpha{float:left;width:15px;height:100px;margin-bottom:4px;margin-left:4px;cursor:row-resize}.colorpicker-hue i,.colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:100%;height:1px;margin-top:-1px;background:#000;border-top:1px solid #fff}.colorpicker-hue{background-image:url("../images/bootstrap-colorpicker/hue.png")}.colorpicker-alpha{display:none;background-image:url("../images/bootstrap-colorpicker/alpha.png")}.colorpicker{top:0;left:0;z-index:2500;min-width:130px;padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1}.colorpicker:before,.colorpicker:after{display:table;line-height:0;content:""}.colorpicker:after{clear:both}.colorpicker:before{position:absolute;top:-7px;left:6px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.colorpicker:after{position:absolute;top:-6px;left:7px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.colorpicker div{position:relative}.colorpicker.colorpicker-with-alpha{min-width:140px}.colorpicker.colorpicker-with-alpha .colorpicker-alpha{display:block}.colorpicker-color{height:10px;margin-top:5px;clear:both;background-image:url("../images/bootstrap-colorpicker/alpha.png");background-position:0 100%}.colorpicker-color div{height:10px}.colorpicker-element .input-group-addon i,.colorpicker-element .add-on i{display:inline-block;width:16px;height:16px;vertical-align:text-top;cursor:pointer}.colorpicker.colorpicker-inline{position:relative;z-index:auto;display:inline-block;float:none}.colorpicker.colorpicker-horizontal{width:110px;height:auto;min-width:110px}.colorpicker.colorpicker-horizontal .colorpicker-saturation{margin-bottom:4px}.colorpicker.colorpicker-horizontal .colorpicker-color{width:100px}.colorpicker.colorpicker-horizontal .colorpicker-hue,.colorpicker.colorpicker-horizontal .colorpicker-alpha{float:left;width:100px;height:15px;margin-bottom:4px;margin-left:0;cursor:col-resize}.colorpicker.colorpicker-horizontal .colorpicker-hue i,.colorpicker.colorpicker-horizontal .colorpicker-alpha i{position:absolute;top:0;left:0;display:block;width:1px;height:15px;margin-top:0;background:#fff;border:0}.colorpicker.colorpicker-horizontal .colorpicker-hue{background-image:url("../images/bootstrap-colorpicker/hue-horizontal.png")}.colorpicker.colorpicker-horizontal .colorpicker-alpha{background-image:url("../images/bootstrap-colorpicker/alpha-horizontal.png")}.colorpicker.colorpicker-hidden{display:none}.colorpicker.colorpicker-visible{display:block}.colorpicker-inline.colorpicker-visible{display:inline-block}.colorpicker-right:before{right:6px;left:auto}.colorpicker-right:after{right:7px;left:auto} \ No newline at end of file diff --git a/css/bootstrap/bootstrap-custom.css b/css/bootstrap/bootstrap-custom.css new file mode 100644 index 000000000..eaf0c1c17 --- /dev/null +++ b/css/bootstrap/bootstrap-custom.css @@ -0,0 +1,2125 @@ +/** + * + * Bootstrap fixes for phpipam + * + */ + + + +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 300; + src: local('Lato Light'), local('Lato-Light'), url("../fonts/lato/Lato-light.ttf") format('truetype'); +} +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 400; + src: local('Lato Regular'), local('Lato-Regular'), url("../fonts/lato/Lato-regular.ttf") format('truetype'); +} + + +/* @general ---------- */ +body { + font-size: 13px; + font-weight: 300; +} +input, +select { + font-weight: 300; +} +table.table-full { + width: 100%; +} +table.table-auto { + width: auto; +} +table.table-auto th, +table.table-auto td { + padding-left: 10px; + padding-right: 10px; +} +table.table-top th { + border-bottom: medium double #dddddd; +} +table.table-noborder th, +table.table-noborder td { + border: 0px !important; +} +table th { + vertical-align: top !important; +} +table tr.text-top td { + vertical-align: top !important; +} +table.table-auto-wide th, +table.table-auto-wide td { + padding-left: 25px !important; + padding-right: 25px !important; +} + +hr { + margin: 5px; + margin-left: 0px; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} +h4 { + font-weight: 300; + text-shadow: 1px 1px 0px white; + font-size: 18px; +} +h5 { + font-weight: 300; + text-shadow: 1px 1px 0px white; + font-size: 14px; +} +h1, h2, h3, h4, h5, .pHeader { + font-family: "Lato", Arial, sans-serif; + font-weight: 300; + +/* font-family: 'ecoicons'; */ +/* -webkit-font-smoothing: antialiased; */ +} +.row { + margin: 0px; +} +pre { + font-size: 11px; + line-height: normal; + color: gray; + background: #f9f9f9; + border-radius: 0px; + border: 1px solid #ddd; + color: #686868; + font-weight: normal; +} +xmp { + font-size: 12px; + line-height: normal; + font-weight: normal; +} +blockquote { + border-left-width: 2px !important; +} +blockquote p { + font-size: 15px; +} +blockquote small { + font-size: 12px; +} + + + + +/* @inputs ---------- */ +.table input[type="text"], +.table input[type="select"], +.table input[type="password"], +select, +input[type="file"] { + margin-bottom: 0px; +} +select, input[type="file"] { +} + + + + + +/* @tables ---------- */ +.table td { + vertical-align: middle !important; +} +.table td.actions { + text-align: right !important; + padding-right: 0px !important; + width: auto; +} +.table td.al-center { + text-align: center; +} + +table.customIP tbody { + border-top:none; + border-bottom: none; +} +table.customIP th { + border-top: none; +} +table.customIP td.result { + background: transparent !important; +} +table.customIP h5 { + border: none; + margin: 0px; + margin-top: 5px; +} +table.table-condensed th, +table.table-condensed td { + +} +table td.info2, +.info2, +.muted { + color: #999; + text-shadow: 1px 1px 1px white; + + margin: 2px; +} +.info2 checkbox { + margin-top: 2px; +} +.subnet-mask-table th, +.subnet-mask-table td { + padding-left: 10px; + padding-right: 10px; +} + + + + + +/* @inputs */ +.input-w-100 { + width: 100px; +} +.input-w-150 { + width: 150px; +} +.input-w-200 { + width: 200px; +} +.input-w-250 { + width: 250px; +} +.input-w-auto { + width: auto; +} +.input-max-200 { + max-width: 200px; +} + + + + + + +/* import li */ +ul.progressUl { + margin: 5px; +} +ul.progressUl li { + list-style: none; +} + + + + + +/* @icons buttons ---------- */ +.fa-white { + color: white; +} +.fa-blue { + color: #428bca; +} +.fa-gray { + color: #777 !important; +} +.fa-red { + color: #F59C99; +} +.fa-silver { + color: #aaa; +} +.fa-sfolder { + color: #888; +} +.fa-pad-right { + padding-right: 5px; +} +.fa-marg-right { + margin-right: 5px; +} +.fa-pad-right-3 { + padding-right: 3px; +} +.fa-pad-left { + padding-left: 5px; +} +.fa-sm { + font-size: 12px; +} + +/* fix for closed folder */ +.fa-folder-close-o:before { + content: "\f114"; +} +td#actions .btn-group .btn { + padding: 1px 8px; +} +.btn:hover { + margin-right: 0px; +} + + + + + +/* @alerts ---------- */ +.alert { + padding: 10px; +} +.alert-danger { + background: #fff5f5; +} +.alert-info { + background: #F1FAFE; +} +.alert-success { + background: #eef9ea; +} +.alert-muted { + background: white; + color: #777; + border: none; +} +.text-muted, +td.info2 { + font-size: 12px; +} + + + + + + + + + + +/* @header ---------- */ +#header { + background: #282b2e; + margin: 0px; +} +.header-install { + border-bottom: 1px solid #aaa; + + box-shadow: 0px 12px 0px #40454a; + + margin-bottom: 40px !important; +} +.hero-unit { + text-align: center; + + padding: 0px; + padding-top: 0px; + padding-bottom: 10px; + padding-right: 50px; + + margin-bottom: 0px; + + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + border-radius: 0px; +} +#header #user_menu .container-fluid { + margin-right: 0px; + padding-right: 0px; +} +.hero-pusher { + height: 30px; + color: white; +} +.hero-unit, +.hero-unit a { + font-family: Georgia,Times,'Times New Roman',serif; + font-size: 36px; + + color: white; + text-shadow: black 2px 2px 2px; +/* white-space: nowrap; */ +} + +/* @usermenu top */ +div#user_menu { + padding: 5px; + + text-align: right; + + z-index: 98; +} +div#user_menu span.info { + padding: 0px; + margin: 0px; + font-size: 12px; + color: silver; +} +div#user_menu a { + color: white; +} +div#user_menu #searchForm { + margin-bottom: 5px; +} +#searchSelect { + display: none; + + position: absolute; + background: white; + padding: 5px; + margin-top: -5px; +} + + + + + + + + + +/* @menu ---------- */ +#sections_overlay { +} +#menu { + margin: 0px; + padding: 0px; + + border: 0px; +} +.navbar { + position: relative; + min-height:inherit; + margin-bottom: 0px; + border: 1px solid transparent; +} +nav.navbar#menu-navbar { + margin: 0px; + padding: 0px; + + border-left: 0px; + border-right: 0px; + border-top: 0px; + + border-radius: 0px; +} +#menu-collapse { + margin: 0px; + padding: 0px; +} +.navbar-nav.navbar-right:last-child { + margin-right: 0px; +} + + +table#subnetsMenu td#subnetsLeft .menu-administration, +table#subnetsMenu td#subnetsLeft .menu-tools { + padding: 0px; + width: 230px; +} +table#subnetsMenu td#subnetsLeft .menu-tools { + width: 200px; +} +#leftMenu.menu-administration ul li, +#leftMenu.menu-tools li a { + border-radius: 0px !important; + border-left: 0px; + border-right: 0px; +} +.adminMenu, +.toolsMenu { + border: 1px solid #efefef; + border-right: 0px; + border-radius: 0px !important; + border-bottom: 0px; + + margin-bottom: 0px; +} +.adminMenu .panel-heading, +.toolsMenu .panel-heading { + + background: #f9f9f9 !important; + + border-radius: 0px !important; + border-top-left-radius: 0px; + border-top-right-radius: 0px; + + border-color: #efefef; + border-right: 0px; + + padding: 10px 10px; + padding-top: 15px; + + padding-left: 2px; +} +.panel-heading { + text-transform: uppercase; +} +.adminMenu, +.toolsMenu { + border: 0px; +} + +.adminMenu li, +.toolsMenu li { + border-left: 5px solid #ccc !important; +} +.toolsMenu li.active { + border-left: 5px solid #428bca !important; +} +.adminMenu li.active { + background: #fff1f1 !important; + border-left: 5px solid #a94442 !important; +} + +.adminMenu .panel-heading i, +.toolsMenu .panel-heading i { + padding-right: 5px; + color: #aaa; +} +.adminMenu .list-group li i, +.toolsMenu .list-group li i { + display: none; + margin-top: 3px; +} +.adminMenu .list-group li.active i, +.toolsMenu .list-group li.active i { + display: block; +} + +.adminMenu:nth-child(1) div, +.toolsMenu:nth-child(1) div { + padding-top: 30px; +} +.adminMenu .panel-heading h3, +.toolsMenu .panel-heading h3 { + font-size: 14px; +} +.adminMenu .list-group li, +.toolsMenu .list-group li { + padding: 10px 15px; + border-color: #efefef; + padding-left: 18px; + + border-radius: 0px !important; +} +.toolsMenu .list-group li:last-child { + margin-bottom: 1px; + border-radius: 0px; +} +.toolsMenu li.active a { + font-weight: normal !important; +} +.adminMenu li.active a { + color: #a94442; + font-weight: normal !important; +/* + #ebccd1; + #a94442; + #fff5f5; +*/ +/* font-weight: 300 !important; */ +} + +#subnetsContent .menu-administration, +#subnetsContent .menu-tools { + padding-left: 20px; +} + + + + +/* @nav navigation ---------- */ +.navbar#menu-navbar { + min-height: 0px; + margin-bottom: 0px; + + background: #40454a; +} +.navbar#menu-navbar a { + font-weight: 200; +} +.navbar#menu-navbar li a { + color: white; +} +.navbar#menu-navbar li:hover a { + background: #282b2e +} +#menu-navbar .navbar-nav>li>a { + padding: 10px 12px; +} +#menu-navbar ul.navbar-nav li.active a { + padding-bottom: 11px; +} +#menu-navbar ul li.active a, +#menu-navbar ul li.dropdown.open a, +.navbar#menu-navbar ul.icon-ul li.active { + background: #585e65; + color: white; +} +#menu-navbar ul li.dropdown.open.administration a { + background: #d9534f; + color: white; +} +#menu-navbar ul li.dropdown.open li a, +#menu-navbar ul li.dropdown.open.administration li a { + color: #ccc; +} +#menu-navbar ul li ul, +#menu-navbar ul li ul a { + background: #40454a !important; + font-size: 13px; +} +#menu-navbar ul li ul li { + margin: 0px !important; + border-radius: 0px; +} +#menu-navbar ul li ul li.active a { + background: #222 !important; +} +#menu-navbar ul li ul li.nav-header, +#menu-navbar ul.icon-ul li ul li.nav-header { + color: white; + padding-left: 10px; + white-space: nowrap; + padding-bottom: 5px; + + background: transparent; + + padding-top: 15px; + padding-bottom: 5px; +} +#menu-navbar ul li ul li.nav-header:hover { + background: transparent !important; +} +/* admin color */ +.navbar#menu-navbar li a.btn-danger, +.navbar#menu-navbar li ul.admin li:hover { + background: #d9534f; + border-color: #d43f3a; +} +.navbar#menu-navbar li a.btn-success { + border-color: #d43f3a; +} +#menu-navbar ul li ul.dropdown-menu li a { + color: #ccc; +} +#menu-navbar ul li ul.dropdown-menu li:hover a, +#menu-navbar ul li.dropdown.open.administration li a:hover { + background-color: #585e65 !important; +/* color: #555; */ + color: white; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: white; +} +/* icons */ +#menu-navbar ul.navbar-nav.icon-ul li a.icon-li { + padding: 4px; + text-align: center; +} +.navbar#menu-navbar ul.icon-ul li { + background: #585e65; + + margin: 5px; + margin-top: 6px; + + margin-left: -1px; + width: 35px; + text-align: center; + + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.navbar#menu-navbar ul.icon-ul li:hover a, +.navbar#menu-navbar ul.icon-ul li a.btn-info, +.navbar#menu-navbar ul.icon-ul li a.btn-danger, +.navbar#menu-navbar ul.icon-ul li a.btn-warning, +.navbar#menu-navbar ul.icon-ul li a.btn-success, +.navbar#menu-navbar ul.icon-ul li.active a { + -webkit-border-radius: 3px !important; + -moz-border-radius: 3px !important; + border-radius: 3px !important; +} +.navbar#menu-navbar ul.icon-ul li a.btn-info:hover { + background: #5bc0de; +} +.navbar#menu-navbar ul.icon-ul li a.btn-success:hover { + background: #5cb85c; +} +.navbar#menu-navbar ul.icon-ul li a.btn-warning:hover { + background: #f0ad4e; +} +.navbar#menu-navbar ul.icon-ul li ul li { + width: auto; + text-align: left; +} +.navbar#menu-navbar ul.icon-ul li.icon-li a { + padding: 4px; + font-size: 11px; +} +.navbar#menu-navbar ul.icon-ul li.active a { + background: #282b2e; + color: white; +} +.navbar#menu-navbar li.tools ul li a { + padding: 3px 20px; +} +.dberror { + background: red !important; +} + +/* menu override for small devices */ +.navbar-nav { + margin: 0px !important; +} + + + + +/* @general page style */ +#mainContainer { + margin-top: 0px; + padding: 0px; +} +#content { + padding-left: 10px; + padding-right: 10px; +} +/* tabela za strukturo */ +table#subnetsMenu { + +} +table#subnetsMenu td#subnetsLeft { + vertical-align:top; + white-space:nowrap; + width:200px; + border-right: 1px solid #eee; + + vertical-align: top !important; +} +table#subnetsMenu td#subnetsLeft .menu-subnets { + margin-top: 20px; +} +table#subnetsMenu td#subnetsContent { + padding-top: 20px; +} + + + + + + + + +/* @dashboard ---------- */ + +#dashboard .inner { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + + background: #40464a; + + border: 1px solid #ddd; + + margin: 5px; + margin-bottom: 15px; +/* padding: 3px 10px; */ + + background: white; + + min-height: 260px; +} +.inner.install h4 { + background: #F1FAFE !important; + border-bottom: 1px solid #DAE3EA; + text-align: center; + padding: 20px !important; +} +.inner.install input { + margin: 5px auto; +} +#dashboard .welcome { + margin-left: 1%; + text-align:center; + font-size: 14px; +} +#dashboard .inner h4 { + background: #f5f5f5; + padding: 10px; + + margin-top: 0px; + margin-bottom: 0px; +} +#dashboard .hContent { +} +#dashboard h4 i { + font-size: 12px; +} +#dashboard a h4 { + color: #333; +} +#dashboard a:hover { + text-decoration: none; +} +#dashboard .inner { + overflow: hidden; +} +#dashboard .inner.movable { + +} +#dashboard .inner i.icon-action { + cursor: pointer; + margin: 7px; +} +#dashboard .table { + margin-bottom: 5px; +} +#dashboard .table th, +#dashboard .table td { +} +#dashboard .drag.widget-dash .inner { + border: 1px solid #468847; + background: #dff0d8; +} +#dashboard .inner i.icon-action { + display: none; +} + + +#dashboard.tools-all .widget-dash .inner:hover i { +/* background: #d9edf7; */ + color: #428bca; +} +#dashboard.tools-all .widget-dash .inner { + box-shadow: 0px 0px 1px white; +} +#dashboard.tools-all .inner { + padding: 0px; + overflow: hidden; + position: relative; +} +#dashboard.tools-all .inner { + min-height: 0px; +} +#dashboard.tools-all .inner .hContent div { + padding: 10px; +} +#dashboard.tools-all .inner .hContent .icon { + text-align: center; + border-right: 1px solid #ddd; + width: 50px; + padding-left: 10px; + height: 100%; + padding-top: 20px; + + float: left; + position: absolute; +} +#dashboard.tools-all .inner .hContent .text { + padding-left: 60px; +} +#dashboard.tools-all .inner .hContent i { + font-size: 24px; + color: #ccc; + margin-top: 4px; +} + +.breadcrumb { + padding-left: 0px; + padding-right: 0px; + background: transparent; + padding: 0px; + margin: 0px; +} +span.jclock { + padding-right: 10px; + color: #999; +} + +span.severity0 { + color: green; +} +span.severity1 { + color: orange; +} +span.severity2 { + color: red; +} + +#w-access_logs a, +#w-error_logs a { + color: #333; + font-weight: 300; + cursor: pointer; +} + + + + +/* @subnets list */ +table#subnetsMenu { + width: 100%; +} +table#subnetsMenu td { + vertical-align: top; + border: none; +} +#leftMenu.menu-administration, +#leftMenu.menu-tools { + padding-left: 10px; + padding-right: 10px; +} +table#subnetsMenu li.folder, +table#subnetsMenu li.folderF { + cursor: pointer; +} + +#leftMenu { +/* border-right: 1px solid #eee; */ + + padding: 0px; +} +#leftMenu h4 { + padding-left: 10px; +} +div.subnets { +/* padding-left: 10px; */ +} + + +/* subnets list */ +ul#subnets { + list-style: none; + margin-left: 5px; + padding-left: 0px; +} +ul#subnets li { + line-height: 20px; + padding: 3px; +} +ul.submenu { + list-style: none; + margin-left: 0px; +} +ul[class*="submenu-"] { + /* vertical ul lines */ + background-repeat: repeat-y; + padding-left: 0px; +} +ul[class*="submenu-"] li { + /* horizontal ul lines */ + padding-left: 20px !important; + background: url("../../css/images/li.png") no-repeat 4px -3px; + + /* + barva li: #ced2d2 + barva ozadja: #f9f9f9 + barva active: #f1fafe + */ + +} +ul[class*="submenu-"] li:last-child { + /* last child in inactive line */ + background: url("../../css/images/ul-li-bg.png") no-repeat 3px 0px; +} +li.active ul[class*="submenu-"] li:last-child { + /* last child in active line */ + background: url("../../css/images/ul-li-bg-active.png") no-repeat 3px 0px; +} +/* active leaf */ +ul#subnets li.folder li.leaf.active { + background: #F1FAFE url("../../css/images/li.png") no-repeat 4px -3px !important; +} +/* active submenu background */ +ul[class*="submenu-"] li.folder.active { + background: #F1FAFE url("../../css/images/li.png") no-repeat 4px -3px !important; +} + +ul.submenu.submenu-open { + display: block; +} +/* white folder icon backgrounds */ +ul#subnets li.folder i, +ul.submenu li i.fa-folder-close-o, +/* ul.submenu li[class!=active] i.fa-folder-open-o { */ +ul.submenu li i.fa-folder-open-o { + background: #f9f9f9; +} +/* blue folder icon backgrounds */ +ul#subnets li.active ul li.folder i, +ul.submenu li.active i.fa-folder-close-o, +ul.submenu li.active i.fa-folder-open-o { + background: #F1FAFE; +} +.icon-folder-close, +.icon-folder-open { + cursor: pointer; +} +.action { + background: #F1FAFE; + + border-top: 1px solid #DAE3EA; + border-bottom: 1px solid #DAE3EA; + + text-align: right; + + margin: 0; + + padding: 5px; + text-shadow: 1px 1px 0px white; +} +ul#subnets li.active { + text-align: left; + padding-left: 3px; + + + background-color: #F1FAFE; + + border-top: 1px solid #DAE3EA; + border-bottom: 1px solid #DAE3EA; + + + margin: 0; + + text-shadow: 1px 1px 0px white; +} +ul#subnets li.active a { + color: #333; + font-weight: bold; +} +ul#subnets li.active li a { + font-weight: 300; + color: #333; +} +ul#subnets li.leaf.active i { + padding-left: 0px; + border: none; +} +ul#subnets li i[class*="fa-folder"] { + min-width: 14px; +} + + +.alert hr { + border-top: 0px; +} +.alert-dash { + margin-top: 10px; +} +.alert-nomargin { + margin: 0px; +} +.alert-absolute { +/* position: absolute; */ + clear: both; + float: left; +} +.alert-norounded { + -webkit-border-radius: 0px; + -moz-border-radius: 0px; + border-radius: 0px; + + border-left: 0px; + border-right: 0px; +} + + +/* @IP addresses and subnet details ---------- */ +.table.ipaddresses tbody tr:hover td, +.table.slaves tbody tr:hover td, +.table-striped tbody tr:hover td { + background: rgba(225,225,225,0.3) !important; +} +.table.ipaddresses tbody tr:nth-child(even) td, +.table.slaves tbody tr:nth-child(even) td, +.table-striped tbody tr:nth-child(even) td { + background: white; +} +.table.ipaddresses tbody tr:nth-child(even):hover td, +.table.slaves tbody tr:nth-child(even):hover td, +.table-striped tbody tr:nth-child(even):hover td { + background: rgba(225,225,225,0.01); +} +table.ipaddresses { + border-bottom: 1px solid #ddd; +} + +/* vlans */ +table.vlans tr.odd td { + background: white; +} +table.vlans tr.odd:hover td { + background: rgba(0,0,0,0.005); +} +table.vlans tr.even td { + background: #f9f9f9; +} +table.vlans tr.even:hover td { + background: rgba(0,0,0,0.015); +} +table.vlans tr.success td { + background: rgba(223,240,216,0.35) !important; + text-shadow: 1px 1px 1px white; + color: #aaa; +} +table.vlans tr.change td { + border-top: 1px solid #efefef !important; +} + + +.table.ipaddresses th, +.table.slaves th, +.table-striped th { + font-style: normal; +} +.table.ipaddresses tr.th, +.table.slaves tr.th, +.table-striped tr.th { + color: silver; +/* font-style: italic; */ +} +.usersEdit td:nth-child(1), +#manageRequestEdit th:nth-child(1) { + width: 1px; + white-space: nowrap; +} +.usersEdit th, +.usersEdit td, +.table.ipaddresses td { + vertical-align: middle; + padding: 3px; +} +.table.ipaddresses td.narrow { + width: 1px; +} +.table.ipaddresses th.btn-actions, +.table.ipaddresses td.btn-actions { +/* width: 1px; */ + padding-left: 0px; + padding-right: 3px; + + white-space: nowrap; + text-align: right; +} +.table.ipaddresses tr.offline td.ipaddress { +/* text-decoration: line-through; */ +} +.table.ipaddresses tr.dhcp td { + background: rgba(225,225,225,0.7) !important; + text-shadow: 1px 1px 1px white; +} +.resolved { + color: #777; + font-style: italic; +} + +.ipaddress_subnet th { + text-align: right; + white-space: nowrap; + width: 130px; + padding-left: 30px; + padding-right: 15px; + +/* background: white !important; */ + + vertical-align: top; + + border-bottom: none; + border-right: 1px solid #dddddd; + + font-weight: bold; +} +.ipaddress_subnet th, +.ipaddress_subnet td { + padding: 1px 10px !important; +} +.ipaddress_subnet td { + padding-left: 15px; +} +.ipaddress_subnet td.info { + color: #aaa; +} +.ipaddress_subnet td.actions { + vertical-align: top; + text-align: left; +} +table.ipaddresses tr[class*="hidden-"] { +/* display: none; */ +} +table.ipaddresses td.gateway { + font-weight: bold; +} +table.ipaddresses td.gateway a i.fa-gateway { + margin-left: 5px; + color: #555; +} +a.createfromfree { + padding: 1px 4px; + margin-top: -2px; +} + +div.ip_vis span { + box-shadow: 0px 0px 1px #777; + border-radius:3px; + + border: 1px solid white; + + padding: 3px; + text-align: center; + font-size: 11px; + + margin-right: 8px; + margin-bottom: 8px; + + width: 28px; + height: 22px; + + float: left; +} +div.ip_vis span.modIPaddr { + cursor: pointer; +} +div.ip_vis span[class*="ip-"] { + color: #333; + background: white; +} + +ul.pagination li.active a { + background: #aaa; + border-color: #aaa; +} +ul.pagination li.active:hover a { + background: #888; + border-color: #888; +} +ul.pagination li a { + color: #aaa; +} +ul.pagination li:hover a { + color: #aaa; +} + + +/* IP statuses */ +span.status { + padding: 0px; + margin: 0px; + margin-right: 7px; + margin-top: 2px; + padding: 0px; + + float: left; + width: 14px; + height: 14px; + + border-radius: 8px; + + box-shadow: 0px 0px 1px #666; + border: 1px solid white; +} +span.status-hidden { + display: none; +} +.status-neutral { + background: white; +} +.status-success { + background: #A9C9A4; +} +.status-error { + background: #F59C99; +} +.status-warning { + background: #FFCE57; +} +.status.status-padded { + background: transparent; + border: 0px !important; + box-shadow: none !important; + width: 16px; +} +.status-ip { + float: right !important; +} + + +/* pie graph legend */ +td.legendLabel { + padding-left: 0px; +} +td.legendColorBox { + padding-right: 5px; +} +span#pieLabel0 div { + color: #999 !important; +} + + +.slaves th { +} +.slaves td { + text-align: left; +} +table.slaves tr.sum td, +table.slaves tr.sum th { + background: transparent !important; + border-bottom: 0px solid !important; +} +table.slaves tr.sum th { + border-top: none; +} +table.slaves tr.sumTop th { + border-top: double medium #dddddd; + border-bottom: none; +} +table.slaves th.small, +table.slaves td.small { + white-space: nowrap; + padding-left: 10px; + padding-right: 10px; + width: 10px; + + font-size: 13px; +} +table.slaves th.description, +table.slaves td.description { + padding-right: 20px; + padding-left: 20px; +} +table.ipaddresses tr.success td, +table.slaves tr.success td { +/* background: #dff0d8; */ + background: rgba(223,240,216,0.35) !important; +/* background: #f9f9f9 !important; */ + + text-shadow: 1px 1px 1px white; + color: #aaa; +} + +.editipaddress td:nth-child(1) { + white-space: nowrap; +} +.editIPAddress select { + margin-bottom: 0px; +} +.editIPAddress input[type=text], +.editIPAddress select, +.editIPAddress textarea { + font-size: 13px; +} +.editIPAddress input[type=checkbox], +#popup.popup input[type=checkbox] { +/* margin-top: 0px; */ +} +.editIPAddress .input-group-addon i { + width: 10px; +} +.editIPAddress .input-group-addon i#refreshHostname { + cursor:pointer; +} + +.state { + margin-left: 7px; + cursor: default; +} + +.toolbar-ip { + margin-top: 2px; +} + + + + +/* @tools @admin menu */ +.adminMenu h3, +.toolsMenu h3 { + font-size: 14px; +} +.adminMenu li.active, +.toolsMenu li.active { + background: #F1FAFE +} +.toolsMenu li, +.adminMenu li { + padding: 7px 10px; +} +.adminMenu li.active.list-group-item a, +.toolsMenu li.active.list-group-item a { + font-weight: bold; +} +.favs td:nth-child(1) i { + margin-right: 1px; + padding-right: 5px; + border-right: 1px solid #ccc; +} + + + + + + +/* @tools */ +span.ipreqMenu { + background: #ccc; + border: 1px solid #ddd; + padding: 0px 10px; + + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + + float: right; + margin-top: 0px; + + color: white; +} + +table.ipCalc { + width: auto; +} +table.ipCalc td { + vertical-align: middle; + border: none; +} +table.ipCalcResult td:nth-child(1) { + width: 100px; + white-space: nowrap; + padding-right: 50px; + padding-left: 20px; +} +table.ipCalc td input[type="text"] { + margin-bottom: 0px; +} + +.instructions { + padding: 10px; + background: white; + margin-top: 10px; +} + +table#switchMainTable tbody[id*="content-switch-"] { + display: none; +} +table#switchMainTable tr.switch-title th, +table#vrf tr.vrf-title th, +table#subnets tr.subnets-title th, +table#settings tr.settings-title th { + border-top: none; + padding-top: 20px; + border-bottom-color: silver; +} +table#switchMainTable tr.switch-title th { + border-bottom: 0px; + padding-top: 10px; + padding-bottom: 5px; +} +table#switchMainTable button { + margin-top: -5px; + margin-right: 10px; +} + + +table#hosts tr.same td { + border: none; +} + +table#vlans tr.nochange td { + border: none; +} + +table#userModSelf { + width: auto; +} +table#userModSelf input[type="text"] { + margin-bottom: 0px; +} +table#userModSelf input[type="text"] { + width: 300px; +} +table#userModSelf td { + vertical-align: top; + white-space: nowrap; + padding-left: 10px; + padding-right: 10px; +} + +table.asImport td { + white-space: nowrap; +} + + + + + +/* @sortable items */ +ul.sortable { + list-style: none; + margin-left: 5px; + padding-left: 0px; + + width: auto; +} +ul#sortablePopup li { + border: none; +} +ul.sortable li { + vertical-align: middle; + margin: 5px; + padding: 5px 10px; + background: white; + + border: 1px solid #ddd !important; + border-radius: 3px; + + cursor: move; + + width: auto; +} +ul.sortable li input { + margin-left: 10px; +} +ul.sortable li input[type="checkbox"] { + margin: 0px; + margin-left: 7px; +} +ul.sortable li.alert-success { + background: #dff0d8; +} + + + + + +/* @administration ----------- */ +table#settings td { + vertical-align: middle; +} +table#settings td:nth-child(1) { + white-space: nowrap; + padding-right: 10px; +} +table#settings input[type="text"] { + margin-bottom: 0px; +} +table#settings td.info { + color: gray; + text-shadow: 1px 1px 0px white; +} + +table.userMod td.info { + color: gray; + text-shadow: 1px 1px 0px white; +} +table.userMod td input[type="text"] { + margin-bottom: 0px; +} + +table#manageSubnets { +/* width: auto; */ +} +table#manageSubnets th, +table#manageSubnets td { + padding-right: 0px; + vertical-align: middle; + + padding-top: 6px; + padding-bottom: 6px; +} +table#manageSubnets td[class*="level"] { + padding-left: 10px; +} +table#manageSubnets td.level1 { + padding-left: 5px; +} +table#manageSubnets .structure { + background: url("../../css/images/sn-bg.png") 0px 7px; + padding-top: 8px; + padding-bottom: 8px; + +} +table#manageSubnets .structure i { + +} +table#manageSubnets tbody[id*="content-subnet-"] { +/* display: none; */ +} +table#manageSubnets td:nth-child(5), +table#manageSubnets td:nth-child(6), +table#manageSubnets th:nth-child(5), +table#manageSubnets th:nth-child(6) { + text-align: center; + padding: auto 0px; +} +table#manageSubnets td:nth-child(1), +table#manageSubnets td:nth-child(2), +table#manageSubnets td:last-child() { + white-space: nowrap; +} +table#manageSubnets tr.subnet-title th { + border-top: none; + padding-top: 10px; +} +.table div.alert, +table#manageSubnets div.alert { + margin-bottom: 0px; +} +table.editSubnetDetails td:nth-child(1) { + white-space: nowrap; +} +form#editSubnetDetails { + margin-bottom: 0px; +} +table.editSubnetDetails td.info { + color: gray; + text-shadow: 1px 1px 0px white; +} +table.editSubnetDetails select { + margin-bottom: 0px; +} +table.editSubnetDetails td.hr { + padding-top: 0px; + padding-bottom: 0px; +} +table.editSubnetDetails td.middle { + vertical-align: middle; + white-space: nowrap; +} +table#switchManagement tr.unspecified td { + border-top: double #ddd; +} + +table#manageSection { + width: auto; +} +table#manageSection th, +table#manageSection td { + padding-right: 40px; + vertical-align: top; +} +table#manageSection tr.master td { + border-top: medium double #dddddd; +} +table#manageSection tr.slave td:nth-child(1), +table#manageSection tr.slave td:nth-child(2) { + padding-left: 20px; +} +table#manageSection tr.slave td:nth-child(1):before { + content: " - "; +} + +table#logs span { + border: 1px solid #ccc; + padding: 2px 5px; + margin-right: 5px; + background: white; + + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + + font-size: 10px; +} +table#logs td:nth-child(1) { + white-space: nowrap; +} +table#logs tr.Informational td { + background: #f7fff3; +} +table#logs tr.Informational:hover td { + background: #dbffc8; +} + +td.groups input[type=checkbox] { + margin-top: 0px; + margin-right: 10px; +} +.noborder { + padding: 0px !important; + margin: 0px; +} +.noborder input { + margin: 0px; +} +.dbUpgrade tbody { + border: 0px !important; +} +.upgrade-db pre { + width: 570px; +} + + +/* @tooltip fixes ---------- */ +.tooltip { + white-space: nowrap; + text-shadow: none; + font-size: 11px; +} +.tooltip-inner { + max-width: 500px; +} +.tooltip hr { + height: 1px; + background: #ccc; + border: none; +} + + + + + + + +/* @login ---------- */ +div#login { + + margin: auto; + margin-top: 30px; + + background: white; + width: 500px; + padding: 10px 0px; + +/* + -webkit-box-shadow: 0px 0px 6px silver; + -moz-box-shadow: 0px 0px 6px silver; + box-shadow: 0px 0px 6px silver; +*/ + + border: 1px solid #ddd; + border: 1px solid #DAE3EA; + + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +div#login input[type=text], +div#login input[type=password], +div#login select, +div#login textarea { + margin-bottom: 10px; +} +div#login .login { + width: 100%; +} +#login.request { + width: 650px; +} +#login .loginForm, +#login .requestIP1 { + padding: 0px 20px; +} +div#login .login th { + padding: 0px 8px; + margin: 0px; + vertical-align: middle; + padding-bottom: 10px; + padding-left: 15px; + + white-space: nowrap; + width: 30px; + + vertical-align: middle !important; +} +div#login .login td { + text-align: right; +} +div#loginCheck .alert { + margin: auto; + margin-top: 10px; + width: 500px; +} +.iprequest { + background: #F1FAFE; + + border-top: 1px solid #DAE3EA; + border-bottom: 1px solid #DAE3EA; +/* + border-left: 1px solid #DDD; + border-right: 1px solid #DDD; +*/ + text-align: right; + + margin: 0; + margin-top: 10px; + margin-bottom: 10px; + + padding: 10px; + text-shadow: 1px 1px 0px white; +} +.requestIP { + padding: 10px 20px; +} +table#requestIP td { + border: none !important; +} +.requestIP textarea { + width: 270px; +} +table.requestIP { + width: 100%; +} +table.requestIP th { + vertical-align: top; + text-align: left; + width: 180px; +} +.requestIPresult { + padding:10px; +} +table.changelog td { + vertical-align: top !important; +} +.loginForm .row div { + padding: 0px; +} +.loginForm img { + border-radius: 4px; + border: 1px solid #ccc; +} + +table#userPrint td { + vertical-align: top !important; +} +table#userPrint h4 { + padding-top: 15px; +} + + + + +/* @popup ---------- */ +#popupOverlay { + display: none; + + position: fixed; + + background: rgba(0,0,0,0.5); +/* + width: 100%; + height: 100%; +*/ + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + + z-index: 100; + + overflow-x: hidden; + overflow-y: auto; +} + +.popup_w400 { + width: 400px; + + left: 50%; + margin-left: -200px; + + z-index: 103; +} +.popup_w500 { + width: 500px; + + left: 50%; + margin-left: -250px; + + z-index: 102; +} +.popup_w700, +.popup_wmasks { + width: 700px; + + left: 50%; + margin-left: -350px; + + z-index: 101; +} +.popup_wmasks { + + width: 800px; + margin-left: -400px; + + z-index: 104; +} +.popup_wmasks .pContent { + max-height: 700px !important; +} +.popup_max { + width: 90%; + + left: 50%; + margin-left: -45%; + + z-index: 101; + + height: 90%; + top: 5% !important; +} +.popup .alert { + margin-bottom: 5px; +} + +.popup { + display: none; + + top: 10%; + + position: fixed; + +/* overflow-x: hidden; */ + + background-color: #ffffff; + border: 1px solid #999; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; +} +.popup .pContent::-webkit-scrollbar { + display: none; +} +.popup .pHeader { + padding: 15px; + border-bottom: 1px solid #eee; + + text-rendering: optimizelegibility; + font-size: 16px; + line-height: 20px; + + color: white; +/* text-shadow: 1px 1px 0px black; */ + + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + + background: #585e65; +} +.popup .pContent { + max-height: 450px; + padding: 15px; + padding-bottom: 0px; + overflow-y: auto; +} +.popup .pContent table th, +.popup .pContent table td { + padding-top: 2px; + padding-bottom: 2px; +} +.popup .pContent table.sectionEdit td:nth-child(1) { + white-space: nowrap; +} +.popup .pContent table span.info2 { + margin-left: 7px; +} +.popup .pContent input[type='select'] { + height: 10px; +} +.popup .pFooter { + padding: 4px 14px; + margin-bottom: 0; + text-align: right; + + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + zoom: 1; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; +} +.popup .pFooter .btn { + padding: 4px 10px; +} +.popup .pFooter div { + margin-top: 5px; + text-align: left; +} +.popup .pFooter:before, +.popup .pFooter:after { + display: table; + line-height: 0; + content: ""; +} +.popup .pFooter:after { + clear: both; +} +.popup_max { + overflow: hidden; +} +.popup_max .pContent { + max-height: 92%; + height: 100%; +} +.popup_max .pFooter { + float: left; + width: 100%; + bottom: 0px; +} + + + + + +/* @leftovers from old css ---------- */ + +/* page setup */ +body { + background: #f9f9f9; +/* background: #f2f2f2; */ +} +body.stop-scrolling { + overflow: hidden; +} +div.wrapper { +/* min-height: 100%; */ + height: auto !important; + height: 100%; + margin: 0 auto -50px; +} +div.pusher { + height: 50px; + clear: both; +} +div.footer { + text-align: center; + vertical-align: text-top; + + color: #D5D5D5; + + -webkit-transform: translateZ(0); + + position: fixed; + width: 100%; + + bottom: 0px; + +/* height: 30px; */ + + padding-top: 5px; + padding-bottom: 5px; + + background: rgb(41,51,57); + background: -moz-linear-gradient(top, rgba(41,51,57,1) 0%, rgba(29,36,41,1) 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(41,51,57,1)), color-stop(100%,rgba(29,36,41,1))); + background: -webkit-linear-gradient(top, rgba(41,51,57,1) 0%,rgba(29,36,41,1) 100%); + background: -o-linear-gradient(top, rgba(41,51,57,1) 0%,rgba(29,36,41,1) 100%); + background: -ms-linear-gradient(top, rgba(41,51,57,1) 0%,rgba(29,36,41,1) 100%); + background: linear-gradient(top, rgba(41,51,57,1) 0%,rgba(29,36,41,1) 100%); + z-index: 93; +} +div.footer a { + color: #D5D5D5; +} +div.footer, +div.footer a { + font-weight: 400; +} +table.donate { +/* white-space: nowrap; */ + margin: auto; +} +table.donate td { + color: #D5D5D5; + padding: 0px 10px; + text-shadow: 1px 1px 1px black; + border-left: 1px solid #D5D5D5; +} +table.donate td:nth-child(1) { + border-left: none; +} + + + + + + +/* loading div */ +div.loading { + position: fixed; + color: white; + text-shadow: 1px 1px 1px black; + + display: none; + + left: 47%; + top:200px; + + padding: 10px; + text-align: center; + + background: #494949; + background: -moz-linear-gradient(top, rgba(29,36,41,0.9) 0%, rgba(41,51,57,0.7) 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(29,36,41,0.9)), color-stop(100%,rgba(41,51,57,0.7))); + background: -webkit-linear-gradient(top, rgba(29,36,41,0.9) 0%,rgba(41,51,57,0.7) 100%); + background: -o-linear-gradient(top, rgba(29,36,41,0.9) 0%,rgba(41,51,57,0.7) 100%); + background: -ms-linear-gradient(top, rgba(29,36,41,0.9) 0%,rgba(41,51,57,0.7) 100%); + background: linear-gradient(top, rgba(29,36,41,0.9) 0%,rgba(41,51,57,0.7) 100%); + + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + + border: 1px solid rgba(0,0,0,0.6); + + z-index: 999; +} +div.loading i { + font-size: 24px; + margin-top: 7px; +} + +div.jqueryError { + position: fixed; + width: 100%; + height: 100%; + + background: rgba(0,0,0,0.8); + padding-top: 200px; + text-align: center; + + z-index: 120; + + color: white; + + display: none; + font-weight: bold; +} +div.jqueryError.dieIE { + + background-color: #ccc; + color: white; + z-index: 110; +} +div.jqueryErrorText { + width: 400px; + padding: 10px; + margin: auto; + margin-top: 10px; + font-weight: 300; + display: none; + + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; +} +div.jqueryError a { + clear: both; + margin-top: 10px; +} \ No newline at end of file diff --git a/css/bootstrap/bootstrap-datetimepicker.min.css b/css/bootstrap/bootstrap-datetimepicker.min.css new file mode 100755 index 000000000..09488c7c2 --- /dev/null +++ b/css/bootstrap/bootstrap-datetimepicker.min.css @@ -0,0 +1,22 @@ +/*! + * Datepicker for Bootstrap v3 + * + * Copyright 2012 Stefan Petre + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ + .bootstrap-datetimepicker-widget{top:0;left:0;z-index:3000;width:250px;padding:4px;margin-top:1px;border-radius:4px}.bootstrap-datetimepicker-widget .btn{padding:6px}.bootstrap-datetimepicker-widget:before{position:absolute;top:-7px;left:6px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.bootstrap-datetimepicker-widget:after{position:absolute;top:-6px;left:7px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid white;border-left:6px solid transparent;content:''}.bootstrap-datetimepicker-widget.pull-right:before{right:6px;left:auto}.bootstrap-datetimepicker-widget.pull-right:after{right:7px;left:auto}.bootstrap-datetimepicker-widget>ul{margin:0;list-style-type:none}.bootstrap-datetimepicker-widget .timepicker-hour,.bootstrap-datetimepicker-widget .timepicker-minute,.bootstrap-datetimepicker-widget .timepicker-second{width:100%;font-size:1.2em;font-weight:bold}.bootstrap-datetimepicker-widget table[data-hour-format="12"] .separator{width:4px;padding:0;margin:0}.bootstrap-datetimepicker-widget .datepicker>div{display:none}.bootstrap-datetimepicker-widget .picker-switch{text-align:center}.bootstrap-datetimepicker-widget table{width:100%;margin:0}.bootstrap-datetimepicker-widget td,.bootstrap-datetimepicker-widget th{width:20px;height:20px;text-align:center;border-radius:4px}.bootstrap-datetimepicker-widget td.day:hover,.bootstrap-datetimepicker-widget td.hour:hover,.bootstrap-datetimepicker-widget td.minute:hover,.bootstrap-datetimepicker-widget td.second:hover{cursor:pointer;background:#eee}.bootstrap-datetimepicker-widget td.old,.bootstrap-datetimepicker-widget td.new{color:#999}.bootstrap-datetimepicker-widget td.active,.bootstrap-datetimepicker-widget td.active:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#428bca}.bootstrap-datetimepicker-widget td.disabled,.bootstrap-datetimepicker-widget td.disabled:hover{color:#999;cursor:not-allowed;background:0}.bootstrap-datetimepicker-widget td span{display:block;float:left;width:47px;height:54px;margin:2px;line-height:54px;cursor:pointer;border-radius:4px}.bootstrap-datetimepicker-widget td span:hover{background:#eee}.bootstrap-datetimepicker-widget td span.active{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#428bca}.bootstrap-datetimepicker-widget td span.old{color:#999}.bootstrap-datetimepicker-widget td span.disabled,.bootstrap-datetimepicker-widget td span.disabled:hover{color:#999;cursor:not-allowed;background:0}.bootstrap-datetimepicker-widget th.switch{width:145px}.bootstrap-datetimepicker-widget th.next,.bootstrap-datetimepicker-widget th.prev{font-size:21px}.bootstrap-datetimepicker-widget th.disabled,.bootstrap-datetimepicker-widget th.disabled:hover{color:#999;cursor:not-allowed;background:0}.bootstrap-datetimepicker-widget thead tr:first-child th{cursor:pointer}.bootstrap-datetimepicker-widget thead tr:first-child th:hover{background:#eee}.input-group.date .input-group-addon span{display:block;width:16px;height:16px;cursor:pointer}.bootstrap-datetimepicker-widget.left-oriented:before{right:6px;left:auto}.bootstrap-datetimepicker-widget.left-oriented:after{right:7px;left:auto}.bootstrap-datetimepicker-widget ul.list-unstyled li.in div.timepicker div.timepicker-picker table.table-condensed tbody>tr>td{padding:0!important} + +.bootstrap-datetimepicker-widget td span { + width: auto; + height: auto; + line-height: 1; +} +li.picker-switch.accordion-toggle a { + width: auto; + margin-bottom: 5px; +} +a.btn.btn-xs.btn-default { + width: auto; +} \ No newline at end of file diff --git a/css/bootstrap/bootstrap-switch.min.css b/css/bootstrap/bootstrap-switch.min.css new file mode 100644 index 000000000..6eb3d4d9e --- /dev/null +++ b/css/bootstrap/bootstrap-switch.min.css @@ -0,0 +1,22 @@ +/* ======================================================================== + * bootstrap-switch - v3.3.2 + * http://www.bootstrap-switch.org + * ======================================================================== + * Copyright 2012-2013 Mattia Larentis + * + * ======================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================================== + */ + +.bootstrap-switch{display:inline-block;direction:ltr;cursor:pointer;border-radius:4px;border:1px solid;border-color:#ccc;position:relative;text-align:left;overflow:hidden;line-height:8px;z-index:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle;-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.bootstrap-switch .bootstrap-switch-container{display:inline-block;top:0;border-radius:4px;-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0)}.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-handle-off,.bootstrap-switch .bootstrap-switch-label{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;cursor:pointer;display:inline-block !important;height:100%;padding:6px 12px;font-size:14px;line-height:20px}.bootstrap-switch .bootstrap-switch-handle-on,.bootstrap-switch .bootstrap-switch-handle-off{text-align:center;z-index:1}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary{color:#fff;background:#428bca}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-info,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-info{color:#fff;background:#5bc0de}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success{color:#fff;background:#5cb85c}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-warning,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-warning{background:#f0ad4e;color:#fff}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-danger,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-danger{color:#fff;background:#d9534f}.bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-default,.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default{color:#000;background:#eee}.bootstrap-switch .bootstrap-switch-label{text-align:center;margin-top:-1px;margin-bottom:-1px;z-index:100;color:#333;background:#fff}.bootstrap-switch .bootstrap-switch-handle-on{border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch .bootstrap-switch-handle-off{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch input[type='radio'],.bootstrap-switch input[type='checkbox']{position:absolute !important;top:0;left:0;opacity:0;filter:alpha(opacity=0);z-index:-1}.bootstrap-switch input[type='radio'].form-control,.bootstrap-switch input[type='checkbox'].form-control{height:auto}.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-mini .bootstrap-switch-label{padding:1px 5px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-small .bootstrap-switch-label{padding:5px 10px;font-size:12px;line-height:1.5}.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-large .bootstrap-switch-label{padding:6px 16px;font-size:18px;line-height:1.33}.bootstrap-switch.bootstrap-switch-disabled,.bootstrap-switch.bootstrap-switch-readonly,.bootstrap-switch.bootstrap-switch-indeterminate{cursor:default !important}.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-on,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-handle-off,.bootstrap-switch.bootstrap-switch-disabled .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-readonly .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-indeterminate .bootstrap-switch-label{opacity:.5;filter:alpha(opacity=50);cursor:default !important}.bootstrap-switch.bootstrap-switch-animate .bootstrap-switch-container{-webkit-transition:margin-left .5s;transition:margin-left .5s}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-on{border-bottom-left-radius:0;border-top-left-radius:0;border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-inverse .bootstrap-switch-handle-off{border-bottom-right-radius:0;border-top-right-radius:0;border-bottom-left-radius:3px;border-top-left-radius:3px}.bootstrap-switch.bootstrap-switch-focused{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.bootstrap-switch.bootstrap-switch-on .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-off .bootstrap-switch-label{border-bottom-right-radius:3px;border-top-right-radius:3px}.bootstrap-switch.bootstrap-switch-off .bootstrap-switch-label,.bootstrap-switch.bootstrap-switch-inverse.bootstrap-switch-on .bootstrap-switch-label{border-bottom-left-radius:3px;border-top-left-radius:3px} \ No newline at end of file diff --git a/css/bootstrap/bootstrap.min.css b/css/bootstrap/bootstrap.min.css new file mode 100755 index 000000000..679272d25 --- /dev/null +++ b/css/bootstrap/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.1.1 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*{text-shadow:none!important;color:#000!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#999}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;white-space:nowrap;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:0}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:0}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:0}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:0}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:0}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:0}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:0}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:0}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;float:none;display:table-column}table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date]{line-height:34px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:400;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.has-feedback .form-control-feedback{position:absolute;top:25px;right:0;display:block;width:34px;height:34px;line-height:34px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-control-static{padding-top:7px}@media (min-width:768px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:400;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{float:none;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:gray}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#999;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#faebcc}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#ebccd1}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ebccd1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:scroll;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{margin-top:15px;padding:19px 20px 20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.5) 0),color-stop(rgba(0,0,0,.0001) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.0001) 0),color-stop(rgba(0,0,0,.5) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/css/font-awesome/font-awesome.min.css b/css/font-awesome/font-awesome.min.css new file mode 100644 index 000000000..24fcc04c4 --- /dev/null +++ b/css/font-awesome/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.3.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.3.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.3.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.3.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.3.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0, 0)}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-genderless:before,.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"} \ No newline at end of file diff --git a/css/fonts/FontAwesome.otf b/css/fonts/FontAwesome.otf new file mode 100644 index 000000000..f7936cc1e Binary files /dev/null and b/css/fonts/FontAwesome.otf differ diff --git a/css/fonts/fontawesome-webfont.eot b/css/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..33b2bb800 Binary files /dev/null and b/css/fonts/fontawesome-webfont.eot differ diff --git a/css/fonts/fontawesome-webfont.svg b/css/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..1ee89d436 --- /dev/null +++ b/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/css/fonts/fontawesome-webfont.ttf b/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..ed9372f8e Binary files /dev/null and b/css/fonts/fontawesome-webfont.ttf differ diff --git a/css/fonts/fontawesome-webfont.woff b/css/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..8b280b98f Binary files /dev/null and b/css/fonts/fontawesome-webfont.woff differ diff --git a/css/fonts/fontawesome-webfont.woff2 b/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..3311d5851 Binary files /dev/null and b/css/fonts/fontawesome-webfont.woff2 differ diff --git a/css/fonts/lato/Lato-light.ttf b/css/fonts/lato/Lato-light.ttf new file mode 100644 index 000000000..9b0788e85 Binary files /dev/null and b/css/fonts/lato/Lato-light.ttf differ diff --git a/css/fonts/lato/Lato-regular.ttf b/css/fonts/lato/Lato-regular.ttf new file mode 100644 index 000000000..8db77ea9f Binary files /dev/null and b/css/fonts/lato/Lato-regular.ttf differ diff --git a/css/images/bootstrap-colorpicker/alpha-horizontal.png b/css/images/bootstrap-colorpicker/alpha-horizontal.png new file mode 100644 index 000000000..d0a65c08b Binary files /dev/null and b/css/images/bootstrap-colorpicker/alpha-horizontal.png differ diff --git a/css/images/bootstrap-colorpicker/alpha.png b/css/images/bootstrap-colorpicker/alpha.png new file mode 100644 index 000000000..38043f1c8 Binary files /dev/null and b/css/images/bootstrap-colorpicker/alpha.png differ diff --git a/css/images/bootstrap-colorpicker/hue-horizontal.png b/css/images/bootstrap-colorpicker/hue-horizontal.png new file mode 100644 index 000000000..a0d9add8e Binary files /dev/null and b/css/images/bootstrap-colorpicker/hue-horizontal.png differ diff --git a/css/images/bootstrap-colorpicker/hue.png b/css/images/bootstrap-colorpicker/hue.png new file mode 100644 index 000000000..d89560e99 Binary files /dev/null and b/css/images/bootstrap-colorpicker/hue.png differ diff --git a/css/images/bootstrap-colorpicker/saturation.png b/css/images/bootstrap-colorpicker/saturation.png new file mode 100644 index 000000000..594ae50ed Binary files /dev/null and b/css/images/bootstrap-colorpicker/saturation.png differ diff --git a/css/images/btn_donate_SM.gif b/css/images/btn_donate_SM.gif new file mode 100755 index 000000000..c3d5f8f96 Binary files /dev/null and b/css/images/btn_donate_SM.gif differ diff --git a/css/images/csvuploadexample.jpg b/css/images/csvuploadexample.jpg new file mode 100755 index 000000000..eb0ccb991 Binary files /dev/null and b/css/images/csvuploadexample.jpg differ diff --git a/css/images/favicon.png b/css/images/favicon.png new file mode 100755 index 000000000..48ad909db Binary files /dev/null and b/css/images/favicon.png differ diff --git a/css/images/hosterdam.png b/css/images/hosterdam.png new file mode 100755 index 000000000..66a20f3c5 Binary files /dev/null and b/css/images/hosterdam.png differ diff --git a/css/images/li.png b/css/images/li.png new file mode 100755 index 000000000..189186016 Binary files /dev/null and b/css/images/li.png differ diff --git a/css/images/phpipam-favicon.pxm b/css/images/phpipam-favicon.pxm new file mode 100755 index 000000000..2f30fa663 Binary files /dev/null and b/css/images/phpipam-favicon.pxm differ diff --git a/css/images/sn-bg.png b/css/images/sn-bg.png new file mode 100755 index 000000000..548003368 Binary files /dev/null and b/css/images/sn-bg.png differ diff --git a/css/images/ul-li-bg-active.png b/css/images/ul-li-bg-active.png new file mode 100755 index 000000000..79bfd551f Binary files /dev/null and b/css/images/ul-li-bg-active.png differ diff --git a/css/images/ul-li-bg.png b/css/images/ul-li-bg.png new file mode 100755 index 000000000..6e0668ef6 Binary files /dev/null and b/css/images/ul-li-bg.png differ diff --git a/css/images/userTrooper.png b/css/images/userTrooper.png new file mode 100755 index 000000000..3cb65e567 Binary files /dev/null and b/css/images/userTrooper.png differ diff --git a/css/images/userVader.png b/css/images/userVader.png new file mode 100755 index 000000000..b063018c9 Binary files /dev/null and b/css/images/userVader.png differ diff --git a/db/SCHEMA.sql b/db/SCHEMA.sql new file mode 100755 index 000000000..70462b1b7 --- /dev/null +++ b/db/SCHEMA.sql @@ -0,0 +1,549 @@ +# Dump of table instructions +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `instructions`; + +CREATE TABLE `instructions` ( + `id` int(11) NOT NULL, + `instructions` text, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `instructions` (`id`, `instructions`) +VALUES + (1,'You can write instructions under admin menu!'); + + +# Dump of table ipaddresses +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `ipaddresses`; + +CREATE TABLE `ipaddresses` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `subnetId` INT(11) UNSIGNED NULL DEFAULT NULL, + `ip_addr` varchar(100) NOT NULL, + `is_gateway` TINYINT(1) NULL DEFAULT '0', + `description` varchar(64) DEFAULT NULL, + `dns_name` varchar(100) DEFAULT NULL, + `mac` varchar(20) DEFAULT NULL, + `owner` varchar(32) DEFAULT NULL, + `state` INT(3) NULL DEFAULT '2', + `switch` INT(11) UNSIGNED NULL DEFAULT NULL, + `port` varchar(32) DEFAULT NULL, + `note` text, + `lastSeen` DATETIME NULL DEFAULT '0000-00-00 00:00:00', + `excludePing` BINARY NULL DEFAULT '0', + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `subnetid` (`subnetId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `ipaddresses` (`id`, `subnetId`, `ip_addr`, `description`, `dns_name`, `state`) +VALUES + (1,3,'168427779','Server1','server1.cust1.local',2), + (2,3,'168427780','Server2','server2.cust1.local',2), + (3,3,'168427781','Server3','server3.cust1.local',3), + (4,3,'168427782','Server4','server4.cust1.local',3), + (5,3,'168428021','Gateway',NULL,2), + (6,4,'168428286','Gateway',NULL,2), + (7,4,'168428042','Server1','ser1.client2.local',2), + (8,6,'172037636','DHCP range',NULL,4), + (9,6,'172037637','DHCP range',NULL,4), + (10,6,'172037638','DHCP range',NULL,4); + + +# Dump of table logs +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `logs`; + +CREATE TABLE `logs` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `severity` int(11) DEFAULT NULL, + `date` varchar(32) DEFAULT NULL, + `username` varchar(32) DEFAULT NULL, + `ipaddr` varchar(64) DEFAULT NULL, + `command` varchar(128) DEFAULT '0', + `details` varchar(1024) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +# Dump of table requests +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `requests`; + +CREATE TABLE `requests` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `subnetId` INT(11) UNSIGNED NULL DEFAULT NULL, + `ip_addr` varchar(100) DEFAULT NULL, + `description` varchar(32) DEFAULT NULL, + `dns_name` varchar(32) DEFAULT NULL, + `owner` varchar(32) DEFAULT NULL, + `requester` varchar(128) DEFAULT NULL, + `comment` text, + `processed` binary(1) DEFAULT NULL, + `accepted` binary(1) DEFAULT NULL, + `adminComment` text, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +# Dump of table sections +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `sections`; + +CREATE TABLE `sections` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(128) NOT NULL DEFAULT '', + `description` text, + `masterSection` INT(11) NULL DEFAULT '0', + `permissions` varchar(1024) DEFAULT NULL, + `strictMode` BINARY(1) NOT NULL DEFAULT '0', + `subnetOrdering` VARCHAR(16) NULL DEFAULT NULL, + `order` INT(3) NULL DEFAULT NULL, + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + `showVLAN` BOOL NOT NULL DEFAULT '0', + `showVRF` BOOL NOT NULL DEFAULT '0', + `DNS` VARCHAR(128) NULL DEFAULT NULL, + PRIMARY KEY (`name`), + UNIQUE KEY `id_2` (`id`), + KEY `id` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `sections` (`id`, `name`, `description`, `permissions`) +VALUES + (1,'Customers','Section for customers','{\"3\":\"1\",\"2\":\"2\"}'), + (2,'IPv6','Section for IPv6 addresses','{\"3\":\"1\",\"2\":\"2\"}'); + + +# Dump of table settings +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `settings`; + +CREATE TABLE `settings` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `siteTitle` varchar(64) DEFAULT NULL, + `siteAdminName` varchar(64) DEFAULT NULL, + `siteAdminMail` varchar(64) DEFAULT NULL, + `siteDomain` varchar(32) DEFAULT NULL, + `siteURL` varchar(64) DEFAULT NULL, + `domainAuth` tinyint(1) DEFAULT NULL, + `enableIPrequests` tinyint(1) DEFAULT NULL, + `enableVRF` tinyint(1) DEFAULT '1', + `enableDNSresolving` tinyint(1) DEFAULT NULL, + `version` varchar(5) DEFAULT NULL, + `dbverified` BINARY(1) NOT NULL DEFAULT '0', + `donate` tinyint(1) DEFAULT '0', + `IPfilter` varchar(128) DEFAULT NULL, + `vlanDuplicate` int(1) DEFAULT '0', + `vlanMax` INT(8) NULL DEFAULT '4096', + `subnetOrdering` varchar(16) DEFAULT 'subnet,asc', + `visualLimit` int(2) NOT NULL DEFAULT '0', + `autoSuggestNetwork` TINYINT(1) NOT NULL DEFAULT '0', + `permitUserVlanCreate` TINYINT(1) NOT NULL DEFAULT '0', + `pingStatus` VARCHAR(12) NOT NULL DEFAULT '1800;3600', + `defaultLang` INT(3) NULL DEFAULT NULL, + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + `vcheckDate` DATETIME NULL DEFAULT NULL , + `api` BINARY NOT NULL DEFAULT '0', + `enableChangelog` TINYINT(1) NOT NULL DEFAULT '1', + `scanPingPath` VARCHAR(64) NULL DEFAULT '/bin/ping', + `scanFPingPath` VARCHAR(64) NULL DEFAULT '/bin/fping', + `scanPingType` SET('ping','pear','fping') NOT NULL DEFAULT 'ping', + `scanMaxThreads` INT(4) NULL DEFAULT '128', + `prettyLinks` SET("Yes","No") NOT NULL DEFAULT 'No', + `hiddenCustomFields` VARCHAR(1024) NULL DEFAULT NULL, + `inactivityTimeout` INT(5) NOT NULL DEFAULT '3600', + `authmigrated` TINYINT NOT NULL DEFAULT '0', + `tempShare` TINYINT(1) NULL DEFAULT '0', + `tempAccess` TEXT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `settings` (`id`, `siteTitle`, `siteAdminName`, `siteAdminMail`, `siteDomain`, `siteURL`, `domainAuth`, `enableIPrequests`, `enableVRF`, `enableDNSresolving`, `version`, `donate`, `IPfilter`, `vlanDuplicate`, `subnetOrdering`, `visualLimit`) +VALUES + (1, 'phpipam IP address management', 'Sysadmin', 'admin@domain.local', 'domain.local', 'http://yourpublicurl.com', 0, 0, 0, 0, '1.1', 0, 'mac;owner;state;switch;note', 1, 'subnet,asc', 24); + + +# Dump of table settingsDomain +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `settingsDomain`; + +CREATE TABLE `settingsDomain` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `account_suffix` varchar(256) DEFAULT '@domain.local', + `base_dn` varchar(256) DEFAULT 'CN=Users,CN=Company,DC=domain,DC=local', + `domain_controllers` varchar(256) DEFAULT 'dc1.domain.local;dc2.domain.local', + `use_ssl` tinyint(1) DEFAULT '0', + `use_tls` tinyint(1) DEFAULT '0', + `ad_port` int(5) DEFAULT '389', + `adminUsername` VARCHAR(64) NULL DEFAULT NULL , + `adminPassword` VARCHAR(64) NULL DEFAULT NULL , + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `settingsDomain` (`id`, `account_suffix`, `base_dn`, `domain_controllers`) +VALUES + (1,'@domain.local','CN=Users,CN=Company,DC=domain,DC=local','dc1.domain.local;dc2.domain.local'); + + +# Dump of table settingsMail +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `settingsMail`; + +CREATE TABLE `settingsMail` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `mtype` set('localhost','smtp') NOT NULL DEFAULT 'localhost', + `msecure` SET('none','ssl','tls') NOT NULL DEFAULT 'none', + `mauth` set('yes','no') NOT NULL DEFAULT 'no', + `mserver` varchar(128) DEFAULT NULL, + `mport` int(5) DEFAULT '25', + `muser` varchar(64) DEFAULT NULL, + `mpass` varchar(64) DEFAULT NULL, + `mAdminName` varchar(64) DEFAULT NULL, + `mAdminMail` varchar(64) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `settingsMail` (`id`, `mtype`) +VALUES + (1, 'localhost'); + + +# Dump of table subnets +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `subnets`; + +CREATE TABLE `subnets` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `subnet` VARCHAR(255) NULL DEFAULT NULL, + `mask` VARCHAR(255) NULL DEFAULT NULL, + `sectionId` INT(11) UNSIGNED NULL DEFAULT NULL, + `description` text, + `vrfId` INT(11) UNSIGNED NULL DEFAULT NULL, + `masterSubnetId` INT(11) UNSIGNED NULL DEFAULT NULL, + `allowRequests` tinyint(1) DEFAULT '0', + `vlanId` INT(11) UNSIGNED NULL DEFAULT NULL, + `showName` tinyint(1) DEFAULT '0', + `permissions` varchar(1024) DEFAULT NULL, + `pingSubnet` BOOL NULL DEFAULT '0', + `discoverSubnet` BINARY(1) NULL DEFAULT '0', + `isFolder` BOOL NULL DEFAULT '0', + `isFull` TINYINT(1) NULL DEFAULT '0', + `state` INT(3) NULL DEFAULT '2', + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `subnets` (`id`, `subnet`, `mask`, `sectionId`, `description`, `vrfId`, `masterSubnetId`, `allowRequests`, `vlanId`, `showName`, `permissions`, `isFolder`) +VALUES + (1,'336395549904799703390415618052362076160','64',2,'Private subnet 1',0,'0',1,1,1,'{\"3\":\"1\",\"2\":\"2\"}',0), + (2,'168427520','16','1','Business customers',0,'0',1,0,1,'{\"3\":\"1\",\"2\":\"2\"}',0), + (3,'168427776','24','1','Customer 1',0,'2',1,0,1,'{\"3\":\"1\",\"2\":\"2\"}',0), + (4,'168428032','24','1','Customer 2',0,'2',1,0,1,'{\"3\":\"1\",\"2\":\"2\"}',0), + (5, '0', '', 1, 'My folder', 0, 0, 0, 0, 0, '{\"3\":\"1\",\"2\":\"2\"}', 1), + (6, '172037632', '24', 1, 'DHCP range', 0, 5, 0, 0, 1, '{\"3\":\"1\",\"2\":\"2\"}', 0); + + +# Dump of table devices +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `devices`; + +CREATE TABLE `devices` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `hostname` varchar(32) DEFAULT NULL, + `ip_addr` varchar(100) DEFAULT NULL, + `type` int(2) DEFAULT '0', + `vendor` varchar(156) DEFAULT NULL, + `model` varchar(124) DEFAULT NULL, + `description` varchar(256) DEFAULT NULL, + `sections` varchar(128) DEFAULT NULL, + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `hostname` (`hostname`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `devices` (`id`, `hostname`, `ip_addr`, `type`, `vendor`, `model`, `sections`) +VALUES + (1,'CoreSwitch','10.10.10.254',0,'Cisco','c6500','1;2;3'), + (2,'Wifi-1','10.10.20.245',4,'Cisco','','1'); + + +# Dump of table userGroups +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `userGroups`; + +CREATE TABLE `userGroups` ( + `g_id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `g_name` varchar(32) DEFAULT NULL, + `g_desc` varchar(1024) DEFAULT NULL, + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`g_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `userGroups` (`g_id`, `g_name`, `g_desc`) +VALUES + (2,'Operators','default Operator group'), + (3,'Guests','default Guest group (viewers)'); + + +# Dump of table users +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `users`; + +CREATE TABLE `users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `username` varchar(20) CHARACTER SET utf8 NOT NULL DEFAULT '', + `authMethod` INT(2) NULL DEFAULT 1, + `password` CHAR(128) COLLATE utf8_bin DEFAULT NULL, + `groups` varchar(1024) COLLATE utf8_bin DEFAULT NULL, + `role` text CHARACTER SET utf8, + `real_name` varchar(128) CHARACTER SET utf8 DEFAULT NULL, + `email` varchar(64) CHARACTER SET utf8 DEFAULT NULL, + `domainUser` binary(1) DEFAULT '0', + `widgets` VARCHAR(1024) NULL DEFAULT 'statistics;favourite_subnets;changelog;top10_hosts_v4', + `lang` INT(11) UNSIGNED NULL DEFAULT '1', + `favourite_subnets` VARCHAR(1024) NULL DEFAULT NULL, + `mailNotify` SET('Yes','No') NULL DEFAULT 'No', + `mailChangelog` SET('Yes','No') NULL DEFAULT 'No', + `passChange` SET('Yes','No') NOT NULL DEFAULT 'No', + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + `lastLogin` TIMESTAMP NULL, + `lastActivity` TIMESTAMP NULL, + `compressOverride` SET('default','Uncompress') NOT NULL DEFAULT 'default', + `hideFreeRange` tinyint(1) DEFAULT '0', + `printLimit` int(4) unsigned DEFAULT '30', + PRIMARY KEY (`username`), + UNIQUE KEY `id_2` (`id`), + KEY `id` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; +/* insert default values */ +INSERT INTO `users` (`id`, `username`, `password`, `groups`, `role`, `real_name`, `email`, `domainUser`,`widgets`, `passChange`) +VALUES + (1,'Admin',X'243624726F756E64733D33303030244A51454536644C394E70766A6546733424524B3558336F6132382E557A742F6835564166647273766C56652E3748675155594B4D58544A5573756438646D5766507A5A51506252626B38784A6E314B797974342E64576D346E4A4959684156326D624F5A33672E',X'','Administrator','phpIPAM Admin','admin@domain.local',X'30','statistics;favourite_subnets;changelog;access_logs;error_logs;top10_hosts_v4', 'Yes'); + + +# Dump of table lang +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `lang`; + +CREATE TABLE `lang` ( + `l_id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `l_code` varchar(12) NOT NULL DEFAULT '', + `l_name` varchar(32) DEFAULT NULL, + PRIMARY KEY (`l_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `lang` (`l_id`, `l_code`, `l_name`) +VALUES + (1, 'en', 'English'), + (2, 'sl_SI', 'Slovenščina'), + (3, 'fr_FR', 'Français'), + (4, 'nl_NL','Nederlands'), + (5, 'de_DE','Deutsch'), + (6, 'pt_BR', 'Brazil'); + + +# Dump of table vlans +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `vlans`; + +CREATE TABLE `vlans` ( + `vlanId` int(11) NOT NULL AUTO_INCREMENT, + `domainId` INT NOT NULL DEFAULT '1', + `name` varchar(255) NOT NULL, + `number` int(4) DEFAULT NULL, + `description` text, + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`vlanId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `vlans` (`vlanId`, `name`, `number`, `description`) +VALUES + (1,'IPv6 private 1',2001,'IPv6 private 1 subnets'), + (2,'Servers DMZ',4101,'DMZ public'); + + +# Dump of table vlanDomains +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `vlanDomains`; + +CREATE TABLE `vlanDomains` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(64) DEFAULT NULL, + `description` text, + `permissions` varchar(128) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `vlanDomains` (`id`, `name`, `description`, `permissions`) +VALUES + (1, 'default', 'default L2 domain', NULL); + + +# Dump of table vrf +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `vrf`; + +CREATE TABLE `vrf` ( + `vrfId` int(11) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(32) NOT NULL DEFAULT '', + `rd` varchar(32) DEFAULT NULL, + `description` varchar(256) DEFAULT NULL, + `editDate` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`vrfId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +# Dump of table api +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `api`; + +CREATE TABLE `api` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `app_id` varchar(32) NOT NULL DEFAULT '', + `app_code` varchar(32) NULL DEFAULT '', + `app_permissions` int(1) DEFAULT '1', + `app_comment` TEXT NULL, + `app_security` SET('crypt','ssl','none') NOT NULL DEFAULT 'ssl', + PRIMARY KEY (`id`), + UNIQUE KEY `app_id` (`app_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +# Dump of table changelog +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `changelog`; + +CREATE TABLE `changelog` ( + `cid` int(11) unsigned NOT NULL AUTO_INCREMENT, + `ctype` set('ip_addr','subnet','section') NOT NULL DEFAULT '', + `coid` int(11) unsigned NOT NULL, + `cuser` int(11) unsigned NOT NULL, + `caction` set('add','edit','delete','truncate','resize','perm_change') NOT NULL DEFAULT 'edit', + `cresult` set('error','success') NOT NULL DEFAULT '', + `cdate` datetime NOT NULL, + `cdiff` varchar(2048) DEFAULT NULL, + PRIMARY KEY (`cid`), + KEY `coid` (`coid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +# Dump of table widgets +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `widgets`; + +CREATE TABLE `widgets` ( + `wid` int(11) unsigned NOT NULL AUTO_INCREMENT, + `wtitle` varchar(64) NOT NULL DEFAULT '', + `wdescription` varchar(1024) DEFAULT NULL, + `wfile` varchar(64) NOT NULL DEFAULT '', + `wparams` varchar(1024) DEFAULT NULL, + `whref` set('yes','no') NOT NULL DEFAULT 'no', + `wsize` SET('4','6','8','12') NOT NULL DEFAULT '6', + `wadminonly` set('yes','no') NOT NULL DEFAULT 'no', + `wactive` set('yes','no') NOT NULL DEFAULT 'no', + PRIMARY KEY (`wid`) +) DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `widgets` (`wid`, `wtitle`, `wdescription`, `wfile`, `wparams`, `whref`, `wsize`, `wadminonly`, `wactive`) +VALUES + (1, 'Statistics', 'Shows some statistics on number of hosts, subnets', 'statistics', NULL, 'no', '4', 'no', 'yes'), + (2, 'Favourite subnets', 'Shows 5 favourite subnets', 'favourite_subnets', NULL, 'yes', '8', 'no', 'yes'), + (3, 'Top 10 IPv4 subnets by number of hosts', 'Shows graph of top 10 IPv4 subnets by number of hosts', 'top10_hosts_v4', NULL, 'yes', '6', 'no', 'yes'), + (4, 'Top 10 IPv6 subnets by number of hosts', 'Shows graph of top 10 IPv6 subnets by number of hosts', 'top10_hosts_v6', NULL, 'yes', '6', 'no', 'yes'), + (5, 'Top 10 IPv4 subnets by usage percentage', 'Shows graph of top 10 IPv4 subnets by usage percentage', 'top10_percentage', NULL, 'yes', '6', 'no', 'yes'), + (6, 'Last 5 change log entries', 'Shows last 5 change log entries', 'changelog', NULL, 'yes', '12', 'no', 'yes'), + (7, 'Active IP addresses requests', 'Shows list of active IP address request', 'requests', NULL, 'yes', '6', 'yes', 'yes'), + (8, 'Last 5 informational logs', 'Shows list of last 5 informational logs', 'access_logs', NULL, 'yes', '6', 'yes', 'yes'), + (9, 'Last 5 warning / error logs', 'Shows list of last 5 warning and error logs', 'error_logs', NULL, 'yes', '6', 'yes', 'yes'), + (10,'Tools menu', 'Shows quick access to tools menu', 'tools', NULL, 'yes', '6', 'no', 'yes'), + (11,'IP Calculator', 'Shows IP calculator as widget', 'ipcalc', NULL, 'yes', '6', 'no', 'yes'); + + +# Dump of table deviceTypes +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `deviceTypes`; + +CREATE TABLE `deviceTypes` ( + `tid` int(11) unsigned NOT NULL AUTO_INCREMENT, + `tname` varchar(128) DEFAULT NULL, + `tdescription` varchar(128) DEFAULT NULL, + PRIMARY KEY (`tid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `deviceTypes` (`tid`, `tname`, `tdescription`) +VALUES + (1, 'Switch', 'Switch'), + (2, 'Router', 'Router'), + (3, 'Firewall', 'Firewall'), + (4, 'Hub', 'Hub'), + (5, 'Wireless', 'Wireless'), + (6, 'Database', 'Database'), + (7, 'Workstation', 'Workstation'), + (8, 'Laptop', 'Laptop'), + (9, 'Other', 'Other'); + + +# Dump of table loginAttempts +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `loginAttempts`; + +CREATE TABLE `loginAttempts` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `ip` varchar(128) NOT NULL DEFAULT '', + `count` int(2) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ip` (`ip`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +# Dump of table usersAuthMethod +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `usersAuthMethod`; + +CREATE TABLE `usersAuthMethod` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `type` set('local','AD','LDAP', 'Radius') NOT NULL DEFAULT 'local', + `params` varchar(1024) DEFAULT NULL, + `protected` set('Yes','No') NOT NULL DEFAULT 'Yes', + `description` text, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `usersAuthMethod` (`id`, `type`, `params`, `protected`, `description`) +VALUES + (1, 'local', NULL, 'Yes', 'Local database'); + + +# Dump of table usersAuthMethod +# ------------------------------------------------------------ +DROP TABLE IF EXISTS `ipTags`; + +CREATE TABLE `ipTags` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(32) DEFAULT NULL, + `showtag` tinyint(4) DEFAULT '1', + `bgcolor` varchar(7) DEFAULT '#000', + `fgcolor` varchar(7) DEFAULT '#fff', + `compress` SET('No','Yes') NOT NULL DEFAULT 'No', + `locked` set('No','Yes') NOT NULL DEFAULT 'No', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `ipTags` (`id`, `type`, `showtag`, `bgcolor`, `fgcolor`, `compress`, `locked`) +VALUES + (1, 'Offline', 1, '#f59c99', '#ffffff', 'No', 'Yes'), + (2, 'Used', 0, '#a9c9a4', '#ffffff', 'No', 'Yes'), + (3, 'Reserved', 1, '#9ac0cd', '#ffffff', 'No', 'Yes'), + (4, 'DHCP', 1, '#c9c9c9', '#ffffff', 'Yes', 'Yes'); + + +# Dump of table -- for autofix comment, leave as it is +# ------------------------------------------------------------ + + +# update version +# ------------------------------------------------------------ +UPDATE `settings` set `version` = '1.16'; \ No newline at end of file diff --git a/db/UPDATE-v1.11.sql b/db/UPDATE-v1.11.sql new file mode 100644 index 000000000..f8e4d1aaf --- /dev/null +++ b/db/UPDATE-v1.11.sql @@ -0,0 +1,68 @@ +/* Update from v 1.1 to 1.11 */ +UPDATE `settings` set `version` = '1.11'; + +/* reset db check field */ +UPDATE `settings` set `dbverified` = 0; + + +/* set flag if auth was migrated to new database */ +ALTER TABLE `settings` ADD `authmigrated` TINYINT NOT NULL DEFAULT '0'; +/* add userAuthMethod table */ +CREATE TABLE `usersAuthMethod` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `type` set('local','AD','LDAP') NOT NULL DEFAULT 'local', + `params` varchar(1024) DEFAULT NULL, + `protected` set('Yes','No') NOT NULL DEFAULT 'Yes', + `description` text, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/* insert default - local */ +INSERT INTO `usersAuthMethod` (`id`, `type`, `params`, `protected`, `description`) +VALUES + (1, 'local', NULL, 'Yes', 'Local database'); +/* Add authMethod field */ +ALTER TABLE `users` ADD `authMethod` INT(2) NULL DEFAULT 1 AFTER `username`; +/* update all domain users to use domain auth, settings will be migrated after first successfull login */ +UPDATE `users` set `authMethod`=2 where `domainUser` = 1; + + +/* add ping types */ +ALTER TABLE `settings` ADD `scanFPingPath` VARCHAR(64) NULL DEFAULT '/bin/fping' AFTER `scanPingPath`; +ALTER TABLE `settings` ADD `scanPingType` SET('ping','pear','fping') NOT NULL DEFAULT 'ping' AFTER `scanFPingPath`; + + +/* vlanDomains */ +CREATE TABLE `vlanDomains` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(64) DEFAULT NULL, + `description` text, + `permissions` varchar(128) DEFAULT NULL, + PRIMARY KEY (`id`) +) DEFAULT CHARSET=utf8; +/* insert default domain */ +INSERT INTO `vlanDomains` (`id`, `name`, `description`, `permissions`) +VALUES + (1, 'default', 'default L2 domain', NULL); +/* add domainId to vlans */ +ALTER TABLE `vlans` ADD `domainId` INT NOT NULL DEFAULT '1' AFTER `vlanId`; + + +/* add last login for users */ +ALTER TABLE `users` ADD `lastLogin` TIMESTAMP NULL AFTER `editDate`; +ALTER TABLE `users` ADD `lastActivity` TIMESTAMP NULL AFTER `lastLogin`; + +/* permit null dns_name */ +ALTER TABLE `ipaddresses` CHANGE `dns_name` `dns_name` VARCHAR(100) CHARACTER SET utf8 NULL DEFAULT NULL; + +/* set ip addresses to null */ +UPDATE `ipaddresses` set `dns_name` = NULL where `dns_name` = ""; +UPDATE `ipaddresses` set `description` = NULL where `description` = ""; +UPDATE `ipaddresses` set `mac` = NULL where `mac` = ""; +UPDATE `ipaddresses` set `owner` = NULL where `owner` = ""; +UPDATE `ipaddresses` set `port` = NULL where `port` = ""; +UPDATE `ipaddresses` set `note` = NULL where `note` = ""; + +/* permit null description and subnet and mask */ +ALTER TABLE `subnets` CHANGE `description` `description` text DEFAULT NULL; +ALTER TABLE `subnets` CHANGE `subnet` `subnet` VARCHAR(255) NULL DEFAULT NULL; +ALTER TABLE `subnets` CHANGE `mask` `mask` VARCHAR(255) NULL DEFAULT NULL; \ No newline at end of file diff --git a/db/UPDATE-v1.12.sql b/db/UPDATE-v1.12.sql new file mode 100644 index 000000000..951ecd441 --- /dev/null +++ b/db/UPDATE-v1.12.sql @@ -0,0 +1,29 @@ +/* Update from v 1.11 to 1.12 */ +UPDATE `settings` set `version` = '1.12'; + +/* reset db check field */ +UPDATE `settings` set `dbverified` = 0; + +/* add gateway field to database */ +ALTER TABLE `ipaddresses` ADD `is_gateway` TINYINT(1) NULL DEFAULT '0' AFTER `ip_addr`; + +/* change tag */ +ALTER TABLE `ipaddresses` CHANGE `state` `state` INT(3) NULL DEFAULT '1'; + +/* ip types */ +CREATE TABLE `ipTags` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(32) DEFAULT NULL, + `showtag` tinyint(4) DEFAULT '1', + `bgcolor` varchar(7) DEFAULT '#000', + `fgcolor` varchar(7) DEFAULT '#fff', + `locked` set('No','Yes') NOT NULL DEFAULT 'No', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +/* insert default values */ +INSERT INTO `ipTags` (`id`, `type`, `showtag`, `bgcolor`, `fgcolor`, `locked`) +VALUES + (0, 'Offline', 1, '#f59c99', '#ffffff', 'Yes'), + (1, 'Used', 0, '#a9c9a4', '#ffffff', 'Yes'), + (2, 'Reserved', 1, '#9ac0cd', '#ffffff', 'Yes'), + (3, 'DHCP', 1, '#c9c9c9', '#ffffff', 'Yes'); diff --git a/db/UPDATE-v1.13.sql b/db/UPDATE-v1.13.sql new file mode 100644 index 000000000..4307501fd --- /dev/null +++ b/db/UPDATE-v1.13.sql @@ -0,0 +1,11 @@ +/* Update version */ +UPDATE `settings` set `version` = '1.13'; + +/* reset db check field */ +UPDATE `settings` set `dbverified` = 0; + +/* add radius auth */ +ALTER TABLE `usersAuthMethod` CHANGE `type` `type` SET('local','AD','LDAP','Radius') CHARACTER SET utf8 NOT NULL DEFAULT 'local'; + +/* add temp access */ +ALTER TABLE `settings` ADD `tempAccess` TEXT NULL AFTER `authmigrated`; \ No newline at end of file diff --git a/db/UPDATE-v1.14.sql b/db/UPDATE-v1.14.sql new file mode 100644 index 000000000..314af2833 --- /dev/null +++ b/db/UPDATE-v1.14.sql @@ -0,0 +1,18 @@ +/* Update version */ +UPDATE `settings` set `version` = '1.14'; + +/* reset db check field */ +UPDATE `settings` set `dbverified` = 0; + +/* add tempShare */ +ALTER TABLE `settings` ADD `tempShare` TINYINT(1) NULL DEFAULT '0' AFTER `authmigrated`; + +/* move display Settings to user */ +ALTER TABLE `users` ADD `dhcpCompress` BOOL NOT NULL DEFAULT '0' AFTER `lastActivity`; +ALTER TABLE `users` ADD `hideFreeRange` tinyint(1) DEFAULT '0' AFTER `dhcpCompress`; +ALTER TABLE `users` ADD `printLimit` int(4) unsigned DEFAULT '30' AFTER `hideFreeRange`; + +/* drop old display settings */ +ALTER TABLE `settings` DROP `dhcpCompress`; +ALTER TABLE `settings` DROP `hideFreeRange`; +ALTER TABLE `settings` DROP `printLimit`; \ No newline at end of file diff --git a/db/UPDATE-v1.15.sql b/db/UPDATE-v1.15.sql new file mode 100644 index 000000000..5be8d8a98 --- /dev/null +++ b/db/UPDATE-v1.15.sql @@ -0,0 +1,48 @@ +/* Update version */ +UPDATE `settings` set `version` = '1.15'; +/* reset db check field */ +UPDATE `settings` set `dbverified` = 0; + +/* reset iptags */ +DROP TABLE IF EXISTS `ipTags`; + +CREATE TABLE `ipTags` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(32) DEFAULT NULL, + `showtag` tinyint(4) DEFAULT '1', + `bgcolor` varchar(7) DEFAULT '#000', + `fgcolor` varchar(7) DEFAULT '#fff', + `locked` set('No','Yes') NOT NULL DEFAULT 'No', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +/* insert default values */ +INSERT INTO `ipTags` (`id`, `type`, `showtag`, `bgcolor`, `fgcolor`, `locked`) +VALUES + (1, 'Offline', 1, '#f59c99', '#ffffff', 'Yes'), + (2, 'Used', 0, '#a9c9a4', '#ffffff', 'Yes'), + (3, 'Reserved', 1, '#9ac0cd', '#ffffff', 'Yes'), + (4, 'DHCP', 1, '#c9c9c9', '#ffffff', 'Yes'); + +/* update ipaddresses */ +UPDATE `ipaddresses` SET `state` = 1 WHERE `state` > 3; +UPDATE `ipaddresses` SET `state` = 4 WHERE `state` = 3; +UPDATE `ipaddresses` SET `state` = 3 WHERE `state` = 2; +UPDATE `ipaddresses` SET `state` = 2 WHERE `state` = 1; +UPDATE `ipaddresses` SET `state` = 1 WHERE `state` = 0; + +/* change tag */ +ALTER TABLE `ipaddresses` CHANGE `state` `state` INT(3) NULL DEFAULT '2'; + +/* add autoSuggestNetwork flag and permitRWAvlan */ +ALTER TABLE `settings` ADD `autoSuggestNetwork` TINYINT(1) NOT NULL DEFAULT '0' AFTER `visualLimit`; +ALTER TABLE `settings` ADD `permitUserVlanCreate` TINYINT(1) NOT NULL DEFAULT '0' AFTER `autoSuggestNetwork`; + +/* add section DNS */ +ALTER TABLE `sections` ADD `DNS` VARCHAR(128) NULL DEFAULT NULL AFTER `showVRF`; + +/* mark subnet as full */ +ALTER TABLE `subnets` ADD `isFull` TINYINT(1) NULL DEFAULT '0' AFTER `isFolder`; + +/* add state */ +ALTER TABLE `subnets` ADD `state` INT(3) NULL DEFAULT '2' AFTER `isFull`; \ No newline at end of file diff --git a/db/UPDATE-v1.16.sql b/db/UPDATE-v1.16.sql new file mode 100644 index 000000000..d7f04089c --- /dev/null +++ b/db/UPDATE-v1.16.sql @@ -0,0 +1,48 @@ +/* Update version */ +UPDATE `settings` set `version` = '1.16'; + +/* reset db check field */ +UPDATE `settings` set `dbverified` = 0; + +/* add compress tag for ranges */ +ALTER TABLE `ipTags` ADD `compress` SET('No','Yes') NOT NULL DEFAULT 'No' AFTER `locked`; +UPDATE `ipTags` SET `compress` = 'Yes' WHERE `id` = '4'; + +/* dhcp compress */ +ALTER TABLE `users` CHANGE `dhcpCompress` `compressOverride` SET('default','Uncompress') NOT NULL DEFAULT 'default'; +UPDATE `users` set `compressOverride` = 'default'; + +/* convert all tables to innodb */ +ALTER TABLE `api` ENGINE = InnoDB; +ALTER TABLE `changelog` ENGINE = InnoDB; +ALTER TABLE `deviceTypes` ENGINE = InnoDB; +ALTER TABLE `devices` ENGINE = InnoDB; +ALTER TABLE `instructions` ENGINE = InnoDB; +ALTER TABLE `ipTags` ENGINE = InnoDB; +ALTER TABLE `ipaddresses` ENGINE = InnoDB; +ALTER TABLE `lang` ENGINE = InnoDB; +ALTER TABLE `loginAttempts` ENGINE = InnoDB; +ALTER TABLE `logs` ENGINE = InnoDB; +ALTER TABLE `requests` ENGINE = InnoDB; +ALTER TABLE `sections` ENGINE = InnoDB; +ALTER TABLE `settings` ENGINE = InnoDB; +ALTER TABLE `settingsDomain` ENGINE = InnoDB; +ALTER TABLE `settingsMail` ENGINE = InnoDB; +ALTER TABLE `subnets` ENGINE = InnoDB; +ALTER TABLE `userGroups` ENGINE = InnoDB; +ALTER TABLE `users` ENGINE = InnoDB; +ALTER TABLE `usersAuthMethod` ENGINE = InnoDB; +ALTER TABLE `vlanDomains` ENGINE = InnoDB; +ALTER TABLE `vlans` ENGINE = InnoDB; +ALTER TABLE `vrf` ENGINE = InnoDB; +ALTER TABLE `widgets` ENGINE = InnoDB; + +/* add new widgets */ +INSERT INTO `widgets` (`wtitle`, `wdescription`, `wfile`, `wparams`, `whref`, `wsize`, `wadminonly`, `wactive`) +VALUES + ('Tools menu', 'Shows quick access to tools menu', 'tools', NULL, 'yes', '6', 'no', 'yes'), + ('IP Calculator', 'Shows IP calculator as widget', 'ipcalc', NULL, 'yes', '6', 'no', 'yes'); + +/* add security type and permit empty app code */ +ALTER TABLE `api` ADD `app_security` SET('crypt','ssl','none') NOT NULL DEFAULT 'ssl' AFTER `app_comment`; +ALTER TABLE `api` CHANGE `app_code` `app_code` VARCHAR(32) NULL DEFAULT ''; \ No newline at end of file diff --git a/db/UPDATE-v1.17.sql b/db/UPDATE-v1.17.sql new file mode 100644 index 000000000..9a37abda2 --- /dev/null +++ b/db/UPDATE-v1.17.sql @@ -0,0 +1,16 @@ +/* Update version */ +UPDATE `settings` set `version` = '1.17'; + +/* reset db check field */ +UPDATE `settings` set `dbverified` = 0; + + + +/* drop table domainsettings */ +settingsDomain + +/* drop settings.domainAuth */ +domainAuth + +/* drop users.domainUser */ +domainUser \ No newline at end of file diff --git a/db/bkp/.htaccess b/db/bkp/.htaccess new file mode 100755 index 000000000..ede920ebe --- /dev/null +++ b/db/bkp/.htaccess @@ -0,0 +1,4 @@ + + Order Allow,Deny + Deny from all + \ No newline at end of file diff --git a/functions/PEAR/Net/IPv4.php b/functions/PEAR/Net/IPv4.php new file mode 100755 index 000000000..c6815cd6a --- /dev/null +++ b/functions/PEAR/Net/IPv4.php @@ -0,0 +1,470 @@ + +* @author Marco Kaiser +* @author Florian Anderiasch +* @copyright 1997-2005 The PHP Group +* @license http://www.php.net/license/3_01.txt PHP License 3.01 +* @version CVS: $Id: IPv4.php 302879 2010-08-30 06:52:41Z bate $ +* @link http://pear.php.net/package/Net_IPv4 +*/ + +require_once 'PEAR.php'; + +// {{{ GLOBALS +/** + * Map of bitmasks to subnets + * + * This array contains every valid netmask. The index of the dot quad + * netmask value is the corresponding CIDR notation (bitmask). + * + * @global array $GLOBALS['Net_IPv4_Netmask_Map'] + */ +$GLOBALS['Net_IPv4_Netmask_Map'] = array( + 0 => "0.0.0.0", + 1 => "128.0.0.0", + 2 => "192.0.0.0", + 3 => "224.0.0.0", + 4 => "240.0.0.0", + 5 => "248.0.0.0", + 6 => "252.0.0.0", + 7 => "254.0.0.0", + 8 => "255.0.0.0", + 9 => "255.128.0.0", + 10 => "255.192.0.0", + 11 => "255.224.0.0", + 12 => "255.240.0.0", + 13 => "255.248.0.0", + 14 => "255.252.0.0", + 15 => "255.254.0.0", + 16 => "255.255.0.0", + 17 => "255.255.128.0", + 18 => "255.255.192.0", + 19 => "255.255.224.0", + 20 => "255.255.240.0", + 21 => "255.255.248.0", + 22 => "255.255.252.0", + 23 => "255.255.254.0", + 24 => "255.255.255.0", + 25 => "255.255.255.128", + 26 => "255.255.255.192", + 27 => "255.255.255.224", + 28 => "255.255.255.240", + 29 => "255.255.255.248", + 30 => "255.255.255.252", + 31 => "255.255.255.254", + 32 => "255.255.255.255" + ); +// }}} +// {{{ Net_IPv4 + +/** +* Class to provide IPv4 calculations +* +* Provides methods for validating IP addresses, calculating netmasks, +* broadcast addresses, network addresses, conversion routines, etc. +* +* @category Net +* @package Net_IPv4 +* @author Eric Kilfoil +* @author Marco Kaiser +* @author Florian Anderiasch +* @copyright 1997-2005 The PHP Group +* @license http://www.php.net/license/3_01.txt PHP License 3.01 +* @version CVS: @package_version@ +* @link http://pear.php.net/package/Net_IPv4 +* @access public +*/ +class Net_IPv4 +{ + // {{{ properties + var $ip = ""; + var $bitmask = false; + var $netmask = ""; + var $network = ""; + var $broadcast = ""; + var $long = 0; + + //pear + public $pear; + + + //initialize PEAR object on init + public function __construct () { + $this->pear = new PEAR (); + } + + // }}} + // {{{ validateIP() + + /** + * Validate the syntax of the given IP adress + * + * Using the PHP long2ip() and ip2long() functions, convert the IP + * address from a string to a long and back. If the original still + * matches the converted IP address, it's a valid address. This + * function does not allow for IP addresses to be formatted as long + * integers. + * + * @param string $ip IP address in the format x.x.x.x + * @return bool true if syntax is valid, otherwise false + */ + function validateIP($ip) + { + if ($ip == long2ip(ip2long($ip))) { + return true; + } else { + return false; + } + } + + // }}} + // {{{ check_ip() + + /** + * Validate the syntax of the given IP address (compatibility) + * + * This function is identical to Net_IPv4::validateIP(). It is included + * merely for compatibility reasons. + * + * @param string $ip IP address + * @return bool true if syntax is valid, otherwise false + */ + function check_ip($ip) + { + return $this->validateIP($ip); + } + + // }}} + // {{{ validateNetmask() + + /** + * Validate the syntax of a four octet netmask + * + * There are 33 valid netmask values. This function will compare the + * string passed as $netmask to the predefined 33 values and return + * true or false. This is most likely much faster than performing the + * calculation to determine the validity of the netmask. + * + * @param string $netmask Netmask + * @return bool true if syntax is valid, otherwise false + */ + function validateNetmask($netmask) + { + if (! in_array($netmask, $GLOBALS['Net_IPv4_Netmask_Map'])) { + return false; + } + return true; + } + + // }}} + // {{{ parseAddress() + + /** + * Parse a formatted IP address + * + * Given a network qualified IP address, attempt to parse out the parts + * and calculate qualities of the address. + * + * The following formats are possible: + * + * [dot quad ip]/[ bitmask ] + * [dot quad ip]/[ dot quad netmask ] + * [dot quad ip]/[ hex string netmask ] + * + * The first would be [IP Address]/[BitMask]: + * 192.168.0.0/16 + * + * The second would be [IP Address] [Subnet Mask in dot quad notation]: + * 192.168.0.0/255.255.0.0 + * + * The third would be [IP Address] [Subnet Mask as Hex string] + * 192.168.0.0/ffff0000 + * + * Usage: + * + * $cidr = '192.168.0.50/16'; + * $net = Net_IPv4::parseAddress($cidr); + * echo $net->network; // 192.168.0.0 + * echo $net->ip; // 192.168.0.50 + * echo $net->broadcast; // 192.168.255.255 + * echo $net->bitmask; // 16 + * echo $net->long; // 3232235520 (long/double version of 192.168.0.50) + * echo $net->netmask; // 255.255.0.0 + * + * @param string $ip IP address netmask combination + * @return object true if syntax is valid, otherwise false + */ + function parseAddress($address) + { + $myself = new Net_IPv4; + + if (strchr($address, "/")) { + $parts = explode("/", $address); + if (! $myself->validateIP($parts[0])) { + return $this->pear->raiseError("invalid IP address"); + } + $myself->ip = $parts[0]; + + // Check the style of netmask that was entered + /* + * a hexadecimal string was entered + */ + if (preg_match("/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i", $parts[1], $regs)) { + // hexadecimal string + $myself->netmask = hexdec($regs[1]) . "." . hexdec($regs[2]) . "." . + hexdec($regs[3]) . "." . hexdec($regs[4]); + + /* + * a standard dot quad netmask was entered. + */ + } else if (strchr($parts[1], ".")) { + if (! $myself->validateNetmask($parts[1])) { + return $this->pear->raiseError("invalid netmask value"); + } + $myself->netmask = $parts[1]; + + /* + * a CIDR bitmask type was entered + */ + } else if (ctype_digit($parts[1]) && $parts[1] >= 0 && $parts[1] <= 32) { + // bitmask was entered + $myself->bitmask = $parts[1]; + + /* + * Some unknown format of netmask was entered + */ + } else { + return $this->pear->raiseError("invalid netmask value"); + } + $myself->calculate(); + return $myself; + } else if ($myself->validateIP($address)) { + $myself->ip = $address; + return $myself; + } else { + return $this->pear->raiseError("invalid IP address"); + } + } + + // }}} + // {{{ calculate() + + /** + * Calculates network information based on an IP address and netmask. + * + * Fully populates the object properties based on the IP address and + * netmask/bitmask properties. Once these two fields are populated, + * calculate() will perform calculations to determine the network and + * broadcast address of the network. + * + * @return mixed true if no errors occured, otherwise PEAR_Error object + */ + function calculate() + { + $validNM = $GLOBALS['Net_IPv4_Netmask_Map']; + + if (! is_a($this, "net_ipv4")) { + $myself = new Net_IPv4; + return $this->pear->raiseError("cannot calculate on uninstantiated Net_IPv4 class"); + } + + /* Find out if we were given an ip address in dot quad notation or + * a network long ip address. Whichever was given, populate the + * other field + */ + if (strlen($this->ip)) { + if (! $this->validateIP($this->ip)) { + return $this->pear->raiseError("invalid IP address"); + } + $this->long = $this->ip2double($this->ip); + } else if (is_numeric($this->long)) { + $this->ip = long2ip($this->long); + } else { + return $this->pear->raiseError("ip address not specified"); + } + + /* + * Check to see if we were supplied with a bitmask or a netmask. + * Populate the other field as needed. + */ + if (strlen($this->bitmask)) { + $this->netmask = $validNM[$this->bitmask]; + } else if (strlen($this->netmask)) { + $validNM_rev = array_flip($validNM); + $this->bitmask = $validNM_rev[$this->netmask]; + } else { + return $this->pear->raiseError("netmask or bitmask are required for calculation"); + } + $this->network = long2ip(ip2long($this->ip) & ip2long($this->netmask)); + $this->broadcast = long2ip(ip2long($this->ip) | + (ip2long($this->netmask) ^ ip2long("255.255.255.255"))); + return true; + } + + // }}} + // {{{ getNetmask() + + function getNetmask($length) + { + if (! PEAR::isError($ipobj = Net_IPv4::parseAddress("0.0.0.0/" . $length))) { + $mask = $ipobj->netmask; + unset($ipobj); + return $mask; + } + return false; + } + + // }}} + // {{{ getNetLength() + + function getNetLength($netmask) + { + if (! PEAR::isError($ipobj = Net_IPv4::parseAddress("0.0.0.0/" . $netmask))) { + $bitmask = $ipobj->bitmask; + unset($ipobj); + return $bitmask; + } + return false; + } + + // }}} + // {{{ getSubnet() + + function getSubnet($ip, $netmask) + { + if (! PEAR::isError($ipobj = Net_IPv4::parseAddress($ip . "/" . $netmask))) { + $net = $ipobj->network; + unset($ipobj); + return $net; + } + return false; + } + + // }}} + // {{{ inSameSubnet() + + function inSameSubnet($ip1, $ip2) + { + if (! is_object($ip1) || strcasecmp(get_class($ip1), 'net_ipv4') <> 0) { + $ipobj1 = Net_IPv4::parseAddress($ip1); + if (PEAR::isError($ipobj)) { + return $this->pear->raiseError("IP addresses must be an understood format or a Net_IPv4 object"); + } + } + if (! is_object($ip2) || strcasecmp(get_class($ip2), 'net_ipv4') <> 0) { + $ipobj2 = Net_IPv4::parseAddress($ip2); + if (PEAR::isError($ipobj)) { + return $this->pear->raiseError("IP addresses must be an understood format or a Net_IPv4 object"); + } + } + if ($ipobj1->network == $ipobj2->network && + $ipobj1->bitmask == $ipobj2->bitmask) { + return true; + } + return false; + } + + // }}} + // {{{ atoh() + + /** + * Converts a dot-quad formatted IP address into a hexadecimal string + * @param string $addr IP-adress in dot-quad format + * @return mixed false if invalid IP and hexadecimal representation as string if valid + */ + function atoh($addr) + { + if (! Net_IPv4::validateIP($addr)) { + return false; + } + $ap = explode(".", $addr); + return sprintf("%02x%02x%02x%02x", $ap[0], $ap[1], $ap[2], $ap[3]); + } + + // }}} + // {{{ htoa() + + /** + * Converts a hexadecimal string into a dot-quad formatted IP address + * @param string $addr IP-adress in hexadecimal format + * @return mixed false if invalid IP and dot-quad formatted IP as string if valid + */ + function htoa($addr) + { + if (preg_match("/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i", + $addr, $regs)) { + return hexdec($regs[1]) . "." . hexdec($regs[2]) . "." . + hexdec($regs[3]) . "." . hexdec($regs[4]); + } + return false; + } + + // }}} + // {{{ ip2double() + + /** + * Converts an IP address to a PHP double. Better than ip2long because + * a long in PHP is a signed integer. + * @param string $ip dot-quad formatted IP adress + * @return float IP adress as double - positive value unlike ip2long + */ + function ip2double($ip) + { + return (double)(sprintf("%u", ip2long($ip))); + } + + // }}} + // {{{ ipInNetwork() + + /** + * Determines whether or not the supplied IP is within the supplied network. + * + * This function determines whether an IP address is within a network. + * The IP address ($ip) must be supplied in dot-quad format, and the + * network ($network) may be either a string containing a CIDR + * formatted network definition, or a Net_IPv4 object. + * + * @param string $ip A dot quad representation of an IP address + * @param string $network A string representing the network in CIDR format or a Net_IPv4 object. + * @return bool true if the IP address exists within the network + */ + function ipInNetwork($ip, $network) + { + if (! is_object($network) || strcasecmp(get_class($network), 'net_ipv4') <> 0) { + $network = Net_IPv4::parseAddress($network); + } + if (strcasecmp(get_class($network), 'pear_error') === 0) { + return false; + } + $net = Net_IPv4::ip2double($network->network); + $bcast = Net_IPv4::ip2double($network->broadcast); + $ip = Net_IPv4::ip2double($ip); + unset($network); + if ($ip >= $net && $ip <= $bcast) { + return true; + } + return false; + } + + // }}} +} + +// }}} + +/* + * vim: sts=4 ts=4 sw=4 cindent fdm=marker + */ +?> diff --git a/functions/PEAR/Net/IPv6.php b/functions/PEAR/Net/IPv6.php new file mode 100755 index 000000000..4d603fa97 --- /dev/null +++ b/functions/PEAR/Net/IPv6.php @@ -0,0 +1,1043 @@ + + * @copyright 2003-2005 The PHP Group + * @license BSD License http://www.opensource.org/licenses/bsd-license.php + * @version CVS: $Id: IPv6.php 305477 2010-11-18 02:29:17Z alexmerz $ + * @link http://pear.php.net/package/Net_IPv6 + */ + + +require_once 'PEAR.php'; + +// {{{ constants + +/** + * Error message if netmask bits was not found + * @see isInNetmask + */ +define("NET_IPV6_NO_NETMASK_MSG", "Netmask length not found"); + +/** + * Error code if netmask bits was not found + * @see isInNetmask + */ +define("NET_IPV6_NO_NETMASK", 10); + +/** + * Address Type: Unassigned (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_UNASSIGNED", 1); + +/** + * Address Type: Reserved (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_RESERVED", 11); + +/** + * Address Type: Reserved for NSAP Allocation (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_RESERVED_NSAP", 12); + +/** + * Address Type: Reserved for IPX Allocation (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_RESERVED_IPX", 13); + +/** + * Address Type: Reserved for Geographic-Based Unicast Addresses + * (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC", 14); + +/** + * Address Type: Provider-Based Unicast Address (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_UNICAST_PROVIDER", 22); + +/** + * Address Type: Multicast Addresses (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_MULTICAST", 31); + +/** + * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_LOCAL_LINK", 42); + +/** + * Address Type: Link Local Use Addresses (RFC 1884, Section 2.3) + * @see getAddressType() + */ +define("NET_IPV6_LOCAL_SITE", 43); + +/** + * Address Type: Address range to embedded IPv4 ip in an IPv6 address (RFC 4291, Section 2.5.5) + * @see getAddressType() + */ +define("NET_IPV6_IPV4MAPPING", 51); + +/** + * Address Type: Unspecified (RFC 4291, Section 2.5.2) + * @see getAddressType() + */ +define("NET_IPV6_UNSPECIFIED", 52); + +/** + * Address Type: Unspecified (RFC 4291, Section 2.5.3) + * @see getAddressType() + */ +define("NET_IPV6_LOOPBACK", 53); + +/** + * Address Type: address can not assigned to a specific type + * @see getAddressType() + */ +define("NET_IPV6_UNKNOWN_TYPE", 1001); + +// }}} +// {{{ Net_IPv6 + +/** + * Class to validate and to work with IPv6 addresses. + * + * @category Net + * @package Net_IPv6 + * @author Alexander Merz + * @author + * @author Josh Peck + * @copyright 2003-2010 The PHP Group + * @license BSD License http://www.opensource.org/licenses/bsd-license.php + * @version Release: 1.1.0RC5 + * @link http://pear.php.net/package/Net_IPv6 + */ +class Net_IPv6 +{ + //pear + private $pear; + + + //initialize PEAR object on init + public function __construct () { + $this->pear = new PEAR; + } + + // {{{ separate() + /** + * Separates an IPv6 address into the address and a prefix length part + * + * @param String $ip the (compressed) IP as Hex representation + * + * @return Array the first element is the IP, the second the prefix length + * @since 1.2.0 + * @access public + * @static + */ + function separate($ip) + { + + $addr = $ip; + $spec = ''; + + if(false === strrpos($ip, '/')) { + + return array($addr, $spec); + + } + + $elements = explode('/', $ip); + + if(2 == count($elements)) { + + $addr = $elements[0]; + $spec = $elements[1]; + + } + + return array($addr, $spec); + + } + // }}} + + // {{{ removeNetmaskSpec() + + /** + * Removes a possible existing prefix length/ netmask specification at an IP addresse. + * + * @param String $ip the (compressed) IP as Hex representation + * + * @return String the IP without netmask length + * @since 1.1.0 + * @access public + * @static + */ + function removeNetmaskSpec($ip) + { + + $elements = Net_IPv6::separate($ip); + + return $elements[0]; + + } + // }}} + // {{{ removePrefixLength() + + /** + * Tests for a prefix length specification in the address + * and removes the prefix length, if exists + * + * The method is technically identical to removeNetmaskSpec() and + * will be dropped in a future release. + * + * @param String $ip a valid ipv6 address + * + * @return String the address without a prefix length + * @access public + * @static + * @see removeNetmaskSpec() + * @deprecated + */ + function removePrefixLength($ip) + { + $pos = strrpos($ip, '/'); + + if (false !== $pos) { + + return substr($ip, 0, $pos); + + } + + return $ip; + } + + // }}} + // {{{ getNetmaskSpec() + + /** + * Returns a possible existing prefix length/netmask specification on an IP addresse. + * + * @param String $ip the (compressed) IP as Hex representation + * + * @return String the netmask spec + * @since 1.1.0 + * @access public + * @static + */ + function getNetmaskSpec($ip) + { + + $elements = Net_IPv6::separate($ip); + + return $elements[1]; + + } + + // }}} + // {{{ getPrefixLength() + + /** + * Tests for a prefix length specification in the address + * and returns the prefix length, if exists + * + * The method is technically identical to getNetmaskSpec() and + * will be dropped in a future release. + * + * @param String $ip a valid ipv6 address + * + * @return Mixed the prefix as String or false, if no prefix was found + * @access public + * @static + * @deprecated + */ + function getPrefixLength($ip) + { + if (preg_match("/^([0-9a-fA-F:]{2,39})\/(\d{1,3})*$/", + $ip, $matches)) { + + return $matches[2]; + + } else { + + return false; + + } + + } + + // }}} + // {{{ getNetmask() + + /** + * Calculates the network prefix based on the netmask bits. + * + * @param String $ip the (compressed) IP in Hex format + * @param int $bits if the number of netmask bits is not part of the IP + * you must provide the number of bits + * + * @return String the network prefix + * @since 1.1.0 + * @access public + * @static + */ + function getNetmask($ip, $bits = null) + { + if (null==$bits) { + + $elements = explode('/', $ip); + + if (2 == count($elements)) { + + $addr = $elements[0]; + $bits = $elements[1]; + + } else { + + include_once 'PEAR.php'; + + return $this->pear->raiseError(NET_IPV6_NO_NETMASK_MSG, + NET_IPV6_NO_NETMASK); + } + + } else { + + $addr = $ip; + + } + + $addr = Net_IPv6::uncompress($addr); + $binNetmask = str_repeat('1', $bits).str_repeat('0', 128 - $bits); + + return Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($addr) & $binNetmask); + } + + // }}} + // {{{ isInNetmask() + + /** + * Checks if an (compressed) IP is in a specific address space. + * + * IF the IP does not contains the number of netmask bits (F8000::FFFF/16) + * then you have to use the $bits parameter. + * + * @param String $ip the IP to check (eg. F800::FFFF) + * @param String $netmask the netmask (eg F800::) + * @param int $bits the number of netmask bits to compare, + * if not given in $ip + * + * @return boolean true if $ip is in the netmask + * @since 1.1.0 + * @access public + * @static + */ + function isInNetmask($ip, $netmask, $bits=null) + { + // try to get the bit count + + if (null == $bits) { + + $elements = explode('/', $ip); + + if (2 == count($elements)) { + + $ip = $elements[0]; + $bits = $elements[1]; + + } else if (null == $bits) { + + $elements = explode('/', $netmask); + + if (2 == count($elements)) { + + $netmask = $elements[0]; + $bits = $elements[1]; + + } + + if (null == $bits) { + + include_once 'PEAR.php'; + return $this->pear->raiseError(NET_IPV6_NO_NETMASK_MSG, + NET_IPV6_NO_NETMASK); + + } + + } + + } + + $binIp = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($ip)); + $binNetmask = Net_IPv6::_ip2Bin(Net_IPv6::removeNetmaskSpec($netmask)); + + if (null != $bits + && "" != $bits + && 0 == strncmp($binNetmask, $binIp, $bits)) { + + return true; + + } + + return false; + } + + // }}} + // {{{ getAddressType() + + /** + * Returns the type of an IPv6 address. + * + * RFC 2373, Section 2.3 describes several types of addresses in + * the IPv6 addresse space. + * Several addresse types are markers for reserved spaces and as + * consequence a subject to change. + * + * @param String $ip the IP address in Hex format, + * compressed IPs are allowed + * + * @return int one of the addresse type constants + * @access public + * @since 1.1.0 + * @static + * + * @see NET_IPV6_UNASSIGNED + * @see NET_IPV6_RESERVED + * @see NET_IPV6_RESERVED_NSAP + * @see NET_IPV6_RESERVED_IPX + * @see NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC + * @see NET_IPV6_UNICAST_PROVIDER + * @see NET_IPV6_MULTICAST + * @see NET_IPV6_LOCAL_LINK + * @see NET_IPV6_LOCAL_SITE + * @see NET_IPV6_IPV4MAPPING + * @see NET_IPV6_UNSPECIFIED + * @see NET_IPV6_LOOPBACK + * @see NET_IPV6_UNKNOWN_TYPE + */ + function getAddressType($ip) + { + $ip = Net_IPv6::removeNetmaskSpec($ip); + $binip = Net_IPv6::_ip2Bin($ip); + + if(0 == strncmp(str_repeat('0', 128), $binip, 128)) { // ::/128 + + return NET_IPV6_UNSPECIFIED; + + } else if(0 == strncmp(str_repeat('0', 127).'1', $binip, 128)) { // ::/128 + + return NET_IPV6_LOOPBACK; + + } else if (0 == strncmp(str_repeat('0', 80).str_repeat('1', 16), $binip, 96)) { // ::ffff/96 + + return NET_IPV6_IPV4MAPPING; + + } else if (0 == strncmp('1111111010', $binip, 10)) { + + return NET_IPV6_LOCAL_LINK; + + } else if (0 == strncmp('1111111011', $binip, 10)) { + + return NET_IPV6_LOCAL_SITE; + + } else if (0 == strncmp('111111100', $binip, 9)) { + + return NET_IPV6_UNASSIGNED; + + } else if (0 == strncmp('11111111', $binip, 8)) { + + return NET_IPV6_MULTICAST; + + } else if (0 == strncmp('00000000', $binip, 8)) { + + return NET_IPV6_RESERVED; + + } else if (0 == strncmp('00000001', $binip, 8) + || 0 == strncmp('1111110', $binip, 7)) { + + return NET_IPV6_UNASSIGNED; + + } else if (0 == strncmp('0000001', $binip, 7)) { + + return NET_IPV6_RESERVED_NSAP; + + } else if (0 == strncmp('0000010', $binip, 7)) { + + return NET_IPV6_RESERVED_IPX;; + + } else if (0 == strncmp('0000011', $binip, 7) || + 0 == strncmp('111110', $binip, 6) || + 0 == strncmp('11110', $binip, 5) || + 0 == strncmp('00001', $binip, 5) || + 0 == strncmp('1110', $binip, 4) || + 0 == strncmp('0001', $binip, 4) || + 0 == strncmp('001', $binip, 3) || + 0 == strncmp('011', $binip, 3) || + 0 == strncmp('101', $binip, 3) || + 0 == strncmp('110', $binip, 3)) { + + return NET_IPV6_UNASSIGNED; + + } else if (0 == strncmp('010', $binip, 3)) { + + return NET_IPV6_UNICAST_PROVIDER; + + } else if (0 == strncmp('100', $binip, 3)) { + + return NET_IPV6_RESERVED_UNICAST_GEOGRAPHIC; + + } + + return NET_IPV6_UNKNOWN_TYPE; + } + + // }}} + // {{{ Uncompress() + + /** + * Uncompresses an IPv6 adress + * + * RFC 2373 allows you to compress zeros in an adress to '::'. This + * function expects an valid IPv6 adress and expands the '::' to + * the required zeros. + * + * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 + * ::1 -> 0:0:0:0:0:0:0:1 + * + * @param String $ip a valid IPv6-adress (hex format) + * + * @return String the uncompressed IPv6-adress (hex format) + * @access public + * @see Compress() + * @static + */ + function uncompress($ip) + { + + $prefix = Net_IPv6::getPrefixLength($ip); + + if (false === $prefix) { + + $prefix = ''; + + } else { + + $ip = Net_IPv6::removePrefixLength($ip); + $prefix = '/'.$prefix; + + } + + $netmask = Net_IPv6::getNetmaskSpec($ip); + $uip = Net_IPv6::removeNetmaskSpec($ip); + + $c1 = -1; + $c2 = -1; + + if (false !== strpos($uip, '::') ) { + + list($ip1, $ip2) = explode('::', $uip); + + if ("" == $ip1) { + + $c1 = -1; + + } else { + + $pos = 0; + + if (0 < ($pos = substr_count($ip1, ':'))) { + + $c1 = $pos; + + } else { + + $c1 = 0; + + } + } + if ("" == $ip2) { + + $c2 = -1; + + } else { + + $pos = 0; + + if (0 < ($pos = substr_count($ip2, ':'))) { + + $c2 = $pos; + + } else { + + $c2 = 0; + + } + + } + + if (strstr($ip2, '.')) { + + $c2++; + + } + if (-1 == $c1 && -1 == $c2) { // :: + + $uip = "0:0:0:0:0:0:0:0"; + + } else if (-1 == $c1) { // ::xxx + + $fill = str_repeat('0:', 7-$c2); + $uip = str_replace('::', $fill, $uip); + + } else if (-1 == $c2) { // xxx:: + + $fill = str_repeat(':0', 7-$c1); + $uip = str_replace('::', $fill, $uip); + + } else { // xxx::xxx + + $fill = str_repeat(':0:', 6-$c2-$c1); + $uip = str_replace('::', $fill, $uip); + $uip = str_replace('::', ':', $uip); + + } + } + if ('' != $netmask) { + + $uip = $uip.'/'.$netmask; + + } + + return $uip.$prefix; + } + + // }}} + // {{{ Compress() + + /** + * Compresses an IPv6 adress + * + * RFC 2373 allows you to compress zeros in an adress to '::'. This + * function expects an valid IPv6 adress and compresses successive zeros + * to '::' + * + * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 + * 0:0:0:0:0:0:0:1 -> ::1 + * + * Whe $ip is an already compressed adress the methode returns the value as is, + * also if the adress can be compressed further. + * + * Example: FF01::0:1 -> FF01::0:1 + * + * To enforce maximum compression, you can set the second argument $force to true. + * + * Example: FF01::0:1 -> FF01::1 + * + * @param String $ip a valid IPv6-adress (hex format) + * @param boolean $force if true the adress will be compresses as best as possible (since 1.2.0) + * + * @return tring the compressed IPv6-adress (hex format) + * @access public + * @see Uncompress() + * @static + * @author elfrink at introweb dot nl + */ + function compress($ip, $force = false) + { + + if(false !== strpos($ip, '::')) { // its already compressed + + if(true == $force) { + + $ip = Net_IPv6::uncompress($ip); + + } else { + + return $ip; + + } + + } + + $prefix = Net_IPv6::getPrefixLength($ip); + + if (false === $prefix) { + + $prefix = ''; + + } else { + + $ip = Net_IPv6::removePrefixLength($ip); + $prefix = '/'.$prefix; + + } + + $netmask = Net_IPv6::getNetmaskSpec($ip); + $ip = Net_IPv6::removeNetmaskSpec($ip); + + $ipp = explode(':', $ip); + + for ($i = 0; $i < count($ipp); $i++) { + + $ipp[$i] = dechex(hexdec($ipp[$i])); + + } + + $cip = ':' . join(':', $ipp) . ':'; + + preg_match_all("/(:0)+/", $cip, $zeros); + + if (count($zeros[0]) > 0) { + + $match = ''; + + foreach ($zeros[0] as $zero) { + + if (strlen($zero) > strlen($match)) { + + $match = $zero; + + } + } + + $cip = preg_replace('/' . $match . '/', ':', $cip, 1); + + } + + $cip = preg_replace('/((^:)|(:$))/', '', $cip); + $cip = preg_replace('/((^:)|(:$))/', '::', $cip); + + if ('' != $netmask) { + + $cip = $cip.'/'.$netmask; + + } + + return $cip.$prefix; + + } + + // }}} + // {{{ isCompressible() + + /** + * Checks, if an IPv6 adress can be compressed + * + * @param String $ip a valid IPv6 adress + * + * @return Boolean true, if adress can be compressed + * + * @access public + * @since 1.2.0b + * @static + * @author Manuel Schmitt + */ + function isCompressible($ip) + { + + return (bool)($ip != Net_IPv6::compress($address)); + + } + + // }}} + // {{{ SplitV64() + + /** + * Splits an IPv6 adress into the IPv6 and a possible IPv4 part + * + * RFC 2373 allows you to note the last two parts of an IPv6 adress as + * an IPv4 compatible adress + * + * Example: 0:0:0:0:0:0:13.1.68.3 + * 0:0:0:0:0:FFFF:129.144.52.38 + * + * @param String $ip a valid IPv6-adress (hex format) + * @param Boolean $uncompress if true, the address will be uncompressed + * before processing + * + * @return Array [0] contains the IPv6 part, + * [1] the IPv4 part (hex format) + * @access public + * @static + */ + function SplitV64($ip, $uncompress = true) + { + $ip = Net_IPv6::removeNetmaskSpec($ip); + + if ($uncompress) { + + $ip = Net_IPv6::Uncompress($ip); + + } + + if (strstr($ip, '.')) { + + $pos = strrpos($ip, ':'); + $ip{$pos} = '_'; + $ipPart = explode('_', $ip); + + return $ipPart; + + } else { + + return array($ip, ""); + + } + } + + // }}} + // {{{ checkIPv6() + + /** + * Checks an IPv6 adress + * + * Checks if the given IP is IPv6-compatible + * + * @param String $ip a valid IPv6-adress + * + * @return Boolean true if $ip is an IPv6 adress + * @access public + * @static + */ + function checkIPv6($ip) + { + $ip = Net_IPv6::removePrefixLength($ip); + $ip = Net_IPv6::removeNetmaskSpec($ip); + + $ipPart = Net_IPv6::SplitV64($ip); + $count = 0; + + if (!empty($ipPart[0])) { + $ipv6 =explode(':', $ipPart[0]); + + for ($i = 0; $i < count($ipv6); $i++) { + + if(4 < strlen($ipv6[$i])) { + + return false; + + } + + $dec = hexdec($ipv6[$i]); + $hex = strtoupper(preg_replace("/^[0]{1,3}(.*[0-9a-fA-F])$/", + "\\1", + $ipv6[$i])); + + if ($ipv6[$i] >= 0 && $dec <= 65535 + && $hex == strtoupper(dechex($dec))) { + + $count++; + + } + + } + + if (8 == $count) { + + return true; + + } else if (6 == $count and !empty($ipPart[1])) { + + $ipv4 = explode('.', $ipPart[1]); + $count = 0; + + for ($i = 0; $i < count($ipv4); $i++) { + + if ($ipv4[$i] >= 0 && (integer)$ipv4[$i] <= 255 + && preg_match("/^\d{1,3}$/", $ipv4[$i])) { + + $count++; + + } + + } + + if (4 == $count) { + + return true; + + } + + } else { + + return false; + + } + + } else { + + return false; + + } + + } + + // }}} + + // {{{ _parseAddress() + + /** + * Returns the lowest and highest IPv6 address + * for a given IP and netmask specification + * + * The netmask may be a part of the $ip or + * the number of netwask bits is provided via $bits + * + * The result is an indexed array. The key 'start' + * contains the lowest possible IP adress. The key + * 'end' the highest address. + * + * @param String $ipToParse the IPv6 address + * @param String $bits the optional count of netmask bits + * + * @return Array ['start', 'end'] the lowest and highest IPv6 address + * @access public + * @static + * @author Nicholas Williams + */ + + function parseAddress($ipToParse, $bits = null) + { + + $ip = null; + $bitmask = null; + + if ( null == $bits ) { + + $elements = explode('/', $ipToParse); + + if ( 2 == count($elements) ) { + + $ip = Net_IPv6::uncompress($elements[0]); + $bitmask = $elements[1]; + + } else { + + include_once 'PEAR.php'; + + return $this->pear->raiseError(NET_IPV6_NO_NETMASK_MSG, + NET_IPV6_NO_NETMASK); + } + } else { + + $ip = Net_IPv6::uncompress($ipToParse); + $bitmask = $bits; + + } + + $binNetmask = str_repeat('1', $bitmask). + str_repeat('0', 128 - $bitmask); + $maxNetmask = str_repeat('1', 128); + $netmask = Net_IPv6::_bin2Ip($binNetmask); + + $startAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) + & $binNetmask); + $endAddress = Net_IPv6::_bin2Ip(Net_IPv6::_ip2Bin($ip) + | ($binNetmask ^ $maxNetmask)); + + return array('start' => $startAddress, 'end' => $endAddress); + } + + // }}} + + // {{{ _ip2Bin() + + /** + * Converts an IPv6 address from Hex into Binary representation. + * + * @param String $ip the IP to convert (a:b:c:d:e:f:g:h), + * compressed IPs are allowed + * + * @return String the binary representation + * @access private + @ @since 1.1.0 + */ + function _ip2Bin($ip) + { + $binstr = ''; + + $ip = Net_IPv6::removeNetmaskSpec($ip); + $ip = Net_IPv6::Uncompress($ip); + + $parts = explode(':', $ip); + + foreach ( $parts as $v ) { + + $str = base_convert($v, 16, 2); + $binstr .= str_pad($str, 16, '0', STR_PAD_LEFT); + + } + + return $binstr; + } + + // }}} + // {{{ _bin2Ip() + + /** + * Converts an IPv6 address from Binary into Hex representation. + * + * @param String $bin the IP address as binary + * + * @return String the uncompressed Hex representation + * @access private + @ @since 1.1.0 + */ + function _bin2Ip($bin) + { + $ip = ""; + + if (strlen($bin) < 128) { + + $bin = str_pad($bin, 128, '0', STR_PAD_LEFT); + + } + + $parts = str_split($bin, "16"); + + foreach ( $parts as $v ) { + + $str = base_convert($v, 2, 16); + $ip .= $str.":"; + + } + + $ip = substr($ip, 0, -1); + + return $ip; + } + + // }}} +} +// }}} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ + +?> diff --git a/functions/PEAR/Net/Ping.php b/functions/PEAR/Net/Ping.php new file mode 100644 index 000000000..9665f184d --- /dev/null +++ b/functions/PEAR/Net/Ping.php @@ -0,0 +1,1230 @@ + | +// | Tomas V.V.Cox | +// | Jan Lehnardt | +// | Kai Schrder | +// | Craig Constantine | +// +----------------------------------------------------------------------+ +// +// $Id: Ping.php 274728 2009-01-27 20:14:00Z cconstantine $ + +require_once "PEAR.php"; +require_once "OS/Guess.php"; + +define('NET_PING_FAILED_MSG', 'execution of ping failed' ); +define('NET_PING_HOST_NOT_FOUND_MSG', 'unknown host' ); +define('NET_PING_INVALID_ARGUMENTS_MSG', 'invalid argument array' ); +define('NET_PING_CANT_LOCATE_PING_BINARY_MSG', 'unable to locate the ping binary'); +define('NET_PING_RESULT_UNSUPPORTED_BACKEND_MSG', 'Backend not Supported' ); + +define('NET_PING_FAILED', 0); +define('NET_PING_HOST_NOT_FOUND', 1); +define('NET_PING_INVALID_ARGUMENTS', 2); +define('NET_PING_CANT_LOCATE_PING_BINARY', 3); +define('NET_PING_RESULT_UNSUPPORTED_BACKEND', 4); + + +/** +* Wrapper class for ping calls +* +* Usage: +* +* getMessage(); +* } else { +* $ping->setArgs(array('count' => 2)); +* var_dump($ping->ping('example.com')); +* } +* ?> +* +* @author Jan Lehnardt +* @version $Revision: 274728 $ +* @package Net +* @access public +*/ +class Net_Ping +{ + /** + * Location where the ping program is stored + * + * @var string + * @access private + */ + var $_ping_path = ""; + + /** + * Array with the result from the ping execution + * + * @var array + * @access private + */ + var $_result = array(); + + /** + * OS_Guess instance + * + * @var object + * @access private + */ + var $_OS_Guess = ""; + + /** + * OS_Guess->getSysname result + * + * @var string + * @access private + */ + var $_sysname = ""; + + /** + * Ping command arguments + * + * @var array + * @access private + */ + var $_args = array(); + + /** + * Indicates if an empty array was given to setArgs + * + * @var boolean + * @access private + */ + var $_noArgs = true; + + /** + * Contains the argument->option relation + * + * @var array + * @access private + */ + var $_argRelation = array(); + + //pear + public $pear; + + /** + * Constructor for the Class + * + * @access private + */ + function Net_Ping($ping_path, $sysname) + { + $this->_ping_path = $ping_path; + $this->_sysname = $sysname; + $this->_initArgRelation(); + $this->pear = new PEAR (); + } /* function Net_Ping() */ + + + /** + * Factory for Net_Ping + * + * @access public + */ + public static function factory() + { + $ping_path = ''; + + $sysname = Net_Ping::_setSystemName(); + + if (($ping_path = Net_Ping::_setPingPath($sysname)) == NET_PING_CANT_LOCATE_PING_BINARY) { + return $this->pear->raiseError(NET_PING_CANT_LOCATE_PING_BINARY_MSG, NET_PING_CANT_LOCATE_PING_BINARY); + } else { + return new Net_Ping($ping_path, $sysname); + } + } /* function factory() */ + + /** + * Resolve the system name + * + * @access private + */ + public static function _setSystemName() + { + $OS_Guess = new OS_Guess; + $sysname = $OS_Guess->getSysname(); + + // Refine the sysname for different Linux bundles/vendors. (This + // should go away if OS_Guess was ever extended to give vendor + // and vendor-version guesses.) + // + // Bear in mind that $sysname is eventually used to craft a + // method name to figure out which backend gets used to parse + // the ping output. Elsewhere, we'll set $sysname back before + // that. + if ('linux' == $sysname) { + if ( file_exists('/etc/lsb-release') + && false !== ($release=@file_get_contents('/etc/lsb-release')) + && preg_match('/gutsy/i', $release) + ) { + $sysname = 'linuxredhat9'; + } + else if ( file_exists('/etc/debian_version') ) { + $sysname = 'linuxdebian'; + }else if (file_exists('/etc/redhat-release') + && false !== ($release= @file_get_contents('/etc/redhat-release')) + ) + { + if (preg_match('/release 8/i', $release)) { + $sysname = 'linuxredhat8'; + }elseif (preg_match('/release 9/i', $release)) { + $sysname = 'linuxredhat9'; + } + } + } + + return $sysname; + + } /* function _setSystemName */ + + /** + * Set the arguments array + * + * @param array $args Hash with options + * @return mixed true or PEAR_error + * @access public + */ + function setArgs($args) + { + if (!is_array($args)) { + return $this->pear->raiseError(NET_PING_INVALID_ARGUMENTS_MSG, NET_PING_INVALID_ARGUMENTS); + } + + $this->_setNoArgs($args); + + $this->_args = $args; + + return true; + } /* function setArgs() */ + + /** + * Set the noArgs flag + * + * @param array $args Hash with options + * @return void + * @access private + */ + function _setNoArgs($args) + { + if (0 == count($args)) { + $this->_noArgs = true; + } else { + $this->_noArgs = false; + } + } /* function _setNoArgs() */ + + /** + * Sets the system's path to the ping binary + * + * @access private + */ + public static function _setPingPath($sysname) + { + $status = ''; + $output = array(); + $ping_path = ''; + + if ("windows" == $sysname) { + return "ping"; + } else { + $ping_path = exec("/usr/bin/which ping", $output, $status); + if (0 != $status) { + return NET_PING_CANT_LOCATE_PING_BINARY; + } else { + // be certain "which" did what we expect. (ref bug #12791) + if ( is_executable($ping_path) ) { + return $ping_path; + } + else { + return NET_PING_CANT_LOCATE_PING_BINARY; + } + } + } + } /* function _setPingPath() */ + + /** + * Creates the argument list according to platform differences + * + * @return string Argument line + * @access private + */ + function _createArgList() + { + $retval = array(); + + $timeout = ""; + $iface = ""; + $ttl = ""; + $count = ""; + $quiet = ""; + $size = ""; + $seq = ""; + $deadline = ""; + + foreach($this->_args AS $option => $value) { + if(!empty($option) && isset($this->_argRelation[$this->_sysname][$option]) && NULL != $this->_argRelation[$this->_sysname][$option]) { + ${$option} = $this->_argRelation[$this->_sysname][$option]." ".$value." "; + } + } + + switch($this->_sysname) { + + case "sunos": + if ($size || $count || $iface) { + /* $size and $count must be _both_ defined */ + $seq = " -s "; + if ($size == "") { + $size = " 56 "; + } + if ($count == "") { + $count = " 5 "; + } + } + $retval['pre'] = $iface.$seq.$ttl; + $retval['post'] = $size.$count; + break; + + case "freebsd": + $retval['pre'] = $quiet.$count.$ttl.$timeout; + $retval['post'] = ""; + break; + + case "darwin": + $retval['pre'] = $count.$timeout.$size; + $retval['post'] = ""; + break; + + case "netbsd": + $retval['pre'] = $quiet.$count.$iface.$size.$ttl.$timeout; + $retval['post'] = ""; + break; + + case "openbsd": + $retval['pre'] = $quiet.$count.$iface.$size.$ttl.$timeout; + $retval['post'] = ""; + break; + + case "linux": + $retval['pre'] = $quiet.$deadline.$count.$ttl.$size.$timeout; + $retval['post'] = ""; + break; + + case "linuxdebian": + $retval['pre'] = $quiet.$count.$ttl.$size.$timeout; + $retval['post'] = ""; + $this->_sysname = 'linux'; // undo linux vendor refinement hack + break; + + case "linuxredhat8": + $retval['pre'] = $iface.$ttl.$count.$quiet.$size.$deadline; + $retval['post'] = ""; + $this->_sysname = 'linux'; // undo linux vendor refinement hack + break; + + case "linuxredhat9": + $retval['pre'] = $timeout.$iface.$ttl.$count.$quiet.$size.$deadline; + $retval['post'] = ""; + $this->_sysname = 'linux'; // undo linux vendor refinement hack + break; + + case "windows": + $retval['pre'] = $count.$ttl.$timeout; + $retval['post'] = ""; + break; + + case "hpux": + $retval['pre'] = $ttl; + $retval['post'] = $size.$count; + break; + + case "aix": + $retval['pre'] = $count.$timeout.$ttl.$size; + $retval['post'] = ""; + break; + + default: + $retval['pre'] = ""; + $retval['post'] = ""; + break; + } + return($retval); + } /* function _createArgList() */ + + /** + * Execute ping + * + * @param string $host hostname + * @return mixed String on error or array with the result + * @access public + */ + function ping($host) + { + if($this->_noArgs) { + $this->setArgs(array('count' => 3)); + } + + $argList = $this->_createArgList(); + $cmd = $this->_ping_path." ".$argList['pre']." ".escapeshellcmd($host)." ".$argList['post']; + + // since we return a new instance of Net_Ping_Result (on + // success), users may call the ping() method repeatedly to + // perform unrelated ping tests Make sure we don't have raw data + // from a previous call laying in the _result array. + $this->_result = array(); + + exec($cmd, $this->_result); + + if (!is_array($this->_result)) { + return $this->pear->raiseError(NET_PING_FAILED_MSG, NET_PING_FAILED); + } + + if (count($this->_result) == 0) { + return $this->pear->raiseError(NET_PING_HOST_NOT_FOUND_MSG, NET_PING_HOST_NOT_FOUND); + } + else { + // Here we pass $this->_sysname to the factory(), but it is + // not actually used by the class. It's only maintained in + // the Net_Ping_Result class because the + // Net_Ping_Result::getSysName() method needs to be retained + // for backwards compatibility. + return Net_Ping_Result::factory($this->_result, $this->_sysname); + } + } /* function ping() */ + + /** + * Check if a host is up by pinging it + * + * @param string $host The host to test + * @param bool $severely If some of the packages did reach the host + * and severely is false the function will return true + * @return bool True on success or false otherwise + * + */ + function checkHost($host, $severely = true) + { + $matches = array(); + + $this->setArgs(array("count" => 10, + "size" => 32, + "quiet" => null, + "deadline" => 10 + ) + ); + $res = $this->ping($host); + if ($this->pear->isError($res)) { + return false; + } + if ($res->_received == 0) { + return false; + } + if ($res->_received != $res->_transmitted && $severely) { + return false; + } + return true; + } /* function checkHost() */ + + /** + * Output errors with PHP trigger_error(). You can silence the errors + * with prefixing a "@" sign to the function call: @Net_Ping::ping(..); + * + * @param mixed $error a PEAR error or a string with the error message + * @return bool false + * @access private + * @author Kai Schrder + */ + public static function _raiseError($error) + { + if ($this->pear->isError($error)) { + $error = $error->getMessage(); + } + trigger_error($error, E_USER_WARNING); + return false; + } /* function _raiseError() */ + + /** + * Creates the argument list according to platform differences + * + * @return string Argument line + * @access private + */ + function _initArgRelation() + { + $this->_argRelation["sunos"] = array( + "timeout" => NULL, + "ttl" => "-t", + "count" => " ", + "quiet" => "-q", + "size" => " ", + "iface" => "-i" + ); + + $this->_argRelation["freebsd"] = array ( + "timeout" => "-t", + "ttl" => "-m", + "count" => "-c", + "quiet" => "-q", + "size" => NULL, + "iface" => NULL + ); + + $this->_argRelation["netbsd"] = array ( + "timeout" => "-w", + "iface" => "-I", + "ttl" => "-T", + "count" => "-c", + "quiet" => "-q", + "size" => "-s" + ); + + $this->_argRelation["openbsd"] = array ( + "timeout" => "-w", + "iface" => "-I", + "ttl" => "-t", + "count" => "-c", + "quiet" => "-q", + "size" => "-s" + ); + + $this->_argRelation["darwin"] = array ( + "timeout" => "-t", + "iface" => NULL, + "ttl" => NULL, + "count" => "-c", + "quiet" => "-q", + "size" => NULL + ); + + $this->_argRelation["linux"] = array ( + "timeout" => "-W", + "iface" => NULL, + "ttl" => "-t", + "count" => "-c", + "quiet" => "-q", + "size" => "-s", + "deadline" => "-w" + ); + + $this->_argRelation["linuxdebian"] = array ( + "timeout" => "-W", + "iface" => NULL, + "ttl" => "-t", + "count" => "-c", + "quiet" => "-q", + "size" => "-s", + "deadline" => "-w", + ); + + $this->_argRelation["linuxredhat8"] = array ( + "timeout" => NULL, + "iface" => "-I", + "ttl" => "-t", + "count" => "-c", + "quiet" => "-q", + "size" => "-s", + "deadline" => "-w" + ); + + $this->_argRelation["linuxredhat9"] = array ( + "timeout" => "-W", + "iface" => "-I", + "ttl" => "-t", + "count" => "-c", + "quiet" => "-q", + "size" => "-s", + "deadline" => "-w" + ); + + $this->_argRelation["windows"] = array ( + "timeout" => "-w", + "iface" => NULL, + "ttl" => "-i", + "count" => "-n", + "quiet" => NULL, + "size" => "-l" + ); + + $this->_argRelation["hpux"] = array ( + "timeout" => NULL, + "iface" => NULL, + "ttl" => "-t", + "count" => "-n", + "quiet" => NULL, + "size" => " " + ); + + $this->_argRelation["aix"] = array ( + "timeout" => "-i", + "iface" => NULL, + "ttl" => "-T", + "count" => "-c", + "quiet" => NULL, + "size" => "-s" + ); + } /* function _initArgRelation() */ +} /* class Net_Ping */ + +/** +* Container class for Net_Ping results +* +* @author Jan Lehnardt +* @version $Revision: 274728 $ +* @package Net +* @access private +*/ +class Net_Ping_Result +{ + /** + * ICMP sequence number and associated time in ms + * + * @var array + * @access private + */ + var $_icmp_sequence = array(); /* array($sequence_number => $time ) */ + + /** + * The target's IP Address + * + * @var string + * @access private + */ + var $_target_ip; + + /** + * Number of bytes that are sent with each ICMP request + * + * @var int + * @access private + */ + var $_bytes_per_request; + + /** + * The total number of bytes that are sent with all ICMP requests + * + * @var int + * @access private + */ + var $_bytes_total; + + /** + * The ICMP request's TTL + * + * @var int + * @access private + */ + var $_ttl; + + /** + * The raw Net_Ping::result + * + * @var array + * @access private + */ + var $_raw_data = array(); + + /** + * The Net_Ping::_sysname + * + * @var int + * @access private + */ + var $_sysname; + + /** + * Statistical information about the ping + * + * @var int + * @access private + */ + var $_round_trip = array(); /* array('min' => xxx, 'avg' => yyy, 'max' => zzz) */ + + + /** + * Constructor for the Class + * + * @access private + */ + function Net_Ping_Result($result, $sysname) + { + $this->_raw_data = $result; + + // The _sysname property is no longer used by Net_Ping_result. + // The property remains for backwards compatibility so the + // getSystemName() method continues to work. + $this->_sysname = $sysname; + + $this->_parseResult(); + } /* function Net_Ping_Result() */ + + /** + * Factory for Net_Ping_Result + * + * @access public + * @param array $result Net_Ping result + * @param string $sysname OS_Guess::sysname + */ + public static function factory($result, $sysname) + { + return new Net_Ping_Result($result, $sysname); + } /* function factory() */ + + /** + * Parses the raw output from the ping utility. + * + * @access private + */ + function _parseResult() + { + // MAINTAINERS: + // + // If you're in this class fixing or extending the parser + // please add another file in the 'tests/test_parser_data/' + // directory which exemplafies the problem. And of course + // you'll want to run the 'tests/test_parser.php' (which + // contains easy how-to instructions) to make sure you haven't + // broken any existing behaviour. + + // operate on a copy of the raw output since we're going to modify it + $data = $this->_raw_data; + + // remove leading and trailing blank lines from output + $this->_parseResultTrimLines($data); + + // separate the output into upper and lower portions, + // and trim those portions + $this->_parseResultSeparateParts($data, $upper, $lower); + $this->_parseResultTrimLines($upper); + $this->_parseResultTrimLines($lower); + + // extract various things from the ping output . . . + + $this->_target_ip = $this->_parseResultDetailTargetIp($upper); + $this->_bytes_per_request = $this->_parseResultDetailBytesPerRequest($upper); + $this->_ttl = $this->_parseResultDetailTtl($upper); + $this->_icmp_sequence = $this->_parseResultDetailIcmpSequence($upper); + $this->_round_trip = $this->_parseResultDetailRoundTrip($lower); + + $this->_parseResultDetailTransmitted($lower); + $this->_parseResultDetailReceived($lower); + $this->_parseResultDetailLoss($lower); + + if ( isset($this->_transmitted) ) { + $this->_bytes_total = $this->_transmitted * $this->_bytes_per_request; + } + + } /* function _parseResult() */ + + /** + * determinces the number of bytes sent by ping per ICMP ECHO + * + * @access private + */ + function _parseResultDetailBytesPerRequest($upper) + { + // The ICMP ECHO REQUEST and REPLY packets should be the same + // size. So we can also find what we want in the output for any + // succesful ICMP reply which ping printed. + for ( $i=1; $i_loss = (int)$matches[1]; + return; + } + } + } + + /** + * Locates the "packets received" in the ping output + * + * @access private + */ + function _parseResultDetailReceived($lower) + { + for ( $i=1; $i_received = (int)$matches[1]; + return; + } + } + } + + /** + * determines the mininum, maximum, average and standard deviation + * of the round trip times. + * + * @access private + */ + function _parseResultDetailRoundTrip($lower) + { + // The first pattern will match a sequence of 3 or 4 + // alaphabet-char strings separated with slashes without + // presuming the order. eg, "min/max/avg" and + // "min/max/avg/mdev". Some ping flavors don't have the standard + // deviation value, and some have different names for it when + // present. + $p1 = '[a-z]+/[a-z]+/[a-z]+/?[a-z]*'; + + // And the pattern for 3 or 4 numbers (decimal values permitted) + // separated by slashes. + $p2 = '[0-9\.]+/[0-9\.]+/[0-9\.]+/?[0-9\.]*'; + + $results = array(); + $matches = array(); + for ( $i=(count($lower)-1); $i>=0; $i-- ) { + if ( preg_match('|('.$p1.')[^0-9]+('.$p2.')|i', $lower[$i], $matches) ) { + break; + } + } + + // matches? + if ( count($matches) > 0 ) { + // we want standardized keys in the array we return. Here we + // look for the values (min, max, etc) and setup the return + // hash + $fields = explode('/', $matches[1]); + $values = explode('/', $matches[2]); + for ( $i=0; $i=0; $i-- ) { + if ( preg_match('/min.*max/i', $lower[$i]) ) { + if ( preg_match('/'.$p3.$p3.$p3.'/i', $lower[$i], $matches) ) { + $results['min'] = $matches[1]; + $results['max'] = $matches[2]; + $results['avg'] = $matches[3]; + } + break; + } + } + + // either an array of min, max and avg from just above, or still + // the empty array from initialization way above + return( $results ); + } + + /** + * determinces the target IP address actually used by ping + * + * @access private + */ + function _parseResultDetailTargetIp($upper) + { + // Grab the first IP addr we can find. Most ping flavors + // put the target IP on the first line, but some only list it + // in successful ping packet lines. + for ( $i=0; $i_transmitted = (int)$matches[1]; + return; + } + } + } + + /** + * determinces the time to live (TTL) actually used by ping + * + * @access private + */ + function _parseResultDetailTtl($upper) + { + //extract TTL from first icmp echo line + for ( $i=1; $i 0 + ) { + return( (int)$matches[1] ); + } + } + + // No idea what ttl was used. Probably because no packets + // received in reply. + return( NULL ); + } + + /** + * Modifies the array to temoves leading and trailing blank lines + * + * @access private + */ + function _parseResultTrimLines(&$data) + { +if ( !is_array($data) ) { +print_r($this); +exit; +} + // Trim empty elements from the front + while ( preg_match('/^\s*$/', $data[0]) ) { + array_splice($data, 0, 1); + } + // Trim empty elements from the back + while ( preg_match('/^\s*$/', $data[(count($data)-1)]) ) { + array_splice($data, -1, 1); + } + } + + /** + * Separates the upper portion (data about individual ICMP ECHO + * packets) and the lower portion (statistics about the ping + * execution as a whole.) + * + * @access private + */ + function _parseResultSeparateParts($data, &$upper, &$lower) + { + $upper = array(); + $lower = array(); + + // find the blank line closest to the end + $dividerIndex = count($data) - 1; + while ( !preg_match('/^\s*$/', $data[$dividerIndex]) ) { + $dividerIndex--; + if ( $dividerIndex < 0 ) { + break; + } + } + + // This is horrible; All the other methods assume we're able to + // separate the upper (preamble and per-packet output) and lower + // (statistics and summary output) sections. + if ( $dividerIndex < 0 ) { + $upper = $data; + $lower = $data; + return; + } + + for ( $i=0; $i<$dividerIndex; $i++ ) { + $upper[] = $data[$i]; + } + for ( $i=(1+$dividerIndex); $i$name)?$this->$name:''; + } /* function getValue() */ + + /** + * Accessor for $this->_target_ip; + * + * @return string IP address + * @access public + * @see Ping_Result::_target_ip + */ + function getTargetIp() + { + return $this->_target_ip; + } /* function getTargetIp() */ + + /** + * Accessor for $this->_icmp_sequence; + * + * @return array ICMP sequence + * @access private + * @see Ping_Result::_icmp_sequence + */ + function getICMPSequence() + { + return $this->_icmp_sequence; + } /* function getICMPSequencs() */ + + /** + * Accessor for $this->_bytes_per_request; + * + * @return int bytes per request + * @access private + * @see Ping_Result::_bytes_per_request + */ + function getBytesPerRequest() + { + return $this->_bytes_per_request; + } /* function getBytesPerRequest() */ + + /** + * Accessor for $this->_bytes_total; + * + * @return int total bytes + * @access private + * @see Ping_Result::_bytes_total + */ + function getBytesTotal() + { + return $this->_bytes_total; + } /* function getBytesTotal() */ + + /** + * Accessor for $this->_ttl; + * + * @return int TTL + * @access private + * @see Ping_Result::_ttl + */ + function getTTL() + { + return $this->_ttl; + } /* function getTTL() */ + + /** + * Accessor for $this->_raw_data; + * + * @return array raw data + * @access private + * @see Ping_Result::_raw_data + */ + function getRawData() + { + return $this->_raw_data; + } /* function getRawData() */ + + /** + * Accessor for $this->_sysname; + * + * @return string OS_Guess::sysname + * @access private + * @see Ping_Result::_sysname + */ + function getSystemName() + { + return $this->_sysname; + } /* function getSystemName() */ + + /** + * Accessor for $this->_round_trip; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getRoundTrip() + { + return $this->_round_trip; + } /* function getRoundTrip() */ + + /** + * Accessor for $this->_round_trip['min']; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getMin() + { + return $this->_round_trip['min']; + } /* function getMin() */ + + /** + * Accessor for $this->_round_trip['max']; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getMax() + { + return $this->_round_trip['max']; + } /* function getMax() */ + + /** + * Accessor for $this->_round_trip['stddev']; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getStddev() + { + return $this->_round_trip['stddev']; + } /* function getStddev() */ + + /** + * Accessor for $this->_round_tripp['avg']; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getAvg() + { + return $this->_round_trip['avg']; + } /* function getAvg() */ + + /** + * Accessor for $this->_transmitted; + * + * @return array statistical information + * @access private + */ + function getTransmitted() + { + return $this->_transmitted; + } /* function getTransmitted() */ + + /** + * Accessor for $this->_received; + * + * @return array statistical information + * @access private + */ + function getReceived() + { + return $this->_received; + } /* function getReceived() */ + + /** + * Accessor for $this->_loss; + * + * @return array statistical information + * @access private + */ + function getLoss() + { + return $this->_loss; + } /* function getLoss() */ + +} /* class Net_Ping_Result */ +?> diff --git a/functions/PEAR/OLE/ChainedBlockStream.php b/functions/PEAR/OLE/ChainedBlockStream.php new file mode 100755 index 000000000..129b77b4c --- /dev/null +++ b/functions/PEAR/OLE/ChainedBlockStream.php @@ -0,0 +1,229 @@ + + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: ChainedBlockStream.php,v 1.1 2007/02/13 21:00:42 schmidt Exp $ + * @link http://pear.php.net/package/OLE + * @since File available since Release 0.6.0 + */ + +require_once 'functions/PEAR/PEAR.php'; +require_once 'functions/PEAR/OLE.php'; + +/** + * Stream wrapper for reading data stored in an OLE file. Implements methods + * for PHP's stream_wrapper_register(). For creating streams using this + * wrapper, use OLE_PPS_File::getStream(). + * + * @category Structures + * @package OLE + * @author Christian Schmidt + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/OLE + * @since Class available since Release 0.6.0 + */ +class OLE_ChainedBlockStream extends PEAR +{ + /** + * The OLE container of the file that is being read. + * @var OLE + */ + var $ole; + + /** + * Parameters specified by fopen(). + * @var array + */ + var $params; + + /** + * The binary data of the file. + * @var string + */ + var $data; + + /** + * The file pointer. + * @var int byte offset + */ + var $pos; + + /** + * Implements support for fopen(). + * For creating streams using this wrapper, use OLE_PPS_File::getStream(). + * @param string resource name including scheme, e.g. + * ole-chainedblockstream://oleInstanceId=1 + * @param string only "r" is supported + * @param int mask of STREAM_REPORT_ERRORS and STREAM_USE_PATH + * @param string absolute path of the opened stream (out parameter) + * @return bool true on success + */ + function stream_open($path, $mode, $options, &$openedPath) + { + if ($mode != 'r') { + if ($options & STREAM_REPORT_ERRORS) { + trigger_error('Only reading is supported', E_USER_WARNING); + } + return false; + } + + // 25 is length of "ole-chainedblockstream://" + parse_str(substr($path, 25), $this->params); + if (!isset($this->params['oleInstanceId'], + $this->params['blockId'], + $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) { + + if ($options & STREAM_REPORT_ERRORS) { + trigger_error('OLE stream not found', E_USER_WARNING); + } + return false; + } + $this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']]; + + $blockId = $this->params['blockId']; + $this->data = ''; + if (isset($this->params['size']) && + $this->params['size'] < $this->ole->bigBlockThreshold && + $blockId != $this->ole->root->_StartBlock) { + + // Block id refers to small blocks + $rootPos = $this->ole->_getBlockOffset($this->ole->root->_StartBlock); + while ($blockId != -2) { + $pos = $rootPos + $blockId * $this->ole->bigBlockSize; + $blockId = $this->ole->sbat[$blockId]; + fseek($this->ole->_file_handle, $pos); + $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize); + } + } else { + // Block id refers to big blocks + while ($blockId != -2) { + $pos = $this->ole->_getBlockOffset($blockId); + fseek($this->ole->_file_handle, $pos); + $this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize); + $blockId = $this->ole->bbat[$blockId]; + } + } + if (isset($this->params['size'])) { + $this->data = substr($this->data, 0, $this->params['size']); + } + + if ($options & STREAM_USE_PATH) { + $openedPath = $path; + } + + return true; + } + + /** + * Implements support for fclose(). + * @return string + */ + function stream_close() + { + $this->ole = null; + unset($GLOBALS['_OLE_INSTANCES']); + } + + /** + * Implements support for fread(), fgets() etc. + * @param int maximum number of bytes to read + * @return string + */ + function stream_read($count) + { + if ($this->stream_eof()) { + return false; + } + $s = substr($this->data, $this->pos, $count); + $this->pos += $count; + return $s; + } + + /** + * Implements support for feof(). + * @return bool TRUE if the file pointer is at EOF; otherwise FALSE + */ + function stream_eof() + { + $eof = $this->pos >= strlen($this->data); + // Workaround for bug in PHP 5.0.x: http://bugs.php.net/27508 + if (version_compare(PHP_VERSION, '5.0', '>=') && + version_compare(PHP_VERSION, '5.1', '<')) { + + $eof = !$eof; + } + return $eof; + } + + /** + * Returns the position of the file pointer, i.e. its offset into the file + * stream. Implements support for ftell(). + * @return int + */ + function stream_tell() + { + return $this->pos; + } + + /** + * Implements support for fseek(). + * @param int byte offset + * @param int SEEK_SET, SEEK_CUR or SEEK_END + * @return bool + */ + function stream_seek($offset, $whence) + { + if ($whence == SEEK_SET && $offset >= 0) { + $this->pos = $offset; + } elseif ($whence == SEEK_CUR && -$offset <= $this->pos) { + $this->pos += $offset; + } elseif ($whence == SEEK_END && -$offset <= sizeof($this->data)) { + $this->pos = strlen($this->data) + $offset; + } else { + return false; + } + return true; + } + + /** + * Implements support for fstat(). Currently the only supported field is + * "size". + * @return array + */ + function stream_stat() + { + return array( + 'size' => strlen($this->data), + ); + } + + // Methods used by stream_wrapper_register() that are not implemented: + // bool stream_flush ( void ) + // int stream_write ( string data ) + // bool rename ( string path_from, string path_to ) + // bool mkdir ( string path, int mode, int options ) + // bool rmdir ( string path, int options ) + // bool dir_opendir ( string path, int options ) + // array url_stat ( string path, int flags ) + // string dir_readdir ( void ) + // bool dir_rewinddir ( void ) + // bool dir_closedir ( void ) +} + +?> diff --git a/functions/PEAR/OLE/OLE.php b/functions/PEAR/OLE/OLE.php new file mode 100755 index 000000000..88d358797 --- /dev/null +++ b/functions/PEAR/OLE/OLE.php @@ -0,0 +1,570 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +// $Id: OLE.php,v 1.15 2007/12/18 20:59:11 schmidt Exp $ + + +/** +* Constants for OLE package +*/ +define('OLE_PPS_TYPE_ROOT', 5); +define('OLE_PPS_TYPE_DIR', 1); +define('OLE_PPS_TYPE_FILE', 2); +define('OLE_DATA_SIZE_SMALL', 0x1000); +define('OLE_LONG_INT_SIZE', 4); +define('OLE_PPS_SIZE', 0x80); + +require_once 'PEAR.php'; + +/** +* Array for storing OLE instances that are accessed from +* OLE_ChainedBlockStream::stream_open(). +* @var array +*/ +$GLOBALS['_OLE_INSTANCES'] = array(); + +/** +* OLE package base class. +* +* @category Structures +* @package OLE +* @author Xavier Noguer +* @author Christian Schmidt +*/ +class OLE extends PEAR +{ + + /** + * The file handle for reading an OLE container + * @var resource + */ + var $_file_handle; + + /** + * Array of PPS's found on the OLE container + * @var array + */ + var $_list; + + /** + * Root directory of OLE container + * @var OLE_PPS_Root + */ + var $root; + + /** + * Big Block Allocation Table + * @var array (blockId => nextBlockId) + */ + var $bbat; + + /** + * Short Block Allocation Table + * @var array (blockId => nextBlockId) + */ + var $sbat; + + /** + * Size of big blocks. This is usually 512. + * @var int number of octets per block. + */ + var $bigBlockSize; + + /** + * Size of small blocks. This is usually 64. + * @var int number of octets per block + */ + var $smallBlockSize; + + /** + * Creates a new OLE object + * @access public + */ + function OLE() + { + $this->_list = array(); + } + + /** + * Destructor (using PEAR) + * Just closes the file handle on the OLE file. + * + * @access private + */ + function _OLE() + { + fclose($this->_file_handle); + } + + /** + * Reads an OLE container from the contents of the file given. + * + * @access public + * @param string $file + * @return mixed true on success, PEAR_Error on failure + */ + function read($file) + { + $fh = @fopen($file, "r"); + if (!$fh) { + return $this->raiseError("Can't open file $file"); + } + $this->_file_handle = $fh; + + $signature = fread($fh, 8); + if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) { + return $this->raiseError("File doesn't seem to be an OLE container."); + } + fseek($fh, 28); + if (fread($fh, 2) != "\xFE\xFF") { + // This shouldn't be a problem in practice + return $this->raiseError("Only Little-Endian encoding is supported."); + } + // Size of blocks and short blocks in bytes + $this->bigBlockSize = pow(2, $this->_readInt2($fh)); + $this->smallBlockSize = pow(2, $this->_readInt2($fh)); + + // Skip UID, revision number and version number + fseek($fh, 44); + // Number of blocks in Big Block Allocation Table + $bbatBlockCount = $this->_readInt4($fh); + + // Root chain 1st block + $directoryFirstBlockId = $this->_readInt4($fh); + + // Skip unused bytes + fseek($fh, 56); + // Streams shorter than this are stored using small blocks + $this->bigBlockThreshold = $this->_readInt4($fh); + // Block id of first sector in Short Block Allocation Table + $sbatFirstBlockId = $this->_readInt4($fh); + // Number of blocks in Short Block Allocation Table + $sbbatBlockCount = $this->_readInt4($fh); + // Block id of first sector in Master Block Allocation Table + $mbatFirstBlockId = $this->_readInt4($fh); + // Number of blocks in Master Block Allocation Table + $mbbatBlockCount = $this->_readInt4($fh); + $this->bbat = array(); + + // Remaining 4 * 109 bytes of current block is beginning of Master + // Block Allocation Table + $mbatBlocks = array(); + for ($i = 0; $i < 109; $i++) { + $mbatBlocks[] = $this->_readInt4($fh); + } + + // Read rest of Master Block Allocation Table (if any is left) + $pos = $this->_getBlockOffset($mbatFirstBlockId); + for ($i = 0; $i < $mbbatBlockCount; $i++) { + fseek($fh, $pos); + for ($j = 0; $j < $this->bigBlockSize / 4 - 1; $j++) { + $mbatBlocks[] = $this->_readInt4($fh); + } + // Last block id in each block points to next block + $pos = $this->_getBlockOffset($this->_readInt4($fh)); + } + + // Read Big Block Allocation Table according to chain specified by + // $mbatBlocks + for ($i = 0; $i < $bbatBlockCount; $i++) { + $pos = $this->_getBlockOffset($mbatBlocks[$i]); + fseek($fh, $pos); + for ($j = 0 ; $j < $this->bigBlockSize / 4; $j++) { + $this->bbat[] = $this->_readInt4($fh); + } + } + + // Read short block allocation table (SBAT) + $this->sbat = array(); + $shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4; + $sbatFh = $this->getStream($sbatFirstBlockId); + for ($blockId = 0; $blockId < $shortBlockCount; $blockId++) { + $this->sbat[$blockId] = $this->_readInt4($sbatFh); + } + fclose($sbatFh); + + $this->_readPpsWks($directoryFirstBlockId); + + return true; + } + + /** + * @param int $blockId block id + * @return int byte offset from beginning of file + * @access private + */ + function _getBlockOffset($blockId) + { + return 512 + $blockId * $this->bigBlockSize; + } + + /** + * Returns a stream for use with fread() etc. External callers should + * use OLE_PPS_File::getStream(). + * @param int|PPS $blockIdOrPps block id or PPS + * @return resource read-only stream + */ + function getStream($blockIdOrPps) + { + include_once 'OLE/ChainedBlockStream.php'; + static $isRegistered = false; + if (!$isRegistered) { + stream_wrapper_register('ole-chainedblockstream', + 'OLE_ChainedBlockStream'); + $isRegistered = true; + } + + // Store current instance in global array, so that it can be accessed + // in OLE_ChainedBlockStream::stream_open(). + // Object is removed from self::$instances in OLE_Stream::close(). + $GLOBALS['_OLE_INSTANCES'][] = $this; + $instanceId = end(array_keys($GLOBALS['_OLE_INSTANCES'])); + + $path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId; + if (is_a($blockIdOrPps, 'OLE_PPS')) { + $path .= '&blockId=' . $blockIdOrPps->_StartBlock; + $path .= '&size=' . $blockIdOrPps->Size; + } else { + $path .= '&blockId=' . $blockIdOrPps; + } + return fopen($path, 'r'); + } + + /** + * Reads a signed char. + * @param resource $fh file handle + * @return int + * @access private + */ + function _readInt1($fh) + { + list(, $tmp) = unpack("c", fread($fh, 1)); + return $tmp; + } + + /** + * Reads an unsigned short (2 octets). + * @param resource $fh file handle + * @return int + * @access private + */ + function _readInt2($fh) + { + list(, $tmp) = unpack("v", fread($fh, 2)); + return $tmp; + } + + /** + * Reads an unsigned long (4 octets). + * @param resource file handle + * @return int + * @access private + */ + function _readInt4($fh) + { + list(, $tmp) = unpack("V", fread($fh, 4)); + return $tmp; + } + + /** + * Gets information about all PPS's on the OLE container from the PPS WK's + * creates an OLE_PPS object for each one. + * + * @access private + * @param integer $blockId the block id of the first block + * @return mixed true on success, PEAR_Error on failure + */ + function _readPpsWks($blockId) + { + $fh = $this->getStream($blockId); + for ($pos = 0; ; $pos += 128) { + fseek($fh, $pos, SEEK_SET); + $nameUtf16 = fread($fh, 64); + $nameLength = $this->_readInt2($fh); + $nameUtf16 = substr($nameUtf16, 0, $nameLength - 2); + // Simple conversion from UTF-16LE to ISO-8859-1 + $name = str_replace("\x00", "", $nameUtf16); + $type = $this->_readInt1($fh); + switch ($type) { + case OLE_PPS_TYPE_ROOT: + require_once 'OLE/PPS/Root.php'; + $pps = new OLE_PPS_Root(null, null, array()); + $this->root = $pps; + break; + case OLE_PPS_TYPE_DIR: + $pps = new OLE_PPS(null, null, null, null, null, + null, null, null, null, array()); + break; + case OLE_PPS_TYPE_FILE: + require_once 'OLE/PPS/File.php'; + $pps = new OLE_PPS_File($name); + break; + default: + continue; + } + fseek($fh, 1, SEEK_CUR); + $pps->Type = $type; + $pps->Name = $name; + $pps->PrevPps = $this->_readInt4($fh); + $pps->NextPps = $this->_readInt4($fh); + $pps->DirPps = $this->_readInt4($fh); + fseek($fh, 20, SEEK_CUR); + $pps->Time1st = OLE::OLE2LocalDate(fread($fh, 8)); + $pps->Time2nd = OLE::OLE2LocalDate(fread($fh, 8)); + $pps->_StartBlock = $this->_readInt4($fh); + $pps->Size = $this->_readInt4($fh); + $pps->No = count($this->_list); + $this->_list[] = $pps; + + // check if the PPS tree (starting from root) is complete + if (isset($this->root) && + $this->_ppsTreeComplete($this->root->No)) { + + break; + } + } + fclose($fh); + + // Initialize $pps->children on directories + foreach ($this->_list as $pps) { + if ($pps->Type == OLE_PPS_TYPE_DIR || $pps->Type == OLE_PPS_TYPE_ROOT) { + $nos = array($pps->DirPps); + $pps->children = array(); + while ($nos) { + $no = array_pop($nos); + if ($no != -1) { + $childPps = $this->_list[$no]; + $nos[] = $childPps->PrevPps; + $nos[] = $childPps->NextPps; + $pps->children[] = $childPps; + } + } + } + } + + return true; + } + + /** + * It checks whether the PPS tree is complete (all PPS's read) + * starting with the given PPS (not necessarily root) + * + * @access private + * @param integer $index The index of the PPS from which we are checking + * @return boolean Whether the PPS tree for the given PPS is complete + */ + function _ppsTreeComplete($index) + { + return isset($this->_list[$index]) && + ($pps = $this->_list[$index]) && + ($pps->PrevPps == -1 || + $this->_ppsTreeComplete($pps->PrevPps)) && + ($pps->NextPps == -1 || + $this->_ppsTreeComplete($pps->NextPps)) && + ($pps->DirPps == -1 || + $this->_ppsTreeComplete($pps->DirPps)); + } + + /** + * Checks whether a PPS is a File PPS or not. + * If there is no PPS for the index given, it will return false. + * @param integer $index The index for the PPS + * @return bool true if it's a File PPS, false otherwise + * @access public + */ + function isFile($index) + { + if (isset($this->_list[$index])) { + return ($this->_list[$index]->Type == OLE_PPS_TYPE_FILE); + } + return false; + } + + /** + * Checks whether a PPS is a Root PPS or not. + * If there is no PPS for the index given, it will return false. + * @param integer $index The index for the PPS. + * @return bool true if it's a Root PPS, false otherwise + * @access public + */ + function isRoot($index) + { + if (isset($this->_list[$index])) { + return ($this->_list[$index]->Type == OLE_PPS_TYPE_ROOT); + } + return false; + } + + /** + * Gives the total number of PPS's found in the OLE container. + * @return integer The total number of PPS's found in the OLE container + * @access public + */ + function ppsTotal() + { + return count($this->_list); + } + + /** + * Gets data from a PPS + * If there is no PPS for the index given, it will return an empty string. + * @param integer $index The index for the PPS + * @param integer $position The position from which to start reading + * (relative to the PPS) + * @param integer $length The amount of bytes to read (at most) + * @return string The binary string containing the data requested + * @access public + * @see OLE_PPS_File::getStream() + */ + function getData($index, $position, $length) + { + // if position is not valid return empty string + if (!isset($this->_list[$index]) || + $position >= $this->_list[$index]->Size || + $position < 0) { + + return ''; + } + $fh = $this->getStream($this->_list[$index]); + $data = stream_get_contents($fh, $length, $position); + fclose($fh); + return $data; + } + + /** + * Gets the data length from a PPS + * If there is no PPS for the index given, it will return 0. + * @param integer $index The index for the PPS + * @return integer The amount of bytes in data the PPS has + * @access public + */ + function getDataLength($index) + { + if (isset($this->_list[$index])) { + return $this->_list[$index]->Size; + } + return 0; + } + + /** + * Utility function to transform ASCII text to Unicode + * + * @access public + * @static + * @param string $ascii The ASCII string to transform + * @return string The string in Unicode + */ + public static function Asc2Ucs($ascii) + { + $rawname = ''; + for ($i = 0; $i < strlen($ascii); $i++) { + $rawname .= $ascii{$i} . "\x00"; + } + return $rawname; + } + + /** + * Utility function + * Returns a string for the OLE container with the date given + * + * @access public + * @static + * @param integer $date A timestamp + * @return string The string for the OLE container + */ + public static function LocalDate2OLE($date = null) + { + if (!isset($date)) { + return "\x00\x00\x00\x00\x00\x00\x00\x00"; + } + + // factor used for separating numbers into 4 bytes parts + $factor = pow(2, 32); + + // days from 1-1-1601 until the beggining of UNIX era + $days = 134774; + // calculate seconds + $big_date = $days * 24 * 3600 + + gmmktime(date("H",$date),date("i",$date),date("s",$date), + date("m",$date),date("d",$date),date("Y",$date)); + // multiply just to make MS happy + $big_date *= 10000000; + + $high_part = floor($big_date / $factor); + // lower 4 bytes + $low_part = floor((($big_date / $factor) - $high_part) * $factor); + + // Make HEX string + $res = ''; + + for ($i = 0; $i < 4; $i++) { + $hex = $low_part % 0x100; + $res .= pack('c', $hex); + $low_part /= 0x100; + } + for ($i = 0; $i < 4; $i++) { + $hex = $high_part % 0x100; + $res .= pack('c', $hex); + $high_part /= 0x100; + } + return $res; + } + + /** + * Returns a timestamp from an OLE container's date + * @param integer $string A binary string with the encoded date + * @return string The timestamp corresponding to the string + * @access public + * @static + */ + function OLE2LocalDate($string) + { + if (strlen($string) != 8) { + return new PEAR_Error("Expecting 8 byte string"); + } + + // factor used for separating numbers into 4 bytes parts + $factor = pow(2,32); + $high_part = 0; + for ($i = 0; $i < 4; $i++) { + list(, $high_part) = unpack('C', $string{(7 - $i)}); + if ($i < 3) { + $high_part *= 0x100; + } + } + $low_part = 0; + for ($i = 4; $i < 8; $i++) { + list(, $low_part) = unpack('C', $string{(7 - $i)}); + if ($i < 7) { + $low_part *= 0x100; + } + } + $big_date = ($high_part * $factor) + $low_part; + // translate to seconds + $big_date /= 10000000; + + // days from 1-1-1601 until the beggining of UNIX era + $days = 134774; + + // translate to seconds from beggining of UNIX era + $big_date -= $days * 24 * 3600; + return floor($big_date); + } +} +?> diff --git a/functions/PEAR/OLE/PPS.php b/functions/PEAR/OLE/PPS.php new file mode 100755 index 000000000..a4f455487 --- /dev/null +++ b/functions/PEAR/OLE/PPS.php @@ -0,0 +1,222 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +// $Id: PPS.php,v 1.7 2007/02/13 21:00:42 schmidt Exp $ + + +require_once 'PEAR.php'; +require_once 'OLE.php'; + +/** +* Class for creating PPS's for OLE containers +* +* @author Xavier Noguer +* @category Structures +* @package OLE +*/ +class OLE_PPS extends PEAR +{ + /** + * The PPS index + * @var integer + */ + var $No; + + /** + * The PPS name (in Unicode) + * @var string + */ + var $Name; + + /** + * The PPS type. Dir, Root or File + * @var integer + */ + var $Type; + + /** + * The index of the previous PPS + * @var integer + */ + var $PrevPps; + + /** + * The index of the next PPS + * @var integer + */ + var $NextPps; + + /** + * The index of it's first child if this is a Dir or Root PPS + * @var integer + */ + var $DirPps; + + /** + * A timestamp + * @var integer + */ + var $Time1st; + + /** + * A timestamp + * @var integer + */ + var $Time2nd; + + /** + * Starting block (small or big) for this PPS's data inside the container + * @var integer + */ + var $_StartBlock; + + /** + * The size of the PPS's data (in bytes) + * @var integer + */ + var $Size; + + /** + * The PPS's data (only used if it's not using a temporary file) + * @var string + */ + var $_data; + + /** + * Array of child PPS's (only used by Root and Dir PPS's) + * @var array + */ + var $children = array(); + + /** + * Pointer to OLE container + * @var OLE + */ + var $ole; + + /** + * The constructor + * + * @access public + * @param integer $No The PPS index + * @param string $name The PPS name + * @param integer $type The PPS type. Dir, Root or File + * @param integer $prev The index of the previous PPS + * @param integer $next The index of the next PPS + * @param integer $dir The index of it's first child if this is a Dir or Root PPS + * @param integer $time_1st A timestamp + * @param integer $time_2nd A timestamp + * @param string $data The (usually binary) source data of the PPS + * @param array $children Array containing children PPS for this PPS + */ + function OLE_PPS($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children) + { + $this->No = $No; + $this->Name = $name; + $this->Type = $type; + $this->PrevPps = $prev; + $this->NextPps = $next; + $this->DirPps = $dir; + $this->Time1st = $time_1st; + $this->Time2nd = $time_2nd; + $this->_data = $data; + $this->children = $children; + if ($data != '') { + $this->Size = strlen($data); + } else { + $this->Size = 0; + } + } + + /** + * Returns the amount of data saved for this PPS + * + * @access private + * @return integer The amount of data (in bytes) + */ + function _DataLen() + { + if (!isset($this->_data)) { + return 0; + } + if (isset($this->_PPS_FILE)) { + fseek($this->_PPS_FILE, 0); + $stats = fstat($this->_PPS_FILE); + return $stats[7]; + } else { + return strlen($this->_data); + } + } + + /** + * Returns a string with the PPS's WK (What is a WK?) + * + * @access private + * @return string The binary string + */ + function _getPpsWk() + { + $ret = $this->Name; + for ($i = 0; $i < (64 - strlen($this->Name)); $i++) { + $ret .= "\x00"; + } + $ret .= pack("v", strlen($this->Name) + 2) // 66 + . pack("c", $this->Type) // 67 + . pack("c", 0x00) //UK // 68 + . pack("V", $this->PrevPps) //Prev // 72 + . pack("V", $this->NextPps) //Next // 76 + . pack("V", $this->DirPps) //Dir // 80 + . "\x00\x09\x02\x00" // 84 + . "\x00\x00\x00\x00" // 88 + . "\xc0\x00\x00\x00" // 92 + . "\x00\x00\x00\x46" // 96 // Seems to be ok only for Root + . "\x00\x00\x00\x00" // 100 + . OLE::LocalDate2OLE($this->Time1st) // 108 + . OLE::LocalDate2OLE($this->Time2nd) // 116 + . pack("V", isset($this->_StartBlock)? + $this->_StartBlock:0) // 120 + . pack("V", $this->Size) // 124 + . pack("V", 0); // 128 + return $ret; + } + + /** + * Updates index and pointers to previous, next and children PPS's for this + * PPS. I don't think it'll work with Dir PPS's. + * + * @access private + * @param array &$pps_array Reference to the array of PPS's for the whole OLE + * container + * @return integer The index for this PPS + */ + function _savePpsSetPnt(&$pps_array) + { + $pps_array[count($pps_array)] = &$this; + $this->No = count($pps_array) - 1; + $this->PrevPps = 0xFFFFFFFF; + $this->NextPps = 0xFFFFFFFF; + if (count($this->children) > 0) { + $this->DirPps = $this->children[0]->_savePpsSetPnt($pps_array); + } else { + $this->DirPps = 0xFFFFFFFF; + } + return $this->No; + } +} +?> diff --git a/functions/PEAR/OLE/PPS/File.php b/functions/PEAR/OLE/PPS/File.php new file mode 100755 index 000000000..f257779af --- /dev/null +++ b/functions/PEAR/OLE/PPS/File.php @@ -0,0 +1,126 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +// $Id: File.php,v 1.12 2008/02/02 21:00:37 schmidt Exp $ + + +require_once( dirname(__FILE__) . '/../PPS.php'); +require_once 'System.php'; + +/** +* Class for creating File PPS's for OLE containers +* +* @author Xavier Noguer +* @category Structures +* @package OLE +*/ +class OLE_PPS_File extends OLE_PPS +{ + /** + * The temporary dir for storing the OLE file + * @var string + */ + var $_tmp_dir; + + /** + * The constructor + * + * @access public + * @param string $name The name of the file (in Unicode) + * @see OLE::Asc2Ucs() + */ + function OLE_PPS_File($name) + { + $tempDir = new System(); + $this->_tmp_dir = $tempDir->tmpdir(); + $this->OLE_PPS( + null, + $name, + OLE_PPS_TYPE_FILE, + null, + null, + null, + null, + null, + '', + array()); + } + + /** + * Sets the temp dir used for storing the OLE file + * + * @access public + * @param string $dir The dir to be used as temp dir + * @return true if given dir is valid, false otherwise + */ + function setTempDir($dir) + { + if (is_dir($dir)) { + $this->_tmp_dir = $dir; + return true; + } + return false; + } + + /** + * Initialization method. Has to be called right after OLE_PPS_File(). + * + * @access public + * @return mixed true on success. PEAR_Error on failure + */ + function init() + { + $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_File"); + $fh = @fopen($this->_tmp_filename, "w+b"); + if ($fh == false) { + return $this->raiseError("Can't create temporary file"); + } + $this->_PPS_FILE = $fh; + if ($this->_PPS_FILE) { + fseek($this->_PPS_FILE, 0); + } + + return true; + } + + /** + * Append data to PPS + * + * @access public + * @param string $data The data to append + */ + function append($data) + { + if ($this->_PPS_FILE) { + fwrite($this->_PPS_FILE, $data); + } else { + $this->_data .= $data; + } + } + + /** + * Returns a stream for reading this file using fread() etc. + * @return resource a read-only stream + */ + function getStream() + { + $this->ole->getStream($this); + } +} +?> diff --git a/functions/PEAR/OLE/PPS/Root.php b/functions/PEAR/OLE/PPS/Root.php new file mode 100755 index 000000000..42d52a829 --- /dev/null +++ b/functions/PEAR/OLE/PPS/Root.php @@ -0,0 +1,487 @@ + | +// | Based on OLE::Storage_Lite by Kawai, Takanori | +// +----------------------------------------------------------------------+ +// +// $Id: Root.php,v 1.10 2008/02/02 21:00:37 schmidt Exp $ + + +require_once( dirname(__FILE__) . '/../PPS.php'); +require_once 'System.php'; + +/** +* Class for creating Root PPS's for OLE containers +* +* @author Xavier Noguer +* @category Structures +* @package OLE +*/ +class OLE_PPS_Root extends OLE_PPS +{ + /** + * The temporary dir for storing the OLE file + * @var string + */ + var $_tmp_dir; + + /** + * Constructor + * + * @access public + * @param integer $time_1st A timestamp + * @param integer $time_2nd A timestamp + */ + function OLE_PPS_Root($time_1st, $time_2nd, $raChild) + { + $tempDir = new System(); + $this->_tmp_dir = $tempDir->tmpdir(); + $this->OLE_PPS( + null, + OLE::Asc2Ucs('Root Entry'), + OLE_PPS_TYPE_ROOT, + null, + null, + null, + $time_1st, + $time_2nd, + null, + $raChild); + } + + /** + * Sets the temp dir used for storing the OLE file + * + * @access public + * @param string $dir The dir to be used as temp dir + * @return true if given dir is valid, false otherwise + */ + function setTempDir($dir) + { + if (is_dir($dir)) { + $this->_tmp_dir = $dir; + return true; + } + return false; + } + + /** + * Method for saving the whole OLE container (including files). + * In fact, if called with an empty argument (or '-'), it saves to a + * temporary file and then outputs it's contents to stdout. + * + * @param string $filename The name of the file where to save the OLE container + * @access public + * @return mixed true on success, PEAR_Error on failure + */ + function save($filename) + { + // Initial Setting for saving + $this->_BIG_BLOCK_SIZE = pow(2, + ((isset($this->_BIG_BLOCK_SIZE))? $this->_adjust2($this->_BIG_BLOCK_SIZE) : 9)); + $this->_SMALL_BLOCK_SIZE= pow(2, + ((isset($this->_SMALL_BLOCK_SIZE))? $this->_adjust2($this->_SMALL_BLOCK_SIZE): 6)); + + // Open temp file if we are sending output to stdout + if (($filename == '-') || ($filename == '')) { + $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root"); + $this->_FILEH_ = @fopen($this->_tmp_filename,"w+b"); + if ($this->_FILEH_ == false) { + return $this->raiseError("Can't create temporary file."); + } + } else { + $this->_FILEH_ = @fopen($filename, "wb"); + if ($this->_FILEH_ == false) { + return $this->raiseError("Can't open $filename. It may be in use or protected."); + } + } + // Make an array of PPS's (for Save) + $aList = array(); + $this->_savePpsSetPnt($aList); + // calculate values for header + list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo); + // Save Header + $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt); + + // Make Small Data string (write SBD) + $this->_data = $this->_makeSmallData($aList); + + // Write BB + $this->_saveBigData($iSBDcnt, $aList); + // Write PPS + $this->_savePps($aList); + // Write Big Block Depot and BDList and Adding Header informations + $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt); + // Close File, send it to stdout if necessary + if (($filename == '-') || ($filename == '')) { + fseek($this->_FILEH_, 0); + fpassthru($this->_FILEH_); + @fclose($this->_FILEH_); + // Delete the temporary file. + @unlink($this->_tmp_filename); + } else { + @fclose($this->_FILEH_); + } + + return true; + } + + /** + * Calculate some numbers + * + * @access private + * @param array $raList Reference to an array of PPS's + * @return array The array of numbers + */ + function _calcSize(&$raList) + { + // Calculate Basic Setting + list($iSBDcnt, $iBBcnt, $iPPScnt) = array(0,0,0); + $iSmallLen = 0; + $iSBcnt = 0; + for ($i = 0; $i < count($raList); $i++) { + if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) { + $raList[$i]->Size = $raList[$i]->_DataLen(); + if ($raList[$i]->Size < OLE_DATA_SIZE_SMALL) { + $iSBcnt += floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) + + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); + } else { + $iBBcnt += (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + + (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); + } + } + } + $iSmallLen = $iSBcnt * $this->_SMALL_BLOCK_SIZE; + $iSlCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE); + $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt)? 1:0); + $iBBcnt += (floor($iSmallLen / $this->_BIG_BLOCK_SIZE) + + (( $iSmallLen % $this->_BIG_BLOCK_SIZE)? 1: 0)); + $iCnt = count($raList); + $iBdCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE; + $iPPScnt = (floor($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0)); + + return array($iSBDcnt, $iBBcnt, $iPPScnt); + } + + /** + * Helper function for caculating a magic value for block sizes + * + * @access private + * @param integer $i2 The argument + * @see save() + * @return integer + */ + function _adjust2($i2) + { + $iWk = log($i2)/log(2); + return ($iWk > floor($iWk))? floor($iWk)+1:$iWk; + } + + /** + * Save OLE header + * + * @access private + * @param integer $iSBDcnt + * @param integer $iBBcnt + * @param integer $iPPScnt + */ + function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt) + { + $FILE = $this->_FILEH_; + + // Calculate Basic Setting + $iBlCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; + $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE; + + $iBdExL = 0; + $iAll = $iBBcnt + $iPPScnt + $iSBDcnt; + $iAllW = $iAll; + $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); + $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); + + // Calculate BD count + if ($iBdCnt > $i1stBdL) { + while (1) { + $iBdExL++; + $iAllW++; + $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); + $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); + if ($iBdCnt <= ($iBdExL*$iBlCnt+ $i1stBdL)) { + break; + } + } + } + + // Save Header + fwrite($FILE, + "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" + . "\x00\x00\x00\x00" + . "\x00\x00\x00\x00" + . "\x00\x00\x00\x00" + . "\x00\x00\x00\x00" + . pack("v", 0x3b) + . pack("v", 0x03) + . pack("v", -2) + . pack("v", 9) + . pack("v", 6) + . pack("v", 0) + . "\x00\x00\x00\x00" + . "\x00\x00\x00\x00" + . pack("V", $iBdCnt) + . pack("V", $iBBcnt+$iSBDcnt) //ROOT START + . pack("V", 0) + . pack("V", 0x1000) + . pack("V", 0) //Small Block Depot + . pack("V", 1) + ); + // Extra BDList Start, Count + if ($iBdCnt < $i1stBdL) { + fwrite($FILE, + pack("V", -2). // Extra BDList Start + pack("V", 0) // Extra BDList Count + ); + } else { + fwrite($FILE, pack("V", $iAll+$iBdCnt) . pack("V", $iBdExL)); + } + + // BDList + for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; $i++) { + fwrite($FILE, pack("V", $iAll+$i)); + } + if ($i < $i1stBdL) { + for ($j = 0; $j < ($i1stBdL-$i); $j++) { + fwrite($FILE, (pack("V", -1))); + } + } + } + + /** + * Saving big data (PPS's with data bigger than OLE_DATA_SIZE_SMALL) + * + * @access private + * @param integer $iStBlk + * @param array &$raList Reference to array of PPS's + */ + function _saveBigData($iStBlk, &$raList) + { + $FILE = $this->_FILEH_; + + // cycle through PPS's + for ($i = 0; $i < count($raList); $i++) { + if ($raList[$i]->Type != OLE_PPS_TYPE_DIR) { + $raList[$i]->Size = $raList[$i]->_DataLen(); + if (($raList[$i]->Size >= OLE_DATA_SIZE_SMALL) || + (($raList[$i]->Type == OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) + { + // Write Data + if (isset($raList[$i]->_PPS_FILE)) { + $iLen = 0; + fseek($raList[$i]->_PPS_FILE, 0); // To The Top + while($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) { + $iLen += strlen($sBuff); + fwrite($FILE, $sBuff); + } + } else { + fwrite($FILE, $raList[$i]->_data); + } + + if ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE) { + for ($j = 0; $j < ($this->_BIG_BLOCK_SIZE - ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)); $j++) { + fwrite($FILE, "\x00"); + } + } + // Set For PPS + $raList[$i]->_StartBlock = $iStBlk; + $iStBlk += + (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + + (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); + } + // Close file for each PPS, and unlink it + if (isset($raList[$i]->_PPS_FILE)) { + @fclose($raList[$i]->_PPS_FILE); + $raList[$i]->_PPS_FILE = null; + @unlink($raList[$i]->_tmp_filename); + } + } + } + } + + /** + * get small data (PPS's with data smaller than OLE_DATA_SIZE_SMALL) + * + * @access private + * @param array &$raList Reference to array of PPS's + */ + function _makeSmallData(&$raList) + { + $sRes = ''; + $FILE = $this->_FILEH_; + $iSmBlk = 0; + + for ($i = 0; $i < count($raList); $i++) { + // Make SBD, small data string + if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) { + if ($raList[$i]->Size <= 0) { + continue; + } + if ($raList[$i]->Size < OLE_DATA_SIZE_SMALL) { + $iSmbCnt = floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) + + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); + // Add to SBD + for ($j = 0; $j < ($iSmbCnt-1); $j++) { + fwrite($FILE, pack("V", $j+$iSmBlk+1)); + } + fwrite($FILE, pack("V", -2)); + + // Add to Data String(this will be written for RootEntry) + if ($raList[$i]->_PPS_FILE) { + fseek($raList[$i]->_PPS_FILE, 0); // To The Top + while ($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) { + $sRes .= $sBuff; + } + } else { + $sRes .= $raList[$i]->_data; + } + if ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE) { + for ($j = 0; $j < ($this->_SMALL_BLOCK_SIZE - ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)); $j++) { + $sRes .= "\x00"; + } + } + // Set for PPS + $raList[$i]->_StartBlock = $iSmBlk; + $iSmBlk += $iSmbCnt; + } + } + } + $iSbCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE); + if ($iSmBlk % $iSbCnt) { + for ($i = 0; $i < ($iSbCnt - ($iSmBlk % $iSbCnt)); $i++) { + fwrite($FILE, pack("V", -1)); + } + } + return $sRes; + } + + /** + * Saves all the PPS's WKs + * + * @access private + * @param array $raList Reference to an array with all PPS's + */ + function _savePps(&$raList) + { + // Save each PPS WK + for ($i = 0; $i < count($raList); $i++) { + fwrite($this->_FILEH_, $raList[$i]->_getPpsWk()); + } + // Adjust for Block + $iCnt = count($raList); + $iBCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE; + if ($iCnt % $iBCnt) { + for ($i = 0; $i < (($iBCnt - ($iCnt % $iBCnt)) * OLE_PPS_SIZE); $i++) { + fwrite($this->_FILEH_, "\x00"); + } + } + } + + /** + * Saving Big Block Depot + * + * @access private + * @param integer $iSbdSize + * @param integer $iBsize + * @param integer $iPpsCnt + */ + function _saveBbd($iSbdSize, $iBsize, $iPpsCnt) + { + $FILE = $this->_FILEH_; + // Calculate Basic Setting + $iBbCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; + $i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE; + + $iBdExL = 0; + $iAll = $iBsize + $iPpsCnt + $iSbdSize; + $iAllW = $iAll; + $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); + $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0); + // Calculate BD count + if ($iBdCnt >$i1stBdL) { + while (1) { + $iBdExL++; + $iAllW++; + $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); + $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0); + if ($iBdCnt <= ($iBdExL*$iBbCnt+ $i1stBdL)) { + break; + } + } + } + + // Making BD + // Set for SBD + if ($iSbdSize > 0) { + for ($i = 0; $i < ($iSbdSize - 1); $i++) { + fwrite($FILE, pack("V", $i+1)); + } + fwrite($FILE, pack("V", -2)); + } + // Set for B + for ($i = 0; $i < ($iBsize - 1); $i++) { + fwrite($FILE, pack("V", $i+$iSbdSize+1)); + } + fwrite($FILE, pack("V", -2)); + + // Set for PPS + for ($i = 0; $i < ($iPpsCnt - 1); $i++) { + fwrite($FILE, pack("V", $i+$iSbdSize+$iBsize+1)); + } + fwrite($FILE, pack("V", -2)); + // Set for BBD itself ( 0xFFFFFFFD : BBD) + for ($i = 0; $i < $iBdCnt; $i++) { + fwrite($FILE, pack("V", 0xFFFFFFFD)); + } + // Set for ExtraBDList + for ($i = 0; $i < $iBdExL; $i++) { + fwrite($FILE, pack("V", 0xFFFFFFFC)); + } + // Adjust for Block + if (($iAllW + $iBdCnt) % $iBbCnt) { + for ($i = 0; $i < ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)); $i++) { + fwrite($FILE, pack("V", -1)); + } + } + // Extra BDList + if ($iBdCnt > $i1stBdL) { + $iN=0; + $iNb=0; + for ($i = $i1stBdL;$i < $iBdCnt; $i++, $iN++) { + if ($iN >= ($iBbCnt - 1)) { + $iN = 0; + $iNb++; + fwrite($FILE, pack("V", $iAll+$iBdCnt+$iNb)); + } + fwrite($FILE, pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i)); + } + if (($iBdCnt-$i1stBdL) % ($iBbCnt-1)) { + for ($i = 0; $i < (($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1))); $i++) { + fwrite($FILE, pack("V", -1)); + } + } + fwrite($FILE, pack("V", -2)); + } + } +} +?> diff --git a/functions/PEAR/Spreadsheet/Excel/Writer.php b/functions/PEAR/Spreadsheet/Excel/Writer.php new file mode 100755 index 000000000..5cf177836 --- /dev/null +++ b/functions/PEAR/Spreadsheet/Excel/Writer.php @@ -0,0 +1,105 @@ + +* +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +require_once 'PEAR.php'; +require_once( dirname(__FILE__) . '/Writer/Workbook.php'); + + +/** +* Class for writing Excel Spreadsheets. This class should change COMPLETELY. +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer extends Spreadsheet_Excel_Writer_Workbook +{ + /** + * The constructor. It just creates a Workbook + * + * @param string $filename The optional filename for the Workbook. + * @return Spreadsheet_Excel_Writer_Workbook The Workbook created + */ + function Spreadsheet_Excel_Writer($filename = '') + { + $this->_filename = $filename; + $this->Spreadsheet_Excel_Writer_Workbook($filename); + } + + /** + * Send HTTP headers for the Excel file. + * + * @param string $filename The filename to use for HTTP headers + * @access public + */ + function send($filename) + { + header("Content-type: application/vnd.ms-excel"); + header("Content-Disposition: attachment; filename=\"$filename\""); + header("Expires: 0"); + header("Cache-Control: must-revalidate, post-check=0,pre-check=0"); + header("Pragma: public"); + } + + /** + * Utility function for writing formulas + * Converts a cell's coordinates to the A1 format. + * + * @access public + * @static + * @param integer $row Row for the cell to convert (0-indexed). + * @param integer $col Column for the cell to convert (0-indexed). + * @return string The cell identifier in A1 format + */ + function rowcolToCell($row, $col) + { + if ($col > 255) { //maximum column value exceeded + return new PEAR_Error("Maximum column value exceeded: $col"); + } + + $int = (int)($col / 26); + $frac = $col % 26; + $chr1 = ''; + + if ($int > 0) { + $chr1 = chr(ord('A') + $int - 1); + } + + $chr2 = chr(ord('A') + $frac); + $row++; + + return $chr1 . $chr2 . $row; + } +} +?> diff --git a/functions/PEAR/Spreadsheet/Excel/Writer/BIFFwriter.php b/functions/PEAR/Spreadsheet/Excel/Writer/BIFFwriter.php new file mode 100755 index 000000000..2e129f961 --- /dev/null +++ b/functions/PEAR/Spreadsheet/Excel/Writer/BIFFwriter.php @@ -0,0 +1,261 @@ + +* +* The majority of this is _NOT_ my code. I simply ported it from the +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@php.net +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +require_once 'PEAR.php'; + +/** +* Class for writing Excel BIFF records. +* +* From "MICROSOFT EXCEL BINARY FILE FORMAT" by Mark O'Brien (Microsoft Corporation): +* +* BIFF (BInary File Format) is the file format in which Excel documents are +* saved on disk. A BIFF file is a complete description of an Excel document. +* BIFF files consist of sequences of variable-length records. There are many +* different types of BIFF records. For example, one record type describes a +* formula entered into a cell; one describes the size and location of a +* window into a document; another describes a picture format. +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_BIFFwriter extends PEAR +{ + /** + * The BIFF/Excel version (5). + * @var integer + */ + var $_BIFF_version = 0x0500; + + /** + * The byte order of this architecture. 0 => little endian, 1 => big endian + * @var integer + */ + var $_byte_order; + + /** + * The string containing the data of the BIFF stream + * @var string + */ + var $_data; + + /** + * The size of the data in bytes. Should be the same as strlen($this->_data) + * @var integer + */ + var $_datasize; + + /** + * The maximun length for a BIFF record. See _addContinue() + * @var integer + * @see _addContinue() + */ + var $_limit; + + /** + * The temporary dir for storing the OLE file + * @var string + */ + var $_tmp_dir; + + /** + * Constructor + * + * @access public + */ + function Spreadsheet_Excel_Writer_BIFFwriter() + { + $this->_byte_order = ''; + $this->_data = ''; + $this->_datasize = 0; + $this->_limit = 2080; + $this->_tmp_dir = ''; + // Set the byte order + $this->_setByteOrder(); + } + + /** + * Determine the byte order and store it as class data to avoid + * recalculating it for each call to new(). + * + * @access private + */ + function _setByteOrder() + { + // Check if "pack" gives the required IEEE 64bit float + $teststr = pack("d", 1.2345); + $number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); + if ($number == $teststr) { + $byte_order = 0; // Little Endian + } elseif ($number == strrev($teststr)){ + $byte_order = 1; // Big Endian + } else { + // Give up. I'll fix this in a later version. + return $this->raiseError("Required floating point format ". + "not supported on this platform."); + } + $this->_byte_order = $byte_order; + } + + /** + * General storage function + * + * @param string $data binary data to prepend + * @access private + */ + function _prepend($data) + { + if (strlen($data) > $this->_limit) { + $data = $this->_addContinue($data); + } + $this->_data = $data.$this->_data; + $this->_datasize += strlen($data); + } + + /** + * General storage function + * + * @param string $data binary data to append + * @access private + */ + function _append($data) + { + if (strlen($data) > $this->_limit) { + $data = $this->_addContinue($data); + } + $this->_data = $this->_data.$data; + $this->_datasize += strlen($data); + } + + /** + * Writes Excel BOF record to indicate the beginning of a stream or + * sub-stream in the BIFF file. + * + * @param integer $type Type of BIFF file to write: 0x0005 Workbook, + * 0x0010 Worksheet. + * @access private + */ + function _storeBof($type) + { + $record = 0x0809; // Record identifier + + // According to the SDK $build and $year should be set to zero. + // However, this throws a warning in Excel 5. So, use magic numbers. + if ($this->_BIFF_version == 0x0500) { + $length = 0x0008; + $unknown = ''; + $build = 0x096C; + $year = 0x07C9; + } elseif ($this->_BIFF_version == 0x0600) { + $length = 0x0010; + $unknown = pack("VV", 0x00000041, 0x00000006); //unknown last 8 bytes for BIFF8 + $build = 0x0DBB; + $year = 0x07CC; + } + $version = $this->_BIFF_version; + + $header = pack("vv", $record, $length); + $data = pack("vvvv", $version, $type, $build, $year); + $this->_prepend($header . $data . $unknown); + } + + /** + * Writes Excel EOF record to indicate the end of a BIFF stream. + * + * @access private + */ + function _storeEof() + { + $record = 0x000A; // Record identifier + $length = 0x0000; // Number of bytes to follow + $header = pack("vv", $record, $length); + $this->_append($header); + } + + /** + * Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In + * Excel 97 the limit is 8228 bytes. Records that are longer than these limits + * must be split up into CONTINUE blocks. + * + * This function takes a long BIFF record and inserts CONTINUE records as + * necessary. + * + * @param string $data The original binary data to be written + * @return string A very convenient string of continue blocks + * @access private + */ + function _addContinue($data) + { + $limit = $this->_limit; + $record = 0x003C; // Record identifier + + // The first 2080/8224 bytes remain intact. However, we have to change + // the length field of the record. + $tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4); + + $header = pack("vv", $record, $limit); // Headers for continue records + + // Retrieve chunks of 2080/8224 bytes +4 for the header. + $data_length = strlen($data); + for ($i = $limit; $i < ($data_length - $limit); $i += $limit) { + $tmp .= $header; + $tmp .= substr($data, $i, $limit); + } + + // Retrieve the last chunk of data + $header = pack("vv", $record, strlen($data) - $i); + $tmp .= $header; + $tmp .= substr($data, $i, strlen($data) - $i); + + return $tmp; + } + + /** + * Sets the temp dir used for storing the OLE file + * + * @access public + * @param string $dir The dir to be used as temp dir + * @return true if given dir is valid, false otherwise + */ + function setTempDir($dir) + { + if (is_dir($dir)) { + $this->_tmp_dir = $dir; + return true; + } + return false; + } +} +?> diff --git a/functions/PEAR/Spreadsheet/Excel/Writer/Format.php b/functions/PEAR/Spreadsheet/Excel/Writer/Format.php new file mode 100755 index 000000000..2783769e0 --- /dev/null +++ b/functions/PEAR/Spreadsheet/Excel/Writer/Format.php @@ -0,0 +1,1114 @@ + +* +* The majority of this is _NOT_ my code. I simply ported it from the +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +require_once 'PEAR.php'; + +/** +* Class for generating Excel XF records (formats) +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_Format extends PEAR +{ + /** + * The index given by the workbook when creating a new format. + * @var integer + */ + var $_xf_index; + + /** + * Index to the FONT record. + * @var integer + */ + var $font_index; + + /** + * The font name (ASCII). + * @var string + */ + var $_font_name; + + /** + * Height of font (1/20 of a point) + * @var integer + */ + var $_size; + + /** + * Bold style + * @var integer + */ + var $_bold; + + /** + * Bit specifiying if the font is italic. + * @var integer + */ + var $_italic; + + /** + * Index to the cell's color + * @var integer + */ + var $_color; + + /** + * The text underline property + * @var integer + */ + var $_underline; + + /** + * Bit specifiying if the font has strikeout. + * @var integer + */ + var $_font_strikeout; + + /** + * Bit specifiying if the font has outline. + * @var integer + */ + var $_font_outline; + + /** + * Bit specifiying if the font has shadow. + * @var integer + */ + var $_font_shadow; + + /** + * 2 bytes specifiying the script type for the font. + * @var integer + */ + var $_font_script; + + /** + * Byte specifiying the font family. + * @var integer + */ + var $_font_family; + + /** + * Byte specifiying the font charset. + * @var integer + */ + var $_font_charset; + + /** + * An index (2 bytes) to a FORMAT record (number format). + * @var integer + */ + var $_num_format; + + /** + * Bit specifying if formulas are hidden. + * @var integer + */ + var $_hidden; + + /** + * Bit specifying if the cell is locked. + * @var integer + */ + var $_locked; + + /** + * The three bits specifying the text horizontal alignment. + * @var integer + */ + var $_text_h_align; + + /** + * Bit specifying if the text is wrapped at the right border. + * @var integer + */ + var $_text_wrap; + + /** + * The three bits specifying the text vertical alignment. + * @var integer + */ + var $_text_v_align; + + /** + * 1 bit, apparently not used. + * @var integer + */ + var $_text_justlast; + + /** + * The two bits specifying the text rotation. + * @var integer + */ + var $_rotation; + + /** + * The cell's foreground color. + * @var integer + */ + var $_fg_color; + + /** + * The cell's background color. + * @var integer + */ + var $_bg_color; + + /** + * The cell's background fill pattern. + * @var integer + */ + var $_pattern; + + /** + * Style of the bottom border of the cell + * @var integer + */ + var $_bottom; + + /** + * Color of the bottom border of the cell. + * @var integer + */ + var $_bottom_color; + + /** + * Style of the top border of the cell + * @var integer + */ + var $_top; + + /** + * Color of the top border of the cell. + * @var integer + */ + var $_top_color; + + /** + * Style of the left border of the cell + * @var integer + */ + var $_left; + + /** + * Color of the left border of the cell. + * @var integer + */ + var $_left_color; + + /** + * Style of the right border of the cell + * @var integer + */ + var $_right; + + /** + * Color of the right border of the cell. + * @var integer + */ + var $_right_color; + + /** + * Constructor + * + * @access private + * @param integer $index the XF index for the format. + * @param array $properties array with properties to be set on initialization. + */ + function Spreadsheet_Excel_Writer_Format($BIFF_version, $index = 0, $properties = array()) + { + $this->_xf_index = $index; + $this->_BIFF_version = $BIFF_version; + $this->font_index = 0; + $this->_font_name = 'Arial'; + $this->_size = 10; + $this->_bold = 0x0190; + $this->_italic = 0; + $this->_color = 0x7FFF; + $this->_underline = 0; + $this->_font_strikeout = 0; + $this->_font_outline = 0; + $this->_font_shadow = 0; + $this->_font_script = 0; + $this->_font_family = 0; + $this->_font_charset = 0; + + $this->_num_format = 0; + + $this->_hidden = 0; + $this->_locked = 0; + + $this->_text_h_align = 0; + $this->_text_wrap = 0; + $this->_text_v_align = 2; + $this->_text_justlast = 0; + $this->_rotation = 0; + + $this->_fg_color = 0x40; + $this->_bg_color = 0x41; + + $this->_pattern = 0; + + $this->_bottom = 0; + $this->_top = 0; + $this->_left = 0; + $this->_right = 0; + $this->_diag = 0; + + $this->_bottom_color = 0x40; + $this->_top_color = 0x40; + $this->_left_color = 0x40; + $this->_right_color = 0x40; + $this->_diag_color = 0x40; + + // Set properties passed to Spreadsheet_Excel_Writer_Workbook::addFormat() + foreach ($properties as $property => $value) + { + if (method_exists($this, 'set'.ucwords($property))) { + $method_name = 'set'.ucwords($property); + $this->$method_name($value); + } + } + } + + + /** + * Generate an Excel BIFF XF record (style or cell). + * + * @param string $style The type of the XF record ('style' or 'cell'). + * @return string The XF record + */ + function getXf($style) + { + // Set the type of the XF record and some of the attributes. + if ($style == 'style') { + $style = 0xFFF5; + } else { + $style = $this->_locked; + $style |= $this->_hidden << 1; + } + + // Flags to indicate if attributes have been set. + $atr_num = ($this->_num_format != 0)?1:0; + $atr_fnt = ($this->font_index != 0)?1:0; + $atr_alc = ($this->_text_wrap)?1:0; + $atr_bdr = ($this->_bottom || + $this->_top || + $this->_left || + $this->_right)?1:0; + $atr_pat = (($this->_fg_color != 0x40) || + ($this->_bg_color != 0x41) || + $this->_pattern)?1:0; + $atr_prot = $this->_locked | $this->_hidden; + + // Zero the default border colour if the border has not been set. + if ($this->_bottom == 0) { + $this->_bottom_color = 0; + } + if ($this->_top == 0) { + $this->_top_color = 0; + } + if ($this->_right == 0) { + $this->_right_color = 0; + } + if ($this->_left == 0) { + $this->_left_color = 0; + } + if ($this->_diag == 0) { + $this->_diag_color = 0; + } + + $record = 0x00E0; // Record identifier + if ($this->_BIFF_version == 0x0500) { + $length = 0x0010; // Number of bytes to follow + } + if ($this->_BIFF_version == 0x0600) { + $length = 0x0014; + } + + $ifnt = $this->font_index; // Index to FONT record + $ifmt = $this->_num_format; // Index to FORMAT record + if ($this->_BIFF_version == 0x0500) { + $align = $this->_text_h_align; // Alignment + $align |= $this->_text_wrap << 3; + $align |= $this->_text_v_align << 4; + $align |= $this->_text_justlast << 7; + $align |= $this->_rotation << 8; + $align |= $atr_num << 10; + $align |= $atr_fnt << 11; + $align |= $atr_alc << 12; + $align |= $atr_bdr << 13; + $align |= $atr_pat << 14; + $align |= $atr_prot << 15; + + $icv = $this->_fg_color; // fg and bg pattern colors + $icv |= $this->_bg_color << 7; + + $fill = $this->_pattern; // Fill and border line style + $fill |= $this->_bottom << 6; + $fill |= $this->_bottom_color << 9; + + $border1 = $this->_top; // Border line style and color + $border1 |= $this->_left << 3; + $border1 |= $this->_right << 6; + $border1 |= $this->_top_color << 9; + + $border2 = $this->_left_color; // Border color + $border2 |= $this->_right_color << 7; + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvv", $ifnt, $ifmt, $style, $align, + $icv, $fill, + $border1, $border2); + } elseif ($this->_BIFF_version == 0x0600) { + $align = $this->_text_h_align; // Alignment + $align |= $this->_text_wrap << 3; + $align |= $this->_text_v_align << 4; + $align |= $this->_text_justlast << 7; + + $used_attrib = $atr_num << 2; + $used_attrib |= $atr_fnt << 3; + $used_attrib |= $atr_alc << 4; + $used_attrib |= $atr_bdr << 5; + $used_attrib |= $atr_pat << 6; + $used_attrib |= $atr_prot << 7; + + $icv = $this->_fg_color; // fg and bg pattern colors + $icv |= $this->_bg_color << 7; + + $border1 = $this->_left; // Border line style and color + $border1 |= $this->_right << 4; + $border1 |= $this->_top << 8; + $border1 |= $this->_bottom << 12; + $border1 |= $this->_left_color << 16; + $border1 |= $this->_right_color << 23; + $diag_tl_to_rb = 0; // FIXME: add method + $diag_tr_to_lb = 0; // FIXME: add method + $border1 |= $diag_tl_to_rb << 30; + $border1 |= $diag_tr_to_lb << 31; + + $border2 = $this->_top_color; // Border color + $border2 |= $this->_bottom_color << 7; + $border2 |= $this->_diag_color << 14; + $border2 |= $this->_diag << 21; + $border2 |= $this->_pattern << 26; + + $header = pack("vv", $record, $length); + + $rotation = $this->_rotation; + $biff8_options = 0x00; + $data = pack("vvvC", $ifnt, $ifmt, $style, $align); + $data .= pack("CCC", $rotation, $biff8_options, $used_attrib); + $data .= pack("VVv", $border1, $border2, $icv); + } + + return($header . $data); + } + + /** + * Generate an Excel BIFF FONT record. + * + * @return string The FONT record + */ + function getFont() + { + $dyHeight = $this->_size * 20; // Height of font (1/20 of a point) + $icv = $this->_color; // Index to color palette + $bls = $this->_bold; // Bold style + $sss = $this->_font_script; // Superscript/subscript + $uls = $this->_underline; // Underline + $bFamily = $this->_font_family; // Font family + $bCharSet = $this->_font_charset; // Character set + $encoding = 0; // TODO: Unicode support + + $cch = strlen($this->_font_name); // Length of font name + $record = 0x31; // Record identifier + if ($this->_BIFF_version == 0x0500) { + $length = 0x0F + $cch; // Record length + } elseif ($this->_BIFF_version == 0x0600) { + $length = 0x10 + $cch; + } + $reserved = 0x00; // Reserved + $grbit = 0x00; // Font attributes + if ($this->_italic) { + $grbit |= 0x02; + } + if ($this->_font_strikeout) { + $grbit |= 0x08; + } + if ($this->_font_outline) { + $grbit |= 0x10; + } + if ($this->_font_shadow) { + $grbit |= 0x20; + } + + $header = pack("vv", $record, $length); + if ($this->_BIFF_version == 0x0500) { + $data = pack("vvvvvCCCCC", $dyHeight, $grbit, $icv, $bls, + $sss, $uls, $bFamily, + $bCharSet, $reserved, $cch); + } elseif ($this->_BIFF_version == 0x0600) { + $data = pack("vvvvvCCCCCC", $dyHeight, $grbit, $icv, $bls, + $sss, $uls, $bFamily, + $bCharSet, $reserved, $cch, $encoding); + } + return($header . $data . $this->_font_name); + } + + /** + * Returns a unique hash key for a font. + * Used by Spreadsheet_Excel_Writer_Workbook::_storeAllFonts() + * + * The elements that form the key are arranged to increase the probability of + * generating a unique key. Elements that hold a large range of numbers + * (eg. _color) are placed between two binary elements such as _italic + * + * @return string A key for this font + */ + function getFontKey() + { + $key = "$this->_font_name$this->_size"; + $key .= "$this->_font_script$this->_underline"; + $key .= "$this->_font_strikeout$this->_bold$this->_font_outline"; + $key .= "$this->_font_family$this->_font_charset"; + $key .= "$this->_font_shadow$this->_color$this->_italic"; + $key = str_replace(' ', '_', $key); + return ($key); + } + + /** + * Returns the index used by Spreadsheet_Excel_Writer_Worksheet::_XF() + * + * @return integer The index for the XF record + */ + function getXfIndex() + { + return($this->_xf_index); + } + + /** + * Used in conjunction with the set_xxx_color methods to convert a color + * string into a number. Color range is 0..63 but we will restrict it + * to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15. + * + * @access private + * @param string $name_color name of the color (i.e.: 'blue', 'red', etc..). Optional. + * @return integer The color index + */ + function _getColor($name_color = '') + { + $colors = array( + 'aqua' => 0x0F, + 'cyan' => 0x0F, + 'black' => 0x08, + 'blue' => 0x0C, + 'brown' => 0x10, + 'magenta' => 0x0E, + 'fuchsia' => 0x0E, + 'gray' => 0x17, + 'grey' => 0x17, + 'green' => 0x11, + 'lime' => 0x0B, + 'navy' => 0x12, + 'orange' => 0x35, + 'purple' => 0x14, + 'red' => 0x0A, + 'silver' => 0x16, + 'white' => 0x09, + 'yellow' => 0x0D + ); + + // Return the default color, 0x7FFF, if undef, + if ($name_color == '') { + return(0x7FFF); + } + + // or the color string converted to an integer, + if (isset($colors[$name_color])) { + return($colors[$name_color]); + } + + // or the default color if string is unrecognised, + if (preg_match("/\D/",$name_color)) { + return(0x7FFF); + } + + // or an index < 8 mapped into the correct range, + if ($name_color < 8) { + return($name_color + 8); + } + + // or the default color if arg is outside range, + if ($name_color > 63) { + return(0x7FFF); + } + + // or an integer in the valid range + return($name_color); + } + + /** + * Set cell alignment. + * + * @access public + * @param string $location alignment for the cell ('left', 'right', etc...). + */ + function setAlign($location) + { + if (preg_match("/\d/",$location)) { + return; // Ignore numbers + } + + $location = strtolower($location); + + if ($location == 'left') { + $this->_text_h_align = 1; + } + if ($location == 'centre') { + $this->_text_h_align = 2; + } + if ($location == 'center') { + $this->_text_h_align = 2; + } + if ($location == 'right') { + $this->_text_h_align = 3; + } + if ($location == 'fill') { + $this->_text_h_align = 4; + } + if ($location == 'justify') { + $this->_text_h_align = 5; + } + if ($location == 'merge') { + $this->_text_h_align = 6; + } + if ($location == 'equal_space') { // For T.K. + $this->_text_h_align = 7; + } + if ($location == 'top') { + $this->_text_v_align = 0; + } + if ($location == 'vcentre') { + $this->_text_v_align = 1; + } + if ($location == 'vcenter') { + $this->_text_v_align = 1; + } + if ($location == 'bottom') { + $this->_text_v_align = 2; + } + if ($location == 'vjustify') { + $this->_text_v_align = 3; + } + if ($location == 'vequal_space') { // For T.K. + $this->_text_v_align = 4; + } + } + + /** + * Set cell horizontal alignment. + * + * @access public + * @param string $location alignment for the cell ('left', 'right', etc...). + */ + function setHAlign($location) + { + if (preg_match("/\d/",$location)) { + return; // Ignore numbers + } + + $location = strtolower($location); + + if ($location == 'left') { + $this->_text_h_align = 1; + } + if ($location == 'centre') { + $this->_text_h_align = 2; + } + if ($location == 'center') { + $this->_text_h_align = 2; + } + if ($location == 'right') { + $this->_text_h_align = 3; + } + if ($location == 'fill') { + $this->_text_h_align = 4; + } + if ($location == 'justify') { + $this->_text_h_align = 5; + } + if ($location == 'merge') { + $this->_text_h_align = 6; + } + if ($location == 'equal_space') { // For T.K. + $this->_text_h_align = 7; + } + } + + /** + * Set cell vertical alignment. + * + * @access public + * @param string $location alignment for the cell ('top', 'vleft', 'vright', etc...). + */ + function setVAlign($location) + { + if (preg_match("/\d/",$location)) { + return; // Ignore numbers + } + + $location = strtolower($location); + + if ($location == 'top') { + $this->_text_v_align = 0; + } + if ($location == 'vcentre') { + $this->_text_v_align = 1; + } + if ($location == 'vcenter') { + $this->_text_v_align = 1; + } + if ($location == 'bottom') { + $this->_text_v_align = 2; + } + if ($location == 'vjustify') { + $this->_text_v_align = 3; + } + if ($location == 'vequal_space') { // For T.K. + $this->_text_v_align = 4; + } + } + + /** + * This is an alias for the unintuitive setAlign('merge') + * + * @access public + */ + function setMerge() + { + $this->setAlign('merge'); + } + + /** + * Sets the boldness of the text. + * Bold has a range 100..1000. + * 0 (400) is normal. 1 (700) is bold. + * + * @access public + * @param integer $weight Weight for the text, 0 maps to 400 (normal text), + 1 maps to 700 (bold text). Valid range is: 100-1000. + It's Optional, default is 1 (bold). + */ + function setBold($weight = 1) + { + if ($weight == 1) { + $weight = 0x2BC; // Bold text + } + if ($weight == 0) { + $weight = 0x190; // Normal text + } + if ($weight < 0x064) { + $weight = 0x190; // Lower bound + } + if ($weight > 0x3E8) { + $weight = 0x190; // Upper bound + } + $this->_bold = $weight; + } + + + /************************************ + * FUNCTIONS FOR SETTING CELLS BORDERS + */ + + /** + * Sets the width for the bottom border of the cell + * + * @access public + * @param integer $style style of the cell border. 1 => thin, 2 => thick. + */ + function setBottom($style) + { + $this->_bottom = $style; + } + + /** + * Sets the width for the top border of the cell + * + * @access public + * @param integer $style style of the cell top border. 1 => thin, 2 => thick. + */ + function setTop($style) + { + $this->_top = $style; + } + + /** + * Sets the width for the left border of the cell + * + * @access public + * @param integer $style style of the cell left border. 1 => thin, 2 => thick. + */ + function setLeft($style) + { + $this->_left = $style; + } + + /** + * Sets the width for the right border of the cell + * + * @access public + * @param integer $style style of the cell right border. 1 => thin, 2 => thick. + */ + function setRight($style) + { + $this->_right = $style; + } + + + /** + * Set cells borders to the same style + * + * @access public + * @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick. + */ + function setBorder($style) + { + $this->setBottom($style); + $this->setTop($style); + $this->setLeft($style); + $this->setRight($style); + } + + + /******************************************* + * FUNCTIONS FOR SETTING CELLS BORDERS COLORS + */ + + /** + * Sets all the cell's borders to the same color + * + * @access public + * @param mixed $color The color we are setting. Either a string (like 'blue'), + * or an integer (range is [8...63]). + */ + function setBorderColor($color) + { + $this->setBottomColor($color); + $this->setTopColor($color); + $this->setLeftColor($color); + $this->setRightColor($color); + } + + /** + * Sets the cell's bottom border color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + function setBottomColor($color) + { + $value = $this->_getColor($color); + $this->_bottom_color = $value; + } + + /** + * Sets the cell's top border color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + function setTopColor($color) + { + $value = $this->_getColor($color); + $this->_top_color = $value; + } + + /** + * Sets the cell's left border color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + function setLeftColor($color) + { + $value = $this->_getColor($color); + $this->_left_color = $value; + } + + /** + * Sets the cell's right border color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + function setRightColor($color) + { + $value = $this->_getColor($color); + $this->_right_color = $value; + } + + + /** + * Sets the cell's foreground color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + function setFgColor($color) + { + $value = $this->_getColor($color); + $this->_fg_color = $value; + if ($this->_pattern == 0) { // force color to be seen + $this->_pattern = 1; + } + } + + /** + * Sets the cell's background color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + function setBgColor($color) + { + $value = $this->_getColor($color); + $this->_bg_color = $value; + if ($this->_pattern == 0) { // force color to be seen + $this->_pattern = 1; + } + } + + /** + * Sets the cell's color + * + * @access public + * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]). + */ + function setColor($color) + { + $value = $this->_getColor($color); + $this->_color = $value; + } + + /** + * Sets the fill pattern attribute of a cell + * + * @access public + * @param integer $arg Optional. Defaults to 1. Meaningful values are: 0-18, + * 0 meaning no background. + */ + function setPattern($arg = 1) + { + $this->_pattern = $arg; + } + + /** + * Sets the underline of the text + * + * @access public + * @param integer $underline The value for underline. Possible values are: + * 1 => underline, 2 => double underline. + */ + function setUnderline($underline) + { + $this->_underline = $underline; + } + + /** + * Sets the font style as italic + * + * @access public + */ + function setItalic() + { + $this->_italic = 1; + } + + /** + * Sets the font size + * + * @access public + * @param integer $size The font size (in pixels I think). + */ + function setSize($size) + { + $this->_size = $size; + } + + /** + * Sets text wrapping + * + * @access public + */ + function setTextWrap() + { + $this->_text_wrap = 1; + } + + /** + * Sets the orientation of the text + * + * @access public + * @param integer $angle The rotation angle for the text (clockwise). Possible + values are: 0, 90, 270 and -1 for stacking top-to-bottom. + */ + function setTextRotation($angle) + { + switch ($angle) + { + case 0: + $this->_rotation = 0; + break; + case 90: + if ($this->_BIFF_version == 0x0500) { + $this->_rotation = 3; + } elseif ($this->_BIFF_version == 0x0600) { + $this->_rotation = 180; + } + break; + case 270: + if ($this->_BIFF_version == 0x0500) { + $this->_rotation = 2; + } elseif ($this->_BIFF_version == 0x0600) { + $this->_rotation = 90; + } + break; + case -1: + if ($this->_BIFF_version == 0x0500) { + $this->_rotation = 1; + } elseif ($this->_BIFF_version == 0x0600) { + $this->_rotation = 255; + } + break; + default : + return $this->raiseError("Invalid value for angle.". + " Possible values are: 0, 90, 270 and -1 ". + "for stacking top-to-bottom."); + $this->_rotation = 0; + break; + } + } + + /** + * Sets the numeric format. + * It can be date, time, currency, etc... + * + * @access public + * @param integer $num_format The numeric format. + */ + function setNumFormat($num_format) + { + $this->_num_format = $num_format; + } + + /** + * Sets font as strikeout. + * + * @access public + */ + function setStrikeOut() + { + $this->_font_strikeout = 1; + } + + /** + * Sets outlining for a font. + * + * @access public + */ + function setOutLine() + { + $this->_font_outline = 1; + } + + /** + * Sets font as shadow. + * + * @access public + */ + function setShadow() + { + $this->_font_shadow = 1; + } + + /** + * Sets the script type of the text + * + * @access public + * @param integer $script The value for script type. Possible values are: + * 1 => superscript, 2 => subscript. + */ + function setScript($script) + { + $this->_font_script = $script; + } + + /** + * Locks a cell. + * + * @access public + */ + function setLocked() + { + $this->_locked = 1; + } + + /** + * Unlocks a cell. Useful for unprotecting particular cells of a protected sheet. + * + * @access public + */ + function setUnLocked() + { + $this->_locked = 0; + } + + /** + * Sets the font family name. + * + * @access public + * @param string $fontfamily The font family name. Possible values are: + * 'Times New Roman', 'Arial', 'Courier'. + */ + function setFontFamily($font_family) + { + $this->_font_name = $font_family; + } +} +?> diff --git a/functions/PEAR/Spreadsheet/Excel/Writer/Parser.php b/functions/PEAR/Spreadsheet/Excel/Writer/Parser.php new file mode 100755 index 000000000..eaa330eb8 --- /dev/null +++ b/functions/PEAR/Spreadsheet/Excel/Writer/Parser.php @@ -0,0 +1,1703 @@ +" +*/ +define('SPREADSHEET_EXCEL_WRITER_GT', ">"); + +/** +* @const SPREADSHEET_EXCEL_WRITER_LT token identifier for character "<" +*/ +define('SPREADSHEET_EXCEL_WRITER_LT', "<"); + +/** +* @const SPREADSHEET_EXCEL_WRITER_LE token identifier for character "<=" +*/ +define('SPREADSHEET_EXCEL_WRITER_LE', "<="); + +/** +* @const SPREADSHEET_EXCEL_WRITER_GE token identifier for character ">=" +*/ +define('SPREADSHEET_EXCEL_WRITER_GE', ">="); + +/** +* @const SPREADSHEET_EXCEL_WRITER_EQ token identifier for character "=" +*/ +define('SPREADSHEET_EXCEL_WRITER_EQ', "="); + +/** +* @const SPREADSHEET_EXCEL_WRITER_NE token identifier for character "<>" +*/ +define('SPREADSHEET_EXCEL_WRITER_NE', "<>"); + +/** +* * @const SPREADSHEET_EXCEL_WRITER_CONCAT token identifier for character "&" +*/ +define('SPREADSHEET_EXCEL_WRITER_CONCAT', "&"); + +require_once 'PEAR.php'; + +/** +* Class for parsing Excel formulas +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_Parser extends PEAR +{ + /** + * The index of the character we are currently looking at + * @var integer + */ + var $_current_char; + + /** + * The token we are working on. + * @var string + */ + var $_current_token; + + /** + * The formula to parse + * @var string + */ + var $_formula; + + /** + * The character ahead of the current char + * @var string + */ + var $_lookahead; + + /** + * The parse tree to be generated + * @var string + */ + var $_parse_tree; + + /** + * The byte order. 1 => big endian, 0 => little endian. + * @var integer + */ + var $_byte_order; + + /** + * Array of external sheets + * @var array + */ + var $_ext_sheets; + + /** + * Array of sheet references in the form of REF structures + * @var array + */ + var $_references; + + /** + * The BIFF version for the workbook + * @var integer + */ + var $_BIFF_version; + + /** + * The class constructor + * + * @param integer $byte_order The byte order (Little endian or Big endian) of the architecture + (optional). 1 => big endian, 0 (default) little endian. + */ + function Spreadsheet_Excel_Writer_Parser($byte_order, $biff_version) + { + $this->_current_char = 0; + $this->_BIFF_version = $biff_version; + $this->_current_token = ''; // The token we are working on. + $this->_formula = ''; // The formula to parse. + $this->_lookahead = ''; // The character ahead of the current char. + $this->_parse_tree = ''; // The parse tree to be generated. + $this->_initializeHashes(); // Initialize the hashes: ptg's and function's ptg's + $this->_byte_order = $byte_order; // Little Endian or Big Endian + $this->_ext_sheets = array(); + $this->_references = array(); + } + + /** + * Initialize the ptg and function hashes. + * + * @access private + */ + function _initializeHashes() + { + // The Excel ptg indices + $this->ptg = array( + 'ptgExp' => 0x01, + 'ptgTbl' => 0x02, + 'ptgAdd' => 0x03, + 'ptgSub' => 0x04, + 'ptgMul' => 0x05, + 'ptgDiv' => 0x06, + 'ptgPower' => 0x07, + 'ptgConcat' => 0x08, + 'ptgLT' => 0x09, + 'ptgLE' => 0x0A, + 'ptgEQ' => 0x0B, + 'ptgGE' => 0x0C, + 'ptgGT' => 0x0D, + 'ptgNE' => 0x0E, + 'ptgIsect' => 0x0F, + 'ptgUnion' => 0x10, + 'ptgRange' => 0x11, + 'ptgUplus' => 0x12, + 'ptgUminus' => 0x13, + 'ptgPercent' => 0x14, + 'ptgParen' => 0x15, + 'ptgMissArg' => 0x16, + 'ptgStr' => 0x17, + 'ptgAttr' => 0x19, + 'ptgSheet' => 0x1A, + 'ptgEndSheet' => 0x1B, + 'ptgErr' => 0x1C, + 'ptgBool' => 0x1D, + 'ptgInt' => 0x1E, + 'ptgNum' => 0x1F, + 'ptgArray' => 0x20, + 'ptgFunc' => 0x21, + 'ptgFuncVar' => 0x22, + 'ptgName' => 0x23, + 'ptgRef' => 0x24, + 'ptgArea' => 0x25, + 'ptgMemArea' => 0x26, + 'ptgMemErr' => 0x27, + 'ptgMemNoMem' => 0x28, + 'ptgMemFunc' => 0x29, + 'ptgRefErr' => 0x2A, + 'ptgAreaErr' => 0x2B, + 'ptgRefN' => 0x2C, + 'ptgAreaN' => 0x2D, + 'ptgMemAreaN' => 0x2E, + 'ptgMemNoMemN' => 0x2F, + 'ptgNameX' => 0x39, + 'ptgRef3d' => 0x3A, + 'ptgArea3d' => 0x3B, + 'ptgRefErr3d' => 0x3C, + 'ptgAreaErr3d' => 0x3D, + 'ptgArrayV' => 0x40, + 'ptgFuncV' => 0x41, + 'ptgFuncVarV' => 0x42, + 'ptgNameV' => 0x43, + 'ptgRefV' => 0x44, + 'ptgAreaV' => 0x45, + 'ptgMemAreaV' => 0x46, + 'ptgMemErrV' => 0x47, + 'ptgMemNoMemV' => 0x48, + 'ptgMemFuncV' => 0x49, + 'ptgRefErrV' => 0x4A, + 'ptgAreaErrV' => 0x4B, + 'ptgRefNV' => 0x4C, + 'ptgAreaNV' => 0x4D, + 'ptgMemAreaNV' => 0x4E, + 'ptgMemNoMemN' => 0x4F, + 'ptgFuncCEV' => 0x58, + 'ptgNameXV' => 0x59, + 'ptgRef3dV' => 0x5A, + 'ptgArea3dV' => 0x5B, + 'ptgRefErr3dV' => 0x5C, + 'ptgAreaErr3d' => 0x5D, + 'ptgArrayA' => 0x60, + 'ptgFuncA' => 0x61, + 'ptgFuncVarA' => 0x62, + 'ptgNameA' => 0x63, + 'ptgRefA' => 0x64, + 'ptgAreaA' => 0x65, + 'ptgMemAreaA' => 0x66, + 'ptgMemErrA' => 0x67, + 'ptgMemNoMemA' => 0x68, + 'ptgMemFuncA' => 0x69, + 'ptgRefErrA' => 0x6A, + 'ptgAreaErrA' => 0x6B, + 'ptgRefNA' => 0x6C, + 'ptgAreaNA' => 0x6D, + 'ptgMemAreaNA' => 0x6E, + 'ptgMemNoMemN' => 0x6F, + 'ptgFuncCEA' => 0x78, + 'ptgNameXA' => 0x79, + 'ptgRef3dA' => 0x7A, + 'ptgArea3dA' => 0x7B, + 'ptgRefErr3dA' => 0x7C, + 'ptgAreaErr3d' => 0x7D + ); + + // Thanks to Michael Meeks and Gnumeric for the initial arg values. + // + // The following hash was generated by "function_locale.pl" in the distro. + // Refer to function_locale.pl for non-English function names. + // + // The array elements are as follow: + // ptg: The Excel function ptg code. + // args: The number of arguments that the function takes: + // >=0 is a fixed number of arguments. + // -1 is a variable number of arguments. + // class: The reference, value or array class of the function args. + // vol: The function is volatile. + // + $this->_functions = array( + // function ptg args class vol + 'COUNT' => array( 0, -1, 0, 0 ), + 'IF' => array( 1, -1, 1, 0 ), + 'ISNA' => array( 2, 1, 1, 0 ), + 'ISERROR' => array( 3, 1, 1, 0 ), + 'SUM' => array( 4, -1, 0, 0 ), + 'AVERAGE' => array( 5, -1, 0, 0 ), + 'MIN' => array( 6, -1, 0, 0 ), + 'MAX' => array( 7, -1, 0, 0 ), + 'ROW' => array( 8, -1, 0, 0 ), + 'COLUMN' => array( 9, -1, 0, 0 ), + 'NA' => array( 10, 0, 0, 0 ), + 'NPV' => array( 11, -1, 1, 0 ), + 'STDEV' => array( 12, -1, 0, 0 ), + 'DOLLAR' => array( 13, -1, 1, 0 ), + 'FIXED' => array( 14, -1, 1, 0 ), + 'SIN' => array( 15, 1, 1, 0 ), + 'COS' => array( 16, 1, 1, 0 ), + 'TAN' => array( 17, 1, 1, 0 ), + 'ATAN' => array( 18, 1, 1, 0 ), + 'PI' => array( 19, 0, 1, 0 ), + 'SQRT' => array( 20, 1, 1, 0 ), + 'EXP' => array( 21, 1, 1, 0 ), + 'LN' => array( 22, 1, 1, 0 ), + 'LOG10' => array( 23, 1, 1, 0 ), + 'ABS' => array( 24, 1, 1, 0 ), + 'INT' => array( 25, 1, 1, 0 ), + 'SIGN' => array( 26, 1, 1, 0 ), + 'ROUND' => array( 27, 2, 1, 0 ), + 'LOOKUP' => array( 28, -1, 0, 0 ), + 'INDEX' => array( 29, -1, 0, 1 ), + 'REPT' => array( 30, 2, 1, 0 ), + 'MID' => array( 31, 3, 1, 0 ), + 'LEN' => array( 32, 1, 1, 0 ), + 'VALUE' => array( 33, 1, 1, 0 ), + 'TRUE' => array( 34, 0, 1, 0 ), + 'FALSE' => array( 35, 0, 1, 0 ), + 'AND' => array( 36, -1, 0, 0 ), + 'OR' => array( 37, -1, 0, 0 ), + 'NOT' => array( 38, 1, 1, 0 ), + 'MOD' => array( 39, 2, 1, 0 ), + 'DCOUNT' => array( 40, 3, 0, 0 ), + 'DSUM' => array( 41, 3, 0, 0 ), + 'DAVERAGE' => array( 42, 3, 0, 0 ), + 'DMIN' => array( 43, 3, 0, 0 ), + 'DMAX' => array( 44, 3, 0, 0 ), + 'DSTDEV' => array( 45, 3, 0, 0 ), + 'VAR' => array( 46, -1, 0, 0 ), + 'DVAR' => array( 47, 3, 0, 0 ), + 'TEXT' => array( 48, 2, 1, 0 ), + 'LINEST' => array( 49, -1, 0, 0 ), + 'TREND' => array( 50, -1, 0, 0 ), + 'LOGEST' => array( 51, -1, 0, 0 ), + 'GROWTH' => array( 52, -1, 0, 0 ), + 'PV' => array( 56, -1, 1, 0 ), + 'FV' => array( 57, -1, 1, 0 ), + 'NPER' => array( 58, -1, 1, 0 ), + 'PMT' => array( 59, -1, 1, 0 ), + 'RATE' => array( 60, -1, 1, 0 ), + 'MIRR' => array( 61, 3, 0, 0 ), + 'IRR' => array( 62, -1, 0, 0 ), + 'RAND' => array( 63, 0, 1, 1 ), + 'MATCH' => array( 64, -1, 0, 0 ), + 'DATE' => array( 65, 3, 1, 0 ), + 'TIME' => array( 66, 3, 1, 0 ), + 'DAY' => array( 67, 1, 1, 0 ), + 'MONTH' => array( 68, 1, 1, 0 ), + 'YEAR' => array( 69, 1, 1, 0 ), + 'WEEKDAY' => array( 70, -1, 1, 0 ), + 'HOUR' => array( 71, 1, 1, 0 ), + 'MINUTE' => array( 72, 1, 1, 0 ), + 'SECOND' => array( 73, 1, 1, 0 ), + 'NOW' => array( 74, 0, 1, 1 ), + 'AREAS' => array( 75, 1, 0, 1 ), + 'ROWS' => array( 76, 1, 0, 1 ), + 'COLUMNS' => array( 77, 1, 0, 1 ), + 'OFFSET' => array( 78, -1, 0, 1 ), + 'SEARCH' => array( 82, -1, 1, 0 ), + 'TRANSPOSE' => array( 83, 1, 1, 0 ), + 'TYPE' => array( 86, 1, 1, 0 ), + 'ATAN2' => array( 97, 2, 1, 0 ), + 'ASIN' => array( 98, 1, 1, 0 ), + 'ACOS' => array( 99, 1, 1, 0 ), + 'CHOOSE' => array( 100, -1, 1, 0 ), + 'HLOOKUP' => array( 101, -1, 0, 0 ), + 'VLOOKUP' => array( 102, -1, 0, 0 ), + 'ISREF' => array( 105, 1, 0, 0 ), + 'LOG' => array( 109, -1, 1, 0 ), + 'CHAR' => array( 111, 1, 1, 0 ), + 'LOWER' => array( 112, 1, 1, 0 ), + 'UPPER' => array( 113, 1, 1, 0 ), + 'PROPER' => array( 114, 1, 1, 0 ), + 'LEFT' => array( 115, -1, 1, 0 ), + 'RIGHT' => array( 116, -1, 1, 0 ), + 'EXACT' => array( 117, 2, 1, 0 ), + 'TRIM' => array( 118, 1, 1, 0 ), + 'REPLACE' => array( 119, 4, 1, 0 ), + 'SUBSTITUTE' => array( 120, -1, 1, 0 ), + 'CODE' => array( 121, 1, 1, 0 ), + 'FIND' => array( 124, -1, 1, 0 ), + 'CELL' => array( 125, -1, 0, 1 ), + 'ISERR' => array( 126, 1, 1, 0 ), + 'ISTEXT' => array( 127, 1, 1, 0 ), + 'ISNUMBER' => array( 128, 1, 1, 0 ), + 'ISBLANK' => array( 129, 1, 1, 0 ), + 'T' => array( 130, 1, 0, 0 ), + 'N' => array( 131, 1, 0, 0 ), + 'DATEVALUE' => array( 140, 1, 1, 0 ), + 'TIMEVALUE' => array( 141, 1, 1, 0 ), + 'SLN' => array( 142, 3, 1, 0 ), + 'SYD' => array( 143, 4, 1, 0 ), + 'DDB' => array( 144, -1, 1, 0 ), + 'INDIRECT' => array( 148, -1, 1, 1 ), + 'CALL' => array( 150, -1, 1, 0 ), + 'CLEAN' => array( 162, 1, 1, 0 ), + 'MDETERM' => array( 163, 1, 2, 0 ), + 'MINVERSE' => array( 164, 1, 2, 0 ), + 'MMULT' => array( 165, 2, 2, 0 ), + 'IPMT' => array( 167, -1, 1, 0 ), + 'PPMT' => array( 168, -1, 1, 0 ), + 'COUNTA' => array( 169, -1, 0, 0 ), + 'PRODUCT' => array( 183, -1, 0, 0 ), + 'FACT' => array( 184, 1, 1, 0 ), + 'DPRODUCT' => array( 189, 3, 0, 0 ), + 'ISNONTEXT' => array( 190, 1, 1, 0 ), + 'STDEVP' => array( 193, -1, 0, 0 ), + 'VARP' => array( 194, -1, 0, 0 ), + 'DSTDEVP' => array( 195, 3, 0, 0 ), + 'DVARP' => array( 196, 3, 0, 0 ), + 'TRUNC' => array( 197, -1, 1, 0 ), + 'ISLOGICAL' => array( 198, 1, 1, 0 ), + 'DCOUNTA' => array( 199, 3, 0, 0 ), + 'ROUNDUP' => array( 212, 2, 1, 0 ), + 'ROUNDDOWN' => array( 213, 2, 1, 0 ), + 'RANK' => array( 216, -1, 0, 0 ), + 'ADDRESS' => array( 219, -1, 1, 0 ), + 'DAYS360' => array( 220, -1, 1, 0 ), + 'TODAY' => array( 221, 0, 1, 1 ), + 'VDB' => array( 222, -1, 1, 0 ), + 'MEDIAN' => array( 227, -1, 0, 0 ), + 'SUMPRODUCT' => array( 228, -1, 2, 0 ), + 'SINH' => array( 229, 1, 1, 0 ), + 'COSH' => array( 230, 1, 1, 0 ), + 'TANH' => array( 231, 1, 1, 0 ), + 'ASINH' => array( 232, 1, 1, 0 ), + 'ACOSH' => array( 233, 1, 1, 0 ), + 'ATANH' => array( 234, 1, 1, 0 ), + 'DGET' => array( 235, 3, 0, 0 ), + 'INFO' => array( 244, 1, 1, 1 ), + 'DB' => array( 247, -1, 1, 0 ), + 'FREQUENCY' => array( 252, 2, 0, 0 ), + 'ERROR.TYPE' => array( 261, 1, 1, 0 ), + 'REGISTER.ID' => array( 267, -1, 1, 0 ), + 'AVEDEV' => array( 269, -1, 0, 0 ), + 'BETADIST' => array( 270, -1, 1, 0 ), + 'GAMMALN' => array( 271, 1, 1, 0 ), + 'BETAINV' => array( 272, -1, 1, 0 ), + 'BINOMDIST' => array( 273, 4, 1, 0 ), + 'CHIDIST' => array( 274, 2, 1, 0 ), + 'CHIINV' => array( 275, 2, 1, 0 ), + 'COMBIN' => array( 276, 2, 1, 0 ), + 'CONFIDENCE' => array( 277, 3, 1, 0 ), + 'CRITBINOM' => array( 278, 3, 1, 0 ), + 'EVEN' => array( 279, 1, 1, 0 ), + 'EXPONDIST' => array( 280, 3, 1, 0 ), + 'FDIST' => array( 281, 3, 1, 0 ), + 'FINV' => array( 282, 3, 1, 0 ), + 'FISHER' => array( 283, 1, 1, 0 ), + 'FISHERINV' => array( 284, 1, 1, 0 ), + 'FLOOR' => array( 285, 2, 1, 0 ), + 'GAMMADIST' => array( 286, 4, 1, 0 ), + 'GAMMAINV' => array( 287, 3, 1, 0 ), + 'CEILING' => array( 288, 2, 1, 0 ), + 'HYPGEOMDIST' => array( 289, 4, 1, 0 ), + 'LOGNORMDIST' => array( 290, 3, 1, 0 ), + 'LOGINV' => array( 291, 3, 1, 0 ), + 'NEGBINOMDIST' => array( 292, 3, 1, 0 ), + 'NORMDIST' => array( 293, 4, 1, 0 ), + 'NORMSDIST' => array( 294, 1, 1, 0 ), + 'NORMINV' => array( 295, 3, 1, 0 ), + 'NORMSINV' => array( 296, 1, 1, 0 ), + 'STANDARDIZE' => array( 297, 3, 1, 0 ), + 'ODD' => array( 298, 1, 1, 0 ), + 'PERMUT' => array( 299, 2, 1, 0 ), + 'POISSON' => array( 300, 3, 1, 0 ), + 'TDIST' => array( 301, 3, 1, 0 ), + 'WEIBULL' => array( 302, 4, 1, 0 ), + 'SUMXMY2' => array( 303, 2, 2, 0 ), + 'SUMX2MY2' => array( 304, 2, 2, 0 ), + 'SUMX2PY2' => array( 305, 2, 2, 0 ), + 'CHITEST' => array( 306, 2, 2, 0 ), + 'CORREL' => array( 307, 2, 2, 0 ), + 'COVAR' => array( 308, 2, 2, 0 ), + 'FORECAST' => array( 309, 3, 2, 0 ), + 'FTEST' => array( 310, 2, 2, 0 ), + 'INTERCEPT' => array( 311, 2, 2, 0 ), + 'PEARSON' => array( 312, 2, 2, 0 ), + 'RSQ' => array( 313, 2, 2, 0 ), + 'STEYX' => array( 314, 2, 2, 0 ), + 'SLOPE' => array( 315, 2, 2, 0 ), + 'TTEST' => array( 316, 4, 2, 0 ), + 'PROB' => array( 317, -1, 2, 0 ), + 'DEVSQ' => array( 318, -1, 0, 0 ), + 'GEOMEAN' => array( 319, -1, 0, 0 ), + 'HARMEAN' => array( 320, -1, 0, 0 ), + 'SUMSQ' => array( 321, -1, 0, 0 ), + 'KURT' => array( 322, -1, 0, 0 ), + 'SKEW' => array( 323, -1, 0, 0 ), + 'ZTEST' => array( 324, -1, 0, 0 ), + 'LARGE' => array( 325, 2, 0, 0 ), + 'SMALL' => array( 326, 2, 0, 0 ), + 'QUARTILE' => array( 327, 2, 0, 0 ), + 'PERCENTILE' => array( 328, 2, 0, 0 ), + 'PERCENTRANK' => array( 329, -1, 0, 0 ), + 'MODE' => array( 330, -1, 2, 0 ), + 'TRIMMEAN' => array( 331, 2, 0, 0 ), + 'TINV' => array( 332, 2, 1, 0 ), + 'CONCATENATE' => array( 336, -1, 1, 0 ), + 'POWER' => array( 337, 2, 1, 0 ), + 'RADIANS' => array( 342, 1, 1, 0 ), + 'DEGREES' => array( 343, 1, 1, 0 ), + 'SUBTOTAL' => array( 344, -1, 0, 0 ), + 'SUMIF' => array( 345, -1, 0, 0 ), + 'COUNTIF' => array( 346, 2, 0, 0 ), + 'COUNTBLANK' => array( 347, 1, 0, 0 ), + 'ROMAN' => array( 354, -1, 1, 0 ) + ); + } + + /** + * Convert a token to the proper ptg value. + * + * @access private + * @param mixed $token The token to convert. + * @return mixed the converted token on success. PEAR_Error if the token + * is not recognized + */ + function _convert($token) + { + if (preg_match("/^\"[^\"]{0,255}\"$/", $token)) { + return $this->_convertString($token); + + } elseif (is_numeric($token)) { + return $this->_convertNumber($token); + + // match references like A1 or $A$1 + } elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/',$token)) { + return $this->_convertRef2d($token); + + // match external references like Sheet1!A1 or Sheet1:Sheet2!A1 + } elseif (preg_match("/^\w+(\:\w+)?\![A-Ia-i]?[A-Za-z](\d+)$/u",$token)) { + return $this->_convertRef3d($token); + + // match external references like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1 + } elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\![A-Ia-i]?[A-Za-z](\d+)$/u",$token)) { + return $this->_convertRef3d($token); + + // match ranges like A1:B2 + } elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/",$token)) { + return $this->_convertRange2d($token); + + // match ranges like A1..B2 + } elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/",$token)) { + return $this->_convertRange2d($token); + + // match external ranges like Sheet1!A1 or Sheet1:Sheet2!A1:B2 + } elseif (preg_match("/^\w+(\:\w+)?\!([A-Ia-i]?[A-Za-z])?(\d+)\:([A-Ia-i]?[A-Za-z])?(\d+)$/u",$token)) { + return $this->_convertRange3d($token); + + // match external ranges like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1:B2 + } elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\!([A-Ia-i]?[A-Za-z])?(\d+)\:([A-Ia-i]?[A-Za-z])?(\d+)$/u",$token)) { + return $this->_convertRange3d($token); + + // operators (including parentheses) + } elseif (isset($this->ptg[$token])) { + return pack("C", $this->ptg[$token]); + + // commented so argument number can be processed correctly. See toReversePolish(). + /*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/",$token)) + { + return($this->_convertFunction($token,$this->_func_args)); + }*/ + + // if it's an argument, ignore the token (the argument remains) + } elseif ($token == 'arg') { + return ''; + } + // TODO: use real error codes + return $this->raiseError("Unknown token $token"); + } + + /** + * Convert a number token to ptgInt or ptgNum + * + * @access private + * @param mixed $num an integer or double for conversion to its ptg value + */ + function _convertNumber($num) + { + // Integer in the range 0..2**16-1 + if ((preg_match("/^\d+$/", $num)) and ($num <= 65535)) { + return pack("Cv", $this->ptg['ptgInt'], $num); + } else { // A float + if ($this->_byte_order) { // if it's Big Endian + $num = strrev($num); + } + return pack("Cd", $this->ptg['ptgNum'], $num); + } + } + + /** + * Convert a string token to ptgStr + * + * @access private + * @param string $string A string for conversion to its ptg value. + * @return mixed the converted token on success. PEAR_Error if the string + * is longer than 255 characters. + */ + function _convertString($string) + { + // chop away beggining and ending quotes + $string = substr($string, 1, strlen($string) - 2); + if (strlen($string) > 255) { + return $this->raiseError("String is too long"); + } + + if ($this->_BIFF_version == 0x0500) { + return pack("CC", $this->ptg['ptgStr'], strlen($string)).$string; + } elseif ($this->_BIFF_version == 0x0600) { + $encoding = 0; // TODO: Unicode support + return pack("CCC", $this->ptg['ptgStr'], strlen($string), $encoding).$string; + } + } + + /** + * Convert a function to a ptgFunc or ptgFuncVarV depending on the number of + * args that it takes. + * + * @access private + * @param string $token The name of the function for convertion to ptg value. + * @param integer $num_args The number of arguments the function receives. + * @return string The packed ptg for the function + */ + function _convertFunction($token, $num_args) + { + $args = $this->_functions[$token][1]; + $volatile = $this->_functions[$token][3]; + + // Fixed number of args eg. TIME($i,$j,$k). + if ($args >= 0) { + return pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]); + } + // Variable number of args eg. SUM($i,$j,$k, ..). + if ($args == -1) { + return pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]); + } + } + + /** + * Convert an Excel range such as A1:D4 to a ptgRefV. + * + * @access private + * @param string $range An Excel range in the A1:A2 or A1..A2 format. + */ + function _convertRange2d($range, $class=0) + { + + // TODO: possible class value 0,1,2 check Formula.pm + // Split the range into 2 cell refs + if (preg_match("/^([A-Ia-i]?[A-Za-z])(\d+)\:([A-Ia-i]?[A-Za-z])(\d+)$/", $range)) { + list($cell1, $cell2) = split(':', $range); + } elseif (preg_match("/^([A-Ia-i]?[A-Za-z])(\d+)\.\.([A-Ia-i]?[A-Za-z])(\d+)$/", $range)) { + list($cell1, $cell2) = split('\.\.', $range); + + } else { + // TODO: use real error codes + return $this->raiseError("Unknown range separator", 0, PEAR_ERROR_DIE); + } + + // Convert the cell references + $cell_array1 = $this->_cellToPackedRowcol($cell1); + if (PEAR::isError($cell_array1)) { + return $cell_array1; + } + list($row1, $col1) = $cell_array1; + $cell_array2 = $this->_cellToPackedRowcol($cell2); + if (PEAR::isError($cell_array2)) { + return $cell_array2; + } + list($row2, $col2) = $cell_array2; + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgArea = pack("C", $this->ptg['ptgArea']); + } elseif ($class == 1) { + $ptgArea = pack("C", $this->ptg['ptgAreaV']); + } elseif ($class == 2) { + $ptgArea = pack("C", $this->ptg['ptgAreaA']); + } else { + // TODO: use real error codes + return $this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE); + } + return $ptgArea . $row1 . $row2 . $col1. $col2; + } + + /** + * Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to + * a ptgArea3d. + * + * @access private + * @param string $token An Excel range in the Sheet1!A1:A2 format. + * @return mixed The packed ptgArea3d token on success, PEAR_Error on failure. + */ + function _convertRange3d($token) + { + $class = 2; // as far as I know, this is magick. + + // Split the ref at the ! symbol + list($ext_ref, $range) = split('!', $token); + + // Convert the external reference part (different for BIFF8) + if ($this->_BIFF_version == 0x0500) { + $ext_ref = $this->_packExtRef($ext_ref); + if (PEAR::isError($ext_ref)) { + return $ext_ref; + } + } elseif ($this->_BIFF_version == 0x0600) { + $ext_ref = $this->_getRefIndex($ext_ref); + if (PEAR::isError($ext_ref)) { + return $ext_ref; + } + } + + // Split the range into 2 cell refs + list($cell1, $cell2) = split(':', $range); + + // Convert the cell references + if (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/", $cell1)) { + $cell_array1 = $this->_cellToPackedRowcol($cell1); + if (PEAR::isError($cell_array1)) { + return $cell_array1; + } + list($row1, $col1) = $cell_array1; + $cell_array2 = $this->_cellToPackedRowcol($cell2); + if (PEAR::isError($cell_array2)) { + return $cell_array2; + } + list($row2, $col2) = $cell_array2; + } else { // It's a rows range (like 26:27) + $cells_array = $this->_rangeToPackedRange($cell1.':'.$cell2); + if (PEAR::isError($cells_array)) { + return $cells_array; + } + list($row1, $col1, $row2, $col2) = $cells_array; + } + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgArea = pack("C", $this->ptg['ptgArea3d']); + } elseif ($class == 1) { + $ptgArea = pack("C", $this->ptg['ptgArea3dV']); + } elseif ($class == 2) { + $ptgArea = pack("C", $this->ptg['ptgArea3dA']); + } else { + return $this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE); + } + + return $ptgArea . $ext_ref . $row1 . $row2 . $col1. $col2; + } + + /** + * Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV. + * + * @access private + * @param string $cell An Excel cell reference + * @return string The cell in packed() format with the corresponding ptg + */ + function _convertRef2d($cell) + { + $class = 2; // as far as I know, this is magick. + + // Convert the cell reference + $cell_array = $this->_cellToPackedRowcol($cell); + if (PEAR::isError($cell_array)) { + return $cell_array; + } + list($row, $col) = $cell_array; + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgRef = pack("C", $this->ptg['ptgRef']); + } elseif ($class == 1) { + $ptgRef = pack("C", $this->ptg['ptgRefV']); + } elseif ($class == 2) { + $ptgRef = pack("C", $this->ptg['ptgRefA']); + } else { + // TODO: use real error codes + return $this->raiseError("Unknown class $class"); + } + return $ptgRef.$row.$col; + } + + /** + * Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a + * ptgRef3d. + * + * @access private + * @param string $cell An Excel cell reference + * @return mixed The packed ptgRef3d token on success, PEAR_Error on failure. + */ + function _convertRef3d($cell) + { + $class = 2; // as far as I know, this is magick. + + // Split the ref at the ! symbol + list($ext_ref, $cell) = split('!', $cell); + + // Convert the external reference part (different for BIFF8) + if ($this->_BIFF_version == 0x0500) { + $ext_ref = $this->_packExtRef($ext_ref); + if (PEAR::isError($ext_ref)) { + return $ext_ref; + } + } elseif ($this->_BIFF_version == 0x0600) { + $ext_ref = $this->_getRefIndex($ext_ref); + if (PEAR::isError($ext_ref)) { + return $ext_ref; + } + } + + // Convert the cell reference part + list($row, $col) = $this->_cellToPackedRowcol($cell); + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgRef = pack("C", $this->ptg['ptgRef3d']); + } elseif ($class == 1) { + $ptgRef = pack("C", $this->ptg['ptgRef3dV']); + } elseif ($class == 2) { + $ptgRef = pack("C", $this->ptg['ptgRef3dA']); + } else { + return $this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE); + } + + return $ptgRef . $ext_ref. $row . $col; + } + + /** + * Convert the sheet name part of an external reference, for example "Sheet1" or + * "Sheet1:Sheet2", to a packed structure. + * + * @access private + * @param string $ext_ref The name of the external reference + * @return string The reference index in packed() format + */ + function _packExtRef($ext_ref) + { + $ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any. + $ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any. + + // Check if there is a sheet range eg., Sheet1:Sheet2. + if (preg_match("/:/", $ext_ref)) { + list($sheet_name1, $sheet_name2) = split(':', $ext_ref); + + $sheet1 = $this->_getSheetIndex($sheet_name1); + if ($sheet1 == -1) { + return $this->raiseError("Unknown sheet name $sheet_name1 in formula"); + } + $sheet2 = $this->_getSheetIndex($sheet_name2); + if ($sheet2 == -1) { + return $this->raiseError("Unknown sheet name $sheet_name2 in formula"); + } + + // Reverse max and min sheet numbers if necessary + if ($sheet1 > $sheet2) { + list($sheet1, $sheet2) = array($sheet2, $sheet1); + } + } else { // Single sheet name only. + $sheet1 = $this->_getSheetIndex($ext_ref); + if ($sheet1 == -1) { + return $this->raiseError("Unknown sheet name $ext_ref in formula"); + } + $sheet2 = $sheet1; + } + + // References are stored relative to 0xFFFF. + $offset = -1 - $sheet1; + + return pack('vdvv', $offset, 0x00, $sheet1, $sheet2); + } + + /** + * Look up the REF index that corresponds to an external sheet name + * (or range). If it doesn't exist yet add it to the workbook's references + * array. It assumes all sheet names given must exist. + * + * @access private + * @param string $ext_ref The name of the external reference + * @return mixed The reference index in packed() format on success, + * PEAR_Error on failure + */ + function _getRefIndex($ext_ref) + { + $ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any. + $ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any. + + // Check if there is a sheet range eg., Sheet1:Sheet2. + if (preg_match("/:/", $ext_ref)) { + list($sheet_name1, $sheet_name2) = split(':', $ext_ref); + + $sheet1 = $this->_getSheetIndex($sheet_name1); + if ($sheet1 == -1) { + return $this->raiseError("Unknown sheet name $sheet_name1 in formula"); + } + $sheet2 = $this->_getSheetIndex($sheet_name2); + if ($sheet2 == -1) { + return $this->raiseError("Unknown sheet name $sheet_name2 in formula"); + } + + // Reverse max and min sheet numbers if necessary + if ($sheet1 > $sheet2) { + list($sheet1, $sheet2) = array($sheet2, $sheet1); + } + } else { // Single sheet name only. + $sheet1 = $this->_getSheetIndex($ext_ref); + if ($sheet1 == -1) { + return $this->raiseError("Unknown sheet name $ext_ref in formula"); + } + $sheet2 = $sheet1; + } + + // assume all references belong to this document + $supbook_index = 0x00; + $ref = pack('vvv', $supbook_index, $sheet1, $sheet2); + $total_references = count($this->_references); + $index = -1; + for ($i = 0; $i < $total_references; $i++) { + if ($ref == $this->_references[$i]) { + $index = $i; + break; + } + } + // if REF was not found add it to references array + if ($index == -1) { + $this->_references[$total_references] = $ref; + $index = $total_references; + } + + return pack('v', $index); + } + + /** + * Look up the index that corresponds to an external sheet name. The hash of + * sheet names is updated by the addworksheet() method of the + * Spreadsheet_Excel_Writer_Workbook class. + * + * @access private + * @return integer The sheet index, -1 if the sheet was not found + */ + function _getSheetIndex($sheet_name) + { + if (!isset($this->_ext_sheets[$sheet_name])) { + return -1; + } else { + return $this->_ext_sheets[$sheet_name]; + } + } + + /** + * This method is used to update the array of sheet names. It is + * called by the addWorksheet() method of the + * Spreadsheet_Excel_Writer_Workbook class. + * + * @access public + * @see Spreadsheet_Excel_Writer_Workbook::addWorksheet() + * @param string $name The name of the worksheet being added + * @param integer $index The index of the worksheet being added + */ + function setExtSheet($name, $index) + { + $this->_ext_sheets[$name] = $index; + } + + /** + * pack() row and column into the required 3 or 4 byte format. + * + * @access private + * @param string $cell The Excel cell reference to be packed + * @return array Array containing the row and column in packed() format + */ + function _cellToPackedRowcol($cell) + { + $cell = strtoupper($cell); + list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell); + if ($col >= 256) { + return $this->raiseError("Column in: $cell greater than 255"); + } + // FIXME: change for BIFF8 + if ($row >= 16384) { + return $this->raiseError("Row in: $cell greater than 16384 "); + } + + // Set the high bits to indicate if row or col are relative. + if ($this->_BIFF_version == 0x0500) { + $row |= $col_rel << 14; + $row |= $row_rel << 15; + $col = pack('C', $col); + } elseif ($this->_BIFF_version == 0x0600) { + $col |= $col_rel << 14; + $col |= $row_rel << 15; + $col = pack('v', $col); + } + $row = pack('v', $row); + + return array($row, $col); + } + + /** + * pack() row range into the required 3 or 4 byte format. + * Just using maximum col/rows, which is probably not the correct solution + * + * @access private + * @param string $range The Excel range to be packed + * @return array Array containing (row1,col1,row2,col2) in packed() format + */ + function _rangeToPackedRange($range) + { + preg_match('/(\$)?(\d+)\:(\$)?(\d+)/', $range, $match); + // return absolute rows if there is a $ in the ref + $row1_rel = empty($match[1]) ? 1 : 0; + $row1 = $match[2]; + $row2_rel = empty($match[3]) ? 1 : 0; + $row2 = $match[4]; + // Convert 1-index to zero-index + $row1--; + $row2--; + // Trick poor inocent Excel + $col1 = 0; + $col2 = 16383; // FIXME: maximum possible value for Excel 5 (change this!!!) + + // FIXME: this changes for BIFF8 + if (($row1 >= 16384) or ($row2 >= 16384)) { + return $this->raiseError("Row in: $range greater than 16384 "); + } + + // Set the high bits to indicate if rows are relative. + if ($this->_BIFF_version == 0x0500) { + $row1 |= $row1_rel << 14; // FIXME: probably a bug + $row2 |= $row2_rel << 15; + $col1 = pack('C', $col1); + $col2 = pack('C', $col2); + } elseif ($this->_BIFF_version == 0x0600) { + $col1 |= $row1_rel << 15; + $col2 |= $row2_rel << 15; + $col1 = pack('v', $col1); + $col2 = pack('v', $col2); + } + $row1 = pack('v', $row1); + $row2 = pack('v', $row2); + + return array($row1, $col1, $row2, $col2); + } + + /** + * Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero + * indexed row and column number. Also returns two (0,1) values to indicate + * whether the row or column are relative references. + * + * @access private + * @param string $cell The Excel cell reference in A1 format. + * @return array + */ + function _cellToRowcol($cell) + { + preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match); + // return absolute column if there is a $ in the ref + $col_rel = empty($match[1]) ? 1 : 0; + $col_ref = $match[2]; + $row_rel = empty($match[3]) ? 1 : 0; + $row = $match[4]; + + // Convert base26 column string to a number. + $expn = strlen($col_ref) - 1; + $col = 0; + $col_ref_length = strlen($col_ref); + for ($i = 0; $i < $col_ref_length; $i++) { + $col += (ord($col_ref{$i}) - ord('A') + 1) * pow(26, $expn); + $expn--; + } + + // Convert 1-index to zero-index + $row--; + $col--; + + return array($row, $col, $row_rel, $col_rel); + } + + /** + * Advance to the next valid token. + * + * @access private + */ + function _advance() + { + $i = $this->_current_char; + $formula_length = strlen($this->_formula); + // eat up white spaces + if ($i < $formula_length) { + while ($this->_formula{$i} == " ") { + $i++; + } + + if ($i < ($formula_length - 1)) { + $this->_lookahead = $this->_formula{$i+1}; + } + $token = ''; + } + + while ($i < $formula_length) { + $token .= $this->_formula{$i}; + if ($i < ($formula_length - 1)) { + $this->_lookahead = $this->_formula{$i+1}; + } else { + $this->_lookahead = ''; + } + + if ($this->_match($token) != '') { + //if ($i < strlen($this->_formula) - 1) { + // $this->_lookahead = $this->_formula{$i+1}; + //} + $this->_current_char = $i + 1; + $this->_current_token = $token; + return 1; + } + + if ($i < ($formula_length - 2)) { + $this->_lookahead = $this->_formula{$i+2}; + } else { // if we run out of characters _lookahead becomes empty + $this->_lookahead = ''; + } + $i++; + } + //die("Lexical error ".$this->_current_char); + } + + /** + * Checks if it's a valid token. + * + * @access private + * @param mixed $token The token to check. + * @return mixed The checked token or false on failure + */ + function _match($token) + { + switch($token) { + case SPREADSHEET_EXCEL_WRITER_ADD: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_SUB: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_MUL: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_DIV: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_OPEN: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_CLOSE: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_COMA: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_SEMICOLON: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_GT: + if ($this->_lookahead == '=') { // it's a GE token + break; + } + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_LT: + // it's a LE or a NE token + if (($this->_lookahead == '=') or ($this->_lookahead == '>')) { + break; + } + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_GE: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_LE: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_EQ: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_NE: + return $token; + break; + case SPREADSHEET_EXCEL_WRITER_CONCAT: + return $token; + break; + default: + // if it's a reference + if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$token) and + !preg_match("/[0-9]/",$this->_lookahead) and + ($this->_lookahead != ':') and ($this->_lookahead != '.') and + ($this->_lookahead != '!')) + { + return $token; + } + // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1) + elseif (preg_match("/^\w+(\:\w+)?\![A-Ia-i]?[A-Za-z][0-9]+$/u",$token) and + !preg_match("/[0-9]/",$this->_lookahead) and + ($this->_lookahead != ':') and ($this->_lookahead != '.')) + { + return $token; + } + // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1) + elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\![A-Ia-i]?[A-Za-z][0-9]+$/u",$token) and + !preg_match("/[0-9]/",$this->_lookahead) and + ($this->_lookahead != ':') and ($this->_lookahead != '.')) + { + return $token; + } + // if it's a range (A1:A2) + elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // if it's a range (A1..A2) + elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // If it's an external range like Sheet1!A1 or Sheet1:Sheet2!A1:B2 + elseif (preg_match("/^\w+(\:\w+)?\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/u",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // If it's an external range like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1:B2 + elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/u",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // If it's a number (check that it's not a sheet name or range) + elseif (is_numeric($token) and + (!is_numeric($token.$this->_lookahead) or ($this->_lookahead == '')) and + ($this->_lookahead != '!') and ($this->_lookahead != ':')) + { + return $token; + } + // If it's a string (of maximum 255 characters) + elseif (preg_match("/^\"[^\"]{0,255}\"$/",$token)) + { + return $token; + } + // if it's a function call + elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$token) and ($this->_lookahead == "(")) + { + return $token; + } + return ''; + } + } + + /** + * The parsing method. It parses a formula. + * + * @access public + * @param string $formula The formula to parse, without the initial equal + * sign (=). + * @return mixed true on success, PEAR_Error on failure + */ + function parse($formula) + { + $this->_current_char = 0; + $this->_formula = $formula; + $this->_lookahead = $formula{1}; + $this->_advance(); + $this->_parse_tree = $this->_condition(); + if (PEAR::isError($this->_parse_tree)) { + return $this->_parse_tree; + } + return true; + } + + /** + * It parses a condition. It assumes the following rule: + * Cond -> Expr [(">" | "<") Expr] + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + function _condition() + { + $result = $this->_expression(); + if (PEAR::isError($result)) { + return $result; + } + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LT) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgLT', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GT) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgGT', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LE) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgLE', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GE) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgGE', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_EQ) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgEQ', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_NE) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgNE', $result, $result2); + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_CONCAT) { + $this->_advance(); + $result2 = $this->_expression(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgConcat', $result, $result2); + } + return $result; + } + + /** + * It parses a expression. It assumes the following rule: + * Expr -> Term [("+" | "-") Term] + * -> "string" + * -> "-" Term + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + function _expression() + { + // If it's a string return a string node + if (preg_match("/^\"[^\"]{0,255}\"$/", $this->_current_token)) { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_SUB) { + // catch "-" Term + $this->_advance(); + $result2 = $this->_expression(); + $result = $this->_createTree('ptgUminus', $result2, ''); + return $result; + } + $result = $this->_term(); + if (PEAR::isError($result)) { + return $result; + } + while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD) or + ($this->_current_token == SPREADSHEET_EXCEL_WRITER_SUB)) { + /**/ + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD) { + $this->_advance(); + $result2 = $this->_term(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgAdd', $result, $result2); + } else { + $this->_advance(); + $result2 = $this->_term(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgSub', $result, $result2); + } + } + return $result; + } + + /** + * This function just introduces a ptgParen element in the tree, so that Excel + * doesn't get confused when working with a parenthesized formula afterwards. + * + * @access private + * @see _fact() + * @return array The parsed ptg'd tree + */ + function _parenthesizedExpression() + { + $result = $this->_createTree('ptgParen', $this->_expression(), ''); + return $result; + } + + /** + * It parses a term. It assumes the following rule: + * Term -> Fact [("*" | "/") Fact] + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + function _term() + { + $result = $this->_fact(); + if (PEAR::isError($result)) { + return $result; + } + while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL) or + ($this->_current_token == SPREADSHEET_EXCEL_WRITER_DIV)) { + /**/ + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL) { + $this->_advance(); + $result2 = $this->_fact(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgMul', $result, $result2); + } else { + $this->_advance(); + $result2 = $this->_fact(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgDiv', $result, $result2); + } + } + return $result; + } + + /** + * It parses a factor. It assumes the following rule: + * Fact -> ( Expr ) + * | CellRef + * | CellRange + * | Number + * | Function + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + function _fact() + { + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_OPEN) { + $this->_advance(); // eat the "(" + $result = $this->_parenthesizedExpression(); + if ($this->_current_token != SPREADSHEET_EXCEL_WRITER_CLOSE) { + return $this->raiseError("')' token expected."); + } + $this->_advance(); // eat the ")" + return $result; + } + // if it's a reference + if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1) + elseif (preg_match("/^\w+(\:\w+)?\![A-Ia-i]?[A-Za-z][0-9]+$/u",$this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1) + elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\![A-Ia-i]?[A-Za-z][0-9]+$/u",$this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + // if it's a range + elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$this->_current_token) or + preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$this->_current_token)) + { + $result = $this->_current_token; + $this->_advance(); + return $result; + } + // If it's an external range (Sheet1!A1 or Sheet1!A1:B2) + elseif (preg_match("/^\w+(\:\w+)?\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/u",$this->_current_token)) + { + $result = $this->_current_token; + $this->_advance(); + return $result; + } + // If it's an external range ('Sheet1'!A1 or 'Sheet1'!A1:B2) + elseif (preg_match("/^'[\w -]+(\:[\w -]+)?'\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/u",$this->_current_token)) + { + $result = $this->_current_token; + $this->_advance(); + return $result; + } + elseif (is_numeric($this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + // if it's a function call + elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$this->_current_token)) + { + $result = $this->_func(); + return $result; + } + return $this->raiseError("Syntax error: ".$this->_current_token. + ", lookahead: ".$this->_lookahead. + ", current char: ".$this->_current_char); + } + + /** + * It parses a function call. It assumes the following rule: + * Func -> ( Expr [,Expr]* ) + * + * @access private + * @return mixed The parsed ptg'd tree on success, PEAR_Error on failure + */ + function _func() + { + $num_args = 0; // number of arguments received + $function = strtoupper($this->_current_token); + $result = ''; // initialize result + $this->_advance(); + $this->_advance(); // eat the "(" + while ($this->_current_token != ')') { + /**/ + if ($num_args > 0) { + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_COMA or + $this->_current_token == SPREADSHEET_EXCEL_WRITER_SEMICOLON) + { + $this->_advance(); // eat the "," or ";" + } else { + return $this->raiseError("Syntax error: comma expected in ". + "function $function, arg #{$num_args}"); + } + $result2 = $this->_condition(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('arg', $result, $result2); + } else { // first argument + $result2 = $this->_condition(); + if (PEAR::isError($result2)) { + return $result2; + } + $result = $this->_createTree('arg', '', $result2); + } + $num_args++; + } + if (!isset($this->_functions[$function])) { + return $this->raiseError("Function $function() doesn't exist"); + } + $args = $this->_functions[$function][1]; + // If fixed number of args eg. TIME($i,$j,$k). Check that the number of args is valid. + if (($args >= 0) and ($args != $num_args)) { + return $this->raiseError("Incorrect number of arguments in function $function() "); + } + + $result = $this->_createTree($function, $result, $num_args); + $this->_advance(); // eat the ")" + return $result; + } + + /** + * Creates a tree. In fact an array which may have one or two arrays (sub-trees) + * as elements. + * + * @access private + * @param mixed $value The value of this node. + * @param mixed $left The left array (sub-tree) or a final node. + * @param mixed $right The right array (sub-tree) or a final node. + * @return array A tree + */ + function _createTree($value, $left, $right) + { + return array('value' => $value, 'left' => $left, 'right' => $right); + } + + /** + * Builds a string containing the tree in reverse polish notation (What you + * would use in a HP calculator stack). + * The following tree: + * + * + + * / \ + * 2 3 + * + * produces: "23+" + * + * The following tree: + * + * + + * / \ + * 3 * + * / \ + * 6 A1 + * + * produces: "36A1*+" + * + * In fact all operands, functions, references, etc... are written as ptg's + * + * @access public + * @param array $tree The optional tree to convert. + * @return string The tree in reverse polish notation + */ + function toReversePolish($tree = array()) + { + $polish = ""; // the string we are going to return + if (empty($tree)) { // If it's the first call use _parse_tree + $tree = $this->_parse_tree; + } + if (is_array($tree['left'])) { + $converted_tree = $this->toReversePolish($tree['left']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } elseif ($tree['left'] != '') { // It's a final node + $converted_tree = $this->_convert($tree['left']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } + if (is_array($tree['right'])) { + $converted_tree = $this->toReversePolish($tree['right']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } elseif ($tree['right'] != '') { // It's a final node + $converted_tree = $this->_convert($tree['right']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } + // if it's a function convert it here (so we can set it's arguments) + if (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/",$tree['value']) and + !preg_match('/^([A-Ia-i]?[A-Za-z])(\d+)$/',$tree['value']) and + !preg_match("/^[A-Ia-i]?[A-Za-z](\d+)\.\.[A-Ia-i]?[A-Za-z](\d+)$/",$tree['value']) and + !is_numeric($tree['value']) and + !isset($this->ptg[$tree['value']])) + { + // left subtree for a function is always an array. + if ($tree['left'] != '') { + $left_tree = $this->toReversePolish($tree['left']); + } else { + $left_tree = ''; + } + if (PEAR::isError($left_tree)) { + return $left_tree; + } + // add it's left subtree and return. + return $left_tree.$this->_convertFunction($tree['value'], $tree['right']); + } else { + $converted_tree = $this->_convert($tree['value']); + if (PEAR::isError($converted_tree)) { + return $converted_tree; + } + } + $polish .= $converted_tree; + return $polish; + } +} +?> diff --git a/functions/PEAR/Spreadsheet/Excel/Writer/Validator.php b/functions/PEAR/Spreadsheet/Excel/Writer/Validator.php new file mode 100755 index 000000000..0f091c61d --- /dev/null +++ b/functions/PEAR/Spreadsheet/Excel/Writer/Validator.php @@ -0,0 +1,230 @@ + +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +//require_once('PEAR.php'); + +// Possible operator types + +/* +FIXME: change prefixes +*/ +define("OP_BETWEEN", 0x00); +define("OP_NOTBETWEEN", 0x01); +define("OP_EQUAL", 0x02); +define("OP_NOTEQUAL", 0x03); +define("OP_GT", 0x04); +define("OP_LT", 0x05); +define("OP_GTE", 0x06); +define("OP_LTE", 0x07); + +/** +* Baseclass for generating Excel DV records (validations) +* +* @author Herman Kuiper +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ +class Spreadsheet_Excel_Writer_Validator +{ + var $_type; + var $_style; + var $_fixedList; + var $_blank; + var $_incell; + var $_showprompt; + var $_showerror; + var $_title_prompt; + var $_descr_prompt; + var $_title_error; + var $_descr_error; + var $_operator; + var $_formula1; + var $_formula2; + /** + * The parser from the workbook. Used to parse validation formulas also + * @var Spreadsheet_Excel_Writer_Parser + */ + var $_parser; + + function Spreadsheet_Excel_Writer_Validator(&$parser) + { + $this->_parser = $parser; + $this->_type = 0x01; // FIXME: add method for setting datatype + $this->_style = 0x00; + $this->_fixedList = false; + $this->_blank = false; + $this->_incell = false; + $this->_showprompt = false; + $this->_showerror = true; + $this->_title_prompt = "\x00"; + $this->_descr_prompt = "\x00"; + $this->_title_error = "\x00"; + $this->_descr_error = "\x00"; + $this->_operator = 0x00; // default is equal + $this->_formula1 = ''; + $this->_formula2 = ''; + } + + function setPrompt($promptTitle = "\x00", $promptDescription = "\x00", $showPrompt = true) + { + $this->_showprompt = $showPrompt; + $this->_title_prompt = $promptTitle; + $this->_descr_prompt = $promptDescription; + } + + function setError($errorTitle = "\x00", $errorDescription = "\x00", $showError = true) + { + $this->_showerror = $showError; + $this->_title_error = $errorTitle; + $this->_descr_error = $errorDescription; + } + + function allowBlank() + { + $this->_blank = true; + } + + function onInvalidStop() + { + $this->_style = 0x00; + } + + function onInvalidWarn() + { + $this->_style = 0x01; + } + + function onInvalidInfo() + { + $this->_style = 0x02; + } + + function setFormula1($formula) + { + // Parse the formula using the parser in Parser.php + $error = $this->_parser->parse($formula); + if (PEAR::isError($error)) { + return $this->_formula1; + } + + $this->_formula1 = $this->_parser->toReversePolish(); + if (PEAR::isError($this->_formula1)) { + return $this->_formula1; + } + return true; + } + + function setFormula2($formula) + { + // Parse the formula using the parser in Parser.php + $error = $this->_parser->parse($formula); + if (PEAR::isError($error)) { + return $this->_formula2; + } + + $this->_formula2 = $this->_parser->toReversePolish(); + if (PEAR::isError($this->_formula2)) { + return $this->_formula2; + } + return true; + } + + function _getOptions() + { + $options = $this->_type; + $options |= $this->_style << 3; + if ($this->_fixedList) { + $options |= 0x80; + } + if ($this->_blank) { + $options |= 0x100; + } + if (!$this->_incell) { + $options |= 0x200; + } + if ($this->_showprompt) { + $options |= 0x40000; + } + if ($this->_showerror) { + $options |= 0x80000; + } + $options |= $this->_operator << 20; + + return $options; + } + + function _getData() + { + $title_prompt_len = strlen($this->_title_prompt); + $descr_prompt_len = strlen($this->_descr_prompt); + $title_error_len = strlen($this->_title_error); + $descr_error_len = strlen($this->_descr_error); + + $formula1_size = strlen($this->_formula1); + $formula2_size = strlen($this->_formula2); + + $data = pack("V", $this->_getOptions()); + $data .= pack("vC", $title_prompt_len, 0x00) . $this->_title_prompt; + $data .= pack("vC", $title_error_len, 0x00) . $this->_title_error; + $data .= pack("vC", $descr_prompt_len, 0x00) . $this->_descr_prompt; + $data .= pack("vC", $descr_error_len, 0x00) . $this->_descr_error; + + $data .= pack("vv", $formula1_size, 0x0000) . $this->_formula1; + $data .= pack("vv", $formula2_size, 0x0000) . $this->_formula2; + + return $data; + } +} + +/*class Spreadsheet_Excel_Writer_Validation_List extends Spreadsheet_Excel_Writer_Validation +{ + function Spreadsheet_Excel_Writer_Validation_list() + { + parent::Spreadsheet_Excel_Writer_Validation(); + $this->_type = 0x03; + } + + function setList($source, $incell = true) + { + $this->_incell = $incell; + $this->_fixedList = true; + + $source = implode("\x00", $source); + $this->_formula1 = pack("CCC", 0x17, strlen($source), 0x0c) . $source; + } + + function setRow($row, $col1, $col2, $incell = true) + { + $this->_incell = $incell; + //$this->_formula1 = ...; + } + + function setCol($col, $row1, $row2, $incell = true) + { + $this->_incell = $incell; + //$this->_formula1 = ...; + } +}*/ + +?> diff --git a/functions/PEAR/Spreadsheet/Excel/Writer/Workbook.php b/functions/PEAR/Spreadsheet/Excel/Writer/Workbook.php new file mode 100755 index 000000000..4d938c287 --- /dev/null +++ b/functions/PEAR/Spreadsheet/Excel/Writer/Workbook.php @@ -0,0 +1,1591 @@ + +* +* The majority of this is _NOT_ my code. I simply ported it from the +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +require_once( dirname(__FILE__) . '/Format.php'); +require_once( dirname(__FILE__) . '/BIFFwriter.php'); +require_once( dirname(__FILE__) . '/Worksheet.php'); +require_once( dirname(__FILE__) . '/Parser.php'); +require_once( dirname(__FILE__) . '/../../../OLE/PPS/Root.php'); +require_once( dirname(__FILE__) . '/../../../OLE/PPS/File.php'); + +/** +* Class for generating Excel Spreadsheets +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_Workbook extends Spreadsheet_Excel_Writer_BIFFwriter +{ + /** + * Filename for the Workbook + * @var string + */ + var $_filename; + + /** + * Formula parser + * @var object Parser + */ + var $_parser; + + /** + * Flag for 1904 date system (0 => base date is 1900, 1 => base date is 1904) + * @var integer + */ + var $_1904; + + /** + * The active worksheet of the workbook (0 indexed) + * @var integer + */ + var $_activesheet; + + /** + * 1st displayed worksheet in the workbook (0 indexed) + * @var integer + */ + var $_firstsheet; + + /** + * Number of workbook tabs selected + * @var integer + */ + var $_selected; + + /** + * Index for creating adding new formats to the workbook + * @var integer + */ + var $_xf_index; + + /** + * Flag for preventing close from being called twice. + * @var integer + * @see close() + */ + var $_fileclosed; + + /** + * The BIFF file size for the workbook. + * @var integer + * @see _calcSheetOffsets() + */ + var $_biffsize; + + /** + * The default sheetname for all sheets created. + * @var string + */ + var $_sheetname; + + /** + * The default XF format. + * @var object Format + */ + var $_tmp_format; + + /** + * Array containing references to all of this workbook's worksheets + * @var array + */ + var $_worksheets; + + /** + * Array of sheetnames for creating the EXTERNSHEET records + * @var array + */ + var $_sheetnames; + + /** + * Array containing references to all of this workbook's formats + * @var array + */ + var $_formats; + + /** + * Array containing the colour palette + * @var array + */ + var $_palette; + + /** + * The default format for URLs. + * @var object Format + */ + var $_url_format; + + /** + * The codepage indicates the text encoding used for strings + * @var integer + */ + var $_codepage; + + /** + * The country code used for localization + * @var integer + */ + var $_country_code; + + /** + * number of bytes for sizeinfo of strings + * @var integer + */ + var $_string_sizeinfo_size; + + /** + * Class constructor + * + * @param string filename for storing the workbook. "-" for writing to stdout. + * @access public + */ + function Spreadsheet_Excel_Writer_Workbook($filename) + { + // It needs to call its parent's constructor explicitly + $this->Spreadsheet_Excel_Writer_BIFFwriter(); + + $this->_filename = $filename; + $this->_parser = new Spreadsheet_Excel_Writer_Parser($this->_byte_order, $this->_BIFF_version); + $this->_1904 = 0; + $this->_activesheet = 0; + $this->_firstsheet = 0; + $this->_selected = 0; + $this->_xf_index = 16; // 15 style XF's and 1 cell XF. + $this->_fileclosed = 0; + $this->_biffsize = 0; + $this->_sheetname = 'Sheet'; + $this->_tmp_format = new Spreadsheet_Excel_Writer_Format($this->_BIFF_version); + $this->_worksheets = array(); + $this->_sheetnames = array(); + $this->_formats = array(); + $this->_palette = array(); + $this->_codepage = 0x04E4; // FIXME: should change for BIFF8 + $this->_country_code = -1; + $this->_string_sizeinfo = 3; + + // Add the default format for hyperlinks + $this->_url_format = $this->addFormat(array('color' => 'blue', 'underline' => 1)); + $this->_str_total = 0; + $this->_str_unique = 0; + $this->_str_table = array(); + $this->_setPaletteXl97(); + } + + /** + * Calls finalization methods. + * This method should always be the last one to be called on every workbook + * + * @access public + * @return mixed true on success. PEAR_Error on failure + */ + function close() + { + if ($this->_fileclosed) { // Prevent close() from being called twice. + return true; + } + $res = $this->_storeWorkbook(); + if ($this->isError($res)) { + return $this->raiseError($res->getMessage()); + } + $this->_fileclosed = 1; + return true; + } + + /** + * An accessor for the _worksheets[] array + * Returns an array of the worksheet objects in a workbook + * It actually calls to worksheets() + * + * @access public + * @see worksheets() + * @return array + */ + function sheets() + { + return $this->worksheets(); + } + + /** + * An accessor for the _worksheets[] array. + * Returns an array of the worksheet objects in a workbook + * + * @access public + * @return array + */ + function worksheets() + { + return $this->_worksheets; + } + + /** + * Sets the BIFF version. + * This method exists just to access experimental functionality + * from BIFF8. It will be deprecated ! + * Only possible value is 8 (Excel 97/2000). + * For any other value it fails silently. + * + * @access public + * @param integer $version The BIFF version + */ + function setVersion($version) + { + if ($version == 8) { // only accept version 8 + $version = 0x0600; + $this->_BIFF_version = $version; + // change BIFFwriter limit for CONTINUE records + $this->_limit = 8228; + $this->_tmp_format->_BIFF_version = $version; + $this->_url_format->_BIFF_version = $version; + $this->_parser->_BIFF_version = $version; + $this->_codepage = 0x04B0; + + $total_worksheets = count($this->_worksheets); + // change version for all worksheets too + for ($i = 0; $i < $total_worksheets; $i++) { + $this->_worksheets[$i]->_BIFF_version = $version; + } + + $total_formats = count($this->_formats); + // change version for all formats too + for ($i = 0; $i < $total_formats; $i++) { + $this->_formats[$i]->_BIFF_version = $version; + } + } + } + + /** + * Set the country identifier for the workbook + * + * @access public + * @param integer $code Is the international calling country code for the + * chosen country. + */ + function setCountry($code) + { + $this->_country_code = $code; + } + + /** + * Add a new worksheet to the Excel workbook. + * If no name is given the name of the worksheet will be Sheeti$i, with + * $i in [1..]. + * + * @access public + * @param string $name the optional name of the worksheet + * @return mixed reference to a worksheet object on success, PEAR_Error + * on failure + */ + function &addWorksheet($name = '') + { + $index = count($this->_worksheets); + $sheetname = $this->_sheetname; + + if ($name == '') { + $name = $sheetname.($index+1); + } + + // Check that sheetname is <= 31 chars (Excel limit before BIFF8). + if ($this->_BIFF_version != 0x0600) + { + if (strlen($name) > 31) { + return $this->raiseError("Sheetname $name must be <= 31 chars"); + } + } + + // Check that the worksheet name doesn't already exist: a fatal Excel error. + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + if ($this->_worksheets[$i]->getName() == $name) { + return $this->raiseError("Worksheet '$name' already exists"); + } + } + + $worksheet = new Spreadsheet_Excel_Writer_Worksheet($this->_BIFF_version, + $name, $index, + $this->_activesheet, $this->_firstsheet, + $this->_str_total, $this->_str_unique, + $this->_str_table, $this->_url_format, + $this->_parser, $this->_tmp_dir); + + $this->_worksheets[$index] = &$worksheet; // Store ref for iterator + $this->_sheetnames[$index] = $name; // Store EXTERNSHEET names + $this->_parser->setExtSheet($name, $index); // Register worksheet name with parser + return $worksheet; + } + + /** + * Add a new format to the Excel workbook. + * Also, pass any properties to the Format constructor. + * + * @access public + * @param array $properties array with properties for initializing the format. + * @return &Spreadsheet_Excel_Writer_Format reference to an Excel Format + */ + function &addFormat($properties = array()) + { + $format = new Spreadsheet_Excel_Writer_Format($this->_BIFF_version, $this->_xf_index, $properties); + $this->_xf_index += 1; + $this->_formats[] = &$format; + return $format; + } + + /** + * Create new validator. + * + * @access public + * @return &Spreadsheet_Excel_Writer_Validator reference to a Validator + */ + function &addValidator() + { + include_once 'Spreadsheet/Excel/Writer/Validator.php'; + /* FIXME: check for successful inclusion*/ + $valid = new Spreadsheet_Excel_Writer_Validator($this->_parser); + return $valid; + } + + /** + * Change the RGB components of the elements in the colour palette. + * + * @access public + * @param integer $index colour index + * @param integer $red red RGB value [0-255] + * @param integer $green green RGB value [0-255] + * @param integer $blue blue RGB value [0-255] + * @return integer The palette index for the custom color + */ + function setCustomColor($index, $red, $green, $blue) + { + // Match a HTML #xxyyzz style parameter + /*if (defined $_[1] and $_[1] =~ /^#(\w\w)(\w\w)(\w\w)/ ) { + @_ = ($_[0], hex $1, hex $2, hex $3); + }*/ + + // Check that the colour index is the right range + if ($index < 8 or $index > 64) { + // TODO: assign real error codes + return $this->raiseError("Color index $index outside range: 8 <= index <= 64"); + } + + // Check that the colour components are in the right range + if (($red < 0 or $red > 255) || + ($green < 0 or $green > 255) || + ($blue < 0 or $blue > 255)) + { + return $this->raiseError("Color component outside range: 0 <= color <= 255"); + } + + $index -= 8; // Adjust colour index (wingless dragonfly) + + // Set the RGB value + $this->_palette[$index] = array($red, $green, $blue, 0); + return($index + 8); + } + + /** + * Sets the colour palette to the Excel 97+ default. + * + * @access private + */ + function _setPaletteXl97() + { + $this->_palette = array( + array(0x00, 0x00, 0x00, 0x00), // 8 + array(0xff, 0xff, 0xff, 0x00), // 9 + array(0xff, 0x00, 0x00, 0x00), // 10 + array(0x00, 0xff, 0x00, 0x00), // 11 + array(0x00, 0x00, 0xff, 0x00), // 12 + array(0xff, 0xff, 0x00, 0x00), // 13 + array(0xff, 0x00, 0xff, 0x00), // 14 + array(0x00, 0xff, 0xff, 0x00), // 15 + array(0x80, 0x00, 0x00, 0x00), // 16 + array(0x00, 0x80, 0x00, 0x00), // 17 + array(0x00, 0x00, 0x80, 0x00), // 18 + array(0x80, 0x80, 0x00, 0x00), // 19 + array(0x80, 0x00, 0x80, 0x00), // 20 + array(0x00, 0x80, 0x80, 0x00), // 21 + array(0xc0, 0xc0, 0xc0, 0x00), // 22 + array(0x80, 0x80, 0x80, 0x00), // 23 + array(0x99, 0x99, 0xff, 0x00), // 24 + array(0x99, 0x33, 0x66, 0x00), // 25 + array(0xff, 0xff, 0xcc, 0x00), // 26 + array(0xcc, 0xff, 0xff, 0x00), // 27 + array(0x66, 0x00, 0x66, 0x00), // 28 + array(0xff, 0x80, 0x80, 0x00), // 29 + array(0x00, 0x66, 0xcc, 0x00), // 30 + array(0xcc, 0xcc, 0xff, 0x00), // 31 + array(0x00, 0x00, 0x80, 0x00), // 32 + array(0xff, 0x00, 0xff, 0x00), // 33 + array(0xff, 0xff, 0x00, 0x00), // 34 + array(0x00, 0xff, 0xff, 0x00), // 35 + array(0x80, 0x00, 0x80, 0x00), // 36 + array(0x80, 0x00, 0x00, 0x00), // 37 + array(0x00, 0x80, 0x80, 0x00), // 38 + array(0x00, 0x00, 0xff, 0x00), // 39 + array(0x00, 0xcc, 0xff, 0x00), // 40 + array(0xcc, 0xff, 0xff, 0x00), // 41 + array(0xcc, 0xff, 0xcc, 0x00), // 42 + array(0xff, 0xff, 0x99, 0x00), // 43 + array(0x99, 0xcc, 0xff, 0x00), // 44 + array(0xff, 0x99, 0xcc, 0x00), // 45 + array(0xcc, 0x99, 0xff, 0x00), // 46 + array(0xff, 0xcc, 0x99, 0x00), // 47 + array(0x33, 0x66, 0xff, 0x00), // 48 + array(0x33, 0xcc, 0xcc, 0x00), // 49 + array(0x99, 0xcc, 0x00, 0x00), // 50 + array(0xff, 0xcc, 0x00, 0x00), // 51 + array(0xff, 0x99, 0x00, 0x00), // 52 + array(0xff, 0x66, 0x00, 0x00), // 53 + array(0x66, 0x66, 0x99, 0x00), // 54 + array(0x96, 0x96, 0x96, 0x00), // 55 + array(0x00, 0x33, 0x66, 0x00), // 56 + array(0x33, 0x99, 0x66, 0x00), // 57 + array(0x00, 0x33, 0x00, 0x00), // 58 + array(0x33, 0x33, 0x00, 0x00), // 59 + array(0x99, 0x33, 0x00, 0x00), // 60 + array(0x99, 0x33, 0x66, 0x00), // 61 + array(0x33, 0x33, 0x99, 0x00), // 62 + array(0x33, 0x33, 0x33, 0x00), // 63 + ); + } + + /** + * Assemble worksheets into a workbook and send the BIFF data to an OLE + * storage. + * + * @access private + * @return mixed true on success. PEAR_Error on failure + */ + public function _storeWorkbook() + { + if (count($this->_worksheets) == 0) { + return true; + } + + // Ensure that at least one worksheet has been selected. + if ($this->_activesheet == 0) { + $this->_worksheets[0]->selected = 1; + } + + // Calculate the number of selected worksheet tabs and call the finalization + // methods for each worksheet + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + if ($this->_worksheets[$i]->selected) { + $this->_selected++; + } + $this->_worksheets[$i]->close($this->_sheetnames); + } + + // Add Workbook globals + $this->_storeBof(0x0005); + $this->_storeCodepage(); + if ($this->_BIFF_version == 0x0600) { + $this->_storeWindow1(); + } + if ($this->_BIFF_version == 0x0500) { + $this->_storeExterns(); // For print area and repeat rows + } + $this->_storeNames(); // For print area and repeat rows + if ($this->_BIFF_version == 0x0500) { + $this->_storeWindow1(); + } + $this->_storeDatemode(); + $this->_storeAllFonts(); + $this->_storeAllNumFormats(); + $this->_storeAllXfs(); + $this->_storeAllStyles(); + $this->_storePalette(); + $this->_calcSheetOffsets(); + + // Add BOUNDSHEET records + for ($i = 0; $i < $total_worksheets; $i++) { + $this->_storeBoundsheet($this->_worksheets[$i]->name,$this->_worksheets[$i]->offset); + } + + if ($this->_country_code != -1) { + $this->_storeCountry(); + } + + if ($this->_BIFF_version == 0x0600) { + //$this->_storeSupbookInternal(); + /* TODO: store external SUPBOOK records and XCT and CRN records + in case of external references for BIFF8 */ + //$this->_storeExternsheetBiff8(); + $this->_storeSharedStringsTable(); + } + + // End Workbook globals + $this->_storeEof(); + + // Store the workbook in an OLE container + $res = $this->_storeOLEFile(); + if ($this->isError($res)) { + return $this->raiseError($res->getMessage()); + } + return true; + } + + /** + * Store the workbook in an OLE container + * + * @access private + * @return mixed true on success. PEAR_Error on failure + */ + function _storeOLEFile() + { + if($this->_BIFF_version == 0x0600) { + $OLE = new OLE_PPS_File(OLE::Asc2Ucs('Workbook')); + } else { + $OLE = new OLE_PPS_File(OLE::Asc2Ucs('Book')); + } + if ($this->_tmp_dir != '') { + $OLE->setTempDir($this->_tmp_dir); + } + $res = $OLE->init(); + if ($this->isError($res)) { + return $this->raiseError("OLE Error: ".$res->getMessage()); + } + $OLE->append($this->_data); + + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + while ($tmp = $this->_worksheets[$i]->getData()) { + $OLE->append($tmp); + } + } + + $root = new OLE_PPS_Root(time(), time(), array($OLE)); + if ($this->_tmp_dir != '') { + $root->setTempDir($this->_tmp_dir); + } + + $res = $root->save($this->_filename); + if ($this->isError($res)) { + return $this->raiseError("OLE Error: ".$res->getMessage()); + } + return true; + } + + /** + * Calculate offsets for Worksheet BOF records. + * + * @access private + */ + function _calcSheetOffsets() + { + if ($this->_BIFF_version == 0x0600) { + $boundsheet_length = 12; // fixed length for a BOUNDSHEET record + } else { + $boundsheet_length = 11; + } + $EOF = 4; + $offset = $this->_datasize; + + if ($this->_BIFF_version == 0x0600) { + // add the length of the SST + /* TODO: check this works for a lot of strings (> 8224 bytes) */ + $offset += $this->_calculateSharedStringsSizes(); + if ($this->_country_code != -1) { + $offset += 8; // adding COUNTRY record + } + // add the lenght of SUPBOOK, EXTERNSHEET and NAME records + //$offset += 8; // FIXME: calculate real value when storing the records + } + $total_worksheets = count($this->_worksheets); + // add the length of the BOUNDSHEET records + for ($i = 0; $i < $total_worksheets; $i++) { + $offset += $boundsheet_length + strlen($this->_worksheets[$i]->name); + } + $offset += $EOF; + + for ($i = 0; $i < $total_worksheets; $i++) { + $this->_worksheets[$i]->offset = $offset; + $offset += $this->_worksheets[$i]->_datasize; + } + $this->_biffsize = $offset; + } + + /** + * Store the Excel FONT records. + * + * @access private + */ + function _storeAllFonts() + { + // tmp_format is added by the constructor. We use this to write the default XF's + $format = $this->_tmp_format; + $font = $format->getFont(); + + // Note: Fonts are 0-indexed. According to the SDK there is no index 4, + // so the following fonts are 0, 1, 2, 3, 5 + // + for ($i = 1; $i <= 5; $i++){ + $this->_append($font); + } + + // Iterate through the XF objects and write a FONT record if it isn't the + // same as the default FONT and if it hasn't already been used. + // + $fonts = array(); + $index = 6; // The first user defined FONT + + $key = $format->getFontKey(); // The default font from _tmp_format + $fonts[$key] = 0; // Index of the default font + + $total_formats = count($this->_formats); + for ($i = 0; $i < $total_formats; $i++) { + $key = $this->_formats[$i]->getFontKey(); + if (isset($fonts[$key])) { + // FONT has already been used + $this->_formats[$i]->font_index = $fonts[$key]; + } else { + // Add a new FONT record + $fonts[$key] = $index; + $this->_formats[$i]->font_index = $index; + $index++; + $font = $this->_formats[$i]->getFont(); + $this->_append($font); + } + } + } + + /** + * Store user defined numerical formats i.e. FORMAT records + * + * @access private + */ + function _storeAllNumFormats() + { + // Leaning num_format syndrome + $hash_num_formats = array(); + $num_formats = array(); + $index = 164; + + // Iterate through the XF objects and write a FORMAT record if it isn't a + // built-in format type and if the FORMAT string hasn't already been used. + $total_formats = count($this->_formats); + for ($i = 0; $i < $total_formats; $i++) { + $num_format = $this->_formats[$i]->_num_format; + + // Check if $num_format is an index to a built-in format. + // Also check for a string of zeros, which is a valid format string + // but would evaluate to zero. + // + if (!preg_match("/^0+\d/", $num_format)) { + if (preg_match("/^\d+$/", $num_format)) { // built-in format + continue; + } + } + + if (isset($hash_num_formats[$num_format])) { + // FORMAT has already been used + $this->_formats[$i]->_num_format = $hash_num_formats[$num_format]; + } else{ + // Add a new FORMAT + $hash_num_formats[$num_format] = $index; + $this->_formats[$i]->_num_format = $index; + array_push($num_formats,$num_format); + $index++; + } + } + + // Write the new FORMAT records starting from 0xA4 + $index = 164; + foreach ($num_formats as $num_format) { + $this->_storeNumFormat($num_format,$index); + $index++; + } + } + + /** + * Write all XF records. + * + * @access private + */ + function _storeAllXfs() + { + // _tmp_format is added by the constructor. We use this to write the default XF's + // The default font index is 0 + // + $format = $this->_tmp_format; + for ($i = 0; $i <= 14; $i++) { + $xf = $format->getXf('style'); // Style XF + $this->_append($xf); + } + + $xf = $format->getXf('cell'); // Cell XF + $this->_append($xf); + + // User defined XFs + $total_formats = count($this->_formats); + for ($i = 0; $i < $total_formats; $i++) { + $xf = $this->_formats[$i]->getXf('cell'); + $this->_append($xf); + } + } + + /** + * Write all STYLE records. + * + * @access private + */ + function _storeAllStyles() + { + $this->_storeStyle(); + } + + /** + * Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for + * the NAME records. + * + * @access private + */ + function _storeExterns() + { + // Create EXTERNCOUNT with number of worksheets + $this->_storeExterncount(count($this->_worksheets)); + + // Create EXTERNSHEET for each worksheet + foreach ($this->_sheetnames as $sheetname) { + $this->_storeExternsheet($sheetname); + } + } + + /** + * Write the NAME record to define the print area and the repeat rows and cols. + * + * @access private + */ + function _storeNames() + { + // Create the print area NAME records + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + // Write a Name record if the print area has been defined + if (isset($this->_worksheets[$i]->print_rowmin)) { + $this->_storeNameShort( + $this->_worksheets[$i]->index, + 0x06, // NAME type + $this->_worksheets[$i]->print_rowmin, + $this->_worksheets[$i]->print_rowmax, + $this->_worksheets[$i]->print_colmin, + $this->_worksheets[$i]->print_colmax + ); + } + } + + // Create the print title NAME records + $total_worksheets = count($this->_worksheets); + for ($i = 0; $i < $total_worksheets; $i++) { + $rowmin = $this->_worksheets[$i]->title_rowmin; + $rowmax = $this->_worksheets[$i]->title_rowmax; + $colmin = $this->_worksheets[$i]->title_colmin; + $colmax = $this->_worksheets[$i]->title_colmax; + + // Determine if row + col, row, col or nothing has been defined + // and write the appropriate record + // + if (isset($rowmin) && isset($colmin)) { + // Row and column titles have been defined. + // Row title has been defined. + $this->_storeNameLong( + $this->_worksheets[$i]->index, + 0x07, // NAME type + $rowmin, + $rowmax, + $colmin, + $colmax + ); + } elseif (isset($rowmin)) { + // Row title has been defined. + $this->_storeNameShort( + $this->_worksheets[$i]->index, + 0x07, // NAME type + $rowmin, + $rowmax, + 0x00, + 0xff + ); + } elseif (isset($colmin)) { + // Column title has been defined. + $this->_storeNameShort( + $this->_worksheets[$i]->index, + 0x07, // NAME type + 0x0000, + 0x3fff, + $colmin, + $colmax + ); + } else { + // Print title hasn't been defined. + } + } + } + + + + + /****************************************************************************** + * + * BIFF RECORDS + * + */ + + /** + * Stores the CODEPAGE biff record. + * + * @access private + */ + function _storeCodepage() + { + $record = 0x0042; // Record identifier + $length = 0x0002; // Number of bytes to follow + $cv = $this->_codepage; // The code page + + $header = pack('vv', $record, $length); + $data = pack('v', $cv); + + $this->_append($header . $data); + } + + /** + * Write Excel BIFF WINDOW1 record. + * + * @access private + */ + function _storeWindow1() + { + $record = 0x003D; // Record identifier + $length = 0x0012; // Number of bytes to follow + + $xWn = 0x0000; // Horizontal position of window + $yWn = 0x0000; // Vertical position of window + $dxWn = 0x25BC; // Width of window + $dyWn = 0x1572; // Height of window + + $grbit = 0x0038; // Option flags + $ctabsel = $this->_selected; // Number of workbook tabs selected + $wTabRatio = 0x0258; // Tab to scrollbar ratio + + $itabFirst = $this->_firstsheet; // 1st displayed worksheet + $itabCur = $this->_activesheet; // Active worksheet + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn, + $grbit, + $itabCur, $itabFirst, + $ctabsel, $wTabRatio); + $this->_append($header . $data); + } + + /** + * Writes Excel BIFF BOUNDSHEET record. + * FIXME: inconsistent with BIFF documentation + * + * @param string $sheetname Worksheet name + * @param integer $offset Location of worksheet BOF + * @access private + */ + function _storeBoundsheet($sheetname,$offset) + { + $record = 0x0085; // Record identifier + if ($this->_BIFF_version == 0x0600) { + $length = 0x08 + strlen($sheetname); // Number of bytes to follow + } else { + $length = 0x07 + strlen($sheetname); // Number of bytes to follow + } + + $grbit = 0x0000; // Visibility and sheet type + $cch = strlen($sheetname); // Length of sheet name + + $header = pack("vv", $record, $length); + if ($this->_BIFF_version == 0x0600) { + $data = pack("Vvv", $offset, $grbit, $cch); + } else { + $data = pack("VvC", $offset, $grbit, $cch); + } + $this->_append($header.$data.$sheetname); + } + + /** + * Write Internal SUPBOOK record + * + * @access private + */ + function _storeSupbookInternal() + { + $record = 0x01AE; // Record identifier + $length = 0x0004; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("vv", count($this->_worksheets), 0x0104); + $this->_append($header . $data); + } + + /** + * Writes the Excel BIFF EXTERNSHEET record. These references are used by + * formulas. + * + * @param string $sheetname Worksheet name + * @access private + */ + function _storeExternsheetBiff8() + { + $total_references = count($this->_parser->_references); + $record = 0x0017; // Record identifier + $length = 2 + 6 * $total_references; // Number of bytes to follow + + $supbook_index = 0; // FIXME: only using internal SUPBOOK record + $header = pack("vv", $record, $length); + $data = pack('v', $total_references); + for ($i = 0; $i < $total_references; $i++) { + $data .= $this->_parser->_references[$i]; + } + $this->_append($header . $data); + } + + /** + * Write Excel BIFF STYLE records. + * + * @access private + */ + function _storeStyle() + { + $record = 0x0293; // Record identifier + $length = 0x0004; // Bytes to follow + + $ixfe = 0x8000; // Index to style XF + $BuiltIn = 0x00; // Built-in style + $iLevel = 0xff; // Outline style level + + $header = pack("vv", $record, $length); + $data = pack("vCC", $ixfe, $BuiltIn, $iLevel); + $this->_append($header . $data); + } + + + /** + * Writes Excel FORMAT record for non "built-in" numerical formats. + * + * @param string $format Custom format string + * @param integer $ifmt Format index code + * @access private + */ + function _storeNumFormat($format, $ifmt) + { + $record = 0x041E; // Record identifier + + if ($this->_BIFF_version == 0x0600) { + $length = 5 + strlen($format); // Number of bytes to follow + $encoding = 0x0; + } elseif ($this->_BIFF_version == 0x0500) { + $length = 3 + strlen($format); // Number of bytes to follow + } + + $cch = strlen($format); // Length of format string + + $header = pack("vv", $record, $length); + if ($this->_BIFF_version == 0x0600) { + $data = pack("vvC", $ifmt, $cch, $encoding); + } elseif ($this->_BIFF_version == 0x0500) { + $data = pack("vC", $ifmt, $cch); + } + $this->_append($header . $data . $format); + } + + /** + * Write DATEMODE record to indicate the date system in use (1904 or 1900). + * + * @access private + */ + function _storeDatemode() + { + $record = 0x0022; // Record identifier + $length = 0x0002; // Bytes to follow + + $f1904 = $this->_1904; // Flag for 1904 date system + + $header = pack("vv", $record, $length); + $data = pack("v", $f1904); + $this->_append($header . $data); + } + + + /** + * Write BIFF record EXTERNCOUNT to indicate the number of external sheet + * references in the workbook. + * + * Excel only stores references to external sheets that are used in NAME. + * The workbook NAME record is required to define the print area and the repeat + * rows and columns. + * + * A similar method is used in Worksheet.php for a slightly different purpose. + * + * @param integer $cxals Number of external references + * @access private + */ + function _storeExterncount($cxals) + { + $record = 0x0016; // Record identifier + $length = 0x0002; // Number of bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $cxals); + $this->_append($header . $data); + } + + + /** + * Writes the Excel BIFF EXTERNSHEET record. These references are used by + * formulas. NAME record is required to define the print area and the repeat + * rows and columns. + * + * A similar method is used in Worksheet.php for a slightly different purpose. + * + * @param string $sheetname Worksheet name + * @access private + */ + function _storeExternsheet($sheetname) + { + $record = 0x0017; // Record identifier + $length = 0x02 + strlen($sheetname); // Number of bytes to follow + + $cch = strlen($sheetname); // Length of sheet name + $rgch = 0x03; // Filename encoding + + $header = pack("vv", $record, $length); + $data = pack("CC", $cch, $rgch); + $this->_append($header . $data . $sheetname); + } + + + /** + * Store the NAME record in the short format that is used for storing the print + * area, repeat rows only and repeat columns only. + * + * @param integer $index Sheet index + * @param integer $type Built-in name type + * @param integer $rowmin Start row + * @param integer $rowmax End row + * @param integer $colmin Start colum + * @param integer $colmax End column + * @access private + */ + function _storeNameShort($index, $type, $rowmin, $rowmax, $colmin, $colmax) + { + $record = 0x0018; // Record identifier + $length = 0x0024; // Number of bytes to follow + + $grbit = 0x0020; // Option flags + $chKey = 0x00; // Keyboard shortcut + $cch = 0x01; // Length of text name + $cce = 0x0015; // Length of text definition + $ixals = $index + 1; // Sheet index + $itab = $ixals; // Equal to ixals + $cchCustMenu = 0x00; // Length of cust menu text + $cchDescription = 0x00; // Length of description text + $cchHelptopic = 0x00; // Length of help topic text + $cchStatustext = 0x00; // Length of status bar text + $rgch = $type; // Built-in name type + + $unknown03 = 0x3b; + $unknown04 = 0xffff-$index; + $unknown05 = 0x0000; + $unknown06 = 0x0000; + $unknown07 = 0x1087; + $unknown08 = 0x8005; + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + $data .= pack("C", $chKey); + $data .= pack("C", $cch); + $data .= pack("v", $cce); + $data .= pack("v", $ixals); + $data .= pack("v", $itab); + $data .= pack("C", $cchCustMenu); + $data .= pack("C", $cchDescription); + $data .= pack("C", $cchHelptopic); + $data .= pack("C", $cchStatustext); + $data .= pack("C", $rgch); + $data .= pack("C", $unknown03); + $data .= pack("v", $unknown04); + $data .= pack("v", $unknown05); + $data .= pack("v", $unknown06); + $data .= pack("v", $unknown07); + $data .= pack("v", $unknown08); + $data .= pack("v", $index); + $data .= pack("v", $index); + $data .= pack("v", $rowmin); + $data .= pack("v", $rowmax); + $data .= pack("C", $colmin); + $data .= pack("C", $colmax); + $this->_append($header . $data); + } + + + /** + * Store the NAME record in the long format that is used for storing the repeat + * rows and columns when both are specified. This shares a lot of code with + * _storeNameShort() but we use a separate method to keep the code clean. + * Code abstraction for reuse can be carried too far, and I should know. ;-) + * + * @param integer $index Sheet index + * @param integer $type Built-in name type + * @param integer $rowmin Start row + * @param integer $rowmax End row + * @param integer $colmin Start colum + * @param integer $colmax End column + * @access private + */ + function _storeNameLong($index, $type, $rowmin, $rowmax, $colmin, $colmax) + { + $record = 0x0018; // Record identifier + $length = 0x003d; // Number of bytes to follow + $grbit = 0x0020; // Option flags + $chKey = 0x00; // Keyboard shortcut + $cch = 0x01; // Length of text name + $cce = 0x002e; // Length of text definition + $ixals = $index + 1; // Sheet index + $itab = $ixals; // Equal to ixals + $cchCustMenu = 0x00; // Length of cust menu text + $cchDescription = 0x00; // Length of description text + $cchHelptopic = 0x00; // Length of help topic text + $cchStatustext = 0x00; // Length of status bar text + $rgch = $type; // Built-in name type + + $unknown01 = 0x29; + $unknown02 = 0x002b; + $unknown03 = 0x3b; + $unknown04 = 0xffff-$index; + $unknown05 = 0x0000; + $unknown06 = 0x0000; + $unknown07 = 0x1087; + $unknown08 = 0x8008; + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + $data .= pack("C", $chKey); + $data .= pack("C", $cch); + $data .= pack("v", $cce); + $data .= pack("v", $ixals); + $data .= pack("v", $itab); + $data .= pack("C", $cchCustMenu); + $data .= pack("C", $cchDescription); + $data .= pack("C", $cchHelptopic); + $data .= pack("C", $cchStatustext); + $data .= pack("C", $rgch); + $data .= pack("C", $unknown01); + $data .= pack("v", $unknown02); + // Column definition + $data .= pack("C", $unknown03); + $data .= pack("v", $unknown04); + $data .= pack("v", $unknown05); + $data .= pack("v", $unknown06); + $data .= pack("v", $unknown07); + $data .= pack("v", $unknown08); + $data .= pack("v", $index); + $data .= pack("v", $index); + $data .= pack("v", 0x0000); + $data .= pack("v", 0x3fff); + $data .= pack("C", $colmin); + $data .= pack("C", $colmax); + // Row definition + $data .= pack("C", $unknown03); + $data .= pack("v", $unknown04); + $data .= pack("v", $unknown05); + $data .= pack("v", $unknown06); + $data .= pack("v", $unknown07); + $data .= pack("v", $unknown08); + $data .= pack("v", $index); + $data .= pack("v", $index); + $data .= pack("v", $rowmin); + $data .= pack("v", $rowmax); + $data .= pack("C", 0x00); + $data .= pack("C", 0xff); + // End of data + $data .= pack("C", 0x10); + $this->_append($header . $data); + } + + /** + * Stores the COUNTRY record for localization + * + * @access private + */ + function _storeCountry() + { + $record = 0x008C; // Record identifier + $length = 4; // Number of bytes to follow + + $header = pack('vv', $record, $length); + /* using the same country code always for simplicity */ + $data = pack('vv', $this->_country_code, $this->_country_code); + $this->_append($header . $data); + } + + /** + * Stores the PALETTE biff record. + * + * @access private + */ + function _storePalette() + { + $aref = $this->_palette; + + $record = 0x0092; // Record identifier + $length = 2 + 4 * count($aref); // Number of bytes to follow + $ccv = count($aref); // Number of RGB values to follow + $data = ''; // The RGB data + + // Pack the RGB data + foreach ($aref as $color) { + foreach ($color as $byte) { + $data .= pack("C",$byte); + } + } + + $header = pack("vvv", $record, $length, $ccv); + $this->_append($header . $data); + } + + /** + * Calculate + * Handling of the SST continue blocks is complicated by the need to include an + * additional continuation byte depending on whether the string is split between + * blocks or whether it starts at the beginning of the block. (There are also + * additional complications that will arise later when/if Rich Strings are + * supported). + * + * @access private + */ + function _calculateSharedStringsSizes() + { + /* Iterate through the strings to calculate the CONTINUE block sizes. + For simplicity we use the same size for the SST and CONTINUE records: + 8228 : Maximum Excel97 block size + -4 : Length of block header + -8 : Length of additional SST header information + -8 : Arbitrary number to keep within _add_continue() limit = 8208 + */ + $continue_limit = 8208; + $block_length = 0; + $written = 0; + $this->_block_sizes = array(); + $continue = 0; + + foreach (array_keys($this->_str_table) as $string) { + $string_length = strlen($string); + $headerinfo = unpack("vlength/Cencoding", $string); + $encoding = $headerinfo["encoding"]; + $split_string = 0; + + // Block length is the total length of the strings that will be + // written out in a single SST or CONTINUE block. + $block_length += $string_length; + + // We can write the string if it doesn't cross a CONTINUE boundary + if ($block_length < $continue_limit) { + $written += $string_length; + continue; + } + + // Deal with the cases where the next string to be written will exceed + // the CONTINUE boundary. If the string is very long it may need to be + // written in more than one CONTINUE record. + while ($block_length >= $continue_limit) { + + // We need to avoid the case where a string is continued in the first + // n bytes that contain the string header information. + $header_length = 3; // Min string + header size -1 + $space_remaining = $continue_limit - $written - $continue; + + + /* TODO: Unicode data should only be split on char (2 byte) + boundaries. Therefore, in some cases we need to reduce the + amount of available + */ + $align = 0; + + // Only applies to Unicode strings + if ($encoding == 1) { + // Min string + header size -1 + $header_length = 4; + + if ($space_remaining > $header_length) { + // String contains 3 byte header => split on odd boundary + if (!$split_string && $space_remaining % 2 != 1) { + $space_remaining--; + $align = 1; + } + // Split section without header => split on even boundary + else if ($split_string && $space_remaining % 2 == 1) { + $space_remaining--; + $align = 1; + } + + $split_string = 1; + } + } + + + if ($space_remaining > $header_length) { + // Write as much as possible of the string in the current block + $written += $space_remaining; + + // Reduce the current block length by the amount written + $block_length -= $continue_limit - $continue - $align; + + // Store the max size for this block + $this->_block_sizes[] = $continue_limit - $align; + + // If the current string was split then the next CONTINUE block + // should have the string continue flag (grbit) set unless the + // split string fits exactly into the remaining space. + if ($block_length > 0) { + $continue = 1; + } else { + $continue = 0; + } + } else { + // Store the max size for this block + $this->_block_sizes[] = $written + $continue; + + // Not enough space to start the string in the current block + $block_length -= $continue_limit - $space_remaining - $continue; + $continue = 0; + + } + + // If the string (or substr) is small enough we can write it in the + // new CONTINUE block. Else, go through the loop again to write it in + // one or more CONTINUE blocks + if ($block_length < $continue_limit) { + $written = $block_length; + } else { + $written = 0; + } + } + } + + // Store the max size for the last block unless it is empty + if ($written + $continue) { + $this->_block_sizes[] = $written + $continue; + } + + + /* Calculate the total length of the SST and associated CONTINUEs (if any). + The SST record will have a length even if it contains no strings. + This length is required to set the offsets in the BOUNDSHEET records since + they must be written before the SST records + */ + + $tmp_block_sizes = array(); + $tmp_block_sizes = $this->_block_sizes; + + $length = 12; + if (!empty($tmp_block_sizes)) { + $length += array_shift($tmp_block_sizes); // SST + } + while (!empty($tmp_block_sizes)) { + $length += 4 + array_shift($tmp_block_sizes); // CONTINUEs + } + + return $length; + } + + /** + * Write all of the workbooks strings into an indexed array. + * See the comments in _calculate_shared_string_sizes() for more information. + * + * The Excel documentation says that the SST record should be followed by an + * EXTSST record. The EXTSST record is a hash table that is used to optimise + * access to SST. However, despite the documentation it doesn't seem to be + * required so we will ignore it. + * + * @access private + */ + function _storeSharedStringsTable() + { + $record = 0x00fc; // Record identifier + $length = 0x0008; // Number of bytes to follow + $total = 0x0000; + + // Iterate through the strings to calculate the CONTINUE block sizes + $continue_limit = 8208; + $block_length = 0; + $written = 0; + $continue = 0; + + // sizes are upside down + $tmp_block_sizes = $this->_block_sizes; + // $tmp_block_sizes = array_reverse($this->_block_sizes); + + // The SST record is required even if it contains no strings. Thus we will + // always have a length + // + if (!empty($tmp_block_sizes)) { + $length = 8 + array_shift($tmp_block_sizes); + } + else { + // No strings + $length = 8; + } + + + + // Write the SST block header information + $header = pack("vv", $record, $length); + $data = pack("VV", $this->_str_total, $this->_str_unique); + $this->_append($header . $data); + + + + + /* TODO: not good for performance */ + foreach (array_keys($this->_str_table) as $string) { + + $string_length = strlen($string); + $headerinfo = unpack("vlength/Cencoding", $string); + $encoding = $headerinfo["encoding"]; + $split_string = 0; + + // Block length is the total length of the strings that will be + // written out in a single SST or CONTINUE block. + // + $block_length += $string_length; + + + // We can write the string if it doesn't cross a CONTINUE boundary + if ($block_length < $continue_limit) { + $this->_append($string); + $written += $string_length; + continue; + } + + // Deal with the cases where the next string to be written will exceed + // the CONTINUE boundary. If the string is very long it may need to be + // written in more than one CONTINUE record. + // + while ($block_length >= $continue_limit) { + + // We need to avoid the case where a string is continued in the first + // n bytes that contain the string header information. + // + $header_length = 3; // Min string + header size -1 + $space_remaining = $continue_limit - $written - $continue; + + + // Unicode data should only be split on char (2 byte) boundaries. + // Therefore, in some cases we need to reduce the amount of available + // space by 1 byte to ensure the correct alignment. + $align = 0; + + // Only applies to Unicode strings + if ($encoding == 1) { + // Min string + header size -1 + $header_length = 4; + + if ($space_remaining > $header_length) { + // String contains 3 byte header => split on odd boundary + if (!$split_string && $space_remaining % 2 != 1) { + $space_remaining--; + $align = 1; + } + // Split section without header => split on even boundary + else if ($split_string && $space_remaining % 2 == 1) { + $space_remaining--; + $align = 1; + } + + $split_string = 1; + } + } + + + if ($space_remaining > $header_length) { + // Write as much as possible of the string in the current block + $tmp = substr($string, 0, $space_remaining); + $this->_append($tmp); + + // The remainder will be written in the next block(s) + $string = substr($string, $space_remaining); + + // Reduce the current block length by the amount written + $block_length -= $continue_limit - $continue - $align; + + // If the current string was split then the next CONTINUE block + // should have the string continue flag (grbit) set unless the + // split string fits exactly into the remaining space. + // + if ($block_length > 0) { + $continue = 1; + } else { + $continue = 0; + } + } else { + // Not enough space to start the string in the current block + $block_length -= $continue_limit - $space_remaining - $continue; + $continue = 0; + } + + // Write the CONTINUE block header + if (!empty($this->_block_sizes)) { + $record = 0x003C; + $length = array_shift($tmp_block_sizes); + + $header = pack('vv', $record, $length); + if ($continue) { + $header .= pack('C', $encoding); + } + $this->_append($header); + } + + // If the string (or substr) is small enough we can write it in the + // new CONTINUE block. Else, go through the loop again to write it in + // one or more CONTINUE blocks + // + if ($block_length < $continue_limit) { + $this->_append($string); + $written = $block_length; + } else { + $written = 0; + } + } + } + } + + +} + diff --git a/functions/PEAR/Spreadsheet/Excel/Writer/Worksheet.php b/functions/PEAR/Spreadsheet/Excel/Writer/Worksheet.php new file mode 100755 index 000000000..f92456a9e --- /dev/null +++ b/functions/PEAR/Spreadsheet/Excel/Writer/Worksheet.php @@ -0,0 +1,3521 @@ + +* +* The majority of this is _NOT_ my code. I simply ported it from the +* PERL Spreadsheet::WriteExcel module. +* +* The author of the Spreadsheet::WriteExcel module is John McNamara +* +* +* I _DO_ maintain this code, and John McNamara has nothing to do with the +* porting of this code to PHP. Any questions directly related to this +* class library should be directed to me. +* +* License Information: +* +* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets +* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +require_once( dirname(__FILE__) . '/Parser.php'); +require_once( dirname(__FILE__) . '/BIFFwriter.php'); + +/** +* Class for generating Excel Spreadsheets +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + +class Spreadsheet_Excel_Writer_Worksheet extends Spreadsheet_Excel_Writer_BIFFwriter +{ + /** + * Name of the Worksheet + * @var string + */ + var $name; + + /** + * Index for the Worksheet + * @var integer + */ + var $index; + + /** + * Reference to the (default) Format object for URLs + * @var object Format + */ + var $_url_format; + + /** + * Reference to the parser used for parsing formulas + * @var object Format + */ + var $_parser; + + /** + * Filehandle to the temporary file for storing data + * @var resource + */ + var $_filehandle; + + /** + * Boolean indicating if we are using a temporary file for storing data + * @var bool + */ + var $_using_tmpfile; + + /** + * Maximum number of rows for an Excel spreadsheet (BIFF5) + * @var integer + */ + var $_xls_rowmax; + + /** + * Maximum number of columns for an Excel spreadsheet (BIFF5) + * @var integer + */ + var $_xls_colmax; + + /** + * Maximum number of characters for a string (LABEL record in BIFF5) + * @var integer + */ + var $_xls_strmax; + + /** + * First row for the DIMENSIONS record + * @var integer + * @see _storeDimensions() + */ + var $_dim_rowmin; + + /** + * Last row for the DIMENSIONS record + * @var integer + * @see _storeDimensions() + */ + var $_dim_rowmax; + + /** + * First column for the DIMENSIONS record + * @var integer + * @see _storeDimensions() + */ + var $_dim_colmin; + + /** + * Last column for the DIMENSIONS record + * @var integer + * @see _storeDimensions() + */ + var $_dim_colmax; + + /** + * Array containing format information for columns + * @var array + */ + var $_colinfo; + + /** + * Array containing the selected area for the worksheet + * @var array + */ + var $_selection; + + /** + * Array containing the panes for the worksheet + * @var array + */ + var $_panes; + + /** + * The active pane for the worksheet + * @var integer + */ + var $_active_pane; + + /** + * Bit specifying if panes are frozen + * @var integer + */ + var $_frozen; + + /** + * Bit specifying if the worksheet is selected + * @var integer + */ + var $selected; + + /** + * The paper size (for printing) (DOCUMENT!!!) + * @var integer + */ + var $_paper_size; + + /** + * Bit specifying paper orientation (for printing). 0 => landscape, 1 => portrait + * @var integer + */ + var $_orientation; + + /** + * The page header caption + * @var string + */ + var $_header; + + /** + * The page footer caption + * @var string + */ + var $_footer; + + /** + * The horizontal centering value for the page + * @var integer + */ + var $_hcenter; + + /** + * The vertical centering value for the page + * @var integer + */ + var $_vcenter; + + /** + * The margin for the header + * @var float + */ + var $_margin_head; + + /** + * The margin for the footer + * @var float + */ + var $_margin_foot; + + /** + * The left margin for the worksheet in inches + * @var float + */ + var $_margin_left; + + /** + * The right margin for the worksheet in inches + * @var float + */ + var $_margin_right; + + /** + * The top margin for the worksheet in inches + * @var float + */ + var $_margin_top; + + /** + * The bottom margin for the worksheet in inches + * @var float + */ + var $_margin_bottom; + + /** + * First row to reapeat on each printed page + * @var integer + */ + var $title_rowmin; + + /** + * Last row to reapeat on each printed page + * @var integer + */ + var $title_rowmax; + + /** + * First column to reapeat on each printed page + * @var integer + */ + var $title_colmin; + + /** + * First row of the area to print + * @var integer + */ + var $print_rowmin; + + /** + * Last row to of the area to print + * @var integer + */ + var $print_rowmax; + + /** + * First column of the area to print + * @var integer + */ + var $print_colmin; + + /** + * Last column of the area to print + * @var integer + */ + var $print_colmax; + + /** + * Whether to use outline. + * @var integer + */ + var $_outline_on; + + /** + * Auto outline styles. + * @var bool + */ + var $_outline_style; + + /** + * Whether to have outline summary below. + * @var bool + */ + var $_outline_below; + + /** + * Whether to have outline summary at the right. + * @var bool + */ + var $_outline_right; + + /** + * Outline row level. + * @var integer + */ + var $_outline_row_level; + + /** + * Whether to fit to page when printing or not. + * @var bool + */ + var $_fit_page; + + /** + * Number of pages to fit wide + * @var integer + */ + var $_fit_width; + + /** + * Number of pages to fit high + * @var integer + */ + var $_fit_height; + + /** + * Reference to the total number of strings in the workbook + * @var integer + */ + var $_str_total; + + /** + * Reference to the number of unique strings in the workbook + * @var integer + */ + var $_str_unique; + + /** + * Reference to the array containing all the unique strings in the workbook + * @var array + */ + var $_str_table; + + /** + * Merged cell ranges + * @var array + */ + var $_merged_ranges; + + /** + * Charset encoding currently used when calling writeString() + * @var string + */ + var $_input_encoding; + + /** + * Constructor + * + * @param string $name The name of the new worksheet + * @param integer $index The index of the new worksheet + * @param mixed &$activesheet The current activesheet of the workbook we belong to + * @param mixed &$firstsheet The first worksheet in the workbook we belong to + * @param mixed &$url_format The default format for hyperlinks + * @param mixed &$parser The formula parser created for the Workbook + * @param string $tmp_dir The path to the directory for temporary files + * @access private + */ + function Spreadsheet_Excel_Writer_Worksheet($BIFF_version, $name, + $index, &$activesheet, + &$firstsheet, &$str_total, + &$str_unique, &$str_table, + &$url_format, &$parser, + $tmp_dir) + { + // It needs to call its parent's constructor explicitly + $this->Spreadsheet_Excel_Writer_BIFFwriter(); + $this->_BIFF_version = $BIFF_version; + $rowmax = 65536; // 16384 in Excel 5 + $colmax = 256; + + $this->name = $name; + $this->index = $index; + $this->activesheet = &$activesheet; + $this->firstsheet = &$firstsheet; + $this->_str_total = &$str_total; + $this->_str_unique = &$str_unique; + $this->_str_table = &$str_table; + $this->_url_format = &$url_format; + $this->_parser = &$parser; + + //$this->ext_sheets = array(); + $this->_filehandle = ''; + $this->_using_tmpfile = true; + //$this->fileclosed = 0; + //$this->offset = 0; + $this->_xls_rowmax = $rowmax; + $this->_xls_colmax = $colmax; + $this->_xls_strmax = 255; + $this->_dim_rowmin = $rowmax + 1; + $this->_dim_rowmax = 0; + $this->_dim_colmin = $colmax + 1; + $this->_dim_colmax = 0; + $this->_colinfo = array(); + $this->_selection = array(0,0,0,0); + $this->_panes = array(); + $this->_active_pane = 3; + $this->_frozen = 0; + $this->selected = 0; + + $this->_paper_size = 0x0; + $this->_orientation = 0x1; + $this->_header = ''; + $this->_footer = ''; + $this->_hcenter = 0; + $this->_vcenter = 0; + $this->_margin_head = 0.50; + $this->_margin_foot = 0.50; + $this->_margin_left = 0.75; + $this->_margin_right = 0.75; + $this->_margin_top = 1.00; + $this->_margin_bottom = 1.00; + + $this->title_rowmin = null; + $this->title_rowmax = null; + $this->title_colmin = null; + $this->title_colmax = null; + $this->print_rowmin = null; + $this->print_rowmax = null; + $this->print_colmin = null; + $this->print_colmax = null; + + $this->_print_gridlines = 1; + $this->_screen_gridlines = 1; + $this->_print_headers = 0; + + $this->_fit_page = 0; + $this->_fit_width = 0; + $this->_fit_height = 0; + + $this->_hbreaks = array(); + $this->_vbreaks = array(); + + $this->_protect = 0; + $this->_password = null; + + $this->col_sizes = array(); + $this->_row_sizes = array(); + + $this->_zoom = 100; + $this->_print_scale = 100; + + $this->_outline_row_level = 0; + $this->_outline_style = 0; + $this->_outline_below = 1; + $this->_outline_right = 1; + $this->_outline_on = 1; + + $this->_merged_ranges = array(); + + $this->_input_encoding = ''; + + $this->_dv = array(); + + $this->_tmp_dir = $tmp_dir; + + $this->_initialize(); + } + + /** + * Open a tmp file to store the majority of the Worksheet data. If this fails, + * for example due to write permissions, store the data in memory. This can be + * slow for large files. + * + * @access private + */ + function _initialize() + { + if ($this->_using_tmpfile == false) { + return; + } + + if ($this->_tmp_dir === '' && ini_get('open_basedir') === false) { + // open_basedir restriction in effect - store data in memory + // ToDo: Let the error actually have an effect somewhere + $this->_using_tmpfile = false; + return new PEAR_Error('Temp file could not be opened since open_basedir restriction in effect - please use setTmpDir() - using memory storage instead'); + } + + // Open tmp file for storing Worksheet data + if ($this->_tmp_dir === '') { + $fh = tmpfile(); + } else { + // For people with open base dir restriction + $tmpfilename = tempnam($this->_tmp_dir, "Spreadsheet_Excel_Writer"); + $fh = @fopen($tmpfilename, "w+b"); + } + + if ($fh === false) { + // If tmpfile() fails store data in memory + $this->_using_tmpfile = false; + } else { + // Store filehandle + $this->_filehandle = $fh; + } + } + + /** + * Add data to the beginning of the workbook (note the reverse order) + * and to the end of the workbook. + * + * @access public + * @see Spreadsheet_Excel_Writer_Workbook::storeWorkbook() + * @param array $sheetnames The array of sheetnames from the Workbook this + * worksheet belongs to + */ + function close($sheetnames) + { + $num_sheets = count($sheetnames); + + /*********************************************** + * Prepend in reverse order!! + */ + + // Prepend the sheet dimensions + $this->_storeDimensions(); + + // Prepend the sheet password + $this->_storePassword(); + + // Prepend the sheet protection + $this->_storeProtect(); + + // Prepend the page setup + $this->_storeSetup(); + + /* FIXME: margins are actually appended */ + // Prepend the bottom margin + $this->_storeMarginBottom(); + + // Prepend the top margin + $this->_storeMarginTop(); + + // Prepend the right margin + $this->_storeMarginRight(); + + // Prepend the left margin + $this->_storeMarginLeft(); + + // Prepend the page vertical centering + $this->_storeVcenter(); + + // Prepend the page horizontal centering + $this->_storeHcenter(); + + // Prepend the page footer + $this->_storeFooter(); + + // Prepend the page header + $this->_storeHeader(); + + // Prepend the vertical page breaks + $this->_storeVbreak(); + + // Prepend the horizontal page breaks + $this->_storeHbreak(); + + // Prepend WSBOOL + $this->_storeWsbool(); + + // Prepend GRIDSET + $this->_storeGridset(); + + // Prepend GUTS + if ($this->_BIFF_version == 0x0500) { + $this->_storeGuts(); + } + + // Prepend PRINTGRIDLINES + $this->_storePrintGridlines(); + + // Prepend PRINTHEADERS + $this->_storePrintHeaders(); + + // Prepend EXTERNSHEET references + if ($this->_BIFF_version == 0x0500) { + for ($i = $num_sheets; $i > 0; $i--) { + $sheetname = $sheetnames[$i-1]; + $this->_storeExternsheet($sheetname); + } + } + + // Prepend the EXTERNCOUNT of external references. + if ($this->_BIFF_version == 0x0500) { + $this->_storeExterncount($num_sheets); + } + + // Prepend the COLINFO records if they exist + if (!empty($this->_colinfo)) { + $colcount = count($this->_colinfo); + for ($i = 0; $i < $colcount; $i++) { + $this->_storeColinfo($this->_colinfo[$i]); + } + $this->_storeDefcol(); + } + + // Prepend the BOF record + $this->_storeBof(0x0010); + + /* + * End of prepend. Read upwards from here. + ***********************************************/ + + // Append + $this->_storeWindow2(); + $this->_storeZoom(); + if (!empty($this->_panes)) { + $this->_storePanes($this->_panes); + } + $this->_storeSelection($this->_selection); + $this->_storeMergedCells(); + /* TODO: add data validity */ + /*if ($this->_BIFF_version == 0x0600) { + $this->_storeDataValidity(); + }*/ + $this->_storeEof(); + } + + /** + * Retrieve the worksheet name. + * This is usefull when creating worksheets without a name. + * + * @access public + * @return string The worksheet's name + */ + function getName() + { + return $this->name; + } + + /** + * Retrieves data from memory in one chunk, or from disk in $buffer + * sized chunks. + * + * @return string The data + */ + function getData() + { + $buffer = 4096; + + // Return data stored in memory + if (isset($this->_data)) { + $tmp = $this->_data; + unset($this->_data); + $fh = $this->_filehandle; + if ($this->_using_tmpfile) { + fseek($fh, 0); + } + return $tmp; + } + // Return data stored on disk + if ($this->_using_tmpfile) { + if ($tmp = fread($this->_filehandle, $buffer)) { + return $tmp; + } + } + + // No data to return + return ''; + } + + /** + * Sets a merged cell range + * + * @access public + * @param integer $first_row First row of the area to merge + * @param integer $first_col First column of the area to merge + * @param integer $last_row Last row of the area to merge + * @param integer $last_col Last column of the area to merge + */ + function setMerge($first_row, $first_col, $last_row, $last_col) + { + if (($last_row < $first_row) || ($last_col < $first_col)) { + return; + } + // don't check rowmin, rowmax, etc... because we don't know when this + // is going to be called + $this->_merged_ranges[] = array($first_row, $first_col, $last_row, $last_col); + } + + /** + * Set this worksheet as a selected worksheet, + * i.e. the worksheet has its tab highlighted. + * + * @access public + */ + function select() + { + $this->selected = 1; + } + + /** + * Set this worksheet as the active worksheet, + * i.e. the worksheet that is displayed when the workbook is opened. + * Also set it as selected. + * + * @access public + */ + function activate() + { + $this->selected = 1; + $this->activesheet = $this->index; + } + + /** + * Set this worksheet as the first visible sheet. + * This is necessary when there are a large number of worksheets and the + * activated worksheet is not visible on the screen. + * + * @access public + */ + function setFirstSheet() + { + $this->firstsheet = $this->index; + } + + /** + * Set the worksheet protection flag + * to prevent accidental modification and to + * hide formulas if the locked and hidden format properties have been set. + * + * @access public + * @param string $password The password to use for protecting the sheet. + */ + function protect($password) + { + $this->_protect = 1; + $this->_password = $this->_encodePassword($password); + } + + /** + * Set the width of a single column or a range of columns. + * + * @access public + * @param integer $firstcol first column on the range + * @param integer $lastcol last column on the range + * @param integer $width width to set + * @param mixed $format The optional XF format to apply to the columns + * @param integer $hidden The optional hidden atribute + * @param integer $level The optional outline level + */ + function setColumn($firstcol, $lastcol, $width, $format = null, $hidden = 0, $level = 0) + { + $this->_colinfo[] = array($firstcol, $lastcol, $width, &$format, $hidden, $level); + + // Set width to zero if column is hidden + $width = ($hidden) ? 0 : $width; + + for ($col = $firstcol; $col <= $lastcol; $col++) { + $this->col_sizes[$col] = $width; + } + } + + /** + * Set which cell or cells are selected in a worksheet + * + * @access public + * @param integer $first_row first row in the selected quadrant + * @param integer $first_column first column in the selected quadrant + * @param integer $last_row last row in the selected quadrant + * @param integer $last_column last column in the selected quadrant + */ + function setSelection($first_row,$first_column,$last_row,$last_column) + { + $this->_selection = array($first_row,$first_column,$last_row,$last_column); + } + + /** + * Set panes and mark them as frozen. + * + * @access public + * @param array $panes This is the only parameter received and is composed of the following: + * 0 => Vertical split position, + * 1 => Horizontal split position + * 2 => Top row visible + * 3 => Leftmost column visible + * 4 => Active pane + */ + function freezePanes($panes) + { + $this->_frozen = 1; + $this->_panes = $panes; + } + + /** + * Set panes and mark them as unfrozen. + * + * @access public + * @param array $panes This is the only parameter received and is composed of the following: + * 0 => Vertical split position, + * 1 => Horizontal split position + * 2 => Top row visible + * 3 => Leftmost column visible + * 4 => Active pane + */ + function thawPanes($panes) + { + $this->_frozen = 0; + $this->_panes = $panes; + } + + /** + * Set the page orientation as portrait. + * + * @access public + */ + function setPortrait() + { + $this->_orientation = 1; + } + + /** + * Set the page orientation as landscape. + * + * @access public + */ + function setLandscape() + { + $this->_orientation = 0; + } + + /** + * Set the paper type. Ex. 1 = US Letter, 9 = A4 + * + * @access public + * @param integer $size The type of paper size to use + */ + function setPaper($size = 0) + { + $this->_paper_size = $size; + } + + + /** + * Set the page header caption and optional margin. + * + * @access public + * @param string $string The header text + * @param float $margin optional head margin in inches. + */ + function setHeader($string,$margin = 0.50) + { + if (strlen($string) >= 255) { + //carp 'Header string must be less than 255 characters'; + return; + } + $this->_header = $string; + $this->_margin_head = $margin; + } + + /** + * Set the page footer caption and optional margin. + * + * @access public + * @param string $string The footer text + * @param float $margin optional foot margin in inches. + */ + function setFooter($string,$margin = 0.50) + { + if (strlen($string) >= 255) { + //carp 'Footer string must be less than 255 characters'; + return; + } + $this->_footer = $string; + $this->_margin_foot = $margin; + } + + /** + * Center the page horinzontally. + * + * @access public + * @param integer $center the optional value for centering. Defaults to 1 (center). + */ + function centerHorizontally($center = 1) + { + $this->_hcenter = $center; + } + + /** + * Center the page vertically. + * + * @access public + * @param integer $center the optional value for centering. Defaults to 1 (center). + */ + function centerVertically($center = 1) + { + $this->_vcenter = $center; + } + + /** + * Set all the page margins to the same value in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function setMargins($margin) + { + $this->setMarginLeft($margin); + $this->setMarginRight($margin); + $this->setMarginTop($margin); + $this->setMarginBottom($margin); + } + + /** + * Set the left and right margins to the same value in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function setMargins_LR($margin) + { + $this->setMarginLeft($margin); + $this->setMarginRight($margin); + } + + /** + * Set the top and bottom margins to the same value in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function setMargins_TB($margin) + { + $this->setMarginTop($margin); + $this->setMarginBottom($margin); + } + + /** + * Set the left margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function setMarginLeft($margin = 0.75) + { + $this->_margin_left = $margin; + } + + /** + * Set the right margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function setMarginRight($margin = 0.75) + { + $this->_margin_right = $margin; + } + + /** + * Set the top margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function setMarginTop($margin = 1.00) + { + $this->_margin_top = $margin; + } + + /** + * Set the bottom margin in inches. + * + * @access public + * @param float $margin The margin to set in inches + */ + function setMarginBottom($margin = 1.00) + { + $this->_margin_bottom = $margin; + } + + /** + * Set the rows to repeat at the top of each printed page. + * + * @access public + * @param integer $first_row First row to repeat + * @param integer $last_row Last row to repeat. Optional. + */ + function repeatRows($first_row, $last_row = null) + { + $this->title_rowmin = $first_row; + if (isset($last_row)) { //Second row is optional + $this->title_rowmax = $last_row; + } else { + $this->title_rowmax = $first_row; + } + } + + /** + * Set the columns to repeat at the left hand side of each printed page. + * + * @access public + * @param integer $first_col First column to repeat + * @param integer $last_col Last column to repeat. Optional. + */ + function repeatColumns($first_col, $last_col = null) + { + $this->title_colmin = $first_col; + if (isset($last_col)) { // Second col is optional + $this->title_colmax = $last_col; + } else { + $this->title_colmax = $first_col; + } + } + + /** + * Set the area of each worksheet that will be printed. + * + * @access public + * @param integer $first_row First row of the area to print + * @param integer $first_col First column of the area to print + * @param integer $last_row Last row of the area to print + * @param integer $last_col Last column of the area to print + */ + function printArea($first_row, $first_col, $last_row, $last_col) + { + $this->print_rowmin = $first_row; + $this->print_colmin = $first_col; + $this->print_rowmax = $last_row; + $this->print_colmax = $last_col; + } + + + /** + * Set the option to hide gridlines on the printed page. + * + * @access public + */ + function hideGridlines() + { + $this->_print_gridlines = 0; + } + + /** + * Set the option to hide gridlines on the worksheet (as seen on the screen). + * + * @access public + */ + function hideScreenGridlines() + { + $this->_screen_gridlines = 0; + } + + /** + * Set the option to print the row and column headers on the printed page. + * + * @access public + * @param integer $print Whether to print the headers or not. Defaults to 1 (print). + */ + function printRowColHeaders($print = 1) + { + $this->_print_headers = $print; + } + + /** + * Set the vertical and horizontal number of pages that will define the maximum area printed. + * It doesn't seem to work with OpenOffice. + * + * @access public + * @param integer $width Maximun width of printed area in pages + * @param integer $height Maximun heigth of printed area in pages + * @see setPrintScale() + */ + function fitToPages($width, $height) + { + $this->_fit_page = 1; + $this->_fit_width = $width; + $this->_fit_height = $height; + } + + /** + * Store the horizontal page breaks on a worksheet (for printing). + * The breaks represent the row after which the break is inserted. + * + * @access public + * @param array $breaks Array containing the horizontal page breaks + */ + function setHPagebreaks($breaks) + { + foreach ($breaks as $break) { + array_push($this->_hbreaks, $break); + } + } + + /** + * Store the vertical page breaks on a worksheet (for printing). + * The breaks represent the column after which the break is inserted. + * + * @access public + * @param array $breaks Array containing the vertical page breaks + */ + function setVPagebreaks($breaks) + { + foreach ($breaks as $break) { + array_push($this->_vbreaks, $break); + } + } + + + /** + * Set the worksheet zoom factor. + * + * @access public + * @param integer $scale The zoom factor + */ + function setZoom($scale = 100) + { + // Confine the scale to Excel's range + if ($scale < 10 || $scale > 400) { + $this->raiseError("Zoom factor $scale outside range: 10 <= zoom <= 400"); + $scale = 100; + } + + $this->_zoom = floor($scale); + } + + /** + * Set the scale factor for the printed page. + * It turns off the "fit to page" option + * + * @access public + * @param integer $scale The optional scale factor. Defaults to 100 + */ + function setPrintScale($scale = 100) + { + // Confine the scale to Excel's range + if ($scale < 10 || $scale > 400) { + $this->raiseError("Print scale $scale outside range: 10 <= zoom <= 400"); + $scale = 100; + } + + // Turn off "fit to page" option + $this->_fit_page = 0; + + $this->_print_scale = floor($scale); + } + + /** + * Map to the appropriate write method acording to the token recieved. + * + * @access public + * @param integer $row The row of the cell we are writing to + * @param integer $col The column of the cell we are writing to + * @param mixed $token What we are writing + * @param mixed $format The optional format to apply to the cell + */ + function write($row, $col, $token, $format = null) + { + // Check for a cell reference in A1 notation and substitute row and column + /*if ($_[0] =~ /^\D/) { + @_ = $this->_substituteCellref(@_); + }*/ + + if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/", $token)) { + // Match number + return $this->writeNumber($row, $col, $token, $format); + } elseif (preg_match("/^[fh]tt?p:\/\//", $token)) { + // Match http or ftp URL + return $this->writeUrl($row, $col, $token, '', $format); + } elseif (preg_match("/^mailto:/", $token)) { + // Match mailto: + return $this->writeUrl($row, $col, $token, '', $format); + } elseif (preg_match("/^(?:in|ex)ternal:/", $token)) { + // Match internal or external sheet link + return $this->writeUrl($row, $col, $token, '', $format); + } elseif (preg_match("/^=/", $token)) { + // Match formula + return $this->writeFormula($row, $col, $token, $format); + } elseif ($token == '') { + // Match blank + return $this->writeBlank($row, $col, $format); + } else { + // Default: match string + return $this->writeString($row, $col, $token, $format); + } + } + + /** + * Write an array of values as a row + * + * @access public + * @param integer $row The row we are writing to + * @param integer $col The first col (leftmost col) we are writing to + * @param array $val The array of values to write + * @param mixed $format The optional format to apply to the cell + * @return mixed PEAR_Error on failure + */ + + function writeRow($row, $col, $val, $format = null) + { + $retval = ''; + if (is_array($val)) { + foreach ($val as $v) { + if (is_array($v)) { + $this->writeCol($row, $col, $v, $format); + } else { + $this->write($row, $col, $v, $format); + } + $col++; + } + } else { + $retval = new PEAR_Error('$val needs to be an array'); + } + return($retval); + } + + /** + * Write an array of values as a column + * + * @access public + * @param integer $row The first row (uppermost row) we are writing to + * @param integer $col The col we are writing to + * @param array $val The array of values to write + * @param mixed $format The optional format to apply to the cell + * @return mixed PEAR_Error on failure + */ + + function writeCol($row, $col, $val, $format = null) + { + $retval = ''; + if (is_array($val)) { + foreach ($val as $v) { + $this->write($row, $col, $v, $format); + $row++; + } + } else { + $retval = new PEAR_Error('$val needs to be an array'); + } + return($retval); + } + + /** + * Returns an index to the XF record in the workbook + * + * @access private + * @param mixed &$format The optional XF format + * @return integer The XF record index + */ + function _XF(&$format) + { + if ($format) { + return($format->getXfIndex()); + } else { + return(0x0F); + } + } + + + /****************************************************************************** + ******************************************************************************* + * + * Internal methods + */ + + + /** + * Store Worksheet data in memory using the parent's class append() or to a + * temporary file, the default. + * + * @access private + * @param string $data The binary data to append + */ + function _append($data) + { + if ($this->_using_tmpfile) { + // Add CONTINUE records if necessary + if (strlen($data) > $this->_limit) { + $data = $this->_addContinue($data); + } + fwrite($this->_filehandle, $data); + $this->_datasize += strlen($data); + } else { + parent::_append($data); + } + } + + /** + * Substitute an Excel cell reference in A1 notation for zero based row and + * column values in an argument list. + * + * Ex: ("A4", "Hello") is converted to (3, 0, "Hello"). + * + * @access private + * @param string $cell The cell reference. Or range of cells. + * @return array + */ + function _substituteCellref($cell) + { + $cell = strtoupper($cell); + + // Convert a column range: 'A:A' or 'B:G' + if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/", $cell, $match)) { + list($no_use, $col1) = $this->_cellToRowcol($match[1] .'1'); // Add a dummy row + list($no_use, $col2) = $this->_cellToRowcol($match[2] .'1'); // Add a dummy row + return(array($col1, $col2)); + } + + // Convert a cell range: 'A1:B7' + if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/", $cell, $match)) { + list($row1, $col1) = $this->_cellToRowcol($match[1]); + list($row2, $col2) = $this->_cellToRowcol($match[2]); + return(array($row1, $col1, $row2, $col2)); + } + + // Convert a cell reference: 'A1' or 'AD2000' + if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/", $cell)) { + list($row1, $col1) = $this->_cellToRowcol($match[1]); + return(array($row1, $col1)); + } + + // TODO use real error codes + $this->raiseError("Unknown cell reference $cell", 0, PEAR_ERROR_DIE); + } + + /** + * Convert an Excel cell reference in A1 notation to a zero based row and column + * reference; converts C1 to (0, 2). + * + * @access private + * @param string $cell The cell reference. + * @return array containing (row, column) + */ + function _cellToRowcol($cell) + { + preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match); + $col = $match[1]; + $row = $match[2]; + + // Convert base26 column string to number + $chars = split('', $col); + $expn = 0; + $col = 0; + + while ($chars) { + $char = array_pop($chars); // LS char first + $col += (ord($char) -ord('A') +1) * pow(26,$expn); + $expn++; + } + + // Convert 1-index to zero-index + $row--; + $col--; + + return(array($row, $col)); + } + + /** + * Based on the algorithm provided by Daniel Rentz of OpenOffice. + * + * @access private + * @param string $plaintext The password to be encoded in plaintext. + * @return string The encoded password + */ + function _encodePassword($plaintext) + { + $password = 0x0000; + $i = 1; // char position + + // split the plain text password in its component characters + $chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY); + foreach ($chars as $char) { + $value = ord($char) << $i; // shifted ASCII value + $rotated_bits = $value >> 15; // rotated bits beyond bit 15 + $value &= 0x7fff; // first 15 bits + $password ^= ($value | $rotated_bits); + $i++; + } + + $password ^= strlen($plaintext); + $password ^= 0xCE4B; + + return($password); + } + + /** + * This method sets the properties for outlining and grouping. The defaults + * correspond to Excel's defaults. + * + * @param bool $visible + * @param bool $symbols_below + * @param bool $symbols_right + * @param bool $auto_style + */ + function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false) + { + $this->_outline_on = $visible; + $this->_outline_below = $symbols_below; + $this->_outline_right = $symbols_right; + $this->_outline_style = $auto_style; + + // Ensure this is a boolean vale for Window2 + if ($this->_outline_on) { + $this->_outline_on = 1; + } + } + + /****************************************************************************** + ******************************************************************************* + * + * BIFF RECORDS + */ + + + /** + * Write a double to the specified row and column (zero indexed). + * An integer can be written as a double. Excel will display an + * integer. $format is optional. + * + * Returns 0 : normal termination + * -2 : row or column out of range + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param float $num The number to write + * @param mixed $format The optional XF format + * @return integer + */ + function writeNumber($row, $col, $num, $format = null) + { + $record = 0x0203; // Record identifier + $length = 0x000E; // Number of bytes to follow + + $xf = $this->_XF($format); // The cell format + + // Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { + return(-2); + } + if ($col >= $this->_xls_colmax) { + return(-2); + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $xf); + $xl_double = pack("d", $num); + if ($this->_byte_order) { // if it's Big Endian + $xl_double = strrev($xl_double); + } + + $this->_append($header.$data.$xl_double); + return(0); + } + + /** + * Write a string to the specified row and column (zero indexed). + * NOTE: there is an Excel 5 defined limit of 255 characters. + * $format is optional. + * Returns 0 : normal termination + * -2 : row or column out of range + * -3 : long string truncated to 255 chars + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $str The string to write + * @param mixed $format The XF format for the cell + * @return integer + */ + function writeString($row, $col, $str, $format = null) + { + if ($this->_BIFF_version == 0x0600) { + return $this->writeStringBIFF8($row, $col, $str, $format); + } + $strlen = strlen($str); + $record = 0x0204; // Record identifier + $length = 0x0008 + $strlen; // Bytes to follow + $xf = $this->_XF($format); // The cell format + + $str_error = 0; + + // Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { + return(-2); + } + if ($col >= $this->_xls_colmax) { + return(-2); + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + + if ($strlen > $this->_xls_strmax) { // LABEL must be < 255 chars + $str = substr($str, 0, $this->_xls_strmax); + $length = 0x0008 + $this->_xls_strmax; + $strlen = $this->_xls_strmax; + $str_error = -3; + } + + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row, $col, $xf, $strlen); + $this->_append($header . $data . $str); + return($str_error); + } + + /** + * Sets Input Encoding for writing strings + * + * @access public + * @param string $encoding The encoding. Ex: 'UTF-16LE', 'utf-8', 'ISO-859-7' + */ + function setInputEncoding($encoding) + { + if ($encoding != 'UTF-16LE' && !function_exists('iconv')) { + $this->raiseError("Using an input encoding other than UTF-16LE requires PHP support for iconv"); + } + $this->_input_encoding = $encoding; + } + + /** + * Write a string to the specified row and column (zero indexed). + * This is the BIFF8 version (no 255 chars limit). + * $format is optional. + * Returns 0 : normal termination + * -2 : row or column out of range + * -3 : long string truncated to 255 chars + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $str The string to write + * @param mixed $format The XF format for the cell + * @return integer + */ + function writeStringBIFF8($row, $col, $str, $format = null) + { + if ($this->_input_encoding == 'UTF-16LE') + { + $strlen = function_exists('mb_strlen') ? mb_strlen($str, 'UTF-16LE') : (strlen($str) / 2); + $encoding = 0x1; + } + elseif ($this->_input_encoding != '') + { + $str = iconv($this->_input_encoding, 'UTF-16LE', $str); + $strlen = function_exists('mb_strlen') ? mb_strlen($str, 'UTF-16LE') : (strlen($str) / 2); + $encoding = 0x1; + } + else + { + $strlen = strlen($str); + $encoding = 0x0; + } + $record = 0x00FD; // Record identifier + $length = 0x000A; // Bytes to follow + $xf = $this->_XF($format); // The cell format + + $str_error = 0; + + // Check that row and col are valid and store max and min values + if ($this->_checkRowCol($row, $col) == false) { + return -2; + } + + $str = pack('vC', $strlen, $encoding).$str; + + /* check if string is already present */ + if (!isset($this->_str_table[$str])) { + $this->_str_table[$str] = $this->_str_unique++; + } + $this->_str_total++; + + $header = pack('vv', $record, $length); + $data = pack('vvvV', $row, $col, $xf, $this->_str_table[$str]); + $this->_append($header.$data); + return $str_error; + } + + /** + * Check row and col before writing to a cell, and update the sheet's + * dimensions accordingly + * + * @access private + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @return boolean true for success, false if row and/or col are grester + * then maximums allowed. + */ + function _checkRowCol($row, $col) + { + if ($row >= $this->_xls_rowmax) { + return false; + } + if ($col >= $this->_xls_colmax) { + return false; + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + return true; + } + + /** + * Writes a note associated with the cell given by the row and column. + * NOTE records don't have a length limit. + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $note The note to write + */ + function writeNote($row, $col, $note) + { + $note_length = strlen($note); + $record = 0x001C; // Record identifier + $max_length = 2048; // Maximun length for a NOTE record + //$length = 0x0006 + $note_length; // Bytes to follow + + // Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { + return(-2); + } + if ($col >= $this->_xls_colmax) { + return(-2); + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + + // Length for this record is no more than 2048 + 6 + $length = 0x0006 + min($note_length, 2048); + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $note_length); + $this->_append($header . $data . substr($note, 0, 2048)); + + for ($i = $max_length; $i < $note_length; $i += $max_length) { + $chunk = substr($note, $i, $max_length); + $length = 0x0006 + strlen($chunk); + $header = pack("vv", $record, $length); + $data = pack("vvv", -1, 0, strlen($chunk)); + $this->_append($header.$data.$chunk); + } + return(0); + } + + /** + * Write a blank cell to the specified row and column (zero indexed). + * A blank cell is used to specify formatting without adding a string + * or a number. + * + * A blank cell without a format serves no purpose. Therefore, we don't write + * a BLANK record unless a format is specified. + * + * Returns 0 : normal termination (including no format) + * -1 : insufficient number of arguments + * -2 : row or column out of range + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param mixed $format The XF format + */ + function writeBlank($row, $col, $format) + { + // Don't write a blank cell unless it has a format + if (!$format) { + return(0); + } + + $record = 0x0201; // Record identifier + $length = 0x0006; // Number of bytes to follow + $xf = $this->_XF($format); // The cell format + + // Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { + return(-2); + } + if ($col >= $this->_xls_colmax) { + return(-2); + } + if ($row < $this->_dim_rowmin) { + $this->_dim_rowmin = $row; + } + if ($row > $this->_dim_rowmax) { + $this->_dim_rowmax = $row; + } + if ($col < $this->_dim_colmin) { + $this->_dim_colmin = $col; + } + if ($col > $this->_dim_colmax) { + $this->_dim_colmax = $col; + } + + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $xf); + $this->_append($header . $data); + return 0; + } + + /** + * Write a formula to the specified row and column (zero indexed). + * The textual representation of the formula is passed to the parser in + * Parser.php which returns a packed binary string. + * + * Returns 0 : normal termination + * -1 : formula errors (bad formula) + * -2 : row or column out of range + * + * @access public + * @param integer $row Zero indexed row + * @param integer $col Zero indexed column + * @param string $formula The formula text string + * @param mixed $format The optional XF format + * @return integer + */ + function writeFormula($row, $col, $formula, $format = null) + { + $record = 0x0006; // Record identifier + + // Excel normally stores the last calculated value of the formula in $num. + // Clearly we are not in a position to calculate this a priori. Instead + // we set $num to zero and set the option flags in $grbit to ensure + // automatic calculation of the formula when the file is opened. + // + $xf = $this->_XF($format); // The cell format + $num = 0x00; // Current value of formula + $grbit = 0x03; // Option flags + $unknown = 0x0000; // Must be zero + + + // Check that row and col are valid and store max and min values + if ($this->_checkRowCol($row, $col) == false) { + return -2; + } + + // Strip the '=' or '@' sign at the beginning of the formula string + if (preg_match("/^=/", $formula)) { + $formula = preg_replace("/(^=)/", "", $formula); + } elseif (preg_match("/^@/", $formula)) { + $formula = preg_replace("/(^@)/", "", $formula); + } else { + // Error handling + $this->writeString($row, $col, 'Unrecognised character for formula'); + return -1; + } + + // Parse the formula using the parser in Parser.php + $error = $this->_parser->parse($formula); + if ($this->isError($error)) { + $this->writeString($row, $col, $error->getMessage()); + return -1; + } + + $formula = $this->_parser->toReversePolish(); + if ($this->isError($formula)) { + $this->writeString($row, $col, $formula->getMessage()); + return -1; + } + + $formlen = strlen($formula); // Length of the binary string + $length = 0x16 + $formlen; // Length of the record data + + $header = pack("vv", $record, $length); + $data = pack("vvvdvVv", $row, $col, $xf, $num, + $grbit, $unknown, $formlen); + + $this->_append($header . $data . $formula); + return 0; + } + + /** + * Write a hyperlink. + * This is comprised of two elements: the visible label and + * the invisible link. The visible label is the same as the link unless an + * alternative string is specified. The label is written using the + * writeString() method. Therefore the 255 characters string limit applies. + * $string and $format are optional. + * + * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external + * directory url. + * + * Returns 0 : normal termination + * -2 : row or column out of range + * -3 : long string truncated to 255 chars + * + * @access public + * @param integer $row Row + * @param integer $col Column + * @param string $url URL string + * @param string $string Alternative label + * @param mixed $format The cell format + * @return integer + */ + function writeUrl($row, $col, $url, $string = '', $format = null) + { + // Add start row and col to arg list + return($this->_writeUrlRange($row, $col, $row, $col, $url, $string, $format)); + } + + /** + * This is the more general form of writeUrl(). It allows a hyperlink to be + * written to a range of cells. This function also decides the type of hyperlink + * to be written. These are either, Web (http, ftp, mailto), Internal + * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1'). + * + * @access private + * @see writeUrl() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $string Alternative label + * @param mixed $format The cell format + * @return integer + */ + + function _writeUrlRange($row1, $col1, $row2, $col2, $url, $string = '', $format = null) + { + + // Check for internal/external sheet links or default to web link + if (preg_match('[^internal:]', $url)) { + return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url, $string, $format)); + } + if (preg_match('[^external:]', $url)) { + return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url, $string, $format)); + } + return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url, $string, $format)); + } + + + /** + * Used to write http, ftp and mailto hyperlinks. + * The link type ($options) is 0x03 is the same as absolute dir ref without + * sheet. However it is differentiated by the $unknown2 data stream. + * + * @access private + * @see writeUrl() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $str Alternative label + * @param mixed $format The cell format + * @return integer + */ + function _writeUrlWeb($row1, $col1, $row2, $col2, $url, $str, $format = null) + { + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + if (!$format) { + $format = $this->_url_format; + } + + // Write the visible label using the writeString() method. + if ($str == '') { + $str = $url; + } + $str_error = $this->writeString($row1, $col1, $str, $format); + if (($str_error == -2) || ($str_error == -3)) { + return $str_error; + } + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); + $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B"); + + // Pack the option flags + $options = pack("V", 0x03); + + // Convert URL to a null terminated wchar string + $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); + $url = $url . "\0\0\0"; + + // Pack the length of the URL + $url_len = pack("V", strlen($url)); + + // Calculate the data length + $length = 0x34 + strlen($url); + + // Pack the header data + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row1, $row2, $col1, $col2); + + // Write the packed data + $this->_append($header . $data . + $unknown1 . $options . + $unknown2 . $url_len . $url); + return($str_error); + } + + /** + * Used to write internal reference hyperlinks such as "Sheet1!A1". + * + * @access private + * @see writeUrl() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $str Alternative label + * @param mixed $format The cell format + * @return integer + */ + function _writeUrlInternal($row1, $col1, $row2, $col2, $url, $str, $format = null) + { + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + if (!$format) { + $format = $this->_url_format; + } + + // Strip URL type + $url = preg_replace('/^internal:/', '', $url); + + // Write the visible label + if ($str == '') { + $str = $url; + } + $str_error = $this->writeString($row1, $col1, $str, $format); + if (($str_error == -2) || ($str_error == -3)) { + return $str_error; + } + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); + + // Pack the option flags + $options = pack("V", 0x08); + + // Convert the URL type and to a null terminated wchar string + $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); + $url = $url . "\0\0\0"; + + // Pack the length of the URL as chars (not wchars) + $url_len = pack("V", floor(strlen($url)/2)); + + // Calculate the data length + $length = 0x24 + strlen($url); + + // Pack the header data + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row1, $row2, $col1, $col2); + + // Write the packed data + $this->_append($header . $data . + $unknown1 . $options . + $url_len . $url); + return($str_error); + } + + /** + * Write links to external directory names such as 'c:\foo.xls', + * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'. + * + * Note: Excel writes some relative links with the $dir_long string. We ignore + * these cases for the sake of simpler code. + * + * @access private + * @see writeUrl() + * @param integer $row1 Start row + * @param integer $col1 Start column + * @param integer $row2 End row + * @param integer $col2 End column + * @param string $url URL string + * @param string $str Alternative label + * @param mixed $format The cell format + * @return integer + */ + function _writeUrlExternal($row1, $col1, $row2, $col2, $url, $str, $format = null) + { + // Network drives are different. We will handle them separately + // MS/Novell network drives and shares start with \\ + if (preg_match('[^external:\\\\]', $url)) { + return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format)); + } + + $record = 0x01B8; // Record identifier + $length = 0x00000; // Bytes to follow + + if (!$format) { + $format = $this->_url_format; + } + + // Strip URL type and change Unix dir separator to Dos style (if needed) + // + $url = preg_replace('/^external:/', '', $url); + $url = preg_replace('/\//', "\\", $url); + + // Write the visible label + if ($str == '') { + $str = preg_replace('/\#/', ' - ', $url); + } + $str_error = $this->writeString($row1, $col1, $str, $format); + if (($str_error == -2) or ($str_error == -3)) { + return $str_error; + } + + // Determine if the link is relative or absolute: + // relative if link contains no dir separator, "somefile.xls" + // relative if link starts with up-dir, "..\..\somefile.xls" + // otherwise, absolute + + $absolute = 0x02; // Bit mask + if (!preg_match("/\\\/", $url)) { + $absolute = 0x00; + } + if (preg_match("/^\.\.\\\/", $url)) { + $absolute = 0x00; + } + $link_type = 0x01 | $absolute; + + // Determine if the link contains a sheet reference and change some of the + // parameters accordingly. + // Split the dir name and sheet name (if it exists) + /*if (preg_match("/\#/", $url)) { + list($dir_long, $sheet) = split("\#", $url); + } else { + $dir_long = $url; + } + + if (isset($sheet)) { + $link_type |= 0x08; + $sheet_len = pack("V", strlen($sheet) + 0x01); + $sheet = join("\0", split('', $sheet)); + $sheet .= "\0\0\0"; + } else { + $sheet_len = ''; + $sheet = ''; + }*/ + $dir_long = $url; + if (preg_match("/\#/", $url)) { + $link_type |= 0x08; + } + + + + // Pack the link type + $link_type = pack("V", $link_type); + + // Calculate the up-level dir count e.g.. (..\..\..\ == 3) + $up_count = preg_match_all("/\.\.\\\/", $dir_long, $useless); + $up_count = pack("v", $up_count); + + // Store the short dos dir name (null terminated) + $dir_short = preg_replace("/\.\.\\\/", '', $dir_long) . "\0"; + + // Store the long dir name as a wchar string (non-null terminated) + //$dir_long = join("\0", split('', $dir_long)); + $dir_long = $dir_long . "\0"; + + // Pack the lengths of the dir strings + $dir_short_len = pack("V", strlen($dir_short) ); + $dir_long_len = pack("V", strlen($dir_long) ); + $stream_len = pack("V", 0);//strlen($dir_long) + 0x06); + + // Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' ); + $unknown2 = pack("H*",'0303000000000000C000000000000046' ); + $unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000'); + $unknown4 = pack("v", 0x03 ); + + // Pack the main data stream + $data = pack("vvvv", $row1, $row2, $col1, $col2) . + $unknown1 . + $link_type . + $unknown2 . + $up_count . + $dir_short_len. + $dir_short . + $unknown3 . + $stream_len ;/*. + $dir_long_len . + $unknown4 . + $dir_long . + $sheet_len . + $sheet ;*/ + + // Pack the header data + $length = strlen($data); + $header = pack("vv", $record, $length); + + // Write the packed data + $this->_append($header. $data); + return($str_error); + } + + + /** + * This method is used to set the height and format for a row. + * + * @access public + * @param integer $row The row to set + * @param integer $height Height we are giving to the row. + * Use null to set XF without setting height + * @param mixed $format XF format we are giving to the row + * @param bool $hidden The optional hidden attribute + * @param integer $level The optional outline level for row, in range [0,7] + */ + function setRow($row, $height, $format = null, $hidden = false, $level = 0) + { + $record = 0x0208; // Record identifier + $length = 0x0010; // Number of bytes to follow + + $colMic = 0x0000; // First defined column + $colMac = 0x0000; // Last defined column + $irwMac = 0x0000; // Used by Excel to optimise loading + $reserved = 0x0000; // Reserved + $grbit = 0x0000; // Option flags + $ixfe = $this->_XF($format); // XF index + + // set _row_sizes so _sizeRow() can use it + $this->_row_sizes[$row] = $height; + + // Use setRow($row, null, $XF) to set XF format without setting height + if ($height != null) { + $miyRw = $height * 20; // row height + } else { + $miyRw = 0xff; // default row height is 256 + } + + $level = max(0, min($level, 7)); // level should be between 0 and 7 + $this->_outline_row_level = max($level, $this->_outline_row_level); + + + // Set the options flags. fUnsynced is used to show that the font and row + // heights are not compatible. This is usually the case for WriteExcel. + // The collapsed flag 0x10 doesn't seem to be used to indicate that a row + // is collapsed. Instead it is used to indicate that the previous row is + // collapsed. The zero height flag, 0x20, is used to collapse a row. + + $grbit |= $level; + if ($hidden) { + $grbit |= 0x0020; + } + $grbit |= 0x0040; // fUnsynced + if ($format) { + $grbit |= 0x0080; + } + $grbit |= 0x0100; + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw, + $irwMac,$reserved, $grbit, $ixfe); + $this->_append($header.$data); + } + + /** + * Writes Excel DIMENSIONS to define the area in which there is data. + * + * @access private + */ + function _storeDimensions() + { + $record = 0x0200; // Record identifier + $row_min = $this->_dim_rowmin; // First row + $row_max = $this->_dim_rowmax + 1; // Last row plus 1 + $col_min = $this->_dim_colmin; // First column + $col_max = $this->_dim_colmax + 1; // Last column plus 1 + $reserved = 0x0000; // Reserved by Excel + + if ($this->_BIFF_version == 0x0500) { + $length = 0x000A; // Number of bytes to follow + $data = pack("vvvvv", $row_min, $row_max, + $col_min, $col_max, $reserved); + } elseif ($this->_BIFF_version == 0x0600) { + $length = 0x000E; + $data = pack("VVvvv", $row_min, $row_max, + $col_min, $col_max, $reserved); + } + $header = pack("vv", $record, $length); + $this->_prepend($header.$data); + } + + /** + * Write BIFF record Window2. + * + * @access private + */ + function _storeWindow2() + { + $record = 0x023E; // Record identifier + if ($this->_BIFF_version == 0x0500) { + $length = 0x000A; // Number of bytes to follow + } elseif ($this->_BIFF_version == 0x0600) { + $length = 0x0012; + } + + $grbit = 0x00B6; // Option flags + $rwTop = 0x0000; // Top row visible in window + $colLeft = 0x0000; // Leftmost column visible in window + + + // The options flags that comprise $grbit + $fDspFmla = 0; // 0 - bit + $fDspGrid = $this->_screen_gridlines; // 1 + $fDspRwCol = 1; // 2 + $fFrozen = $this->_frozen; // 3 + $fDspZeros = 1; // 4 + $fDefaultHdr = 1; // 5 + $fArabic = 0; // 6 + $fDspGuts = $this->_outline_on; // 7 + $fFrozenNoSplit = 0; // 0 - bit + $fSelected = $this->selected; // 1 + $fPaged = 1; // 2 + + $grbit = $fDspFmla; + $grbit |= $fDspGrid << 1; + $grbit |= $fDspRwCol << 2; + $grbit |= $fFrozen << 3; + $grbit |= $fDspZeros << 4; + $grbit |= $fDefaultHdr << 5; + $grbit |= $fArabic << 6; + $grbit |= $fDspGuts << 7; + $grbit |= $fFrozenNoSplit << 8; + $grbit |= $fSelected << 9; + $grbit |= $fPaged << 10; + + $header = pack("vv", $record, $length); + $data = pack("vvv", $grbit, $rwTop, $colLeft); + // FIXME !!! + if ($this->_BIFF_version == 0x0500) { + $rgbHdr = 0x00000000; // Row/column heading and gridline color + $data .= pack("V", $rgbHdr); + } elseif ($this->_BIFF_version == 0x0600) { + $rgbHdr = 0x0040; // Row/column heading and gridline color index + $zoom_factor_page_break = 0x0000; + $zoom_factor_normal = 0x0000; + $data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000); + } + $this->_append($header.$data); + } + + /** + * Write BIFF record DEFCOLWIDTH if COLINFO records are in use. + * + * @access private + */ + function _storeDefcol() + { + $record = 0x0055; // Record identifier + $length = 0x0002; // Number of bytes to follow + $colwidth = 0x0008; // Default column width + + $header = pack("vv", $record, $length); + $data = pack("v", $colwidth); + $this->_prepend($header . $data); + } + + /** + * Write BIFF record COLINFO to define column widths + * + * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C + * length record. + * + * @access private + * @param array $col_array This is the only parameter received and is composed of the following: + * 0 => First formatted column, + * 1 => Last formatted column, + * 2 => Col width (8.43 is Excel default), + * 3 => The optional XF format of the column, + * 4 => Option flags. + * 5 => Optional outline level + */ + function _storeColinfo($col_array) + { + if (isset($col_array[0])) { + $colFirst = $col_array[0]; + } + if (isset($col_array[1])) { + $colLast = $col_array[1]; + } + if (isset($col_array[2])) { + $coldx = $col_array[2]; + } else { + $coldx = 8.43; + } + if (isset($col_array[3])) { + $format = $col_array[3]; + } else { + $format = 0; + } + if (isset($col_array[4])) { + $grbit = $col_array[4]; + } else { + $grbit = 0; + } + if (isset($col_array[5])) { + $level = $col_array[5]; + } else { + $level = 0; + } + $record = 0x007D; // Record identifier + $length = 0x000B; // Number of bytes to follow + + $coldx += 0.72; // Fudge. Excel subtracts 0.72 !? + $coldx *= 256; // Convert to units of 1/256 of a char + + $ixfe = $this->_XF($format); + $reserved = 0x00; // Reserved + + $level = max(0, min($level, 7)); + $grbit |= $level << 8; + + $header = pack("vv", $record, $length); + $data = pack("vvvvvC", $colFirst, $colLast, $coldx, + $ixfe, $grbit, $reserved); + $this->_prepend($header.$data); + } + + /** + * Write BIFF record SELECTION. + * + * @access private + * @param array $array array containing ($rwFirst,$colFirst,$rwLast,$colLast) + * @see setSelection() + */ + function _storeSelection($array) + { + list($rwFirst,$colFirst,$rwLast,$colLast) = $array; + $record = 0x001D; // Record identifier + $length = 0x000F; // Number of bytes to follow + + $pnn = $this->_active_pane; // Pane position + $rwAct = $rwFirst; // Active row + $colAct = $colFirst; // Active column + $irefAct = 0; // Active cell ref + $cref = 1; // Number of refs + + if (!isset($rwLast)) { + $rwLast = $rwFirst; // Last row in reference + } + if (!isset($colLast)) { + $colLast = $colFirst; // Last col in reference + } + + // Swap last row/col for first row/col as necessary + if ($rwFirst > $rwLast) { + list($rwFirst, $rwLast) = array($rwLast, $rwFirst); + } + + if ($colFirst > $colLast) { + list($colFirst, $colLast) = array($colLast, $colFirst); + } + + $header = pack("vv", $record, $length); + $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct, + $irefAct, $cref, + $rwFirst, $rwLast, + $colFirst, $colLast); + $this->_append($header . $data); + } + + /** + * Store the MERGEDCELLS record for all ranges of merged cells + * + * @access private + */ + function _storeMergedCells() + { + // if there are no merged cell ranges set, return + if (count($this->_merged_ranges) == 0) { + return; + } + $record = 0x00E5; + $length = 2 + count($this->_merged_ranges) * 8; + + $header = pack('vv', $record, $length); + $data = pack('v', count($this->_merged_ranges)); + foreach ($this->_merged_ranges as $range) { + $data .= pack('vvvv', $range[0], $range[2], $range[1], $range[3]); + } + $this->_append($header . $data); + } + + /** + * Write BIFF record EXTERNCOUNT to indicate the number of external sheet + * references in a worksheet. + * + * Excel only stores references to external sheets that are used in formulas. + * For simplicity we store references to all the sheets in the workbook + * regardless of whether they are used or not. This reduces the overall + * complexity and eliminates the need for a two way dialogue between the formula + * parser the worksheet objects. + * + * @access private + * @param integer $count The number of external sheet references in this worksheet + */ + function _storeExterncount($count) + { + $record = 0x0016; // Record identifier + $length = 0x0002; // Number of bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $count); + $this->_prepend($header . $data); + } + + /** + * Writes the Excel BIFF EXTERNSHEET record. These references are used by + * formulas. A formula references a sheet name via an index. Since we store a + * reference to all of the external worksheets the EXTERNSHEET index is the same + * as the worksheet index. + * + * @access private + * @param string $sheetname The name of a external worksheet + */ + function _storeExternsheet($sheetname) + { + $record = 0x0017; // Record identifier + + // References to the current sheet are encoded differently to references to + // external sheets. + // + if ($this->name == $sheetname) { + $sheetname = ''; + $length = 0x02; // The following 2 bytes + $cch = 1; // The following byte + $rgch = 0x02; // Self reference + } else { + $length = 0x02 + strlen($sheetname); + $cch = strlen($sheetname); + $rgch = 0x03; // Reference to a sheet in the current workbook + } + + $header = pack("vv", $record, $length); + $data = pack("CC", $cch, $rgch); + $this->_prepend($header . $data . $sheetname); + } + + /** + * Writes the Excel BIFF PANE record. + * The panes can either be frozen or thawed (unfrozen). + * Frozen panes are specified in terms of an integer number of rows and columns. + * Thawed panes are specified in terms of Excel's units for rows and columns. + * + * @access private + * @param array $panes This is the only parameter received and is composed of the following: + * 0 => Vertical split position, + * 1 => Horizontal split position + * 2 => Top row visible + * 3 => Leftmost column visible + * 4 => Active pane + */ + function _storePanes($panes) + { + $y = $panes[0]; + $x = $panes[1]; + $rwTop = $panes[2]; + $colLeft = $panes[3]; + if (count($panes) > 4) { // if Active pane was received + $pnnAct = $panes[4]; + } else { + $pnnAct = null; + } + $record = 0x0041; // Record identifier + $length = 0x000A; // Number of bytes to follow + + // Code specific to frozen or thawed panes. + if ($this->_frozen) { + // Set default values for $rwTop and $colLeft + if (!isset($rwTop)) { + $rwTop = $y; + } + if (!isset($colLeft)) { + $colLeft = $x; + } + } else { + // Set default values for $rwTop and $colLeft + if (!isset($rwTop)) { + $rwTop = 0; + } + if (!isset($colLeft)) { + $colLeft = 0; + } + + // Convert Excel's row and column units to the internal units. + // The default row height is 12.75 + // The default column width is 8.43 + // The following slope and intersection values were interpolated. + // + $y = 20*$y + 255; + $x = 113.879*$x + 390; + } + + + // Determine which pane should be active. There is also the undocumented + // option to override this should it be necessary: may be removed later. + // + if (!isset($pnnAct)) { + if ($x != 0 && $y != 0) { + $pnnAct = 0; // Bottom right + } + if ($x != 0 && $y == 0) { + $pnnAct = 1; // Top right + } + if ($x == 0 && $y != 0) { + $pnnAct = 2; // Bottom left + } + if ($x == 0 && $y == 0) { + $pnnAct = 3; // Top left + } + } + + $this->_active_pane = $pnnAct; // Used in _storeSelection + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct); + $this->_append($header . $data); + } + + /** + * Store the page setup SETUP BIFF record. + * + * @access private + */ + function _storeSetup() + { + $record = 0x00A1; // Record identifier + $length = 0x0022; // Number of bytes to follow + + $iPaperSize = $this->_paper_size; // Paper size + $iScale = $this->_print_scale; // Print scaling factor + $iPageStart = 0x01; // Starting page number + $iFitWidth = $this->_fit_width; // Fit to number of pages wide + $iFitHeight = $this->_fit_height; // Fit to number of pages high + $grbit = 0x00; // Option flags + $iRes = 0x0258; // Print resolution + $iVRes = 0x0258; // Vertical print resolution + $numHdr = $this->_margin_head; // Header Margin + $numFtr = $this->_margin_foot; // Footer Margin + $iCopies = 0x01; // Number of copies + + $fLeftToRight = 0x0; // Print over then down + $fLandscape = $this->_orientation; // Page orientation + $fNoPls = 0x0; // Setup not read from printer + $fNoColor = 0x0; // Print black and white + $fDraft = 0x0; // Print draft quality + $fNotes = 0x0; // Print notes + $fNoOrient = 0x0; // Orientation not set + $fUsePage = 0x0; // Use custom starting page + + $grbit = $fLeftToRight; + $grbit |= $fLandscape << 1; + $grbit |= $fNoPls << 2; + $grbit |= $fNoColor << 3; + $grbit |= $fDraft << 4; + $grbit |= $fNotes << 5; + $grbit |= $fNoOrient << 6; + $grbit |= $fUsePage << 7; + + $numHdr = pack("d", $numHdr); + $numFtr = pack("d", $numFtr); + if ($this->_byte_order) { // if it's Big Endian + $numHdr = strrev($numHdr); + $numFtr = strrev($numFtr); + } + + $header = pack("vv", $record, $length); + $data1 = pack("vvvvvvvv", $iPaperSize, + $iScale, + $iPageStart, + $iFitWidth, + $iFitHeight, + $grbit, + $iRes, + $iVRes); + $data2 = $numHdr.$numFtr; + $data3 = pack("v", $iCopies); + $this->_prepend($header . $data1 . $data2 . $data3); + } + + /** + * Store the header caption BIFF record. + * + * @access private + */ + function _storeHeader() + { + $record = 0x0014; // Record identifier + + $str = $this->_header; // header string + $cch = strlen($str); // Length of header string + if ($this->_BIFF_version == 0x0600) { + $encoding = 0x0; // TODO: Unicode support + $length = 3 + $cch; // Bytes to follow + } else { + $length = 1 + $cch; // Bytes to follow + } + + $header = pack("vv", $record, $length); + if ($this->_BIFF_version == 0x0600) { + $data = pack("vC", $cch, $encoding); + } else { + $data = pack("C", $cch); + } + + $this->_prepend($header.$data.$str); + } + + /** + * Store the footer caption BIFF record. + * + * @access private + */ + function _storeFooter() + { + $record = 0x0015; // Record identifier + + $str = $this->_footer; // Footer string + $cch = strlen($str); // Length of footer string + if ($this->_BIFF_version == 0x0600) { + $encoding = 0x0; // TODO: Unicode support + $length = 3 + $cch; // Bytes to follow + } else { + $length = 1 + $cch; + } + + $header = pack("vv", $record, $length); + if ($this->_BIFF_version == 0x0600) { + $data = pack("vC", $cch, $encoding); + } else { + $data = pack("C", $cch); + } + + $this->_prepend($header . $data . $str); + } + + /** + * Store the horizontal centering HCENTER BIFF record. + * + * @access private + */ + function _storeHcenter() + { + $record = 0x0083; // Record identifier + $length = 0x0002; // Bytes to follow + + $fHCenter = $this->_hcenter; // Horizontal centering + + $header = pack("vv", $record, $length); + $data = pack("v", $fHCenter); + + $this->_prepend($header.$data); + } + + /** + * Store the vertical centering VCENTER BIFF record. + * + * @access private + */ + function _storeVcenter() + { + $record = 0x0084; // Record identifier + $length = 0x0002; // Bytes to follow + + $fVCenter = $this->_vcenter; // Horizontal centering + + $header = pack("vv", $record, $length); + $data = pack("v", $fVCenter); + $this->_prepend($header . $data); + } + + /** + * Store the LEFTMARGIN BIFF record. + * + * @access private + */ + function _storeMarginLeft() + { + $record = 0x0026; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_left; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) { // if it's Big Endian + $data = strrev($data); + } + + $this->_prepend($header . $data); + } + + /** + * Store the RIGHTMARGIN BIFF record. + * + * @access private + */ + function _storeMarginRight() + { + $record = 0x0027; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_right; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) { // if it's Big Endian + $data = strrev($data); + } + + $this->_prepend($header . $data); + } + + /** + * Store the TOPMARGIN BIFF record. + * + * @access private + */ + function _storeMarginTop() + { + $record = 0x0028; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_top; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) { // if it's Big Endian + $data = strrev($data); + } + + $this->_prepend($header . $data); + } + + /** + * Store the BOTTOMMARGIN BIFF record. + * + * @access private + */ + function _storeMarginBottom() + { + $record = 0x0029; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_bottom; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + if ($this->_byte_order) { // if it's Big Endian + $data = strrev($data); + } + + $this->_prepend($header . $data); + } + + /** + * Merges the area given by its arguments. + * This is an Excel97/2000 method. It is required to perform more complicated + * merging than the normal setAlign('merge'). + * + * @access public + * @param integer $first_row First row of the area to merge + * @param integer $first_col First column of the area to merge + * @param integer $last_row Last row of the area to merge + * @param integer $last_col Last column of the area to merge + */ + function mergeCells($first_row, $first_col, $last_row, $last_col) + { + $record = 0x00E5; // Record identifier + $length = 0x000A; // Bytes to follow + $cref = 1; // Number of refs + + // Swap last row/col for first row/col as necessary + if ($first_row > $last_row) { + list($first_row, $last_row) = array($last_row, $first_row); + } + + if ($first_col > $last_col) { + list($first_col, $last_col) = array($last_col, $first_col); + } + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $cref, $first_row, $last_row, + $first_col, $last_col); + + $this->_append($header.$data); + } + + /** + * Write the PRINTHEADERS BIFF record. + * + * @access private + */ + function _storePrintHeaders() + { + $record = 0x002a; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintRwCol = $this->_print_headers; // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fPrintRwCol); + $this->_prepend($header . $data); + } + + /** + * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the + * GRIDSET record. + * + * @access private + */ + function _storePrintGridlines() + { + $record = 0x002b; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintGrid = $this->_print_gridlines; // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fPrintGrid); + $this->_prepend($header . $data); + } + + /** + * Write the GRIDSET BIFF record. Must be used in conjunction with the + * PRINTGRIDLINES record. + * + * @access private + */ + function _storeGridset() + { + $record = 0x0082; // Record identifier + $length = 0x0002; // Bytes to follow + + $fGridSet = !($this->_print_gridlines); // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fGridSet); + $this->_prepend($header . $data); + } + + /** + * Write the GUTS BIFF record. This is used to configure the gutter margins + * where Excel outline symbols are displayed. The visibility of the gutters is + * controlled by a flag in WSBOOL. + * + * @see _storeWsbool() + * @access private + */ + function _storeGuts() + { + $record = 0x0080; // Record identifier + $length = 0x0008; // Bytes to follow + + $dxRwGut = 0x0000; // Size of row gutter + $dxColGut = 0x0000; // Size of col gutter + + $row_level = $this->_outline_row_level; + $col_level = 0; + + // Calculate the maximum column outline level. The equivalent calculation + // for the row outline level is carried out in setRow(). + $colcount = count($this->_colinfo); + for ($i = 0; $i < $colcount; $i++) { + // Skip cols without outline level info. + if (count($this->_colinfo[$i]) >= 6) { + $col_level = max($this->_colinfo[$i][5], $col_level); + } + } + + // Set the limits for the outline levels (0 <= x <= 7). + $col_level = max(0, min($col_level, 7)); + + // The displayed level is one greater than the max outline levels + if ($row_level) { + $row_level++; + } + if ($col_level) { + $col_level++; + } + + $header = pack("vv", $record, $length); + $data = pack("vvvv", $dxRwGut, $dxColGut, $row_level, $col_level); + + $this->_prepend($header.$data); + } + + + /** + * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction + * with the SETUP record. + * + * @access private + */ + function _storeWsbool() + { + $record = 0x0081; // Record identifier + $length = 0x0002; // Bytes to follow + $grbit = 0x0000; + + // The only option that is of interest is the flag for fit to page. So we + // set all the options in one go. + // + /*if ($this->_fit_page) { + $grbit = 0x05c1; + } else { + $grbit = 0x04c1; + }*/ + // Set the option flags + $grbit |= 0x0001; // Auto page breaks visible + if ($this->_outline_style) { + $grbit |= 0x0020; // Auto outline styles + } + if ($this->_outline_below) { + $grbit |= 0x0040; // Outline summary below + } + if ($this->_outline_right) { + $grbit |= 0x0080; // Outline summary right + } + if ($this->_fit_page) { + $grbit |= 0x0100; // Page setup fit to page + } + if ($this->_outline_on) { + $grbit |= 0x0400; // Outline symbols displayed + } + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + $this->_prepend($header . $data); + } + + /** + * Write the HORIZONTALPAGEBREAKS BIFF record. + * + * @access private + */ + function _storeHbreak() + { + // Return if the user hasn't specified pagebreaks + if (empty($this->_hbreaks)) { + return; + } + + // Sort and filter array of page breaks + $breaks = $this->_hbreaks; + sort($breaks, SORT_NUMERIC); + if ($breaks[0] == 0) { // don't use first break if it's 0 + array_shift($breaks); + } + + $record = 0x001b; // Record identifier + $cbrk = count($breaks); // Number of page breaks + if ($this->_BIFF_version == 0x0600) { + $length = 2 + 6*$cbrk; // Bytes to follow + } else { + $length = 2 + 2*$cbrk; // Bytes to follow + } + + $header = pack("vv", $record, $length); + $data = pack("v", $cbrk); + + // Append each page break + foreach ($breaks as $break) { + if ($this->_BIFF_version == 0x0600) { + $data .= pack("vvv", $break, 0x0000, 0x00ff); + } else { + $data .= pack("v", $break); + } + } + + $this->_prepend($header.$data); + } + + + /** + * Write the VERTICALPAGEBREAKS BIFF record. + * + * @access private + */ + function _storeVbreak() + { + // Return if the user hasn't specified pagebreaks + if (empty($this->_vbreaks)) { + return; + } + + // 1000 vertical pagebreaks appears to be an internal Excel 5 limit. + // It is slightly higher in Excel 97/200, approx. 1026 + $breaks = array_slice($this->_vbreaks,0,1000); + + // Sort and filter array of page breaks + sort($breaks, SORT_NUMERIC); + if ($breaks[0] == 0) { // don't use first break if it's 0 + array_shift($breaks); + } + + $record = 0x001a; // Record identifier + $cbrk = count($breaks); // Number of page breaks + if ($this->_BIFF_version == 0x0600) { + $length = 2 + 6*$cbrk; // Bytes to follow + } else { + $length = 2 + 2*$cbrk; // Bytes to follow + } + + $header = pack("vv", $record, $length); + $data = pack("v", $cbrk); + + // Append each page break + foreach ($breaks as $break) { + if ($this->_BIFF_version == 0x0600) { + $data .= pack("vvv", $break, 0x0000, 0xffff); + } else { + $data .= pack("v", $break); + } + } + + $this->_prepend($header . $data); + } + + /** + * Set the Biff PROTECT record to indicate that the worksheet is protected. + * + * @access private + */ + function _storeProtect() + { + // Exit unless sheet protection has been specified + if ($this->_protect == 0) { + return; + } + + $record = 0x0012; // Record identifier + $length = 0x0002; // Bytes to follow + + $fLock = $this->_protect; // Worksheet is protected + + $header = pack("vv", $record, $length); + $data = pack("v", $fLock); + + $this->_prepend($header.$data); + } + + /** + * Write the worksheet PASSWORD record. + * + * @access private + */ + function _storePassword() + { + // Exit unless sheet protection and password have been specified + if (($this->_protect == 0) || (!isset($this->_password))) { + return; + } + + $record = 0x0013; // Record identifier + $length = 0x0002; // Bytes to follow + + $wPassword = $this->_password; // Encoded password + + $header = pack("vv", $record, $length); + $data = pack("v", $wPassword); + + $this->_prepend($header . $data); + } + + + /** + * Insert a 24bit bitmap image in a worksheet. + * + * @access public + * @param integer $row The row we are going to insert the bitmap into + * @param integer $col The column we are going to insert the bitmap into + * @param string $bitmap The bitmap filename + * @param integer $x The horizontal position (offset) of the image inside the cell. + * @param integer $y The vertical position (offset) of the image inside the cell. + * @param integer $scale_x The horizontal scale + * @param integer $scale_y The vertical scale + */ + function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) + { + $bitmap_array = $this->_processBitmap($bitmap); + if ($this->isError($bitmap_array)) { + $this->writeString($row, $col, $bitmap_array->getMessage()); + return; + } + list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap); + + // Scale the frame of the image. + $width *= $scale_x; + $height *= $scale_y; + + // Calculate the vertices of the image and write the OBJ record + $this->_positionImage($col, $row, $x, $y, $width, $height); + + // Write the IMDATA record to store the bitmap data + $record = 0x007f; + $length = 8 + $size; + $cf = 0x09; + $env = 0x01; + $lcb = $size; + + $header = pack("vvvvV", $record, $length, $cf, $env, $lcb); + $this->_append($header.$data); + } + + /** + * Calculate the vertices that define the position of the image as required by + * the OBJ record. + * + * +------------+------------+ + * | A | B | + * +-----+------------+------------+ + * | |(x1,y1) | | + * | 1 |(A1)._______|______ | + * | | | | | + * | | | | | + * +-----+----| BITMAP |-----+ + * | | | | | + * | 2 | |______________. | + * | | | (B2)| + * | | | (x2,y2)| + * +---- +------------+------------+ + * + * Example of a bitmap that covers some of the area from cell A1 to cell B2. + * + * Based on the width and height of the bitmap we need to calculate 8 vars: + * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2. + * The width and height of the cells are also variable and have to be taken into + * account. + * The values of $col_start and $row_start are passed in from the calling + * function. The values of $col_end and $row_end are calculated by subtracting + * the width and height of the bitmap from the width and height of the + * underlying cells. + * The vertices are expressed as a percentage of the underlying cell width as + * follows (rhs values are in pixels): + * + * x1 = X / W *1024 + * y1 = Y / H *256 + * x2 = (X-1) / W *1024 + * y2 = (Y-1) / H *256 + * + * Where: X is distance from the left side of the underlying cell + * Y is distance from the top of the underlying cell + * W is the width of the cell + * H is the height of the cell + * + * @access private + * @note the SDK incorrectly states that the height should be expressed as a + * percentage of 1024. + * @param integer $col_start Col containing upper left corner of object + * @param integer $row_start Row containing top left corner of object + * @param integer $x1 Distance to left side of object + * @param integer $y1 Distance to top of object + * @param integer $width Width of image frame + * @param integer $height Height of image frame + */ + function _positionImage($col_start, $row_start, $x1, $y1, $width, $height) + { + // Initialise end cell to the same as the start cell + $col_end = $col_start; // Col containing lower right corner of object + $row_end = $row_start; // Row containing bottom right corner of object + + // Zero the specified offset if greater than the cell dimensions + if ($x1 >= $this->_sizeCol($col_start)) { + $x1 = 0; + } + if ($y1 >= $this->_sizeRow($row_start)) { + $y1 = 0; + } + + $width = $width + $x1 -1; + $height = $height + $y1 -1; + + // Subtract the underlying cell widths to find the end cell of the image + while ($width >= $this->_sizeCol($col_end)) { + $width -= $this->_sizeCol($col_end); + $col_end++; + } + + // Subtract the underlying cell heights to find the end cell of the image + while ($height >= $this->_sizeRow($row_end)) { + $height -= $this->_sizeRow($row_end); + $row_end++; + } + + // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell + // with zero eight or width. + // + if ($this->_sizeCol($col_start) == 0) { + return; + } + if ($this->_sizeCol($col_end) == 0) { + return; + } + if ($this->_sizeRow($row_start) == 0) { + return; + } + if ($this->_sizeRow($row_end) == 0) { + return; + } + + // Convert the pixel values to the percentage value expected by Excel + $x1 = $x1 / $this->_sizeCol($col_start) * 1024; + $y1 = $y1 / $this->_sizeRow($row_start) * 256; + $x2 = $width / $this->_sizeCol($col_end) * 1024; // Distance to right side of object + $y2 = $height / $this->_sizeRow($row_end) * 256; // Distance to bottom of object + + $this->_storeObjPicture($col_start, $x1, + $row_start, $y1, + $col_end, $x2, + $row_end, $y2); + } + + /** + * Convert the width of a cell from user's units to pixels. By interpolation + * the relationship is: y = 7x +5. If the width hasn't been set by the user we + * use the default value. If the col is hidden we use a value of zero. + * + * @access private + * @param integer $col The column + * @return integer The width in pixels + */ + function _sizeCol($col) + { + // Look up the cell value to see if it has been changed + if (isset($this->col_sizes[$col])) { + if ($this->col_sizes[$col] == 0) { + return(0); + } else { + return(floor(7 * $this->col_sizes[$col] + 5)); + } + } else { + return(64); + } + } + + /** + * Convert the height of a cell from user's units to pixels. By interpolation + * the relationship is: y = 4/3x. If the height hasn't been set by the user we + * use the default value. If the row is hidden we use a value of zero. (Not + * possible to hide row yet). + * + * @access private + * @param integer $row The row + * @return integer The width in pixels + */ + function _sizeRow($row) + { + // Look up the cell value to see if it has been changed + if (isset($this->_row_sizes[$row])) { + if ($this->_row_sizes[$row] == 0) { + return(0); + } else { + return(floor(4/3 * $this->_row_sizes[$row])); + } + } else { + return(17); + } + } + + /** + * Store the OBJ record that precedes an IMDATA record. This could be generalise + * to support other Excel objects. + * + * @access private + * @param integer $colL Column containing upper left corner of object + * @param integer $dxL Distance from left side of cell + * @param integer $rwT Row containing top left corner of object + * @param integer $dyT Distance from top of cell + * @param integer $colR Column containing lower right corner of object + * @param integer $dxR Distance from right of cell + * @param integer $rwB Row containing bottom right corner of object + * @param integer $dyB Distance from bottom of cell + */ + function _storeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB) + { + $record = 0x005d; // Record identifier + $length = 0x003c; // Bytes to follow + + $cObj = 0x0001; // Count of objects in file (set to 1) + $OT = 0x0008; // Object type. 8 = Picture + $id = 0x0001; // Object ID + $grbit = 0x0614; // Option flags + + $cbMacro = 0x0000; // Length of FMLA structure + $Reserved1 = 0x0000; // Reserved + $Reserved2 = 0x0000; // Reserved + + $icvBack = 0x09; // Background colour + $icvFore = 0x09; // Foreground colour + $fls = 0x00; // Fill pattern + $fAuto = 0x00; // Automatic fill + $icv = 0x08; // Line colour + $lns = 0xff; // Line style + $lnw = 0x01; // Line weight + $fAutoB = 0x00; // Automatic border + $frs = 0x0000; // Frame style + $cf = 0x0009; // Image format, 9 = bitmap + $Reserved3 = 0x0000; // Reserved + $cbPictFmla = 0x0000; // Length of FMLA structure + $Reserved4 = 0x0000; // Reserved + $grbit2 = 0x0001; // Option flags + $Reserved5 = 0x0000; // Reserved + + + $header = pack("vv", $record, $length); + $data = pack("V", $cObj); + $data .= pack("v", $OT); + $data .= pack("v", $id); + $data .= pack("v", $grbit); + $data .= pack("v", $colL); + $data .= pack("v", $dxL); + $data .= pack("v", $rwT); + $data .= pack("v", $dyT); + $data .= pack("v", $colR); + $data .= pack("v", $dxR); + $data .= pack("v", $rwB); + $data .= pack("v", $dyB); + $data .= pack("v", $cbMacro); + $data .= pack("V", $Reserved1); + $data .= pack("v", $Reserved2); + $data .= pack("C", $icvBack); + $data .= pack("C", $icvFore); + $data .= pack("C", $fls); + $data .= pack("C", $fAuto); + $data .= pack("C", $icv); + $data .= pack("C", $lns); + $data .= pack("C", $lnw); + $data .= pack("C", $fAutoB); + $data .= pack("v", $frs); + $data .= pack("V", $cf); + $data .= pack("v", $Reserved3); + $data .= pack("v", $cbPictFmla); + $data .= pack("v", $Reserved4); + $data .= pack("v", $grbit2); + $data .= pack("V", $Reserved5); + + $this->_append($header . $data); + } + + /** + * Convert a 24 bit bitmap into the modified internal format used by Windows. + * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the + * MSDN library. + * + * @access private + * @param string $bitmap The bitmap to process + * @return array Array with data and properties of the bitmap + */ + function _processBitmap($bitmap) + { + // Open file. + $bmp_fd = @fopen($bitmap,"rb"); + if (!$bmp_fd) { + $this->raiseError("Couldn't import $bitmap"); + } + + // Slurp the file into a string. + $data = fread($bmp_fd, filesize($bitmap)); + + // Check that the file is big enough to be a bitmap. + if (strlen($data) <= 0x36) { + $this->raiseError("$bitmap doesn't contain enough data.\n"); + } + + // The first 2 bytes are used to identify the bitmap. + $identity = unpack("A2ident", $data); + if ($identity['ident'] != "BM") { + $this->raiseError("$bitmap doesn't appear to be a valid bitmap image.\n"); + } + + // Remove bitmap data: ID. + $data = substr($data, 2); + + // Read and remove the bitmap size. This is more reliable than reading + // the data size at offset 0x22. + // + $size_array = unpack("Vsa", substr($data, 0, 4)); + $size = $size_array['sa']; + $data = substr($data, 4); + $size -= 0x36; // Subtract size of bitmap header. + $size += 0x0C; // Add size of BIFF header. + + // Remove bitmap data: reserved, offset, header length. + $data = substr($data, 12); + + // Read and remove the bitmap width and height. Verify the sizes. + $width_and_height = unpack("V2", substr($data, 0, 8)); + $width = $width_and_height[1]; + $height = $width_and_height[2]; + $data = substr($data, 8); + if ($width > 0xFFFF) { + $this->raiseError("$bitmap: largest image width supported is 65k.\n"); + } + if ($height > 0xFFFF) { + $this->raiseError("$bitmap: largest image height supported is 65k.\n"); + } + + // Read and remove the bitmap planes and bpp data. Verify them. + $planes_and_bitcount = unpack("v2", substr($data, 0, 4)); + $data = substr($data, 4); + if ($planes_and_bitcount[2] != 24) { // Bitcount + $this->raiseError("$bitmap isn't a 24bit true color bitmap.\n"); + } + if ($planes_and_bitcount[1] != 1) { + $this->raiseError("$bitmap: only 1 plane supported in bitmap image.\n"); + } + + // Read and remove the bitmap compression. Verify compression. + $compression = unpack("Vcomp", substr($data, 0, 4)); + $data = substr($data, 4); + + //$compression = 0; + if ($compression['comp'] != 0) { + $this->raiseError("$bitmap: compression not supported in bitmap image.\n"); + } + + // Remove bitmap data: data size, hres, vres, colours, imp. colours. + $data = substr($data, 20); + + // Add the BITMAPCOREHEADER data + $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18); + $data = $header . $data; + + return (array($width, $height, $size, $data)); + } + + /** + * Store the window zoom factor. This should be a reduced fraction but for + * simplicity we will store all fractions with a numerator of 100. + * + * @access private + */ + function _storeZoom() + { + // If scale is 100 we don't need to write a record + if ($this->_zoom == 100) { + return; + } + + $record = 0x00A0; // Record identifier + $length = 0x0004; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("vv", $this->_zoom, 100); + $this->_append($header . $data); + } + + /** + * FIXME: add comments + */ + function setValidation($row1, $col1, $row2, $col2, &$validator) + { + $this->_dv[] = $validator->_getData() . + pack("vvvvv", 1, $row1, $row2, $col1, $col2); + } + + /** + * Store the DVAL and DV records. + * + * @access private + */ + function _storeDataValidity() + { + $record = 0x01b2; // Record identifier + $length = 0x0012; // Bytes to follow + + $grbit = 0x0002; // Prompt box at cell, no cached validity data at DV records + $horPos = 0x00000000; // Horizontal position of prompt box, if fixed position + $verPos = 0x00000000; // Vertical position of prompt box, if fixed position + $objId = 0xffffffff; // Object identifier of drop down arrow object, or -1 if not visible + + $header = pack('vv', $record, $length); + $data = pack('vVVVV', $grbit, $horPos, $verPos, $objId, + count($this->_dv)); + $this->_append($header.$data); + + $record = 0x01be; // Record identifier + foreach ($this->_dv as $dv) { + $length = strlen($dv); // Bytes to follow + $header = pack("vv", $record, $length); + $this->_append($header . $dv); + } + } +} +?> diff --git a/functions/adLDAP/src/adLDAP.php b/functions/adLDAP/src/adLDAP.php new file mode 100755 index 000000000..c7aab5a83 --- /dev/null +++ b/functions/adLDAP/src/adLDAP.php @@ -0,0 +1,1001 @@ +ldapConnection) { + return $this->ldapConnection; + } + return false; + } + + /** + * Get the bind status + * + * @return bool + */ + public function getLdapBind() { + return $this->ldapBind; + } + + /** + * Get the current base DN + * + * @return string + */ + public function getBaseDn() { + return $this->baseDn; + } + + /** + * Set the current base DN + * + * @param string $baseDn + */ + public function setBaseDn($baseDn) { + $this->baseDn = $baseDn; + } + + /** + * The group class + * + * @var \adLDAP\classes\adLDAPGroups + */ + protected $groupClass; + + /** + * Get the group class interface + * + * @return \adLDAP\classes\adLDAPGroups + */ + public function group() { + if (!$this->groupClass) { + $this->groupClass = new \adLDAP\classes\adLDAPGroups($this); + } + return $this->groupClass; + } + + /** + * The user class + * + * @var \adLDAP\classes\adLDAPUsers + */ + protected $userClass; + + /** + * Get the userclass interface + * + * @return \adLDAP\classes\adLDAPUsers + */ + public function user() { + if (!$this->userClass) { + $this->userClass = new \adLDAP\classes\adLDAPUsers($this); + } + return $this->userClass; + } + + /** + * The folders class + * + * @var \adLDAP\classes\adLDAPFolders + */ + protected $folderClass; + + /** + * Get the folder class interface + * + * @return \adLDAP\classes\adLDAPFolders + */ + public function folder() { + if (!$this->folderClass) { + $this->folderClass = new \adLDAP\classes\adLDAPFolders($this); + } + return $this->folderClass; + } + + /** + * The utils class + * + * @var \adLDAP\classes\adLDAPUtils + */ + protected $utilClass; + + /** + * Get the utils class interface + * + * @return \adLDAP\classes\adLDAPUtils + */ + public function utilities() { + if (!$this->utilClass) { + $this->utilClass = new \adLDAP\classes\adLDAPUtils($this); + } + return $this->utilClass; + } + + /** + * The contacts class + * + * @var \adLDAP\classes\adLDAPContacts + */ + protected $contactClass; + + /** + * Get the contacts class interface + * + * @return \adLDAP\classes\adLDAPContacts + */ + public function contact() { + if (!$this->contactClass) { + $this->contactClass = new \adLDAP\classes\adLDAPContacts($this); + } + return $this->contactClass; + } + + /** + * The exchange class + * + * @var \adLDAP\classes\adLDAPExchange + */ + protected $exchangeClass; + + /** + * Get the exchange class interface + * + * @return \adLDAP\classes\adLDAPExchange + */ + public function exchange() { + if (!$this->exchangeClass) { + $this->exchangeClass = new \adLDAP\classes\adLDAPExchange($this); + } + return $this->exchangeClass; + } + + /** + * The computers class + * + * @var \adLDAP\classes\adLDAPComputers + */ + protected $computerClass; + + /** + * Get the computers class interface + * + * @return \adLDAP\classes\adLDAPComputers + */ + public function computer() { + if (!$this->computerClass) { + $this->computerClass = new \adLDAP\classes\adLDAPComputers($this); + } + return $this->computerClass; + } + + /** + * Getters and Setters + */ + + /** + * Set the account suffix + * + * @param string $accountSuffix + * @return void + */ + public function setAccountSuffix($accountSuffix) { + $this->accountSuffix = $accountSuffix; + } + + /** + * Get the account suffix + * + * @return string + */ + public function getAccountSuffix() { + return $this->accountSuffix; + } + + /** + * Set the domain controllers array + * + * @param array $domainControllers + * @return void + */ + public function setDomainControllers(array $domainControllers) { + $this->domainControllers = $domainControllers; + } + + /** + * Get the list of domain controllers + * + * @return void + */ + public function getDomainControllers() { + return $this->domainControllers; + } + + /** + * Sets the port number your domain controller communicates over + * + * @param int $adPort + */ + public function setPort($adPort) { + $this->adPort = $adPort; + } + + /** + * Gets the port number your domain controller communicates over + * + * @return int + */ + public function getPort() { + return $this->adPort; + } + + /** + * Set the username of an account with higher priviledges + * + * @param string $adminUsername + * @return void + */ + public function setAdminUsername($adminUsername) { + $this->adminUsername = $adminUsername; + } + + /** + * Get the username of the account with higher priviledges + * + * This will throw an exception for security reasons + */ + public function getAdminUsername() { + throw new adLDAPException('For security reasons you cannot access the domain administrator account details'); + } + + /** + * Set the password of an account with higher priviledges + * + * @param string $adminPassword + * @return void + */ + public function setAdminPassword($adminPassword) { + $this->adminPassword = $adminPassword; + } + + /** + * Get the password of the account with higher priviledges + * + * This will throw an exception for security reasons + */ + public function getAdminPassword() { + throw new adLDAPException('For security reasons you cannot access the domain administrator account details'); + } + + /** + * Set whether to detect the true primary group + * + * @param bool $realPrimaryGroup + * @return void + */ + public function setRealPrimaryGroup($realPrimaryGroup) { + $this->realPrimaryGroup = $realPrimaryGroup; + } + + /** + * Get the real primary group setting + * + * @return bool + */ + public function getRealPrimaryGroup() { + return $this->realPrimaryGroup; + } + + /** + * Set whether to use SSL + * + * @param bool $useSSL + * @return void + */ + public function setUseSSL($useSSL) { + $this->useSSL = $useSSL; + // Set the default port correctly + if($this->useSSL) { + $this->setPort(self::ADLDAP_LDAPS_PORT); + } + else { + $this->setPort(self::ADLDAP_LDAP_PORT); + } + } + + /** + * Get the SSL setting + * + * @return bool + */ + public function getUseSSL() { + return $this->useSSL; + } + + /** + * Set whether to use TLS + * + * @param bool $useTLS + * @return void + */ + public function setUseTLS($useTLS) { + $this->useTLS = $useTLS; + } + + /** + * Get the TLS setting + * + * @return bool + */ + public function getUseTLS() { + return $this->useTLS; + } + + /** + * Set whether to use SSO + * Requires ldap_sasl_bind support. Be sure --with-ldap-sasl is used when configuring PHP otherwise this function will be undefined. + * + * @param bool $useSSO + * @return void + */ + public function setUseSSO($useSSO) { + if ($useSSO === true && !$this->ldapSaslSupported()) { + throw new adLDAPException('No LDAP SASL support for PHP. See: http://www.php.net/ldap_sasl_bind'); + } + $this->useSSO = $useSSO; + } + + /** + * Get the SSO setting + * + * @return bool + */ + public function getUseSSO() { + return $this->useSSO; + } + + /** + * Set whether to lookup recursive groups + * + * @param bool $recursiveGroups + * @return void + */ + public function setRecursiveGroups($recursiveGroups) { + $this->recursiveGroups = $recursiveGroups; + } + + /** + * Get the recursive groups setting + * + * @return bool + */ + public function getRecursiveGroups() { + return $this->recursiveGroups; + } + + /** + * Default Constructor + * + * Tries to bind to the AD domain over LDAP or LDAPs + * + * @param array $options Array of options to pass to the constructor + * @throws \Exception - if unable to bind to Domain Controller + * @return bool + */ + function __construct($options = array()) { + // You can specifically overide any of the default configuration options setup above + if (count($options)>0) { + if (array_key_exists("account_suffix",$options)) { $this->accountSuffix = $options["account_suffix"]; } + if (array_key_exists("base_dn",$options)) { $this->baseDn = $options["base_dn"]; } + if (array_key_exists("domain_controllers",$options)) { + if (!is_array($options["domain_controllers"])) { + throw new adLDAPException('[domain_controllers] option must be an array'); + } + $this->domainControllers = $options["domain_controllers"]; + } + if (array_key_exists("admin_username",$options)) { $this->adminUsername = $options["admin_username"]; } + if (array_key_exists("admin_password",$options)) { $this->adminPassword = $options["admin_password"]; } + if (array_key_exists("real_primarygroup",$options)) { $this->realPrimaryGroup = $options["real_primarygroup"]; } + if (array_key_exists("use_ssl",$options)) { $this->setUseSSL($options["use_ssl"]); } + if (array_key_exists("use_tls",$options)) { $this->useTLS = $options["use_tls"]; } + if (array_key_exists("recursive_groups",$options)) { $this->recursiveGroups = $options["recursive_groups"]; } + if (array_key_exists("follow_referrals", $options)) { $this->followReferrals = $options["follow_referrals"]; } + if (array_key_exists("ad_port",$options)) { $this->setPort($options["ad_port"]); } + if (array_key_exists("sso",$options)) { + $this->setUseSSO($options["sso"]); + if (!$this->ldapSaslSupported()) { + $this->setUseSSO(false); + } + } + } + + if ($this->ldapSupported() === false) { + throw new adLDAPException('No LDAP support for PHP. See: http://www.php.net/ldap'); + } + + return $this->connect(); + } + + /** + * Default Destructor + * + * Closes the LDAP connection + * + * @return void + */ + function __destruct() { + $this->close(); + } + + /** + * Connects and Binds to the Domain Controller + * + * @return bool + */ + public function connect() { + // Connect to the AD/LDAP server as the username/password + $domainController = $this->randomController(); + //save controller + $this->selected_controller = $domainController; + + if ($this->useSSL) { + $this->ldapConnection = ldap_connect("ldaps://" . $domainController, $this->adPort); + } else { + $this->ldapConnection = ldap_connect("ldap://" . $domainController, $this->adPort); + } + + // Set some ldap options for talking to AD + ldap_set_option($this->ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); + ldap_set_option($this->ldapConnection, LDAP_OPT_REFERRALS, $this->followReferrals); + + if ($this->useTLS) { + ldap_start_tls($this->ldapConnection); + } + + // Bind as a domain admin if they've set it up + if ($this->adminUsername !== NULL && $this->adminPassword !== NULL) { + $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix, $this->adminPassword); + if (!$this->ldapBind) { + if ($this->useSSL && !$this->useTLS) { + // If you have problems troubleshooting, remove the @ character from the ldapldapBind command above to get the actual error message + throw new adLDAPException('Bind to Active Directory failed. Either the LDAPs connection failed or the login credentials are incorrect. AD said: ' . $this->getLastError()); + } + else { + throw new adLDAPException('Bind to Active Directory failed. Check the login credentials and/or server details. AD said: ' . $this->getLastError()); + } + } + } + if ($this->useSSO && $_SERVER['REMOTE_USER'] && $this->adminUsername === null && $_SERVER['KRB5CCNAME']) { + putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']); + $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI"); + if (!$this->ldapBind) { + throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError()); + } + else { + return true; + } + } + + if ($this->baseDn == NULL) { + $this->baseDn = $this->findBaseDn(); + } + return true; + } + + /** + * Closes the LDAP connection + * + * @return void + */ + public function close() { + if ($this->ldapConnection) { + @ldap_close($this->ldapConnection); + } + } + + /** + * Validate a user's login credentials + * + * @param string $username A user's AD username + * @param string $password A user's AD password + * @param bool optional $preventRebind + * @return bool + */ + public function authenticate($username, $password, $preventRebind = false) { + // Prevent null binding + if ($username === NULL || $password === NULL) { return false; } + if (empty($username) || empty($password)) { return false; } + + // Allow binding over SSO for Kerberos + if ($this->useSSO && $_SERVER['REMOTE_USER'] && $_SERVER['REMOTE_USER'] == $username && $this->adminUsername === NULL && $_SERVER['KRB5CCNAME']) { + putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']); + $this->ldapBind = @ldap_sasl_bind($this->ldapConnection, NULL, NULL, "GSSAPI"); + if (!$this->ldapBind) { + throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError()); + } + else { + return true; + } + } + + // Bind as the user + $ret = true; + $this->ldapBind = @ldap_bind($this->ldapConnection, $username . $this->accountSuffix, $password); + if (!$this->ldapBind) { + $ret = false; + } + + // Cnce we've checked their details, kick back into admin mode if we have it + if ($this->adminUsername !== NULL && !$preventRebind) { + $this->ldapBind = @ldap_bind($this->ldapConnection, $this->adminUsername . $this->accountSuffix , $this->adminPassword); + if (!$this->ldapBind) { + // This should never happen in theory + throw new adLDAPException('Rebind to Active Directory failed. AD said: ' . $this->getLastError()); + } + } + return $ret; + } + + /** + * Return a list of all found objects (except computer) in AD + * $search has to match either cn, displayname, samaccountname or sn + * + * @param bool $includeDescription Return a description,cn, displayname and distinguishedname of the user + * @param string $search Search parameter + * @param bool $sorted Sort the user accounts + * @return array, if $includeDescription=true then a multi-dimensional array + */ + public function search($includeDescription = false, $search = "*", $sorted = true) { + if (!$this->getLdapBind()) { return false; } + + // Perform the search and grab all their details + //$filter = "(|(cn=" . $search . ")(displayname=" . $search . ")(samaccountname=" . $search . "))"; + $filter = "(&(!(objectClass=computer))(|(anr=" . $search . ")))"; + $fields = array("cn","description","displayname","distinguishedname","samaccountname"); + $sr = ldap_search($this->getLdapConnection(), $this->getBaseDn(), $filter, $fields); + $entries = ldap_get_entries($this->getLdapConnection(), $sr); + + $objectArray = array(); + for ($i=0; $i<$entries["count"]; $i++) { + if ($includeDescription && strlen($entries[$i]["description"][0])>0) { + $objectArray[$entries[$i]["samaccountname"][0]] = array($entries[$i]["cn"][0],$entries[$i]["description"][0],$entries[$i]["displayname"][0],$entries[$i]["distinguishedname"][0]); + } elseif ($includeDescription) { + // description is set to displayname if no description is present + $objectArray[$entries[$i]["samaccountname"][0]] = array($entries[$i]["cn"][0],$entries[$i]["displayname"][0],$entries[$i]["displayname"][0],$entries[$i]["distinguishedname"][0]); + } else { + array_push($objectArray, $entries[$i]["samaccountname"][0]); + } + } + if ($sorted) { + asort($objectArray); + } + return $objectArray; + } + + /** + * Returns objectClass in an array + * + * @param string $distinguisedName The full DN of a contact + * @return array + */ + public function getObjectClass($distinguishedName) { + if ($distinguishedName === NULL) { return false; } + if (!$this->getLdapBind()) { return false; } + + $filter = "distinguishedName=" . $this->utilities()->ldapSlashes($distinguishedName); + + $fields = array("objectclass"); + $sr = ldap_search($this->getLdapConnection(), $this->getBaseDn(), $filter, $fields); + $entries = ldap_get_entries($this->getLdapConnection(), $sr); + + $objects = array(); + for ($i=0; $i<$entries[0]["objectclass"]["count"]; $i++) { + array_push($objects, $entries[0]["objectclass"][$i]); + } + return $objects; + } + + /** + * Find the Base DN of your domain controller + * + * @return string + */ + public function findBaseDn() { + $namingContext = $this->getRootDse(array('defaultnamingcontext')); + return $namingContext[0]['defaultnamingcontext'][0]; + } + + /** + * Get the RootDSE properties from a domain controller + * + * @param array $attributes The attributes you wish to query e.g. defaultnamingcontext + * @return array + */ + public function getRootDse($attributes = array("*", "+")) { + if (!$this->ldapBind) { return (false); } + + $sr = @ldap_read($this->ldapConnection, NULL, 'objectClass=*', $attributes); + $entries = @ldap_get_entries($this->ldapConnection, $sr); + return $entries; + } + + /** + * Get last error from Active Directory + * + * This function gets the last message from Active Directory + * This may indeed be a 'Success' message but if you get an unknown error + * it might be worth calling this function to see what errors were raised + * + * return string + */ + public function getLastError() { + return @ldap_error($this->ldapConnection); + } + + /** + * Detect LDAP support in php + * + * @return bool + */ + protected function ldapSupported() { + if (!function_exists('ldap_connect')) { + return false; + } + return true; + } + + /** + * Detect ldap_sasl_bind support in PHP + * + * @return bool + */ + protected function ldapSaslSupported() { + if (!function_exists('ldap_sasl_bind')) { + return false; + } + return true; + } + + /** + * Schema + * + * @param array $attributes Attributes to be queried + * @return array + */ + public function adldap_schema($attributes) { + + // LDAP doesn't like NULL attributes, only set them if they have values + // If you wish to remove an attribute you should set it to a space + // TO DO: Adapt user_modify to use ldap_mod_delete to remove a NULL attribute + $mod=array(); + + // Check every attribute to see if it contains 8bit characters and then UTF8 encode them + array_walk($attributes, array($this, 'encode8bit')); + + if (isset($attributes["address_city"])){ $mod["l"][0]=$attributes["address_city"]; } + if (isset($attributes["address_code"])){ $mod["postalCode"][0]=$attributes["address_code"]; } + //if ($attributes["address_country"]){ $mod["countryCode"][0]=$attributes["address_country"]; } // use country codes? + if (isset($attributes["address_country"])){ $mod["c"][0]=$attributes["address_country"]; } + if (isset($attributes["address_pobox"])){ $mod["postOfficeBox"][0]=$attributes["address_pobox"]; } + if (isset($attributes["address_state"])){ $mod["st"][0]=$attributes["address_state"]; } + if (isset($attributes["address_street"])){ $mod["streetAddress"][0]=$attributes["address_street"]; } + if (isset($attributes["company"])){ $mod["company"][0]=$attributes["company"]; } + if (isset($attributes["change_password"])){ $mod["pwdLastSet"][0]=0; } + if (isset($attributes["department"])){ $mod["department"][0]=$attributes["department"]; } + if (isset($attributes["description"])){ $mod["description"][0]=$attributes["description"]; } + if (isset($attributes["display_name"])){ $mod["displayName"][0]=$attributes["display_name"]; } + if (isset($attributes["email"])){ $mod["mail"][0]=$attributes["email"]; } + if (isset($attributes["expires"])){ $mod["accountExpires"][0]=$attributes["expires"]; } //unix epoch format? + if (isset($attributes["firstname"])){ $mod["givenName"][0]=$attributes["firstname"]; } + if (isset($attributes["home_directory"])){ $mod["homeDirectory"][0]=$attributes["home_directory"]; } + if (isset($attributes["home_drive"])){ $mod["homeDrive"][0]=$attributes["home_drive"]; } + if (isset($attributes["initials"])){ $mod["initials"][0]=$attributes["initials"]; } + if (isset($attributes["logon_name"])){ $mod["userPrincipalName"][0]=$attributes["logon_name"]; } + if (isset($attributes["manager"])){ $mod["manager"][0]=$attributes["manager"]; } //UNTESTED ***Use DistinguishedName*** + if (isset($attributes["office"])){ $mod["physicalDeliveryOfficeName"][0]=$attributes["office"]; } + if (isset($attributes["password"])){ $mod["unicodePwd"][0]=$this->user()->encodePassword($attributes["password"]); } + if (isset($attributes["profile_path"])){ $mod["profilepath"][0]=$attributes["profile_path"]; } + if (isset($attributes["script_path"])){ $mod["scriptPath"][0]=$attributes["script_path"]; } + if (isset($attributes["surname"])){ $mod["sn"][0]=$attributes["surname"]; } + if (isset($attributes["title"])){ $mod["title"][0]=$attributes["title"]; } + if (isset($attributes["telephone"])){ $mod["telephoneNumber"][0]=$attributes["telephone"]; } + if (isset($attributes["mobile"])){ $mod["mobile"][0]=$attributes["mobile"]; } + if (isset($attributes["pager"])){ $mod["pager"][0]=$attributes["pager"]; } + if (isset($attributes["ipphone"])){ $mod["ipphone"][0]=$attributes["ipphone"]; } + if (isset($attributes["web_page"])){ $mod["wWWHomePage"][0]=$attributes["web_page"]; } + if (isset($attributes["fax"])){ $mod["facsimileTelephoneNumber"][0]=$attributes["fax"]; } + if (isset($attributes["enabled"])){ $mod["userAccountControl"][0]=$attributes["enabled"]; } + if (isset($attributes["homephone"])){ $mod["homephone"][0]=$attributes["homephone"]; } + + // Distribution List specific schema + if (isset($attributes["group_sendpermission"])){ $mod["dlMemSubmitPerms"][0]=$attributes["group_sendpermission"]; } + if (isset($attributes["group_rejectpermission"])){ $mod["dlMemRejectPerms"][0]=$attributes["group_rejectpermission"]; } + + // Exchange Schema + if (isset($attributes["exchange_homemdb"])){ $mod["homeMDB"][0]=$attributes["exchange_homemdb"]; } + if (isset($attributes["exchange_mailnickname"])){ $mod["mailNickname"][0]=$attributes["exchange_mailnickname"]; } + if (isset($attributes["exchange_proxyaddress"])){ $mod["proxyAddresses"][0]=$attributes["exchange_proxyaddress"]; } + if (isset($attributes["exchange_usedefaults"])){ $mod["mDBUseDefaults"][0]=$attributes["exchange_usedefaults"]; } + if (isset($attributes["exchange_policyexclude"])){ $mod["msExchPoliciesExcluded"][0]=$attributes["exchange_policyexclude"]; } + if (isset($attributes["exchange_policyinclude"])){ $mod["msExchPoliciesIncluded"][0]=$attributes["exchange_policyinclude"]; } + if (isset($attributes["exchange_addressbook"])){ $mod["showInAddressBook"][0]=$attributes["exchange_addressbook"]; } + if (isset($attributes["exchange_altrecipient"])){ $mod["altRecipient"][0]=$attributes["exchange_altrecipient"]; } + if (isset($attributes["exchange_deliverandredirect"])){ $mod["deliverAndRedirect"][0]=$attributes["exchange_deliverandredirect"]; } + + // This schema is designed for contacts + if (isset($attributes["exchange_hidefromlists"])){ $mod["msExchHideFromAddressLists"][0]=$attributes["exchange_hidefromlists"]; } + if (isset($attributes["contact_email"])){ $mod["targetAddress"][0]=$attributes["contact_email"]; } + + //echo ("
    "); print_r($mod);
    +        /*
    +        // modifying a name is a bit fiddly
    +        if ($attributes["firstname"] && $attributes["surname"]){
    +            $mod["cn"][0]=$attributes["firstname"]." ".$attributes["surname"];
    +            $mod["displayname"][0]=$attributes["firstname"]." ".$attributes["surname"];
    +            $mod["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
    +        }
    +        */
    +
    +        if (count($mod)==0) { return (false); }
    +        return ($mod);
    +    }
    +
    +    /**
    +    * Convert 8bit characters e.g. accented characters to UTF8 encoded characters
    +    */
    +    protected function encode8Bit(&$item, $key) {
    +        $encode = false;
    +        if (is_string($item)) {
    +            for ($i=0; $i> 7) {
    +                    $encode = true;
    +                }
    +            }
    +        }
    +        if ($encode === true && $key != 'password') {
    +            $item = utf8_encode($item);
    +        }
    +    }
    +
    +    /**
    +    * Select a random domain controller from your domain controller array
    +    *
    +    * @return string
    +    */
    +    protected function randomController() {
    +        mt_srand(doubleval(microtime()) * 100000000); // For older PHP versions
    +        /*if (sizeof($this->domainControllers) > 1) {
    +            $adController = $this->domainControllers[array_rand($this->domainControllers)];
    +            // Test if the controller is responding to pings
    +            $ping = $this->pingController($adController);
    +            if ($ping === false) {
    +                // Find the current key in the domain controllers array
    +                $key = array_search($adController, $this->domainControllers);
    +                // Remove it so that we don't end up in a recursive loop
    +                unset($this->domainControllers[$key]);
    +                // Select a new controller
    +                return $this->randomController();
    +            }
    +            else {
    +                return ($adController);
    +            }
    +        } */
    +        return $this->domainControllers[array_rand($this->domainControllers)];
    +    }
    +
    +    /**
    +    * Test basic connectivity to controller
    +    *
    +    * @return bool
    +    */
    +    public function pingController($host) {
    +        $port = $this->adPort;
    +        fsockopen($host, $port, $errno, $errstr, 10);
    +        if ($errno > 0) {
    +            return false;
    +        }
    +        return true;
    +    }
    +
    +}
    +
    +/**
    +* adLDAP Exception Handler
    +*
    +* Exceptions of this type are thrown on bind failure or when SSL is required but not configured
    +* Example:
    +* try {
    +*   $adldap = new adLDAP();
    +* }
    +* catch (adLDAPException $e) {
    +*   echo $e;
    +*   exit();
    +* }
    +*/
    +class adLDAPException extends \Exception {}
    +
    +?>
    diff --git a/functions/adLDAP/src/classes/adLDAPComputers.php b/functions/adLDAP/src/classes/adLDAPComputers.php
    new file mode 100755
    index 000000000..d5339857f
    --- /dev/null
    +++ b/functions/adLDAP/src/classes/adLDAPComputers.php
    @@ -0,0 +1,147 @@
    +adldap = $adldap;
    +    }
    +    
    +    /**
    +    * Get information about a specific computer. Returned in a raw array format from AD
    +    * 
    +    * @param string $computerName The name of the computer
    +    * @param array $fields Attributes to return
    +    * @return array
    +    */
    +    public function info($computerName, $fields = NULL) {
    +        if ($computerName === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +
    +        $filter = "(&(objectClass=computer)(cn=" . $computerName . "))";
    +        if ($fields === NULL) { 
    +            $fields = array("memberof","cn","displayname","dnshostname","distinguishedname","objectcategory","operatingsystem","operatingsystemservicepack","operatingsystemversion"); 
    +        }
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +        
    +        return $entries;
    +    }
    +    
    +    /**
    +    * Find information about the computers. Returned in a raw array format from AD
    +    * 
    +    * @param string $computerName The name of the computer
    +    * @param array $fields Array of parameters to query
    +    * @return mixed
    +    */
    +    public function infoCollection($computerName, $fields = NULL) {
    +        if ($computerName === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        $info = $this->info($computerName, $fields);
    +        
    +        if ($info !== false) {
    +            $collection = new \adLDAP\collections\adLDAPComputerCollection($info, $this->adldap);
    +            return $collection;
    +        }
    +        return false;
    +    }
    +    
    +    /**
    +    * Check if a computer is in a group
    +    * 
    +    * @param string $computerName The name of the computer
    +    * @param string $group The group to check
    +    * @param bool $recursive Whether to check recursively
    +    * @return array
    +    */
    +    public function inGroup($computerName, $group, $recursive = NULL) {
    +        if ($computerName === NULL) { return false; }
    +        if ($group === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // use the default option if they haven't set it
    +
    +        //get a list of the groups
    +        $groups = $this->groups($computerName, array("memberof"), $recursive);
    +
    +        //return true if the specified group is in the group list
    +        if (in_array($group, $groups)) { 
    +            return true; 
    +        }
    +        return false;
    +    }
    +    
    +    /**
    +    * Get the groups a computer is in
    +    * 
    +    * @param string $computerName The name of the computer
    +    * @param bool $recursive Whether to check recursively
    +    * @return array
    +    */
    +    public function groups($computerName, $recursive = NULL) {
    +        if ($computerName === NULL) { return false; }
    +        if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } //use the default option if they haven't set it
    +        if (!$this->adldap->getLdapBind()){ return false; }
    +
    +        //search the directory for their information
    +        $info = @$this->info($computerName, array("memberof", "primarygroupid"));
    +        $groups = $this->adldap->utilities()->niceNames($info[0]["memberof"]); //presuming the entry returned is our guy (unique usernames)
    +
    +        if ($recursive === true) {
    +            foreach ($groups as $id => $groupName){
    +              $extraGroups = $this->adldap->group()->recursiveGroups($groupName);
    +              $groups = array_merge($groups, $extraGroups);
    +            }
    +        }
    +        return $groups;
    +    }
    +}
    +?>
    diff --git a/functions/adLDAP/src/classes/adLDAPContacts.php b/functions/adLDAP/src/classes/adLDAPContacts.php
    new file mode 100755
    index 000000000..11b2b4341
    --- /dev/null
    +++ b/functions/adLDAP/src/classes/adLDAPContacts.php
    @@ -0,0 +1,283 @@
    +adldap = $adldap;
    +    }
    +    
    +    //*****************************************************************************************************************
    +    // CONTACT FUNCTIONS
    +    // * Still work to do in this area, and new functions to write
    +    
    +    /**
    +    * Create a contact
    +    * 
    +    * @param array $attributes The attributes to set to the contact
    +    * @return bool
    +    */
    +    public function create($attributes) {
    +        // Check for compulsory fields
    +        if (!array_key_exists("display_name", $attributes)) { return "Missing compulsory field [display_name]"; }
    +        if (!array_key_exists("email", $attributes)) { return "Missing compulsory field [email]"; }
    +        if (!array_key_exists("container", $attributes)) { return "Missing compulsory field [container]"; }
    +        if (!is_array($attributes["container"])) { return "Container attribute must be an array."; }
    +
    +        // Translate the schema
    +        $add = $this->adldap->adldap_schema($attributes);
    +        
    +        // Additional stuff only used for adding contacts
    +        $add["cn"][0] = $attributes["display_name"];
    +        $add["objectclass"][0] = "top";
    +        $add["objectclass"][1] = "person";
    +        $add["objectclass"][2] = "organizationalPerson";
    +        $add["objectclass"][3] = "contact"; 
    +        if (!isset($attributes['exchange_hidefromlists'])) {
    +            $add["msExchHideFromAddressLists"][0] = "TRUE";
    +        }
    +
    +        // Determine the container
    +        $attributes["container"] = array_reverse($attributes["container"]);
    +        $container= "OU=" . implode(",OU=", $attributes["container"]);
    +
    +        // Add the entry
    +        $result = @ldap_add($this->adldap->getLdapConnection(), "CN=" . $this->adldap->utilities()->escapeCharacters($add["cn"][0]) . ", " . $container . "," . $this->adldap->getBaseDn(), $add);
    +        if ($result != true) { 
    +            return false; 
    +        }
    +        return true;
    +    }  
    +    
    +    /**
    +    * Determine the list of groups a contact is a member of
    +    * 
    +    * @param string $distinguisedname The full DN of a contact
    +    * @param bool $recursive Recursively check groups
    +    * @return array
    +    */
    +    public function groups($distinguishedName, $recursive = NULL) {
    +        if ($distinguishedName === NULL) { return false; }
    +        if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } //use the default option if they haven't set it
    +        if (!$this->adldap->getLdapBind()){ return false; }
    +        
    +        // Search the directory for their information
    +        $info = @$this->info($distinguishedName, array("memberof", "primarygroupid"));
    +        $groups = $this->adldap->utilities()->niceNames($info[0]["memberof"]); //presuming the entry returned is our contact
    +
    +        if ($recursive === true) {
    +            foreach ($groups as $id => $groupName) {
    +                $extraGroups = $this->adldap->group()->recursiveGroups($groupName);
    +                $groups = array_merge($groups, $extraGroups);
    +            }
    +        }
    +        return $groups;
    +    }
    +    
    +    /**
    +    * Get contact information. Returned in a raw array format from AD
    +    * 
    +    * @param string $distinguisedname The full DN of a contact
    +    * @param array $fields Attributes to be returned
    +    * @return array
    +    */
    +    public function info($distinguishedName, $fields = NULL)
    +    {
    +        if ($distinguishedName === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +
    +        $filter = "distinguishedName=" . $this->adldap->utilities()->ldapSlashes($distinguishedName);
    +        if ($fields === NULL) { 
    +            $fields = array("distinguishedname", "mail", "memberof", "department", "displayname", "telephonenumber", "primarygroupid", "objectsid"); 
    +        }
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +        
    +        if ($entries[0]['count'] >= 1) {
    +            // AD does not return the primary group in the ldap query, we may need to fudge it
    +            if ($this->adldap->getRealPrimaryGroup() && isset($entries[0]["primarygroupid"][0]) && isset($entries[0]["primarygroupid"][0])) {
    +                //$entries[0]["memberof"][]=$this->group_cn($entries[0]["primarygroupid"][0]);
    +                $entries[0]["memberof"][] = $this->adldap->group()->getPrimaryGroup($entries[0]["primarygroupid"][0], $entries[0]["objectsid"][0]);
    +            } else {
    +                $entries[0]["memberof"][] = "CN=Domain Users,CN=Users," . $this->adldap->getBaseDn();
    +            }
    +        }
    +        
    +        $entries[0]["memberof"]["count"]++;
    +        return $entries;
    +    }
    +    
    +    /**
    +    * Find information about the contacts. Returned in a raw array format from AD
    +    * 
    +    * @param string $distinguishedName The full DN of a contact 
    +    * @param array $fields Array of parameters to query
    +    * @return mixed
    +    */
    +    public function infoCollection($distinguishedName, $fields = NULL) {
    +        if ($distinguishedName === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        $info = $this->info($distinguishedName, $fields);
    +        
    +        if ($info !== false) {
    +            $collection = new \adLDAP\collections\adLDAPContactCollection($info, $this->adldap);
    +            return $collection;
    +        }
    +        return false;
    +    }
    +    
    +    /**
    +    * Determine if a contact is a member of a group
    +    * 
    +    * @param string $distinguisedName The full DN of a contact
    +    * @param string $group The group name to query
    +    * @param bool $recursive Recursively check groups
    +    * @return bool
    +    */
    +    public function inGroup($distinguisedName, $group, $recursive = NULL) {
    +        if ($distinguisedName === NULL) { return false; }
    +        if ($group === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } //use the default option if they haven't set it
    +        
    +        // Get a list of the groups
    +        $groups = $this->groups($distinguisedName, array("memberof"), $recursive);
    +        
    +        // Return true if the specified group is in the group list
    +        if (in_array($group, $groups)) { 
    +            return true; 
    +        }
    +        return false;
    +    }          
    +
    +    /**
    +    * Modify a contact
    +    * 
    +    * @param string $distinguishedName The contact to query
    +    * @param array $attributes The attributes to modify.  Note if you set the enabled attribute you must not specify any other attributes
    +    * @return bool
    +    */
    +    public function modify($distinguishedName, $attributes) {
    +        if ($distinguishedName === NULL) { return "Missing compulsory field [distinguishedname]"; }
    +        
    +        // Translate the update to the LDAP schema                
    +        $mod = $this->adldap->adldap_schema($attributes);
    +        
    +        // Check to see if this is an enabled status update
    +        if (!$mod) { 
    +            return false; 
    +        }
    +        
    +        // Do the update
    +        $result = ldap_modify($this->adldap->getLdapConnection(), $distinguishedName, $mod);
    +        if ($result == false) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Delete a contact
    +    * 
    +    * @param string $distinguishedName The contact dn to delete (please be careful here!)
    +    * @return array
    +    */
    +    public function delete($distinguishedName) {
    +        $result = $this->folder()->delete($distinguishedName);
    +        if ($result != true) { 
    +            return false; 
    +        }       
    +        return true;
    +    }
    +    
    +    /**
    +    * Return a list of all contacts
    +    * 
    +    * @param bool $includeDescription Include a description of a contact
    +    * @param string $search The search parameters
    +    * @param bool $sorted Whether to sort the results
    +    * @return array
    +    */
    +    public function all($includeDescription = false, $search = "*", $sorted = true) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        // Perform the search and grab all their details
    +        $filter = "(&(objectClass=contact)(cn=" . $search . "))";
    +        $fields = array("displayname","distinguishedname");           
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +
    +        $usersArray = array();
    +        for ($i=0; $i<$entries["count"]; $i++) {
    +            if ($includeDescription && strlen($entries[$i]["displayname"][0])>0){
    +                $usersArray[$entries[$i]["distinguishedname"][0]] = $entries[$i]["displayname"][0];
    +            } elseif ($includeDescription){
    +                $usersArray[$entries[$i]["distinguishedname"][0]] = $entries[$i]["distinguishedname"][0];
    +            } else {
    +                array_push($usersArray, $entries[$i]["distinguishedname"][0]);
    +            }
    +        }
    +        if ($sorted) { 
    +            asort($usersArray); 
    +        }
    +        return $usersArray;
    +    }
    +    
    +    /**
    +    * Mail enable a contact
    +    * Allows email to be sent to them through Exchange
    +    * 
    +    * @param string $distinguishedname The contact to mail enable
    +    * @param string $emailaddress The email address to allow emails to be sent through
    +    * @param string $mailnickname The mailnickname for the contact in Exchange.  If NULL this will be set to the display name
    +    * @return bool
    +    */
    +    public function contactMailEnable($distinguishedName, $emailAddress, $mailNickname = NULL) {
    +        return $this->adldap->exchange()->contactMailEnable($distinguishedName, $emailAddress, $mailNickname);
    +    }
    +}
    +?>
    diff --git a/functions/adLDAP/src/classes/adLDAPExchange.php b/functions/adLDAP/src/classes/adLDAPExchange.php
    new file mode 100755
    index 000000000..e0763f7d0
    --- /dev/null
    +++ b/functions/adLDAP/src/classes/adLDAPExchange.php
    @@ -0,0 +1,376 @@
    +adldap = $adldap;
    +    }
    +    
    +    /**
    +    * Create an Exchange account
    +    * 
    +    * @param string $username The username of the user to add the Exchange account to
    +    * @param array $storageGroup The mailbox, Exchange Storage Group, for the user account, this must be a full CN
    +    *                            If the storage group has a different base_dn to the adLDAP configuration, set it using $base_dn
    +    * @param string $emailAddress The primary email address to add to this user
    +    * @param string $mailNickname The mail nick name.  If mail nickname is blank, the username will be used
    +    * @param bool $mdbUseDefaults Indicates whether the store should use the default quota, rather than the per-mailbox quota.
    +    * @param string $baseDn Specify an alternative base_dn for the Exchange storage group
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function createMailbox($username, $storageGroup, $emailAddress, $mailNickname=NULL, $useDefaults=TRUE, $baseDn=NULL, $isGUID=false) {
    +        if ($username === NULL){  return "Missing compulsory field [username]"; }     
    +        if ($storageGroup === NULL) { return "Missing compulsory array [storagegroup]"; }
    +        if (!is_array($storageGroup)) { return "[storagegroup] must be an array"; }
    +        if ($emailAddress === NULL) { return "Missing compulsory field [emailAddress]"; }
    +        
    +        if ($baseDn === NULL) {
    +            $baseDn = $this->adldap->getBaseDn();   
    +        }
    +        
    +        $container = "CN=" . implode(",CN=", $storageGroup);
    +        
    +        if ($mailNickname === NULL) { 
    +            $mailNickname = $username; 
    +        }
    +        $mdbUseDefaults = $this->adldap->utilities()->boolToStr($useDefaults);
    +        
    +        $attributes = array(
    +            'exchange_homemdb'=>$container.",".$baseDn,
    +            'exchange_proxyaddress'=>'SMTP:' . $emailAddress,
    +            'exchange_mailnickname'=>$mailNickname,
    +            'exchange_usedefaults'=>$mdbUseDefaults
    +        );
    +        $result = $this->adldap->user()->modify($username, $attributes, $isGUID);
    +        if ($result == false) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Add an X400 address to Exchange
    +    * See http://tools.ietf.org/html/rfc1685 for more information.
    +    * An X400 Address looks similar to this X400:c=US;a= ;p=Domain;o=Organization;s=Doe;g=John;
    +    * 
    +    * @param string $username The username of the user to add the X400 to to
    +    * @param string $country Country
    +    * @param string $admd Administration Management Domain
    +    * @param string $pdmd Private Management Domain (often your AD domain)
    +    * @param string $org Organization
    +    * @param string $surname Surname
    +    * @param string $givenName Given name
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function addX400($username, $country, $admd, $pdmd, $org, $surname, $givenName, $isGUID=false) {
    +        if ($username === NULL) { return "Missing compulsory field [username]"; }     
    +        
    +        $proxyValue = 'X400:';
    +            
    +        // Find the dn of the user
    +        $user = $this->adldap->user()->info($username, array("cn","proxyaddresses"), $isGUID);
    +        if ($user[0]["dn"] === NULL) { return false; }
    +        $userDn = $user[0]["dn"];
    +        
    +        // We do not have to demote an email address from the default so we can just add the new proxy address
    +        $attributes['exchange_proxyaddress'] = $proxyValue . 'c=' . $country . ';a=' . $admd . ';p=' . $pdmd . ';o=' . $org . ';s=' . $surname . ';g=' . $givenName . ';';
    +       
    +        // Translate the update to the LDAP schema                
    +        $add = $this->adldap->adldap_schema($attributes);
    +        
    +        if (!$add) { return false; }
    +        
    +        // Do the update
    +        // Take out the @ to see any errors, usually this error might occur because the address already
    +        // exists in the list of proxyAddresses
    +        $result = @ldap_mod_add($this->adldap->getLdapConnection(), $userDn, $add);
    +        if ($result == false) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Add an address to Exchange
    +    * 
    +    * @param string $username The username of the user to add the Exchange account to
    +    * @param string $emailAddress The email address to add to this user
    +    * @param bool $default Make this email address the default address, this is a bit more intensive as we have to demote any existing default addresses
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function addAddress($username, $emailAddress, $default = FALSE, $isGUID = false) {
    +        if ($username === NULL) { return "Missing compulsory field [username]"; }     
    +        if ($emailAddress === NULL) { return "Missing compulsory fields [emailAddress]"; }
    +        
    +        $proxyValue = 'smtp:';
    +        if ($default === true) {
    +            $proxyValue = 'SMTP:';
    +        }
    +              
    +        // Find the dn of the user
    +        $user = $this->adldap->user()->info($username, array("cn","proxyaddresses"), $isGUID);
    +        if ($user[0]["dn"] === NULL){ return false; }
    +        $userDn = $user[0]["dn"];
    +        
    +        // We need to scan existing proxy addresses and demote the default one
    +        if (is_array($user[0]["proxyaddresses"]) && $default === true) {
    +            $modAddresses = array();
    +            for ($i=0;$i<$user[0]['proxyaddresses']['count'];$i++) {
    +                if (strpos($user[0]['proxyaddresses'][$i], 'SMTP:') !== false) {
    +                    $user[0]['proxyaddresses'][$i] = str_replace('SMTP:', 'smtp:', $user[0]['proxyaddresses'][$i]);
    +                }
    +                if ($user[0]['proxyaddresses'][$i] != '') {
    +                    $modAddresses['proxyAddresses'][$i] = $user[0]['proxyaddresses'][$i];
    +                }
    +            }
    +            $modAddresses['proxyAddresses'][($user[0]['proxyaddresses']['count']-1)] = 'SMTP:' . $emailAddress;
    +            
    +            $result = @ldap_mod_replace($this->adldap->getLdapConnection(), $userDn, $modAddresses);
    +            if ($result == false) { 
    +                return false; 
    +            }
    +            return true;
    +        }
    +        else {
    +            // We do not have to demote an email address from the default so we can just add the new proxy address
    +            $attributes['exchange_proxyaddress'] = $proxyValue . $emailAddress;
    +            
    +            // Translate the update to the LDAP schema                
    +            $add = $this->adldap->adldap_schema($attributes);
    +            
    +            if (!$add) { 
    +                return false; 
    +            }
    +            
    +            // Do the update
    +            // Take out the @ to see any errors, usually this error might occur because the address already
    +            // exists in the list of proxyAddresses
    +            $result = @ldap_mod_add($this->adldap->getLdapConnection(), $userDn,$add);
    +            if ($result == false) { 
    +                return false; 
    +            }
    +            return true;
    +        }
    +    }
    +    
    +    /**
    +    * Remove an address to Exchange
    +    * If you remove a default address the account will no longer have a default, 
    +    * we recommend changing the default address first
    +    * 
    +    * @param string $username The username of the user to add the Exchange account to
    +    * @param string $emailAddress The email address to add to this user
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function deleteAddress($username, $emailAddress, $isGUID=false) {
    +        if ($username === NULL) { return "Missing compulsory field [username]"; }     
    +        if ($emailAddress === NULL) { return "Missing compulsory fields [emailAddress]"; }
    +        
    +        // Find the dn of the user
    +        $user = $this->adldap->user()->info($username, array("cn","proxyaddresses"), $isGUID);
    +        if ($user[0]["dn"] === NULL) { return false; }
    +        $userDn = $user[0]["dn"];
    +        
    +        if (is_array($user[0]["proxyaddresses"])) {
    +            $mod = array();
    +            for ($i=0;$i<$user[0]['proxyaddresses']['count'];$i++) {
    +                if (strpos($user[0]['proxyaddresses'][$i], 'SMTP:') !== false && $user[0]['proxyaddresses'][$i] == 'SMTP:' . $emailAddress) {
    +                    $mod['proxyAddresses'][0] = 'SMTP:' . $emailAddress;
    +                }
    +                elseif (strpos($user[0]['proxyaddresses'][$i], 'smtp:') !== false && $user[0]['proxyaddresses'][$i] == 'smtp:' . $emailAddress) {
    +                    $mod['proxyAddresses'][0] = 'smtp:' . $emailAddress;
    +                }
    +            }
    +            
    +            $result = @ldap_mod_del($this->adldap->getLdapConnection(), $userDn,$mod);
    +            if ($result == false) { 
    +                return false; 
    +            }     
    +            return true;
    +        }
    +        else {
    +            return false;
    +        }
    +    }
    +    /**
    +    * Change the default address
    +    * 
    +    * @param string $username The username of the user to add the Exchange account to
    +    * @param string $emailAddress The email address to make default
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function primaryAddress($username, $emailAddress, $isGUID = false) {
    +        if ($username === NULL) { return "Missing compulsory field [username]"; }     
    +        if ($emailAddress === NULL) { return "Missing compulsory fields [emailAddress]"; }
    +        
    +        // Find the dn of the user
    +        $user = $this->adldap->user()->info($username, array("cn","proxyaddresses"), $isGUID);
    +        if ($user[0]["dn"] === NULL) { return false; }
    +        $userDn = $user[0]["dn"];
    +        
    +        if (is_array($user[0]["proxyaddresses"])) {
    +            $modAddresses = array();
    +            for ($i=0;$i<$user[0]['proxyaddresses']['count'];$i++) {
    +                if (strpos($user[0]['proxyaddresses'][$i], 'SMTP:') !== false) {
    +                    $user[0]['proxyaddresses'][$i] = str_replace('SMTP:', 'smtp:', $user[0]['proxyaddresses'][$i]);
    +                }
    +                if ($user[0]['proxyaddresses'][$i] == 'smtp:' . $emailAddress) {
    +                    $user[0]['proxyaddresses'][$i] = str_replace('smtp:', 'SMTP:', $user[0]['proxyaddresses'][$i]);
    +                }
    +                if ($user[0]['proxyaddresses'][$i] != '') {
    +                    $modAddresses['proxyAddresses'][$i] = $user[0]['proxyaddresses'][$i];
    +                }
    +            }
    +            
    +            $result = @ldap_mod_replace($this->adldap->getLdapConnection(), $userDn, $modAddresses);
    +            if ($result == false) { 
    +                return false; 
    +            }
    +            return true;
    +        }
    +    }
    +    
    +    /**
    +    * Mail enable a contact
    +    * Allows email to be sent to them through Exchange
    +    * 
    +    * @param string $distinguishedName The contact to mail enable
    +    * @param string $emailAddress The email address to allow emails to be sent through
    +    * @param string $mailNickname The mailnickname for the contact in Exchange.  If NULL this will be set to the display name
    +    * @return bool
    +    */
    +    public function contactMailEnable($distinguishedName, $emailAddress, $mailNickname = NULL) {
    +        if ($distinguishedName === NULL) { return "Missing compulsory field [distinguishedName]"; }   
    +        if ($emailAddress === NULL) { return "Missing compulsory field [emailAddress]"; }  
    +        
    +        if ($mailNickname !== NULL) {
    +            // Find the dn of the user
    +            $user = $this->adldap->contact()->info($distinguishedName, array("cn","displayname"));
    +            if ($user[0]["displayname"] === NULL) { return false; }
    +            $mailNickname = $user[0]['displayname'][0];
    +        }
    +        
    +        $attributes = array("email"=>$emailAddress,"contact_email"=>"SMTP:" . $emailAddress,"exchange_proxyaddress"=>"SMTP:" . $emailAddress,"exchange_mailnickname" => $mailNickname);
    +         
    +        // Translate the update to the LDAP schema                
    +        $mod = $this->adldap->adldap_schema($attributes);
    +        
    +        // Check to see if this is an enabled status update
    +        if (!$mod) { return false; }
    +        
    +        // Do the update
    +        $result = ldap_modify($this->adldap->getLdapConnection(), $distinguishedName, $mod);
    +        if ($result == false) { return false; }
    + 
    +        return true;
    +    }
    +    
    +    /**
    +    * Returns a list of Exchange Servers in the ConfigurationNamingContext of the domain
    +    * 
    +    * @param array $attributes An array of the AD attributes you wish to return
    +    * @return array
    +    */
    +    public function servers($attributes = array('cn','distinguishedname','serialnumber')) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        $configurationNamingContext = $this->adldap->getRootDse(array('configurationnamingcontext'));
    +        $sr = @ldap_search($this->adldap->getLdapConnection(), $configurationNamingContext[0]['configurationnamingcontext'][0],'(&(objectCategory=msExchExchangeServer))', $attributes);
    +        $entries = @ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +        return $entries;
    +    }
    +    
    +    /**
    +    * Returns a list of Storage Groups in Exchange for a given mail server
    +    * 
    +    * @param string $exchangeServer The full DN of an Exchange server.  You can use exchange_servers() to find the DN for your server
    +    * @param array $attributes An array of the AD attributes you wish to return
    +    * @param bool $recursive If enabled this will automatically query the databases within a storage group
    +    * @return array
    +    */
    +    public function storageGroups($exchangeServer, $attributes = array('cn','distinguishedname'), $recursive = NULL) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if ($exchangeServer === NULL) { return "Missing compulsory field [exchangeServer]"; }
    +        if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); }
    +
    +        $filter = '(&(objectCategory=msExchStorageGroup))';
    +        $sr = @ldap_search($this->adldap->getLdapConnection(), $exchangeServer, $filter, $attributes);
    +        $entries = @ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +
    +        if ($recursive === true) {
    +            for ($i=0; $i<$entries['count']; $i++) {
    +                $entries[$i]['msexchprivatemdb'] = $this->storageDatabases($entries[$i]['distinguishedname'][0]);       
    +            }
    +        }
    +        return $entries;
    +    }
    +    
    +    /**
    +    * Returns a list of Databases within any given storage group in Exchange for a given mail server
    +    * 
    +    * @param string $storageGroup The full DN of an Storage Group.  You can use exchange_storage_groups() to find the DN 
    +    * @param array $attributes An array of the AD attributes you wish to return
    +    * @return array
    +    */
    +    public function storageDatabases($storageGroup, $attributes = array('cn','distinguishedname','displayname')) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if ($storageGroup === NULL) { return "Missing compulsory field [storageGroup]"; }
    +        
    +        $filter = '(&(objectCategory=msExchPrivateMDB))';
    +        $sr = @ldap_search($this->adldap->getLdapConnection(), $storageGroup, $filter, $attributes);
    +        $entries = @ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +        return $entries;
    +    }
    +}
    +?>
    diff --git a/functions/adLDAP/src/classes/adLDAPFolders.php b/functions/adLDAP/src/classes/adLDAPFolders.php
    new file mode 100755
    index 000000000..5a78e16f2
    --- /dev/null
    +++ b/functions/adLDAP/src/classes/adLDAPFolders.php
    @@ -0,0 +1,175 @@
    +adldap = $adldap;
    +    }
    +    
    +    /**
    +    * Delete a distinguished name from Active Directory
    +    * You should never need to call this yourself, just use the wrapper functions user_delete and contact_delete
    +    *
    +    * @param string $dn The distinguished name to delete
    +    * @return bool
    +    */
    +    public function delete($dn) { 
    +        $result = ldap_delete($this->adldap->getLdapConnection(), $dn);
    +        if ($result != true) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Returns a folder listing for a specific OU
    +    * See http://adldap.sourceforge.net/wiki/doku.php?id=api_folder_functions
    +    * 
    +    * @param array $folderName An array to the OU you wish to list. 
    +    *                           If set to NULL will list the root, strongly recommended to set 
    +    *                           $recursive to false in that instance!
    +    * @param string $dnType The type of record to list.  This can be ADLDAP_FOLDER or ADLDAP_CONTAINER.
    +    * @param bool $recursive Recursively search sub folders
    +    * @param bool $type Specify a type of object to search for
    +    * @return array
    +    */
    +    public function listing($folderName = NULL, $dnType = adLDAP::ADLDAP_FOLDER, $recursive = NULL, $type = NULL) {
    +        if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } //use the default option if they haven't set it
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +
    +        $filter = '(&';
    +        if ($type !== NULL) {
    +            switch ($type) {
    +                case 'contact':
    +                    $filter .= '(objectClass=contact)';
    +                    break;
    +                case 'computer':
    +                    $filter .= '(objectClass=computer)';
    +                    break;
    +                case 'group':
    +                    $filter .= '(objectClass=group)';
    +                    break;
    +                case 'folder':
    +                    $filter .= '(objectClass=organizationalUnit)';
    +                    break;
    +                case 'container':
    +                    $filter .= '(objectClass=container)';
    +                    break;
    +                case 'domain':
    +                    $filter .= '(objectClass=builtinDomain)';
    +                    break;
    +                default:
    +                    $filter .= '(objectClass=user)';
    +                    break;   
    +            }
    +        }
    +        else {
    +            $filter .= '(objectClass=*)';   
    +        }
    +        // If the folder name is null then we will search the root level of AD
    +        // This requires us to not have an OU= part, just the base_dn
    +        $searchOu = $this->adldap->getBaseDn();
    +        if (is_array($folderName)) {
    +            $ou = $dnType . "=" . implode("," . $dnType . "=", $folderName);
    +            $filter .= '(!(distinguishedname=' . $ou . ',' . $this->adldap->getBaseDn() . ')))';
    +            $searchOu = $ou . ',' . $this->adldap->getBaseDn();
    +        }
    +        else {
    +            $filter .= '(!(distinguishedname=' . $this->adldap->getBaseDn() . ')))';
    +        }
    +
    +        if ($recursive === true) {
    +            $sr = ldap_search($this->adldap->getLdapConnection(), $searchOu, $filter, array('objectclass', 'distinguishedname', 'samaccountname'));
    +            $entries = @ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +            if (is_array($entries)) {
    +                return $entries;
    +            }
    +        }
    +        else {
    +            $sr = ldap_list($this->adldap->getLdapConnection(), $searchOu, $filter, array('objectclass', 'distinguishedname', 'samaccountname'));
    +            $entries = @ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +            if (is_array($entries)) {
    +                return $entries;
    +            }
    +        }
    +        return false;
    +    }
    +
    +    /**
    +    * Create an organizational unit
    +    * 
    +    * @param array $attributes Default attributes of the ou
    +    * @return bool
    +    */
    +    public function create($attributes) {
    +        if (!is_array($attributes)) { return "Attributes must be an array"; }
    +        if (!is_array($attributes["container"])) { return "Container attribute must be an array."; }
    +        if (!array_key_exists("ou_name",$attributes)) { return "Missing compulsory field [ou_name]"; }
    +        if (!array_key_exists("container",$attributes)) { return "Missing compulsory field [container]"; }
    +        
    +        $attributes["container"] = array_reverse($attributes["container"]);
    +
    +        $add=array();
    +        $add["objectClass"] = "organizationalUnit";
    +        $add["OU"] = $attributes['ou_name'];
    +        $containers = "";
    +        if (count($attributes['container']) > 0) {
    +            $containers = "OU=" . implode(",OU=", $attributes["container"]) . ",";
    +        }
    +
    +        $containers = "OU=" . implode(",OU=", $attributes["container"]);
    +        $result = ldap_add($this->adldap->getLdapConnection(), "OU=" . $add["OU"] . ", " . $containers . $this->adldap->getBaseDn(), $add);
    +        if ($result != true) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +}
    +
    +?>
    diff --git a/functions/adLDAP/src/classes/adLDAPGroups.php b/functions/adLDAP/src/classes/adLDAPGroups.php
    new file mode 100755
    index 000000000..5faa9c171
    --- /dev/null
    +++ b/functions/adLDAP/src/classes/adLDAPGroups.php
    @@ -0,0 +1,690 @@
    +adldap = $adldap;
    +    }
    +    
    +    /**
    +    * Add a group to a group
    +    * 
    +    * @param string $parent The parent group name
    +    * @param string $child The child group name
    +    * @return bool
    +    */
    +    public function addGroup($parent,$child) {
    +
    +        // Find the parent group's dn
    +        $parentGroup = $this->ginfo($parent, array("cn"));
    +        if ($parentGroup[0]["dn"] === NULL) {
    +            return false; 
    +        }
    +        $parentDn = $parentGroup[0]["dn"];
    +        
    +        // Find the child group's dn
    +        $childGroup = $this->info($child, array("cn"));
    +        if ($childGroup[0]["dn"] === NULL) { 
    +            return false; 
    +        } 
    +        $childDn = $childGroup[0]["dn"];
    +                
    +        $add = array();
    +        $add["member"] = $childDn;
    +        
    +        $result = @ldap_mod_add($this->adldap->getLdapConnection(), $parentDn, $add);
    +        if ($result == false) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Add a user to a group
    +    * 
    +    * @param string $group The group to add the user to
    +    * @param string $user The user to add to the group
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function addUser($group, $user, $isGUID = false) {
    +        // Adding a user is a bit fiddly, we need to get the full DN of the user
    +        // and add it using the full DN of the group
    +        
    +        // Find the user's dn
    +        $userDn = $this->adldap->user()->dn($user, $isGUID);
    +        if ($userDn === false) { 
    +            return false; 
    +        }
    +        
    +        // Find the group's dn
    +        $groupInfo = $this->info($group, array("cn"));
    +        if ($groupInfo[0]["dn"] === NULL) { 
    +            return false; 
    +        }
    +        $groupDn = $groupInfo[0]["dn"];
    +        
    +        $add = array();
    +        $add["member"] = $userDn;
    +        
    +        $result = @ldap_mod_add($this->adldap->getLdapConnection(), $groupDn, $add);
    +        if ($result == false) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Add a contact to a group
    +    * 
    +    * @param string $group The group to add the contact to
    +    * @param string $contactDn The DN of the contact to add
    +    * @return bool
    +    */
    +    public function addContact($group, $contactDn)
    +    {
    +        // To add a contact we take the contact's DN
    +        // and add it using the full DN of the group
    +        
    +        // Find the group's dn
    +        $groupInfo = $this->info($group, array("cn"));
    +        if ($groupInfo[0]["dn"] === NULL) { 
    +            return false; 
    +        }
    +        $groupDn = $groupInfo[0]["dn"];
    +        
    +        $add = array();
    +        $add["member"] = $contactDn;
    +        
    +        $result = @ldap_mod_add($this->adldap->getLdapConnection(), $groupDn, $add);
    +        if ($result == false) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +
    +    /**
    +    * Create a group
    +    * 
    +    * @param array $attributes Default attributes of the group
    +    * @return bool
    +    */
    +    public function create($attributes) {
    +        if (!is_array($attributes)) { return "Attributes must be an array"; }
    +        if (!array_key_exists("group_name", $attributes)) { return "Missing compulsory field [group_name]"; }
    +        if (!array_key_exists("container", $attributes)) { return "Missing compulsory field [container]"; }
    +        if (!array_key_exists("description", $attributes)) { return "Missing compulsory field [description]"; }
    +        if (!is_array($attributes["container"])) { return "Container attribute must be an array."; }
    +        $attributes["container"] = array_reverse($attributes["container"]);
    +
    +        //$member_array = array();
    +        //$member_array[0] = "cn=user1,cn=Users,dc=yourdomain,dc=com";
    +        //$member_array[1] = "cn=administrator,cn=Users,dc=yourdomain,dc=com";
    +        
    +        $add = array();
    +        $add["cn"] = $attributes["group_name"];
    +        $add["samaccountname"] = $attributes["group_name"];
    +        $add["objectClass"] = "Group";
    +        $add["description"] = $attributes["description"];
    +        //$add["member"] = $member_array; UNTESTED
    +
    +        $container = "OU=" . implode(",OU=", $attributes["container"]);
    +        $result = ldap_add($this->adldap->getLdapConnection(), "CN=" . $add["cn"] . ", " . $container . "," . $this->adldap->getBaseDn(), $add);
    +        if ($result != true) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Delete a group account 
    +    * 
    +    * @param string $group The group to delete (please be careful here!) 
    +    * 
    +    * @return array 
    +    */
    +    public function delete($group) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if ($group === null) { return "Missing compulsory field [group]"; }
    +        
    +        $groupInfo = $this->info($group, array("*"));
    +        $dn = $groupInfo[0]['distinguishedname'][0]; 
    +        $result = $this->adldap->folder()->delete($dn); 
    +        if ($result !== true) { 
    +            return false; 
    +        } 
    +	return true;   
    +    }
    +
    +    /**
    +     * Rename group with new group
    +     * @param $group
    +     * @param $newName
    +     * @param $container
    +     *
    +     * @return bool
    +     */
    +    public function rename($group, $newName, $container) {
    +        $info = $this->info($group);
    +        if ($info[0]["dn"] === NULL) {
    +            return false;
    +        }
    +        else{
    +            $groupDN = $info[0]["dn"];
    +        }
    +        $newRDN = 'CN='.$newName;
    +
    +        // Determine the container
    +        $container = array_reverse($container);
    +        $container = "OU=" . implode(", OU=",$container);
    +
    +        // Do the update
    +        $result = @ldap_rename($this->adldap->getLdapConnection(), $groupDN, $newRDN, $container.', '.$this->adldap->getBaseDn(), true);
    +        if ($result == false) {
    +            return false;
    +        }
    +        return true;
    +    }
    +
    +    /**
    +    * Remove a group from a group
    +    * 
    +    * @param string $parent The parent group name
    +    * @param string $child The child group name
    +    * @return bool
    +    */
    +    public function removeGroup($parent , $child) {
    +    
    +        // Find the parent dn
    +        $parentGroup = $this->info($parent, array("cn"));
    +        if ($parentGroup[0]["dn"] === NULL) { 
    +            return false; 
    +        }
    +        $parentDn = $parentGroup[0]["dn"];
    +        
    +        // Find the child dn
    +        $childGroup = $this->info($child, array("cn"));
    +        if ($childGroup[0]["dn"] === NULL) { 
    +            return false; 
    +        }
    +        $childDn = $childGroup[0]["dn"];
    +        
    +        $del = array();
    +        $del["member"] = $childDn;
    +        
    +        $result = @ldap_mod_del($this->adldap->getLdapConnection(), $parentDn, $del);
    +        if ($result == false) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Remove a user from a group
    +    * 
    +    * @param string $group The group to remove a user from
    +    * @param string $user The AD user to remove from the group
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function removeUser($group, $user, $isGUID = false) {
    +    
    +        // Find the parent dn
    +        $groupInfo = $this->info($group, array("cn"));
    +        if ($groupInfo[0]["dn"] === NULL) {  
    +            return false; 
    +        }
    +        $groupDn = $groupInfo[0]["dn"];
    +        
    +        // Find the users dn
    +        $userDn = $this->adldap->user()->dn($user, $isGUID);
    +        if ($userDn === false) {
    +            return false; 
    +        }
    +
    +        $del = array();
    +        $del["member"] = $userDn;
    +        
    +        $result = @ldap_mod_del($this->adldap->getLdapConnection(), $groupDn, $del);
    +        if ($result == false) {
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Remove a contact from a group
    +    * 
    +    * @param string $group The group to remove a user from
    +    * @param string $contactDn The DN of a contact to remove from the group
    +    * @return bool
    +    */
    +    public function removeContact($group, $contactDn) {
    +    
    +        // Find the parent dn
    +        $groupInfo = $this->info($group, array("cn"));
    +        if ($groupInfo[0]["dn"] === NULL) { 
    +            return false; 
    +        }
    +        $groupDn = $groupInfo[0]["dn"];
    +    
    +        $del = array();
    +        $del["member"] = $contactDn;
    +        
    +        $result = @ldap_mod_del($this->adldap->getLdapConnection(), $groupDn, $del);
    +        if ($result == false) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Return a list of groups in a group
    +    * 
    +    * @param string $group The group to query
    +    * @param bool $recursive Recursively get groups
    +    * @return array
    +    */
    +    public function inGroup($group, $recursive = NULL) {
    +        if (!$this->adldap->getLdapBind()){ return false; }
    +        if ($recursive === NULL){ $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it 
    +        
    +        // Search the directory for the members of a group
    +        $info = $this->info($group, array("member","cn"));
    +        $groups = $info[0]["member"];
    +        if (!is_array($groups)) {
    +            return false;   
    +        }
    + 
    +        $groupArray = array();
    +
    +        for ($i=0; $i<$groups["count"]; $i++) { 
    +             $filter = "(&(objectCategory=group)(distinguishedName=" . $this->adldap->utilities()->ldapSlashes($groups[$i]) . "))";
    +             $fields = array("samaccountname", "distinguishedname", "objectClass");
    +             $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +             $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +
    +             // not a person, look for a group  
    +             if ($entries['count'] == 0 && $recursive == true) {  
    +                $filter = "(&(objectCategory=group)(distinguishedName=" . $this->adldap->utilities()->ldapSlashes($groups[$i]) . "))";  
    +                $fields = array("distinguishedname");  
    +                $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);  
    +                $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);  
    +                if (!isset($entries[0]['distinguishedname'][0])) {
    +                    continue;  
    +                }
    +                $subGroups = $this->inGroup($entries[0]['distinguishedname'][0], $recursive);  
    +                if (is_array($subGroups)) {
    +                    $groupArray = array_merge($groupArray, $subGroups); 
    +                    $groupArray = array_unique($groupArray);  
    +                }
    +                continue;  
    +             } 
    +             $groupArray[] = $entries[0]['distinguishedname'][0];
    +        }
    +        return $groupArray;
    +    }
    +    
    +    /**
    +    * Return a list of members in a group
    +    * 
    +    * @param string $group The group to query
    +    * @param bool $recursive Recursively get group members
    +    * @return array
    +    */
    +    public function members($group, $recursive = NULL) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if ($recursive === NULL){ $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it 
    +        // Search the directory for the members of a group
    +        $info = $this->info($group, array("member","cn"));
    +        if (isset($info[0]["member"])) {
    +            $users = $info[0]["member"];
    +            if (!is_array($users)) {
    +                return false;   
    +            }
    +        } else {
    +            return false;
    +        }
    + 
    +        $userArray = array();
    +
    +        for ($i=0; $i<$users["count"]; $i++) { 
    +             $filter = "(&(objectCategory=person)(distinguishedName=" . $this->adldap->utilities()->ldapSlashes($users[$i]) . "))";
    +             $fields = array("samaccountname", "distinguishedname", "objectClass");
    +             $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +             $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +
    +             // not a person, look for a group  
    +             if ($entries['count'] == 0 && $recursive == true) {  
    +                $filter = "(&(objectCategory=group)(distinguishedName=" . $this->adldap->utilities()->ldapSlashes($users[$i]) . "))";  
    +                $fields = array("samaccountname");  
    +                $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);  
    +                $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);  
    +                if (!isset($entries[0]['samaccountname'][0])) {
    +                    continue;  
    +                }
    +                $subUsers = $this->members($entries[0]['samaccountname'][0], $recursive);  
    +                if (is_array($subUsers)) {
    +                    $userArray = array_merge($userArray, $subUsers); 
    +                    $userArray = array_unique($userArray);  
    +                }
    +                continue;  
    +             } 
    +             else if ($entries['count'] == 0) {   
    +                continue; 
    +             } 
    +
    +             if ((!isset($entries[0]['samaccountname'][0]) || $entries[0]['samaccountname'][0] === NULL) && $entries[0]['distinguishedname'][0] !== NULL) {
    +                 $userArray[] = $entries[0]['distinguishedname'][0];
    +             }
    +             else if ($entries[0]['samaccountname'][0] !== NULL) {
    +                $userArray[] = $entries[0]['samaccountname'][0];
    +             }
    +        }
    +        return $userArray;
    +    }
    +    
    +    /**
    +    * Group Information.  Returns an array of raw information about a group.
    +    * The group name is case sensitive
    +    * 
    +    * @param string $groupName The group name to retrieve info about
    +    * @param array $fields Fields to retrieve
    +    * @return array
    +    */
    +    public function info($groupName, $fields = NULL) {
    +        if ($groupName === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        if (stristr($groupName, '+')) {
    +            $groupName = stripslashes($groupName);   
    +        }
    +        
    +        $filter = "(&(objectCategory=group)(name=" . $this->adldap->utilities()->ldapSlashes($groupName) . "))";
    +        if ($fields === NULL) { 
    +            $fields = array("member","memberof","cn","description","distinguishedname","objectcategory","samaccountname"); 
    +        }
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +
    +        // Windows 2003: Returns up to 1500 values (Windows 2000 only 1000 is not supported).
    +        if (isset($entries[0]['member;range=0-1499']) && $entries[0]['member;range=0-1499']['count'] == 1500) {
    +            $entries[0]['member']['count'] = "0";
    +            $rangestep = 1499;     // Step site
    +            $rangelow  = 0;        // Initial low range
    +            $rangehigh = $rangelow + $rangestep;     // Initial high range
    +            // do until array_keys($members[0])[0] ends with a '*', e. g. member;range=1499-*. It indicates end of the range
    +            do {
    +                $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, array("member;range=" . $rangelow . "-" . $rangehigh));
    +                $members = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +                $memberrange = array_keys($members[0]);
    +                $membercount = $members[0][$memberrange[0]]['count'];
    +                // Copy range entries to member
    +                for ($i = 0; $i <= $membercount -1; $i++) {
    +                    $entries[0]['member'][] = $members[0][$memberrange[0]][$i];
    +                }
    +                $entries[0]['member']['count'] += $membercount;
    +                $rangelow  += $rangestep +1;
    +                $rangehigh += $rangestep +1;
    +            } while (substr($memberrange[0], -1) != '*');
    +        }
    +        return $entries;
    +    }
    +    
    +    /**
    +    * Group Information.  Returns an collection
    +    * The group name is case sensitive
    +    * 
    +    * @param string $groupName The group name to retrieve info about
    +    * @param array $fields Fields to retrieve
    +    * @return \adLDAP\collections\adLDAPGroupCollection
    +    */
    +    public function infoCollection($groupName, $fields = NULL) {
    +        if ($groupName === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        $info = $this->info($groupName, $fields);
    +        if ($info !== false) {
    +            $collection = new \adLDAP\collections\adLDAPGroupCollection($info, $this->adldap);
    +            return $collection;
    +        }
    +        return false;
    +    }
    +    
    +    /**
    +    * Return a complete list of "groups in groups"
    +    * 
    +    * @param string $group The group to get the list from
    +    * @return array
    +    */
    +    public function recursiveGroups($group) {
    +        if ($group === NULL) { return false; }
    +
    +        $stack = array(); 
    +        $processed = array(); 
    +        $retGroups = array(); 
    +     
    +        array_push($stack, $group); // Initial Group to Start with 
    +        while (count($stack) > 0) {
    +            $parent = array_pop($stack);
    +            array_push($processed, $parent);
    +            
    +            $info = $this->info($parent, array("memberof"));
    +            
    +            if (isset($info[0]["memberof"]) && is_array($info[0]["memberof"])) {
    +                $groups = $info[0]["memberof"]; 
    +                if ($groups) {
    +                    $groupNames = $this->adldap->utilities()->niceNames($groups);  
    +                    $retGroups = array_merge($retGroups, $groupNames); //final groups to return
    +                    foreach ($groupNames as $id => $groupName) { 
    +                        if (!in_array($groupName, $processed)) {
    +                            array_push($stack, $groupName);
    +                        }
    +                    }
    +                }
    +            }
    +        }
    +        return $retGroups;
    +    }
    +    
    +    /**
    +    * Returns a complete list of the groups in AD based on a SAM Account Type  
    +    * 
    +    * @param string $sAMAaccountType The account type to return
    +    * @param bool $includeDescription Whether to return a description
    +    * @param string $search Search parameters
    +    * @param bool $sorted Whether to sort the results
    +    * @return array
    +    */
    +    public function search($sAMAaccountType = adLDAP::ADLDAP_SECURITY_GLOBAL_GROUP, $includeDescription = false, $search = "*", $sorted = true) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        $filter = '(&(objectCategory=group)';
    +        if ($sAMAaccountType !== null) {
    +            $filter .= '(samaccounttype='. $sAMAaccountType .')';
    +        }
    +        $filter .= '(cn=' . $search . '))';
    +        // Perform the search and grab all their details
    +        $fields = array("samaccountname", "description");
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +
    +        $groupsArray = array();        
    +        for ($i=0; $i<$entries["count"]; $i++) {
    +            if ($includeDescription && strlen($entries[$i]["description"][0]) > 0 ) {
    +                $groupsArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["description"][0];
    +            }
    +            else if ($includeDescription) {
    +                $groupsArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0];
    +            }
    +            else {
    +                array_push($groupsArray, $entries[$i]["samaccountname"][0]);
    +            }
    +        }
    +        if ($sorted) { 
    +            asort($groupsArray); 
    +        }
    +        return $groupsArray;
    +    }
    +
    +    /**
    +    * Obtain the group's distinguished name based on their groupid
    +    *
    +    *
    +    * @param string $groupname The groupname
    +    * @return string
    +    */
    +    public function dn($groupname) {
    +        $group = $this->info($groupname, array("cn"));
    +        if ($group[0]["dn"] === NULL) {
    +            return false;
    +        }
    +        $groupDn = $group[0]["dn"];
    +        return $groupDn;
    +    }
    +
    +    /**
    +    * Returns a complete list of all groups in AD
    +    * 
    +    * @param bool $includeDescription Whether to return a description
    +    * @param string $search Search parameters
    +    * @param bool $sorted Whether to sort the results
    +    * @return array
    +    */
    +    public function all($includeDescription = false, $search = "*", $sorted = true) {
    +        $groupsArray = $this->search(null, $includeDescription, $search, $sorted);
    +        return $groupsArray;
    +    }
    +    
    +    /**
    +    * Returns a complete list of security groups in AD
    +    * 
    +    * @param bool $includeDescription Whether to return a description
    +    * @param string $search Search parameters
    +    * @param bool $sorted Whether to sort the results
    +    * @return array
    +    */
    +    public function allSecurity($includeDescription = false, $search = "*", $sorted = true) {
    +        $groupsArray = $this->search(adLDAP::ADLDAP_SECURITY_GLOBAL_GROUP, $includeDescription, $search, $sorted);
    +        return $groupsArray;
    +    }
    +    
    +    /**
    +    * Returns a complete list of distribution lists in AD
    +    * 
    +    * @param bool $includeDescription Whether to return a description
    +    * @param string $search Search parameters
    +    * @param bool $sorted Whether to sort the results
    +    * @return array
    +    */
    +    public function allDistribution($includeDescription = false, $search = "*", $sorted = true) {
    +        $groupsArray = $this->search(adLDAP::ADLDAP_DISTRIBUTION_GROUP, $includeDescription, $search, $sorted);
    +        return $groupsArray;
    +    }
    +    
    +    /**
    +    * Coping with AD not returning the primary group
    +    * http://support.microsoft.com/?kbid=321360 
    +    * 
    +    * This is a re-write based on code submitted by Bruce which prevents the 
    +    * need to search each security group to find the true primary group
    +    * 
    +    * @param string $gid Group ID
    +    * @param string $usersid User's Object SID
    +    * @return mixed
    +    */
    +    public function getPrimaryGroup($gid, $usersid) {
    +        if ($gid === NULL || $usersid === NULL) { return false; }
    +        $sr = false;
    +
    +        $gsid = substr_replace($usersid, pack('V',$gid), strlen($usersid)-4,4);
    +        $filter = '(objectsid=' . $this->adldap->utilities()->getTextSID($gsid).')';
    +        $fields = array("samaccountname","distinguishedname");
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +
    +        if (isset($entries[0]['distinguishedname'][0])) {
    +            return $entries[0]['distinguishedname'][0];
    +        }
    +        return false;
    +     }
    +     
    +     /**
    +    * Coping with AD not returning the primary group
    +    * http://support.microsoft.com/?kbid=321360 
    +    * 
    +    * For some reason it's not possible to search on primarygrouptoken=XXX
    +    * If someone can show otherwise, I'd like to know about it :)
    +    * this way is resource intensive and generally a pain in the @#%^
    +    * 
    +    * @deprecated deprecated since version 3.1, see get get_primary_group
    +    * @param string $gid Group ID
    +    * @return string
    +    */
    +    public function cn($gid) {    
    +        if ($gid === NULL) { return false; }
    +        $sr = false;
    +        $r = '';
    +        
    +        $filter = "(&(objectCategory=group)(samaccounttype=" . adLDAP::ADLDAP_SECURITY_GLOBAL_GROUP . "))";
    +        $fields = array("primarygrouptoken", "samaccountname", "distinguishedname");
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +        
    +        for ($i=0; $i<$entries["count"]; $i++) {
    +            if ($entries[$i]["primarygrouptoken"][0] == $gid) {
    +                $r = $entries[$i]["distinguishedname"][0];
    +                $i = $entries["count"];
    +            }
    +        }
    +        return $r;
    +    }
    +}
    +?>
    diff --git a/functions/adLDAP/src/classes/adLDAPUsers.php b/functions/adLDAP/src/classes/adLDAPUsers.php
    new file mode 100755
    index 000000000..c3ea8c6b7
    --- /dev/null
    +++ b/functions/adLDAP/src/classes/adLDAPUsers.php
    @@ -0,0 +1,658 @@
    +adldap = $adldap;
    +    }
    +    
    +    /**
    +    * Validate a user's login credentials
    +    * 
    +    * @param string $username A user's AD username
    +    * @param string $password A user's AD password
    +    * @param bool optional $prevent_rebind
    +    * @return bool
    +    */
    +    public function authenticate($username, $password, $preventRebind = false) {
    +        return $this->adldap->authenticate($username, $password, $preventRebind);
    +    }
    +    
    +    /**
    +    * Create a user
    +    * 
    +    * If you specify a password here, this can only be performed over SSL
    +    * 
    +    * @param array $attributes The attributes to set to the user account
    +    * @return bool
    +    */
    +    public function create($attributes) {
    +        // Check for compulsory fields
    +        if (!array_key_exists("username", $attributes)) { return "Missing compulsory field [username]"; }
    +        if (!array_key_exists("firstname", $attributes)) { return "Missing compulsory field [firstname]"; }
    +        if (!array_key_exists("surname", $attributes)) { return "Missing compulsory field [surname]"; }
    +        if (!array_key_exists("email", $attributes)) { return "Missing compulsory field [email]"; }
    +        if (!array_key_exists("container", $attributes)) { return "Missing compulsory field [container]"; }
    +        if (!is_array($attributes["container"])) { return "Container attribute must be an array."; }
    +
    +        if (array_key_exists("password",$attributes) && (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS())) { 
    +            throw new \adLDAP\adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
    +        }
    +
    +        if (!array_key_exists("display_name", $attributes)) { 
    +            $attributes["display_name"] = $attributes["firstname"] . " " . $attributes["surname"]; 
    +        }
    +
    +        // Translate the schema
    +        $add = $this->adldap->adldap_schema($attributes);
    +        
    +        // Additional stuff only used for adding accounts
    +        $add["cn"][0] = $attributes["display_name"];
    +        $add["samaccountname"][0] = $attributes["username"];
    +        $add["objectclass"][0] = "top";
    +        $add["objectclass"][1] = "person";
    +        $add["objectclass"][2] = "organizationalPerson";
    +        $add["objectclass"][3] = "user"; //person?
    +        //$add["name"][0]=$attributes["firstname"]." ".$attributes["surname"];
    +
    +        // Set the account control attribute
    +        $control_options = array("NORMAL_ACCOUNT");
    +        if (!$attributes["enabled"]) { 
    +            $control_options[] = "ACCOUNTDISABLE"; 
    +        }
    +        $add["userAccountControl"][0] = $this->accountControl($control_options);
    +        
    +        // Determine the container
    +        $attributes["container"] = array_reverse($attributes["container"]);
    +        $container = "OU=" . implode(", OU=",$attributes["container"]);
    +
    +        // Add the entry
    +        $result = @ldap_add($this->adldap->getLdapConnection(), "CN=" . $add["cn"][0] . ", " . $container . "," . $this->adldap->getBaseDn(), $add);
    +        if ($result != true) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Account control options
    +    *
    +    * @param array $options The options to convert to int 
    +    * @return int
    +    */
    +    protected function accountControl($options) {
    +        $val=0;
    +
    +        if (is_array($options)) {
    +            if (in_array("SCRIPT",$options)) { $val=$val+1; }
    +            if (in_array("ACCOUNTDISABLE",$options)) { $val=$val+2; }
    +            if (in_array("HOMEDIR_REQUIRED",$options)) { $val=$val+8; }
    +            if (in_array("LOCKOUT",$options)) { $val=$val+16; }
    +            if (in_array("PASSWD_NOTREQD",$options)) { $val=$val+32; }
    +            //PASSWD_CANT_CHANGE Note You cannot assign this permission by directly modifying the UserAccountControl attribute.
    +            //For information about how to set the permission programmatically, see the "Property flag descriptions" section.
    +            if (in_array("ENCRYPTED_TEXT_PWD_ALLOWED",$options)) { $val=$val+128; }
    +            if (in_array("TEMP_DUPLICATE_ACCOUNT",$options)) { $val=$val+256; }
    +            if (in_array("NORMAL_ACCOUNT",$options)) { $val=$val+512; }
    +            if (in_array("INTERDOMAIN_TRUST_ACCOUNT",$options)) { $val=$val+2048; }
    +            if (in_array("WORKSTATION_TRUST_ACCOUNT",$options)) { $val=$val+4096; }
    +            if (in_array("SERVER_TRUST_ACCOUNT",$options)) { $val=$val+8192; }
    +            if (in_array("DONT_EXPIRE_PASSWORD",$options)){ $val=$val+65536; }
    +            if (in_array("MNS_LOGON_ACCOUNT",$options)) { $val=$val+131072; }
    +            if (in_array("SMARTCARD_REQUIRED",$options)) { $val=$val+262144; }
    +            if (in_array("TRUSTED_FOR_DELEGATION",$options)) { $val=$val+524288; }
    +            if (in_array("NOT_DELEGATED",$options)) { $val=$val+1048576; }
    +            if (in_array("USE_DES_KEY_ONLY",$options)) { $val=$val+2097152; }
    +            if (in_array("DONT_REQ_PREAUTH",$options)) { $val=$val+4194304; } 
    +            if (in_array("PASSWORD_EXPIRED",$options)) { $val=$val+8388608; }
    +            if (in_array("TRUSTED_TO_AUTH_FOR_DELEGATION",$options)) { $val=$val+16777216; }
    +        }
    +        return $val;
    +    }
    +    
    +    /**
    +    * Delete a user account
    +    * 
    +    * @param string $username The username to delete (please be careful here!)
    +    * @param bool $isGUID Is the username a GUID or a samAccountName
    +    * @return array
    +    */
    +    public function delete($username, $isGUID = false) {      
    +        $userinfo = $this->info($username, array("*"), $isGUID);
    +        $dn = $userinfo[0]['distinguishedname'][0];
    +        $result = $this->adldap->folder()->delete($dn);
    +        if ($result != true) { 
    +            return false;
    +        }        
    +        return true;
    +    }
    +    
    +    /**
    +    * Groups the user is a member of
    +    * 
    +    * @param string $username The username to query
    +    * @param bool $recursive Recursive list of groups
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return array
    +    */
    +    public function groups($username, $recursive = NULL, $isGUID = false) {
    +        if ($username === NULL) { return false; }
    +        if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        // Search the directory for their information
    +        $info = @$this->info($username, array("memberof", "primarygroupid"), $isGUID);
    +        $groups = $this->adldap->utilities()->niceNames($info[0]["memberof"]); // Presuming the entry returned is our guy (unique usernames)
    +
    +        if ($recursive === true) {
    +            foreach ($groups as $id => $groupName) {
    +                $extraGroups = $this->adldap->group()->recursiveGroups($groupName);
    +                $groups = array_merge($groups, $extraGroups);
    +            }
    +        }
    +        return $groups;
    +    }
    +    
    +    /**
    +    * Find information about the users. Returned in a raw array format from AD
    +    * 
    +    * @param string $username The username to query
    +    * @param array $fields Array of parameters to query
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return array
    +    */
    +    public function info($username, $fields = NULL, $isGUID = false) {
    +        if ($username === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +
    +        if ($isGUID === true) {
    +            $username = $this->adldap->utilities()->strGuidToHex($username);
    +            $filter = "objectguid=" . $username;
    +        }
    +        else if (strpos($username, "@")) {
    +             $filter = "userPrincipalName=" . $username;
    +        }
    +        else {
    +             $filter = "samaccountname=" . $username;
    +        }
    +        $filter = "(&(objectCategory=person)({$filter}))";
    +        if ($fields === NULL) { 
    +            $fields = array("samaccountname","mail","memberof","department","displayname","telephonenumber","primarygroupid","objectsid"); 
    +        }
    +        if (!in_array("objectsid", $fields)) {
    +            $fields[] = "objectsid";
    +        }
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +        
    +        if (isset($entries[0])) {
    +            if ($entries[0]['count'] >= 1) {
    +                if (in_array("memberof", $fields)) {
    +                    // AD does not return the primary group in the ldap query, we may need to fudge it
    +                    if ($this->adldap->getRealPrimaryGroup() && isset($entries[0]["primarygroupid"][0]) && isset($entries[0]["objectsid"][0])) {
    +                        //$entries[0]["memberof"][]=$this->group_cn($entries[0]["primarygroupid"][0]);
    +                        $entries[0]["memberof"][] = $this->adldap->group()->getPrimaryGroup($entries[0]["primarygroupid"][0], $entries[0]["objectsid"][0]);
    +                    } else {
    +                        $entries[0]["memberof"][] = "CN=Domain Users,CN=Users," . $this->adldap->getBaseDn();
    +                    }
    +                    if (!isset($entries[0]["memberof"]["count"])) {
    +                        $entries[0]["memberof"]["count"] = 0;
    +                    }
    +                    $entries[0]["memberof"]["count"]++;
    +                }
    +            }
    +            return $entries;
    +        }
    +        return false;
    +    }
    +    
    +    /**
    +    * Find information about the users. Returned in a raw array format from AD
    +    * 
    +    * @param string $username The username to query
    +    * @param array $fields Array of parameters to query
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return mixed
    +    */
    +    public function infoCollection($username, $fields = NULL, $isGUID = false) {
    +        if ($username === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        $info = $this->info($username, $fields, $isGUID);
    +        
    +        if ($info !== false) {
    +            $collection = new \adLDAP\collections\adLDAPUserCollection($info, $this->adldap);
    +            return $collection;
    +        }
    +        return false;
    +    }
    +    
    +    /**
    +    * Determine if a user is in a specific group
    +    * 
    +    * @param string $username The username to query
    +    * @param string $group The name of the group to check against
    +    * @param bool $recursive Check groups recursively
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function inGroup($username, $group, $recursive = NULL, $isGUID = false) {
    +        if ($username === NULL) { return false; }
    +        if ($group === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it
    +        
    +        // Get a list of the groups
    +        $groups = $this->groups($username, $recursive, $isGUID);
    +        
    +        // Return true if the specified group is in the group list
    +        if (in_array($group, $groups)) { 
    +            return true; 
    +        }
    +        return false;
    +    }
    +    
    +    /**
    +    * Determine a user's password expiry date
    +    * 
    +    * @param string $username The username to query
    +    * @param book $isGUID Is the username passed a GUID or a samAccountName
    +    * @requires bcmath http://www.php.net/manual/en/book.bc.php
    +    * @return array
    +    */
    +    public function passwordExpiry($username, $isGUID = false) {
    +        if ($username === NULL) { return "Missing compulsory field [username]"; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if (!function_exists('bcmod')) { throw new \adLDAP\adLDAPException("Missing function support [bcmod] http://www.php.net/manual/en/book.bc.php"); };
    +        
    +        $userInfo = $this->info($username, array("pwdlastset", "useraccountcontrol"), $isGUID);
    +        $pwdLastSet = $userInfo[0]['pwdlastset'][0];
    +        $status = array();
    +        
    +        if ($userInfo[0]['useraccountcontrol'][0] == '66048') {
    +            // Password does not expire
    +            return "Does not expire";
    +        }
    +        if ($pwdLastSet === '0') {
    +            // Password has already expired
    +            return "Password has expired";
    +        }
    +        
    +         // Password expiry in AD can be calculated from TWO values:
    +         //   - User's own pwdLastSet attribute: stores the last time the password was changed
    +         //   - Domain's maxPwdAge attribute: how long passwords last in the domain
    +         //
    +         // Although Microsoft chose to use a different base and unit for time measurements.
    +         // This function will convert them to Unix timestamps
    +         $sr = ldap_read($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), 'objectclass=*', array('maxPwdAge'));
    +         if (!$sr) {
    +             return false;
    +         }
    +         $info = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +         $maxPwdAge = $info[0]['maxpwdage'][0];
    +         
    +         // See MSDN: http://msdn.microsoft.com/en-us/library/ms974598.aspx
    +         //
    +         // pwdLastSet contains the number of 100 nanosecond intervals since January 1, 1601 (UTC), 
    +         // stored in a 64 bit integer. 
    +         //
    +         // The number of seconds between this date and Unix epoch is 11644473600.
    +         //
    +         // maxPwdAge is stored as a large integer that represents the number of 100 nanosecond
    +         // intervals from the time the password was set before the password expires.
    +         //
    +         // We also need to scale this to seconds but also this value is a _negative_ quantity!
    +         //
    +         // If the low 32 bits of maxPwdAge are equal to 0 passwords do not expire
    +         //
    +         // Unfortunately the maths involved are too big for PHP integers, so I've had to require
    +         // BCMath functions to work with arbitrary precision numbers.
    +         if (bcmod($maxPwdAge, 4294967296) === '0') {
    +            return "Domain does not expire passwords";
    +        }
    +        
    +        // Add maxpwdage and pwdlastset and we get password expiration time in Microsoft's
    +        // time units.  Because maxpwd age is negative we need to subtract it.
    +        $pwdExpire = bcsub($pwdLastSet, $maxPwdAge);
    +    
    +        // Convert MS's time to Unix time
    +        $status['expiryts'] = bcsub(bcdiv($pwdExpire, '10000000'), '11644473600');
    +        $status['expiryformat'] = date('Y-m-d H:i:s', bcsub(bcdiv($pwdExpire, '10000000'), '11644473600'));
    +        
    +        return $status;
    +    }
    +    
    +    /**
    +    * Modify a user
    +    * 
    +    * @param string $username The username to query
    +    * @param array $attributes The attributes to modify.  Note if you set the enabled attribute you must not specify any other attributes
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function modify($username, $attributes, $isGUID = false) {
    +        if ($username === NULL) { return "Missing compulsory field [username]"; }
    +        if (array_key_exists("password", $attributes) && !$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) { 
    +            throw new \adLDAP\adLDAPException('SSL/TLS must be configured on your webserver and enabled in the class to set passwords.');
    +        }
    +
    +        // Find the dn of the user
    +        $userDn = $this->dn($username, $isGUID);
    +        if ($userDn === false) { 
    +            return false; 
    +        }
    +        
    +        // Translate the update to the LDAP schema                
    +        $mod = $this->adldap->adldap_schema($attributes);
    +        
    +        // Check to see if this is an enabled status update
    +        if (!$mod && !array_key_exists("enabled", $attributes)) { 
    +            return false; 
    +        }
    +        
    +        // Set the account control attribute (only if specified)
    +        if (array_key_exists("enabled", $attributes)) {
    +            if ($attributes["enabled"]) { 
    +                $controlOptions = array("NORMAL_ACCOUNT"); 
    +            }
    +            else { 
    +                $controlOptions = array("NORMAL_ACCOUNT", "ACCOUNTDISABLE"); 
    +            }
    +            $mod["userAccountControl"][0] = $this->accountControl($controlOptions);
    +        }
    +
    +        // Do the update
    +        $result = @ldap_modify($this->adldap->getLdapConnection(), $userDn, $mod);
    +        if ($result == false) { 
    +            return false; 
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Disable a user account
    +    * 
    +    * @param string $username The username to disable
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function disable($username, $isGUID = false) {
    +        if ($username === NULL) { return "Missing compulsory field [username]"; }
    +        $attributes = array("enabled" => 0);
    +        $result = $this->modify($username, $attributes, $isGUID);
    +        if ($result == false) { return false; }
    +       
    +        return true;
    +    }
    +    
    +    /**
    +    * Enable a user account
    +    * 
    +    * @param string $username The username to enable
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function enable($username, $isGUID = false) {
    +        if ($username === NULL) { return "Missing compulsory field [username]"; }
    +        $attributes = array("enabled" => 1);
    +        $result = $this->modify($username, $attributes, $isGUID);
    +        if ($result == false) { return false; }
    +        
    +        return true;
    +    }
    +    
    +    /**
    +    * Set the password of a user - This must be performed over SSL
    +    * 
    +    * @param string $username The username to modify
    +    * @param string $password The new password
    +    * @param bool $isGUID Is the username passed a GUID or a samAccountName
    +    * @return bool
    +    */
    +    public function password($username, $password, $isGUID = false) {
    +        if ($username === NULL) { return false; }
    +        if ($password === NULL) { return false; }
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) { 
    +            throw new \adLDAP\adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
    +        }
    +        
    +        $userDn = $this->dn($username, $isGUID);
    +        if ($userDn === false) { 
    +            return false; 
    +        }
    +                
    +        $add=array();
    +        $add["unicodePwd"][0] = $this->encodePassword($password);
    +        
    +        $result = @ldap_mod_replace($this->adldap->getLdapConnection(), $userDn, $add);
    +        if ($result === false) {
    +            $err = ldap_errno($this->adldap->getLdapConnection());
    +            if ($err) {
    +                $msg = 'Error ' . $err . ': ' . ldap_err2str($err) . '.';
    +                if($err == 53) {
    +                    $msg .= ' Your password might not match the password policy.';
    +                }
    +                throw new \adLDAP\adLDAPException($msg);
    +            }
    +            else {
    +                return false;
    +            }
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Encode a password for transmission over LDAP
    +    *
    +    * @param string $password The password to encode
    +    * @return string
    +    */
    +    public function encodePassword($password) {
    +        $password="\"".$password."\"";
    +        $encoded="";
    +        for ($i=0; $i info($username, array("cn"), $isGUID);
    +        if ($user[0]["dn"] === NULL) { 
    +            return false; 
    +        }
    +        $userDn = $user[0]["dn"];
    +        return $userDn;
    +    }
    +    
    +    /**
    +    * Return a list of all users in AD
    +    * 
    +    * @param bool $includeDescription Return a description of the user
    +    * @param string $search Search parameter
    +    * @param bool $sorted Sort the user accounts
    +    * @return array
    +    */
    +    public function all($includeDescription = false, $search = "*", $sorted = true) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        
    +        // Perform the search and grab all their details
    +        $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)(cn=" . $search . "))";
    +        $fields = array("samaccountname","displayname");
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +
    +        $usersArray = array();
    +        for ($i=0; $i<$entries["count"]; $i++) {
    +            if ($includeDescription && strlen($entries[$i]["displayname"][0])>0) {
    +                $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0];
    +            } elseif ($includeDescription) {
    +                $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0];
    +            } else {
    +                array_push($usersArray, $entries[$i]["samaccountname"][0]);
    +            }
    +        }
    +        if ($sorted) { 
    +            asort($usersArray); 
    +        }
    +        return $usersArray;
    +    }
    +    
    +    /**
    +    * Converts a username (samAccountName) to a GUID
    +    * 
    +    * @param string $username The username to query
    +    * @return string
    +    */
    +    public function usernameToGuid($username) {
    +        if (!$this->adldap->getLdapBind()){ return false; }
    +        if ($username === null){ return "Missing compulsory field [username]"; }
    +        
    +        $filter = "samaccountname=" . $username; 
    +        $fields = array("objectGUID"); 
    +        $sr = @ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields); 
    +        if (ldap_count_entries($this->adldap->getLdapConnection(), $sr) > 0) { 
    +            $entry = @ldap_first_entry($this->adldap->getLdapConnection(), $sr); 
    +            $guid = @ldap_get_values_len($this->adldap->getLdapConnection(), $entry, 'objectGUID'); 
    +            $strGUID = $this->adldap->utilities()->binaryToText($guid[0]);          
    +            return $strGUID; 
    +        }
    +        return false; 
    +    }
    +    
    +    /**
    +    * Return a list of all users in AD that have a specific value in a field
    +    *
    +    * @param bool $includeDescription Return a description of the user
    +    * @param string $searchField Field to search search for
    +    * @param string $searchFilter Value to search for in the specified field
    +    * @param bool $sorted Sort the user accounts
    +    * @return array
    +    */
    +    public function find($includeDescription = false, $searchField = false, $searchFilter = false, $sorted = true) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +          
    +        // Perform the search and grab all their details
    +        $searchParams = "";
    +        if ($searchField) {
    +            $searchParams = "(" . $searchField . "=" . $searchFilter . ")";
    +        }                           
    +        $filter = "(&(objectClass=user)(samaccounttype=" . adLDAP::ADLDAP_NORMAL_ACCOUNT .")(objectCategory=person)" . $searchParams . ")";
    +        $fields = array("samaccountname","displayname");
    +        $sr = ldap_search($this->adldap->getLdapConnection(), $this->adldap->getBaseDn(), $filter, $fields);
    +        $entries = ldap_get_entries($this->adldap->getLdapConnection(), $sr);
    +
    +        $usersArray = array();
    +        for ($i=0; $i < $entries["count"]; $i++) {
    +            if ($includeDescription && strlen($entries[$i]["displayname"][0]) > 0) {
    +                $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["displayname"][0];
    +            }
    +            else if ($includeDescription) {
    +                $usersArray[$entries[$i]["samaccountname"][0]] = $entries[$i]["samaccountname"][0];
    +            }
    +            else {
    +                array_push($usersArray, $entries[$i]["samaccountname"][0]);
    +            }
    +        }
    +        if ($sorted) { 
    +          asort($usersArray); 
    +        }
    +        return ($usersArray);
    +    }
    +    
    +    /**
    +    * Move a user account to a different OU
    +    *
    +    * @param string $username The username to move (please be careful here!)
    +    * @param array $container The container or containers to move the user to (please be careful here!).
    +    * accepts containers in 1. parent 2. child order
    +    * @return array
    +    */
    +    public function move($username, $container) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if ($username === null) { return "Missing compulsory field [username]"; }
    +        if ($container === null) { return "Missing compulsory field [container]"; }
    +        if (!is_array($container)) { return "Container must be an array"; }
    +        
    +        $userInfo = $this->info($username, array("*"));
    +        $dn = $userInfo[0]['distinguishedname'][0];
    +        $newRDn = "cn=" . $username;
    +        $container = array_reverse($container);
    +        $newContainer = "ou=" . implode(",ou=",$container);
    +        $newBaseDn = strtolower($newContainer) . "," . $this->adldap->getBaseDn();
    +        $result = @ldap_rename($this->adldap->getLdapConnection(), $dn, $newRDn, $newBaseDn, true);
    +        if ($result !== true) {
    +            return false;
    +        }
    +        return true;
    +    }
    +    
    +    /**
    +    * Get the last logon time of any user as a Unix timestamp
    +    * 
    +    * @param string $username
    +    * @return long $unixTimestamp
    +    */
    +    public function getLastLogon($username) {
    +        if (!$this->adldap->getLdapBind()) { return false; }
    +        if ($username === null) { return "Missing compulsory field [username]"; }
    +        $userInfo = $this->info($username, array("lastLogonTimestamp"));
    +        $lastLogon = adLDAPUtils::convertWindowsTimeToUnixTime($userInfo[0]['lastLogonTimestamp'][0]);
    +        return $lastLogon;
    +    }
    +}
    +?>
    diff --git a/functions/adLDAP/src/classes/adLDAPUtils.php b/functions/adLDAP/src/classes/adLDAPUtils.php
    new file mode 100755
    index 000000000..a039e7dc9
    --- /dev/null
    +++ b/functions/adLDAP/src/classes/adLDAPUtils.php
    @@ -0,0 +1,287 @@
    +adldap = $adldap;
    +    }
    +    
    +    
    +    /**
    +    * Take an LDAP query and return the nice names, without all the LDAP prefixes (eg. CN, DN)
    +    *
    +    * @param array $groups
    +    * @return array
    +    */
    +    public function niceNames($groups) {
    +
    +        $groupArray = array();
    +        for ($i=0; $i<$groups["count"]; $i++) { // For each group
    +            $line = $groups[$i];
    +            
    +            if (strlen($line)>0) { 
    +                // More presumptions, they're all prefixed with CN=
    +                // so we ditch the first three characters and the group
    +                // name goes up to the first comma
    +                $bits=explode(",", $line);
    +                $groupArray[] = substr($bits[0], 3, (strlen($bits[0])-3));
    +            }
    +        }
    +        return $groupArray;    
    +    }
    +    
    +    /**
    +    * Escape characters for use in an ldap_create function
    +    * 
    +    * @param string $str
    +    * @return string
    +    */
    +    public function escapeCharacters($str) {
    +        $str = str_replace(",", "\,", $str);
    +        return $str;
    +    }
    +    
    +    /**
    +    * Escape strings for the use in LDAP filters
    +    * 
    +    * DEVELOPERS SHOULD BE DOING PROPER FILTERING IF THEY'RE ACCEPTING USER INPUT
    +    * Ported from Perl's Net::LDAP::Util escape_filter_value
    +    *
    +    * @param string $str The string the parse
    +    * @author Port by Andreas Gohr 
    +    * @author Modified for PHP55 by Esteban Santana Santana 
    +    * @return string
    +    */
    +    public function ldapSlashes($str) {
    +        return preg_replace_callback(
    +      		'/([\x00-\x1F\*\(\)\\\\])/',
    +        	function ($matches) {
    +            	return "\\".join("", unpack("H2", $matches[1]));
    +        	},
    +        	$str
    +    	);
    +    }
    +    
    +    /**
    +    * Converts a string GUID to a hexdecimal value so it can be queried
    +    * 
    +    * @param string $strGUID A string representation of a GUID
    +    * @return string
    +    */
    +    public function strGuidToHex($strGUID) {
    +        $strGUID = str_replace('-', '', $strGUID);
    +
    +        $octet_str = '\\' . substr($strGUID, 6, 2);
    +        $octet_str .= '\\' . substr($strGUID, 4, 2);
    +        $octet_str .= '\\' . substr($strGUID, 2, 2);
    +        $octet_str .= '\\' . substr($strGUID, 0, 2);
    +        $octet_str .= '\\' . substr($strGUID, 10, 2);
    +        $octet_str .= '\\' . substr($strGUID, 8, 2);
    +        $octet_str .= '\\' . substr($strGUID, 14, 2);
    +        $octet_str .= '\\' . substr($strGUID, 12, 2);
    +        //$octet_str .= '\\' . substr($strGUID, 16, strlen($strGUID));
    +        for ($i=16; $i<=(strlen($strGUID)-2); $i++) {
    +            if (($i % 2) == 0) {
    +                $octet_str .= '\\' . substr($strGUID, $i, 2);
    +            }
    +        }
    +        
    +        return $octet_str;
    +    }
    +    
    +    /**
    +    * Convert a binary SID to a text SID
    +    * 
    +    * @param string $binsid A Binary SID
    +    * @return string
    +    */
    +     public function getTextSID($binsid) {
    +        $hex_sid = bin2hex($binsid);
    +        $rev = hexdec(substr($hex_sid, 0, 2));
    +        $subcount = hexdec(substr($hex_sid, 2, 2));
    +        $auth = hexdec(substr($hex_sid, 4, 12));
    +        $result = "$rev-$auth";
    +
    +        for ($x=0;$x < $subcount; $x++) {
    +            $subauth[$x] =
    +                hexdec($this->littleEndian(substr($hex_sid, 16 + ($x * 8), 8)));
    +                $result .= "-" . $subauth[$x];
    +        }
    +
    +        // Cheat by tacking on the S-
    +        return 'S-' . $result;
    +     }
    +     
    +    /**
    +    * Converts a little-endian hex number to one that hexdec() can convert
    +    * 
    +    * @param string $hex A hex code
    +    * @return string
    +    */
    +     public function littleEndian($hex) {
    +        $result = '';
    +        for ($x = strlen($hex) - 2; $x >= 0; $x = $x - 2) {
    +            $result .= substr($hex, $x, 2);
    +        }
    +        return $result;
    +     }
    +     
    +     /**
    +    * Converts a binary attribute to a string
    +    * 
    +    * @param string $bin A binary LDAP attribute
    +    * @return string
    +    */
    +    public function binaryToText($bin) {
    +        $hex_guid = bin2hex($bin); 
    +        $hex_guid_to_guid_str = ''; 
    +        for($k = 1; $k <= 4; ++$k) { 
    +            $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2); 
    +        } 
    +        $hex_guid_to_guid_str .= '-'; 
    +        for($k = 1; $k <= 2; ++$k) { 
    +            $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2); 
    +        } 
    +        $hex_guid_to_guid_str .= '-'; 
    +        for($k = 1; $k <= 2; ++$k) { 
    +            $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2); 
    +        } 
    +        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4); 
    +        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20); 
    +        return strtoupper($hex_guid_to_guid_str);   
    +    }
    +    
    +    /**
    +    * Converts a binary GUID to a string GUID
    +    * 
    +    * @param string $binaryGuid The binary GUID attribute to convert
    +    * @return string
    +    */
    +    public function decodeGuid($binaryGuid) {
    +        if ($binaryGuid === null){ return "Missing compulsory field [binaryGuid]"; }
    +        
    +        $strGUID = $this->binaryToText($binaryGuid);          
    +        return $strGUID; 
    +    }
    +    
    +    /**
    +    * Convert a boolean value to a string
    +    * You should never need to call this yourself
    +    *
    +    * @param bool $bool Boolean value
    +    * @return string
    +    */
    +    public function boolToStr($bool) {
    +        return ($bool) ? 'TRUE' : 'FALSE';
    +    }
    +    
    +    /**
    +    * Convert 8bit characters e.g. accented characters to UTF8 encoded characters
    +    */
    +    public function encode8Bit(&$item, $key) {
    +        $encode = false;
    +        if (is_string($item)) {
    +            for ($i=0; $i> 7) {
    +                    $encode = true;
    +                }
    +            }
    +        }
    +        if ($encode === true && $key != 'password') {
    +            $item = utf8_encode($item);   
    +        }
    +    }  
    +    
    +    /**
    +    * Get the current class version number
    +    * 
    +    * @return string
    +    */
    +    public function getVersion() {
    +        return self::ADLDAP_VERSION;
    +    }
    +    
    +    /**
    +    * Round a Windows timestamp down to seconds and remove the seconds between 1601-01-01 and 1970-01-01
    +    * 
    +    * @param long $windowsTime
    +    * @return long $unixTime
    +    */
    +    public static function convertWindowsTimeToUnixTime($windowsTime) {
    +      $unixTime = round($windowsTime / 10000000) - 11644473600; 
    +      return $unixTime; 
    +    }
    +
    +    /**
    +     * Convert DN string to array
    +     *
    +     * @param $dnStr
    +     * @param bool $excludeBaseDn exclude base DN from results
    +     *
    +     * @return array
    +     */
    +    public function dnStrToArr($dnStr, $excludeBaseDn = true){
    +        $dnArr = array();
    +        if(!empty($dnStr)){
    +            $tmpArr = explode(',', $dnStr);
    +            $baseDnArr = explode(',', $this->adldap->getBaseDn());
    +            foreach($tmpArr as $_tmpStr){
    +                if($excludeBaseDn && in_array($_tmpStr, $baseDnArr)){
    +                    continue;
    +                }
    +                $dnArr[]= substr($_tmpStr, strpos($_tmpStr, '=') + 1);
    +            }
    +        }
    +        return $dnArr;
    +    }
    +}
    +
    +?>
    diff --git a/functions/adLDAP/src/collections/adLDAPCollection.php b/functions/adLDAP/src/collections/adLDAPCollection.php
    new file mode 100755
    index 000000000..b847ce832
    --- /dev/null
    +++ b/functions/adLDAP/src/collections/adLDAPCollection.php
    @@ -0,0 +1,134 @@
    +setInfo($info);   
    +        $this->adldap = $adldap;
    +    }
    +    
    +    /**
    +    * Set the raw info array from Active Directory
    +    * 
    +    * @param array $info
    +    */
    +    public function setInfo(array $info) {
    +        if ($this->info && sizeof($info) >= 1) {
    +            unset($this->info);
    +        }
    +        $this->info = $info;   
    +    }
    +    
    +    /**
    +    * Magic get method to retrieve data from the raw array in a formatted way
    +    * 
    +    * @param string $attribute
    +    * @return mixed
    +    */
    +    public function __get($attribute) {
    +        if (isset($this->info[0]) && is_array($this->info[0])) {
    +            foreach ($this->info[0] as $keyAttr => $valueAttr) {
    +                if (strtolower($keyAttr) == strtolower($attribute)) {
    +                    if ($this->info[0][strtolower($attribute)]['count'] == 1) {
    +                        return $this->info[0][strtolower($attribute)][0];   
    +                    }
    +                    else {
    +                        $array = array();
    +                        foreach ($this->info[0][strtolower($attribute)] as $key => $value) {
    +                            if ((string)$key != 'count') {
    +                                $array[$key] = $value;
    +                            } 
    +                        }  
    +                        return $array;   
    +                    }
    +                }   
    +            }
    +        }
    +        else {
    +            return NULL;   
    +        }
    +    }    
    +    
    +    /**
    +    * Magic set method to update an attribute
    +    * 
    +    * @param string $attribute
    +    * @param string $value
    +    * @return bool
    +    */
    +    abstract public function __set($attribute, $value);
    +    
    +    /** 
    +    * Magic isset method to check for the existence of an attribute 
    +    * 
    +    * @param string $attribute 
    +    * @return bool 
    +    */ 
    +    public function __isset($attribute) {
    +        if (isset($this->info[0]) && is_array($this->info[0])) { 
    +            foreach ($this->info[0] as $keyAttr => $valueAttr) { 
    +                if (strtolower($keyAttr) == strtolower($attribute)) { 
    +                    return true; 
    +                } 
    +            } 
    +        } 
    +        return false; 
    +     } 
    +}
    +?>
    diff --git a/functions/adLDAP/src/collections/adLDAPComputerCollection.php b/functions/adLDAP/src/collections/adLDAPComputerCollection.php
    new file mode 100755
    index 000000000..028ccad70
    --- /dev/null
    +++ b/functions/adLDAP/src/collections/adLDAPComputerCollection.php
    @@ -0,0 +1,45 @@
    +
    diff --git a/functions/adLDAP/src/collections/adLDAPContactCollection.php b/functions/adLDAP/src/collections/adLDAPContactCollection.php
    new file mode 100755
    index 000000000..a7c7c9d02
    --- /dev/null
    +++ b/functions/adLDAP/src/collections/adLDAPContactCollection.php
    @@ -0,0 +1,45 @@
    +
    diff --git a/functions/adLDAP/src/collections/adLDAPGroupCollection.php b/functions/adLDAP/src/collections/adLDAPGroupCollection.php
    new file mode 100755
    index 000000000..049da3043
    --- /dev/null
    +++ b/functions/adLDAP/src/collections/adLDAPGroupCollection.php
    @@ -0,0 +1,45 @@
    +
    diff --git a/functions/adLDAP/src/collections/adLDAPUserCollection.php b/functions/adLDAP/src/collections/adLDAPUserCollection.php
    new file mode 100755
    index 000000000..fe3af13f0
    --- /dev/null
    +++ b/functions/adLDAP/src/collections/adLDAPUserCollection.php
    @@ -0,0 +1,45 @@
    +
    diff --git a/functions/checks/check_db_install.php b/functions/checks/check_db_install.php
    new file mode 100755
    index 000000000..b5610746b
    --- /dev/null
    +++ b/functions/checks/check_db_install.php
    @@ -0,0 +1,12 @@
    +check_db_connection(true);
    +# connection is ok, check that table exists
    +$Install->check_table("vrf", true);
    +
    +?>
    \ No newline at end of file
    diff --git a/functions/checks/check_db_structure.php b/functions/checks/check_db_structure.php
    new file mode 100755
    index 000000000..490562658
    --- /dev/null
    +++ b/functions/checks/check_db_structure.php
    @@ -0,0 +1,47 @@
    +verify_database();
    +
    +# print result
    +if( (!isset($errors['tableError'])) && (!isset($errors['fieldError'])) ) {
    +	print 'All tables and fields are installed properly'. "\n";
    +}
    +else {
    +	# missing tables
    +	if (isset($errors['tableError'])) {
    +		print 'Missing tables:'. "\n";
    +
    +		foreach ($errors['tableError'] as $table) {
    +			print " - ".$table."\n";
    +		}
    +	}
    +
    +	# missing fields
    +	if (isset($errors['fieldError'])) {
    +		print "\n".'Missing fields'. "\n";
    +
    +		foreach ($errors['fieldError'] as $table=>$field) {
    +			print 'Table `'. $table .'`: missing field `'. $field .'`;'."\n";
    +		}
    +	}
    +}
    +print "\n";
    +?>
    \ No newline at end of file
    diff --git a/functions/checks/check_db_upgrade.php b/functions/checks/check_db_upgrade.php
    new file mode 100755
    index 000000000..c1994ac0d
    --- /dev/null
    +++ b/functions/checks/check_db_upgrade.php
    @@ -0,0 +1,14 @@
    +settings->version < VERSION) {
    +	header("Location: ".create_link("upgrade"));
    +	die();
    +}
    +?>
    \ No newline at end of file
    diff --git a/functions/checks/check_php_build.php b/functions/checks/check_php_build.php
    new file mode 100755
    index 000000000..74a32edae
    --- /dev/null
    +++ b/functions/checks/check_php_build.php
    @@ -0,0 +1,82 @@
    +";
    +    $error  .= "";
    +    $error  .= "";
    +    $error  .= '';
    +	$error  .= '';
    +	$error  .= "";
    +    $error  .= "";
    +	$error  .= '';
    +
    +    /* error */
    +    $error  .= "
    "._('The following required PHP extensions are missing').":

    "; + $error .= '
      ' . "\n"; + foreach ($missingExt as $missing) { + $error .= '
    • '. $missing .'
    • ' . "\n"; + } + $error .= '

    ' . "\n"; + $error .= _('Please recompile PHP to include missing extensions and restart Apache.') . "\n"; + + $error .= ""; + $error .= ""; + + die($error); +} +?> \ No newline at end of file diff --git a/functions/classes/class.Addresses.php b/functions/classes/class.Addresses.php new file mode 100644 index 000000000..2f42ae162 --- /dev/null +++ b/functions/classes/class.Addresses.php @@ -0,0 +1,1406 @@ +Database = $database; + # initialize Result + $this->Result = new Result (); + # debugging + $this->set_debugging(); + } + + /** + * fetches settings from database + * + * @access private + * @return none + */ + private function get_settings () { + # cache check + if($this->settings == false) { + try { $this->settings = $this->Database->getObject("settings", 1); } + catch (Exception $e) { $this->Result->show("danger", _("Database error: ").$e->getMessage()); } + } + } + + /** + * Strip tags from array or field to protect from XSS + * + * @access public + * @param mixed $input + * @return void + */ + public function strip_input_tags ($input) { + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = strip_tags($v); } + } + else { + $input = strip_tags($input); + } + # stripped + return $input; + } + + /** + * Sets debugging + * + * @access private + * @return void + */ + private function set_debugging () { + include( dirname(__FILE__) . '/../../config.php' ); + $this->debugging = $debugging ? true : false; + } + + /** + * Initializes PEAR Net IPv4 object + * + * @access private + * @return void + */ + private function initialize_pear_net_IPv4 () { + //initialize NET object + if(!is_object($this->Net_IPv4)) { + require_once( dirname(__FILE__) . '/../../functions/PEAR/Net/IPv4.php' ); + //initialize object + $this->Net_IPv4 = new Net_IPv4(); + } + } + /** + * Initializes PEAR Net IPv6 object + * + * @access private + * @return void + */ + private function initialize_pear_net_IPv6 () { + //initialize NET object + if(!is_object($this->Net_IPv6)) { + require_once( dirname(__FILE__) . '/../../functions/PEAR/Net/IPv6.php' ); + //initialize object + $this->Net_IPv6 = new Net_IPv6(); + } + } + + + + + + + + + + + /** + * @address tag methods + * ------------------------------- + */ + + /** + * Returns array of address types. + * + * @access public + * @return array of address types and parameters + */ + public function addresses_types_fetch () { + # check cache + if($this->address_types!=null) { + return $this->address_types; + } + else { + try { $types = $this->Database->getObjects("ipTags", 'id', true); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + # save to array + foreach($types as $t) { + $types_out[$t->id] = (array) $t; + } + # save to cache + $this->address_types = $types_out; + # return + return $types_out; + } + } + + /** + * Sets address tag + * + * @access public + * @param int $state + * @return mixed tag + */ + public function address_type_format_tag ($state) { + # fetch address states + $this->addresses_types_fetch(); + # result + if(!isset($this->address_types[$state])) { + return $state; + } + else { + if($this->address_types[$state]['showtag']==1) { + return ""; + } + } + } + + /** + * returns address type from index + * + * 1 > Offline + * + * @access public + * @param int $index + * @return mixed address type + */ + public function address_type_index_to_type ($index) { + # fetch address states + $this->addresses_types_fetch(); + # return + if(isset($this->address_types[$index])) { + return $this->address_types[$index]['type']; + } + else { + return $index; + } + } + + /** + * Returns address index from type + * + * Offline > 1 + * + * @access public + * @param mixed $type + * @return void + */ + public function address_type_type_to_index ($type) { + # fetch address states + $this->addresses_types_fetch(); + # reindex + foreach($this->address_types as $s) { + $states_assoc[$s['type']] = $s; + } + # return + if(isset($states_assoc[$type])) { + return $states_assoc[$type]['index']; + } + else { + return $type; + } + } + + + + + + + + + + + + + /** + * @address methods + * ------------------------------- + */ + + /** + * Fetches address by specified method + * + * @access public + * @param string $method (default: "id") + * @param mixed $id + * @return object address + */ + public function fetch_address ($method, $id) { + # null method + $method = is_null($method) ? "id" : $this->Database->escape($method); + # check cache first + if(isset($this->addresses[$id])) { + return $this->addresses[$id]; + } + else { + try { $address = $this->Database->getObjectQuery("SELECT * FROM `ipaddresses` where `$method` = ? limit 1;", array($id)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to addresses cache + if(sizeof($address)>0) { + # add decimal format + $address->ip = $this->transform_to_dotted ($address->ip_addr); + # save to subnets + $this->addresses[$id] = (object) $address; + } + #result + return sizeof($address)>0 ? $address : false; + } + } + + /** + * Fetch addresses on int ip_addr and subnetId + * + * @access public + * @param mixed $ip_addr + * @param mixed $subnetId + * @return void + */ + public function fetch_address_multiple_criteria ($ip_addr, $subnetId) { + try { $address = $this->Database->getObjectQuery("SELECT * FROM `ipaddresses` where `ip_addr` = ? and `subnetId` = ? limit 1;", array($ip_addr, $subnetId)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to addresses cache + if(sizeof($address)>0) { + # add decimal format + $address->ip = $this->transform_to_dotted ($address->ip_addr); + # save to subnets + $this->addresses[$address->id] = (object) $address; + } + #result + return sizeof($address)>0 ? $address : false; + } + + /** + * Address modification + * + * @access public + * @param array $address + * @return void + */ + public function modify_address ($address) { + # null empty values + $address = $this->reformat_empty_array_fields ($address, null); + # strip tags + $address = $this->strip_input_tags ($address); + # execute based on action + if($address['action']=="add") { return $this->modify_address_add ($address); } //create new address + elseif($address['action']=="edit") { return $this->modify_address_edit ($address); } //modify existing address + elseif($address['action']=="delete") { return $this->modify_address_delete ($address); } //delete address + elseif($address['action']=="move") { return $this->modify_address_move ($address); } //move to new subnet + else { return $this->Result->show("danger", _("Invalid action"), true); } + } + + /** + * Inserts new IP address to table + * + * @access protected + * @param array $address + * @return boolean success/failure + */ + protected function modify_address_add ($address) { + # set insert array + $insert = array("ip_addr"=>$this->transform_address($address['ip_addr'],"decimal"), + "subnetId"=>$address['subnetId'], + "description"=>@$address['description'], + "dns_name"=>@$address['dns_name'], + "mac"=>@$address['mac'], + "owner"=>@$address['owner'], + "state"=>@$address['state'], + "switch"=>@$address['switch'], + "port"=>@$address['port'], + "note"=>@$address['note'], + "is_gateway"=>@$address['is_gateway'], + "excludePing"=>@$address['excludePing'] + ); + # custom fields, append to array + foreach($this->set_custom_fields() as $c) { + $insert[$c['name']] = strlen(@$address[$c['name']])>0 ? @$address[$c['name']] : null; + } + + # null empty values + $insert = $this->reformat_empty_array_fields ($insert, null); + + # remove gateway + if($address['is_gateway']==1) { $this->remove_gateway ($address['subnetId']); } + + # execute + try { $this->Database->insertObject("ipaddresses", $insert); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + # ok + return true; + } + + /** + * Modifies address in table or whole range if requested + * + * @access protected + * @param array $address + * @return boolean success/failure + */ + protected function modify_address_edit ($address) { + # fetch old details for logging + $address_old = $this->fetch_address (null, $address['id']); + # set update array + $insert = array("id"=>$address['id'], + "subnetId"=>$address['subnetId'], + "ip_addr"=>$this->transform_address($address['ip_addr'], "decimal"), + "description"=>@$address['description'], + "dns_name"=>@$address['dns_name'], + "mac"=>@$address['mac'], + "owner"=>@$address['owner'], + "state"=>@$address['state'], + "switch"=>@$address['switch'], + "port"=>@$address['port'], + "note"=>@$address['note'], + "is_gateway"=>@$address['is_gateway'], + "excludePing"=>@$address['excludePing'] + ); + # custom fields, append to array + foreach($this->set_custom_fields() as $c) { + $insert[$c['name']] = strlen(@$address[$c['name']])>0 ? @$address[$c['name']] : null; + } + + # set primary key for update + if($address['type']=="series") { + $id1 = "subnetId"; + $id2 = "ip_addr"; + unset($insert['id']); + } else { + $id1 = "id"; + $id2 = null; + } + + # remove gateway + if($address['is_gateway']==1) { $this->remove_gateway ($address['subnetId']); } + + # execute + try { $this->Database->updateObject("ipaddresses", $insert, $id1, $id2); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + # ok + return true; + } + + /** + * Deletes address or address range. + * + * @access protected + * @param array $address + * @return boolean success/failure + */ + protected function modify_address_delete ($address) { + # series? + if($address['type']=="series") { + $field = "subnetId"; $value = $address['subnetId']; + $field2 = "ip_addr"; $value2 = $this->transform_address ($address['ip_addr'], "decimal"); + } else { + $field = "id"; $value = $address['id']; + $field2 = null; $value2 = null; + } + # execute + try { $this->Database->deleteRow("ipaddresses", $field, $value, $field2, $value2); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + # ok + return true; + } + + /** + * Moves address to new subnet + * + * @access protected + * @param array $address + * @return boolean success/failure + */ + protected function modify_address_move ($address) { + # execute + try { $this->Database->updateObject("ipaddresses", array("subnetId"=>$address['newSubnet'], "id"=>$address['id'])); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + # ok + return true; + } + /** + * Removes gateway if it exists + * + * @access public + * @param mixed $subnetId + * @return void + */ + public function remove_gateway ($subnetId) { + try { $this->Database->updateObject("ipaddresses", array("subnetId"=>$subnetId, "is_gateway"=>0), "subnetId"); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + } + + /** + * Fetches custom IP address fields + * + * @access public + * @return object custom address fields + */ + public function set_custom_fields () { + # Tools object + $Tools = new Tools ($this->Database); + # fetch + return $Tools->fetch_custom_fields ('ipaddresses'); + } + + /** + * Checks if address already exists in subnet + * + * @access public + * @param int $address + * @param int $subnetId + * @return boolean success/failure + */ + public function address_exists ($address, $subnetId) { + # make sure it is in decimal format + $address = $this->transform_address($address, "decimal"); + # check + $query = "select count(*) as `cnt` from `ipaddresses` where `subnetId`=? and `ip_addr`=?;"; + # fetch + try { $count = $this->Database->getObjectQuery($query, array($subnetId, $address)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return $count->cnt==0 ? false : true; + } + + /** + * Calculates diff between two IP addresses + * + * @access public + * @param int $ip1 + * @param int $ip2 + * @return void + */ + public function calculate_address_diff ($ip1, $ip2) { + return gmp_strval(gmp_sub($ip2, $ip1)); + } + + + /** + * Returns first available subnet address, false if none + * + * @access public + * @param int $subnetId + * @param obj $Subnets + * @return int / false + */ + public function get_first_available_address ($subnetId, $Subnets) { + + # fetch all addresses in subnet and subnet + $addresses = $this->fetch_subnet_addresses ($subnetId, "ip_addr", "asc"); + $subnet = (array) $Subnets->fetch_subnet(null, $subnetId); + + # get max hosts + $max_hosts = $Subnets->get_max_hosts ($subnet['mask'], $this->identify_address($subnet['subnet'])); + + # full subnet? + if(sizeof($addresses)>=$max_hosts) { return false; } //full subnet + + # set type + $ip_version = $this->identify_address ($subnet['subnet']); + # get first diff > 1 + if(sizeof($addresses)>0) { + foreach($addresses as $k=>$ipaddress) { + # check subnet and first IP + if($k==0) { + # /31 fix + if($subnet['mask']==31) { + if(gmp_strval(gmp_sub($addresses[$k]->ip_addr, $subnet['subnet']))>0) { return gmp_strval($subnet['subnet']); } + } else { + if(gmp_strval(gmp_sub($addresses[$k]->ip_addr, $subnet['subnet']))>1) { return gmp_strval(gmp_add($subnet['subnet'], 1)); } + elseif($ip_version=="IPv6") { + if(sizeof($addresses)==1) { + if(gmp_strval(gmp_sub($addresses[$k]->ip_addr, $subnet['subnet']))==0) { return gmp_strval(gmp_add($subnet['subnet'], 1)); } + } + } + } + } + else { + if(gmp_strval(gmp_sub($addresses[$k]->ip_addr, $addresses[$k-1]->ip_addr))>1) { return gmp_strval(gmp_add($addresses[$k-1]->ip_addr, 1)); } + } + } + # all consecutive, last + 1 + { return gmp_strval(gmp_add($addresses[$k]->ip_addr, 1)); } + } + # no addresses + else { + # /32, /31 + if($subnet['mask']==32 || $subnet['mask']==31 || $ip_version=="IPv6") { return $subnet['subnet']; } + else { return gmp_strval(gmp_add($subnet['subnet'], 1)); } + } + + } + + + + + + + + + + + + + /** + * @import address methods + * ------------------------------- + */ + + /** + * Import single line from csv to database + * + * @access public + * @param array $address + * @param int $subnetId + * @return void + */ + public function import_address_from_csv ($address, $subnetId) { + # Subnets object + $this->initialize_subnets_object (); + + # fetch subnet details + $subnet = (array) $this->Subnets->fetch_subnet(null, $subnetId); + + # verify address + if($this->verify_address( $address[0], $this->transform_to_dotted($subnet['subnet'])."/".$subnet['mask'], false, false)!==false) { return false; } + # check for duplicates + if ($this->address_exists($address[0], $subnetId)) { return _('IP address already exists').' - '.$address[0]; } + + # format insert array + $address_insert = array("subnetId"=>$subnetId, "ip_addr"=>$address[0], "state"=>$address[1], "description"=>$address[2], "dns_name"=>$address[3], "mac"=>$address[4], + "owner"=>$address[5], "switch"=>$address[6], "port"=>$address[7], "note"=>$address[8]); + + # switch to 0, state to active + $address_insert['switch'] = strlen($address_insert['switch'])==0 ? 0 : $address_insert['switch']; + $address_insert['state'] = strlen($address_insert['state'])==0 ? 1 : $address_insert['state']; + + # custom fields, append to array + $m=9; + $custom_fields = $this->set_custom_fields(); + if(sizeof($custom_fields) > 0) { + foreach($custom_fields as $c) { + $address_insert[$c['name']] = $address[$m]; + $m++; + } + } + + # insert + return $this->modify_address_add ($address_insert); + } + + + + + + + + + + + /** + * @address subnet methods + * ------------------------------- + */ + + /** + * Opens new Subnets connection if not already opened + * + * @access private + * @return void + */ + private function initialize_subnets_object () { + if(!is_object($this->Subnets)) { $this->Subnets = new Subnets ($this->Database); } + } + + /** + * Fetches all IP addresses in subnet + * + * @access public + * @param int $subnetId + * @param mixed $order + * @param mixed $order_direction + * @return objects addresses + */ + public function fetch_subnet_addresses ($subnetId, $order=null, $order_direction=null) { + # set order + if(!is_null($order)) { $order = array($order, $order_direction); } + else { $order = array("ip_addr", "asc"); } + + try { $addresses = $this->Database->getObjectsQuery("SELECT * FROM `ipaddresses` where `subnetId` = ? order by `$order[0]` $order[1];", array($subnetId)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to addresses cache + if(sizeof($addresses)>0) { + foreach($addresses as $k=>$address) { + # add decimal format + $address->ip = $this->transform_to_dotted ($address->ip_addr); + # save to subnets + $this->addresses[$address->id] = (object) $address; + $addresses[$k]->ip = $address->ip; + } + } + # result + return sizeof($addresses)>0 ? $addresses : array(); + } + + /** + * Count number of IP addresses in subnet + * + * @access public + * @param int $subnetId + * @return int number of addresses in subnet + */ + public function count_subnet_addresses ($subnetId) { + try { $count = $this->Database->numObjectsFilter("ipaddresses", "subnetId", $subnetId); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return (int) $count; + } + + /** + * Count number of addresses in multiple subnets + * + * we provide array of all subnet ids + * + * @access public + * @param mixed $subnets + * @return void + */ + public function count_addresses_in_multiple_subnets ($subnets) { + # empty + if(empty($address)) { return 0; } + + # create query + foreach($subnets as $k=>$s) { + $tmp[] = " `subnetId`=$s "; + } + $query = "select count(*) as `cnt` from `ipaddresses` where ".implode("or", $tmp).";"; + + # fetch + try { $addresses = $this->Database->getObjectsQuery($query); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # return count + return $addresses[0]->cnt; + } + + + /** + * Fetch IP addresses for all recursive slaves + * + * count returns count only, else whole subnets + * + * @access public + * @param int $subnetId + * @param {bool} $count + * @return count or addresses + */ + public function fetch_subnet_addresses_recursive ($subnetId, $count = false, $order=null, $order_direction=null ) { + # initialize subnets + $this->initialize_subnets_object (); + $this->Subnets = new Subnets ($this->Database); + $this->Subnets->reset_subnet_slaves_recursive(); //reset array of slaves before continuing + $this->Subnets->fetch_subnet_slaves_recursive($subnetId); //fetch array of slaves + $this->Subnets->slaves = array_unique($this->Subnets->slaves); //remove possible duplicates + + # ip address order + if(!is_null($order)) { $order_addr = array($order, $order_direction); } + else { $order_addr = array("ip_addr", "asc"); } + + # set query to fetch all ip addresses for specified subnets or just count + if($count) { $query = 'select count(*) as cnt from `ipaddresses` where `subnetId` = "" '; } + else { $query = 'select * from `ipaddresses` where `subnetId` = "" '; } + foreach($this->Subnets->slaves as $subnetId2) { + # ignore orphaned + if($subnetId2 != $subnetId) { + $query .= " or `subnetId` = ? "; + $ids[] = $subnetId2; + } + } + + $query .= "order by `$order_addr[0]` $order_addr[1];"; + # fetch + try { $addresses = $this->Database->getObjectsQuery($query, $ids); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # return ip address array or just count + return $count ? (int) $addresses[0]->cnt : $addresses; + } + + /** + * Search for unused address space between 2 IP addresses + * + * @access public + * @param int $address1 + * @param int $address2 + * @param int $netmask + * @return possible unused addresses by type + */ + public function find_unused_addresses ($address1, $address2, $netmask, $empty=false) { + # make sure addresses are in decimal format + $address1 = $this->transform_address ($address1, "decimal"); + $address2 = $this->transform_address ($address2, "decimal"); + # check for space + return $this->identify_address($address1)=="IPv6" ? $this->find_unused_addresses_IPv6 ($address1, $address2, $netmask, $empty) : $this->find_unused_addresses_IPv4 ($address1, $address2, $netmask, $empty); + } + + /** + * Search for unused address space between 2 IPv4 addresses. + * + * @access protected + * @param int $address1 + * @param int $address2 + * @param int $netmask + * @return unused address range or false if none available + */ + protected function find_unused_addresses_IPv4 ($address1, $address2, $netmask, $empty) { + # calculate diff + $diff = $this->calculate_address_diff ($address1, $address2); + # 32 subnets + if($netmask==32) { + if($empty) { + return array("ip"=>$this->transform_to_dotted($address1), "hosts"=>1); + } + else { + return false; + } + } + # 31 subnets + elseif($netmask==31) { + + if($empty) { + return array("ip"=>$this->transform_to_dotted($address1), "hosts"=>2); + } + elseif($diff==1) { + if($this->is_network($address1, $netmask)) { + return array("ip"=>$this->transform_to_dotted($address2), "hosts"=>1); + } + elseif($this->is_broadcast($address2, $netmask)) { + return array("ip"=>$this->transform_to_dotted($address1), "hosts"=>1); + } + else { + return false; + } + } + else { + return false; + } + } + # if diff is less than 2 return false */ + elseif ( $diff < 2 ) { + return false; + } + # if diff is 2 return 1 IP address in the middle */ + elseif ( $diff == 2 ) { + return array("ip"=>$this->transform_to_dotted($address1+1), "hosts"=>1); + } + # if diff is more than 2 return pool */ + else { + return array("ip"=>$this->transform_to_dotted($address1+1)." - ".$this->transform_to_dotted(($address2-1)), "hosts"=>gmp_strval(gmp_sub($diff, 1))); + } + # default false + return false; + } + + /** + * Search for unused address space between 2 IPv6 addresses + * + * @access protected + * @param int $address1 + * @param int $address2 + * @param int $netmask + * @return unused address range or false if none available + */ + protected function find_unused_addresses_IPv6 ($address1, $address2, $netmask, $empty) { + # calculate diff + $diff = $this->calculate_address_diff ($address1, $address2); + + # /128 + if($netmask == 128) { + return array("ip"=>$this->transform_to_dotted(gmp_strval($address1)), "hosts"=>1); + } + # /127 + elseif($netmask == 127) { + if($diff==1 && $this->is_network($address1, $netmask)) { + return array("ip"=>$this->transform_to_dotted($address2), "hosts"=>1); + } + elseif($diff==1 && $this->is_broadcast($address2, $netmask)) { + return array("ip"=>$this->transform_to_dotted($address1), "hosts"=>1); + } + else { + return array("ip"=>$this->transform_to_dotted($address1), "hosts"=>2); + } + } + # if diff is less than 2 return false */ + elseif ($diff < 2) { + return false; + } + # if diff is 2 return 1 IP address in the middle */ + elseif ($diff == 2) { + return array("ip"=>$this->transform_to_dotted(gmp_strval($address1)), "hosts"=>1); + } + # if diff is more than 2 return pool */ + else { + return array("ip"=>$this->transform_to_dotted(gmp_strval($address1))." - ".$this->transform_to_dotted(gmp_strval($address2)), "hosts"=>$this->reformat_number(gmp_strval($diff))); + } + # default false + return false; + } + + + + + + + + + + + + /** + * @address verification methods + * ------------------------------- + */ + + /** + * Verify IP address + * + * @access public + * @param int $address + * @param mixed $subnet (CIDR) + * @param bool $no_strict (default: false) + * @param bool $die (default: false) + * @return boolean + */ + public function verify_address( $address, $subnet, $no_strict = false, $die=true ) { + # subnet should be in CIDR format + $this->initialize_subnets_object (); + if(strlen($error = $this->Subnets->verify_cidr ($subnet))>1) { $this->Result->show("danger", $error, $die); return true; } + + # make checks + return $this->identify_address ($address)=="IPv6" ? $this->verify_address_IPv6 ($address, $subnet, $die) : $this->verify_address_IPv4 ($address, $subnet, $no_strict, $die); + } + + /** + * Verify IPv4 address + * + * @access public + * @param int $address + * @param mixed $subnet (CIDR) + * @param bool $no_strict + * @param bool $die + * @return boolean + */ + public function verify_address_IPv4 ($address, $subnet, $no_strict, $die) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv4 (); + # fetch mask part + $mask = explode("/", $subnet); + + # is address valid? + if (!$this->Net_IPv4->validateIP($address)) { $this->Result->show("danger", _("IP address not valid")."! ($ip)", $die); return true; } + # is address in provided subnet + elseif (!$this->Net_IPv4->ipInNetwork($address, $subnet)) { $this->Result->show("danger", _("IP address not in selected subnet")."! ($address)", $die); return true; } + # ignore /31 and /32 subnet broadcast and subnet checks! + elseif ($mask[1] == 31 || $mask[1] == 32 || $no_strict == true) { } + # It cannot be subnet or broadcast + else { + $net = $this->Net_IPv4->parseAddress($subnet); + + if ($net->network == $address) { $this->Result->show("danger", _("Cannot add subnet as IP address!"), $die); return true; } + elseif ($net->broadcast == $address) { $this->Result->show("danger", _("Cannot add broadcast as IP address!"), $die); return true; } + } + # default + return false; + } + + /** + * Verify IPv6 address + * + * @access public + * @param int $address + * @param mixed $subnet (CIDR) + * @param bool $die + * @return boolean + */ + public function verify_address_IPv6 ($address, $subnet, $die) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv6 (); + + # remove /xx from subnet + $subnet_short = $this->Net_IPv6->removeNetmaskSpec($subnet); + + # is it valid? + if (!$this->Net_IPv6->checkIPv6($address)) { $this->Result->show("danger", _("IP address not valid")."! ($address)", $die); return true; } + # it must be in provided subnet + elseif (!$this->Net_IPv6->isInNetmask($address, $subnet)) { $this->Result->show("danger", _("IP address not in selected subnet")."! ($address)", $die); return true; } + # default + return false; + } + + /** + * Checks if address is subnet for IPv4 addresses + * + * @access public + * @param mixed $address + * @param int $mask + * @return boolean + */ + public function is_network ($address, $netmask) { + $this->initialize_subnets_object (); + $boundaries = $this->Subnets->get_network_boundaries ($address, $netmask); + return $this->transform_address($address,"dotted")==$boundaries['network'] ? true : false; + } + + /** + * Checks if address is broadcast for IPv4 addresses + * + * @access public + * @param mixed $address + * @param int $mask + * @return boolean + */ + public function is_broadcast ($address, $netmask) { + $this->initialize_subnets_object (); + $boundaries = $this->Subnets->get_network_boundaries ($address, $netmask); + return $this->transform_address($address,"dotted")==$boundaries['broadcast'] ? true : false; + } + + /** + * Checks if hostname in database is unique + * + * @access public + * @param mixed $hostname + * @return boolean + */ + public function is_hostname_unique ($hostname) { + try { $cnt = $this->Database->numObjectsFilter("ipaddresses", "dns_name", $hostname); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + return $cnt==0 ? true : false; + } + + + + + + + + + + + + + + /** + * @transform address methods + * ------------------------------- + */ + + /** + * identify ip address type - ipv4 or ipv6 + * + * @access public + * @param mixed $address + * @return mixed IP version + */ + public function identify_address ($address) { + # dotted representation + if (strpos($address, ":")) { return 'IPv6'; } + elseif (strpos($address, ".")) { return 'IPv4'; } + # decimal representation + else { + # IPv4 address + if(strlen($address) < 12) { return 'IPv4'; } + # IPv6 address + else { return 'IPv6'; } + } + } + + /** + * Identifies IP address format + * + * 0 = decimal + * 1 = dotted + * + * @access public + * @param mixed $address + * @return mixed decimal or dotted + */ + private function identify_address_format ($address) { + return is_numeric($address) ? "decimal" : "dotted"; + } + + /** + * Transforms IP address to required format + * + * format can be decimal (1678323323) or dotted (10.10.0.0) + * + * @access public + * @param mixed $address + * @param string $format (default: "dotted") + * @return mixed requested format + */ + public function transform_address ($address, $format = "dotted") { + # no change + if($this->identify_address_format ($address) == $format) { return $address; } + else { + if($this->identify_address_format ($address) == "dotted") { return $this->transform_to_decimal ($address); } + else { return $this->transform_to_dotted ($address); } + } + } + + /** + * Transform IP address from decimal to dotted (167903488 -> 10.2.1.0) + * + * @access public + * @param int $address + * @return mixed dotted format + */ + public function transform_to_dotted ($address) { + if ($this->identify_address ($address) == "IPv4" ) { return(long2ip($address)); } + else { return(long2ip6($address)); } + } + + /** + * Transform IP address from dotted to decimal (10.2.1.0 -> 167903488) + * + * @access public + * @param mixed $address + * @return int IP address + */ + public function transform_to_decimal ($address) { + if ($this->identify_address ($address) == "IPv4" ) { return( sprintf("%u", ip2long($address)) ); } + else { return(ip2long6($address)); } + } + + /** + * This function compresses all ranges + * + * input is array of ip addresses + * output compresses address range + * + * @access public + * @param array $addresses + * @return void + */ + public function compress_address_ranges ($addresses, $state=4) { + # loop through IP addresses + for($c=0; $cclass!="compressed-range") { + # gap between this and previous + if(gmp_strval( @gmp_sub($addresses[$c]->ip_addr, $addresses[$c-1]->ip_addr)) != 1) { + # remove index flag + unset($fIndex); + # save IP address + $addresses_formatted[$c] = $addresses[$c]; + $addresses_formatted[$c]->class = "ip"; + + # no gap this -> next + if(gmp_strval( @gmp_sub($addresses[$c]->ip_addr, $addresses[$c+1]->ip_addr)) == -1 && $addresses[$c]->state==$state) { + //is state the same? + if($addresses[$c]->state==$addresses[$c+1]->state) { + $fIndex = $c; + $addresses_formatted[$fIndex]->startIP = $addresses[$c]->ip_addr; + $addresses_formatted[$c]->class = "compressed-range"; + } + } + } + # no gap between this and previous + else { + # is state same as previous? + if($addresses[$c]->state==$addresses[$c-1]->state && $addresses[$c]->state==$state) { + $addresses_formatted[$fIndex]->stopIP = $addresses[$c]->ip_addr; //adds dhcp state + $addresses_formatted[$fIndex]->numHosts = gmp_strval( gmp_add(@gmp_sub($addresses[$c]->ip_addr, $addresses_formatted[$fIndex]->ip_addr),1)); //add number of hosts + } + # different state + else { + # remove index flag + unset($fIndex); + # save IP address + $addresses_formatted[$c] = $addresses[$c]; + $addresses_formatted[$c]->class = "ip"; + # check if state is same as next to start range + if($addresses[$c]->state==@$addresses[$c+1]->state && gmp_strval( @gmp_sub($addresses[$c]->ip_addr, $addresses[$c+1]->ip_addr)) == -1 && $addresses[$c]->state==$state) { + $fIndex = $c; + $addresses_formatted[$fIndex]->startIP = $addresses[$c]->ip_addr; + $addresses_formatted[$c]->class = "compressed-range"; + } + } + } + } + else { + # save already compressed + $addresses_formatted[$c] = $addresses[$c]; + } + } + # overrwrite ipaddresses and rekey + $addresses = @array_values($addresses_formatted); + # return + return $addresses; + } + + /** + * Changes empty array fields to specified character + * + * @access public + * @param array $fields + * @param string $char (default: "/") + * @return array + */ + public function reformat_empty_array_fields ($fields, $char = "/") { + foreach($fields as $k=>$v) { + if(is_null($v) || strlen($v)==0) { + $out[$k] = $char; + } else { + $out[$k] = $v; + } + } + # result + return $out; + } + + + + + + + + + + + + /** + * @permission address methods + * ------------------------------- + */ + + /** + * Checks permission for specified subnet + * + * we provide user details and subnetId + * + * @access public + * @param object $user + * @param int $subnetid + * @return int permission level + */ + public function check_permission ($user, $subnetid) { + + # get all user groups + $groups = json_decode($user->groups); + + # if user is admin then return 3, otherwise check + if($user->role == "Administrator") { return 3; } + + # set subnet permissions + $subnet = $this->fetch_subnet ("id", $subnetId); + $subnetP = json_decode($subnet['permissions']); + + # set section permissions + $Section = new Section ($this->Database); + $section = $Section->fetch_section ("id", $subnet['sectionId']); + $sectionP = json_decode($section->permissions); + + # default permission + $out = 0; + + # for each group check permissions, save highest to $out + if(sizeof($sectionP) > 0) { + foreach($sectionP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + else { + return 0; + } + + # if section permission == 0 then return 0 + if($out == 0) { + return 0; + } + else { + $out = 0; + # ok, user has section access, check also for any higher access from subnet + if(sizeof($subnetP) > 0) { + foreach($subnetP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + } + + # return result + return $out; + } + + + + + + + + + + + /** + * @misc address methods + * ------------------------------- + */ + + /** + * Prints pagination if subnet has multiple pages of IP addresses + * + * @access public + * @param int $page //current page number + * @param int $pages //number of all subpages + * @return mixed + */ + public function print_pagination ($page, $pages) { + + print "
    "; + print "
    "; + print "
      "; + + //previous - disabled? + if($page == 1) { print "
    • «
    • "; } + else { print "
    • «
    • "; } + + # less than 8 + if($pages<8) { + for($m=1; $m<=$pages; $m++) { + //active? + if($page==$m) { print "
    • $m
    • "; } + else { print "
    • $m
    • "; } + } + } + # more than seven + else { + //first page + if($page<=3) { + for($m=1; $m<=5; $m++) { + //active? + if($page==$m) { print "
    • $m
    • "; } + else { print "
    • $m
    • "; } + } + print "
    • ...
    • "; + print "
    • $pages
    • "; + } + //last pages + elseif($page>$pages-4) { + print "
    • 1
    • "; + print "
    • ...
    • "; + for($m=$pages-4; $m<=$pages; $m++) { + //active? + if($page==$m) { print "
    • $m
    • "; } + else { print "
    • $m
    • "; } + } + } + //page more than 2 + else { + print "
    • 1
    • "; + print "
    • ...
    • "; + for($m=$page-1; $m<=$page+1; $m++) { + //active? + if($page==$m) { print "
    • $m
    • "; } + else { print "
    • $m
    • "; } + } + print "
    • ...
    • "; + print "
    • $pages
    • "; + } + } + + //next - disabled? + if($page == $pages) { print "
    • »
    • "; } + else { print "
    • »
    • "; } + + print "
    "; + print "
    "; + } + + /** + * Present numbers in pow 10, only for IPv6 + * + * @access public + * @param mixed $number + * @return void + */ + public function reformat_number ($number) { + $length = strlen($number); + $pos = $length - 3; + + if ($length > 8) { + $number = "~". substr($number, 0, $length - $pos) . "·10^". $pos .""; + } + return $number; + } +} \ No newline at end of file diff --git a/functions/classes/class.Admin.php b/functions/classes/class.Admin.php new file mode 100644 index 000000000..f12202bb3 --- /dev/null +++ b/functions/classes/class.Admin.php @@ -0,0 +1,934 @@ +Database = $database; + # initialize Result + $this->Result = new Result (); + # set debugging + $this->set_debugging (); + # set admin flag + $this->set_admin_required ($admin_required); + # verify that user is admin + $this->is_admin (); + } + + /** + * sets debugging if set in config.php file + * + * @access private + * @return void + */ + private function set_debugging () { + require( dirname(__FILE__) . '/../../config.php' ); + if($debugging==true) { $this->debugging = true; } + } + + /** + * Sets admin required flag if needed + * + * @access public + * @param mixed $bool + * @return void + */ + public function set_admin_required ($bool) { + $this->admin_required = is_bool($bool) ? $bool : true; + } + + /** + * Strip tags from array or field to protect from XSS + * + * @access public + * @param mixed $input + * @return void + */ + public function strip_input_tags ($input) { + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = strip_tags($v); } + } + else { + $input = strip_tags($input); + } + # stripped + return $input; + } + + /** + * Changes empty array fields to specified character + * + * @access public + * @param array $fields + * @param string $char (default: "/") + * @return array + */ + public function reformat_empty_array_fields ($fields, $char = "/") { + foreach($fields as $k=>$v) { + if(is_null($v) || strlen($v)==0) { + $out[$k] = $char; + } else { + $out[$k] = $v; + } + } + # result + return $out; + } + + /** + * Function to verify checkbox if 0 length + * + * @access public + * @param mixed $field + * @return void + */ + public function verify_checkbox ($field=0) { + return $field==""||$field=="0" ? 0 : 1; + } + + /** + * Checks if current user is admin + * + * @access public + * @return void + */ + public function is_admin () { + # initialize user class + $this->User = new User ($this->Database); + # save settings + $this->settings = $this->User->settings; + # if required die ! + if($this->User->isadmin!==true && $this->admin_required==true) { + // popup ? + if(@$_SERVER['HTTP_X_REQUESTED_WITH'] == "XMLHttpRequest") { $this->Result->show("danger", _("Administrative privileges required"),true, true); } + else { $this->Result->show("danger", _("Administrative privileges required"),true); } + } + } + + + + + + + + + + + + + + + + /** + * @general fetch methods + * -------------------------------- + */ + + /** + * Fetch all objects from specified table in database + * + * @access public + * @param mixed $table + * @param mixed $sortField (default:id) + * @return void + */ + public function fetch_all_objects ($table=null, $sortField="id") { + # null table + if(is_null($table)||strlen($table)==0) return false; + # fetch + try { $res = $this->Database->getObjects($table, $sortField); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return sizeof($res)>0 ? $res : false; + } + + /** + * Fetches specified object specified table in database + * + * @access public + * @param mixed $table + * @param mixed $method (default: null) + * @param mixed $id + * @return void + */ + public function fetch_object ($table=null, $method=null, $id) { + # null table + if(is_null($table)||strlen($table)==0) return false; + # null method + $method = is_null($method) ? "id" : $this->Database->escape($method); + + # ignore 0 + if($id===0 || is_null($id)) { + return false; + } + # check cache + elseif(isset($this->table[$table][$method][$id])) { + return $this->table[$table][$method][$id]; + } + else { + try { $res = $this->Database->getObjectQuery("SELECT * from `$table` where `$method` = ? limit 1;", array($id)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to cache array + if(sizeof($res)>0) { + $this->table[$table][$method][$id] = (object) $res; + return $res; + } + else { + return false; + } + } + } + + /** + * Fetches multiple objects in specified table in database + * + * doesnt cache + * + * @access public + * @param mixed $table (default: null) + * @param mixed $method (default: null) + * @param mixed $id + * @return void + */ + public function fetch_multiple_objects ($table, $field, $value, $sortField = 'id', $sortAsc = true) { + # null table + if(is_null($table)||strlen($table)==0) return false; + else { + try { $res = $this->Database->findObjects($table, $field, $value, $sortField, $sortAsc); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return sizeof($res)>0 ? $res : false; + } + } + + /** + * Empties table + * + * @access public + * @param mixed $table (default: null) + * @return void + */ + public function truncate_table ($table = null) { + # null table + if(is_null($table)||strlen($table)==0) return false; + else { + try { $res = $this->Database->emptyTable($table); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return true; + } + } + + + + + + + + + + + + + + /** + * @general update methods + * -------------------------------- + */ + + /** + * Modify database object + * + * @access public + * @param mixed $table + * @param mixed $action + * @param mixed $values + * @return void + */ + public function object_modify ($table, $action=null, $field="id", $values) { + # strip tags + $values = $this->strip_input_tags ($values); + + # execute based on action + if($action=="add") { return $this->object_add ($table, $values); } + elseif($action=="edit") { return $this->object_edit ($table, $field, $values); } + elseif($action=="edit-multiple") { return $this->object_edit_multiple ($table, $field, $values); } //$field = array of ids + elseif($action=="delete") { return $this->object_delete ($table, $field, $values[$field]); } + else { return $this->Result->show("danger", _("Invalid action"), true); } + } + + /** + * Create new database object + * + * $values are all values that should be passed to create object + * + * @access private + * @param mixed $table + * @param mixed $values + * @return void + */ + private function object_add ($table, $values) { + # null empty values + $values = $this->reformat_empty_array_fields ($values, null); + + # execute + try { $this->Database->insertObject($table, $values); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + write_log( "$table object creation", "Failed to create new $table database object
    ".$e->getMessage()."
    ".array_to_log($values), 2, $this->User->username); + return false; + } + # save ID + $this->save_last_insert_id (); + # ok + write_log( "$table object creation", "New $table database object created
    ".array_to_log($values), 0, $this->User->username); + return true; + } + + /** + * Edit object in table by specified object id + * + * $values are all values that should be passed to edit object, + * id will be used to match field to update. + * + * @access private + * @param mixed $table //name of table to update + * @param array $values //update variables + * @param mixed $primary_key //key to update on + * @return void + */ + private function object_edit ($table, $key="id", $values) { + # null empty values + $values = $this->reformat_empty_array_fields ($values, null); + + # execute + try { $this->Database->updateObject($table, $values, $key); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + write_log( "$table object $values[$key] edit", "Failed to edit object $key=$values[$key] in $table
    ".$e->getMessage()."
    ".array_to_log($values), 2, $this->User->username); + return false; + } + # save ID + $this->save_last_insert_id (); + # ok + write_log( "$table object $values[$key] edit", "Object $key=$values[$key] in $table edited
    ".array_to_log($values), 0, $this->User->username); + return true; + } + + /** + * Edit multiple objects in table by specified object id + * + * $values are all values that should be passed to edit object, + * ids will be used to match fields to update. + * + * @access private + * @param mixed $table //name of table to update + * @param array $values //update variables + * @param mixed $primary_key //key to update on + * @return void + */ + private function object_edit_multiple ($table, $ids, $values) { + # null empty values + $values = $this->reformat_empty_array_fields ($values, null); + + # execute + try { $this->Database->updateMultipleObjects($table, $ids, $values); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + write_log( "$table multiple objects edit", "Failed to edit multiple objects in $table
    ".$e->getMessage()."
    ".array_to_log($ids)."
    ".array_to_log($values), 2, $this->User->username); + return false; + } + # save ID + $this->save_last_insert_id (); + # ok + write_log( "$table multiple objects edit", "Multiple objects in $table edited
    ".array_to_log($ids)."
    ".array_to_log($values), 0, $this->User->username); + return true; + } + + /** + * Delete object in table by specified object id + * + * @access private + * @param mixed $table //table to update + * @param mixed $field //field selection (where $field = $id) + * @param mixed $id //field identifier + * @return void + */ + private function object_delete ($table, $field="id", $id) { + # execute + try { $this->Database->deleteRow($table, $field, $id); } + catch (Exception $e) { + write_log( "$table object $values[$id] delete", "Failed to delete object $field=$id in $table
    ".$e->getMessage(), 2, $this->User->username); + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + # save ID + $this->save_last_insert_id (); + # ok + write_log( "$table object $id edit", "Object $field=$id in $table deleted.", 0, $this->User->username); + return true; + } + + /** + * Removes or replaces all old object references + * + * @access public + * @param mixed $table + * @param mixed $field + * @param mixed $old_value + * @param mixed $new_value + * @return void + */ + public function remove_object_references ($table, $field, $old_value) { + try { $this->Database->runQuery("update `$table` set `$field` = NULL where `$field` = ?;", array($old_value)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + } + + /** + * Resets or replaces all old object references + * + * @access public + * @param mixed $table + * @param mixed $field + * @param mixed $old_value + * @param mixed $new_value + * @return void + */ + public function update_object_references ($table, $field, $old_value, $new_value) { + try { $this->Database->runQuery("update `$table` set `$field` = ? where `$field` = ?;", array($new_value, $old_value)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + } + + /** + * Saves last insert ID on object modification. + * + * @access public + * @return void + */ + public function save_last_insert_id () { + $this->lastId = $this->Database->lastInsertId(); + } + + + + + + + + + + + + + /** + * @group methods + * -------------------------------- + */ + + /** + * Parse user groups + * + * input: array of group ids + * output: array of groups ( "id"=>array($group) ) + * + * @access public + * @param mixed $group_ids + * @return void + */ + public function groups_parse ($group_ids) { + if(sizeof($group_ids)>0) { + foreach($group_ids as $g_id) { + $group = $this->fetch_object ("userGroups", "g_id", $g_id); + $out[$group->g_id] = (array) $group; + } + } + # return array of groups + return isset($out) ? $out : array(); + } + + + /** + * Parse user groups + * + * input: array of group ids + * output: array of ids ( "id"=>id ) + * + * @access public + * @param mixed $group_ids + * @return void + */ + public function groups_parse_ids ($group_ids) { + if(sizeof($group_ids) >0) { + foreach($group_ids as $g_id) { + $group = $this->fetch_object ("userGroups", "g_id", $g_id); + $out[$group->g_id] = $group->g_id; + } + } + # return array of group ids + return isset($out) ? $out : array(); + } + + /** + * Fetches all users that are in group + * + * @access public + * @param int $gid + * @param bool $reverse + * @return array of user ids + */ + public function group_fetch_users ($group_id) { + # get all users + $users = $this->fetch_all_objects("users"); + # check if $gid in array + foreach($users as $u) { + $group_array = json_decode($u->groups, true); + $group_array = $this->groups_parse($group_array); + + if(sizeof($group_array)>0) { + foreach($group_array as $group) { + if(in_array($group_id, $group)) { + $out[] = $u->id; + } + } + } + } + # return + return isset($out) ? $out : array(); + } + + /** + * Fetches all users that are not admins and are not in group + * + * @access public + * @param mixed $group_id + * @return void + */ + public function group_fetch_missing_users ($group_id) { + # get all users + $users = $this->fetch_all_objects("users"); + + # check if $gid in array + foreach($users as $u) { + if($u->role != "Administrator") { + $g = json_decode($u->groups, true); + if(!@in_array($group_id, $g)) { $out[] = $u->id; } + } + } + # return + return $out; + } + + /** + * This function adds new group access to user account + * + * @access private + * @param mixed $gid + * @param mixed $uid + * @return void + */ + public function add_group_to_user ($gid, $uid) { + # get old groups + $user = $this->fetch_object ("users", "id", $uid); + + # append new group + $g = json_decode($user->groups, true); + $g[$gid] = $gid; + $g = json_encode($g); + + # update + if(!$this->update_user_groups($uid, $g)) { return false; } + else { return true; } + } + + /** + * This function removes group from users account. + * + * @access public + * @param mixed $gid + * @param mixed $uid + * @return void + */ + public function remove_group_from_user($gid, $uid) { + # get old groups + $user = $this->fetch_object ("users", "id", $uid); + + # remove group + $g = json_decode($user->groups, true); + unset($g[$gid]); + $g = json_encode($g); + + # update + if(!$this->update_user_groups($uid, $g)) { return false; } + else { return true; } + } + + + /** + * Update groups for specified user + * + * @access public + * @param mixed $uid + * @param mixed $groups + * @return void + */ + public function update_user_groups ($uid, $groups) { + return $this->object_modify ("users", "edit", "id", array("id"=>$uid, "groups"=>$groups)); + } + + /** + * Update group permissions for section + * + * @access public + * @param mixed $sid + * @param mixed $groups + * @return void + */ + public function update_section_groups($sid, $groups) { + return $this->object_modify ("sections", "edit", "id", array("id"=>$sid, "permissions"=>$groups)); + } + + /** + * Removes all users from specified group on group delete + * + * @access public + * @param int $gid //group id + * @return void + */ + public function remove_group_from_users($gid) { + # get all users + $users = $this->fetch_all_objects("users"); + # check if $gid in array + foreach($users as $u) { + $g = json_decode($u->groups, true); + $go = $g; + $g = $this->groups_parse($g); + # check + if(sizeof($g)>0) { + foreach($g as $gr) { + if(in_array($gid, $gr)) { + unset($go[$gid]); + $ng = json_encode($go); + $this->update_user_groups($u->id,$ng); + } + } + } + } + return true; + } + + /** + * Removes group ID from all section permissions + * + * @access public + * @param mixed $gid + * @return void + */ + public function remove_group_from_sections ($gid) { + # get all sections + $sections = $this->fetch_all_objects ("sections", "id"); + # check if $gid in array + foreach($sections as $s) { + $g = json_decode($s->permissions, true); + + if(sizeof($g)>0) { + if(array_key_exists($gid, $g)) { + unset($g[$gid]); + $ng = json_encode($g); + $this->update_section_groups($s->id,$ng); + } + } + } + return true; + } + + + + + + + + + + + + + + /** + * @ripe method + * -------------------------------- + */ + + public function ripe_fetch_subnets ($as) { + //open connection + $ripe_connection = fsockopen("whois.ripe.net", 43, $errno, $errstr, 5); + if(!$ripe_connection) { + $this->Result->show("danger", "$errstr ($errno)", false); + return false; + } + else { + //fetch result + fputs ($ripe_connection, '-i origin as'. $as ."\r\n"); + //save result to var out + $out = ""; + while (!feof($ripe_connection)) { $out .= fgets($ripe_connection); } + + //parse it + $out = explode("\n", $out); + + //we only need route + foreach($out as $line) { + if (strlen(strstr($line,"route"))>0) { + //replace route6 with route + $line = str_replace("route6:", "route:", $line); + //only take IP address + $line = explode("route:", $line); + $line = trim($line[1]); + //set result + $subnet[] = $line; + } + } + //return + return isset($subnet) ? $subnet : array(); + } + } + + + + + + + + + + + + + /** + * @search/replace fields + * -------------------------------- + */ + + /** + * Replace fields + * + * @access public + * @param mixed $field + * @param mixed $search + * @param mixed $replace + * @return void + */ + public function replace_fields ($field, $search, $replace) { + # check number of items + $count = $this->count_records ($field, $search, $replace); + # if some exist update + if($count>0) { + # update + try { $cnt = $this->Database->runQuery("update `ipaddresses` set `$field` = replace(`$field`, '$search', ?);", array($replace)); } + catch (Exception $e) { + $this->Result->show("danger alert-absolute", _("Error: ").$e->getMessage(), true); + } + # ok, print count + $this->Result->show("success alert-absolute", _('Replaced').' '. $count .' '._('items successfully').'!', false); + } + else { + $this->Result->show("info alert-absolute", _("No records found to replace"), false); + } + } + + /** + * Count number of records in database + * + * @access private + * @param mixed $field + * @param mixed $search + * @param mixed $replace + * @return void + */ + private function count_records ($field, $search, $replace) { + try { $cnt = $this->Database->numObjectsFilter("ipaddresses", $field, $search); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), true); + } + # ok + return $cnt; + } + + + + + + + + + + + + + + /** + * @custom field methods + * -------------------------------- + */ + + /** + * Updates custom field definition + * + * @access public + * @param array $field + * @return bool + */ + public function update_custom_field_definition ($field) { + + # set type definition and size of needed + if($field['fieldType']=="bool" || $field['fieldType']=="text" || $field['fieldType']=="date" || $field['fieldType']=="datetime") { $field['ftype'] = $field['fieldType']; } + else { $field['ftype'] = $field['fieldType']."(".$field['fieldSize'].")"; } + + # default value null + if(strlen($field['fieldDefault'])==0) { $field['fieldDefault'] = "NULL"; } + else { $field['fieldDefault'] = "'$field[fieldDefault]'"; } + + # character set if needed + if($field['fieldType']=="varchar" || $field['fieldType']=="text" || $field['fieldType']=="set") { $charset = "CHARACTER SET utf8"; } + else { $charset = ""; } + + # escape fields + $field['table'] = $this->Database->escape($field['table']); + $field['name'] = $this->Database->escape($field['name']); + $field['oldname'] = $this->Database->escape($field['oldname']); + # strip values + $field['action'] = $this->strip_input_tags($field['action']); + $field['Comment'] = $this->strip_input_tags($field['Comment']); + + # set update query + if($field['action']=="delete") { $query = "ALTER TABLE `$field[table]` DROP `$field[name]`;"; } + else if ($field['action']=="edit"&&@$field['NULL']=="NO") { $query = "ALTER IGNORE TABLE `$field[table]` CHANGE COLUMN `$field[oldname]` `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] NOT NULL COMMENT '$field[Comment]';"; } + else if ($field['action']=="edit") { $query = "ALTER TABLE `$field[table]` CHANGE COLUMN `$field[oldname]` `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] COMMENT '$field[Comment]';"; } + else if ($field['action']=="add"&&@$field['NULL']=="NO") { $query = "ALTER TABLE `$field[table]` ADD COLUMN `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] NOT NULL COMMENT '$field[Comment]';"; } + else if ($field['action']=="add") { $query = "ALTER TABLE `$field[table]` ADD COLUMN `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] NULL COMMENT '$field[Comment]';"; } + else { + return false; + } + + # execute + try { $res = $this->Database->runQuery($query, array(@$field['Comment'])); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + write_log( "Custom field $field[action]", "Custom Field $field[action] failed ($field[name])
    ".array_to_log($field), 2, $this->User->username); + return false; + } + # field updated + write_log( "Custom field $field[action]", "Custom Field $field[action] success ($field[name])
    ".array_to_log($field), 0, $this->User->username); + return true; + } + + /** + * Save custom fields that should be hidden from normal display + * + * @access public + * @param mixed $table //name of custom fields table + * @param mixed $filtered_fields //array of field to hide for this table + * @return void + */ + public function save_custom_fields_filter ($table, $filtered_fields) { + # old custom fields, save them to array + $hidden_array = strlen($this->settings->hiddenCustomFields)>0 ? json_decode($this->settings->hiddenCustomFields, true) : array(); + + # set new array for table + if(is_null($filtered_fields)) { unset($hidden_array[$table]); } + else { $hidden_array[$table]=$filtered_fields; } + + # encode to json + $hidden_json = json_encode($hidden_array); + + # update database + try { $this->object_edit ("settings", $key="id", array("id"=>1,"hiddenCustomFields"=>$hidden_json)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), true); + } + # ok + return true; + } + + /** + * Reorders custom fields + * + * @access public + * @param mixed $table + * @param mixed $next + * @param mixed $current + * @return void + */ + public function reorder_custom_fields ($table, $next, $current) { + # get current field details + $Tools = new Tools ($this->Database); + $old = (array) $Tools->fetch_full_field_definition ($table, $current); + + # set update request + if($old['Null']=="NO") { $query = 'ALTER TABLE `'.$table.'` MODIFY COLUMN `'. $current .'` '.$old['Type'].' NOT NULL COMMENT "'.$old['Comment'].'" AFTER `'. $next .'`;'; } + else { $query = 'ALTER TABLE `'.$table.'` MODIFY COLUMN `'. $current .'` '.$old['Type'].' DEFAULT NULL COMMENT "'.$old['Comment'].'" AFTER `'. $next .'`;'; } + + # execute + try { $res = $this->Database->runQuery($query); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + # ok + return true; + } + +} \ No newline at end of file diff --git a/functions/classes/class.DNS.php b/functions/classes/class.DNS.php new file mode 100644 index 000000000..c625ec3e2 --- /dev/null +++ b/functions/classes/class.DNS.php @@ -0,0 +1,103 @@ +Result = new Result (); + # initialize object + $this->Database = $Database; + # settings + if(!is_null($settings)) { $this->settings = $settings; } + } + + + /** + * fetches settings from database + * + * @access private + * @return void + */ + private function get_settings () { + # cache check + if($this->settings == false) { + try { $this->settings = $this->Database->getObject("settings", 1); } + catch (Exception $e) { $this->Result->show("danger", _("Database error: ").$e->getMessage()); } + } + } + + + + + + + + + + + /** + * @resolve address methods + * ------------------------------- + */ + + + /** + * Resolves hostname + * + * @access public + * @param mixed $address address object + * @param boolena $override override DNS resolving flag + * @return void + */ + public function resolve_address ($address, $override=false) { + # settings + $this->get_settings(); + # addresses object + $Address = new Addresses ($this->Database); + # make sure it is dotted format + $address->ip = $Address->transform_address ($address->ip_addr, "dotted"); + # if dns_nameis set try to check + if(empty($address->dns_name) || is_null($address->dns_name)) { + # if permitted in settings + if($this->settings->enableDNSresolving == 1 || $override) { + # resolve + $resolved = gethostbyaddr($address->ip); + if($resolved==$address->ip) $resolved=""; //resolve fails + + return array("class"=>"resolved", "name"=>$resolved); + } + else { + return array("class"=>"", "name"=>""); + } + } + else { + return array("class"=>"", "name"=>$address->dns_name); + } + } + + + +} \ No newline at end of file diff --git a/functions/classes/class.Install.php b/functions/classes/class.Install.php new file mode 100644 index 000000000..e30c1ef23 --- /dev/null +++ b/functions/classes/class.Install.php @@ -0,0 +1,419 @@ +Result = new Result (); + # initialize object + $this->Database = $Database; + # set debugging + $this->set_debugging (); + # set debugging + $this->set_db_params (); + } + + /** + * Fetch settings from database + * + * @access private + * @return void + */ + private function fetch_settings () { + # check if already set + if(sizeof($this->settings)>0) { + return $this->settings; + } + # fetch + else { + try { $this->settings = $this->Database->getObject("settings", 1); } + catch (Exception $e) { $this->Result->show("danger", _("Database error: ").$e->getMessage()); } + } + } + + + + + + + + + + /** + * @install methods + * ------------------------------ + */ + + /** + * Install database files + * + * @access public + * @param mixed $rootuser + * @param mixed $rootpass + * @param bool $drop_database (default: false) + * @param bool $create_database (default: false) + * @param bool $create_grants (default: false) + * @return void + */ + public function install_database ($rootuser, $rootpass, $drop_database = false, $create_database = false, $create_grants = false) { + + # open new connection + $this->Database_root = new Database_PDO ($rootuser, $rootpass); + + # set install flag to make sure DB is not trying to be selected via DSN + $this->Database_root->install = true; + + # drop database if requested + if($drop_database===true) { $this->drop_database(); } + + # create database if requested + if($create_database===true) { $this->create_database(); } + + # set permissions! + if($create_grants===true) { $this->create_grants(); } + + # reset connection, reset install flag and connect again + $this->Database_root->resetConn(); + + # install database + $this->install_database_execute (); + + # return true, if some errors occured script already died! */ + sleep(1); + write_log( "Database installation", "Database installed successfully. Version ".VERSION.".".REVISION." installed", 1 ); + return true; + } + + /** + * Drop existing database + * + * @access private + * @return void + */ + private function drop_database () { + # set query + $query = "drop database if exists `". $this->db['name'] ."`;"; + # execute + try { $this->Database_root->runQuery($query); } + catch (Exception $e) { $this->Result->show("danger", $e->getMessage(), true);} + } + + /** + * Create database + * + * @access private + * @return void + */ + private function create_database () { + # set query + $query = "create database `". $this->db['name'] ."`;"; + # execute + try { $this->Database_root->runQuery($query); } + catch (Exception $e) { $this->Result->show("danger", $e->getMessage(), true);} + } + + /** + * Create user grants + * + * @access private + * @return void + */ + private function create_grants () { + # set query + $query = 'grant ALL on '. $this->db['name'] .'.* to '. $this->db['user'] .'@localhost identified by "'. $this->db['pass'] .'";'; + # execute + try { $this->Database_root->runQuery($query); } + catch (Exception $e) { $this->Result->show("danger", $e->getMessage(), true);} + } + + /** + * Execute files installation + * + * @access private + * @return void + */ + private function install_database_execute () { + # import SCHEMA file queries + $query = file_get_contents("../../db/SCHEMA.sql"); + + # formulate queries + $queries = explode(";\n", $query); + + # execute + foreach($queries as $q) { + try { $this->Database_root->runQuery($q.";"); } + catch (Exception $e) { + //unlock tables + $this->Database_root->runQuery("UNLOCK TABLES;"); + //drop database + try { $this->Database_root->runQuery("drop database if exists ". $this->db['name'] .";"); } + catch (Exception $e) { + $this->Result->show("danger", 'Cannot set permissions for user '. $db['user'] .': '.$e->getMessage(), true); + } + //print error + $this->Result->show("danger", "Cannot install sql SCHEMA file: ".$e->getMessage()."
    query that failed:
    $q
    ", false); + $this->Result->show("info", "Database dropped", false); + } + } + } + + + + + + + + + + + /** + * @check methods + * ------------------------------ + */ + + /** + * Tries to connect to database + * + * @access public + * @param bool $redirect + * @return void + */ + public function check_db_connection ($redirect = false) { + # try to connect + try { $res = $this->Database->connect(); } + catch (Exception $e) { + $this->exception = $e->getMessage(); + # redirect ? + if($redirect == true) { $this->redirect_to_install (); } + else { return false; } + } + # ok + return true; + } + + /** + * Checks if table exists + * + * @access public + * @param mixed $table + * @return void + */ + public function check_table ($table, $redirect = false) { + # set query + $query = "SELECT COUNT(*) AS `cnt` FROM information_schema.tables WHERE table_schema = '".$this->db['name']."' AND table_name = '$table';"; + # try to fetch count + try { $table = $this->Database->getObjectQuery($query); } + catch (Exception $e) { if($redirect === true) $this->redirect_to_install (); else return false; } + # redirect if it is not existing + if($table->cnt!=1) { if($redirect === true) $this->redirect_to_install (); else return false; } + # ok + return true; + } + + /** + * This function redirects to install page + * + * @access private + * @return void + */ + private function redirect_to_install () { + # redirect to install + header("Location: ".BASE.create_link("install", null,null,null,null,true)); + } + + /** + * sets debugging if set in config.php file + * + * @access private + * @return void + */ + private function set_debugging () { + require( dirname(__FILE__) . '/../../config.php' ); + if($debugging==true) { $this->debugging = true; } + } + + /** + * Sets DB parmaeters + * + * @access private + * @return void + */ + private function set_db_params () { + require( dirname(__FILE__) . '/../../config.php' ); + $this->db = $db; + } + + + + + + + + + + /** + * @postinstallation functions + * ------------------------------ + */ + + /** + * Post installation settings update. + * + * @access public + * @param mixed $adminpass + * @param mixed $siteTitle + * @param mixed $siteURL + * @return void + */ + function postauth_update($adminpass, $siteTitle, $siteURL) { + # update Admin pass + $this->postauth_update_admin_pass ($adminpass); + # update settings + $this->postauth_update_settings ($siteTitle, $siteURL); + # ok + return true; + } + + /** + * Updates admin password after installation + * + * @access public + * @param mixed $adminpass + * @return void + */ + public function postauth_update_admin_pass ($adminpass) { + try { $this->Database->updateObject("users", array("password"=>$adminpass, "passChange"=>"No","username"=>"Admin"), "username"); } + catch (Exception $e) { $this->Result->show("danger", $e->getMessage(), false); } + return true; + } + + /** + * Updates settings after installation + * + * @access private + * @param mixed $siteTitle + * @param mixed $siteURL + * @return void + */ + private function postauth_update_settings ($siteTitle, $siteURL) { + try { $this->Database->updateObject("settings", array("siteTitle"=>$siteTitle, "siteURL"=>$siteURL,"id"=>1), "id"); } + catch (Exception $e) { $this->Result->show("danger", $e->getMessage(), false); } + return true; + } + + + + + + + + + + + /** + * @upgrade database + * ----------------- + */ + + /** + * Upgrade database checks and executes. + * + * @access public + * @return void + */ + public function upgrade_database () { + # first check version + $this->fetch_settings (); + + if($this->settings->version == VERSION) { $this->Result->show("danger", "Database already at latest version", true); } + else { + # check db connection + if($this->check_db_connection(false)===false) { $this->Result->show("danger", "Cannot connect to database", true); } + # execute + else { + return $this->upgrade_database_execute (); + } + } + } + + /** + * Execute database upgrade. + * + * @access private + * @return void + */ + private function upgrade_database_execute () { + # set queries + $queries = $this->get_upgrade_queries (); + + # execute all queries + foreach($queries as $query) { + try { $this->Database->runQuery($query); } + catch (Exception $e) { + # write log + write_log( "Database upgrade", $e->getMessage()."
    query: ".$query, 2 ); + # fail + $this->Result->show("danger", _("Update: ").$e->getMessage()."
    query: ".$query, true); + } + } + + + # all good, print it + sleep(1); + write_log( "Database upgrade", "Database upgraded from version ".$this->settings->version." to version ".VERSION.".".REVISION, 1 ); + return true; + } + + /** + * Fetch all upgrade queries from DB files + * + * @access private + * @return void + */ + private function get_upgrade_queries () { + $dir = dirname(__FILE__) . '/../../db/'; + $files = scandir($dir); + + # set queries + foreach($files as $f) { + //get only UPGRADE- for specific version + if(substr($f, 0, 6) == "UPDATE") { + $new_version = str_replace(".sql", "",substr($f, 8)); + if($new_version>$this->settings->version) { + $queries[] = file_get_contents($dir.$f); + } + } + } + # return + return $queries; + } +} \ No newline at end of file diff --git a/functions/classes/class.Log.php b/functions/classes/class.Log.php new file mode 100644 index 000000000..2939387e9 --- /dev/null +++ b/functions/classes/class.Log.php @@ -0,0 +1,96 @@ +Database = $database; + # initialize Result + $this->Result = new Result (); + # debugging + $this->set_debugging(); + } + + private function set_log_type () { + + } + + /** + * fetches settings from database + * + * @access private + * @return none + */ + private function get_settings () { + # cache check + if($this->settings == false) { + try { $this->settings = $this->Database->getObject("settings", 1); } + catch (Exception $e) { $this->Result->show("danger", _("Database error: ").$e->getMessage()); } + } + } + + /** + * Strip tags from array or field to protect from XSS + * + * @access public + * @param mixed $input + * @return void + */ + public function strip_input_tags ($input) { + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = strip_tags($v); } + } + else { + $input = strip_tags($input); + } + # stripped + return $input; + } + + /** + * Sets debugging + * + * @access private + * @return void + */ + private function set_debugging () { + include( dirname(__FILE__) . '/../../config.php' ); + $this->debugging = $debugging ? true : false; + } + +} \ No newline at end of file diff --git a/functions/classes/class.Mail.php b/functions/classes/class.Mail.php new file mode 100644 index 000000000..843af5eb5 --- /dev/null +++ b/functions/classes/class.Mail.php @@ -0,0 +1,239 @@ +settings = $settings; + $this->mail_settings= $mail_settings; + + # initialize Result + $this->Result = new Result (); + } + + + + /** + * Initializes mailer object. + * + * @access public + * @return void + */ + public function initialize_mailer () { + # we need phpmailer + require( dirname(__FILE__) . '/../phpMailer/class.phpmailer.php'); + + # initialize object + $this->Php_mailer = new PHPMailer(true); //localhost by default + $this->Php_mailer->CharSet="UTF-8"; //set utf8 + $this->Php_mailer->SMTPDebug = 2; //debugging + $this->Php_mailer->Debugoutput = 'html'; //debug type + + # localhost or smtp? + if($this->mail_settings->mtype=="smtp") { $this->set_smtp(); } + } + + /** + * Sets SMTP parameters + * + * @access private + * @return void + */ + private function set_smtp() { + //set smtp + $this->Php_mailer->isSMTP(); + //tls, ssl? + if($this->mail_settings->msecure!='none') + $this->Php_mailer->SMTPSecure = $this->mail_settings->msecure=='ssl' ? 'ssl' : 'tls'; + //server + $this->Php_mailer->Host = $this->mail_settings->mserver; + $this->Php_mailer->Port = $this->mail_settings->mport; + //set smtp auth + $this->set_smtp_auth(); + } + + /** + * Set SMTP login parameters + * + * @access private + * @return void + */ + private function set_smtp_auth () { + if($this->mail_settings->mauth=="yes") { + $this->Php_mailer->SMTPAuth = true; + $this->Php_mailer->Username = $this->mail_settings->muser; + $this->Php_mailer->Password = $this->mail_settings->mpass; + } + else { + $this->Php_mailer->SMTPAuth = false; + } + } + + /** + * Overrides mail settings in database. For sending test emails. + * + * @access public + * @param mixed $override_settings + * @return void + */ + public function override_settings ($override_settings) { + foreach($override_settings as $k=>$s) { + $this->mail_settings->$k = $s; + } + } + + + + + + + + /** + * Generates mail message + * + * @access public + * @param mixed $body + * @return void + */ + public function generate_message ($body) { + $html[] = $this->set_header (); //set header + $html[] = $this->set_body_start (); //start body + $html[] = $body; //set body + $html[] = $this->set_footer (); //set footer + $html[] = $this->set_body_end (); //end + # return + return implode("\n", $html); + } + + /** + * Generates plain text mail + * + * @access public + * @param mixed $body + * @return void + */ + public function generate_message_plain ($body) { + $html[] = $body; //set body + $html[] = $this->set_footer_plain (); //set footer + } + + /** + * set_header function. + * + * @access private + * @return void + */ + private function set_header () { + $html[] = ""; + $html[] = ""; + $html[] = ""; + $html[] = ""; + # return + return implode("\n", $html); + } + + /** + * Begins message body + * + * @access private + * @return void + */ + private function set_body_start () { + return ""; + } + + /** + * Sets message body + * + * @access public + * @param mixed $body + * @return void + */ + public function set_body ($body) { + return is_array($body) ? implode("\n", $body) : $body; + } + + /** + * ends message body and html + * + * @access private + * @return void + */ + private function set_body_end () { + return ""; + } + + /** + * Sets footer + * + * @access public + * @return void + */ + public function set_footer () { + $html[] = ""; + $html[] = ""; + $html[] = " "; + $html[] = " "; + $html[] = ""; + $html[] = ""; + $html[] = " "; + $html[] = " "; + $html[] = ""; + $html[] = "
    E-mail".$this->settings->siteAdminName."
    www".$this->settings->siteURL."
    "; + # return + return implode("\n", $html); + } + + /** + * Sets plain footer + * + * @access public + * @return void + */ + public function set_footer_plain () { + return "\r\n------------------------------\r\n".$this->settings->siteAdminName." (".$this->settings->siteAdminMail.") :: ".$this->settings->siteURL; + } + +} + +?> \ No newline at end of file diff --git a/functions/classes/class.PDO.php b/functions/classes/class.PDO.php new file mode 100644 index 000000000..27cc34170 --- /dev/null +++ b/functions/classes/class.PDO.php @@ -0,0 +1,657 @@ +set_db_params (); + # rewrite user/pass if requested - for installation + $username==null ? : $this->username = $username; + $password==null ? : $this->password = $password; + # construct + parent::__construct($this->username, $this->password, $this->charset); + } + + + /** + * get database parameters from config.php + * + * @access private + * @return void + */ + private function set_db_params () { + # use config file + require( dirname(__FILE__) . '/../../config.php' ); + # set + $this->host = $db['host']; + $this->port = $db['port']; + $this->username = $db['user']; + $this->password = $db['pass']; + $this->dbname = $db['name']; + } + + /** + * connect function. + * + * @access public + * @return void + */ + public function connect() { + parent::connect(); + @$this->pdo->query('SET NAMES \'' . $this->charset . '\';'); + } + + protected function makeDsn() { + # for installation + if($this->install) { return 'mysql:host=' . $this->host . ';port=' . $this->port . ';charset=' . $this->charset; } + else { return 'mysql:host=' . $this->host . ';port=' . $this->port . ';dbname=' . $this->dbname . ';charset=' . $this->charset; } + } + + //more generic static useful methods + public function getColumnInfo() { + $columns = $this->getObjectsQuery(" + SELECT `table_name`, `column_name`, `column_default`, `is_nullable`, `data_type`,`column_key`, `extra` + FROM `columns` + WHERE `table_schema`='" . $this->dbname . "'; + "); + + $columnsByTable = array(); + foreach ($columns as $column) { + if (!isset($columnsByTable[$column->table_name])) { + $columnsByTable[$column->table_name] = array(); + } + + $columnsByTable[$column->table_name][$column->column_name] = $column; + } + + return $columnsByTable; + } + + public function getForeignKeyInfo() { + $foreignLinks = $this->getObjectsQuery(" + SELECT i.`table_name`, k.`column_name`, i.`constraint_type`, i.`constraint_name`, k.`referenced_table_name`, k.`referenced_column_name` + FROM `table_constraints` i + LEFT JOIN `key_column_usage` k ON i.`constraint_name` = k.`constraint_name` + WHERE i.`constraint_type` = 'FOREIGN KEY' AND i.`table_schema`='" . $this->dbname . "'; + "); + + $foreignLinksByTable = array(); + $foreignLinksByRefTable = array(); + foreach ($foreignLinks as $foreignLink) { + if (!isset($foreignLinksByTable[$foreignLink->table_name])) { + $foreignLinksByTable[$foreignLink->table_name] = array(); + } + + if (!isset($foreignLinksByRefTable[$foreignLink->referenced_table_name])) { + $foreignLinksByRefTable[$foreignLink->referenced_table_name] = array(); + } + + $foreignLinksByTable[$foreignLink->table_name][$foreignLink->column_name] = $foreignLink; + $foreignLinksByRefTable[$foreignLink->referenced_table_name][$foreignLink->table_name] = $foreignLink; + } + + return array($foreignLinksByTable, $foreignLinksByRefTable); + } +} + +abstract class DB { + protected $username = 'root'; + protected $password = ''; + protected $charset = 'utf8'; + protected $pdo = null; + + public function __construct($username = null, $password = null, $charset = null) { + if (isset($username)) $this->username = $username; + if (isset($password)) $this->password = $password; + if (isset($charset)) $this->charset = $charset; + } + + //convert a date object/string ready for use in sql + public static function toDate($date = null) { + if (is_int($date)) { + return date('Y-m-d H:i:s', $date); + } else if (is_string($date)) { + return date('Y-m-d H:i:s', strtotime($date)); + } else { + return date('Y-m-d H:i:s'); + } + } + + /** + * Connect to the database + * Call whenever a connection is needed to be made + */ + public function connect() { + $dsn = $this->makeDsn(); + + try { + $this->pdo = new \PDO($dsn, $this->username, $this->password); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + } catch (\PDOException $e) { + throw new Exception ("Could not connect to database! ".$e->getMessage()); + } + + @$this->pdo->query('SET NAMES \'' . $this->charset . '\';'); + } + + protected function makeDsn() { + return ':charset=' . $this->charset; + } + + public function resetConn() { + unset($this->pdo); + $this->install = false; + } + + /** + * Remove outer quotes from a string + * @param {string} quoted string + * @return {string} unquoted string + */ + public static function unquote_outer($str) { + $len = strlen($str); + + if ($len>1) { + if ($str[0] == "'" && $str[$len-1] == "'") { + return substr($str, 1, -1); + } else if ($str[0] == "'") { + return substr($str, 1); + } else if ($str[$len-1] == "'") { + return substr($str, 0, -1); + } + } else if ($len>0) { + if ($str[0] == "'") { + return ''; + } + } + + return $str; + } + + /** + * Are we currently connected to the database + * @return {boolean} we are connected + */ + public function isConnected() { + return ($this->pdo !== null); + } + + /** + * Returns last insert ID + * + * @access public + * @return void + */ + public function lastInsertId() { + return $this->pdo->lastInsertId(); + } + + /** + * Run a statement on the database + * Note: no objects are fetched + * @param {string} prepared statement + * @param {array} list of values to use in the prepared statement (optional) + * @param {boolean} success + */ + public function runQuery($query, $values = array()) { + if (!$this->isConnected()) $this->connect(); + + $statement = $this->pdo->prepare($query); + + return $statement->execute((array)$values); //this array cast allows single values to be used as the parameter + } + + /** + * Allow a value to be escaped, ready for insertion as a mysql parameter + * Note: for usage as a value (rather than prepared statements), you MUST manually quote around + * + * @param {string} value + * @return {string} mysql safe value + */ + public function escape($str) { + if (!$this->isConnected()) $this->connect(); + + return $this->unquote_outer($this->pdo->quote((string)$str)); + } + + /** + * Get a quick number of objects in a table + * + * @param {string} table name + * @return {integer} number of objects + */ + public function numObjects($tableName) { + if (!$this->isConnected()) $this->connect(); + + $tableName = $this->escape($tableName); + $statement = $this->pdo->prepare('SELECT COUNT(*) as `num` FROM `'.$tableName.'`;'); + + $statement->execute(); + + return $statement->fetchColumn(); + } + + /** + * Get a quick number of objects in a table for filtered field + * + * @param {string} table name + * @return {integer} number of objects + */ + public function numObjectsFilter($tableName, $method, $value) { + if (!$this->isConnected()) $this->connect(); + + $tableName = $this->escape($tableName); + $statement = $this->pdo->prepare('SELECT COUNT(*) as `num` FROM `'.$tableName.'` where `'.$method.'`=?;'); + + $statement->execute(array($value)); + + return $statement->fetchColumn(); + } + + /** + * Update an object in a table with values given + * + * Note: the id of the object is assumed to be in + * @param {string} name of table + * @param {object} new values for the object (can use an assoc array) + * @return {boolean} object updated sucessfully + */ + public function updateObject($tableName, $obj, $primarykey = 'id', $primarykey2 = null) { + if (!$this->isConnected()) $this->connect(); + + $obj = (array)$obj; + + //we cannot update an object without an id specified so quit + if (!isset($obj[$primarykey])) { + throw new Exception('Missing primary key'); + return false; + } + + $tableName = $this->escape($tableName); + + //get the objects id from the provided object and knock it off from the object so we dont try to update it + $objId[] = $obj[$primarykey]; + unset($obj[$primarykey]); + + //secondary primary key? + if(!is_null($primarykey2)) { + $objId[] = $obj[$primarykey2]; + unset($obj[$primarykey2]); + } + + //TODO: validate given object parameters with that of the table (this validates parameters names) + + //formulate an update statement based on the object parameters + $objParams = array_keys($obj); + + $preparedParamArr = array(); + foreach ($objParams as $objParam) { + $preparedParamArr[] = '`' . $this->escape($objParam) . '`=?'; + } + + $preparedParamStr = implode(',', $preparedParamArr); + + //primary key 2? + if(!is_null($primarykey2)) + $statement = $this->pdo->prepare('UPDATE `' . $tableName . '` SET ' . $preparedParamStr . ' WHERE `' . $primarykey . '`=? AND `' . $primarykey2 . '`=?;'); + else + $statement = $this->pdo->prepare('UPDATE `' . $tableName . '` SET ' . $preparedParamStr . ' WHERE `' . $primarykey . '`=?;'); + + //merge the parameters and values + $paramValues = array_merge(array_values($obj), $objId); + + //run the update on the object + return $statement->execute($paramValues); + } + + /** + * Update multiple objects at once + * @param {string} table name + * @param {array} list of ids + * @param {array} list of values + * @return {boolean} success + */ + public function updateMultipleObjects($tableName, $ids, $values) { + $tableName = $this->escape($tableName); + //set ids + $num = count($ids); + $idParts = array_fill(0, $num, '`id`=?'); + //set values + $objParams = array_keys($values); + $preparedParamArr = array(); + foreach ($objParams as $objParam) { + $preparedParamArr[] = '`' . $this->escape($objParam) . '`=?'; + } + //set values + $all_values = array_merge(array_values($values),$ids); + //execute + return $this->runQuery('UPDATE `'.$tableName.'` SET '.implode(',', $preparedParamArr).' WHERE '.implode(' OR ', $idParts), $all_values); + } + + /** + * Insert an object into a table + * Note: an id field is ignored if specified + * @param {string} name of table + * @param {object} object values to insert (can use an assoc array) + * @return {int} id of the object / false on fail + */ + public function insertObject($tableName, $obj, $raw = false, $replace = false) { + if (!$this->isConnected()) $this->connect(); + + $obj = (array)$obj; + + $tableName = $this->escape($tableName); + + if (!$raw && array_key_exists('id', $obj)) { + unset($obj['id']); + } + + if (count($obj)<1) { + return true; + } + + //formulate an update statement based on the object parameters + $objValues = array_values($obj); + + $preparedParamsArr = array(); + foreach ($obj as $key => $value) { + $preparedParamsArr[] = '`' . $this->escape($key) . '`'; + } + + $preparedParamsStr = implode(', ', $preparedParamsArr); + $preparedValuesStr = implode(', ', array_fill(0, count($objValues), '?')); + + if ($replace) { + $statement = $this->pdo->prepare('REPLACE INTO `' . $tableName . '` (' . $preparedParamsStr . ') VALUES (' . $preparedValuesStr . ');'); + } else { + $statement = $this->pdo->prepare('INSERT INTO `' . $tableName . '` (' . $preparedParamsStr . ') VALUES (' . $preparedValuesStr . ');'); + } + + //run the update on the object + if (!$statement->execute($objValues)) { + $errObj = $statement->errorInfo(); + + //return false; + throw new Exception($errObj[2]); + } + + return $this->pdo->lastInsertId(); + } + + /** + * Check if an object exists + */ + public function objectExists($tableName, $query = null, $values = array(), $id = null) { + return is_object($this->getObject($tableName, $query, $values, $id)); + } + + /** + * Get a filtered list of objects from the database + * @param {string} table name + * @param {string} prepared query (optional) + * @param {array} values to use in query (optional) + * @param {int} offset (optional) + * @param {int} number of objects to get (optional) + * @param {string} return objects class (optional) + * @return {array} list of objects + */ + public function getObjects($tableName, $sortField = 'id', $sortAsc = true, $numRecords = null, $offset = 0, $class = 'stdClass') { + if (!$this->isConnected()) $this->connect(); + + $sortStr = ''; + if (!$sortAsc) { + $sortStr = 'DESC'; + } + + //we should escape all of the params that we need to + $tableName = $this->escape($tableName); + $sortField = $this->escape($sortField); + + if ($numRecords === null) { + //get all (no limit) + $statement = $this->pdo->query('SELECT * FROM `'.$tableName.'` ORDER BY `'.$sortField.'` '.$sortStr.';'); + } else { + //get a limited range of objects + $statement = $this->pdo->query('SELECT * FROM `'.$tableName.'` ORDER BY `'.$sortField.'` '.$sortStr.' LIMIT '.$numRecords.' OFFSET '.$offset.';'); + } + + $results = array(); + + if (is_object($statement)) { + while ($newObj = $statement->fetchObject($class)) { + $results[] = $newObj; + } + } + + return $results; + } + + //use this function to conserve memory and read rows one by one rather than reading all of them + public function getObjectsQueryIncremental($query = null, $values = array(), $callback = null) { + if (!$this->isConnected()) $this->connect(); + + $statement = $this->pdo->prepare($query); + $statement->execute((array)$values); + + if (is_object($statement)) { + if ($callback) { + while ($newObj = $statement->fetchObject('stdClass')) { + if ($callback($newObj)===false) { + return false; + } + } + } + } + + return true; + } + + + /** + * Get all objects matching values + * + * @access public + * @param mixed $query (default: null) + * @param array $values (default: array()) + * @param string $class (default: 'stdClass') + * @return void + */ + public function getObjectsQuery($query = null, $values = array(), $class = 'stdClass') { + if (!$this->isConnected()) $this->connect(); + + $statement = $this->pdo->prepare($query); + $statement->execute((array)$values); + + $results = array(); + + if (is_object($statement)) { + while ($newObj = $statement->fetchObject($class)) { + $results[] = $newObj; + } + } + + return $results; + } + + /** + * Get a single object from the database + * @param {string} name of table to get object from + * @param {int} id of object (optional) + * @param {string} class name to bind the results to + * @return {object} instantiated object or null + */ + public function getObject($tableName, $id = null, $class = 'stdClass') { + if (!$this->isConnected()) $this->connect(); + $id = intval($id); + + //has a custom query been provided? + $tableName = $this->escape($tableName); + + //prepare a statement to get a single object from the database + if ($id !== null) { + $statement = $this->pdo->prepare('SELECT * FROM `'.$tableName.'` WHERE `id`=? LIMIT 1;'); + $statement->bindParam(1, $id, \PDO::PARAM_INT); + } else { + $statement = $this->pdo->prepare('SELECT * FROM `'.$tableName.'` LIMIT 1;'); + } + + $statement->execute(); + + //we can then extract the single object (if we have a result) + $resultObj = $statement->fetchObject($class); + + if ($resultObj === false) { + return null; + } else { + return $resultObj; + } + } + + public function getObjectQuery($query = null, $values = array(), $class = 'stdClass') { + if (!$this->isConnected()) $this->connect(); + + $statement = $this->pdo->prepare($query); + $statement->execute((array)$values); + + $resultObj = $statement->fetchObject($class); + + if ($resultObj === false) { + return null; + } else { + return $resultObj; + } + } + + public function getValueQuery($query = null, $values = array(), $class = 'stdClass') { + $obj = $this->getObjectQuery($query, $values, $class); + + if (is_object($obj)) { + $obj = (array)$obj; + return reset($obj); + } else { + return null; + } + } + + public function findObjects($table, $field, $value, $sortField = 'id', $sortAsc = true) { + $table = $this->escape($table); + $field = $this->escape($field); + $sortField = $this->escape($sortField); + + return $this->getObjectsQuery('SELECT * FROM `' . $table . '` WHERE `'. $field .'`=? ORDER BY `'.$sortField.'` ' . ($sortAsc ? '' : 'DESC') . ';', array($value)); + } + + public function findObject($table, $field, $value) { + $table = $this->escape($table); + $field = $this->escape($field); + + return $this->getObjectQuery('SELECT * FROM `' . $table . '` WHERE `' . $field . '` = ? LIMIT 1;', array($value)); + } + + public function getList($query = null, $values = array(), $class = 'stdClass') { + $objs = $this->getObjectsQuery($query, $values, $class); + + $list = array(); + foreach ($objs as $obj) { + $columns = array_values((array)$obj); + $list[] = $columns[0]; + } + + return $list; + } + + /** + * Delete an object from the database + * + * @param {string} table name + * @param {int} object id + * @return {boolean} success + */ + public function deleteObject($tableName, $id) { + $tableName = $this->escape($tableName); + + return $this->runQuery('DELETE FROM `'.$tableName.'` WHERE `id`=?;', array($id)); + } + + /** + * Delete a list of objects from the database + * + * @param {string} table name + * @param {array} list of ids + * @return {boolean} success + */ + public function deleteObjects($tableName, $ids) { + $tableName = $this->escape($tableName); + $num = count($ids); + $idParts = array_fill(0, $num, '`id`=?'); + + return $this->runQuery('DELETE FROM `'.$tableName.'` WHERE ' . implode(' OR ', $idParts), $ids); + } + + /** + * Delete specified row + * + * @access public + * @param {string} $tableName + * @param {string $field + * @param {string $value + * @return void + */ + public function deleteRow($tableName, $field, $value, $field2=null, $value2 = null) { + $tableName = $this->escape($tableName); + $field = $this->escape($field); + + //multiple + if(!is_null($field2)) + return $this->runQuery('DELETE FROM `'.$tableName.'` WHERE `'.$field.'`=? and `'.$field2.'`=?;', array($value, $value2)); + else + return $this->runQuery('DELETE FROM `'.$tableName.'` WHERE `'.$field.'`=?;', array($value)); + } + + /** + * truncate specified table + * + * @access public + * @param {string} $tableName + * @return void + */ + public function emptyTable($tableName) { + //escape talbe name + $tableName = $this->escape($tableName); + //execute + return $this->runQuery('TRUNCATE TABLE `'.$tableName.'`;'); + } +} + +?> \ No newline at end of file diff --git a/functions/classes/class.Radius.php b/functions/classes/class.Radius.php new file mode 100644 index 000000000..d2fa0f5dd --- /dev/null +++ b/functions/classes/class.Radius.php @@ -0,0 +1,838 @@ +. + * + * + * @author: SysCo/al + * @since CreationDate: 2008-01-04 + * @copyright (c) 2008 by SysCo systemes de communication sa + * @version $LastChangedRevision: 1.2.2 $ + * @version $LastChangedDate: 2009-01-05 $ + * @version $LastChangedBy: SysCo/al $ + * @link $HeadURL: radius.class.php $ + * @link http://developer.sysco.ch/php/ + * @link developer@sysco.ch + * Language: PHP 4.0.7 or higher + * + * + * Usage + * + * require_once('radius.class.php'); + * $radius = new Radius($ip_radius_server = 'radius_server_ip_address', $shared_secret = 'radius_shared_secret'[, $radius_suffix = 'optional_radius_suffix'[, $udp_timeout = udp_timeout_in_seconds[, $authentication_port = 1812]]]); + * $result = $radius->Access_Request($username = 'username', $password = 'password'[, $udp_timeout = udp_timeout_in_seconds]); + * + * + * Examples + * + * Example 1 + * SetNasIpAddress('1.2.3.4'); // Needed for some devices, and not auto_detected if PHP not runned through a web server + * if ($radius->AccessRequest('user', 'pass')) + * { + * echo "Authentication accepted."; + * } + * else + * { + * echo "Authentication rejected."; + * } + * ?> + * + * Example 2 + * SetNasPort(0); + * $radius->SetNasIpAddress('1.2.3.4'); // Needed for some devices, and not auto_detected if PHP not runned through a web server + * if ($radius->AccessRequest('user', 'pass')) + * { + * echo "Authentication accepted."; + * echo "
    "; + * } + * else + * { + * echo "Authentication rejected."; + * echo "
    "; + * } + * echo $radius->GetReadableReceivedAttributes(); + * ?> + * + * + * External file needed + * + * none. + * + * + * External file created + * + * none. + * + * + * Special issues + * + * - Sockets support must be enabled. + * * In Linux and *nix environments, the extension is enabled at + * compile time using the --enable-sockets configure option + * * In Windows, PHP Sockets can be activated by un-commenting + * extension=php_sockets.dll in php.ini + * + * + * Other related ressources + * + * FreeRADIUS, a free Radius server implementation for Linux and *nix environments: + * http://www.freeradius.org/ + * + * WinRadius, Windows Radius server (free for 5 users): + * http://www.itconsult2000.com/en/product/WinRadius.zip + * + * Radl, a free Radius server for Windows: + * http://www.loriotpro.com/Products/RadiusServer/FreeRadiusServer_EN.php + * + * DOS command line Radius client: + * http://www.itconsult2000.com/en/product/WinRadiusClient.zip + * + * + * Users feedbacks and comments + * + * 2008-07-02 Pim Koeman/Parantion + * + * When using a radius connection behind a linux iptables firewall + * allow port 1812 and 1813 with udp protocol + * + * IPTABLES EXAMPLE (command line): + * iptables -A AlwaysACCEPT -p udp --dport 1812 -j ACCEPT + * iptables -A AlwaysACCEPT -p udp --dport 1813 -j ACCEPT + * + * or put the lines in /etc/sysconfig/iptables (red-hat type systems (fedora, centos, rhel etc.) + * -A AlwaysACCEPT -p udp --dport 1812 -j ACCEPT + * -A AlwaysACCEPT -p udp --dport 1813 -j ACCEPT + * + * + * Change Log + * + * 2009-01-05 1.2.2 SysCo/al Added Robert Svensson feedback, Mideye RADIUS server is supported + * 2008-11-11 1.2.1 SysCo/al Added Carlo Ferrari resolution in examples (add NAS IP Address for a VASCO Middleware server) + * 2008-07-07 1.2 SysCo/al Added Pim Koeman (Parantion) contribution + * - comments concerning using radius behind a linux iptables firewall + * Added Jon Bright (tick Trading Software AG) contribution + * - false octal encoding with 0xx indexes (indexes are now rewritten in xx only) + * - challenge/response support for the RSA SecurID New-PIN mode + * Added GetRadiusPacketInfo() method + * Added GetAttributesInfo() method + * Added DecodeVendorSpecificContent() (to answer Raul Carvalho's question) + * Added Decoded Vendor Specific Content in debug messages + * 2008-02-04 1.1 SysCo/al Typo error for the udp_timeout parameter (line 256 in the version 1.0) + * 2008-01-07 1.0 SysCo/al Initial release + * + *********************************************************************/ + + +/********************************************************************* + * + * Radius + * Pure PHP radius class + * + * Creation 2008-01-04 + * Update 2009-01-05 + * @package radius + * @version v.1.2.2 + * @author SysCo/al + * + *********************************************************************/ +class Radius +{ + var $_ip_radius_server; // Radius server IP address + var $_shared_secret; // Shared secret with the radius server + var $_radius_suffix; // Radius suffix (default is ''); + var $_udp_timeout; // Timeout of the UDP connection in seconds (default value is 5) + var $_authentication_port; // Authentication port (default value is 1812) + var $_accounting_port; // Accouting port (default value is 1813) + var $_nas_ip_address; // NAS IP address + var $_nas_port; // NAS port + var $_encrypted_password; // Encrypted password, as described in the RFC 2865 + var $_user_ip_address; // Remote IP address of the user + var $_request_authenticator; // Request-Authenticator, 16 octets random number + var $_response_authenticator; // Request-Authenticator, 16 octets random number + var $_username; // Username to sent to the Radius server + var $_password; // Password to sent to the Radius server (clear password, must be encrypted) + var $_identifier_to_send; // Identifier field for the packet to be sent + var $_identifier_received; // Identifier field for the received packet + var $_radius_packet_to_send; // Radius packet code (1=Access-Request, 2=Access-Accept, 3=Access-Reject, 4=Accounting-Request, 5=Accounting-Response, 11=Access-Challenge, 12=Status-Server (experimental), 13=Status-Client (experimental), 255=Reserved + var $_radius_packet_received; // Radius packet code (1=Access-Request, 2=Access-Accept, 3=Access-Reject, 4=Accounting-Request, 5=Accounting-Response, 11=Access-Challenge, 12=Status-Server (experimental), 13=Status-Client (experimental), 255=Reserved + var $_attributes_to_send; // Radius attributes to send + var $_attributes_received; // Radius attributes received + var $_socket_to_server; // Socket connection + var $_debug_mode; // Debug mode flag + var $_attributes_info; // Attributes info array + var $_radius_packet_info; // Radius packet codes info array + var $_last_error_code; // Last error code + var $_last_error_message; // Last error message + + + /********************************************************************* + * + * Name: Radius + * short description: Radius class constructor + * + * Creation 2008-01-04 + * Update 2009-01-05 + * @version v.1.2.2 + * @author SysCo/al + * @param string ip address of the radius server + * @param string shared secret with the radius server + * @param string radius domain name suffix (default is empty) + * @param integer UDP timeout (default is 5) + * @param integer authentication port + * @param integer accounting port + * @return NULL + *********************************************************************/ + public function Radius($ip_radius_server = '127.0.0.1', $shared_secret = '', $radius_suffix = '', $udp_timeout = 5, $authentication_port = 1812, $accounting_port = 1813) + { + $this->_radius_packet_info[1] = 'Access-Request'; + $this->_radius_packet_info[2] = 'Access-Accept'; + $this->_radius_packet_info[3] = 'Access-Reject'; + $this->_radius_packet_info[4] = 'Accounting-Request'; + $this->_radius_packet_info[5] = 'Accounting-Response'; + $this->_radius_packet_info[11] = 'Access-Challenge'; + $this->_radius_packet_info[12] = 'Status-Server (experimental)'; + $this->_radius_packet_info[13] = 'Status-Client (experimental)'; + $this->_radius_packet_info[255] = 'Reserved'; + + $this->_attributes_info[1] = array('User-Name', 'S'); + $this->_attributes_info[2] = array('User-Password', 'S'); + $this->_attributes_info[3] = array('CHAP-Password', 'S'); // Type (1) / Length (1) / CHAP Ident (1) / String + $this->_attributes_info[4] = array('NAS-IP-Address', 'A'); + $this->_attributes_info[5] = array('NAS-Port', 'I'); + $this->_attributes_info[6] = array('Service-Type', 'I'); + $this->_attributes_info[7] = array('Framed-Protocol', 'I'); + $this->_attributes_info[8] = array('Framed-IP-Address', 'A'); + $this->_attributes_info[9] = array('Framed-IP-Netmask', 'A'); + $this->_attributes_info[10] = array('Framed-Routing', 'I'); + $this->_attributes_info[11] = array('Filter-Id', 'T'); + $this->_attributes_info[12] = array('Framed-MTU', 'I'); + $this->_attributes_info[13] = array('Framed-Compression', 'I'); + $this->_attributes_info[14] = array( 'Login-IP-Host', 'A'); + $this->_attributes_info[15] = array('Login-service', 'I'); + $this->_attributes_info[16] = array('Login-TCP-Port', 'I'); + $this->_attributes_info[17] = array('(unassigned)', ''); + $this->_attributes_info[18] = array('Reply-Message', 'T'); + $this->_attributes_info[19] = array('Callback-Number', 'S'); + $this->_attributes_info[20] = array('Callback-Id', 'S'); + $this->_attributes_info[21] = array('(unassigned)', ''); + $this->_attributes_info[22] = array('Framed-Route', 'T'); + $this->_attributes_info[23] = array('Framed-IPX-Network', 'I'); + $this->_attributes_info[24] = array('State', 'S'); + $this->_attributes_info[25] = array('Class', 'S'); + $this->_attributes_info[26] = array('Vendor-Specific', 'S'); // Type (1) / Length (1) / Vendor-Id (4) / Vendor type (1) / Vendor length (1) / Attribute-Specific... + $this->_attributes_info[27] = array('Session-Timeout', 'I'); + $this->_attributes_info[28] = array('Idle-Timeout', 'I'); + $this->_attributes_info[29] = array('Termination-Action', 'I'); + $this->_attributes_info[30] = array('Called-Station-Id', 'S'); + $this->_attributes_info[31] = array('Calling-Station-Id', 'S'); + $this->_attributes_info[32] = array('NAS-Identifier', 'S'); + $this->_attributes_info[33] = array('Proxy-State', 'S'); + $this->_attributes_info[34] = array('Login-LAT-Service', 'S'); + $this->_attributes_info[35] = array('Login-LAT-Node', 'S'); + $this->_attributes_info[36] = array('Login-LAT-Group', 'S'); + $this->_attributes_info[37] = array('Framed-AppleTalk-Link', 'I'); + $this->_attributes_info[38] = array('Framed-AppleTalk-Network', 'I'); + $this->_attributes_info[39] = array('Framed-AppleTalk-Zone', 'S'); + $this->_attributes_info[60] = array('CHAP-Challenge', 'S'); + $this->_attributes_info[61] = array('NAS-Port-Type', 'I'); + $this->_attributes_info[62] = array('Port-Limit', 'I'); + $this->_attributes_info[63] = array('Login-LAT-Port', 'S'); + $this->_attributes_info[76] = array('Prompt', 'I'); + + $this->_identifier_to_send = 0; + $this->_user_ip_address = (isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'0.0.0.0'); + + $this->GenerateRequestAuthenticator(); + $this->SetIpRadiusServer($ip_radius_server); + $this->SetSharedSecret($shared_secret); + $this->SetAuthenticationPort($authentication_port); + $this->SetAccountingPort($accounting_port); + $this->SetRadiusSuffix($radius_suffix); + $this->SetUdpTimeout($udp_timeout); + $this->SetUsername(); + $this->SetPassword(); + $this->SetNasIpAddress(); + $this->SetNasPort(); + + $this->ClearLastError(); + $this->ClearDataToSend(); + $this->ClearDataReceived(); + } + + + function GetNextIdentifier() + { + $this->_identifier_to_send = (($this->_identifier_to_send + 1) % 256); + return $this->_identifier_to_send; + } + + + function GenerateRequestAuthenticator() + { + $this->_request_authenticator = ''; + for ($ra_loop = 0; $ra_loop <= 15; $ra_loop++) + { + $this->_request_authenticator .= chr(rand(1, 255)); + } + } + + + function GetRequestAuthenticator() + { + return $this->_request_authenticator; + } + + + function GetLastError() + { + if (0 < $this->_last_error_code) + { + return $this->_last_error_message.' ('.$this->_last_error_code.')'; + } + else + { + return ''; + } + } + + + function ClearDataToSend() + { + $this->_radius_packet_to_send = 0; + $this->_attributes_to_send = NULL; + } + + + function ClearDataReceived() + { + $this->_radius_packet_received = 0; + $this->_attributes_received = NULL; + } + + + function SetPacketCodeToSend($packet_code) + { + $this->_radius_packet_to_send = $packet_code; + } + + + function SetDebugMode($debug_mode) + { + $this->_debug_mode = (TRUE === $debug_mode); + } + + + function SetIpRadiusServer($ip_radius_server) + { + $this->_ip_radius_server = gethostbyname($ip_radius_server); + } + + + function SetSharedSecret($shared_secret) + { + $this->_shared_secret = $shared_secret; + } + + + function SetRadiusSuffix($radius_suffix) + { + $this->_radius_suffix = $radius_suffix; + } + + + function SetUsername($username = '') + { + $temp_username = $username; + if (false === strpos($temp_username, '@')) + { + $temp_username .= $this->_radius_suffix; + } + + $this->_username = $temp_username; + $this->SetAttribute(1, $this->_username); + } + + + function SetPassword($password = '') + { + $this->_password = $password; + $encrypted_password = ''; + $padded_password = $password; + + if (0 != (strlen($password)%16)) + { + $padded_password .= str_repeat(chr(0),(16-strlen($password)%16)); + } + + $previous_result = $this->_request_authenticator; + + for ($full_loop = 0; $full_loop < (strlen($padded_password)/16); $full_loop++) + { + $xor_value = md5($this->_shared_secret.$previous_result); + + $previous_result = ''; + for ($xor_loop = 0; $xor_loop <= 15; $xor_loop++) + { + $value1 = ord(substr($padded_password, ($full_loop * 16) + $xor_loop, 1)); + $value2 = hexdec(substr($xor_value, 2*$xor_loop, 2)); + $xor_result = $value1 ^ $value2; + $previous_result .= chr($xor_result); + } + $encrypted_password .= $previous_result; + } + + $this->_encrypted_password = $encrypted_password; + $this->SetAttribute(2, $this->_encrypted_password); + } + + + function SetNasIPAddress($nas_ip_address = '') + { + if (0 < strlen($nas_ip_address)) + { + $this->_nas_ip_address = gethostbyname($nas_ip_address); + } + else + { + $this->_nas_ip_address = gethostbyname(isset($_SERVER['SERVER_ADDR'])?$_SERVER['SERVER_ADDR']:'0.0.0.0'); + } + $this->SetAttribute(4, $this->_nas_ip_address); + } + + + function SetNasPort($nas_port = 0) + { + $this->_nas_port = intval($nas_port); + $this->SetAttribute(5, $this->_nas_port); + } + + + function SetUdpTimeout($udp_timeout = 5) + { + if (intval($udp_timeout) > 0) + { + $this->_udp_timeout = intval($udp_timeout); + } + } + + + function ClearLastError() + { + $this->_last_error_code = 0; + $this->_last_error_message = ''; + } + + + function SetAuthenticationPort($authentication_port) + { + if ((intval($authentication_port) > 0) && (intval($authentication_port) < 65536)) + { + $this->_authentication_port = intval($authentication_port); + } + } + + + function SetAccountingPort($accounting_port) + { + if ((intval($accounting_port) > 0) && (intval($accounting_port) < 65536)) + { + $this->_accounting_port = intval($accounting_port); + } + } + + + function GetReceivedPacket() + { + return $this->_radius_packet_received; + } + + + function GetReceivedAttributes() + { + return $this->_attributes_received; + } + + + function GetReadableReceivedAttributes() + { + $readable_attributes = ''; + if (isset($this->_attributes_received)) + { + foreach($this->_attributes_received as $one_received_attribute) + { + $attributes_info = $this->GetAttributesInfo($one_received_attribute[0]); + $readable_attributes .= $attributes_info[0].": "; + if (26 == $one_received_attribute[0]) + { + $vendor_array = $this->DecodeVendorSpecificContent($one_received_attribute[1]); + foreach($vendor_array as $vendor_one) + { + $readable_attributes .= 'Vendor-Id: '.$vendor_one[0].", Vendor-type: ".$vendor_one[1].", Attribute-specific: ".$vendor_one[2]; + } + } + else + { + $readable_attributes .= $one_received_attribute[1]; + } + $readable_attributes .= "
    \n"; + } + } + return $readable_attributes; + } + + + function GetAttribute($attribute_type) + { + $attribute_value = NULL; + foreach($this->_attributes_received as $one_received_attribute) + { + if (intval($attribute_type) == $one_received_attribute[0]) + { + $attribute_value = $one_received_attribute[1]; + break; + } + } + return $attribute_value; + } + + + function GetRadiusPacketInfo($info_index) + { + if (isset($this->_radius_packet_info[intval($info_index)])) + { + return $this->_radius_packet_info[intval($info_index)]; + } + else + { + return ''; + } + } + + + function GetAttributesInfo($info_index) + { + if (isset($this->_attributes_info[intval($info_index)])) + { + return $this->_attributes_info[intval($info_index)]; + } + else + { + return array('',''); + } + } + + + function DebugInfo($debug_info) + { + if ($this->_debug_mode) + { + //save debugging + $this->debug_text[] = date('Y-m-d H:i:s').' DEBUG: '.$debug_info; + } + } + + + function SetAttribute($type, $value) + { + $attribute_index = -1; + for ($attributes_loop = 0; $attributes_loop < count($this->_attributes_to_send); $attributes_loop++) + { + if ($type == ord(substr($this->_attributes_to_send[$attributes_loop], 0, 1))) + { + $attribute_index = $attributes_loop; + break; + } + } + + $temp_attribute = NULL; + + if (isset($this->_attributes_info[$type])) + { + switch ($this->_attributes_info[$type][1]) + { + case 'T': // Text, 1-253 octets containing UTF-8 encoded ISO 10646 characters (RFC 2279). + $temp_attribute = chr($type).chr(2+strlen($value)).$value; + break; + case 'S': // String, 1-253 octets containing binary data (values 0 through 255 decimal, inclusive). + $temp_attribute = chr($type).chr(2+strlen($value)).$value; + break; + case 'A': // Address, 32 bit value, most significant octet first. + $ip_array = explode(".", $value); + $temp_attribute = chr($type).chr(6).chr($ip_array[0]).chr($ip_array[1]).chr($ip_array[2]).chr($ip_array[3]); + break; + case 'I': // Integer, 32 bit unsigned value, most significant octet first. + $temp_attribute = chr($type).chr(6).chr(($value/(256*256*256))%256).chr(($value/(256*256))%256).chr(($value/(256))%256).chr($value%256); + break; + case 'D': // Time, 32 bit unsigned value, most significant octet first -- seconds since 00:00:00 UTC, January 1, 1970. (not used in this RFC) + $temp_attribute = NULL; + break; + default: + $temp_attribute = NULL; + } + } + + if ($attribute_index > -1) + { + $this->_attributes_to_send[$attribute_index] = $temp_attribute; + $additional_debug = 'Modified'; + } + else + { + $this->_attributes_to_send[] = $temp_attribute; + $additional_debug = 'Added'; + } + $attribute_info = $this->GetAttributesInfo($type); + $this->DebugInfo($additional_debug.' Attribute '.$type.' ('.$attribute_info[0].'), format '.$attribute_info[1].', value '.$value.''); + } + + + function DecodeAttribute($attribute_raw_value, $attribute_format) + { + $attribute_value = NULL; + + if (isset($this->_attributes_info[$attribute_format])) + { + switch ($this->_attributes_info[$attribute_format][1]) + { + case 'T': // Text, 1-253 octets containing UTF-8 encoded ISO 10646 characters (RFC 2279). + $attribute_value = $attribute_raw_value; + break; + case 'S': // String, 1-253 octets containing binary data (values 0 through 255 decimal, inclusive). + $attribute_value = $attribute_raw_value; + break; + case 'A': // Address, 32 bit value, most significant octet first. + $attribute_value = ord(substr($attribute_raw_value, 0, 1)).'.'.ord(substr($attribute_raw_value, 1, 1)).'.'.ord(substr($attribute_raw_value, 2, 1)).'.'.ord(substr($attribute_raw_value, 3, 1)); + break; + case 'I': // Integer, 32 bit unsigned value, most significant octet first. + $attribute_value = (ord(substr($attribute_raw_value, 0, 1))*256*256*256)+(ord(substr($attribute_raw_value, 1, 1))*256*256)+(ord(substr($attribute_raw_value, 2, 1))*256)+ord(substr($attribute_raw_value, 3, 1)); + break; + case 'D': // Time, 32 bit unsigned value, most significant octet first -- seconds since 00:00:00 UTC, January 1, 1970. (not used in this RFC) + $attribute_value = NULL; + break; + default: + $attribute_value = NULL; + } + } + return $attribute_value; + } + + + /********************************************************************* + * Array returned: array(array(Vendor-Id1, Vendor type1, Attribute-Specific1), ..., array(Vendor-IdN, Vendor typeN, Attribute-SpecificN) + *********************************************************************/ + function DecodeVendorSpecificContent($vendor_specific_raw_value) + { + $result = array(); + $offset_in_raw = 0; + $vendor_id = (ord(substr($vendor_specific_raw_value, 0, 1))*256*256*256)+(ord(substr($vendor_specific_raw_value, 1, 1))*256*256)+(ord(substr($vendor_specific_raw_value, 2, 1))*256)+ord(substr($vendor_specific_raw_value, 3, 1)); + $offset_in_raw += 4; + while ($offset_in_raw < strlen($vendor_specific_raw_value)) + { + $vendor_type = (ord(substr($vendor_specific_raw_value, 0+$offset_in_raw, 1))); + $vendor_length = (ord(substr($vendor_specific_raw_value, 1+$offset_in_raw, 1))); + $attribute_specific = substr($vendor_specific_raw_value, 2+$offset_in_raw, $vendor_length); + $result[] = array($vendor_id, $vendor_type, $attribute_specific); + $offset_in_raw += ($vendor_length); + } + + return $result; + } + + + /* + * Function : AccessRequest + * + * Return TRUE if Access-Request is accepted, FALSE otherwise + */ + function AccessRequest($username = '', $password = '', $udp_timeout = 0, $state = NULL) + { + $this->ClearDataReceived(); + $this->ClearLastError(); + + $this->SetPacketCodeToSend(1); // Access-Request + + if (0 < strlen($username)) + { + $this->SetUsername($username); + } + + if (0 < strlen($password)) + { + $this->SetPassword($password); + } + + if ($state!==NULL) + { + $this->SetAttribute(24, $state); + } + else + { + $this->SetAttribute(6, 1); // 1=Login + } + + if (intval($udp_timeout) > 0) + { + $this->SetUdpTimeout($udp_timeout); + } + + $attributes_content = ''; + for ($attributes_loop = 0; $attributes_loop < count($this->_attributes_to_send); $attributes_loop++) + { + $attributes_content .= $this->_attributes_to_send[$attributes_loop]; + } + + $packet_length = 4; // Radius packet code + Identifier + Length high + Length low + $packet_length += strlen($this->_request_authenticator); // Request-Authenticator + $packet_length += strlen($attributes_content); // Attributes + + $packet_data = chr($this->_radius_packet_to_send); + $packet_data .= chr($this->GetNextIdentifier()); + $packet_data .= chr(intval($packet_length/256)); + $packet_data .= chr(intval($packet_length%256)); + $packet_data .= $this->_request_authenticator; + $packet_data .= $attributes_content; + + $_socket_to_server = socket_create(AF_INET, SOCK_DGRAM, 17); // UDP packet = 17 + + if ($_socket_to_server === FALSE) + { + $this->_last_error_code = socket_last_error(); + $this->_last_error_message = socket_strerror($this->_last_error_code); + } + elseif (FALSE === socket_connect($_socket_to_server, $this->_ip_radius_server, $this->_authentication_port)) + { + $this->_last_error_code = socket_last_error(); + $this->_last_error_message = socket_strerror($this->_last_error_code); + } + elseif (FALSE === socket_write($_socket_to_server, $packet_data, $packet_length)) + { + $this->_last_error_code = socket_last_error(); + $this->_last_error_message = socket_strerror($this->_last_error_code); + } + else + { + $this->DebugInfo('Packet type '.$this->_radius_packet_to_send.' ('.$this->GetRadiusPacketInfo($this->_radius_packet_to_send).')'.' sent'); + if ($this->_debug_mode) + { + $readable_attributes = ''; + foreach($this->_attributes_to_send as $one_attribute_to_send) + { + $attribute_info = $this->GetAttributesInfo(ord(substr($one_attribute_to_send,0,1))); + $this->DebugInfo('Attribute '.ord(substr($one_attribute_to_send,0,1)).' ('.$attribute_info[0].'), length '.(ord(substr($one_attribute_to_send,1,1))-2).', format '.$attribute_info[1].', value '.$this->DecodeAttribute(substr($one_attribute_to_send,2), ord(substr($one_attribute_to_send,0,1))).''); + } + } + $read_socket_array = array($_socket_to_server); + $write_socket_array = NULL; + $except_socket_array = NULL; + + $received_packet = chr(0); + + if (!(FALSE === socket_select($read_socket_array, $write_socket_array, $except_socket_array, $this->_udp_timeout))) + { + if (in_array($_socket_to_server, $read_socket_array)) + { + if (FALSE === ($received_packet = @socket_read($_socket_to_server, 1024))) // @ used, than no error is displayed if the connection is closed by the remote host + { + $received_packet = chr(0); + $this->_last_error_code = socket_last_error(); + $this->_last_error_message = socket_strerror($this->_last_error_code); + } + else + { + socket_close($_socket_to_server); + } + } + } + else + { + socket_close($_socket_to_server); + } + } + + $this->_radius_packet_received = intval(ord(substr($received_packet, 0, 1))); + + $this->DebugInfo('Packet type '.$this->_radius_packet_received.' ('.$this->GetRadiusPacketInfo($this->_radius_packet_received).')'.' received'); + + if ($this->_radius_packet_received > 0) + { + $this->_identifier_received = intval(ord(substr($received_packet, 1, 1))); + $packet_length = (intval(ord(substr($received_packet, 2, 1))) * 256) + (intval(ord(substr($received_packet, 3, 1)))); + $this->_response_authenticator = substr($received_packet, 4, 16); + $attributes_content = substr($received_packet, 20, ($packet_length - 4 - 16)); + while (strlen($attributes_content) > 2) + { + $attribute_type = intval(ord(substr($attributes_content,0,1))); + $attribute_length = intval(ord(substr($attributes_content,1,1))); + $attribute_raw_value = substr($attributes_content,2,$attribute_length-2); + $attributes_content = substr($attributes_content, $attribute_length); + + $attribute_value = $this->DecodeAttribute($attribute_raw_value, $attribute_type); + + $attribute_info = $this->GetAttributesInfo($attribute_type); + if (26 == $attribute_type) + { + $vendor_array = $this->DecodeVendorSpecificContent($attribute_value); + foreach($vendor_array as $vendor_one) + { + $this->DebugInfo('Attribute '.$attribute_type.' ('.$attribute_info[0].'), length '.($attribute_length-2).', format '.$attribute_info[1].', Vendor-Id: '.$vendor_one[0].", Vendor-type: ".$vendor_one[1].", Attribute-specific: ".$vendor_one[2]); + } + } + else + { + $this->DebugInfo('Attribute '.$attribute_type.' ('.$attribute_info[0].'), length '.($attribute_length-2).', format '.$attribute_info[1].', value '.$attribute_value.''); + } + + $this->_attributes_received[] = array($attribute_type, $attribute_value); + } + } + + return (2 == ($this->_radius_packet_received)); + } +} + +?> diff --git a/functions/classes/class.Result.php b/functions/classes/class.Result.php new file mode 100644 index 000000000..db772e44d --- /dev/null +++ b/functions/classes/class.Result.php @@ -0,0 +1,108 @@ +exit_method == "exception") { + return $this->throw_exception ($utext); + } + + # text + if(!is_array( $utext )) {} + # array + elseif( is_array( $utext ) && sizeof( $utext )>0) { + if(sizeof( $utext )==1) { # single value + $utext = $utext[0]; + } else { # multiple values + $utext = "
      "; + foreach( $utext as $l ) { $utext .= "
    • $l
    • "; } + $utext .= "
    "; + } + } + + # print popup or normal + if(!$popup) { + print "
    ".$utext."
    "; + } + else { + print '
    '._("Error").'
    '; + print '
    '; + print '
    '.$utext.'
    '; + print '
    '; + print '
    '; + } + + # die if set + if($die) die(); + } + + /** + * Shows result for cli functions + * + * @access public + * @param string $utext (default: "No value provided") + * @param bool $die (default: false) + * @return void + */ + public function show_cli ($utext="No value provided", $die=false) { + # text + if(!is_array( $utext )) {} + # array + elseif( is_array( $utext ) && sizeof( $utext )>0) { + if(sizeof( $utext )==1) { # single value + $utext = $utext[0]; + } else { # multiple values + foreach( $utext as $l ) { $utext .= "\t* $l\n"; } + } + } + + # print + print "Error:\n"; + print $utext; + + # die if set + if($die) die(); + } + + /** + * Exists with exception + * + * @access public + * @param mixed $content + * @return void + */ + public function throw_exception ($content) { + // include Exceptions class for API + include_once( dirname(__FILE__) . '../../../api/v2/controllers/Exceptions.php' ); + // initialize exceptions + $Exceptions = new Api_exceptions (); + // throw error + $Exceptions->throw_exception(500, $content); + } +} + +?> \ No newline at end of file diff --git a/functions/classes/class.Scan.php b/functions/classes/class.Scan.php new file mode 100644 index 000000000..a4f5a7bc3 --- /dev/null +++ b/functions/classes/class.Scan.php @@ -0,0 +1,646 @@ +Database = $database; + # initialize Result + $this->Result = new Result (); + # debugging + $this->set_debugging(); + # fetch settings + is_null($settings) ? $this->get_settings() : $this->settings = (object) $settings; + # set type + $this->reset_scan_method ($this->settings->scanPingType); + # set php exec + $this->set_php_exec (); + } + + /** + * fetches settings from database + * + * @access private + * @return none + */ + private function get_settings () { + # cache check + if($this->settings == false) { + try { $this->settings = $this->Database->getObject("settings", 1); } + catch (Exception $e) { $this->Result->show("danger", _("Database error: ").$e->getMessage()); } + # set method + $this->icmp_type = $this->settings->scanPingType; + } + } + + /** + * Returns all possible ping types / apps + * + * @access public + * @return array + */ + public function ping_fetch_types () { + return array("ping", "pear", "fping"); + } + + /** + * This functin resets the scan method, for cron scripts + * + * @access public + * @param mixed $method + * @return void + */ + public function reset_scan_method ($method) { + // fetch possible methods + $possible = $this->ping_fetch_types (); + //check + if(!in_array($method, $possible)) { + //die or print? + if($this->icmp_exit) { die(json_encode(array("status"=>1, "error"=>"Invalid scan method"))); } + else { $this->Result->show("danger", "Invalid scan method", true); } + } + //ok + else { + $this->icmp_type = $method; + } + } + + /** + * Sets debugging + * + * @access private + * @return void + */ + private function set_debugging () { + include( dirname(__FILE__) . '/../../config.php' ); + $this->debugging = $debugging ? true : false; + } + + /** + * Resets debugging + * + * @access public + * @param bool $debug (default: false) + * @return void + */ + public function reset_debugging ($debug = false) { + $this->debugging = $debug; + } + + /** + * Sets php exec + * + * @access private + * @return void + */ + private function set_php_exec () { + $this->php_exec = PHP_BINDIR."/php"; + } + + /** + * Set weather the code sohuld exit or return + * + * Default is return + * + * @access public + * @param bool $exit (default: false) + * @return void + */ + public function ping_set_exit ($exit = false) { + $this->icmp_exit = $exit; + } + + + + + + + + + + /** + * @ping @icmp methods + * -------------------------------- + */ + + /** + * Function that pings address and checks if it responds + * + * any script can be used by extension, important are results + * + * 0 = Alive + * 1 = Offline + * 2 = Offline + * + * all other codes can be explained in ping_exit_explain method + * + * @access public + * @param mixed $address + * @param int $count (default: 1) + * @param int $timeout (default: 1) + * @param bool $exit (default: false) + * @return void + */ + public function ping_address ($address, $count=1, $timeout = 1) { + #set parameters + $this->icmp_timeout = $timeout; + $this->icmp_count = $count; + + # Initialize object + $this->Addresses = new Addresses ($this->Database); + + # escape address + $address = escapeshellarg($address); + + # make sure it is in right format + $address = $this->Addresses->transform_address ($address, "dotted"); + # set method name variable + $ping_method = "ping_address_method_".$this->icmp_type; + # ping with selected method + return $this->$ping_method ($address); + } + + /** + * Ping selected address and return response + * + * timeout value: for miliseconds multiplyy by 1000 + * + * @access protected + * @param ip $address + * @return void + */ + protected function ping_address_method_ping ($address) { + # verify ping path + $this->ping_verify_path ($this->settings->scanPingPath); + + # set ping command based on OS type + if(PHP_OS == "FreeBSD" || PHP_OS == "NetBSD") { $cmd = $this->settings->scanPingPath." -c $this->icmp_count -W ".($this->icmp_timeout*1000)." $address 1>/dev/null 2>&1"; } + elseif(PHP_OS == "Linux" || PHP_OS == "OpenBSD") { $cmd = $this->settings->scanPingPath." -c $this->icmp_count -w $this->icmp_timeout $address 1>/dev/null 2>&1"; } + elseif(PHP_OS == "WIN32" || PHP_OS == "Windows" || PHP_OS == "WINNT") { $cmd = $this->settings->scanPingPath." -n $this->icmp_count -I ".($this->icmp_timeout*1000)." $address 1>/dev/null 2>&1"; } + else { $cmd = $this->settings->scanPingPath." -c $this->icmp_count -n $address 1>/dev/null 2>&1"; } + + # execute command, return $retval + exec($cmd, $output, $retval); + + # return result for web or cmd + if($this->icmp_exit) { exit ($retval); } + else { return $retval; } + } + + /** + * Ping selected address with PEAR ping package + * + * @access protected + * @param ip $address + * @return void + */ + protected function ping_address_method_pear ($address) { + # we need pear ping package + require_once(dirname(__FILE__) . '/../../functions/PEAR/Net/Ping.php'); + $ping = Net_Ping::factory(); + + # check for errors + if($ping->pear->isError($ping)) { + if($this->icmp_exit) { exit ($ping->getMessage()); } + else { return $ping->getMessage(); } + } + else { + //set count and timeout + $ping->setArgs(array('count' => $this->icmp_timeout, 'timeout' => $this->icmp_timeout)); + //execute + $ping_response = $ping->ping($address); + //check response for error + if($ping->pear->isError($ping_response)) { + $result['code'] = 2; + } + else { + //all good + if($ping_response->_transmitted == $ping_response->_received) { + $result['code'] = 0; + $this->rtt = "RTT: ". strstr($ping_response->_round_trip['avg'], ".", true); + } + //ping loss + elseif($ping_response->_received == 0) { + $result['code'] = 1; + } + //failed + else { + $result['code'] = 3; + } + } + } + + //return result for web or cmd + if($this->icmp_exit) { exit ($result['code']); } + else { return $result['code']; } + } + + /** + * Ping selected address with fping function + * + * Exit status is: + * 0 if all the hosts are reachable, + * 1 if some hosts were unreachable, + * 2 if any IP addresses were not found, + * 3 for invalid command line arguments, + * 4 for a system call failure. + * + * fping cannot be run from web, it needs root privileges to be able to open raw socket :/ + * + * @access public + * @param mixed $subnet //CIDR + * @return void + */ + public function ping_address_method_fping ($address) { + # verify ping path + $this->ping_verify_path ($this->settings->scanFPingPath); + # set command + $cmd = $this->settings->scanFPingPath." -c $this->icmp_count -t ".($this->icmp_timeout*1000)." $address"; + # execute command, return $retval + exec($cmd, $output, $retval); + + # save result + if($retval==0) { + $this->save_fping_rtt ($output[0]); + } + + # return result for web or cmd + if($this->icmp_exit) { exit ($retval); } + else { return $retval; } + } + + /** + * Saves RTT for fping + * + * @access private + * @param mixed $line + * @return void + */ + private function save_fping_rtt ($line) { + // 173.192.112.30 : xmt/rcv/%loss = 1/1/0%, min/avg/max = 160/160/160 + $tmp = explode(" ",$line); + + # save rtt + @$this->rtt = "RTT: ".str_replace("(", "", $tmp[7]); + } + + /** + * Ping selected address with fping function + * + * Exit status is: + * 0 if all the hosts are reachable, + * 1 if some hosts were unreachable, + * 2 if any IP addresses were not found, + * 3 for invalid command line arguments, + * 4 for a system call failure. + * + * fping cannot be run from web, it needs root privileges to be able to open raw socket :/ + * + * @access public + * @param mixed $subnet //CIDR + * @return void + */ + public function ping_address_method_fping_subnet ($subnet_cidr, $return_result = false) { + # verify ping path + $this->ping_verify_path ($this->settings->scanFPingPath); + # set command + $cmd = $this->settings->scanFPingPath." -c $this->icmp_count -t ".($this->icmp_timeout*1000)." -Ag $subnet_cidr \n"; + # execute command, return $retval + exec($cmd, $output, $retval); + + # save result + if(sizeof($output)>0) { + foreach($output as $line) { + $tmp = explode(" ",$line); + $out[] = $tmp[0]; + } + } + + # save to var + $this->fping_result = $out; + + # return result? + if($return_result) { return $out; } + + # return result for web or cmd + if($this->icmp_exit) { exit ($retval); } + else { return $retval; } + } + + /** + * Verifies that ping file exists + * + * @access private + * @param mixed $path + * @return void + */ + private function ping_verify_path ($path) { + if(!file_exists($path)) { + if($this->icmp_exit) { exit ($this->ping_exit_explain(1000)); } + else { return $this->Result->show("danger", _($this->ping_exit_explain(1000)), true); } + } + } + + /** + * Explains invalid error codes + * + * @access public + * @param mixed $code + * @return void + */ + public function ping_exit_explain ($code) { + # fetch explain codes + $explain_codes = $this->ping_set_exit_code_explains (); + + # return code + return isset($explain_codes[$code]) ? $explain_codes[$code] : false; + } + + /** + * This function sets ping exit code and message mappings + * + * http://www.freebsd.org/cgi/man.cgi?query=sysexits&apropos=0&sektion=0&manpath=FreeBSD+4.3-RELEASE&arch=default&format=ascii + * + * extend if needed for future scripts + * + * @access public + * @return void + */ + function ping_set_exit_code_explains () { + $explain_codes[0] = "SUCCESS"; + $explain_codes[1] = "OFFLINE"; + $explain_codes[2] = "ERROR"; + $explain_codes[3] = "UNKNOWN ERROR"; + $explain_codes[64] = "EX_USAGE"; + $explain_codes[65] = "EX_DATAERR"; + $explain_codes[68] = "EX_NOHOST"; + $explain_codes[70] = "EX_SOFTWARE"; + $explain_codes[71] = "EX_OSERR"; + $explain_codes[72] = "EX_OSFILE"; + $explain_codes[73] = "EX_CANTCREAT"; + $explain_codes[74] = "EX_IOERR"; + $explain_codes[75] = "EX_TEMPFAIL"; + $explain_codes[77] = "EX_NOPERM"; + $explain_codes[1000] = "Invalid ping path"; + # return codes + return $explain_codes; + } + + /** + * Update lastseen field for specific IP address + * + * @access public + * @param int $id + * @return void + */ + public function ping_update_lastseen ($id) { + # execute + try { $this->Database->updateObject("ipaddresses", array("id"=>$id, "lastSeen"=>date("Y-m-d H:i:s")), "id"); } + catch (Exception $e) { + !$this->debugging ? : $this->Result->show("danger", $e->getMessage(), false); + # log + !$this->debugging ? : write_log ("status_update", _('Failed to update address status'), 0 ); + } + } + + /** + * Opens socket connection on specified TCP ports, if at least one is available host is alive + * + * @access public + * @param mixed $address + * @param mixed $port + * @return void + */ + public function telnet_address ($address, $port) { + # set all ports + $ports = explode(",", str_replace(";",",",$port)); + # default response is dead + $retval = 1; + //try each port untill one is alive + foreach($ports as $p) { + // open socket + $conn = @fsockopen($address, $p, $errno, $errstr, $this->icmp_timeout); + //failed + if (!$conn) {} + //success + else { + $retval = 0; //set return as port if alive + fclose($conn); + break; //end foreach if success + } + } + # exit with result + exit($retval); + } + + + + + + + + + + /** + * @prepare addresses methods + * -------------------------------- + */ + + /** + * Returns all addresses to be scanned or updated + * + * @access public + * @param mixed $type //discovery, update + * @param mixed $subnet + * @return void + */ + public function prepare_addresses_to_scan ($type, $subnet) { + # discover new addresses + if($type=="discovery") { return is_numeric($subnet) ? $this->prepare_addresses_to_discover_subnetId ($subnet) : $this->prepare_addresses_to_discover_subnet ($subnet); } + # update addresses statuses + elseif($type=="update") { return $this->prepare_addresses_to_update ($subnet); } + # fail + else { die(json_encode(array("status"=>1, "error"=>"Invalid scan type provided"))); } + } + + /** + * Returns array of all addresses to be scanned inside subnet defined with subnetId + * + * @access public + * @param mixed $subnetId + * @return void + */ + public function prepare_addresses_to_discover_subnetId ($subnetId) { + # initialize classes + $Subnets = new Subnets ($this->Database); + + //subnet ID is provided, fetch subnet + $subnet = $Subnets->fetch_subnet(null, $subnetId); + if($subnet===false) { die(json_encode(array("status"=>1, "error"=>"Invalid subnet ID provided"))); } + + // we should support only up to 4094 hosts! + if($Subnets->get_max_hosts ($subnet->mask, "IPv4")>4094) { die(json_encode(array("status"=>1, "error"=>"Scanning from GUI is only available for subnets up to /20 or 4094 hosts!"))); } + + # set array of addresses to scan, exclude existing! + $ip = $this->get_all_possible_subnet_addresses ($subnet->subnet, $subnet->mask); + + # remove existing + $ip = $this->remove_existing_subnet_addresses ($ip, $subnetId); + + //none to scan? + if(sizeof($ip)==0) { die(json_encode(array("status"=>1, "error"=>"Didn't find any address to scan!"))); } + + //return + return $ip; + } + + /** + * Fetches all possible subnet addresses + * + * @access private + * @param $subnet //subnet in decimal format + * @param int $mask //subnet mask + * @return void //array of ip addresses in decimal format + */ + private function get_all_possible_subnet_addresses ($subnet, $mask) { + # initialize classes + $Subnets = new Subnets ($this->Database); + # make sure we have proper subnet format + $subnet = $Subnets->transform_address($subnet, "decimal"); + //fetch start and stop addresses + $boundaries = (object) $Subnets->get_network_boundaries ($subnet, $mask); + //create array + if($mask==32) { + $ip[] = $Subnets->transform_to_decimal($boundaries->network); + } + elseif($mask==31) { + $ip[] = $Subnets->transform_to_decimal($boundaries->network); + $ip[] = $Subnets->transform_to_decimal($boundaries->broadcast); + } + else { + //set loop limits + $start = $Subnets->transform_to_decimal($boundaries->network)+1; + $stop = $Subnets->transform_to_decimal($boundaries->broadcast); + //loop + for($m=$start; $m<$stop; $m++) { + $ip[] = $m; + } + } + //return + return $ip; + } + + /** + * Removes existing addresses from + * + * @access private + * @param mixed $ip //array of ip addresses in decimal format + * @param mixed $subnetId //id of subnet + * @return void + */ + private function remove_existing_subnet_addresses ($ip, $subnetId) { + # first fetch all addresses + $Addresses = new Addresses ($this->Database); + // get all existing IP addresses in subnet + $addresses = $Addresses->fetch_subnet_addresses($subnetId); + // if some exist remove them + if(sizeof($addresses)>0 && sizeof(@$ip)>0) { + foreach($addresses as $a) { + $key = array_search($a->ip_addr, $ip); + if($key !== false) { + unset($ip[$key]); + } + } + //reindex array for pinging + $ip = array_values(@$ip); + } + //return + return is_array(@$ip) ? $ip : array(); + } + + /** + * Returns array of all addresses in subnet + * + * @access public + * @param mixed $subnet + * @return void + */ + public function prepare_addresses_to_discover_subnet ($subnet) { + //set subnet / mask + $subnet_parsed = explode("/", $subnet); + # result + $ip = $this->get_all_possible_subnet_addresses ($subnet_parsed[0], $subnet_parsed[1]); + //none to scan? + if(sizeof($ip)==0) { die(json_encode(array("status"=>1, "error"=>"Didn't find any address to scan!"))); } + //result + return $ip; + } + + /** + * Returns array of all addresses to be scanned + * + * @access public + * @param mixed $subnetId + * @return void + */ + public function prepare_addresses_to_update ($subnetId) { + # first fetch all addresses + $Addresses = new Addresses ($this->Database); + // get all existing IP addresses in subnet + $subnet_addresses = $Addresses->fetch_subnet_addresses($subnetId); + //create array + if(sizeof($subnet_addresses)>0) { + foreach($subnet_addresses as $a) { + $scan_addresses[$a->id] = $a->ip_addr; + } + //reindex + $scan_addresses = array_values(@$scan_addresses); + //return + return $scan_addresses; + } + else { + return array(); + } + } + + +} \ No newline at end of file diff --git a/functions/classes/class.Sections.php b/functions/classes/class.Sections.php new file mode 100644 index 000000000..3eea28a05 --- /dev/null +++ b/functions/classes/class.Sections.php @@ -0,0 +1,567 @@ +Database = $database; + # initialize Result + $this->Result = new Result (); + } + + /** + * Strip tags from array or field to protect from XSS + * + * @access public + * @param mixed $input + * @return void + */ + public function strip_input_tags ($input) { + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = strip_tags($v); } + } + else { + $input = strip_tags($input); + } + # stripped + return $input; + } + + /** + * Changes empty array fields to specified character + * + * @access public + * @param array $fields + * @param string $char (default: "/") + * @return array + */ + public function reformat_empty_array_fields ($fields, $char = "/") { + foreach($fields as $k=>$v) { + if(is_null($v) || strlen($v)==0) { + $out[$k] = $char; + } else { + $out[$k] = $v; + } + } + # result + return $out; + } + + /** + * Function to verify checkbox if 0 length + * + * @access public + * @param mixed $field + * @return void + */ + public function verify_checkbox ($field) { + return @$field==""||strlen(@$field)==0 ? 0 : $field; + } + + + + + + + + + + + /** + * @update section methods + * -------------------------------- + */ + + /** + * Modify section + * + * @access public + * @param mixed $action + * @param mixed $values + * @return void + */ + public function modify_section ($action, $values) { + # strip tags + $values = $this->strip_input_tags ($values); + + # fetch user + $User = new User ($this->Database); + $this->user = $User->user; + + # execute based on action + if($action=="add") { return $this->section_add ($values); } + elseif($action=="edit") { return $this->section_edit ($values); } + elseif($action=="delete") { return $this->section_delete ($values); } + elseif($action=="reorder") { return $this->section_reorder ($values); } + else { return $this->Result->show("danger", _("Invalid action"), true); } + } + + /** + * Creates new section + * + * @access private + * @param mixed $values + * @return void + */ + private function section_add ($values) { + # null empty values + $values = $this->reformat_empty_array_fields ($values, null); + + # unset possible delegates permissions and id + unset($values['delegate'], $values['id']); + + # execute + try { $this->Database->insertObject("sections", $values); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + write_log( "Sections creation", "Failed to create new section
    ".$e->getMessage()."
    ".array_to_log($values), 2, $this->user->username); + return false; + } + # save id + $this->lastInsertId = $this->Database->lastInsertId(); + # write changelog + write_changelog('section', "delete", 'success', array(), $values); + # ok + write_log( "$table object creation", "New $table database object createdt
    ".array_to_log($values), 0, $this->user->username); + return true; + } + + /** + * Edit existing section + * + * @access private + * @param mixed $values + * @return void + */ + private function section_edit ($values) { + # null empty values + $values = $this->reformat_empty_array_fields ($values, NULL); + + # save old values + $old_section = $this->fetch_section ("id", $values['id']); + + # set delegations + if(@$values['delegate']==1) { $delegate = true; } + + # unset possible delegates permissions and id + unset($values['delegate']); + + # execute + try { $this->Database->updateObject("sections", $values, "id"); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + write_log( "Section $old_section->name edit", "Failed to edit section $old_section->name
    ".$e->getMessage()."
    ".array_to_log($values), 2, $this->user->username); + return false; + } + + # delegate permissions if requested + if(@$delegate) { + if(!$this->delegate_section_permissions ($values['id'], $values['permissions'])) { $this->Result->show("danger", _("Failed to delegate permissions"), false); } + } + # write changelog + write_changelog('section', "edit", 'success', $old_section, $values); + # ok + write_log( "Section $old_section->name edit", "Section $old_section->name edited
    ".array_to_log($values), 0, $this->user->username); + return true; + } + + /** + * Delete section, subsections, subnets and ip addresses + * + * @access private + * @param mixed $values + * @return void + */ + private function section_delete ($values) { + # subnets class + $Subnets = new Subnets ($this->Database); + + # save old values + $old_section = $this->fetch_section ("id", $values['id']); + + # check for subsections and store all ids + $all_ids = $this->get_all_section_and_subsection_ids ($values['id']); //array of section + all subsections + + # truncate and delete all subnets in all sections, than delete sections + foreach($all_ids as $id) { + $section_subnets = $Subnets->fetch_section_subnets ($id); + if(sizeof($section_subnets)>0) { + foreach($section_subnets as $ss) { + //delete subnet + $Subnets->modify_subnet("delete", array("id"=>$ss->id)); + } + } + # delete all sections + try { $this->Database->deleteRow("sections", "id", $id); } + catch (Exception $e) { + write_log( "Section $old_section->name delete", "Failed to delete section $old_section->name
    ".$e->getMessage()."
    ".array_to_log($values), 2, $this->user->username); + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + } + + # write changelog + write_changelog('section', "delete", 'success', $old_section, array()); + # log + write_log( "Section $old_section->name delete", "Section $old_section->name deleted
    ".array_to_log($old_section), 0, $this->user->username); + return true; + } + + /** + * Updates section order + * + * @access private + * @param mixed $order + * @return void + */ + private function section_reorder ($order) { + # update each section + foreach($order as $key=>$o) { + # execute + try { $this->Database->updateObject("sections", array("order"=>$o, "id"=>$key), "id"); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + } + return true; + } + + + + + + + + + + + /** + * @fetch section methods + * -------------------------------- + */ + + /** + * fetches all available sections + * + * @access public + * @param string $order_by (default: "order") + * @param bool $sort_asc (default: true) + * @return void + */ + public function fetch_all_sections ($order_by="order", $sort_asc=true) { + # fetch all + try { $sections = $this->Database->getObjects("sections", $order_by, $sort_asc); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save them to array + if(sizeof($sections)>0) { + foreach($sections as $s) { + $this->sections[$s->id] = $s; + } + } + # response + return sizeof($sections)>0 ? $sections : false; + } + + /** + * Alias for fetch_all_sections + * + * @param string $order_by (default: "order") + * @param bool $sort_asc (default: true) + * @return void + */ + public function fetch_sections ($order_by="order", $sort_asc=true) { + return $this->fetch_all_sections (); + } + + /** + * fetches section by specified method + * + * @access public + * @param string $method (default: "id") + * @param mixed $id + * @return void + */ + public function fetch_section ($method, $id) { + # null method + $method = is_null($method) ? "id" : $this->Database->escape($method); + # check cache first + if(isset($this->sections[$id])) { + return $this->sections[$id]; + } + else { + try { $section = $this->Database->getObjectQuery("SELECT * FROM `sections` where `$method` = ? limit 1;", array($id)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to sections + $this->sections[$id] = $section; + #result + return $section; + } + } + + /** + * Fetch subsections for specified sectionid + * + * @access public + * @param mixed $sectionid + * @return void + */ + public function fetch_subsections ($sectionid) { + try { $subsections = $this->Database->getObjectsQuery("SELECT * FROM `sections` where `masterSection` = ? limit 1;", array($sectionid)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return sizeof($subsections)>0 ? $subsections : array(); + } + + /** + * Fetches ids of section and possible subsections for deletion + * + * @access private + * @param int $id + * @return void + */ + private function get_all_section_and_subsection_ids ($id) { + # check for subsections and store all ids + $subsections = $this->fetch_subsections ($id); + if(sizeof($subsections)>0) { + foreach($subsections as $ss) { + $subsections_ids[] = $ss->id; + } + } + else { + $subsections_ids = array(); + } + //array of section + all subsections + return array_filter(array_merge($subsections_ids, array($id))); + } + + /** + * Fetches all vlans in section + * + * @access public + * @param mixed $sectionId + * @return void + */ + public function fetch_section_vlans ($sectionId) { + # set query + $query = "select distinct(`v`.`vlanId`),`v`.`name`,`v`.`number`, `v`.`description` from `subnets` as `s`,`vlans` as `v` where `s`.`sectionId` = ? and `s`.`vlanId`=`v`.`vlanId` order by `v`.`number` asc;"; + # fetch + try { $vlans = $this->Database->getObjectsQuery($query, array($sectionId)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return sizeof($vlans)>0 ? $vlans : false; + } + + /** + * Fetches all vrfs in section + * + * @access public + * @param mixed $sectionId + * @return void + */ + public function fetch_section_vrfs ($sectionId) { + # set query + $query = "select distinct(`v`.`vrfId`),`v`.`name`,`v`.`description` from `subnets` as `s`,`vrf` as `v` where `s`.`sectionId` = ? and `s`.`vrfId`=`v`.`vrfId` order by `v`.`name` asc;"; + # fetch + try { $vrfs = $this->Database->getObjectsQuery($query, array($sectionId)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return sizeof($vrfs)>0 ? $vrfs : false; + } + + + public function fetch_section_domains ($sectionId) { + # first fetch all domains + $Admin = new Admin ($this->Database, false); + $domains = $Admin->fetch_all_objects ("vlanDomains"); + # loop and check + foreach($domains as $d) { + //default + if($d->id==1) { + $permitted[] = $d->id; + } + else { + //array + if(in_array($sectionId, explode(";", $d->permissions))) { + $permitted[] = $d->id; + } + } + } + # return permitted + return $permitted; + } + + + + + + + + + + + /** + * @permission section methods + * -------------------------------- + */ + + /** + * Checks section permissions and returns group privilege for each section + * + * @access public + * @param mixed $permissions + * @return void + */ + public function parse_section_permissions($permissions) { + # save to array + $permissions = json_decode($permissions, true); + # start Tools object + $Tools = new Tools ($this->Database); + if(sizeof($permissions)>0) { + foreach($permissions as $key=>$p) { + $group = $Tools->fetch_object("userGroups", "g_id", $key); + $out[$group->g_id] = $p; + } + } + # return array of groups + return isset($out) ? $out : array(); + } + + /** + * returns permission level for specified section + * + * 3 = read/write/admin + * 2 = read/write + * 1 = read + * 0 = no access + * + * @access public + * @param obj $user + * @param int $sectionid + * @return void + */ + public function check_permission ($user, $sectionid) { + # decode groups user belongs to + $groups = json_decode($user->groups); + + # admins always has permission rwa + if($user->role == "Administrator") { return 3; } + else { + # fetch section details and check permissions + $section = $this->fetch_section ("id", $sectionid); + $sectionP = json_decode($section->permissions); + + # default permission is no access + $out = 0; + + # for each group check permissions, save highest to $out + if(sizeof($sectionP)>0) { + foreach($sectionP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + if(sizeof($groups)>0) { + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + } + # return permission level + return $out; + } + } + + /** + * This function returns permissions of group_id for each section + * + * @access public + * @param int $gid //id of group to verify permissions + * @param bool $name (default: true) //should index be name or id? + * @return array + */ + public function get_group_section_permissions ($gid, $name = true) { + # fetch all sections + $sections = $this->fetch_all_sections(); + + # loop through sections and check if group_id in permissions + foreach($sections as $section) { + $p = json_decode($section->permissions, true); + if(sizeof($p)>0) { + if($name) { + if(array_key_exists($gid, $p)) { + $out[$section->name] = $p[$gid]; + } + } + else { + if(array_key_exists($gid, $p)) { + $out[$section->id] = $p[$gid]; + } + } + } + # no permissions + else { + $out[$section->name] = 0; + } + } + # return + return $out; + } + + /** + * Delegates section permissions to all belonging subnets + * + * @access private + * @param mixed $sectionId + * @param mixed $permissions + * @return void + */ + private function delegate_section_permissions ($sectionId, $permissions) { + try { $this->Database->updateObject("subnets", array("permissions"=>$permissions, "sectionId"=>$sectionId), "sectionId"); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + return true; + } +} +?> \ No newline at end of file diff --git a/functions/classes/class.Subnets.php b/functions/classes/class.Subnets.php new file mode 100644 index 000000000..d318cb692 --- /dev/null +++ b/functions/classes/class.Subnets.php @@ -0,0 +1,2648 @@ +Database = $database; + # initialize Result + $this->Result = new Result (); + } + + /** + * fetches settings from database + * + * @access private + * @return void + */ + private function get_settings () { + # cache check + if($this->settings == false) { + try { $this->settings = $this->Database->getObject("settings", 1); } + catch (Exception $e) { $this->Result->show("danger", _("Database error: ").$e->getMessage()); } + } + } + + /** + * Returns array of subnet ordering + * + * @access private + * @return void + */ + private function get_subnet_order () { + $this->get_settings (); + return explode(",", $this->settings->subnetOrdering); + } + + /** + * Strip tags from array or field to protect from XSS + * + * @access public + * @param mixed $input + * @return void + */ + public function strip_input_tags ($input) { + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = strip_tags($v); } + } + else { + $input = strip_tags($input); + } + # stripped + return $input; + } + + /** + * Changes empty array fields to specified character + * + * @access public + * @param array $fields + * @param string $char (default: "/") + * @return array + */ + public function reformat_empty_array_fields ($fields, $char = "/") { + foreach($fields as $k=>$v) { + if(is_null($v) || strlen($v)==0) { + $out[$k] = $char; + } else { + $out[$k] = $v; + } + } + # result + return $out; + } + + /** + * Function to verify checkbox if 0 length + * + * @access public + * @param mixed $field + * @return void + */ + public function verify_checkbox ($field) { + return @$field==""||strlen(@$field)==0 ? 0 : $field; + } + + /** + * Initializes PEAR Net IPv4 object + * + * @access private + * @return void + */ + private function initialize_pear_net_IPv4 () { + //initialize NET object + if(!is_object($this->Net_IPv4)) { + require_once( dirname(__FILE__) . '/../../functions/PEAR/Net/IPv4.php' ); + //initialize object + $this->Net_IPv4 = new Net_IPv4(); + } + } + /** + * Initializes PEAR Net IPv6 object + * + * @access private + * @return void + */ + private function initialize_pear_net_IPv6 () { + //initialize NET object + if(!is_object($this->Net_IPv6)) { + require_once( dirname(__FILE__) . '/../../functions/PEAR/Net/IPv6.php' ); + //initialize object + $this->Net_IPv6 = new Net_IPv6(); + } + } + + + + + + + + + + + + + /** + * @update subnets methods + * -------------------------------- + */ + + /** + * Modify subnet details main method + * + * @access public + * @param mixed $action + * @param mixed $values + * @return void + */ + public function modify_subnet ($action, $values) { + # strip tags + $values = $this->strip_input_tags ($values); + + # fetch user + $User = new User ($this->Database); + $this->user = $User->user; + + # execute based on action + if($action=="add") { return $this->subnet_add ($values); } + elseif($action=="edit") { return $this->subnet_edit ($values); } + elseif($action=="delete") { return $this->subnet_delete ($values['id']); } + elseif($action=="truncate") { return $this->subnet_truncate ($values['id']); } + elseif($action=="resize") { return $this->subnet_resize ($values['id'], $values['mask']); } + else { return $this->Result->show("danger", _("Invalid action"), true); } + } + + /** + * Create new subnet method + * + * @access private + * @param mixed $values + * @return void + */ + private function subnet_add ($values) { + # null empty values + $values = $this->reformat_empty_array_fields ($values, null); + + # execute + try { $this->Database->insertObject("subnets", $values); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + write_log( "Subnet creation", "Failed to add new subnet
    ".$e->getMessage(), 2, $this->User->username); + return false; + } + # save id + $this->lastInsertId = $this->Database->lastInsertId(); + # ok + write_log( "Subnet creation", "New subnet created
    ".array_to_log($values), 0, $this->User->username); + return true; + } + + /** + * Edit subnet + * + * needed for API only + * + * @access private + * @param mixed $values + * @return void + */ + private function subnet_edit ($values) { + # null empty values + $values = $this->reformat_empty_array_fields ($values, null); + + # execute + try { $this->Database->updateObject("subnets", $values, "id"); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + write_log( "Subnet edit", "Failed to edit subnet
    ".$e->getMessage(), 2, $this->User->username); + return false; + } + # save ID + $this->lastInsertId = $this->Database->lastInsertId(); + # ok + write_log( "Subnet edit", "Subnet edited
    ".array_to_log($values), 0, $this->User->username); + return true; + } + + /** + * Deletes subnet and truncates all IP addresses in that subnet + * + * @access private + * @param mixed $id + * @return void + */ + private function subnet_delete ($id) { + # save old values + $old_subnet = $this->fetch_subnet (null, $id); + + # first truncate it + $this->subnet_truncate ($id); + + # delete subnet + try { $this->Database->deleteRow("subnets", "id", $id); } + catch (Exception $e) { + write_log( "Subnet delete", "Failed to delete subnet $old_subnet->name
    ".$e->getMessage(), 2, $this->User->username); + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + return false; + } + # ok + write_log( "Subnet delete", "Subnet $old_subnet->name deleted
    ".array_to_log($old_subnet), 0, $this->User->username); + # write changelog + write_changelog('subnet', "delete", 'success', array(), $old_subnet); + return true; + } + + /** + * Truncate specified subnet (delete all IP addresses in that subnet) + * + * @access public + * @param mixed $values + * @return void + */ + public function subnet_truncate ($subnetId) { + # save old values + $old_subnet = $this->fetch_subnet (null, $subnetId); + # execute + try { $this->Database->deleteRow("ipaddresses", "subnetId", $subnetId); } + catch (Exception $e) { + write_log( "Subnet truncate", "Failed to truncate subnet id $subnetId
    ".$e->getMessage(), 2, $this->User->username); + $this->Result->show("danger", _("Error: ").$e->getMessage(), true); + } + write_log( "Subnet truncate", "Subnet $old_subnet->name truncated", 0, $this->User->username); + return true; + } + + /** + * Resize subnet with new mask + * + * @access private + * @param mixed $subnetId + * @param mixed $mask + * @return void + */ + private function subnet_resize ($subnetId, $mask) { + # save old values + $old_subnet = $this->fetch_subnet (null, $subnetId); + # execute + try { $this->Database->updateObject("subnets", array("id"=>$subnetId, "mask"=>$mask), "id"); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage(), false); + write_log( "Subnet edit", "Failed to resize subnet
    ".$e->getMessage(), 2, $this->User->username); + return false; + } + # ok + write_log( "Subnet resize", "Subnet resized
    ".array_to_log(array("id"=>$subnetId, "mask"=>$mask)), 0, $this->User->username); + return true; + } + + /** + * This function splits subnet into smaller subnets + * + * @access private + * @param mixed $subnet_old + * @param mixed $number + * @param mixed $prefix + * @param string $group (default: "yes") + * @param string $strict (default: "yes") + * @return void + */ + public function subnet_split ($subnet_old, $number, $prefix, $group="yes", $strict="yes") { + + # we first need to check if it is ok to split subnet and get parameters + $check = $this->verify_subnet_split ($subnet_old, $number, $group, $strict); + + # ok, extract parameters from result array - 0 is $newsubnets and 1 is $addresses + $newsubnets = $check[0]; + $addresses = $check[1]; + + # admin object + $Admin = new Admin ($this->Database, false); + + # create new subnets and change subnetId for recalculated hosts + $m = 0; + foreach($newsubnets as $subnet) { + //set new subnet insert values + $values = array("description"=>strlen($prefix)>0 ? $prefix.($m+1) : "split_subnet_".($m+1), + "subnet"=>$subnet['subnet'], + "mask"=>$subnet['mask'], + "sectionId"=>$subnet['sectionId'], + "masterSubnetId"=>$subnet['masterSubnetId'], + "vlanId"=>@$subnet['vlanId'], + "vrfId"=>@$subnet['vrfId'], + "allowRequests"=>@$subnet['allowRequests'], + "showName"=>@$subnet['showName'] + ); + //create new subnets + $this->modify_subnet ("add", $values); + + //get all address ids + unset($ids); + foreach($addresses as $ip) { + if($ip->subnetId == $m) { + $ids[] = $ip->id; + } + } + + //replace all subnetIds in IP addresses to new subnet + if(isset($ids)) { + if(!$Admin->object_modify("ipaddresses", "edit-multiple", $ids, array("subnetId"=>$this->lastInsertId))) { $Result->show("danger", _("Failed to move IP address"), true); } + } + + # next + $m++; + } + + # do we need to remove old subnet? + if($group!="yes") { + if(!$Admin->object_modify("subnets", "delete", "id", array("id"=>$subnet_old->id))) { $Result->show("danger", _("Failed to remove old subnet"), true); } + } + + # result + return true; + } + + + + + + + + + + + + + /** + * @subnet functions + * ------------------------------- + */ + + /** + * Fetches subnetd by specified method + * + * @access public + * @param string $method (default: "id") + * @param mixed $id + * @return void + */ + public function fetch_subnet ($method=null, $id) { + # null method + $method = is_null($method) ? "id" : $this->Database->escape($method); + # check cache first + if(isset($this->subnets[$id])) { + return $this->subnets[$id]; + } + else { + try { $subnet = $this->Database->getObjectQuery("SELECT * FROM `subnets` where `$method` = ? limit 1;", array($id)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to subnets cache + if(sizeof($subnet)>0) { + # add decimal format + $subnet->ip = $this->transform_to_dotted ($subnet->subnet); + # save to subnets + $this->subnets[$subnet->id] = (object) $subnet; + } + #result + return $subnet; + } + } + + /** + * Fetches all subnets in specified section + * + * @access public + * @param mixed $sectionId + * @param mixed $orderType + * @param mixed $orderBy + * @return void + */ + public function fetch_section_subnets ($sectionId) { + # check order + $this->get_settings (); + $order = $this->get_subnet_order (); + # fetch + try { $subnets = $this->Database->getObjectsQuery("SELECT * FROM `subnets` where `sectionId` = ? order by isFolder desc, $order[0] $order[1];", array($sectionId)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to subnets cache + if(sizeof($subnets)>0) { + foreach($subnets as $subnet) { + # add decimal format + $subnet->ip = $this->transform_to_dotted ($subnet->subnet); + # save to subnets + $this->subnets[$subnet->id] = (object) $subnet; + } + } + # result + return sizeof($subnets)>0 ? (array) $subnets : array(); + } + + /** + * This function fetches id, subnet and mask for all subnets + * + * Needed for search > search_subnets_inside + * + * @access public + * @return void + */ + public function fetch_all_subnets_search () { + # fetch + try { $subnets = $this->Database->getObjectsQuery("SELECT `id`,`subnet`,`mask` FROM `subnets`;"); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return $subnets; + } + + /** + * This function fetches id, subnet and mask for all subnets + * + * Needed for pingCheck script + * + * @access public + * @return void + */ + public function fetch_all_subnets_for_pingCheck () { + # fetch + try { $subnets = $this->Database->getObjectsQuery("SELECT `id`,`subnet`,`sectionId`,`mask` FROM `subnets` where `pingSubnet` = 1;"); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return sizeof($subnets)>0 ? $subnets : false; + } + + /** + * This function fetches id, subnet and mask for all subnets + * + * Needed for discoveryCheck script + * + * @access public + * @return void + */ + public function fetch_all_subnets_for_discoveryCheck () { + # fetch + try { $subnets = $this->Database->getObjectsQuery("SELECT `id`,`subnet`,`sectionId`,`mask` FROM `subnets` where `discoverSubnet` = 1;"); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return sizeof($subnets)>0 ? $subnets : false; + } + + /** + * Fetches all subnets within section with specified vlan ID + * + * @access public + * @param mixed $vlanId + * @param mixed $sectionId + * @return void + */ + public function fetch_vlan_subnets ($vlanId, $sectionId=null) { + # fetch settings and set subnet ordering + $this->get_settings(); + $order = $this->get_subnet_order (); + + # fetch section and set section ordering + $Sections = new Sections ($this->Database); + $section = $Sections->fetch_section (null, $sectionId); + + # section ordering - overrides network + if(@$section->subnetOrdering!="default" && strlen(@$section->subnetOrdering)>0 ) { $order = explode(",", $section->subnetOrdering); } + else { $order = $this->get_subnet_order (); } + + # set query + if(!is_null($sectionId)) { + $query = "select * from `subnets` where `vlanId` = ? and `sectionId` = ? ORDER BY isFolder desc, ? ?;"; + $params = array($vlanId, $sectionId, $order[0], $order[1]); + } + else { + $query = "select * from `subnets` where `vlanId` = ? ORDER BY isFolder desc, ? ?;"; + $params = array($vlanId, $order[0], $order[1]); + } + + # fetch + try { $subnets = $this->Database->getObjectsQuery($query, $params); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to subnets cache + if(sizeof($subnets)>0) { + foreach($subnets as $subnet) { + # add decimal format + $subnet->ip = $this->transform_to_dotted ($subnet->subnet); + # save to subnets + $this->subnets[$subnet->id] = (object) $subnet; + } + } + # result + return sizeof($subnets)>0 ? (array) $subnets : false; + } + + + /** + * Checks if subnet is in vlan + * + * @access private + * @param mixed $subnetId + * @param mixed $vlanId + * @return void + */ + private function is_subnet_in_vlan ($subnetId, $vlanId) { + # fetch subnet details + $subnet = $this->fetch_subnet ("id", $subnetId); + # same id? + return @$subnet->vlanId==$vlanId ? true : false; + } + + + /** + * Fetches all subnets within section with specified vrf ID + * + * @access public + * @param mixed $vrfId + * @param mixed $sectionId + * @return void + */ + public function fetch_vrf_subnets ($vrfId, $sectionId=null) { + # fetch settings and set subnet ordering + $this->get_settings(); + $order = $this->get_subnet_order (); + + # fetch section and set section ordering + $Sections = new Sections ($this->Database); + $section = $Sections->fetch_section (null, $sectionId); + + # section ordering - overrides network + if(@$section->subnetOrdering!="default" && strlen(@$section->subnetOrdering)>0 ) { $order = explode(",", $section->subnetOrdering); } + else { $order = $this->get_subnet_order (); } + + # set query + if(!is_null($sectionId)) { + $query = "select * from `subnets` where `vrfId` = ? and `sectionId` = ? ORDER BY isFolder desc, ? ?;"; + $params = array($vrfId, $sectionId, $order[0], $order[1]); + } + else { + $query = "select * from `subnets` where `vrfId` = ? ORDER BY isFolder desc, ? ?;"; + $params = array($vrfId, $order[0], $order[1]); + } + + # fetch + try { $subnets = $this->Database->getObjectsQuery($query, $params); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to subnets cache + if(sizeof($subnets)>0) { + foreach($subnets as $subnet) { + # add decimal format + $subnet->ip = $this->transform_to_dotted ($subnet->subnet); + # save to subnets + $this->subnets[$subnet->id] = (object) $subnet; + } + } + # result + return sizeof($subnets)>0 ? (array) $subnets : false; + } + + /** + * Checks if subnet is in vrf + * + * @access private + * @param mixed $subnetId + * @param mixed $vrfId + * @return void + */ + private function is_subnet_in_vrf ($subnetId, $vrfId) { + # fetch subnet details + $subnet = $this->fetch_subnet ("id", $subnetId); + # same id? + return @$subnet->vrfId==$vrfId ? true : false; + } + + /** + * Checks for all subnets that are marked for scanning and new hosts discovery + * + * @access public + * @return void + */ + public function fetch_scanned_subnets () { + // set query + $query = "select * from `subnets` where `pingSubnet`=1 or `discoverSubnet`=1;"; + # fetch + try { $subnets = $this->Database->getObjectsQuery($query); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + return sizeof($subnets)>0 ? $subnets : false; + } + + /** + * Finds gateway in subnet + * + * @access public + * @param int $subnetId + * @return void + */ + public function find_gateway ($subnetId) { + // set query + $query = "select `ip_addr`,`id` from `ipaddresses` where `subnetId` = ? and `is_gateway` = 1;"; + # fetch + try { $gateway = $this->Database->getObjectQuery($query, array($subnetId)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + return sizeof($gateway)>0 ? $gateway : false; + } + + /** + * Returns all IPv4 subnet masks with different presentations + * + * @access public + * @return void + */ + public function get_ipv4_masks () { + # loop masks + for($mask=30; $mask>=8; $mask--) { + // initialize + $out[$mask] = new StdClass (); + + // fake cidr + $this->initialize_pear_net_IPv4 (); + $net = Net_IPv4::parseAddress("10.0.0.0/$mask"); + + // set + $out[$mask]->bitmask = $mask; // bitmask + $out[$mask]->netmask = $net->netmask; // netmask + $out[$mask]->host_bits = 32-$mask; // host bits + $out[$mask]->subnet_bits = 32-$out[$mask]->host_bits; // network bits + $out[$mask]->hosts = number_format($this->get_max_hosts ($mask, "IPv4"), 0, ",", "."); // max hosts + $out[$mask]->subnets = number_format(pow(2,($mask-8)), 0, ",", "."); + + // binary + $parts = explode(".", $net->netmask); + foreach($parts as $k=>$p) { $parts[$k] = str_pad(decbin($p),8, 0); } + $out[$mask]->binary = implode(".", $parts); + } + # return result + return $out; + } + + + + + + + + + + /** + * @slave subnet functions + * ------------------------------- + */ + + /** + * Checks if subnet has any slaves + * + * @access public + * @param mixed $subnetid + * @return void + */ + public function has_slaves ($subnetid) { + try { $count = $this->Database->numObjectsFilter("subnets", "masterSubnetId", $subnetid); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # result + return $count>0 ? true : false; + } + + /** + * Fetches all immediate slave subnets for specified subnetId + * + * @access public + * @param mixed $subnetid + * @return void + */ + public function fetch_subnet_slaves ($subnetid) { + try { $slaves = $this->Database->getObjectsQuery("SELECT * FROM `subnets` where `masterSubnetId` = ? order by `subnet` asc;", array($subnetid)); } + catch (Exception $e) { + $this->Result->show("danger", _("Error: ").$e->getMessage()); + return false; + } + # save to subnets cache + if(sizeof($slaves)>0) { + foreach($slaves as $slave) { + # add decimal format + $slave->ip = $this->transform_to_dotted ($slave->subnet); + # save to subnets + $this->subnets[$slave->id] = (object) $slave; + } + return $slaves; + } + # no subnets + return false; + } + + /** + * Recursively fetches all slaves + * + * @access public + * @param mixed $subnetId + * @return void + */ + public function fetch_subnet_slaves_recursive ($subnetId) { + $end = false; //loop break flag + # slaves array of id's, add current + $this->slaves[] = (int) $subnetId; //id + + # loop + while($end == false) { + # fetch all immediate slaves + $slaves2 = $this->fetch_subnet_slaves ($subnetId); + + # we have more slaves + if($slaves2) { + # recursive + foreach($slaves2 as $slave) { + # save to full array of slaves + $this->slaves_full[$slave->id] = $slave; + # fetch possible new slaves + $this->fetch_subnet_slaves_recursive ($slave->id); + $end = true; + } + } + # no more slaves + else { + $end = true; + } + } + } + + /** + * Resets array of all slave id's. Needs to be called *BEFORE* all slaves are fetched to prevent stacking + * + * Before reset we should save array to prevent double checking! + * + * @access public + * @return void + */ + public function reset_subnet_slaves_recursive () { + $this->slaves = null; + } + + /** + * Removes master subnet from slave subnets array + * + * @access public + * @param mixed $subnetId + * @return void + */ + public function remove_subnet_slaves_master ($subnetId) { + foreach($this->slaves as $k=>$s) { + if($s==$subnetId) { + unset($this->slaves[$k]); + } + } + } + + /** + * fetch whole tree path for subnetId - from slave to parents + * + * @access public + * @param mixed $subnetId + * @return void + */ + public function fetch_parents_recursive ($subnetId) { + $parents = array(); + $root = false; + + while($root == false) { + $subd = (array) $this->fetch_subnet("id", $subnetId); # get subnet details + if(sizeof($subd)>0) { + # not root yet + if(@$subd['masterSubnetId']!=0) { + array_unshift($parents, $subd['masterSubnetId']); + $subnetId = $subd['masterSubnetId']; + } + # root + else { + array_unshift($parents, $subd['masterSubnetId']); + $root = true; + } + } + else { + $root = true; + } + } + # return array + return $parents; + } + + + + + + + + + + + + + + /** + * @transform IP/subnet functions + * ------------------------------- + */ + + /** + * identify ip address type - ipv4 or ipv6 + * + * @access public + * @param mixed $subnet + * @return void + */ + public function identify_address ($address) { + # if cidr provided strip it ! + + # dotted representation + if (strpos($address, ":")) { return 'IPv6'; } + elseif (strpos($address, ".")) { return 'IPv4'; } + # decimal representation + else { + # IPv4 address + if(strlen($address) < 12) { return 'IPv4'; } + # IPv6 address + else { return 'IPv6'; } + } + } + + /** + * Identifies IP address format + * + * @access public + * @param mixed $address + * @return void + */ + private function identify_address_format ($address) { + return is_numeric($address) ? "decimal" : "dotted"; + } + + /** + * Alias of identify_address_format function + * + * @access public + * @param mixed $address + * @return void + */ + public function get_ip_version ($address) { + return $this->identify_address ($address); + } + + /** + * Transforms IP address to required format + * + * format cen be decimal (1678323323) or dotted (10.10.0.0) + * + * @access public + * @param mixed $address + * @param string $format (default: "dotted") + * @return void + */ + public function transform_address ($address, $format = "dotted") { + # no change + if($this->identify_address_format ($address) == $format) { return $address; } + else { + if($this->identify_address_format ($address) == "dotted") { return $this->transform_to_decimal ($address); } + else { return $this->transform_to_dotted ($address); } + } + } + + /** + * Transform IP address from decimal to dotted (167903488 -> 10.2.1.0) + * + * @access public + * @param mixed $address + * @return void + */ + public function transform_to_dotted ($address) { + if ($this->identify_address ($address) == "IPv4" ) { return(long2ip($address)); } + else { return(long2ip6($address)); } + } + + /** + * Transform IP address from dotted to decimal (10.2.1.0 -> 167903488) + * + * @access public + * @param mixed $address + * @return void + */ + public function transform_to_decimal ($address) { + if ($this->identify_address ($address) == "IPv4" ) { return( sprintf("%u", ip2long($address)) ); } + else { return(ip2long6($address)); } + } + + /** + * Calculate subnet usage + * + * used, maximum, free, free_percentage + * + * @access public + * @param mixed $used_hosts (int) + * @param mixed $netmask (int) + * @param mixed $subnet (int) + * @return void + */ + public function calculate_subnet_usage ($used_hosts, $netmask, $subnet) { + # set IP version + $ipversion = $this->get_ip_version ($subnet); + # set initial vars + $out['used'] = (int) $used_hosts; //set used hosts + $out['maxhosts'] = (int) $this->get_max_hosts ($netmask,$ipversion); //get maximum hosts + $out['freehosts'] = (int) gmp_strval(gmp_sub($out['maxhosts'],$out['used'])); //free hosts + $out['freehosts_percent'] = round((($out['freehosts'] * 100) / $out['maxhosts']),2); //free percentage + # result + return $out; + } + + /** + * Calculates detailed network usage - dhcp, active, ... + * + * @access public + * @param mixed $subnet //subnet in decimal format + * @param mixed $bitmask //netmask in decimal format + * @param mixed $addresses //all addresses to be calculated, either all slave or per subnet + * @return void + */ + public function calculate_subnet_usage_detailed ($subnet, $bitmask, $addresses) { + # get IP address count per address type + $details = $this->calculate_subnet_usage_sort_addresses ($addresses); + + # calculate max number of hosts + $details['maxhosts'] = $this->get_max_hosts($bitmask, $this->identify_address($subnet)); + # calculate free hosts + $details['freehosts'] = gmp_strval( gmp_sub ($details['maxhosts'] , $details['used']) ); + # calculate use percentage for each type + $details['freehosts_percent'] = round( ( ($details['freehosts'] * 100) / $details['maxhosts']), 2 ); + foreach($this->address_types as $t) { + $details[$t['type']."_percent"] = round( ( ($details[$t['type']] * 100) / $details['maxhosts']), 2 ); + } + return( $details ); + } + + /** + * Calculates subnet usage per host type + * + * @access public + * @param mixed $addresses + * @return void + */ + public function calculate_subnet_usage_sort_addresses ($addresses) { + $count['used'] = 0; //initial sum count + # fetch address types + $address_types = $this->get_addresses_types(); + # create array of keys with initial value of 0 + foreach($this->address_types as $a) { + $count[$a['type']] = 0; + } + # count + if($addresses) { + foreach($addresses as $ip) { + $count[$this->translate_address_type($ip->state)]++; + $count['used'] = gmp_strval(gmp_add($count['used'], 1)); + } + } + # result + return $count; + } + + /** + * Returns array of address types + * + * @access public + * @return void + */ + public function get_addresses_types () { + # from cache + if($this->address_types == null) { + # addresses class + $Addresses = new Addresses ($this->Database); + # fetch + $this->address_types = $Addresses->addresses_types_fetch(); + } + } + + /** + * Translates address type from index (int) to type + * + * e.g.: 0 > offline + * + * @access public + * @param mixed $index + * @return void + */ + public function translate_address_type ($index) { + # fetch + $all_types = $this->get_addresses_types(); + # return + return $this->address_types[$index]["type"]; + } + + /** + * Calculates subnet usage recursive for underlaying hosts + * + * @access public + * @param mixed $subnetId + * @param mixed $subnet + * @param mixed $netmask + * @param mixed $Addresses + * @return void + */ + public function calculate_subnet_usage_recursive ($subnetId, $subnet, $netmask, $Addresses) { + # identify address + $address_type = $this->get_ip_version ($subnet); + # fetch all slave subnets recursive + $this->reset_subnet_slaves_recursive (); + $this->fetch_subnet_slaves_recursive ($subnetId); + $this->remove_subnet_slaves_master ($subnetId); + + # go through each and calculate used hosts + # add +2 for subnet and broadcast if required + foreach($this->slaves_full as $s) { + # fetch all addresses + $used = (int) $used + $Addresses->count_subnet_addresses ($s->id); //add to used hosts calculation + # mask fix + if($address_type=="IPv4" && $netmask<31) { + $used = $used+1; + } + } + # we counted, now lets calculate and return result + return $this->calculate_subnet_usage ($used, $netmask, $subnet); + } + + /** + * Present numbers in pow 10, only for IPv6 + * + * @access public + * @param mixed $number + * @return void + */ + public function reformat_number ($number) { + $length = strlen($number); + $pos = $length - 3; + + if ($length > 8) { + $number = "~". substr($number, 0, $length - $pos) . "·10^". $pos .""; + } + return $number; + } + + /** + * Get maxumum number of hosts for netmask + * + * @access public + * @param mixed $netmask + * @param mixed $ipversion + * @param bool $strict (default: true) + * @return void + */ + public function get_max_hosts ($netmask, $ipversion, $strict=true) { + if($ipversion == "IPv4") { return $this->get_max_IPv4_hosts ($netmask, $strict); } + else { return $this->get_max_IPv6_hosts ($netmask, $strict); } + } + + /** + * Get max number of IPv4 hosts + * + * @access public + * @param mixed $netmask + * @return void + */ + public function get_max_IPv4_hosts ($netmask, $strict) { + if($netmask==31) { return 2; } + elseif($netmask==32) { return 1; } + elseif($strict===false) { return (int) pow(2, (32 - $netmask)); } + else { return (int) pow(2, (32 - $netmask)) -2; } + } + + /** + * Get max number of IPv6 hosts + * + * @access public + * @param mixed $netmask + * @return void + */ + public function get_max_IPv6_hosts ($netmask, $strict) { + return gmp_strval(gmp_pow(2, 128 - $netmask)); + } + + /** + * Returns maximum netmask length + * + * @access public + * @param mixed $address + * @return void + */ + private function get_max_netmask ($address) { + return $this->identify_address($address)=="IPv6" ? 128 : 32; + } + + /** + * Parses address + * + * @access public + * @param mixed $address + * @param mixed $netmask + * @return void + */ + public function get_network_boundaries ($address, $netmask) { + # make sure we have dotted format + $address = $this->transform_address ($address, "dotted"); + # set IP version + $ipversion = $this->get_ip_version ($address); + # return boundaries + if($ipversion == "IPv4") { return $this->get_IPv4_network_boundaries ($address, $netmask); } + else { return $this->get_IPv6_network_boundaries ($address, $netmask); } + } + + /** + * Returns IPv4 network boundaries + * + * network, host ip (if not network), broadcast, bitmask, netmask + * + * @access private + * @param mixed $address + * @param mixed $netmask + * @return void + */ + private function get_IPv4_network_boundaries ($address, $netmask) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv4 (); + # parse IP address + $net = $this->Net_IPv4->parseAddress( $address.'/'.$netmask ); + # return boundaries + return (array) $net; + } + + /** + * Returns IPv6 network boundaries + * + * network, host ip (if not network), broadcast, bitmask, netmask + * + * @access private + * @param mixed $address + * @param mixed $netmask + * @return void + */ + private function get_IPv6_network_boundaries ($address, $netmask) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv6 (); + # parse IPv6 subnet + $net = $this->Net_IPv6->parseaddress( "$address/$netmask"); + # set network and masks + $out = new StdClass(); + $out->start = $net['start']; + $out->network = $address; + $out->netmask = $netmask; + $out->bitmask = $netmask; + $out->broadcast = $net['end']; //highest IP address + # result + return (array) $out; + } + + /** + * Calculates first possible subnet from provided subnet and number of next free IP addresses + * + * @access public + * @param mixed $address + * @param mixed $free + * @param bool $print (default: true) + * @return void + */ + public function get_first_possible_subnet ($address, $free, $print = true) { + # set max possible mask for IP range + $maxmask = $this->get_max_netmask ($this->transform_address ($address, "dotted")); + + # calculate maximum possible IP mask + $mask = floor(log($free)/log(2)); + $mask = $maxmask - $mask; + + # we have now maximum mask. We need to verify if subnet is valid + # otherwise add 1 to $mask and go to $maxmask + for($m=$mask; $m<=$maxmask; $m++) { + # validate + $err = $this->verify_cidr_address( $address."/".$m , 1); + if($err===true) { + # ok, it is possible! + $result = $address."/".$m; + break; + } + } + + # result + if(isset($result)) { + # print or return? + if($print) print $result; + else return $result; + } + else { + return false; + } + } + + + + + + + + + + + /** + * @verify address functions + * ------------------------------- + */ + + /** + * Verifies CIDR address + * + * @access public + * @param mixed $cidr + * @param bool $issubnet (default: true) + * @return void + */ + public function verify_cidr_address ($cidr, $issubnet = true) { + # first verify address and mask format + if(strlen($error = $this->verify_cidr ($cidr))>0) { return $error; } + # make checks + return $this->identify_address ($cidr)=="IPv6" ? $this->verify_cidr_address_IPv6 ($cidr, $issubnet) : $this->verify_cidr_address_IPv4 ($cidr, $issubnet); + } + + /** + * Verifies IPv4 CIDR address + * + * @access public + * @param mixed $cidr + * @param bool $issubnet (default: true) + * @return void + */ + public function verify_cidr_address_IPv4 ($cidr, $issubnet = true) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv4 (); + # validate + if ($net = $this->Net_IPv4->parseAddress ($cidr)) { + if (!$this->Net_IPv4->validateIP ($net->ip)) { return _("Invalid IP address!"); } //validate IP + elseif (($net->network != $net->ip) && ($issubnet)) { return _("IP address cannot be subnet! (Consider using")." ". $net->network .")"; } //network must be same as provided IP address + elseif (!$this->Net_IPv4->validateNetmask ($net->netmask)) { return _('Invalid netmask').' ' . $net->netmask; } //validate netmask + else { return true; } + } + else { return _('Invalid CIDR format!'); } + } + + /** + * Verifies IPv6 CIDR address + * + * @access public + * @param mixed $cidr + * @param bool $issubnet (default: true) + * @return void + */ + public function verify_cidr_address_IPv6 ($cidr, $issubnet = true) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv6 (); + # validate + if (!$this->Net_IPv6->checkIPv6 ($cidr) ) { return _("Invalid IPv6 address!"); } + else { + $subnet = $this->Net_IPv6->getNetmask($cidr); //validate subnet + $subnet = $this->Net_IPv6->compress($subnet); //get subnet part + $subnetParse = explode("/", $cidr); + # validate that subnet is subnet + if ( ($subnetParse[0] != $subnet) && ($issubnet) ) { return _("IP address cannot be subnet! (Consider using")." ". $subnet ."/". $subnetParse[1] .")"; } + else { return true; } + } + } + + /** + * Verifies that CIDR address is correct + * + * @access public + * @param mixed $cidr + * @return void + */ + public function verify_cidr ($cidr) { + $cidr = explode("/", $cidr); + # verify network part + if(empty($cidr[0]) || empty($cidr[1])) { return _("Invalid CIDR format!"); } + # verify network part + if($this->identify_address_format ($cidr[0])!="dotted") { return _("Invalid Network!"); } + # verify mask + if(!is_numeric($cidr[1])) { return _("Invalid netmask"); } + if($this->get_max_netmask ($cidr[0])<$cidr[1]) { return _("Invalid netmask"); } + } + + /** + * Verifies if new subnet overlapps with any of existing subnets in that section and same or null VRF + * + * @access public + * @param int $sectionId + * @param CIDR $new_subnet + * @param int $vrfId (default: 0) + * @return void + */ + public function verify_subnet_overlapping ($sectionId, $new_subnet, $vrfId = 0) { + # fetch section subnets + $sections_subnets = $this->fetch_section_subnets ($sectionId); + # fix null vrfid + $vrfId = is_numeric($vrfId) ? $vrfId : 0; + # verify new against each existing + if (sizeof($sections_subnets)>0) { + foreach ($sections_subnets as $existing_subnet) { + //only check if vrfId's match + if($existing_subnet->vrfId==$vrfId || $existing_subnet->vrfId==null) { + # ignore folders! + if($existing_subnet->isFolder!=1) { + # check overlapping + if($this->identify_address($new_subnet)=="IPv4") { + if($this->verify_IPv4_subnet_overlapping ($new_subnet, $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask)!==false) { + return _("Subnet $new_subnet overlapps with").' '. $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask." (".$existing_subnet->description.")"; + } + } + else { + if($this->verify_IPv6_subnet_overlapping ($new_subnet, $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask)!==false) { + return _("Subnet $new_subnet overlapps with").' '. $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask." (".$existing_subnet->description.")"; + } + } + } + } + } + } + # default false - does not overlap + return false; + } + + /** + * Verifies if resized subnet overlapps with any of existing subnets in that section and same or null VRF + * + * @access public + * @param int $sectionId + * @param CIDR $new_subnet + * @param int $old_subnet_id + * @param int $vrfId (default: 0) + * @return void + */ + public function verify_subnet_resize_overlapping ($sectionId, $new_subnet, $old_subnet_id, $vrfId = 0) { + # fetch section subnets + $sections_subnets = $this->fetch_section_subnets ($sectionId); + # fix null vrfid + $vrfId = is_numeric($vrfId) ? $vrfId : 0; + # verify new against each existing + if (sizeof($sections_subnets)>0) { + foreach ($sections_subnets as $existing_subnet) { + //ignore same + if($existing_subnet->id!=$old_subnet_id) { + //only check if vrfId's match + if($existing_subnet->vrfId==$vrfId || $existing_subnet->vrfId==null) { + # ignore folders! + if($existing_subnet->isFolder!=1) { + # check overlapping + if($this->identify_address($new_subnet)=="IPv4") { + if($this->verify_IPv4_subnet_overlapping ($new_subnet, $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask)!==false) { + return _("Subnet $new_subnet overlapps with").' '. $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask." (".$existing_subnet->description.")"; + } + } + else { + if($this->verify_IPv6_subnet_overlapping ($new_subnet, $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask)!==false) { + return _("Subnet $new_subnet overlapps with").' '. $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask." (".$existing_subnet->description.")"; + } + } + } + } + } + } + } + # default false - does not overlap + return false; + } + + /** + * Check if nested subnet already exists in section! + * + * Subnet policy: + * - inside section subnets cannot overlap! + * - same subnet can be configured in different sections + * - if vrf is same do checks, otherwise skip + * - mastersubnetid we need for new checks to permit overlapping of nested clients + * + * @access public + * @param int $sectionId + * @param CIDR $new_subnet + * @param int $vrfId (default: 0) + * @param int $masterSubnetId (default: 0) + * @return void + */ + public function verify_nested_subnet_overlapping ($sectionId, $new_subnet, $vrfId = 0, $masterSubnetId = 0) { + # fetch section subnets + $sections_subnets = $this->fetch_section_subnets ($sectionId); + # fix null vrfid + $vrfId = is_numeric($vrfId) ? $vrfId : 0; + # check + if(sizeof($section_subnets)>0) { + foreach ($section_subnets as $existing_subnet) { + //only check if vrfId's match + if($existing_subnet->vrfId==$vrfId || $existing_subnet->vrfId==null) { + # ignore folders! + if($existing_subnet->isFolder!=1) { + # check if it is nested properly - inside its own parent, otherwise check for overlapping + $allParents = $this->$this->fetch_parents_recursive($masterSubnetId); + //loop + $ignore = false; + foreach($allParents as $kp=>$p) { + if($existing_subnet->id == $kp) { + $ignore = true; + } + } + if($ignore==false) { + # check overlapping + if($this->identify_address($new_subnet)=="IPv4") { + if($this->verify_IPv4_subnet_overlapping ($new_subnet, $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask)!==false) { + return _("Subnet $new_subnet overlapps with").' '. $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask." (".$existing_subnet->description.")"; + } + } + else { + if($this->verify_IPv6_subnet_overlapping ($new_subnet, $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask)!==false) { + return _("Subnet $new_subnet overlapps with").' '. $this->transform_to_dotted($existing_subnet->subnet).'/'.$existing_subnet->mask." (".$existing_subnet->description.")"; + } + } + } + } + } + } + } + # default false - does not overlap + return false; + } + + /** + * Verifies overlapping of 2 subnets + * + * @access public + * @param CIDR $subnet1 + * @param CIDR $subnet2 + * @return void + */ + public function verify_overlapping ($subnet1, $subnet2) { + return $this->identify_address ($subnet1)=="IPv4" ? $this->verify_IPv4_subnet_overlapping ($subnet1, $subnet2) : $this->verify_IPv6_subnet_overlapping ($subnet1, $subnet2); + } + + /** + * Verifies overlapping of 2 IPv4 subnets + * + * does subnet 1 overlapp with subnet 2 ? + * + * @access private + * @param CIDR $subnet1 + * @param CIDR $subnet2 + * @return boolean + */ + private function verify_IPv4_subnet_overlapping ($subnet1, $subnet2) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv4 (); + + # parse subnets to get subnet and broadcast + $net1 = $this->Net_IPv4->parseAddress( $subnet1 ); + $net2 = $this->Net_IPv4->parseAddress( $subnet2 ); + + # calculate delta + $delta1 = $this->transform_to_decimal( @$net1->broadcast) - $this->transform_to_decimal( @$net1->network); + $delta2 = $this->transform_to_decimal( @$net2->broadcast) - $this->transform_to_decimal( @$net2->network); + + # calculate if smaller is inside bigger + if ($delta1 < $delta2) { + //check smaller nw and bc against bigger network + if ( $this->Net_IPv4->ipInNetwork(@$net1->network, $subnet2) || $this->Net_IPv4->ipInNetwork(@$net1->broadcast, $subnet2) ) { return true; } + } + else { + //check smaller nw and bc against bigger network + if ( $this->Net_IPv4->ipInNetwork(@$net2->network, $subnet1) || $this->Net_IPv4->ipInNetwork(@$net2->broadcast, $subnet1) ) { return true; } + } + # do notoverlap + return false; + } + + /** + * Verifies overlapping of 2 IPv6 subnets + * + * does subnet 1 overlapp with subnet 2 ? + * + * @access private + * @param CIDR $subnet1 + * @param CIDR $subnet2 + * @return boolean + */ + private function verify_IPv6_subnet_overlapping ($subnet1, $subnet2) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv6 (); + + //remove netmask from subnet1 */ + $subnet1 = $this->Net_IPv6->removeNetmaskSpec ($subnet1); + //verify + if ($this->Net_IPv6->isInNetmask ( $subnet1 , $subnet2 ) ) { return true; } + + # do notoverlap + return false; + } + + /** + * Checks if subnet (cidr) is inside some subnet. + * + * Needed for subnet nesting + * + * @access public + * @param mixed $masterSubnetId + * @param mixed $cidr + * @return void + */ + public function verify_subnet_nesting ($masterSubnetId, $cidr) { + //first get details for root subnet + $master_details = $this->fetch_subnet (null, $masterSubnetId); + + //IPv4 or ipv6? + $type_master = $this->identify_address( $master_details->subnet ); + $type_nested = $this->identify_address( $cidr ); + + //both must be IPv4 or IPv6 + if($type_master != $type_nested) { return false; } + + //check + return $this->is_subnet_inside_subnet ($cidr, $this->transform_to_dotted ($master_details->subnet)."/".$master_details->mask); + } + + /** + * This function verifies subnet resizing + * + * @access public + * @param mixed $subnet //subnet in decimal or dotted address format + * @param mixed $mask //new subnet mask + * @param mixed $subnetId //subnet Id + * @param mixed $vrfId //vrfId + * @param mixed $masterSubnetId //master Subnet Id + * @param mixed $mask_old //old mask + * @return void + */ + public function verify_subnet_resize ($subnet, $mask, $subnetId, $vrfId, $masterSubnetId, $mask_old) { + # fetch section and set section ordering + $Sections = new Sections ($this->Database); + $section = $Sections->fetch_section (null, $sectionId); + + // subnet myst be in dotted format + + # new mask must be > 8 + if($mask < 8) { $this->Result->show("danger", _('New mask must be at least /8').'!', true); } + if(!is_numeric($mask)) { $this->Result->show("danger", _('Mask must be an integer').'!', true);; } + + # verify new address + $verify = $this->verify_cidr_address($this->transform_address ($subnet, "dotted")."/".$mask); + if($verify!==true) { $this->Result->show("danger", $verify, true); } + + # same mask - ignore + if($mask==$mask_old) { $this->Result->show("warning", _("New network is same as old network"), true); } + # if we are expanding network get new network address! + elseif($mask < $mask_old) { + //new subnet + $new_boundaries = $this->get_network_boundaries ($this->transform_address($subnet, "dotted"), $mask); + $subnet = $this->transform_address($new_boundaries['network'], "decimal"); + + //Checks for strict mode + if ($section->strictMode==1) { + //if it has parent make sure it is still within boundaries + if((int) $masterSubnetId>0) { + //if parent is folder check for other in same folder + $parent_subnet = $this->fetch_subnet(null, $masterSubnetId); + if($parent_subnet->isFolder!=1) { + //check that new is inside its master subnet + if(!$this->verify_subnet_nesting ($parent_subnet->id, $this->transform_to_dotted($subnet)."/".$mask)) { + $this->Result->show("danger", _("New subnet not in master subnet")."!", true); + } + } + //folder + else { + //fetch all folder subnets, remove old subnet and verify overlapping! + $folder_subnets = $this->fetch_subnet_slaves ($parent_subnet->id); + //check + if(sizeof(@$folder_subnets)>0) { + foreach($folder_subnets as $fs) { + //dont check against old + if($fs->id!=$subnetId) { + //verify that all nested are inside its parent + if($this->verify_overlapping ( $this->transform_to_dotted($subnet)."/".$mask, $this->transform_to_dotted($fs->subnet)."/".$fs->mask)) { + $this->Result->show("danger", _("Subnet overlapps with")." ".$this->transform_to_dotted($fs->subnet)."/".$fs->mask, true); + } + } + } + } + } + } + //root subnet, check overlapping ! + else { + $section_subnets = $this->fetch_section_subnets ($section->id); + $overlap = $this->verify_subnet_resize_overlapping ($section->id, $this->transform_to_dotted($subnet)."/".$mask, $subnetId, $vrfId); + if($overlap!==false) { + $this->Result->show("danger", $overlap, true); + } + } + } + } + # we are shrinking subnet + else { + # addresses class + $Addresses = new Addresses ($this->Database); + // fetch all subnet addresses + $subnet_addresses = $Addresses->fetch_subnet_addresses ($subnetId, "ip_addr", "asc"); + + //check all IP addresses against new subnet + foreach($subnet_addresses as $ip) { + $Addresses->verify_address( $this->transform_to_dotted($ip->ip_addr), $this->transform_to_dotted($subnet)."/".$mask, false, true ); + } + //Checks for strict mode + if ($section->strictMode==1) { + //if it has slaves make sure they are still inside network + if($this->has_slaves($subnetId)) { + //fetch slaves + $nested = $this->fetch_subnet_slaves ($subnetId); + foreach($nested as $nested_subnet) { + //if masks and subnets match they are same, error! + if($nested_subnet->subnet==$subnet && $nested_subnet->mask==$mask) { + $this->Result->show("danger", _("Subnet it same as ").$this->transform_to_dotted($nested_subnet->subnet)."/$nested_subnet->mask - $nested_subnet->description)", true); + } + //verify that all nested are inside its parent + if(!$this->is_subnet_inside_subnet ( $this->transform_to_dotted($nested_subnet->subnet)."/".$nested_subnet->mask, $this->transform_to_dotted($subnet)."/".$mask)) { + $this->Result->show("danger", _("Nested subnet out of new subnet")."!
    (".$this->transform_to_dotted($nested_subnet->subnet)."/$nested_subnet->mask - $nested_subnet->description)", true); + } + } + } + } + } + } + + /** + * Checks if it ok to split subnet + * + * @access private + * @param mixed $subnet_old + * @param mixed $number + * @param string $group + * @param string $strict + * @return void + */ + private function verify_subnet_split ($subnet_old, $number, $group, $strict) { + # addresses class + $Addresses = new Addresses ($this->Database); + + # get new mask - how much we need to add to old mask? + switch($number) { + case "2": $mask_diff = 1; break; + case "4": $mask_diff = 2; break; + case "8": $mask_diff = 3; break; + case "16": $mask_diff = 4; break; + case "32": $mask_diff = 5; break; + case "64": $mask_diff = 6; break; + case "128": $mask_diff = 7; break; + case "256": $mask_diff = 8; break; + //otherwise die + default: $Result->show("danger", _("Invalid number of subnets"), true); + } + //set new mask + $mask = $subnet_old->mask + $mask_diff; + //set number of subnets + $number_of_subnets = pow(2,$mask_diff); + //set max hosts per new subnet + $max_hosts = $this->get_max_hosts ($mask, $this->identify_address($this->transform_to_dotted($subnet_old->subnet)), false); + + # create array of new subnets based on number of subnets (number) + for($m=0; $m<$number_of_subnets; $m++) { + $newsubnets[$m] = (array) $subnet_old; + $newsubnets[$m]['id'] = $m; + $newsubnets[$m]['mask'] = $mask; + + // if group is selected rewrite the masterSubnetId! + if($group=="yes") { + $newsubnets[$m]['masterSubnetId'] = $subnet_old->id; + } + // recalculate subnet + if($m>0) { + $newsubnets[$m]['subnet'] = gmp_strval(gmp_add($newsubnets[$m-1]['subnet'], $max_hosts)); + } + } + + // recalculate old hosts to put it to right subnet + $addresses = $Addresses->fetch_subnet_addresses ($subnet_old->id, "ip_addr", "asc"); # get all IP addresses + $subSize = sizeof($newsubnets); # how many times to check + $n = 0; # ip address count + // loop + foreach($addresses as $ip) { + //cast + $ip = (array) $ip; + # check to which it belongs + for($m=0; $m<$subSize; $m++) { + + # check if between this and next - strict + if($strict == "yes") { + # check if last + if(($m+1) == $subSize) { + if($ip['ip_addr'] > $newsubnets[$m]['subnet']) { + $addresses[$n]->subnetId = $newsubnets[$m]['id']; + } + } + elseif( ($ip['ip_addr'] > $newsubnets[$m]['subnet']) && ($ip['ip_addr'] < @$newsubnets[$m+1]['subnet']) ) { + $addresses[$n]->subnetId = $newsubnets[$m]['id']; + } + } + # unstrict - permit network and broadcast + else { + # check if last + if(($m+1) == $subSize) { + if($ip['ip_addr'] >= $newsubnets[$m]['subnet']) { + $addresses[$n]->subnetId = $newsubnets[$m]['id']; + } + } + elseif( ($ip['ip_addr'] >= $newsubnets[$m]['subnet']) && ($ip['ip_addr'] < $newsubnets[$m+1]['subnet']) ) { + $addresses[$n]->subnetId = $newsubnets[$m]['id']; + } + } + } + + # if subnetId is still the same save to error + if($addresses[$n]->subnetId == $subnet_old->id) { + $this->Result->show("danger", _('Wrong IP addresses (subnet or broadcast)').' - '.$this->transform_to_dotted($ip['ip_addr']), true); + } + # next IP address + $n++; + } + + # check if new overlap (e.g. was added twice) + $nested_subnets = $this->fetch_subnet_slaves ($subnet_old->id); + if($nested_subnets!==false) { + //loop through all current slaves and check + foreach($nested_subnets as $nested_subnet) { + //check all new + foreach($newsubnets as $new_subnet) { + $new_subnet = (object) $new_subnet; + if($this->verify_overlapping ($this->transform_to_dotted($new_subnet->subnet)."/".$new_subnet->mask, $this->transform_to_dotted($nested_subnet->subnet)."/".$nested_subnet->mask)===true) { + $Result->show("danger", _("Subnet overlapping - ").$this->transform_to_dotted($new_subnet->subnet)."/".$new_subnet->mask." overlapps with ".$this->transform_to_dotted($nested_subnet->subnet)."/".$nested_subnet->mask, true); + } + } + } + } + + # all good, return result array of newsubnets and addresses + return array(0=>$newsubnets, 1=>$addresses); + } + + /** + * Checks if subnet 1 is inside subnet 2 + * + * @access public + * @param mixed $cidr1 + * @param mixed $cidr2 + * @return void + */ + public function is_subnet_inside_subnet ($cidr1, $cidr2) { + $type = $this->identify_address ($cidr1); + # check based on type + return $type=="IPv4" ? $this->is_IPv4_subnet_inside_subnet($cidr1, $cidr2) : $this->is_IPv6_subnet_inside_subnet($cidr1, $cidr2); + } + + /** + * Checks if IPv4 subnet 1 is inside subnet 2 + * + * @access private + * @param mixed $cidr1 + * @param mixed $cidr2 + * @return void + */ + private function is_IPv4_subnet_inside_subnet ($cidr1, $cidr2) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv4 (); + + //subnet 1 needs to be parsed to get subnet and broadcast + $cidr1 = $this->Net_IPv4->parseAddress($cidr1); + + //both network and broadcast must be inside root subnet! + if( ($this->Net_IPv4->ipInNetwork($cidr1->network, $cidr2)) && ($this->Net_IPv4->ipInNetwork($cidr1->broadcast, $cidr2)) ) { return true; } + else { return false; } + } + + /** + * Checks if IPv6 subnet 1 is inside subnet 2 + * + * @access private + * @param mixed $cidr1 + * @param mixed $cidr2 + * @return void + */ + private function is_IPv6_subnet_inside_subnet ($cidr1, $cidr2) { + # Initialize PEAR NET object + $this->initialize_pear_net_IPv6 (); + + //remove netmask from subnet1 + $cidr1 = $this->Net_IPv6->removeNetmaskSpec ($cidr1); + + //check + if ($this->Net_IPv6->isInNetmask ( $cidr1, $cidr2 ) ) { return true; } + else { return false; } + } + + + + + + + + + + + + + /** + * @permission methods + * ------------------------------- + */ + + /** + * Checks permission for specified subnet + * + * we provide user details and subnetId + * + * @access public + * @param object $user + * @param int $subnetid + * @return void + */ + public function check_permission ($user, $subnetId) { + + # get all user groups + $groups = json_decode($user->groups); + + # if user is admin then return 3, otherwise check + if($user->role == "Administrator") { return 3; } + + # set subnet permissions + $subnet = $this->fetch_subnet ("id", $subnetId); + //null? + if(is_null(@$subnet->permissions) || $subnet->permissions=="null") return 0; + $subnetP = json_decode(@$subnet->permissions); + + # set section permissions + $Section = new Sections ($this->Database); + $section = $Section->fetch_section ("id", @$subnet->sectionId); + $sectionP = json_decode(@$section->permissions); + + # default permission + $out = 0; + + # for each group check permissions, save highest to $out + if(sizeof($sectionP) > 0) { + foreach($sectionP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + else { + return 0; + } + + # if section permission == 0 then return 0 + if($out == 0) { + return 0; + } + else { + $out = 0; + # ok, user has section access, check also for any higher access from subnet + if(sizeof($subnetP) > 0) { + foreach($subnetP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + } + + # return result + return $out; + } + + /** + * Parse subnet permissions to user readable format + * + * @access public + * @param mixed $perm + * @return void + */ + public function parse_permissions ($permissions) { + switch($permissions) { + case 0: $r = _("No access"); break; + case 1: $r = _("Read"); break; + case 2: $r = _("Read / Write"); break; + case 3: $r = _("Read / Write / Admin"); break; + default: $r = _("error"); + } + return $r; + } + + + + + + + + + + + + + /** + * @menu print methods + * ------------------------------- + */ + + /** + * Creates HTML menu for left subnets + * + * based on http://pastebin.com/GAFvSew4 + * + * @access public + * @param mixed $user + * @param mixed $section_subnets //array of all subnets in section + * @param int $rootId (default: 0) + * @return void + */ + public function print_subnets_menu( $user, $section_subnets, $rootId = 0 ) { + # initialize html array + $html = array(); + # create children array + foreach ( $section_subnets as $item ) + $children_subnets[$item->masterSubnetId][] = (array) $item; + + # loop will be false if the root has no children (i.e., an empty menu!) + $loop = !empty( $children_subnets[$rootId] ); + + # initializing $parent as the root + $parent = $rootId; + $parent_stack = array(); + + # must be numeric + if(isset($_GET['section'])) if(!is_numeric($_GET['section'])) { $this->Result->show("danger",_("Invalid ID"), true); } + if(isset($_GET['subnetId'])) if(!is_numeric($_GET['subnetId'])) { $this->Result->show("danger",_("Invalid ID"), true); } + + # display selected subnet as opened + $allParents = isset($_GET['subnetId']) ? $this->fetch_parents_recursive($_GET['subnetId']) : array(); + + # Menu start + $html[] = '
      '; + + # loop through subnets + while ( $loop && ( ( $option = each( $children_subnets[$parent] ) ) || ( $parent > $rootId ) ) ) + { + # count levels + $count = count( $parent_stack ) + 1; + + # set opened or closed tag for displaying proper folders + if(in_array($option['value']['id'], $allParents)) { $open = "open"; $openf = "-open"; } + else { $open = "close"; $openf = ""; } + + # show also child's by default + if($option['value']['id']==@$_GET['subnetId']) { + if($this->has_slaves(@$_GET['subnetId'])) { $open = "open"; $openf = "-open"; } + else { $open = "close"; $openf = ""; } + } + + # override if cookie is set + if(isset($_COOKIE['expandfolders'])) { + if($_COOKIE['expandfolders'] == "1") { $open='open'; $openf = "-open"; } + } + + # for active class + if($_GET['page']=="subnets" && ($option['value']['id'] == @$_GET['subnetId'])) { $active = "active"; $leafClass=""; } + else { $active = ""; $leafClass="icon-gray" ;} + + # override folder + if($option['value']['isFolder'] == 1 && ($option['value']['id'] == @$_GET['subnetId'])) { $open = "open"; $openf = "-open"; $active = "active"; } + + # set permission + $permission = $option['value']['id']!="" ? $this->check_permission ($user, $option['value']['id']) : 0; + + if ( $option === false ) + { + $parent = array_pop( $parent_stack ); + + # HTML for menu item containing childrens (close) + $html[] = '
    '; + $html[] = ''; + } + # Has children + elseif ( !empty( $children_subnets[$option['value']['id']] ) ) + { + # if user has access permission + if($permission != 0) { + # folder + if($option['value']['isFolder'] == 1) { + $html[] = '
  • '; + $html[] = ''.$option['value']['description'].''; + } + # print name + elseif($option['value']['showName'] == 1) { + $html[] = '
  • '; + $html[] = ''.$option['value']['description'].''; + } + # print subnet + else { + $html[] = '
  • '; + $html[] = ''.$this->transform_to_dotted($option['value']['subnet']).'/'.$option['value']['mask'].''; + } + + # print submenu + if($open == "open") { $html[] = '
  • "; } + return false; + } + + if(!isset($e)) + $this->set_charset("utf8"); + + + # change back reporting for exception throwing to scripts + //mysqli_report(MYSQLI_REPORT_ERROR); + } + + + /** + * execute given query + * + */ + public function executeQuery( $query, $lastId = false ) + { + # execute query + $result = parent::query( $query ); + $this->lastSqlId = $this->insert_id; + + # if it failes throw new exception + if ( mysqli_error( $this ) ) { + throw new exception( mysqli_error( $this ), mysqli_errno( $this ) ); + } + else { + # return lastId if requested + if($lastId) { return $this->lastSqlId; } + else { return true; } + } + } + + + /** + * get only 1 row + * + */ + function getRow ( $query ) + { + /* get result */ + if ($result = parent::query($query)) { + $resp = $result->fetch_row(); + } + else { + throw new exception( mysqli_error( $this ), mysqli_errno( $this ) ); + } + /* return result */ + return $resp; + } + + + + /** + * get array of results + * + * returns multi-dimensional array + * first dimension is number + * from second on the values + * + * if nothing is provided use assocciative results + * + */ + function getArray( $query , $assoc = true ) + { + /* execute query */ + $result = parent::query($query); + + /* if it failes throw new exception */ + if(mysqli_error($this)) { + throw new exception(mysqli_error($this), mysqli_errno($this)); + } + + /** + * fetch array of all access responses + * either assoc or num, based on input + * + */ + if ($assoc == true) { + while($row = $result->fetch_array(MYSQLI_ASSOC)) { + $fields[] = $row; + } + } + else { + while($row = $result->fetch_array(MYSQLI_NUM)) { + $fields[] = $row; + } + } + + /* return result array */ + if(isset($fields)) { + return($fields); + } + else { + $fields = array(); + return $fields; + } + } + + + + /** + * get array of multiple results + * + * returns multi-dimensional array + * first dimension is number + * from second on the values + * + * if nothing is provided use assocciative results + * + */ + function getMultipleResults( $query ) + { + /* execute query */ + $result = parent::multi_query($query); + + /** + * get results to array + * first save it, than get each row from result and store it to active[] + */ + do { + $results = parent::store_result(); + + /* save each to array (only first field) */ + while ( $row = $results->fetch_row() ) { + $rows[] = $row[0]; + } + $results->free(); + } + while( parent::next_result() ); + + /* return result array of rows */ + return($rows); + } + + + /** + * Execute multiple querries! + * + */ + function executeMultipleQuerries( $query, $lastId = false ) + { + # execute querries + //$result = parent::multi_query($query); + + if ($result = parent::multi_query($query)) { + do { + /* store first result set */ + if ($result = parent::store_result()) { + $result->free(); + } + } while (parent::next_result()); + } + + # save lastid + $this->lastSqlId = $this->insert_id; + + # if it failes throw new exception + if ( mysqli_error( $this ) ) { + throw new exception( mysqli_error( $this ), mysqli_errno( $this ) ); + } + else { + if($lastId) { return $this->lastSqlId; } + else { return true; } + } + } + + + /** + * Select database + * + */ + function selectDatabase( $database ) + { + /* execute querries */ + $result = parent::select_db($database); + + /* if it failes throw new exception */ + if ( mysqli_error( $this ) ) { + throw new exception( mysqli_error( $this ), mysqli_errno( $this ) ); + } + else { + return true; + } + } +} + +?> \ No newline at end of file diff --git a/functions/functions_old/functions-admin.php b/functions/functions_old/functions-admin.php new file mode 100755 index 000000000..e76c8149e --- /dev/null +++ b/functions/functions_old/functions-admin.php @@ -0,0 +1,2661 @@ +getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + die("
    "._('Error').": $error
    "); + } + + # user already exists + if (sizeof($details) != 0) { $errors[] = _("User")." ".$userModDetails['username']." "._("already exists!"); } + } + # return errors + return($errors); +} + + +/** + * Delete user by ID + */ +function deleteUserById($id, $name = "") +{ + global $database; + + $query = 'delete from `users` where `id` = "'. $id .'";'; # set query, open db connection and fetch results */ + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # ok + if(!isset($error)) { + updateLogTable ('User '. $name .' deleted ok', 'User '. $name .' deleted ok', 1); # write success log + return true; + } + # problem + else { + print "
    "._('Cannot delete user')."!
    "._('Error').": $error
    "; + updateLogTable ('Cannot delete user '. $name, 'Cannot delete user '. $name , 2); # write error log + return false; + } +} + + +/** + * Update user by ID - if id is empty add new user! + */ +function updateUserById ($userModDetails) { + + global $database; + + # replace special chars + $userModDetails['groups'] = mysqli_real_escape_string($database, $userModDetails['groups']); + + # set query - add or edit user + if (empty($userModDetails['userId'])) { + + # custom fields + $myFields = getCustomFields('users'); + $myFieldsInsert['query'] = ''; + $myFieldsInsert['values'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + # empty? + if(strlen($userModDetailsip[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", NULL"; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", '". $userModDetails[$myField['name']] . "'"; + } + } + } + + # reformat passChanged + if($userModDetails['domainUser']=="1") { $userModDetails['passChange'] = "No"; } //never for domain users + elseif(@$userModDetails['passChange']=="On") { $userModDetails['passChange'] = "Yes"; } //yes if requested + else { $userModDetails['passChange'] = "No"; } //no change needed + + $query = "insert into users "; + $query .= "(`username`, `password`, `role`, `real_name`, `email`, `domainUser`,`mailNotify`,`mailChangelog`,`groups`,`lang`,`passChange` $myFieldsInsert[query]) values "; + $query .= "('$userModDetails[username]', '$userModDetails[password1]', '$userModDetails[role]', '$userModDetails[real_name]', '$userModDetails[email]', '$userModDetails[domainUser]', '$userModDetails[mailNotify]','$userModDetails[mailChangelog]','$userModDetails[groups]','$userModDetails[lang]','$userModDetails[passChange]' $myFieldsInsert[values]);"; + } + else { + + # custom fields + $myFields = getCustomFields('users'); + $myFieldsInsert['query'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + if(strlen($userModDetails[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = NULL '; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = \''.$userModDetails[$myField['name']].'\' '; + } + } + } + + $query = "update users set "; + $query .= "`username` = '$userModDetails[username]', "; + if (strlen($userModDetails['password1']) != 0) { + $query .= "`password` = '$userModDetails[password1]', "; + } + $query .= "`role` = '$userModDetails[role]', `real_name`= '$userModDetails[real_name]', `email` = '$userModDetails[email]', `domainUser`= '$userModDetails[domainUser]',`mailNotify`= '$userModDetails[mailNotify]',`mailChangelog`= '$userModDetails[mailChangelog]', `lang`= '$userModDetails[lang]', `groups`='".$userModDetails['groups']."' "; + $query .= $myFieldsInsert['query']; + $query .= "where `id` = '$userModDetails[userId]';"; + } + + $log = prepareLogFromArray ($userModDetails); # prepare log + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # ok + if(!isset($error)) { + updateLogTable ('User '. $userModDetails['username'] .' updated ok', $log, 1); # write success log + return true; + } + # problem + else { + print "
    "._("Cannot $userModDetails[action] user")."!
    "._('Error').": $error
    "; + updateLogTable ('Cannot modify user '. $userModDetails['username'], $log, 2); # write error log + return false; + } +} + + +/** + * User self-update + */ +function selfUpdateUser ($userModDetails) +{ + global $database; + + /* set query */ + $query = "update users set "; + if(strlen($userModDetails['password1']) != 0) { + $query .= "`password` = '$userModDetails[password1]',"; + } + $query .= "`real_name`= '$userModDetails[real_name]', `mailNotify`='$userModDetails[mailNotify]', `mailChangelog`='$userModDetails[mailChangelog]', `email` = '$userModDetails[email]', "; + $query .= "`lang`= '$userModDetails[lang]' "; + $query .= "where `id` = '$userModDetails[userId]';"; + + /* set log file */ + $log = prepareLogFromArray ($userModDetails); # prepare log + + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # ok + if(!isset($error)) { + updateLogTable ('User '. $userModDetails['real_name'] . ' selfupdate ok', $log, 1); # write success log + return true; + } + # problem + else { + print "
    "._('Cannot update user')."!
    "._('Error').": $error
    "; + updateLogTable ('User '. $userModDetails['real_name'] . ' selfupdate failed', $log, 2); # write error log + return false; + } +} + + +/** + * User set dash widgets + */ +function setUserDashWidgets ($userId, $widgets) +{ + global $database; + + /* set query */ + $query = "update users set `widgets`= '$widgets' where `id` = '$userId';"; + + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # ok + if(!isset($error)) { + return true; + } + # problem + else { + print "
    "._('Cannot update user')."!
    "._('Error').": $error
    "; + return false; + } +} + + + +/** + * Modify lang + */ +function modifyLang ($lang) +{ + global $database; + + /* set query based on action */ + if($lang['action'] == "add") { $query = "insert into `lang` (`l_code`,`l_name`) values ('$lang[l_code]','$lang[l_name]');"; } + elseif($lang['action'] == "edit") { $query = "update `lang` set `l_code`='$lang[l_code]',`l_name`='$lang[l_name]' where `l_id`='$lang[l_id]'; "; } + elseif($lang['action'] == "delete") { $query = "delete from `lang` where `l_id`='$lang[l_id]'; "; } + else { return 'false'; } + + /* execute */ + try { $details = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + return true; +} + + +/** + * Modify widget + */ +function modifyWidget ($w) +{ + global $database; + + /* set query based on action */ + if($w['action'] == "add") { $query = "insert into `widgets` (`wtitle`,`wdescription`,`wfile`,`whref`,`wadminonly`,`wactive`,`wsize`) values ('$w[wtitle]','$w[wdescription]','$w[wfile]','$w[whref]','$w[wadminonly]','$w[wactive]','$w[wsize]');"; } + elseif($w['action'] == "edit") { $query = "update `widgets` set `wtitle`='$w[wtitle]',`wdescription`='$w[wdescription]',`wfile`='$w[wfile]',`wadminonly`='$w[wadminonly]',`wactive`='$w[wactive]',`whref`='$w[whref]',`wsize`='$w[wsize]' where `wid`='$w[wid]'; "; } + elseif($w['action'] == "delete") { $query = "delete from `widgets` where `wid`='$w[wid]'; "; } + else { return 'false'; } + + /* execute */ + try { $details = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + return true; +} + + +/** + * Update user password on first login + */ +function update_user_password ($id, $password) +{ + global $database; + + # query + $query = "update `users` set `password`='$password', `passChange`='No' where `id` = $id;"; + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + return true; +} + + + + + + + + + + +/* @group functions ---------------- */ + + +/** + * get all groups + */ +function getAllGroups() +{ + global $database; + + /* execute query */ + $query = "select * from `userGroups` order by `g_name` asc;"; + + /* get groups */ + try { $groups = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + die("
    "._('Error').": $error
    "); + } + + /* return false if none, else list */ + if(sizeof($groups) == 0) { return false; } + else { return $groups; } +} + + +/** + * get all groups - array order by key + */ +function rekeyGroups($groups) +{ + foreach($groups as $k=>$g) { + $tkey = $g['g_id']; + + $out[$tkey]['g_id'] = $g['g_id']; + $out[$tkey]['g_name'] = $g['g_name']; + $out[$tkey]['g_desc'] = $g['g_desc']; + } + + return $out; +} + + +/** + * Group details by ID + */ +function getGroupById($id) +{ + # check if already in cache + if($vtmp = checkCache("group", $id)) { + return $vtmp; + } + # query + else { + global $database; + + /* execute query */ + $query = "select * from `userGroups` where `g_id`= '$id';"; + + /* get group */ + try { $group = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + die("
    "._('Error').": $error
    "); + } + + /* return false if none, else list */ + if(sizeof($group) == 0) { return false; } + else { writeCache("group", $id, $group[0]); return $group[0]; } + } +} + + +/** + * Parse all user groups + */ +function parseUserGroups($groups) +{ + if(sizeof($groups)>0) { + foreach($groups as $g) { + $tmp = getGroupById($g); + $out[$tmp['g_id']] = $tmp; + } + } + /* return array of groups */ + return $out; +} + + +/** + * Parse all user groups - get only Id's + */ +function parseUserGroupsIds($groups) +{ + if(sizeof($groups) >0) { + foreach($groups as $g) { + $tmp = getGroupById($g); + $out[$tmp['g_id']] = $tmp['g_id']; + } + } + /* return array of groups */ + return $out; +} + + + +/** + * Get users in group + */ +function getUsersInGroup($gid) +{ + # get all users + $users = getAllUsers(); + + # check if $gid in array + foreach($users as $u) { + $g = json_decode($u['groups'], true); + $g = parseUserGroups($g); + + if(sizeof($g)>0) { + foreach($g as $gr) { + if(in_array($gid, $gr)) { + $out[] = $u['id']; + } + } + } + } + # return + return $out; +} + + +/** + * Get users not in group + */ +function getUsersNotInGroup($gid) +{ + # get all users + $users = getAllUsers(); + + # check if $gid in array + foreach($users as $u) { + if($u['role'] != "Administrator") { + $g = json_decode($u['groups'], true); + if(!@in_array($gid, $g)) { $out[] = $u['id']; } + } + } + # return + return $out; +} + + +/** + * Function that returns all sections with selected group partitions + */ +function getSectionPermissionsByGroup ($gid, $name = true) +{ + # get all users + $sec = fetchSections(); + + # check if $gid in array + foreach($sec as $s) { + $p = json_decode($s['permissions'], true); + if(sizeof($p)>0) { + if($name) { + if(array_key_exists($gid, $p)) { $out[$s['name']] = $p[$gid]; } + } + else { + if(array_key_exists($gid, $p)) { $out[$s['id']] = $p[$gid]; } + } + } + # no permissions + else { + $out[$s['name']] = 0; + } + } + # return + return $out; +} + + + +/** + * Modify group + */ +function modifyGroup($g) +{ + global $database; + + # set query + if($g['action'] == "add") { $query = "insert into `userGroups` (`g_name`,`g_desc`) values ('$g[g_name]','$g[g_desc]'); "; } + else if($g['action'] == "edit") { $query = "update `userGroups` set `g_name`='$g[g_name]', `g_desc`='$g[g_desc]' where `g_id` = '$g[g_id]';"; } + else if($g['action'] == "delete") { $query = "delete from `userGroups` where `g_id` = '$g[g_id]';"; } + else { return false; } + + # execute + try { $database->executeQuery( $query ); } + catch (Exception $e) { $error = $e->getMessage(); } + + # set log file + $log = prepareLogFromArray ($g); # prepare log + + # ok + if(!isset($error)) { + updateLogTable ("Group $g[action] success", $log, 0); # write success log + return true; + } + # problem + else { + print "
    "._("Cannot $userModDetails[action] user")."!
    "._('Error').": $error
    "; + updateLogTable ("Group $g[action] error", $log, 2); # write error log + return false; + } + +} + + +/** + * Delete all users from group + */ +function deleteUsersFromGroup($gid) +{ + # get all users + $users = getAllUsers(); + + # check if $gid in array + foreach($users as $u) { + $g = json_decode($u['groups'], true); + $go = $g; + $g = parseUserGroups($g); + + if(sizeof($g)>0) { + foreach($g as $gr) { + if(in_array($gid, $gr)) { + unset($go[$gid]); + $ng = json_encode($go); + updateUserGroups($u['id'],$ng); + } + } + } + } + # return + return $out; + +} + + +/** + * Delete all users from group + */ +function deleteGroupFromSections($gid) +{ + # get all users + $sections = fetchSections(); + + # check if $gid in array + foreach($sections as $s) { + $g = json_decode($s['permissions'], true); + + if(sizeof($g)>0) { + if(array_key_exists($gid, $g)) { + unset($g[$gid]); + $ng = json_encode($g); + updateSectionGroups($s['id'],$ng); + } + } + } + # return + return $out; + +} + + + +/** + * Add user to group + */ +function addUserToGroup($gid, $uid) +{ + # get old groups + $user = getUserDetailsById($uid); + + # append new group + $g = json_decode($user['groups'], true); + $g[$gid] = $gid; + $g = json_encode($g); + + # update + if(!updateUserGroups($uid, $g)) { return false; } + else { return true; } +} + + +/** + * Remove user from group + */ +function removeUserFromGroup($gid, $uid) +{ + # get old groups + $user = getUserDetailsById($uid); + + # append new group + $g = json_decode($user['groups'], true); + unset($g[$gid]); + $g = json_encode($g); + + # update + if(!updateUserGroups($uid, $g)) { return false; } + else { return true; } +} + + +/** + * Update users's group + */ +function updateUserGroups($uid, $groups) +{ + global $database; + + # replace special chars + $groups = mysqli_real_escape_string($database, $groups); + + # set query + $query = "update `users` set `groups` = '$groups' where `id` = $uid; "; + + # update + try { $database->executeQuery($query); } + catch (Exception $e) { + print "
    "._('Error').": $e
    "; + return false; + } + + # ok + return true; +} + + +/** + * Update section permissions + */ +function updateSectionGroups($sid, $groups) +{ + global $database; + + # replace special chars + $groups = mysqli_real_escape_string($database, $groups); + + # set query + $query = "update `sections` set `permissions` = '$groups' where `id` = $sid; "; + + # update + try { $database->executeQuery($query); } + catch (Exception $e) { + print "
    "._('Error').": $e
    "; + return false; + } + + # ok + return true; +} + + + + + + + + + + +/* @subnet functions ---------------- */ + + +/** + * Add new subnet + */ +function modifySubnetDetails ($subnetDetails, $lastId = false, $api = false) +{ + global $database; + + /* escape vars to prevent SQL injection */ + $subnetDetails = filter_user_input ($subnetDetails, true, true); + + /* trim user input */ + $subnetDetails = trim_user_input ($subnetDetails); + + # set modify subnet details query + $query = setModifySubnetDetailsQuery ($subnetDetails, $api); + + $log = prepareLogFromArray ($subnetDetails); # prepare log + + /* save old if delete */ + if($subnetDetails['action']=="delete") { $dold = getSubnetDetailsById ($subnetDetails['subnetId']); } + elseif($subnetDetails['action']=="edit") { $old = getSubnetDetailsById ($subnetDetails['subnetId']); } + + # execute query + try { $updateId=$database->executeMultipleQuerries($query, true); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Subnet ('. $subnetDetails['description'] .') '. $subnetDetails['action'] .' failed', $log, 2); # write error log + print "
    $error
    "; + //save changelog + writeChangelog('subnet', $ip['action'], 'error', $old, $new); + return false; + } + + /* for changelog */ + if($subnetDetails['action']=="add") { + $subnetDetails['subnetId'] = $updateId; + writeChangelog('subnet', $subnetDetails['action'], 'success', array(), $subnetDetails); + } elseif ($subnetDetails['action']=="delete") { + $dold['subnetId'] = $dold['id']; + writeChangelog('subnet', $subnetDetails['action'], 'success', $dold, array()); + } else { + writeChangelog('subnet', $subnetDetails['action'], 'success', $old, $subnetDetails); + } + + // success + if($_POST['isFolder']==false) { + updateLogTable ('Subnet '.$subnetDetails['subnet'].' ('. $subnetDetails['description'] .') '. $subnetDetails['action'] .' ok', $log, 1); # write success log + } else { + updateLogTable ('Folder '.$subnetDetails['subnet'].' ('. $subnetDetails['description'] .') '. $subnetDetails['action'] .' ok', $log, 1); # write success log + } + // result + if(!$lastId) { return true; } + else { return $updateId; } +} + + +/** + * Add new subnet - set query + */ +function setModifySubnetDetailsQuery ($subnetDetails, $api) +{ + # add new subnet + if ($subnetDetails['action'] == "add") + { + # api? + if($api) { + $query = 'insert into subnets '. "\n"; + $query .= '(`subnet`, `mask`, `sectionId`, `description`, `vlanId`, `vrfId`, `masterSubnetId`, `allowRequests`, `showName`, `permissions`, `discoverSubnet`, `pingSubnet`) ' . "\n"; + $query .= 'values (' . "\n"; + $query .= ' "'. $subnetDetails['subnet'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['mask'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['sectionId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['description'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['vlanId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['vrfId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['masterSubnetId'] .'", ' . "\n"; + $query .= ''. isCheckbox($subnetDetails['allowRequests']) .','."\n"; + $query .= ''. isCheckbox($subnetDetails['showName']) .','."\n"; + $query .= ' "'. $subnetDetails['permissions'] .'", '."\n"; + $query .= ''. isCheckbox($subnetDetails['discoverSubnet']) .','."\n"; + $query .= ''. isCheckbox($subnetDetails['pingSubnet']) .''."\n"; + $query .= ' );'; + } else { + # remove netmask and calculate decimal values! + $subnetDetails['subnet_temp'] = explode("/", $subnetDetails['subnet']); + $subnetDetails['subnet'] = Transform2decimal ($subnetDetails['subnet_temp'][0]); + $subnetDetails['mask'] = $subnetDetails['subnet_temp'][1]; + + # custom fields + $myFields = getCustomFields('subnets'); + $myFieldsInsert['query'] = ''; + $myFieldsInsert['values'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + # empty? + if(strlen($subnetDetails[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", NULL"; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", '". $subnetDetails[$myField['name']] . "'"; + } + } + } + + $query = 'insert into subnets '. "\n"; + # is folder? + if($subnetDetails['isFolder']) { + $query .= '(`isFolder`,`subnet`, `mask`, `sectionId`, `description`, `vlanId`, `vrfId`, `masterSubnetId`, `allowRequests`, `showName`, `permissions`, `discoverSubnet`, `pingSubnet` '.$myFieldsInsert['query'].') ' . "\n"; + $query .= 'values (' . "\n"; + $query .= '1, ' . "\n"; + } + else { + $query .= '(`subnet`, `mask`, `sectionId`, `description`, `vlanId`, `vrfId`, `masterSubnetId`, `allowRequests`, `showName`, `permissions`, `discoverSubnet`, `pingSubnet` '.$myFieldsInsert['query'].') ' . "\n"; + $query .= 'values (' . "\n"; + } + $query .= ' "'. $subnetDetails['subnet'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['mask'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['sectionId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['description'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['vlanId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['vrfId'] .'", ' . "\n"; + $query .= ' "'. $subnetDetails['masterSubnetId'] .'", ' . "\n"; + $query .= ''. isCheckbox($subnetDetails['allowRequests']) .','."\n"; + $query .= ''. isCheckbox($subnetDetails['showName']) .','."\n"; + $query .= ' "'. $subnetDetails['permissions'] .'", '."\n"; + $query .= ''. isCheckbox($subnetDetails['discoverSubnet']) .','."\n"; + $query .= ''. isCheckbox($subnetDetails['pingSubnet']) .''."\n"; + $query .= $myFieldsInsert['values']; + $query .= ' );'; + } + } + # Delete subnet + else if ($subnetDetails['action'] == "delete") + { + /* get ALL slave subnets, then remove all subnets and IP addresses */ + global $removeSlaves; + getAllSlaves ($subnetDetails['subnetId']); + $removeSlaves = array_unique($removeSlaves); + + $query = ""; + foreach($removeSlaves as $slave) { + $query .= 'delete from `subnets` where `id` = "'. $slave .'"; '."\n"; + $query .= 'delete from `ipaddresses` where `subnetId` = "'. $slave .'"; '."\n"; + } + } + # Edit subnet + else if ($subnetDetails['action'] == "edit") + { + + # custom fields + $myFields = getCustomFields('subnets'); + $myFieldsInsert['query'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + if(strlen($subnetDetails[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = NULL '; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = "'.$subnetDetails[$myField['name']].'" '; + } + } + } + + $query = 'update subnets set '. "\n"; + $query .= '`description` = "'. $subnetDetails['description'] .'", '. "\n"; + if($subnetDetails['sectionId'] != $subnetDetails['sectionIdNew']) { + $query .= '`sectionId` = "'. $subnetDetails['sectionIdNew'] .'", '. "\n"; + } + $query .= '`vlanId` = "'. $subnetDetails['vlanId'] .'", '. "\n"; + $query .= '`vrfId` = "'. $subnetDetails['vrfId'] .'", '. "\n"; + $query .= '`masterSubnetId` = "'. $subnetDetails['masterSubnetId'] .'", '. "\n"; + $query .= '`allowRequests` = "'. isCheckbox($subnetDetails['allowRequests']) .'", '. "\n"; + $query .= '`showName` = "'. isCheckbox($subnetDetails['showName']) .'", '. "\n"; + $query .= '`discoverSubnet` = "'. isCheckbox($subnetDetails['discoverSubnet']) .'", '. "\n"; + $query .= '`pingSubnet` = "'. isCheckbox($subnetDetails['pingSubnet']) .'" '. "\n"; + $query .= $myFieldsInsert['query']; + $query .= 'where id = "'. $subnetDetails['subnetId'] .'"; '."\n"; + + # if section changes + if($subnetDetails['sectionId'] != $subnetDetails['sectionIdNew']) { + # add querry to change slaves! + global $removeSlaves; + getAllSlaves ($subnetDetails['subnetId']); + $removeSlaves = array_unique($removeSlaves); + + foreach($removeSlaves as $slave) { + if($subnetDetails['subnetId'] != $slave) { + $query .= 'update `subnets` set `sectionId` = "'. $subnetDetails['sectionIdNew'] .'" where `id` = "'.$slave.'"; '."\n"; + } + } + } + + # if vrf changes + if($subnetDetails['vrfId'] != $subnetDetails['vrfIdOld']) { + # add querry to change vrfId! + global $removeSlaves; + getAllSlaves ($subnetDetails['subnetId']); + $removeSlaves = array_unique($removeSlaves); + + foreach($removeSlaves as $slave) { + $query .= 'update `subnets` set `vrfId` = "'. $subnetDetails['vrfId'] .'" where `id` = "'.$slave.'"; '."\n"; + } + } + } + # Something is not right! + else { + + } + # return query + return $query; +} + + +/** + * delete subnet - only single subnet, no child/slave hosts and IP addresses are removed!!!! Beware !!! + */ +function deleteSubnet ($subnetId) +{ + global $database; + + # set modify subnet details query + $query = "delete from `subnets` where `id` = '$subnetId';"; + + # execute query + if (!$database->executeQuery($query)) { + updateLogTable ('Subnet delete from split failed', "id:$subnetId", 2); # write error log + return false; + } + else { + updateLogTable ('Subnet deleted from split ok', "id: $subnetId", 0); # write success log + return true; + } +} + + +/** + * Resize subnet - change mask + */ +function modifySubnetMask ($subnetId, $mask) +{ + global $database; + + # set modify subnet details query + $query = "update `subnets` set `mask` = '$mask' where `id` = '$subnetId';"; + + $log = "subnetId: $subnetId\n New mask: $mask"; # prepare log + + # execute query + if (!$database->executeQuery($query)) { + updateLogTable ('Subnet resize failed', $log, 2); # write error log + return false; + } + else { + updateLogTable ('Subnet resized ok', $log, 1); # write success log + + /* changelog */ + writeChangelog('subnet', 'resize', 'success', array(), array("subnetId"=>$subnetId, "mask"=>$mask)); + + return true; + } +} + + +/** + * truncate subnet + */ +function truncateSubnet($subnetId) +{ + global $database; + + /* first update request */ + $query = 'delete from `ipaddresses` where `subnetId` = '. $subnetId .';'; + + /* execute */ + try { $database->executeQuery($query); } + catch (Exception $e) { + $error = $e->getMessage(); + die('
    '.$error.'
    '); + } + + /* changelog */ + writeChangelog('subnet', 'truncate', 'success', array(), array("Truncate"=>'Subnet truncated', "subnetId"=>$subnetId)); + + /* return true if locked */ + return true; +} + + +/** + * Print subnets structure + */ +function printAdminSubnets( $subnets, $actions = true, $vrf = "0" ) +{ + $html = array(); + + $rootId = 0; # root is 0 + + if(sizeof($subnets) > 0) { + foreach ( $subnets as $item ) { + $item = (array) $item; + $children[$item['masterSubnetId']][] = $item; + } + } + + /* get custom fields */ + $custom = getCustomFields('subnets'); + + global $settings; + /* set hidden fields */ + $ffields = json_decode($settings['hiddenCustomFields'], true); + if(is_array($ffields['subnets'])) { $ffields = $ffields['subnets']; } + else { $ffields = array(); } + + # loop will be false if the root has no children (i.e., an empty menu!) + $loop = !empty( $children[$rootId] ); + + # initializing $parent as the root + $parent = $rootId; + $parent_stack = array(); + + # display selected subnet as opened + if(isset($_GET['subnetId'])) { + if(!is_numeric($_GET['subnetId'])) { die('
    '._("Invalid ID").'
    '); } + $allParents = getAllParents ($_GET['subnetId']); + } + # return table content (tr and td's) + while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $rootId ) ) ) + { + # repeat + $repeat = str_repeat( " - ", ( count($parent_stack)) ); + # dashes + if(count($parent_stack) == 0) { $dash = ""; } + else { $dash = "-"; } + + if(count($parent_stack) == 0) { + $margin = "0px"; + $padding = "0px"; + } + else { + # padding + $padding = "10px"; + + # margin + $margin = (count($parent_stack) * 10) -10; + $margin = $margin *2; + $margin = $margin."px"; + } + + # count levels + $count = count( $parent_stack ) + 1; + + # get VLAN + $vlan = subnetGetVLANdetailsById($option['value']['vlanId']); + $vlan = $vlan['number']; + if(empty($vlan) || $vlan == "0") { $vlan = ""; } # no VLAN + + # description + if(strlen($option['value']['description']) == 0) { $description = "/"; } # no description + else { $description = $option['value']['description']; } # description + + # requests + if($option['value']['allowRequests'] == 1) { $requests = ""; } # requests enabled + else { $requests = ""; } # request disabled + + # hosts check + if($option['value']['pingSubnet'] == 1) { $pCheck = ""; } # ping check enabled + else { $pCheck = ""; } # ping check disabled + + #vrf + if($vrf == "1") { + # get VRF details + if(($option['value']['vrfId'] != "0") && ($option['value']['vrfId'] != "NULL") ) { + $vrfTmp = getVRFDetailsById ($option['value']['vrfId']); + $vrfText = $vrfTmp['name']; + } + else { + $vrfText = ""; + } + } + + # print table line + if(strlen($option['value']['subnet']) > 0) { + $html[] = ""; + # folder + if($option['value']['isFolder']==1) { + $html[] = " $description"; + $html[] = " $description"; + } + else { + if($count==1) { + $html[] = " ".transform2long($option['value']['subnet']) ."/".$option['value']['mask'].""; + $html[] = " $description"; + } + else { + # last? + if(!empty( $children[$option['value']['id']])) { + $html[] = " ".transform2long($option['value']['subnet']) ."/".$option['value']['mask'].""; + $html[] = " $description"; + } + else { + $html[] = " ".transform2long($option['value']['subnet']) ."/".$option['value']['mask'].""; + $html[] = " $description"; + } + } + } + $html[] = " $vlan"; + #vrf + if($vrf == "1") { + $html[] = " $vrfText"; + } + $html[] = " $requests"; + $html[] = " $pCheck"; + # custom + if(sizeof($custom)>0) { + foreach($custom as $field) { + if(!in_array($field['name'], $ffields)) { + $html[] = " ".$option['value'][$field['name']].""; + } + } + } + # actions + if($actions) { + $html[] = " "; + $html[] = "
    "; + if($option['value']['isFolder']==1) { + $html[] = " "; + $html[] = " "; + $html[] = " "; + } else { + $html[] = " "; + $html[] = " "; + $html[] = " "; + } + $html[] = "
    "; + $html[] = " "; + } + $html[] = ""; + } + + if ( $option === false ) { $parent = array_pop( $parent_stack ); } + # Has slave subnets + elseif ( !empty( $children[$option['value']['id']] ) ) { + array_push( $parent_stack, $option['value']['masterSubnetId'] ); + $parent = $option['value']['id']; + } + # Last items + else { } + } + return implode( "\n", $html ); +} + + +/** + * Update subnet permissions + */ +function updateSubnetPermissions ($subnet) +{ + global $database; + + # replace special chars + $subnet['permissions'] = mysqli_real_escape_string($database, $subnet['permissions']); + + # set querries for subnet and each slave + foreach($subnet['slaves'] as $slave) { + $query .= "update `subnets` set `permissions` = '$subnet[permissions]' where `id` = $slave;"; + + writeChangelog('subnet', 'perm_change', 'success', array(), array("permissions_change"=>"$subnet[permissions]", "subnetId"=>$slave)); + } + + # execute + try { $database->executeMultipleQuerries($query); } + catch (Exception $e) { + $error = $e->getMessage(); + print('
    '._('Error').': '.$error.'
    '); + return false; + } + + /* return true if passed */ + return true; +} + + + + + + + + + + + +/* @section functions ---------------- */ + + +/** + * Update section + */ +function UpdateSection ($update, $api = false) +{ + global $database; + + # replace special chars for permissions + $update['permissions'] = mysqli_real_escape_string($database, $update['permissions']); + $update['description'] = mysqli_real_escape_string($database, $update['description']); + $update['name'] = mysqli_real_escape_string($database, $update['name']); + + if (!$api && !$update['name']) { die('
    '._('Name is mandatory').'!
    '); } # section name is mandatory + + $query = setUpdateSectionQuery ($update); # set update section query + + $log = prepareLogFromArray ($update); # prepare log + + /* save old if delete */ + if($update['action']=="delete") { $dold = getSectionDetailsById ($update['id']); } + elseif($update['action']=="edit") { $old = getSectionDetailsById ($update['id']); } + + + # delete and edit requires multiquery + if ( ( $update['action'] == "delete") || ( $update['action'] == "edit") ) + { + # execute + try { $result = $database->executeMultipleQuerries($query, true); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Section ' . $update['action'] .' failed ('. $update['name']. ') - '.$error, $log, 2); # write error log + if(!$api) print ('
    '.("Cannot $update[action] all entries").' - '.$error.'!
    '); + return false; + } + # success + updateLogTable ('Section '. $update['name'] . ' ' . $update['action'] .' ok', $log, 1); # write success log + + /* for changelog */ + if ($update['action']=="delete") { + $dold['id'] = $update['id']; + writeChangelog('section', $update['action'], 'success', $dold, array()); + } else { + writeChangelog('section', $update['action'], 'success', $old, $update); + } + + return true; + } + # add is single querry + else + { + # execute + try { $result = $database->executeQuery($query, true); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Adding section '. $update['name'] .'failed - '.$error, $log, 2); # write error log + if(!$api) die('
    '.('Cannot update database').'!
    '.$error.'
    '); + return false; + } + # success + updateLogTable ('Section '. $update['name'] .' added succesfully', $log, 1); # write success log + + /* for changelog */ + $update['id'] = $result; + writeChangelog('section', $update['action'], 'success', array(), $update); + + return true; + } +} + + +/** + * Set Query for update section + */ +function setUpdateSectionQuery ($update) +{ + # add section + if ($update['action'] == "add" || $update['action'] == "create") + { + $query = 'Insert into sections (`name`,`description`,`permissions`,`strictMode`,`subnetOrdering`,`showVLAN`,`showVRF`, `masterSection`) values ("'.$update['name'].'", "'.$update['description'].'", "'.$update['permissions'].'", "'.isCheckbox($update['strictMode']).'", "'.$update['subnetOrdering'].'", "'.isCheckbox($update['showVLAN']).'", "'.isCheckbox($update['showVRF']).'", "'.$update['masterSection'].'");'; + } + # edit section + else if ($update['action'] == "edit" || $update['action'] == "update") + { + $section_old = getSectionDetailsById ( $update['id'] ); # Get old section name for update + # Update section + $query = "update `sections` set `name` = '$update[name]', `description` = '$update[description]', `permissions` = '$update[permissions]', `strictMode`='".isCheckbox($update['strictMode'])."', `subnetOrdering`='$update[subnetOrdering]', `showVLAN`='".isCheckbox($update['showVLAN'])."', `showVRF`='".isCheckbox($update['showVRF'])."', `masterSection`='$update[masterSection]' where `id` = '$update[id]';"; + + # delegate permissions if set + if($update['delegate'] == 1) { + $query .= "update `subnets` set `permissions` = '$update[permissions]' where `sectionId` = '$update[id]';"; + } + } + # delete section + else if( $update['action'] == "delete" ) + { + /* we must delete many entries - section, all belonging subnets and ip addresses */ + $sectionId = $update['id']; + + # delete sections query + $query = "delete from `sections` where `id` = '$sectionId';"."\n"; + # delete belonging subnets + $query .= "delete from `subnets` where `sectionId` = '$sectionId';"."\n"; + # delete IP addresses query + $subnets = fetchSubnets ( $sectionId ); + + if (sizeof($subnets) != 0) { + foreach ($subnets as $subnet) { + $query .= "delete from `ipaddresses` where `subnetId` = '$subnet[id]';"."\n"; + } + } + + # if it has subsections delete all subsections and subnets/ip addresses + if(sizeof($subsections = getAllSubSections($sectionId))>0) { + foreach($subsections as $ss) { + $query .= "delete from `sections` where `id` = '$ss[id]';"."\n"; + $query .= "delete from `subnets` where `sectionId` = '$ss[id]';"."\n"; + $ssubnets = fetchSubnets ( $ss['id'] ); + if (sizeof($ssubnets) != 0) { + foreach ($ssubnets as $subnet) { + $query .= "delete from `ipaddresses` where `subnetId` = '$subnet[id]';"."\n"; + } + } + } + } + } + + /* return query */ + return $query; +} + + +/** + * Update section ordering + */ +function UpdateSectionOrder ($order) +{ + global $database; + + // set querries for each section + $query = ""; + foreach($order as $key=>$o) { + $query .= "update `sections` set `order` = $o where `id` = $key; \n"; + } + //log + $log = prepareLogFromArray ($order); + //execute multiple queries + try { $result = $database->executeMultipleQuerries($query); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Section reordering failed ('. $update['name']. ') - '.$error, $log, 2); # write error log + print ('
    '._("Cannot reorder sections").' - '.$error.'!
    '); + return false; + } + # success + updateLogTable ('Section reordering ok', $log, 1); # write success log + return true; +} + + +/** + * Parse section permissions + */ +function parseSectionPermissions($permissions) +{ + # save to array + $permissions = json_decode($permissions, true); + + if(sizeof($permissions)>0) { + foreach($permissions as $key=>$p) { + $tmp = getGroupById($key); + $out[$tmp['g_id']] = $p; + } + } + /* return array of groups */ + return $out; +} + + + + + + + + + +/* @device functions ---------------- */ + + +/** + * Update device details + */ +function updateDeviceDetails($device) +{ + global $database; + + /* set querry based on action */ + if($device['action'] == "add") { + + # custom fields + $myFields = getCustomFields('devices'); + $myFieldsInsert['query'] = ''; + $myFieldsInsert['values'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + # empty? + if(strlen($device[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", NULL"; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", '". $device[$myField['name']] . "'"; + } + } + } + + $query = 'insert into `devices` '. "\n"; + $query .= '(`hostname`,`ip_addr`, `type`, `vendor`,`model`,`version`,`description`,`sections` '.$myFieldsInsert['query'].') values '. "\n"; + $query .= '("'. $device['hostname'] .'", "'. $device['ip_addr'] .'", "'.$device['type'].'", "'. $device['vendor'] .'", '. "\n"; + $query .= ' "'. $device['model'] .'", "'. $device['version'] .'", "'. $device['description'] .'", "'. $device['sections'] .'" '. $myFieldsInsert['values'] .');'. "\n"; + } + else if($device['action'] == "edit") { + + # custom fields + $myFields = getCustomFields('devices'); + $myFieldsInsert['query'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + if(strlen($device[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = NULL '; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = "'.$device[$myField['name']].'" '; + } + } + } + + $query = 'update `devices` set '. "\n"; + $query .= '`hostname` = "'. $device['hostname'] .'", `ip_addr` = "'. $device['ip_addr'] .'", `type` = "'. $device['type'] .'", `vendor` = "'. $device['vendor'] .'", '. "\n"; + $query .= '`model` = "'. $device['model'] .'", `version` = "'. $device['version'] .'", `description` = "'. $device['description'] .'", '. "\n"; + $query .= '`sections` = "'. $device['sections'] .'" '. "\n"; + $query .= $myFieldsInsert['query']; + $query .= 'where `id` = "'. $device['switchId'] .'";'. "\n"; + } + else if($device['action'] == "delete") { + $query = 'delete from `devices` where id = "'. $device['switchId'] .'";'. "\n"; + } + + /* prepare log */ + $log = prepareLogFromArray ($device); + + /* execute */ + try { $res = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Device ' . $device['action'] .' failed ('. $device['hostname'] . ')'.$error, $log, 2); + return false; + } + + /* success */ + updateLogTable ('Device ' . $device['action'] .' success ('. $device['hostname'] . ')', $log, 0); + return true; +} + + +/** + * Update device details + */ +function updateDevicetypeDetails($device) +{ + global $database; + + /* set querry based on action */ + if($device['action'] == "add") { $query = "insert into `deviceTypes` (`tname`,`tdescription`) values ('$device[tname]', '$device[tdescription]');"; } + else if($device['action'] == "edit") { $query = "update `deviceTypes` set `tname` = '$device[tname]', `tdescription` = '$device[tdescription]' where `tid` = $device[tid]"; } + else if($device['action'] == "delete") { $query = "delete from `deviceTypes` where `tid` = $device[tid];"; } + + /* prepare log */ + $log = prepareLogFromArray ($device); + + /* execute */ + try { $res = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print $error; + updateLogTable ("Device $device[action] failed ($device[tname]) $error", $log, 2); + return false; + } + + /* if delete we need to null type in devices! */ + if($device['action'] == "delete") { + $query = "update `devices` set `type` = NULL where `type` = $device[tid];"; + try { $res = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print "
    $e
    ";; + } + } + + /* success */ + updateLogTable ("Device $device[action] success ($device[tname])", $log, 0); + return true; +} + + +/** + * reformat sections for devices! + * sections are separated with ; + */ +function reformatDeviceSections ($sections) { + + if(sizeof($sections != 0)) { + + //first reformat + $temp = explode(";", $sections); + + foreach($temp as $section) { + //we have sectionId, so get its name + $out = getSectionDetailsById($section); + $out = $out['name']; + + //format output + $result[$out] = $section; + } + } + + //return result if it exists + if($result) { + return $result; + } + else { + return false; + } +} + + +/** + * get switch type + */ +function getDeviceTypes() +{ + global $database; + + $query = "select * from `deviceTypes`;"; + + /* execute */ + try { $devices = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* rekey */ + foreach($devices as $d) { + $devices2[$d['tid']] = _("$d[tname]"); + } + + /* return unique devices */ + return $devices2; +} + + +/** + * Transfor switch type + */ +function TransformDeviceType($type) +{ + global $database; + + $query = "select * from `deviceTypes` where `tid` = $type;"; + + /* execute */ + try { $devices = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + return $devices[0]['tname']; +} + + + + + + + + + + + + +/* @adLDAP functions ---------------- */ + +/** + * Get Domain settings for authentication + */ +if(!function_exists('getADSettings')) { +function getADSettings() +{ + global $database; + + /* Check connection */ + if ($database->connect_error) { + die('Connect Error (' . $database->connect_errno . '): '. $database->connect_error); + } + + /* first update request */ + $query = 'select * from `settingsDomain` limit 1;'; + $settings = $database->getArray($query); + + /* execute */ + try { $settings = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return settings */ + return($settings[0]); +} +} + + +/** + * Get Domain settings for authentication + */ +function updateADsettings($ad) +{ + global $database; + + /* Check connection */ + if ($database->connect_error) { + die('Connect Error (' . $database->connect_errno . '): '. $database->connect_error); + } + + /* if OpenLDAP then append BaseDN to account suffix */ + if($ad['type'] == "2") { $ad['account_suffix'] = ",".$ad['base_dn']; } + + /* set query and update */ + $query = 'update `settingsDomain` set '. "\n"; + $query .= '`domain_controllers` = "'. $ad['domain_controllers'] .'", `base_dn` = "'. $ad['base_dn'] .'", `account_suffix` = "'. $ad['account_suffix'] .'", '. "\n"; + $query .= '`use_ssl` = "'. $ad['use_ssl'] .'", `use_tls` = "'. $ad['use_tls'] .'", `ad_port` = "'. $ad['ad_port'] .'", `adminUsername`="'.$ad['adminUsername'].'", `adminPassword`="'.$ad['adminPassword'].'";'. "\n"; + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + # success + return true; +} + + + + + + + + +/* @VRF functions ---------------- */ + + +/** + * Update VRF details + */ +function updateVRFDetails($vrf) +{ + global $database; + + /* set querry based on action */ + if($vrf['action'] == "add") { + $query = 'insert into `vrf` '. "\n"; + $query .= '(`name`,`rd`,`description`) values '. "\n"; + $query .= '("'. $vrf['name'] .'", "'. $vrf['rd'] .'", "'. $vrf['description'] .'" ); '. "\n"; + } + else if($vrf['action'] == "edit") { + $query = 'update `vrf` set '. "\n"; + $query .= '`name` = "'. $vrf['name'] .'", `rd` = "'. $vrf['rd'] .'", `description` = "'. $vrf['description'] .'" '. "\n"; + $query .= 'where `vrfId` = "'. $vrf['vrfId'] .'";'. "\n"; + } + else if($vrf['action'] == "delete") { + $query = 'delete from `vrf` where `vrfId` = "'. $vrf['vrfId'] .'";'. "\n"; + } + + /* execute */ + try { $res = $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + updateLogTable ('VRF ' . $vrf['action'] .' failed ('. $vrf['name'] . ')'.$error, $log, 2); + return false; + } + + # if delete also NULL all subnets! + if($vrf['action'] == 'delete') { + $query = "update `subnets` set `vrfId` = NULL where `vrfId` = '$vrf[vrfId]';"; + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ('
    '.$error.'
    '); + } + } + + /* prepare log */ + $log = prepareLogFromArray ($vrf); + + /* return details */ + updateLogTable ('VRF ' . $vrf['action'] .' success ('. $vrf['name'] . ')', $log, 0); + return true; +} + + + + + + + + + + +/* @VLAN functions ---------------- */ + + +/** + * Update VLAN details + */ +function updateVLANDetails($vlan, $lastId = false) +{ + global $database; + + /* set querry based on action */ + if($vlan['action'] == "add") { + + # custom fields + $myFields = getCustomFields('vlans'); + $myFieldsInsert['query'] = ''; + $myFieldsInsert['values'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + # empty? + if(strlen($vlan[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", NULL"; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'`'; + $myFieldsInsert['values'] .= ", '". $vlan[$myField['name']] . "'"; + } + } + } + + $query = 'insert into `vlans` '. "\n"; + $query .= '(`name`,`number`,`description` '.$myFieldsInsert['query'].') values '. "\n"; + $query .= '("'. $vlan['name'] .'", "'. $vlan['number'] .'", "'. $vlan['description'] .'" '. $myFieldsInsert['values'] .' ); '. "\n"; + + } + else if($vlan['action'] == "edit") { + + # custom fields + $myFields = getCustomFields('vlans'); + $myFieldsInsert['query'] = ''; + + if(sizeof($myFields) > 0) { + /* set inserts for custom */ + foreach($myFields as $myField) { + if(strlen($vlan[$myField['name']])==0) { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = NULL '; + } else { + $myFieldsInsert['query'] .= ', `'. $myField['name'] .'` = "'.$vlan[$myField['name']].'" '; + } + } + } + + $query = 'update `vlans` set '. "\n"; + $query .= '`name` = "'. $vlan['name'] .'", `number` = "'. $vlan['number'] .'", `description` = "'. $vlan['description'] .'" '. "\n"; + $query .= $myFieldsInsert['query']; + $query .= 'where `vlanId` = "'. $vlan['vlanId'] .'";'. "\n"; + } + else if($vlan['action'] == "delete") { + $query = 'delete from `vlans` where `vlanId` = "'. $vlan['vlanId'] .'";'. "\n"; + } + + /* execute */ + try { $res = $database->executeQuery( $query, true ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + updateLogTable ('VLAN ' . $vlan['action'] .' failed ('. $vlan['name'] . ')'.$error, $log, 2); + return false; + } + + # if delete also NULL all subnets! + if($vlan['action'] == 'delete') { + $query = "update `subnets` set `vlanId` = NULL where `vlanId` = '$vlan[vlanId]';"; + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ('
    '.$error.'
    '); + } + } + + /* prepare log */ + $log = prepareLogFromArray ($vlan); + + /* return success */ + updateLogTable ('VLAN ' . $vlan['action'] .' success ('. $vlan['name'] . ')', $log, 0); + + /* response */ + if($lastId) { return $res; } + else { return true; } +} + + + + + + + + + + +/* @other functions ---------------- */ + + +/** + * update site settings + */ +function updateSettings($settings) +{ + global $database; + + filter_user_input ($settings, true, true, false); + + /* first update request */ + $query = 'update `settings` set ' . "\n"; + $query .= '`siteTitle` = "'. $settings['siteTitle'] .'", ' . "\n"; + $query .= '`siteDomain` = "'. $settings['siteDomain'] .'", ' . "\n"; + $query .= '`siteURL` = "'. $settings['siteURL'] .'", ' . "\n"; + $query .= '`siteAdminName` = "'. $settings['siteAdminName'] .'", ' . "\n"; + $query .= '`siteAdminMail` = "'. $settings['siteAdminMail'] .'", ' . "\n"; + $query .= '`domainAuth` = "'. isCheckbox($settings['domainAuth']) .'", ' . "\n"; + $query .= '`enableIPrequests` = "'. isCheckbox($settings['enableIPrequests']) .'", ' . "\n"; + $query .= '`enableVRF` = "'. isCheckbox($settings['enableVRF']) .'", ' . "\n"; + $query .= '`donate` = "'. isCheckbox($settings['donate']) .'", ' . "\n"; + $query .= '`enableDNSresolving` = "'. isCheckbox($settings['enableDNSresolving']) .'", ' . "\n"; + $query .= '`dhcpCompress` = "'. isCheckbox($settings['dhcpCompress']) .'", ' . "\n"; + $query .= '`printLimit` = "'. $settings['printLimit'] .'", ' . "\n"; + $query .= '`visualLimit` = "'. $settings['visualLimit'] .'", ' . "\n"; + $query .= '`vlanDuplicate` = "'. isCheckbox($settings['vlanDuplicate']) .'", ' . "\n"; + $query .= '`vlanMax` = "'. $settings['vlanMax'] .'", ' . "\n"; + $query .= '`api` = "'. isCheckbox($settings['api']) .'", ' . "\n"; + $query .= '`enableChangelog` = "'. isCheckbox($settings['enableChangelog']) .'", ' . "\n"; + $query .= '`subnetOrdering` = "'. $settings['subnetOrdering'] .'", ' . "\n"; + $query .= '`pingStatus` = "'. $settings['pingStatus'] .'", ' . "\n"; + $query .= '`scanPingPath` = "'. $settings['scanPingPath'] .'", ' . "\n"; + $query .= '`scanMaxThreads` = "'. $settings['scanMaxThreads'] .'", ' . "\n"; + $query .= '`prettyLinks` = "'. $settings['prettyLinks'] .'", ' . "\n"; + $query .= '`inactivityTimeout` = "'. $settings['inactivityTimeout'] .'", ' . "\n"; + $query .= '`hideFreeRange` = "'. isCheckbox($settings['hideFreeRange']) .'", ' . "\n"; + $query .= '`defaultLang` = "'. $settings['defaultLang'] .'" ' . "\n"; + $query .= 'where id = 1;' . "\n"; + + /* set log file */ + foreach($settings as $key=>$setting) { + $log .= " ". $key . ": " . $setting . "
    "; + } + + /* execute */ + try { + $database->executeQuery( $query ); + } + catch (Exception $e) { + $error = $e->getMessage(); + print '
    '._('Update settings error').':
    '. $error .'
    '; + updateLogTable ('Failed to update settings', $log, 2); + return false; + } + + if(!isset($e)) { + updateLogTable ('Settings updated', $log, 1); + return true; + } +} + + +/** + * post-install updates + */ +function postauth_update($adminpass, $siteTitle, $siteURL) +{ + global $database; + + $query = "update `users` set `password`='$adminpass',`passChange`='No' where `username` = 'Admin';"; //to update admin pass + $query .= "update `settings` set `siteTitle`='$siteTitle',`siteURL`='$siteURL';"; + + /* execute */ + try { + $database->executeMultipleQuerries( $query ); + } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('Failed to update settings', $log, 2); + return false; + } + return true; +} + + +/** + * update mail settings + */ +function updateMailSettings($settings) +{ + global $database; + + /* first update request */ + $query = 'update `settingsMail` set ' . "\n"; + $query .= '`mtype` = "'. $settings['mtype'] .'", ' . "\n"; + $query .= '`mserver` = "'. $settings['mserver'] .'", ' . "\n"; + $query .= '`mport` = "'. $settings['mport'] .'", ' . "\n"; + $query .= '`mauth` = "'. $settings['mauth'] .'", ' . "\n"; + $query .= '`msecure` = "'. $settings['msecure'] .'", ' . "\n"; + $query .= '`muser` = "'. $settings['muser'] .'", ' . "\n"; + $query .= '`mpass` = "'. $settings['mpass'] .'", ' . "\n"; + $query .= '`mAdminName` = "'. $settings['mAdminName'] .'", ' . "\n"; + $query .= '`mAdminMail` = "'. $settings['mAdminMail'] .'" ' . "\n"; + $query .= 'where id = 1;' . "\n"; + + /* set log file */ + foreach($settings as $key=>$setting) { + $log .= " ". $key . ": " . $setting . "
    "; + } + + /* execute */ + try { + $database->executeQuery( $query ); + } + catch (Exception $e) { + $error = $e->getMessage(); + print '
    '._('Update settings error').':
    '. $error .'
    '; + updateLogTable ('Failed to update settings', $log, 2); + return false; + } + + if(!isset($e)) { + updateLogTable ('Settings updated', $log, 1); + return true; + } +} + + +/** + * Verify checkboxes for saving config + */ +function isCheckbox($checkbox) +{ + if($checkbox == "") { $chkbox = "0"; } + else { $chkbox = $checkbox; } + + /* return 0 if not checkbos and same result if checkbox */ + return $chkbox; +} + + +/** + * Search and replace fields + */ +function searchAndReplace($query, $post) +{ + global $database; + + /* check how many records are in database */ + $query2 = 'select count(*) as count from `ipaddresses` where '. $post['field'] .' like "%'. $post['search'] .'%";'; + $count = $database->getArray($query2); + $count = $count[0]['count']; + + /* execute */ + try { + $database->executeQuery( $query ); + } + catch (Exception $e) { + $error = $e->getMessage(); + die('
    '._('Error').': '. $error .'
    '); + } + + if(!isset($e)) { + print '
    '._('Replaced').' '. $count .' '._('items successfully').'!
    '; + } +} + + +/** + * Write instructions + */ +function writeInstructions ($instructions) +{ + global $database; + + $instructions = $database->real_escape_string($instructions); //this hides code + + # execute query + $query = "update `instructions` set `instructions` = '". $instructions ."';"; + + /* update database */ + if ( !$database->executeQuery($query) ) { + updateLogTable ('Instructions update failed', $instructions, 2); + return false; + } + else { + updateLogTable ('Instructions update succeeded', $instructions, 1); + return true; + } +} + + +/** + * CSV import IP address + * + * provided input is CSV line! + */ +function importCSVline ($line, $subnetId) +{ + global $database; + + /* get subnet details by Id */ + $subnetDetails = getSubnetDetailsById ($subnetId); + $subnet = Transform2long($subnetDetails['subnet']) . "/" . $subnetDetails['mask']; + + /* verify! */ + $err = VerifyIpAddress( $line[0],$subnet ); + if($err) { return _('Wrong IP address').' - '.$err.' - '.$line[0]; } + + /* check for duplicates */ + if (checkDuplicate ($line[0], $subnetId)) { return _('IP address already exists').' - '.$line[0]; } + + /* get custom fields */ + $myFields = getCustomFields('ipaddresses'); + if(sizeof($myFields) > 0) { + $import['fieldName'] = ""; + $import['fieldValue'] = ""; + $m = 9; + foreach($myFields as $field) { + //escape chars + $line[$m] = mysqli_real_escape_string($database, $line[$m]); + + $import['fieldName'] .= ",`$field[name]`"; + $import['fieldValue'] .= ",'$line[$m]'"; + $m++; + } + } + + /* escape chars */ + foreach($line as $k=>$l) { + $line[$k] = mysqli_real_escape_string($database, $l); + } + + /* all ok, set query */ + $query = "insert into ipaddresses "; + $query .= "(`subnetId`, `ip_addr`, `state`, `description`, `dns_name`, `mac`, `owner`, `switch`, `port`, `note` $import[fieldName] ) "; + $query .= "values "; + $query .= "('$subnetId','".Transform2decimal( $line[0] )."', '$line[1]','$line[2]','$line[3]','$line[4]','$line[5]','$line[6]','$line[7]','$line[8]' $import[fieldValue]);"; + +/* + print "
    ";
    +	print_r($line);
    +	die('alert alert-danger');
    +*/
    +
    +	/* set log details */
    +	$log = prepareLogFromArray ($line);
    +
    +	/* execute */
    +    try {
    +    	$database->executeQuery( $query );
    +    }
    +    catch (Exception $e) {
    +    	$error = $e->getMessage();
    +	}
    +
    +	if(!isset($e)) {
    +        updateLogTable ('CSV import of IP address '. $line[1] .' succeeded', $log, 0);
    +		return true;
    +	}
    +	else {
    +        updateLogTable ('CSV import of IP address '. $line[1] .' failed', $log, 2);
    +        return $error;
    +	}
    +}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +/* @filter functions ---------------- */
    +
    +
    +/**
    + * Get all fields in IP addresses
    + */
    +function getIPaddrFields()
    +{
    +    global $database;
    +
    +    /* first update request */
    +    $query    = 'describe `ipaddresses`;';
    +
    +    /* execute */
    +    try { $fields = $database->getArray( $query ); }
    +    catch (Exception $e) {
    +        $error =  $e->getMessage();
    +        print ("
    "._('Error').": $error
    "); + return false; + } + + /* return Field values only */ + foreach($fields as $field) { + $res[$field['Field']] = $field['Field']; + } + + return $res; +} + + +/** + * Get selected IP fields + */ +function getSelectedIPaddrFields() +{ + global $settings; + # we only need it if it is not already set! + if(!isset($settings)) { + + global $database; + + /* first update request */ + $query = 'select IPfilter from `settings`;'; + + /* execute */ + try { $fields = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + return $fields[0]['IPfilter']; + } + else { + return $settings['IPfilter']; + } + + return $settings['IPfilter']; +} + + +/** + * Set selected IP fields + */ +function updateSelectedIPaddrFields($fields) +{ + global $database; + + /* first update request */ + $query = 'update `settings` set `IPfilter` = "'. $fields .'";'; + + # execute query + if (!$database->executeQuery($query)) { + updateLogTable ('Failed to change IP field filter', $fields, 2); + return false; + } + else { + updateLogTable ('IP field filter change success', $fields, 1); + return true; + } +} + + + + + + + + + + +/* @custom fields */ + + +/** + * Get all custom fields + */ +function getCustomFields($table) +{ + global $database; + + /* first update request */ + $query = "show full columns from `$table`;"; + + /* execute */ + try { $fields = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return Field values only */ + foreach($fields as $field) { + $res[$field['Field']]['name'] = $field['Field']; + $res[$field['Field']]['type'] = $field['Type']; + $res[$field['Field']]['Comment'] = $field['Comment']; + $res[$field['Field']]['Null'] = $field['Null']; + $res[$field['Field']]['Default'] = $field['Default']; + } + + /* unset standard fields */ + if($table == "users") { + unset($res['id'], $res['username'], $res['password'], $res['groups'], $res['role'], $res['real_name'], $res['email'], $res['domainUser'], $res['lang']); + unset($res['editDate'],$res['widgets'],$res['favourite_subnets'],$res['mailNotify'],$res['mailChangelog'], $res['passChange']); + } + elseif($table == "devices") { + unset($res['id'], $res['hostname'], $res['ip_addr'], $res['type'], $res['vendor'], $res['model'], $res['version'], $res['description'], $res['sections'], $res['editDate']); + } + elseif($table == "subnets") { + unset($res['id'], $res['subnet'], $res['mask'], $res['sectionId'], $res['description'], $res['masterSubnetId']); + unset($res['vrfId'], $res['allowRequests'], $res['adminLock'], $res['vlanId'], $res['showName'],$res['permissions'],$res['editDate']); + unset($res['pingSubnet'], $res['isFolder'], $res['discoverSubnet']); + } + elseif($table == "ipaddresses") { + unset($res['id'], $res['subnetId'], $res['ip_addr'], $res['description'], $res['dns_name'], $res['switch']); + unset($res['port'], $res['mac'], $res['owner'], $res['state'], $res['note'], $res['lastSeen'], $res['excludePing'], $res['editDate']); + } + elseif($table == "vlans") { + unset($res['vlanId'], $res['name'], $res['number'], $res['description'],$res['editDate']); + } + + /* reset if empty */ + if(sizeof($res)==0) { $res = array(); } + + return $res; +} + + +/** + * Get all custom fields in number array + */ +function getCustomFieldsNumArr($table) +{ + $res = getCustomFields($table); + + /* reindex */ + foreach($res as $line) { + $out[] = $line['name']; + } + + return $out; +} + + +/** + * Update custom field + */ +function updateCustomField($field) +{ + global $database; + + /* escape vars */ + # set override + if($field['fieldType']!="set") { + $field = filter_user_input ($field, true, true); + } + + /* set db type values */ + if($field['fieldType']=="bool" || $field['fieldType']=="text" || $field['fieldType']=="date" || $field['fieldType']=="datetime") + { $field['ftype'] = "$field[fieldType]"; } + else { $field['ftype'] = "$field[fieldType]($field[fieldSize])"; } + + //default null + if(strlen($field['fieldDefault'])==0) { $field['fieldDefault'] = "NULL"; } + else { $field['fieldDefault'] = "'$field[fieldDefault]'"; } + + //character? + if($field['fieldType']=="varchar" || $field['fieldType']=="text" || $field['fieldType']=="set") { $charset = "CHARACTER SET utf8"; } + else { $charset = ""; } + + /* update request */ + if($field['action']=="delete") { $query = "ALTER TABLE `$field[table]` DROP `$field[name]`;"; } + else if ($field['action']=="edit"&&@$field['NULL']=="NO") { $query = "ALTER TABLE `$field[table]` CHANGE COLUMN `$field[oldname]` `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] NOT NULL COMMENT '$field[Comment]';"; } + else if ($field['action']=="edit") { $query = "ALTER TABLE `$field[table]` CHANGE COLUMN `$field[oldname]` `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] COMMENT '$field[Comment]';"; } + else if ($field['action']=="add"&&@$field['NULL']=="NO") { $query = "ALTER TABLE `$field[table]` ADD COLUMN `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] NOT NULL COMMENT '$field[Comment]';"; } + else if ($field['action']=="add") { $query = "ALTER TABLE `$field[table]` ADD COLUMN `$field[name]` $field[ftype] $charset DEFAULT $field[fieldDefault] NULL COMMENT '$field[Comment]';"; } + else { + return false; + } + + /* prepare log */ + $log = prepareLogFromArray ($field); + + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + updateLogTable ('Custom Field ' . $field['action'] .' failed ('. $field['name'] . ')', $log, 2); + return false; + } + + updateLogTable ('Custom Field ' . $field['action'] .' success ('. $field['name'] . ')', $log, 0); + return true; +} + + +/** + * reorder custom VLAN field + */ +function reorderCustomField($table, $next, $current) +{ + global $database; + + /* get field details */ + $old = getFullFieldData($table, $current); + + /* update request */ + if($old['Null']=="NO") { $query = 'ALTER TABLE `'.$table.'` MODIFY COLUMN `'. $current .'` '.$old['Type'].' NOT NULL COMMENT "'.$old['Comment'].'" AFTER `'. $next .'`;'; } + else { $query = 'ALTER TABLE `'.$table.'` MODIFY COLUMN `'. $current .'` '.$old['Type'].' DEFAULT NULL COMMENT "'.$old['Comment'].'" AFTER `'. $next .'`;'; } + + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + updateLogTable ('Custom Field reordering failed ('. $next .' was not put before '. $current .')', $log, 2); + return false; + } + + updateLogTable ('Custom Field reordering success ('. $next .' put before '. $current .')', $log, 0); + return true; +} + + +/** + * update filter for custom fields + */ +function save_filtered_custom_fields($table, $filtered) +{ + # prepare + if(is_null($filtered)) { $out = null; } + else { $out = $filtered; } + + # write + return write_custom_filter($table,$out); +} + + +/** + * save filtered fields + */ +function write_custom_filter($table, $out) +{ + $settings = getAllSettings(); + + if(strlen($settings['hiddenCustomFields'])>0) { $filterField = json_decode($settings['hiddenCustomFields'], true); } + else { $filterField = array(); } + + # set + if(is_null($out)) { unset($filterField[$table]); } + else { $filterField[$table]=$out; } + + # encode + $filterField = json_encode($filterField); + + # write + global $database; + $query = "update `settings` set `hiddenCustomFields`='$filterField';"; + + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + return true; +} + + + + + + + + + + +/* @api --------------------*/ + +/** + * Get all API keys + */ +function getAPIkeys() +{ + global $database; + + # set query + $query = 'select * from `api`;'; + # get result + try { $apis = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + return $apis; +} + + +/** + * Get API key by name + */ +function getAPIkeyByName($app_id, $reformat = false) +{ + global $database; + + # set query + $query = "select * from `api` where `app_id` = '$app_id';"; + # get result + try { $api = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + # reformat? + if($reformat) { + $out[$api[0]['app_id']] = $api[0]['app_code']; + return $out; + } + else { + return $api[0]; + } +} + + +/** + * Get API key by id + */ +function getAPIkeyById($id) +{ + global $database; + + # set query + $query = "select * from `api` where `id` = $id;"; + # get result + try { $api = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + return $api[0]; +} + + +/** + * Modify API details + */ +function modifyAPI($api) +{ + global $database; + + # set query based on action + if($api['action']=="add") { $query = "insert into `api` (`app_id`,`app_code`,`app_permissions`, `app_comment`) values ('$api[app_id]','$api[app_code]','$api[app_permissions]', '$api[app_comment]');"; } + elseif($api['action']=="edit") { $query = "update `api` set `app_id`='$api[app_id]',`app_code`='$api[app_code]',`app_permissions`='$api[app_permissions]', `app_comment`='$api[app_comment]' where `id`=$api[id] ; "; } + elseif($api['action']=="delete") { $query = "delete from `api` where `id` = $api[id];"; } + else { return false; } + + $log = prepareLogFromArray ($api); # prepare log + + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + updateLogTable ('API update failed - '.$error, $log, 2); # write error log + return false; + } + + # success + updateLogTable ('API updated ok', $log, 1); # write success log + return true; +} + + + +/** + * Verify database + */ +function verifyDatabase() +{ + /* required tables */ + $reqTables = array("instructions", "ipaddresses", "logs", "requests", "sections", "settings", "settingsDomain", "subnets", "devices", "deviceTypes", "users", "vrf", "vlans", "widgets", "changelog", "userGroups", "lang", "api", "settingsMail"); + + /* required fields for each table */ + $fields['instructions'] = array("instructions"); + $fields['ipaddresses'] = array("subnetId", "ip_addr", "description", "dns_name", "mac", "owner", "switch", "port", "owner", "state", "note", "lastSeen", "excludePing"); + $fields['logs'] = array("severity", "date", "username", "ipaddr", "command", "details"); + $fields['requests'] = array("subnetId", "ip_addr", "description", "dns_name", "owner", "requester", "comment", "processed", "accepted", "adminComment"); + $fields['sections'] = array("name", "description", "permissions", "strictMode", "subnetOrdering", "order", "showVLAN", "showVRF", "masterSection"); + $fields['settings'] = array("siteTitle", "siteAdminName", "siteAdminMail", "siteDomain", "siteURL", "domainAuth", "enableIPrequests", "enableVRF", "enableDNSresolving", "version", "dbverified", "donate", "IPfilter", "printLimit", "visualLimit", "vlanDuplicate", "vlanMax", "subnetOrdering", "pingStatus", "defaultLang", "api", "editDate", "vcheckDate", "dhcpCompress", "enableChangelog", "scanPingPath", "scanMaxThreads", "prettyLinks", "hideFreeRange", "hiddenCustomFields", "inactivityTimeout"); + $fields['settingsDomain'] = array("account_suffix", "base_dn", "domain_controllers", "use_ssl", "use_tls", "ad_port", "adminUsername", "adminPassword"); + $fields['subnets'] = array("subnet", "mask", "sectionId", "description", "masterSubnetId", "vrfId", "allowRequests", "vlanId", "showName", "permissions", "pingSubnet", "discoverSubnet", "isFolder"); + $fields['devices'] = array("hostname", "ip_addr", "type", "vendor", "model", "version", "description", "sections"); + $fields['deviceTypes'] = array("tid", "tname", "tdescription"); + $fields['users'] = array("username", "password", "groups", "role", "real_name", "email", "domainUser", "lang", "widgets", "favourite_subnets", "mailNotify", "mailChangelog", "passChange"); + $fields['vrf'] = array("vrfId","name", "rd", "description"); + $fields['vlans'] = array("vlanId", "name", "number", "description"); + $fields['userGroups'] = array("g_id", "g_name", "g_desc"); + $fields['lang'] = array("l_id", "l_code", "l_name"); + $fields['api'] = array("app_id", "app_code", "app_permissions", "app_comment"); + $fields['changelog'] = array("cid", "ctype", "coid", "cuser", "caction", "cresult", "cdate", "cdiff"); + $fields['widgets'] = array("wid", "wtitle", "wdescription", "wfile", "wparams", "whref", "wsize", "wadminonly", "wactive"); + $fields['settingsMail'] = array("id", "mtype", "mauth", "mserver", "mport", "muser", "mpass", "mAdminName", "mAdminMail", "msecure"); + + /** + * check that each database exist - if it does check also fields + * 2 errors -> $tableError, $fieldError[table] = field + ****************************************************************/ + + foreach($reqTables as $table) { + + //check if table exists + if(!tableExists($table)) { + $error['tableError'][] = $table; + } + //check for each field + else { + foreach($fields[$table] as $field) { + //if it doesnt exist store error + if(!fieldExists($table, $field)) { + $error['fieldError'][$table] = $field; + } + } + } + } + + /* result */ + if(isset($error)) { + return $error; + } else { + return array(); + } +} + + +/** + * Update verified flag + */ +function updateDBverify() +{ + global $database; + + # set query based on action + $query = "update `settings` set `dbverified`='1'; "; + /* execute */ + try { $database->executeQuery( $query ); } + catch (Exception $e) { } + # return + return true; +} + + +/** + * get table fix + */ +function getTableFix($table) +{ + $res = fopen(dirname(__FILE__) . "/../db/SCHEMA.sql", "r"); + $file = fread($res, 100000); + + //go from delimiter on + $file = strstr($file, "DROP TABLE IF EXISTS `$table`;"); + $file = trim(strstr($file, "# Dump of table", true)); + + # check + if(strpos($file, "DROP TABLE IF EXISTS `$table`;") > 0 ) return false; + else return $file; +} + + +/** + * get field fix + */ +function getFieldFix($table, $field) +{ + $res = fopen(dirname(__FILE__) . "/../db/SCHEMA.sql", "r"); + $file = fread($res, 100000); + + //go from delimiter on + $file = strstr($file, "DROP TABLE IF EXISTS `$table`;"); + $file = trim(strstr($file, "# Dump of table", true)); + + //get proper line + $file = explode("\n", $file); + foreach($file as $k=>$l) { + if(strpos(trim($l), "$field`")==1) { + //get previous + $prev = trim($file[$k-1]); + $prev = explode("`", $prev); + $prev = "`$prev[1]`"; + + $res = trim($l, ","); + $res .= " after $prev;"; + + return $res; + } + } + + return false; +} + + +/** + * Fix table + */ +function fixTable($table) +{ + global $database; + + //get fix + $query = getTableFix($table); + + /* execute */ + try { $database->executeMultipleQuerries( $query ); } + catch (Exception $e) { + die("
    ".$e->getMessage()."
    "); + } + # return + return true; +} + + +/** + * Fix field + */ +function fixField($table, $field) +{ + global $database; + + //get fix + $query = "alter table `$table` add "; + $query .= trim(getFieldFix($table, $field), ","); + $query .= ";"; + + /* execute */ + try { $database->executeMultipleQuerries( $query ); } + catch (Exception $e) { + die("
    ".$e->getMessage()."
    "); + } + # return + return true; +} + + + + +?> \ No newline at end of file diff --git a/functions/functions_old/functions-common.php b/functions/functions_old/functions-common.php new file mode 100755 index 000000000..f5a4b657e --- /dev/null +++ b/functions/functions_old/functions-common.php @@ -0,0 +1,1924 @@ +0) { + foreach($get as $g) { + if(preg_match('/[^A-Za-z0-9_.#\\-$]/', $g)) { + # permit for search + if($get['section']!="search") { + header("Location:".create_link("error","406")); + } } } } +} + + +/** + * create URL + */ +function createURL () +{ + # reset url for base + if($_SERVER['SERVER_PORT'] == "443") { $url = "https://$_SERVER[HTTP_HOST]".BASE; } + // reverse proxy doing SSL offloading + elseif(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') { $url = "https://$_SERVER[SERVER_NAME]".BASE; } + elseif(isset($_SERVER['HTTP_X_SECURE_REQUEST']) && $_SERVER['HTTP_X_SECURE_REQUEST'] == 'true') { $url = "https://$_SERVER[SERVER_NAME]".BASE; } + // custom port + elseif($_SERVER['SERVER_PORT']!="80") { $url = "http://$_SERVER[HTTP_HOST]:$_SERVER[SERVER_PORT]".BASE; } + // normal http + else { $url = "http://$_SERVER[HTTP_HOST]".BASE; } + + //result + return $url; +} + + + + +/** + * protect against injections + * + * sql protects against SQL injections (mysql_escape_string) + * xss protects agains XSS injections (strip_tags) + * action sets permitted actions! + */ +function filter_user_input ($input, $sql = true, $xss = true, $actions = false) +{ + # XSS + if($xss) { + + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = strip_tags($v); } + } + else { + $input = strip_tags($input); + } + } + + # sql? + if($sql) { + global $database; + + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = $database->real_escape_string($v); } + } + else { + $input = $database->real_escape_string($input); + } + } + + # actions + if($actions) { + $permitted = array("add", "edit", "delete", "truncate", "split", "resize", "move"); + if(!in_array($input, $permitted)) { + die("
    Invalid action!
    "); + } + } + + + return $input; +} + + +/** + * Trim user input + */ +function trim_user_input($input) +{ + if(is_array($input)) { + foreach($input as $k=>$v) { $input[$k] = trim($v); } + } + else { + $input = $database->trim($input); + } + + return $input; +} + + + + + + +/* @user based functions ---------- */ + + + + +/** + * reset inactivity time + */ +function reset_inactivity_time() +{ + $_SESSION['lastactive'] = time(); +} + + +/** + * Functions to check if user is authenticated properly for ajax-loaded pages + * + */ +function isUserAuthenticated($die = true) +{ + /* open session and get username / pass */ + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + /* redirect if not authenticated */ + if (empty($_SESSION['ipamusername'])) { + # save requested page + $_SESSION['phpipamredirect'] = $_SERVER['HTTP_REFERER']; //here we need referrer + + $url = createURL (); + # die + if($die) { die(''); } + else { die("
    "._('Error')."
    "._('Please login first')."!
    "._('Login').""); } + } + + /* close session */ + session_write_close(); +} + + +/** + * Functions to check if user is authenticated properly + * + * If not redirect to login! + */ +function isUserAuthenticatedNoAjax () +{ + /* open session and get username / pass */ + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + /* redirect if not authenticated */ + if (empty($_SESSION['ipamusername'])) { + # save requested page + $_SESSION['phpipamredirect'] = $_SERVER['SCRIPT_URI']; + + $url = createURL (); + # redirect + header("Location:".$url.create_link("login","timeout")); + } + else { + if($_GET['page']!="login" && $_GET['page']!="request_ip" && $_GET['page']!="upgrade" && $_GET['page']!="install") { + global $settings; + /* check inactivity time */ + if( strlen($settings['inactivityTimeout']>0) && (time()-$_SESSION['lastactive']) > $settings['inactivityTimeout']) { + # redirect + $url = createURL (); + header("Location:".$url.create_link("login","timeout")); + } + } + reset_inactivity_time(); + } + + /* close session */ + session_write_close(); +} + + +/** + * Check if user is admin + */ +function checkAdmin ($die = true) +{ + /* first get active username */ + if(!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + $ipamusername = $_SESSION['ipamusername']; + session_write_close(); + + /* set check query and get result */ + global $database; + + /* Check connection */ + if ($database->connect_error) { + if($_SERVER['SERVER_PORT'] == "443") { $url = "https://".$_SERVER['HTTP_HOST'].BASE; } + elseif($_SERVER['SERVER_PORT']!="80") { $url = "http://".$_SERVER['HTTP_HOST'].":".$_SERVER['SERVER_PORT'].BASE; } + else { $url = "http://".$_SERVER['HTTP_HOST'].BASE; } + # redirect + header("Location:".$url.create_link("login")); + } + + /* set query if database exists! */ + $query = 'select role from users where `username` = "'. $ipamusername .'";'; + + /* fetch role */ + try { $role = $database->getRow( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + die ("
    "._('Error').": $error
    "); + } + + + /* return true if admin, else false */ + if ($role[0] == "Administrator") { + return true; + } + else { + //die + if($die == true) { die('
    '._('Administrator level privileges required').'!
    '); } + //return false if called + else { return false; } + } + +} + + +/** + * Get active users username - from session! + */ +function getActiveUserDetails () +{ + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + + if(isset($_SESSION['ipamusername'])) { + return getUserDetailsByName ($_SESSION['ipamusername']); + } + session_write_close(); +} + + +/** + * Get all users + */ +function getAllUsers () +{ + global $database; + + /* set query, open db connection and fetch results */ + $query = 'select * from users order by `role` asc, `real_name` asc;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return results */ + return($details); +} + + +/** + * Get number of users + */ +function getNumberOfUsers () +{ + global $database; + /* set query, open db connection and fetch results */ + $query = 'select count(*) as count from users;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return results */ + return($details[0]['count']); +} + + +/** + * Get all admin users + */ +function getAllAdminUsers () +{ + global $database; + + /* check for possible errors because of cron */ + if(isset($database->error)) { + unset($database); + global $db; + $database = new database($db['host'], $db['user'], $db['pass'], $db['name'], NULL, false); + } + + /* set query, open db connection and fetch results */ + $query = 'select * from users where `role` = "Administrator" order by id desc;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return results */ + return($details); +} + + +/** + * Get user details by ID + */ +function getUserDetailsById ($id) +{ + # check if already in cache + if($user = checkCache("user", $id)) { + return $user; + } + # query + else { + global $database; + /* set query, open db connection and fetch results */ + $query = 'select * from users where id = "'. $id .'";'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + # save cache - id and name + writeCache("user", $id, $details[0]); + writeCache("user", $details[0]['username'], $details[0]); + + /* return results */ + return($details[0]); + } +} + + +/** + * Get user details by name + */ +function getUserDetailsByName ($username, $killsession = true) +{ + # check if already in cache + if($user = checkCache("user", $username)) { + return $user; + } + # query + else { + # for db upgrade! + if(strpos($_SERVER['SCRIPT_URI'], "databaseUpgrade.php")>0) { + global $db; + $database = new database($db['host'], $db['user'], $db['pass'], $db['name']); + } + else { + global $database; + } + /* set query, open db connection and fetch results */ + $query = 'select * from users where `username` = "'. $username .'";'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + # result must be more than 1! + if(!isset($details[0])) { + if($killsession) { + global $phpsessname; + if(strlen($phpsessname)>0) { session_name($phpsessname); } + session_start(); + session_destroy(); + return false; + } + } + else { + # save cache - id and name + writeCache("user", $details[0]['id'], $details[0]); + writeCache("user", $username, $details[0]); + + /* return results */ + return($details[0]); + } + + } +} + +/** + * Get user lang + */ +function getUserLang ($username) +{ + global $database; + /* set query, open db connection and fetch results */ + $query = 'select `lang`,`l_id`,`l_code`,`l_name` from `users` as `u`,`lang` as `l` where `l_id` = `lang` and `username` = "'.$username.'";;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return results */ + return($details[0]); +} + + +/** + * Get all lang + */ +function getLanguages () +{ + global $database; + /* set query, open db connection and fetch results */ + $query = 'select * from `lang`;'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return results */ + return($details); +} + + +/** + * Get lang by id + */ +function getLangById ($id) +{ + # check cache + if($vtmp = checkCache("lang", $id)) { + return $vtmp; + } + else { + + global $database; + /* set query, open db connection and fetch results */ + $query = 'select * from `lang` where `l_id` = "'.$id.'";'; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + # save cache + writeCache("lang", $id, $details[0]); + /* return results */ + return($details[0]); + } +} + + +/** + * Get all widgets + */ +function getAllWidgets($admin = false, $inactive = false) +{ + global $database; + + # inactive also - only for administration + if($inactive) { $query = "select * from `widgets`; "; + } + else { + # admin? + if($admin) { $query = "select * from `widgets` where `wactive` = 'yes'; "; } + else { $query = "select * from `widgets` where `wadminonly` = 'no' and `wactive` = 'yes'; "; } + } + + /* execute */ + try { $widgets = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* reindex */ + foreach($widgets as $w) { + $wout[$w['wfile']] = $w; + } + + /* return results */ + return $wout; +} + + +/** + * Get widget by id + */ +function getWidgetById($wid) +{ + global $database; + # query + $query = "select * from `widgets` where `wid` = '$wid'; "; + + /* execute */ + try { $widget = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return results */ + return $widget[0]; +} + + +/** + * Get widget by filename + */ +function getWidgetByFile($wfile) +{ + global $database; + # query + $query = "select * from `widgets` where `wfile` = '$wfile'; "; + + /* execute */ + try { $widget = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return results */ + return $widget[0]; +} + + +/** + * Verify widget + */ +function verifyWidget ($file) +{ + //verify that proper files exist + if(!file_exists("app/dashboard/widgets/$file.php")) { return false; } + else { return true; } +} + + +/** + * get user favourite subnets + */ +function getFavouriteSubnets() +{ + # get user details + $user = getActiveUserDetails(); + + # none + if(strlen($user['favourite_subnets'])==0) { + return false; + } + # ok + else { + //store to array + $favs = explode(";", $user['favourite_subnets']); + $favs = array_filter($favs); + //fetch details + $subnets = getUserFavouriteSubnets($favs); + + return $subnets; + } + +} + + +/** + * get user favourite subnets + */ +function getUserFavouriteSubnets($subnetIds) +{ + global $database; + + # get details for each id + foreach($subnetIds as $id) { + $query = "select `su`.`id` as `subnetId`,`se`.`id` as `sectionId`, `subnet`, `mask`,`su`.`description`,`se`.`description` as `section`, `vlanId`, `isFolder` + from `subnets` as `su`, `sections` as `se` where `su`.`id` = $id and `su`.`sectionId` = `se`.`id` limit 1;"; + + /* execute */ + try { $sdetails = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + # out array + $subnets[] = $sdetails[0]; + } + + //return result + return $subnets; +} + + +/** + * check if subnet is favourited + */ +function isSubnetFavourite($subnetId) +{ + # get user details + $user = getActiveUserDetails(); + + # none + if(strlen($user['favourite_subnets'])==0) { + return false; + } + # check + else { + //store to array + $favs = explode(";", $user['favourite_subnets']); + //check + if(in_array($subnetId, $favs)) { + return true; + } else { + return false; + } + } +} + + +/** + * edit favourite + */ +function editFavourite($post) +{ + global $database; + + # get user details and favourites + $user = getActiveUserDetails(); + # empty + $old = explode(";", $user['favourite_subnets']); + + # set query + if($post['action'] == "remove") { + $new = implode(";", array_diff($old, array($post['subnetId']))); + $query = "update `users` set `favourite_subnets` = '$new' where `id` = '$user[id]' limit 1;"; + } elseif($post['action'] == "add") { + if(!is_array($old)) { $old = array(); } + $new = implode(";",array_merge(array($post['subnetId']), $old)); + $query = "update `users` set `favourite_subnets` = '$new' where `id` = '$user[id]' limit 1;"; + } else { + return false; + } + + # execute + try { $database->executeQuery( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + return true; +} + + + +/** + * Verify translation + */ +function verifyTranslation ($code) +{ + //verify that proper files exist + if(!file_exists("functions/locale/$code/LC_MESSAGES/phpipam.mo")) { return false; } + else { return true; } +} + + +/** + * Verify translation version + */ +function getTranslationVersion ($code) +{ + //check for version + $ver = shell_exec("grep 'Project-Id-Version:' ".dirname(__FILE__)."/locale/$code/LC_MESSAGES/phpipam.po"); + //parse + $ver = str_replace(array("Project-Id-Version:", " ", '"', "#",'\n', ":"), "", $ver); + //return version + return $ver; +} + + +/** + * Get full field data, including comments + */ +function getFullFieldData($table, $field) +{ + global $database; + + /* escape vars to prevent SQL injection */ + $table = filter_user_input ($table, true, true); + $field = filter_user_input ($field, true, true); + + /* set query, open db connection and fetch results */ + $query = "show full columns from `$table` where `Field` = '$field';"; + + /* execute */ + try { $details = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + //print ("
    "._('Error').": $error
    "); + return false; + } + + /* return results */ + return($details[0]); +} + + + + + + + + + + +/* @permission functions ---------- */ + +/** + * Check section permissions + */ +function checkSectionPermission ($sectionId) +{ + # open session and get username / pass + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + # redirect if not authenticated */ + if (empty($_SESSION['ipamusername'])) { return "0"; } + else { $username = $_SESSION['ipamusername']; } + + # get all user groups + global $userDetails; + if(!isset($userDetails)) { $user = getUserDetailsByName ($username); } + else { $user = $userDetails; } + $groups = json_decode($user['groups']); + + # if user is admin then return 3, otherwise check + if($user['role'] == "Administrator") { return "3"; } + + # get section permissions + $section = getSectionDetailsById($sectionId); + $sectionP = json_decode($section['permissions']); + + # default permission + $out = 0; + + # for each group check permissions, save highest to $out + if(sizeof($sectionP)>0) { + foreach($sectionP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + if(sizeof($groups)>0) { + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + } + # return permission level + return $out; +} + + +/** + * Check subnet permissions + */ +function checkSubnetPermission ($subnetId) +{ + # open session and get username / pass + if (!isset($_SESSION)) { global $phpsessname; if(strlen($phpsessname)>0) { session_name($phpsessname); } session_start(); } + # redirect if not authenticated */ + if (empty($_SESSION['ipamusername'])) { return "0"; } + else { $username = $_SESSION['ipamusername']; } + + # get all user groups + global $userDetails; + if(!isset($userDetails)) { $user = getUserDetailsByName ($username); } + else { $user = $userDetails; } + $groups = json_decode($user['groups']); + + # if user is admin then return 3, otherwise check + if($user['role'] == "Administrator") { return "3"; } + + # get subnet permissions + $subnet = getSubnetDetailsById($subnetId); + $subnetP = json_decode($subnet['permissions']); + + # get section permissions + $section = getSectionDetailsById($subnet['sectionId']); + $sectionP = json_decode($section['permissions']); + + # default permission + $out = 0; + + # for each group check permissions, save highest to $out + if(sizeof($sectionP) > 0) { + foreach($sectionP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + else { + $out = "0"; + } + + # if section permission == 0 then return 0 + if($out == "0") { + return "0"; + } + else { + $out = "0"; + # ok, user has section access, check also for any higher access from subnet + if(sizeof($subnetP) > 0) { + foreach($subnetP as $sk=>$sp) { + # check each group if user is in it and if so check for permissions for that group + foreach($groups as $uk=>$up) { + if($uk == $sk) { + if($sp > $out) { $out = $sp; } + } + } + } + } + } + + # return result + return $out; +} + + + + + + + + +/* @general functions ---------- */ + + +/** + * Get all site settings + */ +function getAllSettings() +{ + global $settings; + # check if it already exists + if(isset($settings)) { + if(isset($settings[0])) { return $settings[0]; } + else { return $settings; } + } + else { + + global $db; + global $database; + + /* first check if table settings exists */ + $query = 'SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = "'. $db['name'] .'" AND table_name = "settings";'; + + /* execute */ + try { $count = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return true if it exists */ + if($count[0]['count'] == 1) { + + /* select database */ + $database->selectDatabase($db['name']); + + /* get settings */ + $query = 'select * from settings where id = 1'; + $settings = $database->getArray($query); + + /* return settings */ + return($settings[0]); + } + else { + return false; + } + + } +} + + +/** + * Get all mail settings + */ +function getAllMailSettings() +{ + global $db; # get variables from config file + global $database; + + /* first check if table settings exists */ + $query = 'SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = "'. $db['name'] .'" AND table_name = "settingsMail";'; + + /* execute */ + try { $count = $database->getArray( $query ); } + catch (Exception $e) { + $error = $e->getMessage(); + print ("
    "._('Error').": $error
    "); + return false; + } + + /* return true if it exists */ + if($count[0]['count'] == 1) { + + /* select database */ + $database->selectDatabase($db['name']); + + /* first update request */ + $query = 'select * from `settingsMail` where id = 1'; + $settings = $database->getArray($query); + + /* return settings */ + return($settings[0]); + } + else { + return false; + } +} + + +/** + * validate email + */ +function checkEmail($email) { + if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/",$email)) { return false; } + else { return true; } +} + + +/** + * validate hostname + */ +function validateHostname($hostname) +{ + return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $hostname) //valid chars check + && preg_match("/^.{1,253}$/", $hostname) //overall length check + && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $hostname) ); //length of each label +} + + +/** + * Shorten text + */ +function ShortenText($text, $chars = 25) { + //count input text size + $startLen = strlen($text); + //cut onwanted chars + $text = substr($text,0,$chars); + //count output text size + $endLen = strlen($text); + + //append dots if it was cut + if($endLen != $startLen) { + $text = $text."..."; + } + + return $text; +} + + +/** + * Parse section/subnet permissions + */ +function parsePermissions($perm) +{ + switch($perm) { + case "0": $r = _("No access"); break; + case "1": $r = _("Read"); break; + case "2": $r = _("Read / Write"); break; + case "3": $r = _("Read / Write / Admin"); break; + default: $r = _("error"); + } + return $r; +} + + +/** + * secunds to hms + */ +function sec2hms($sec, $padHours = false) + { + // holds formatted string + $hms = ""; + + // get the number of hours + $hours = intval(intval($sec) / 3600); + + // add to $hms, with a leading 0 if asked for + $hms .= ($padHours) + ? str_pad($hours, 2, "0", STR_PAD_LEFT). ':' + : $hours. ':'; + + // get the seconds + $minutes = intval(($sec / 60) % 60); + + // then add to $hms (with a leading 0 if needed) + $hms .= str_pad($minutes, 2, "0", STR_PAD_LEFT). ':'; + + // seconds + $seconds = intval($sec % 60); + + // add to $hms, again with a leading 0 if needed + $hms .= str_pad($seconds, 2, "0", STR_PAD_LEFT); + + // return hms + return $hms; +} + + +/** + * get php exec path + */ +function getPHPExecutableFromPath() +{ + /* + not used anymore as it is not reliable, using PHP_BINDIR instead + */ + $paths = explode(PATH_SEPARATOR, getenv('PATH')); + foreach ($paths as $path) { + // we need this for XAMPP (Windows) + if (strstr($path, 'php.exe') && isset($_SERVER["WINDIR"]) && file_exists($path) && is_file($path)) { + return $path; + } + } + + //unix + $php_executable = PHP_BINDIR."/php"; + if (file_exists($php_executable) && is_file($php_executable)) { + return $php_executable; + } + + return FALSE; // not found +} + + + + + + + + + + +/* @menu builder */ + +/** + * Build the HTML menu + * + * based on http://pastebin.com/GAFvSew4 + */ +function get_menu_html( $subnets, $rootId = 0 ) +{ + $html = array(); + + foreach ( $subnets as $item ) + $children[$item['masterSubnetId']][] = $item; + + # loop will be false if the root has no children (i.e., an empty menu!) + $loop = !empty( $children[$rootId] ); + + # initializing $parent as the root + $parent = $rootId; + $parent_stack = array(); + + # verify subnetId + if(isset($_GET['subnetId'])) { + if(!is_numeric($_GET['subnetId'])) { die('
    '._("Invalid ID").'
    '); } + } + + # display selected subnet as opened + if(isset($_GET['subnetId'])) { $allParents = getAllParents ($_GET['subnetId']); } + else { $allParents = array(); } + + # Menu start + $html[] = '
      '; + + while ( $loop && ( ( $option = each( $children[$parent] ) ) || ( $parent > $rootId ) ) ) + { + # count levels + $count = count( $parent_stack ) + 1; + + # set opened or closed tag for displaying proper folders + if(in_array($option['value']['id'], $allParents)) { $open = "open"; $openf = "-open"; } + else { $open = "close"; $openf = ""; } + + # show also child's by default + if($option['value']['id']==$_GET['subnetId']) { + if(subnetContainsSlaves($_GET['subnetId'])) { $open = "open"; $openf = "-open"; } + else { $open = "close"; $openf = ""; } + } + + # override if cookie is set + if(isset($_COOKIE['expandfolders'])) { + if($_COOKIE['expandfolders'] == "1") { $open='open'; $openf = "-open"; } + } + + # for active class + if($_GET['page']=="subnets" && ($option['value']['id'] == $_GET['subnetId'])) { $active = "active"; $leafClass=""; } + else { $active = ""; $leafClass="icon-gray" ;} + + # override folder + if($option['value']['isFolder'] == 1 && ($option['value']['id'] == $_GET['subnetId'])) { $open = "open"; $openf = "-open"; $active = "active"; } + + # check for permissions if id is provided + if($option['value']['id'] != "") { + $sp = checkSubnetPermission ($option['value']['id']); + } + + if ( $option === false ) + { + $parent = array_pop( $parent_stack ); + + # HTML for menu item containing childrens (close) + $html[] = '
    '; + $html[] = ''; + } + # Has children + elseif ( !empty( $children[$option['value']['id']] ) ) + { + # if user has access permission + if($sp != 0) { + # folder + if($option['value']['isFolder'] == 1) { + $html[] = '
  • '; + $html[] = ''.$option['value']['description'].''; + } + # print name + elseif($option['value']['showName'] == 1) { + $html[] = '
  • '; + $html[] = ''.$option['value']['description'].''; + } + # print subnet + else { + $html[] = '
  • '; + $html[] = ''.Transform2long($option['value']['subnet']).'/'.$option['value']['mask'].''; + } + + # print submenu + if($open == "open") { $html[] = '
  • + + + + + + + + +
    + + + + + + \ No newline at end of file diff --git a/install/index.php b/install/index.php new file mode 100755 index 000000000..9aaa48418 --- /dev/null +++ b/install/index.php @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + phpIPAM installation error + + + + + + + + + + + + + + + + + +
    +
    + +
    Mod_rewrite error
    It seems your Apache is not set up properly to handle URL rewrites.
    + Please make sure all the requirements are set properly!

    + +

    1.) Define Base

    +
    + Make sure BASE directive is set for your installation. This is used to properly detect phpIPAM directory. It must be set in config.php and in .htaccess + +
    + Detected BASE: +
    + +

    2.) Enable mod_rewrite

    +
    + Search for Directory directive in default apache config (or vhost config) and add/change it to +
    + vi /etc/apache2/sites-enabled/000-default
    +
    + Options FollowSymLinks
    + AllowOverride all
    + Order allow,deny
    + Allow from all
    +
    + +

    + You can also follow the following guide: http://phpipam.net/phpipam-installation-on-debian-6-0-6/. +
    +
    + + + + \ No newline at end of file diff --git a/js/bootstrap-colorpicker.min.js b/js/bootstrap-colorpicker.min.js new file mode 100644 index 000000000..2eaaa84d1 --- /dev/null +++ b/js/bootstrap-colorpicker.min.js @@ -0,0 +1 @@ +!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):window.jQuery&&!window.jQuery.fn.colorpicker&&a(window.jQuery)}(function(a){"use strict";var b=function(a){this.value={h:0,s:0,b:0,a:1},this.origFormat=null,a&&(void 0!==a.toLowerCase?(a+="","#"===a.charAt(0)||3!==a.length&&6!==a.length||(a="#"+a),this.setColor(a)):void 0!==a.h&&(this.value=a))};b.prototype={constructor:b,colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4","indianred ":"#cd5c5c","indigo ":"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",transparent:"transparent"},_sanitizeNumber:function(a){return"number"==typeof a?a:isNaN(a)||null===a||""===a||void 0===a?1:void 0!==a.toLowerCase?parseFloat(a):1},isTransparent:function(a){return a?(a=a.toLowerCase().trim(),"transparent"==a||a.match(/#?00000000/)||a.match(/(rgba|hsla)\(0,0,0,0?\.?0\)/)):!1},rgbaIsTransparent:function(a){return 0==a.r&&0==a.g&&0==a.b&&0==a.a},setColor:function(a){a=a.toLowerCase().trim(),a&&(this.value=this.isTransparent(a)?{h:0,s:0,b:0,a:0}:this.stringToHSB(a)||{h:0,s:0,b:0,a:1})},stringToHSB:function(b){b=b.toLowerCase();var c=this,d=!1;return a.each(this.stringParsers,function(a,e){var f=e.re.exec(b),g=f&&e.parse.apply(c,[f]),h=e.format||"rgba";return g?(d=h.match(/hsla?/)?c.RGBtoHSB.apply(c,c.HSLtoRGB.apply(c,g)):c.RGBtoHSB.apply(c,g),c.origFormat=h,!1):!0}),d},setHue:function(a){this.value.h=1-a},setSaturation:function(a){this.value.s=a},setBrightness:function(a){this.value.b=1-a},setAlpha:function(a){this.value.a=parseInt(100*(1-a),10)/100},toRGB:function(a,b,c,d){a||(a=this.value.h,b=this.value.s,c=this.value.b),a*=360;var e,f,g,h,i;return a=a%360/60,i=c*b,h=i*(1-Math.abs(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],{r:Math.round(255*e),g:Math.round(255*f),b:Math.round(255*g),a:d||this.value.a}},toHex:function(a,b,c,d){var e=this.toRGB(a,b,c,d);return this.rgbaIsTransparent(e)?"transparent":"#"+(1<<24|parseInt(e.r)<<16|parseInt(e.g)<<8|parseInt(e.b)).toString(16).substr(1)},toHSL:function(a,b,c,d){a=a||this.value.h,b=b||this.value.s,c=c||this.value.b,d=d||this.value.a;var e=a,f=(2-b)*c,g=b*c;return g/=f>0&&1>=f?f:2-f,f/=2,g>1&&(g=1),{h:isNaN(e)?0:e,s:isNaN(g)?0:g,l:isNaN(f)?0:f,a:isNaN(d)?0:d}},toAlias:function(a,b,c,d){var e=this.toHex(a,b,c,d);for(var f in this.colors)if(this.colors[f]==e)return f;return!1},RGBtoHSB:function(a,b,c,d){a/=255,b/=255,c/=255;var e,f,g,h;return g=Math.max(a,b,c),h=g-Math.min(a,b,c),e=0===h?null:g===a?(b-c)/h:g===b?(c-a)/h+2:(a-b)/h+4,e=(e+360)%6*60/360,f=0===h?0:h/g,{h:this._sanitizeNumber(e),s:f,b:g,a:this._sanitizeNumber(d)}},HueToRGB:function(a,b,c){return 0>c?c+=1:c>1&&(c-=1),1>6*c?a+(b-a)*c*6:1>2*c?b:2>3*c?a+(b-a)*(2/3-c)*6:a},HSLtoRGB:function(a,b,c,d){0>b&&(b=0);var e;e=.5>=c?c*(1+b):c+b-c*b;var f=2*c-e,g=a+1/3,h=a,i=a-1/3,j=Math.round(255*this.HueToRGB(f,e,g)),k=Math.round(255*this.HueToRGB(f,e,h)),l=Math.round(255*this.HueToRGB(f,e,i));return[j,k,l,this._sanitizeNumber(d)]},toString:function(a){switch(a=a||"rgba"){case"rgb":var b=this.toRGB();return this.rgbaIsTransparent(b)?"transparent":"rgb("+b.r+","+b.g+","+b.b+")";case"rgba":var b=this.toRGB();return"rgba("+b.r+","+b.g+","+b.b+","+b.a+")";case"hsl":var c=this.toHSL();return"hsl("+Math.round(360*c.h)+","+Math.round(100*c.s)+"%,"+Math.round(100*c.l)+"%)";case"hsla":var c=this.toHSL();return"hsla("+Math.round(360*c.h)+","+Math.round(100*c.s)+"%,"+Math.round(100*c.l)+"%,"+c.a+")";case"hex":return this.toHex();case"alias":return this.toAlias()||this.toHex();default:return!1}},stringParsers:[{re:/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/,format:"rgb",parse:function(a){return[a[1],a[2],a[3],1]}},{re:/rgb\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,format:"rgb",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],1]}},{re:/rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[a[1],a[2],a[3],a[4]]}},{re:/rgba\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],a[4]]}},{re:/hsl\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*?\)/,format:"hsl",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/hsla\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,format:"hsla",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/#?([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,format:"hex",parse:function(a){return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16),1]}},{re:/#?([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,format:"hex",parse:function(a){return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16),1]}},{re:/^([a-z]{3,})$/,format:"alias",parse:function(a){var b=this.colorNameToHex(a[0])||"#000000",c=this.stringParsers[0].re.exec(b),d=c&&this.stringParsers[0].parse.apply(this,[c]);return d}}],colorNameToHex:function(a){return"undefined"!=typeof this.colors[a.toLowerCase()]?this.colors[a.toLowerCase()]:!1}};var c={horizontal:!1,inline:!1,color:!1,format:!1,input:"input",container:!1,component:".add-on, .input-group-addon",sliders:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setHue"},alpha:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setAlpha"}},slidersHorz:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:100,maxTop:0,callLeft:"setHue",callTop:!1},alpha:{maxLeft:100,maxTop:0,callLeft:"setAlpha",callTop:!1}},template:'