From 9a3b44d73d660835075e2ecd50b8abe31f59fde4 Mon Sep 17 00:00:00 2001 From: Andy Smith Date: Wed, 17 Aug 2011 02:21:23 +0100 Subject: [PATCH] Basic API and Authentication extensions. --- application/controllers/api.php | 141 ++++++++++ application/controllers/auth.php | 95 +++++++ application/models/api_model.php | 319 +++++++++++++++++++++++ application/models/auth_model.php | 95 +++++++ application/third_party/PasswordHash.php | 253 ++++++++++++++++++ application/views/api/index.php | 117 +++++++++ application/views/auth/add.php | 70 +++++ application/views/auth/edit.php | 72 +++++ application/views/auth/main.php | 24 ++ application/views/auth/view.php | 8 + css/api.css | 52 ++++ css/api.xsl | 66 +++++ 12 files changed, 1312 insertions(+) create mode 100644 application/controllers/api.php create mode 100644 application/controllers/auth.php create mode 100644 application/models/api_model.php create mode 100644 application/models/auth_model.php create mode 100644 application/third_party/PasswordHash.php create mode 100644 application/views/api/index.php create mode 100644 application/views/auth/add.php create mode 100644 application/views/auth/edit.php create mode 100644 application/views/auth/main.php create mode 100644 application/views/auth/view.php create mode 100644 css/api.css create mode 100644 css/api.xsl diff --git a/application/controllers/api.php b/application/controllers/api.php new file mode 100644 index 00000000..397f17bc --- /dev/null +++ b/application/controllers/api.php @@ -0,0 +1,141 @@ +load->model('logbook_model'); + $data['data'] = array(); + + // load the view + //$this->load->view('layout/header'); + $this->load->view('api/index', $data); + //$this->load->view('layout/footer'); + } + + // FUNCTION: search() + // Handle search requests + /* + Okay, so here's how it works in a nutshell... + + ******************************************************************* + Because this is effectively just a filter between the query string + and a MySQL statement, if done wrong we're just asking for pain. + + DO NOT alter any of the filtering statements without fully + understanding what you're doing. CodeIgniter provides some + protection against unwanted characters in the query string, but + this should in no way be relied upon for safety. + ******************************************************************* + + Example query:- + .../search/query[Call~M0*(and)(Locator~I*(or)Locator~J*)]/limit[10]/fields[distinct(Call),Locator]/order[Call(asc)] + + There's four parts to this query, separated with forward slashes. It's effectively a heavily-sanitised + MySQL query, hence the hideous search and replace code blocks below. + + FIELDS + ------ + Straightforward - input is sanitised and passed on - in the example, this ends up as "DISTINCT (Call),Locator", + which is then the first argument to 'SELECT' + + QUERY + ----- + This forms the 'WHERE' clause. + + * '(and)' and '(or)' are expanded out to ' AND ' and ' OR ' + * Parentheses are preserved + * '~' is expanded out to ' LIKE ' + * '*' is translated to '%' + * Values are encapsulated in quote marks + + So in the example, this translates to "WHERE Call LIKE 'M0%' AND (Locator LIKE 'I%' OR Locator LIKE 'J%')" + + ORDER + ----- + Sanitised, so our example ends up as "ORDER BY Call ASC". + + LIMIT + ----- + Straightforward - what's between the square brackets is passed as an argument to 'LIMIT' + + Finally, once this has been done, each field name is translated to the MySQL column name. + */ + + function search() + { + // Load the API and Logbook models + $this->load->model('api_model'); + $this->load->model('logbook_model'); + + // Retrieve the arguments from the query string + $arguments = $this->_retrieve(); + + // Call the parser within the API model to build the query + $query = $this->api_model->parse($arguments); + + // Execute the query, and retrieve the results + $s = $this->logbook_model->api_search_query($query); + $results = $s['results']; + + // Cycle through the results, and translate between MySQL column names + // and more friendly, descriptive names + $a = 0; + if($results->num_rows != 0) + { + foreach ($results->result() as $row) { + $record = (array)$row; + $r[$a]['rid'] = $a; + while (list($key, $val) = each($record)) { + $r[$a][$this->api_model->name($key)] = $val; + } + $a++; + } + // Add the result record to the main results array + $data['data']['search_Result']['results'] = $r; + } + else + { + // We've got no results, so make this empty for completeness + $data['data']['search_Result']['results'] = ""; + } + + // Add some debugging information to the XML output + $data['data']['queryInfo']['call'] = "search"; + $data['data']['queryInfo']['dbQuery'] = $s['query']; + $data['data']['queryInfo']['numResults'] = $a; + $data['data']['queryInfo']['executionTime'] = $s['time']; + + // Load the XML output view + $this->load->view('api/index', $data); + } + + // FUNCTION: _retrieve() + // Pull the search query arguments from the query string + private function _retrieve() + { + // This whole function could probably have been done in one line... if this was Perl. + $arguments = array(); + + // Retrieve each arguments + $query = preg_grep("/^query\[(.*)\]$/", $this->uri->segments); + $limit = preg_grep("/^limit\[(.*)\]$/", $this->uri->segments); + $order = preg_grep("/^order\[(.*)\]$/", $this->uri->segments); + $fields = preg_grep("/^fields\[(.*)\]$/", $this->uri->segments); + + // Strip each argument + $arguments['query'] = substr(array_pop($query), 6); + $arguments['query'] = substr($arguments['query'], 0, strlen($arguments['query']) - 1); + $arguments['limit'] = substr(array_pop($limit), 6); + $arguments['limit'] = substr($arguments['limit'], 0, strlen($arguments['limit']) - 1); + $arguments['order'] = substr(array_pop($order), 6); + $arguments['order'] = substr($arguments['order'], 0, strlen($arguments['order']) - 1); + $arguments['fields'] = substr(array_pop($fields), 7); + $arguments['fields'] = substr($arguments['fields'], 0, strlen($arguments['fields']) - 1); + + // Return the arguments + return $arguments; + } +} diff --git a/application/controllers/auth.php b/application/controllers/auth.php new file mode 100644 index 00000000..e6b04486 --- /dev/null +++ b/application/controllers/auth.php @@ -0,0 +1,95 @@ +load->model('auth_model'); + + echo "
";
+		echo "Querying for user...\n";
+		$u = $this->auth_model->get("m0vkga");
+		print_r($u);
+		echo "Test hashing\n";
+		echo $this->auth_model->test();
+
+	}
+	/*
+		$this->load->model('note');
+		$data['notes'] = $this->note->list_all();
+	
+		$this->load->view('layout/header');
+		$this->load->view('notes/main', $data);
+		$this->load->view('layout/footer');
+	}
+	
+	function add() {
+	
+		$this->load->model('note');
+	
+		$this->load->library('form_validation');
+
+		$this->form_validation->set_rules('title', 'Note Title', 'required');
+		$this->form_validation->set_rules('content', 'Content', 'required');
+
+
+		if ($this->form_validation->run() == FALSE)
+		{
+			$this->load->view('layout/header');
+			$this->load->view('notes/add');
+			$this->load->view('layout/footer');
+		}
+		else
+		{	
+			$this->note->add();
+			
+			redirect('notes');
+		}
+	}
+	
+	function view($id) {
+		$this->load->model('note');
+		
+		$data['note'] = $this->note->view($id);
+		
+		// Display
+		$this->load->view('layout/header');
+		$this->load->view('notes/view',$data);
+		$this->load->view('layout/footer');
+	}
+	
+	function edit($id) {
+		$this->load->model('note');
+		$data['id'] = $id;
+		
+		$data['note'] = $this->note->view($id);
+			
+		$this->load->library('form_validation');
+
+		$this->form_validation->set_rules('title', 'Note Title', 'required');
+		$this->form_validation->set_rules('content', 'Content', 'required');
+
+
+		if ($this->form_validation->run() == FALSE)
+		{
+			$this->load->view('layout/header');
+			$this->load->view('notes/edit', $data);
+			$this->load->view('layout/footer');
+		}
+		else
+		{
+			$this->note->edit();
+			
+			redirect('notes');
+		}
+	}
+	
+	function delete($id) {
+		$this->load->model('note');
+		$this->note->delete($id);
+		
+		redirect('notes');
+	}
+*/
+}
diff --git a/application/models/api_model.php b/application/models/api_model.php
new file mode 100644
index 00000000..54cd4581
--- /dev/null
+++ b/application/models/api_model.php
@@ -0,0 +1,319 @@
+_columnName[$col])
+		{
+			return $this->_columnName[$col]['Name'];
+		}
+		else
+		{
+			return 0;
+		}
+	}
+
+	// FUNCTION: string description(string $column)
+	// Returns the description for a MySQL column name
+	function description($col)
+	{
+		if($this->_columnName[$col])
+		{
+			if($this->_columnName[$col]['Description'] != "")
+			{
+				return $this->_columnName[$col]['Description'];
+			}
+			else
+			{
+				return "No description available";
+			}
+		}
+		else
+		{
+			return 0;
+		}
+	}
+
+	// FUNCTION: string name(string $name)
+	// Converts a friendly name to a MySQL column name
+	function column($name)
+	{
+		while ($column = current($this->_columnName))
+		{
+			if($this->_columnName[key($this->_columnName)]['Name'] == $name)
+			{
+				return key($this->_columnName);
+			}
+			next($this->_columnName);
+		}
+
+		return 0;
+	}
+
+	// FUNCTION: string parse(array $arguments)
+	// Converts an array of arguments into a MySQL query string
+	// See documentation for search() under the API controller for more details
+	function parse($arguments)
+	{
+		// Initialise our string
+		$q = "SELECT ";
+
+		// Cycle through the fields, converting friendly names to MySQL column names
+		if($arguments['fields'] != "") {
+			$field = "";
+			$fields = explode(",", $arguments['fields']);
+			foreach ($fields as $f) {
+				if($field != "") {
+					$field .= ",";
+				}
+				// Add field to the query, with '++' placeholders for later translation
+				$field .= "++$f++";
+			}
+			// Handle any DISTINCT arguments
+			$field = str_replace("++distinct(", "DISTINCT(++", $field);
+			$field = str_replace(")++", "++)", $field);
+			// Add the MySQL column name to the query
+			$q .= $field." ";
+		} else {
+			// If no fields are specified, display all fields
+			$q .= "* ";
+		}
+
+		// Append the table we're pulling data from
+		$q .= "FROM ".$this->config->item('table_name');
+
+		// Parse the 'query' string, which is converted into a standard MySQL 'WHERE'
+		// clause.
+		// $s and $r can be refactored into single array definitions, but during
+		// development it's easier to list them in this way for quick reference.
+		if($arguments['query'] != "")
+		{
+			$q .= " WHERE ";
+			$s = null;
+			$r = null;
+			// (and), becomes ' AND '
+			$s[0]   = '/(and)/';
+			// (or), becomes ' OR '
+			$s[1]   = '/(or)/';
+			// <, >, [ and ] all translated from their urlencoded forms
+			$s[2]   = '/%3C/';
+			$s[3]   = '/%3E/';
+			$s[4]   = '/%5B/';
+			$s[5]   = '/%5D/';
+			// FieldName=, which becomes '++FieldName++ = '
+			$s[6]   = '/([a-zA-Z0-9\-\_\*\(\)\=\~]+)=/';
+			// =Value, which becomes '= 'Value''
+			$s[7]   = '/=([a-zA-Z0-9\-\_\*\(\)\=\~]+)/';
+			// now(), which becomes 'UNIX_TIMESTAMP(NOW())'
+			$s[8]   = '/now()/';
+			// (, and ), which are translated to their non-HTML entity forms,
+			// and with added padding
+			$s[9]   = '/(/';
+			$s[10]  = '/)/';
+			// FieldName~, becomes '++FieldName++ LIKE~'
+			$s[11]  = '/([a-zA-Z0-9\-\_\*\(\)\=\~]+)~/';
+			// ~Value, becomes ' 'Value''
+			$s[12]  = '/~([a-zA-Z0-9\-\_\*\(\)\=\~]+)/';
+			// *, which becomes '%'
+			$s[13]  = '/\*/';
+	
+			$r[0]   = ' AND ';
+			$r[1]   = ' OR ';
+			$r[2]   = ' < ';
+			$r[3]   = ' > ';
+			$r[4]   = '[';
+			$r[5]   = ']';
+			$r[6]   = '++$1++ =';
+			$r[7]   = '= \'$1\'';
+			$r[8]   = 'UNIX_TIMESTAMP(NOW())';
+			$r[9]   = '( ';
+			$r[10]  = ' )';
+			$r[11]  = '++$1++ LIKE~';
+			$r[12]  = ' \'$1\'';
+			$r[13]  = '%';
+
+			// Bulk replace everything
+			$q .= preg_replace($s, $r, $arguments['query']);
+		}
+
+		// Parse any order arguments
+		if($arguments['order'] != "")
+		{
+			$q .= " ORDER BY ";
+
+			$s = null;
+			$r = null;
+			$s[0]   = '/(/';
+			$s[1]	= '/)/';
+			$s[2]	= '/([a-zA-Z0-9\-\_]+)([,\(]{1}|$)/';
+		    $s[3]   = '/\(asc\)/';
+	    	$s[4]   = '/\(desc\)/';
+			$s[5]	= '/,$/';
+
+			$r[0]	= '(';
+			$r[1]	= ')';
+			$r[2]	= '++$1++ $2';
+		    $r[3]   = ' ASC ';
+		    $r[4]   = ' DESC ';
+			$r[5]	= '';
+
+			$q .= preg_replace($s, $r, $arguments['order']);
+
+		}
+
+		// Do search/replace on field names, to convert from friendly names
+		// to MySQL column names
+		while (list($key, $val) = each($this->_columnName)) {
+			$q = str_replace("++".$val['Name']."++", $key, $q);
+		}
+
+		// Parse any limit arguments
+		if($arguments['limit'] != "")
+		{
+			// Add the limit arguments, removing any characters other than numbers and commas
+			$q .= " LIMIT " . preg_replace(array("/[^0-9\,]/","/,$/"), "", $arguments['limit']);
+		}
+		else
+		{
+			// If no limit argument is given, default to the first 20 results
+			$q .= " LIMIT 0,20";
+		}
+
+		return $q;
+	}
+
+	// ARRAY: $_columnName
+	// An array matching MySQL column names to friendly names, descriptions and types
+	private $_columnName = array(
+		'COL_PRIMARY_KEY'				=> array('Name' => 'ID', 'Description' => 'Unique QSO ID', 'Type' => 'I'),
+		'COL_ADDRESS'					=> array('Name' => 'Address', 'Description' => 'Operator\'s address', 'Type' => 'S'),
+		'COL_AGE'						=> array('Name' => 'Age', 'Description' => 'Operator\'s age', 'Type' => 'I'),
+		'COL_A_INDEX'					=> array('Name' => 'AIndex', 'Description' => 'Solar A Index', 'Type' => 'I'),
+		'COL_ANT_AZ'					=> array('Name' => 'AntennaAzimuth', 'Description' => 'Antenna azimuth', 'Type' => 'I'),
+		'COL_ANT_EL'					=> array('Name' => 'AntennaElevation', 'Description' => 'Antenna elevation', 'Type' => 'I'),
+		'COL_ANT_PATH'					=> array('Name' => 'AntennaPath', 'Description' => 'Antenna path', 'Type' => ''),
+		'COL_ARRL_SECT'					=> array('Name' => 'ARRLSection', 'Description' => 'ARRL Section', 'Type' => ''),
+		'COL_BAND'						=> array('Name' => 'Band', 'Description' => 'Band', 'Type' => ''),
+		'COL_BAND_RX'					=> array('Name' => 'BandRX', 'Description' => '', 'Type' => ''),
+		'COL_BIOGRAPHY'					=> array('Name' => 'Biography', 'Description' => '', 'Type' => ''),
+		'COL_CALL'						=> array('Name' => 'Call', 'Description' => '', 'Type' => ''),
+		'COL_CHECK'						=> array('Name' => 'UNK_CHECK', 'Description' => '', 'Type' => ''),
+		'COL_CLASS'						=> array('Name' => 'Class', 'Description' => '', 'Type' => ''),
+		'COL_CNTY'						=> array('Name' => 'County', 'Description' => '', 'Type' => ''),
+		'COL_COMMENT'					=> array('Name' => 'Comment', 'Description' => '', 'Type' => ''),
+		'COL_CONT'						=> array('Name' => 'Continent', 'Description' => '', 'Type' => ''),
+		'COL_CONTACTED_OP'				=> array('Name' => 'UNK_CONTACTED_OP', 'Description' => '', 'Type' => ''),
+		'COL_CONTEST_ID'				=> array('Name' => 'ContestID', 'Description' => '', 'Type' => ''),
+		'COL_COUNTRY'					=> array('Name' => 'Country', 'Description' => '', 'Type' => ''),
+		'COL_CQZ'						=> array('Name' => 'CQZone', 'Description' => '', 'Type' => ''),
+		'COL_DISTANCE'					=> array('Name' => 'Distance', 'Description' => '', 'Type' => ''),
+		'COL_DXCC'						=> array('Name' => 'DXCC', 'Description' => '', 'Type' => ''),
+		'COL_EMAIL'						=> array('Name' => 'EMail', 'Description' => '', 'Type' => ''),
+		'COL_EQ_CALL'					=> array('Name' => 'UNK_EQ_CALL', 'Description' => '', 'Type' => ''),
+		'COL_EQSL_QSLRDATE'				=> array('Name' => 'EQSLRecievedDate', 'Description' => '', 'Type' => ''),
+		'COL_EQSL_QSLSDATE'				=> array('Name' => 'EQSLSentDate', 'Description' => '', 'Type' => ''),
+		'COL_EQSL_QSL_RCVD'				=> array('Name' => 'EQSLRecieved', 'Description' => '', 'Type' => ''),
+		'COL_EQSL_QSL_SENT'				=> array('Name' => 'EQSLSent', 'Description' => '', 'Type' => ''),
+		'COL_EQSL_STATUS'				=> array('Name' => 'EQSLStatus', 'Description' => '', 'Type' => ''),
+		'COL_FORCE_INIT'				=> array('Name' => 'UNK_FORCE_INIT', 'Description' => '', 'Type' => ''),
+		'COL_FREQ'						=> array('Name' => 'Frequency', 'Description' => '', 'Type' => ''),
+		'COL_FREQ_RX'					=> array('Name' => 'FrequencyRX', 'Description' => '', 'Type' => ''),
+		'COL_GRIDSQUARE'				=> array('Name' => 'Locator', 'Description' => '', 'Type' => ''),
+		'COL_HEADING'					=> array('Name' => 'Heading', 'Description' => '', 'Type' => ''),
+		'COL_IOTA'						=> array('Name' => 'IOTA', 'Description' => '', 'Type' => ''),
+		'COL_ITUZ'						=> array('Name' => 'ITUZone', 'Description' => '', 'Type' => ''),
+		'COL_K_INDEX'					=> array('Name' => 'KIndex', 'Description' => '', 'Type' => ''),
+		'COL_LAT'						=> array('Name' => 'Latitude', 'Description' => '', 'Type' => ''),
+		'COL_LON'						=> array('Name' => 'Longitude', 'Description' => '', 'Type' => ''),
+		'COL_LOTW_QSLRDATE'				=> array('Name' => 'LOTWRecievedDate', 'Description' => '', 'Type' => ''),
+		'COL_LOTW_QSLSDATE'				=> array('Name' => 'LOTWSentDate', 'Description' => '', 'Type' => ''),
+		'COL_LOTW_QSL_RCVD'				=> array('Name' => 'LOTWRecieved', 'Description' => '', 'Type' => ''),
+		'COL_LOTW_QSL_SENT'				=> array('Name' => 'LOTWSent', 'Description' => '', 'Type' => ''),
+		'COL_LOTW_STATUS'				=> array('Name' => 'LOTWStatus', 'Description' => '', 'Type' => ''),
+		'COL_MAX_BURSTS'				=> array('Name' => 'MaxBursts', 'Description' => '', 'Type' => ''),
+		'COL_MODE'						=> array('Name' => 'Mode', 'Description' => '', 'Type' => ''),
+		'COL_MS_SHOWER'					=> array('Name' => 'MSShower', 'Description' => '', 'Type' => ''),
+		'COL_MY_CITY'					=> array('Name' => 'MyCity', 'Description' => '', 'Type' => ''),
+		'COL_MY_CNTY'					=> array('Name' => 'MyCounty', 'Description' => '', 'Type' => ''),
+		'COL_MY_COUNTRY'				=> array('Name' => 'MyCountry', 'Description' => '', 'Type' => ''),
+		'COL_MY_CQ_ZONE'				=> array('Name' => 'MyCQZone', 'Description' => '', 'Type' => ''),
+		'COL_MY_GRIDSQUARE'				=> array('Name' => 'MyLocator', 'Description' => '', 'Type' => ''),
+		'COL_MY_IOTA'					=> array('Name' => 'MyIOTA', 'Description' => '', 'Type' => ''),
+		'COL_MY_ITU_ZONE'				=> array('Name' => 'MyITUZone', 'Description' => '', 'Type' => ''),
+		'COL_MY_LAT'					=> array('Name' => 'MyLatitude', 'Description' => '', 'Type' => ''),
+		'COL_MY_LON'					=> array('Name' => 'MyLongitude', 'Description' => '', 'Type' => ''),
+		'COL_MY_NAME'					=> array('Name' => 'MyName', 'Description' => '', 'Type' => ''),
+		'COL_MY_POSTAL_CODE'			=> array('Name' => 'MyPostalCode', 'Description' => '', 'Type' => ''),
+		'COL_MY_RIG'					=> array('Name' => 'MyRig', 'Description' => '', 'Type' => ''),
+		'COL_MY_SIG'					=> array('Name' => 'MySig', 'Description' => '', 'Type' => ''),
+		'COL_MY_SIG_INFO'				=> array('Name' => 'MySigInfo', 'Description' => '', 'Type' => ''),
+		'COL_MY_STATE'					=> array('Name' => 'MyState', 'Description' => '', 'Type' => ''),
+		'COL_MY_STREET'					=> array('Name' => 'MyStreet', 'Description' => '', 'Type' => ''),
+		'COL_NAME'						=> array('Name' => 'Name', 'Description' => '', 'Type' => ''),
+		'COL_NOTES'						=> array('Name' => 'Notes', 'Description' => '', 'Type' => ''),
+		'COL_NR_BURSTS'					=> array('Name' => 'NumBursts', 'Description' => '', 'Type' => ''),
+		'COL_NR_PINGS'					=> array('Name' => 'NumPings', 'Description' => '', 'Type' => ''),
+		'COL_OPERATOR'					=> array('Name' => 'Operator', 'Description' => '', 'Type' => ''),
+		'COL_OWNER_CALLSIGN'			=> array('Name' => 'OwnerCallsign', 'Description' => '', 'Type' => ''),
+		'COL_PFX'						=> array('Name' => 'Prefix', 'Description' => '', 'Type' => ''),
+		'COL_PRECEDENCE'				=> array('Name' => 'Precedence', 'Description' => '', 'Type' => ''),
+		'COL_PROP_MODE'					=> array('Name' => 'PropMode', 'Description' => '', 'Type' => ''),
+		'COL_PUBLIC_KEY'				=> array('Name' => 'PublicKey', 'Description' => '', 'Type' => ''),
+		'COL_QSLMSG'					=> array('Name' => 'QSLMessage', 'Description' => '', 'Type' => ''),
+		'COL_QSLRDATE'					=> array('Name' => 'QSLRecievedDate', 'Description' => '', 'Type' => ''),
+		'COL_QSLSDATE'					=> array('Name' => 'QSLSentDate', 'Description' => '', 'Type' => ''),
+		'COL_QSL_RCVD'					=> array('Name' => 'QSLRecieved', 'Description' => '', 'Type' => ''),
+		'COL_QSL_RCVD_VIA'				=> array('Name' => 'QSLRecievedVia', 'Description' => '', 'Type' => ''),
+		'COL_QSL_SENT'					=> array('Name' => 'QSLSent', 'Description' => '', 'Type' => ''),
+		'COL_QSL_SENT_VIA'				=> array('Name' => 'QSLSentVia', 'Description' => '', 'Type' => ''),
+		'COL_QSL_VIA'					=> array('Name' => 'QSLVia', 'Description' => '', 'Type' => ''),
+		'COL_QSO_COMPLETE'				=> array('Name' => 'QSOComplete', 'Description' => '', 'Type' => ''),
+		'COL_QSO_RANDOM'				=> array('Name' => 'QSORandom', 'Description' => '', 'Type' => ''),
+		'COL_QTH'						=> array('Name' => 'QTH', 'Description' => '', 'Type' => ''),
+		'COL_RIG'						=> array('Name' => 'Rig', 'Description' => '', 'Type' => ''),
+		'COL_RST_RCVD'					=> array('Name' => 'ReportRecieved', 'Description' => '', 'Type' => ''),
+		'COL_RST_SENT'					=> array('Name' => 'ReportSent', 'Description' => '', 'Type' => ''),
+		'COL_RX_PWR'					=> array('Name' => 'RXPower', 'Description' => '', 'Type' => ''),
+		'COL_SAT_MODE'					=> array('Name' => 'SatMode', 'Description' => '', 'Type' => ''),
+		'COL_SAT_NAME'					=> array('Name' => 'SatName', 'Description' => '', 'Type' => ''),
+		'COL_SFI'						=> array('Name' => 'SFI', 'Description' => '', 'Type' => ''),
+		'COL_SIG'						=> array('Name' => 'Sig', 'Description' => '', 'Type' => ''),
+		'COL_SIG_INFO'					=> array('Name' => 'SigInfo', 'Description' => '', 'Type' => ''),
+		'COL_SRX'						=> array('Name' => 'UNK_SRX', 'Description' => '', 'Type' => ''),
+		'COL_STX'						=> array('Name' => 'UNK_STX', 'Description' => '', 'Type' => ''),
+		'COL_SRX_STRING'				=> array('Name' => 'UNK_SRX_STRING', 'Description' => '', 'Type' => ''),
+		'COL_STX_STRING'				=> array('Name' => 'UNK_STX_STRING', 'Description' => '', 'Type' => ''),
+		'COL_STATE'						=> array('Name' => 'State', 'Description' => '', 'Type' => ''),
+		'COL_STATION_CALLSIGN'			=> array('Name' => 'StationCall', 'Description' => '', 'Type' => ''),
+		'COL_SWL'						=> array('Name' => 'SWL', 'Description' => '', 'Type' => ''),
+		'COL_TEN_TEN'					=> array('Name' => 'TenTen', 'Description' => '', 'Type' => ''),
+		'COL_TIME_OFF'					=> array('Name' => 'TimeOff', 'Description' => '', 'Type' => ''),
+		'COL_TIME_ON'					=> array('Name' => 'TimeOn', 'Description' => '', 'Type' => ''),
+		'COL_TX_PWR'					=> array('Name' => 'TXPower', 'Description' => '', 'Type' => ''),
+		'COL_WEB'						=> array('Name' => 'Website', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_0'			=> array('Name' => 'UNK_USER_DEFINED_0', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_1'			=> array('Name' => 'UNK_USER_DEFINED_1', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_2'			=> array('Name' => 'UNK_USER_DEFINED_2', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_3'			=> array('Name' => 'UNK_USER_DEFINED_3', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_4'			=> array('Name' => 'UNK_USER_DEFINED_4', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_5'			=> array('Name' => 'UNK_USER_DEFINED_5', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_6'			=> array('Name' => 'UNK_USER_DEFINED_6', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_7'			=> array('Name' => 'UNK_USER_DEFINED_7', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_8'			=> array('Name' => 'UNK_USER_DEFINED_8', 'Description' => '', 'Type' => ''),
+		'COL_USER_DEFINED_9'			=> array('Name' => 'UNK_USER_DEFINED_9', 'Description' => '', 'Type' => ''),
+		'COL_CREDIT_GRANTED'			=> array('Name' => 'UNK_CREDIT_GRANTED', 'Description' => '', 'Type' => ''),
+		'COL_CREDIT_SUBMITTED'			=> array('Name' => 'UNK_CREDIT_SUBMITTED', 'Description' => '', 'Type' => ''),
+	);
+
+}
+
+?>
diff --git a/application/models/auth_model.php b/application/models/auth_model.php
new file mode 100644
index 00000000..3d46c02f
--- /dev/null
+++ b/application/models/auth_model.php
@@ -0,0 +1,95 @@
+_hash("password");
+		echo "Password hashed is '".$hash."\n";
+		echo "Does 'password' match '$hash'? result is ".$this->_auth("password", $hash)."\n";
+
+	}
+
+	// Retrieve a user
+	function get($username) {
+		$this->db->where('user_name', $username);
+		$r = $this->db->get($this->config->item('auth_table'));
+		if($r->num_rows == 1) {
+			return $r->result();
+		} else {
+			return 0;
+		}
+	}
+
+	function exists($username) {
+		if($this->get($username)->num_rows == 0) {
+			return 0;
+		} else {
+			return 1;
+		}
+	}
+
+	function add($username, $password, $email, $type) {
+		if(!$this->exists($username)) {
+			$data = array(
+				'user_name' => $username,
+				'user_password' => $this->_hash($password),
+				'user_email' => $email,
+				'user_type' => $type
+			);
+
+			$this->db->insert($this->config->item('auth_table'));
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+
+	function authenticate($username, $password) {
+		$u = $this->get($username);
+		if($this->_hash($password, $u['user_password'])) {
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+
+	function set($username, $data) {
+		$this->db->where('user_name', $username);
+		$this->db->update($this->config->item('auth_table', $data));
+		return 1;
+	}
+
+	private function _auth($password, $hash) {
+		$h = new PasswordHash(8, FALSE);
+		if($h->CheckPassword($password, $hash)) {
+			return 1;
+		} else {
+			return 0;
+		}
+	}
+
+	private function _hash($password) {
+		$h = new PasswordHash(8, FALSE);
+		$hash = $h->HashPassword($password);
+		unset($h);
+
+		if(strlen($hash) < 20) {
+			return 0;
+		} else {
+			return $hash;
+		}
+	}
+		
+}
+
+?>
diff --git a/application/third_party/PasswordHash.php b/application/third_party/PasswordHash.php
new file mode 100644
index 00000000..12958c7f
--- /dev/null
+++ b/application/third_party/PasswordHash.php
@@ -0,0 +1,253 @@
+ in 2004-2006 and placed in
+# the public domain.  Revised in subsequent years, still public domain.
+#
+# There's absolutely no warranty.
+#
+# The homepage URL for this framework is:
+#
+#	http://www.openwall.com/phpass/
+#
+# Please be sure to update the Version line if you edit this file in any way.
+# It is suggested that you leave the main version number intact, but indicate
+# your project name (after the slash) and add your own revision information.
+#
+# Please do not change the "private" password hashing method implemented in
+# here, thereby making your hashes incompatible.  However, if you must, please
+# change the hash type identifier (the "$P$") to something different.
+#
+# Obviously, since this code is in the public domain, the above are not
+# requirements (there can be none), but merely suggestions.
+#
+class PasswordHash {
+	var $itoa64;
+	var $iteration_count_log2;
+	var $portable_hashes;
+	var $random_state;
+
+	function PasswordHash($iteration_count_log2, $portable_hashes)
+	{
+		$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+
+		if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
+			$iteration_count_log2 = 8;
+		$this->iteration_count_log2 = $iteration_count_log2;
+
+		$this->portable_hashes = $portable_hashes;
+
+		$this->random_state = microtime();
+		if (function_exists('getmypid'))
+			$this->random_state .= getmypid();
+	}
+
+	function get_random_bytes($count)
+	{
+		$output = '';
+		if (is_readable('/dev/urandom') &&
+		    ($fh = @fopen('/dev/urandom', 'rb'))) {
+			$output = fread($fh, $count);
+			fclose($fh);
+		}
+
+		if (strlen($output) < $count) {
+			$output = '';
+			for ($i = 0; $i < $count; $i += 16) {
+				$this->random_state =
+				    md5(microtime() . $this->random_state);
+				$output .=
+				    pack('H*', md5($this->random_state));
+			}
+			$output = substr($output, 0, $count);
+		}
+
+		return $output;
+	}
+
+	function encode64($input, $count)
+	{
+		$output = '';
+		$i = 0;
+		do {
+			$value = ord($input[$i++]);
+			$output .= $this->itoa64[$value & 0x3f];
+			if ($i < $count)
+				$value |= ord($input[$i]) << 8;
+			$output .= $this->itoa64[($value >> 6) & 0x3f];
+			if ($i++ >= $count)
+				break;
+			if ($i < $count)
+				$value |= ord($input[$i]) << 16;
+			$output .= $this->itoa64[($value >> 12) & 0x3f];
+			if ($i++ >= $count)
+				break;
+			$output .= $this->itoa64[($value >> 18) & 0x3f];
+		} while ($i < $count);
+
+		return $output;
+	}
+
+	function gensalt_private($input)
+	{
+		$output = '$P$';
+		$output .= $this->itoa64[min($this->iteration_count_log2 +
+			((PHP_VERSION >= '5') ? 5 : 3), 30)];
+		$output .= $this->encode64($input, 6);
+
+		return $output;
+	}
+
+	function crypt_private($password, $setting)
+	{
+		$output = '*0';
+		if (substr($setting, 0, 2) == $output)
+			$output = '*1';
+
+		$id = substr($setting, 0, 3);
+		# We use "$P$", phpBB3 uses "$H$" for the same thing
+		if ($id != '$P$' && $id != '$H$')
+			return $output;
+
+		$count_log2 = strpos($this->itoa64, $setting[3]);
+		if ($count_log2 < 7 || $count_log2 > 30)
+			return $output;
+
+		$count = 1 << $count_log2;
+
+		$salt = substr($setting, 4, 8);
+		if (strlen($salt) != 8)
+			return $output;
+
+		# We're kind of forced to use MD5 here since it's the only
+		# cryptographic primitive available in all versions of PHP
+		# currently in use.  To implement our own low-level crypto
+		# in PHP would result in much worse performance and
+		# consequently in lower iteration counts and hashes that are
+		# quicker to crack (by non-PHP code).
+		if (PHP_VERSION >= '5') {
+			$hash = md5($salt . $password, TRUE);
+			do {
+				$hash = md5($hash . $password, TRUE);
+			} while (--$count);
+		} else {
+			$hash = pack('H*', md5($salt . $password));
+			do {
+				$hash = pack('H*', md5($hash . $password));
+			} while (--$count);
+		}
+
+		$output = substr($setting, 0, 12);
+		$output .= $this->encode64($hash, 16);
+
+		return $output;
+	}
+
+	function gensalt_extended($input)
+	{
+		$count_log2 = min($this->iteration_count_log2 + 8, 24);
+		# This should be odd to not reveal weak DES keys, and the
+		# maximum valid value is (2**24 - 1) which is odd anyway.
+		$count = (1 << $count_log2) - 1;
+
+		$output = '_';
+		$output .= $this->itoa64[$count & 0x3f];
+		$output .= $this->itoa64[($count >> 6) & 0x3f];
+		$output .= $this->itoa64[($count >> 12) & 0x3f];
+		$output .= $this->itoa64[($count >> 18) & 0x3f];
+
+		$output .= $this->encode64($input, 3);
+
+		return $output;
+	}
+
+	function gensalt_blowfish($input)
+	{
+		# This one needs to use a different order of characters and a
+		# different encoding scheme from the one in encode64() above.
+		# We care because the last character in our encoded string will
+		# only represent 2 bits.  While two known implementations of
+		# bcrypt will happily accept and correct a salt string which
+		# has the 4 unused bits set to non-zero, we do not want to take
+		# chances and we also do not want to waste an additional byte
+		# of entropy.
+		$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+		$output = '$2a$';
+		$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
+		$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
+		$output .= '$';
+
+		$i = 0;
+		do {
+			$c1 = ord($input[$i++]);
+			$output .= $itoa64[$c1 >> 2];
+			$c1 = ($c1 & 0x03) << 4;
+			if ($i >= 16) {
+				$output .= $itoa64[$c1];
+				break;
+			}
+
+			$c2 = ord($input[$i++]);
+			$c1 |= $c2 >> 4;
+			$output .= $itoa64[$c1];
+			$c1 = ($c2 & 0x0f) << 2;
+
+			$c2 = ord($input[$i++]);
+			$c1 |= $c2 >> 6;
+			$output .= $itoa64[$c1];
+			$output .= $itoa64[$c2 & 0x3f];
+		} while (1);
+
+		return $output;
+	}
+
+	function HashPassword($password)
+	{
+		$random = '';
+
+		if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
+			$random = $this->get_random_bytes(16);
+			$hash =
+			    crypt($password, $this->gensalt_blowfish($random));
+			if (strlen($hash) == 60)
+				return $hash;
+		}
+
+		if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
+			if (strlen($random) < 3)
+				$random = $this->get_random_bytes(3);
+			$hash =
+			    crypt($password, $this->gensalt_extended($random));
+			if (strlen($hash) == 20)
+				return $hash;
+		}
+
+		if (strlen($random) < 6)
+			$random = $this->get_random_bytes(6);
+		$hash =
+		    $this->crypt_private($password,
+		    $this->gensalt_private($random));
+		if (strlen($hash) == 34)
+			return $hash;
+
+		# Returning '*' on error is safe here, but would _not_ be safe
+		# in a crypt(3)-like function used _both_ for generating new
+		# hashes and for validating passwords against existing hashes.
+		return '*';
+	}
+
+	function CheckPassword($password, $stored_hash)
+	{
+		$hash = $this->crypt_private($password, $stored_hash);
+		if ($hash[0] == '*')
+			$hash = crypt($password, $stored_hash);
+
+		return $hash == $stored_hash;
+	}
+}
+
+?>
diff --git a/application/views/api/index.php b/application/views/api/index.php
new file mode 100644
index 00000000..3b074cfb
--- /dev/null
+++ b/application/views/api/index.php
@@ -0,0 +1,117 @@
+createProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"/css/api.xsl\"");
+$xmlDoc->appendChild($xsl);
+
+// Get the method called, and build the root node
+$call = $data['queryInfo']['call'];
+$rootNode = $xmlDoc->createElement("HRDWebLogbook-API");
+$parentNode = $xmlDoc->appendChild($rootNode);
+
+// Get the results output
+$output = $data[$call."_Result"];
+
+// Add the queryInfo node
+$node = $xmlDoc->createElement("queryInfo");
+$queryElement = $parentNode->appendChild($node);
+$queryElement->setAttribute("timeStamp", date("r", time()));
+$queryElement->setAttribute("calledMethod", $data['queryInfo']['call']);
+//$queryElement->setAttribute("queryArgs", $queryArgsString);
+$queryElement->setAttribute("resultsCount", count($data['queryInfo']['numResults']));
+if(ENVIRONMENT == "development") {
+	$debugInfo = $xmlDoc->createElement("debugInfo");
+	$debugElement = $queryElement->appendChild($debugInfo);
+	$debugElement->setAttribute("dbQuery", $data['queryInfo']['dbQuery']);
+	$debugElement->setAttribute("clientVersion", $_SERVER['HTTP_USER_AGENT']);
+	$debugElement->setAttribute("requestURI", $_SERVER['REQUEST_URI']);
+	$debugElement->setAttribute("benchMark", $this->benchmark->marker['total_execution_time_start'].", ".$this->benchmark->marker['loading_time:_base_classes_start'].", ".$this->benchmark->marker['loading_time:_base_classes_end'].", ".$this->benchmark->marker['controller_execution_time_( api / search )_start']);
+}
+$queryElement->setAttribute("executionTime", $data['queryInfo']['executionTime']);
+$queryElement->setAttribute("logbookURL", $this->config->item('base_url'));
+
+// Add the main results node
+$node = $xmlDoc->createElement("elements");
+$elementsNode = $parentNode->appendChild($node);
+
+// Cycle through the results and add to the results node
+if($output['results'])
+{
+	foreach($output['results'] as $e) {
+		$node = $xmlDoc->createElement("element");
+		$element = $elementsNode->appendChild($node);
+
+		foreach($e as $attr) {
+		#while($attr = current($e)) {
+		    if(is_array($attr))
+			{
+			  foreach($attr as $subattr)
+			  {
+				$node = $xmlDoc->createElement(key($e));
+				foreach($subattr as $subsubattr)
+				{
+				  $node->setAttribute(key($subattr), $subsubattr);
+				  next($subattr);
+				}
+				$element->appendChild($node);
+			  }
+			}
+			else
+			{
+			  $element->setAttribute(key($e), $attr);
+			}
+			next($e);
+		}
+	}
+}
+
+// Output formatted XML
+echo formatXmlString($xmlDoc->saveXML());
+
+// This function tidies up the outputted XML
+function formatXmlString($xml) {
+
+  // add marker linefeeds to aid the pretty-tokeniser (adds a linefeed between all tag-end boundaries)
+  $xml = preg_replace('/(>)(<)(\/*)/', "$1\n$2$3", $xml);
+
+  // now indent the tags
+  $token      = strtok($xml, "\n");
+  $result     = ''; // holds formatted version as it is built
+  $pad        = 0; // initial indent
+  $matches    = array(); // returns from preg_matches()
+
+  // scan each line and adjust indent based on opening/closing tags
+  while ($token !== false) :
+
+    // test for the various tag states
+
+    // 1. open and closing tags on same line - no change
+    if (preg_match('/.+<\/\w[^>]*>$/', $token, $matches)) :
+      $indent=0;
+    // 2. closing tag - outdent now
+    elseif (preg_match('/^<\/\w/', $token, $matches)) :
+      $pad--;
+    // 3. opening tag - don't pad this one, only subsequent tags
+    elseif (preg_match('/^<\w[^>]*[^\/]>.*$/', $token, $matches)) :
+      $indent=1;
+    // 4. no indentation needed
+    else :
+      $indent = 0;
+    endif;
+
+    // pad the line with the required number of leading spaces
+    $line    = str_pad($token, strlen($token)+$pad, ' ', STR_PAD_LEFT);
+    $result .= $line . "\n"; // add to the cumulative result, with linefeed
+    $token   = strtok("\n"); // get the next token
+    $pad    += $indent; // update the pad size for subsequent lines
+  endwhile;
+
+  return $result;
+}
+
+?>
diff --git a/application/views/auth/add.php b/application/views/auth/add.php
new file mode 100644
index 00000000..cde42eb4
--- /dev/null
+++ b/application/views/auth/add.php
@@ -0,0 +1,70 @@
+

Add Note

+
+ +
+ + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ + + + + + + + \ No newline at end of file diff --git a/application/views/auth/edit.php b/application/views/auth/edit.php new file mode 100644 index 00000000..2030a10a --- /dev/null +++ b/application/views/auth/edit.php @@ -0,0 +1,72 @@ +result() as $row) { ?> +

Edit Note - title; ?>

+
+ +
+ + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/application/views/auth/main.php b/application/views/auth/main.php new file mode 100644 index 00000000..85ec58ac --- /dev/null +++ b/application/views/auth/main.php @@ -0,0 +1,24 @@ +

Note

+
+ +num_rows() > 0) + { + echo "
    "; + foreach ($notes->result() as $row) + { + echo "
  • "; + echo "id."\">".$row->title.""; + echo "
  • "; + } + echo "
"; + } else { + echo "

You have no notes, why not create one!

"; + } + +?> + +

Create a Note

+ +
\ No newline at end of file diff --git a/application/views/auth/view.php b/application/views/auth/view.php new file mode 100644 index 00000000..eebd737f --- /dev/null +++ b/application/views/auth/view.php @@ -0,0 +1,8 @@ +result() as $row) { ?> +

Note - title; ?>

+
+note); ?> + +

Options: Edit Delete

+
+ \ No newline at end of file diff --git a/css/api.css b/css/api.css new file mode 100644 index 00000000..542e0549 --- /dev/null +++ b/css/api.css @@ -0,0 +1,52 @@ +body { + background: #eee; + font-family: Verdana, sans-serif; + font-size: 8px; +} +#results table { + border: 0px solid #000; + border-collapse: collapse; +} +#results th { + padding: 4px; + border: 1px solid #000; + background-color: #6AA57B; + font-size: 11px; +} +#results td { + padding: 4px; + border: 1px solid #000; + font-size: 11px; +} +#results tr.row0 { + background-color: #A3BDF5; +} +#results tr.row1 { + background-color: #9ADF9A; +} +img { + border: 0px; +} +#footer { + font-size: 8px; +} +#debug { + border: 1px dotted #fff; + background-color: #c00; + color: #fff; + font-size: 8px; +} +#debug td { + padding: 4px; + color: #fff; + font-size: 10px; +} +.blank { + background-color: transparent; +} +.sub { + background-color: #cfc; +} +.subattr { + background-color: #cfc; +} diff --git a/css/api.xsl b/css/api.xsl new file mode 100644 index 00000000..f81aa8b2 --- /dev/null +++ b/css/api.xsl @@ -0,0 +1,66 @@ + + + + + + + <xsl:value-of select="//queryInfo/@calledMethod"/> + + + +

Output of ''

+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ = +
+
+

+ +

+ + + + + + + + + + +
requestURI
dbQuery
clientVersion
+
+

+ +

+ + +
+