Commits

Dennis T Kaplan committed f929540

init

  • Participants

Comments (0)

Files changed (7)

+<?php
+
+class Citadel {
+
+	const EOL = "\n";
+
+	public static $status_code = array(
+			501 => 'COMMAND_NOT_FOUND', 
+			502 => 'COMMAND_NOT_SET', 
+			511 => 'RESOURCE_TIMEOUT', 
+			512 => 'RESOURCE_BAD_DATA',
+			553 => 'RESOURCE_NOT_OPEN',
+			561 => 'LABEL_KEY_MATCH', 
+		);
+
+	private $messanger = array();
+
+	private $socket_path = NULL;
+	private $socket_timeout = NULL;
+
+	private $stream = FALSE;
+
+	private $conn_status = array();
+
+
+	/**
+	 * Citadel Server Response Array
+	 **/
+	private $response = array(); 
+
+	public static $response_keys = array('cmd', 'status_code', 'first_line', 'lines');
+
+	public function __call($key, $args = array()) {
+		if( in_array($key, self::$response_keys)){
+			if( $this->response[$key] !== NULL ){
+				return $this->response[$key]; 			
+			}else{
+				$this->Messanger(502);				
+			}
+		}else{
+			$this->Messanger(501);
+		}
+	}
+
+	public function Socket($path = NULL) {
+		if($path !== NULL) {
+			$this->socket_path = $path;		
+		}
+		return $this->socket_path;
+	}
+
+	public function Timeout($seconds = NULL) {
+		if($seconds !== NULL) {
+			$this->socket_timeout = $seconds;
+		}
+		return $this->socket_timeout;
+	}
+
+	public function Open() {
+		$this->stream = fsockopen('unix://'.$this->socket(), 0, $errno, $errstr);
+		if($this->stream) {
+			if($this->socket_timeout !== NULL){
+				stream_set_timeout($this->stream , $this->socket_timeout, 0);	
+			}
+			$this->read();
+		}else{
+			$this->Messanger($errno, $errstr);
+		}
+	}
+
+	public function Close() { 
+		$status = fclose($this->stream);
+		if($status) {
+			$this->stream = FALSE;
+		}
+		return $status;
+	}
+
+	/**
+	 * @param developer ID int	(same as the server developer ID numbers in the INFO command – please obtain one if you are a new developer)
+	 * @param client ID int	(which does not have to be globally unique - only unique within the domain of the developer number)
+	 * @param client Version int	the Version of your Client
+	 * @param Client Name string	afree-form text string describing the client
+	 * @return bool
+	 **/
+	public function Identify($dev_id, $client_id, $client_version, $client_name) {
+		$this->Send('IDEN '.$dev_id.'|'.$client_id.'|'.$client_version.'|'.$client_name.'|'.gethostname());
+		if (self::status_code() != '200') {
+			$this->Messanger(self::status_code());
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	public function Login($user, $pass) {
+		$keys = array('username','access','times_called', 'posted', 'flags', 'uid', 'last');
+		$this->Send('USER '.$user);
+		if ($this->status_code() != '300') {
+			$this->Messanger(self::status_code());
+		}else{
+			$this->Send('PASS '.$pass);
+			if (self::status_code() != '200'){
+				$this->Messanger(self::status_code());
+			}else{
+				$this->conn_status['auth'] = TRUE;
+				return $this->Label($this->first_line_array() , $keys);
+			}
+		}
+		return array();
+	}
+
+	public function Logout() {
+		$this->Send('LOUT');
+		if (self::status_code() != '200') {
+			$this->Messanger(self::status_code());
+			return FALSE;
+		}
+		$this->conn_status['auth'] = FALSE;
+		return TRUE;
+	}
+
+	public function Password($pass) {
+		$this->Send('SETP '.$pass);
+		if (self::status_code() != '200') {
+			$this->Messanger(self::status_code());
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	public function Send($string) {
+		$this->write($string);
+		$this->read();
+	}
+
+	public function write($string = NULL) {
+		if( $this->stream === FALSE ) {
+			$this->Messanger(553);
+			return;
+		} 
+
+		foreach( self::$response_keys as $key ){
+			$this->response[$key] = NULL;	
+		}
+
+		$tmp = explode(" ", $string);
+		$this->response['cmd'] = $tmp[0];
+		fwrite($this->stream, $string.self::EOL);
+	}
+
+	private function read() {
+		if( $this->stream === FALSE ) {
+			$this->Messanger(553);
+			return;
+		}
+
+		$data = fread( $this->stream, 8192 );
+		/*
+			$data = '';
+			while( feof($this->stream) === FALSE ) {
+				$data .= fread( $this->stream, 8192 );
+				echo "Reading Stream\n";
+
+			}
+		*/
+		
+		if( empty($data) ){
+			$this->Messanger(512);
+			return;
+		}
+
+		if( $this->timedout() === FALSE ){
+			$data = explode(self::EOL, $data);
+			$this->response['status_code'] = substr($data[0], 0, 3);
+			$this->response['first_line']  = substr($data[0], 4, strlen($data[0]));
+			unset($data[0]);
+			foreach ($data as $line) {
+				if($line == '000') break;
+				if(empty($line)) continue;
+				$this->response['lines'][] = $line;
+			}
+		}
+	}
+
+	public function first_line_array() { 
+		return explode( '|', self::first_line() );
+	}
+
+	public static function Label(array $data, array $keys) {
+		$return = array();
+		if(count($data) !== count($keys)) {
+			$this->Messanger(561);
+		}else{
+			foreach ($keys as $id => $text) {
+				$return[$text] = $data[$id];
+			}			
+		}
+		return $return;
+	}
+
+	public function meta_data() { 
+		return stream_get_meta_data($this->stream); 
+	}
+
+	public function timedout() {
+		$meta = $this->meta_data();
+		if( $meta['timed_out'] ) {
+			$this->Messanger(511);			
+		}
+		return $meta['timed_out'];
+	}	
+
+	public function stream_check() {
+		if( $this->stream === FALSE ) {
+			$this->Messanger(553);
+			return FALSE;
+		}
+		return TRUE;
+	}
+
+	public function __construct(){
+		foreach( self::$response_keys as $key ){
+			$this->response[$key] = NULL;	
+		}
+		$this->conn_status['auth'] = FALSE;
+	}
+
+	public function Messanger( $code = NULL, $text = NULL ) {
+		if($code !== NULL){
+			if( is_string($code) ){
+				$this->messanger[] = array('Cmd' => self::cmd(), 'Code' => $code, 'Text' => $this->cit_status_code() );
+			}elseif( $text === NULL ){
+				$this->messanger[] = array('Cmd' => self::cmd(), 'Code' => $code, 'Text' => self::$status_code[$code]);
+			}else{
+				$this->messanger[] = array('Cmd' => self::cmd(), 'Code' => $code, 'Text' => $text);				
+			}
+			if(php_sapi_name() === 'cli'){
+				debug_print_backtrace();				
+			}
+		}
+		return $this->messanger;
+	}
+
+	public function Status(){
+		$m = $this->Messanger();
+		$r = $m[0]['Cmd'] . ' ';
+		$r .= $m[0]['Code'] . ' ';
+		$r .= $m[0]['Text'];
+		return $r;
+	}
+
+	public function cit_status_code() {
+		if(substr($this->status_code(), 1,2) == '00') {
+			return self::$status_code_prefix[substr(self::status_code(), 0, 1)];
+		} else {
+			return self::$status_code_prefix[substr(self::status_code(), 0, 1)].':'.self::$status_code_suffix[substr(self::status_code(), 1,2)];
+		}
+	}
+
+
+	public function __destruct() {
+		if($this->stream !== FALSE) {
+			if($this->conn_status['auth']) $this->Logout();
+			$this->Close();
+		}	
+	}
+
+
+    /**
+     * @var array $status_code_prefix
+     * @access public
+     * @link http://www.citadel.org/doku.php/documentation:appproto:statuscodes
+	 * @link http://www.citadel.org/doku.php/documentation:appproto:app_proto#resultcodes
+     **/
+    public static $status_code_prefix = array(
+        '1' => 'LISTING_FOLLOWS',   # The requested operation is progressing and is now delivering text.The client *must* now read lines of text until it receives thetermination sequence (?000? on a line by itself).
+        '2' => 'CIT_OK',            # The requested operation succeeded.
+        '3' => 'MORE_DATA',         # The requested operation succeeded so far', but another command is required to complete it.
+        '4' => 'SEND_LISTING',      # The requested operation is progressing and is now expecting text. The client *must* now transmit zero or more lines of text followed by the termination sequence (?000? on a line by itself).
+        '5' => 'ERROR',             # The requested operation failed. The second and third digits of the error code and/or the error message following it describes why.
+        '6' => 'BINARY_FOLLOWS',    # After this line please read n bytes. (n follows after a blank)
+        '7' => 'SEND_BINARY',       # You now may send us n bytes binary data. (n follows after a blank)
+        '8' => 'START_CHAT_MODE',   # Ok, we are in chat mode now. every line you send will be broadcasted.
+        '9' => 'ASYNC_MSG');        # There is a page waiting for you, please fetch it.
+
+    /**
+     * Extended description of status code
+     * @var array $status_code_suffix
+     * @access public
+     * @link http://www.citadel.org/doku.php/documentation:appproto:statuscodes
+	 * @link http://www.citadel.org/doku.php/documentation:appproto:app_proto#resultcodes
+     **/
+    public static $status_code_suffix = array(
+        '02' => 'ASYNC_GEXP',
+        '10' => 'INTERNAL_ERROR',
+        '11' => 'TOO_BIG',
+        '12' => 'ILLEGAL_VALUE',
+        '20' => 'NOT_LOGGED_IN',
+        '30' => 'CMD_NOT_SUPPORTED',
+        '31' => 'SERVER_SHUTTING_DOWN',
+        '40' => 'PASSWORD_REQUIRED',
+        '41' => 'ALREADY_LOGGED_IN',
+        '42' => 'USERNAME_REQUIRED',
+        '50' => 'HIGHER_ACCESS_REQUIRED',
+        '51' => 'MAX_SESSIONS_EXCEEDED',
+        '52' => 'RESOURCE_BUSY',
+        '53' => 'RESOURCE_NOT_OPEN',
+        '60' => 'NOT_HERE',
+        '61' => 'INVALID_FLOOR_OPERATION',
+        '70' => 'NO_SUCH_USER',
+        '71' => 'FILE_NOT_FOUND',
+        '72' => 'ROOM_NOT_FOUND',
+        '73' => 'NO_SUCH_SYSTEM',
+        '74' => 'ALREADY_EXISTS',
+        '75' => 'MESSAGE_NOT_FOUND');
+}
+?>
+html {
+  width: 100%; 
+  height: 100%;
+}
+
+body { 
+  background-color: #FFFFFF; 
+  color: #000000; 
+  font-family: Sans-serif;
+  margin:0;
+  width: 100%; 
+  height: 100%;
+}
+
+form{
+  padding: 10px;   
+}
+
+#FormLogout {
+  padding: 0px;  
+}
+
+#content {
+}
+
+#center {
+  margin: auto;  
+}
+
+#logo{
+  background-color: #000; 
+  background-image:url('citadel.png');
+  background-repeat: no-repeat; 
+  background-position: left top; 
+  color: #fff; 
+  padding-top: 75px; 
+}
+
+#messanger {
+    display: none;
+    position: absolute;
+    left: 100px;
+    top: 75px;
+    z-index: 1000;
+    padding: 2px; 
+    padding-left: 10px;
+    padding-right: 10px;
+}
+
+.alert {
+    background: #FF0000;
+    color: #FFFFFF;    
+}
+
+.info {
+    background: #12B7E4;
+    color: #000000;    
+}

File citadel.html

+<!DOCTYPE html>
+<html>
+<head>
+<title>Citadel</title>
+<meta name="generator" content="Citadel PHP">
+<meta http-equiv="content-language" content="en">
+<meta charset="UTF-8">
+<link rel="icon" href="http://www.riky.net/static/ico/favicon.ico" type="image/x-icon">
+<link href="citadel.css" rel="stylesheet"/>
+
+<script id="template_Login" type="text/template">
+    <form id="FormLogin" method="post"><label>Login</label>
+        <input id="name" type="text" name="user" required placeholder="Username">
+        <input id="pass" type="password" name="pass" required placeholder="Password">
+        <input type="button" value="Submit" id="PostLogin" onclick="userLogin()">
+    </form>
+</script>
+
+<script id="template_User" type="text/template">
+    <form id="FormLogout" method="post">
+        <input type="button" value="Logout" onclick="userLogout()">
+    </form>
+    <form id="FormPassword" method="post"><label>Change Password</label>
+        <input id="pass1" type="password" name="pass1" autocomplete="off" required placeholder="New Password">
+        <input id="pass2" type="password" name="pass2" autocomplete="off" required placeholder="Confirm">
+        <input type="button" value="Submit" onclick="userPassword()">
+    </form>
+</script>
+
+<script src="citadel.js"></script>
+
+
+</head>
+<body>
+    <div id="messanger"></div>
+	<div id="logo"></div>
+    <div id="content"></div>
+</body>
+</html>
+function setCookie(name,value,days) {
+    if (days) {
+            var date = new Date();
+            date.setTime(date.getTime()+(days*24*60*60*1000));
+            var expires = "; expires="+date.toGMTString();
+    }
+    else var expires = "";
+    document.cookie = name+"="+value+expires+"; path=/";
+}
+
+function getCookie(name) {
+    var nameEQ = name + "=";
+    var ca = document.cookie.split(';');
+    for(var i=0;i < ca.length;i++) {
+            var c = ca[i];
+            while (c.charAt(0)==' ') c = c.substring(1,c.length);
+            if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
+    }
+    return null;
+}
+
+function delCookie(name) {
+    setCookie(name,"",-1);
+}
+
+window.onload = function(){
+    checkAuth();
+    // setInterval(function(){checkAuth()}, 10000); 
+};
+
+window.onblur = function() {
+    userLogout();
+}
+
+function checkAuth(){
+    var c = getCookie('citadelAuth');
+    if(c != 1){
+        var content = document.getElementById("content");
+        content.innerHTML = document.getElementById("template_Login").innerHTML;
+    }
+}
+
+function userLogout(){
+    setCookie('citadelAuth',0,-1);
+    delCookie('PHPSESSID');
+    checkAuth();
+}
+
+function Ajax(params){
+        var xmlhttp = new XMLHttpRequest();
+        xmlhttp.onreadystatechange=function() {
+            if (xmlhttp.readyState==4 && xmlhttp.status==200) {
+                if(xmlhttp.responseText != ''){
+                    Messanger(xmlhttp.responseText);                    
+                }else{
+                    Messanger('',-1);                    
+                }
+            }
+        }
+        xmlhttp.open("POST", "citadel.php", true);
+        xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+        xmlhttp.send(params);    
+}
+
+
+function userLogin(){
+    var f = document.getElementById("FormLogin");
+    var p = document.getElementById("pass");
+
+    var data = formData(f);
+
+    if (data.user.length < 3) {
+        Messanger("Username is to short");
+    }else if (data.pass.length < 4) {
+        Messanger("Password is to short");
+    }else{
+        Messanger("Checking server ...",1);
+
+        var params = "a=Login&u=" + data.user + "&p=" + data.pass ;
+        Ajax(params);
+    }
+    p.value = "";
+
+    var content = document.getElementById("content");
+    content.innerHTML = document.getElementById("template_User").innerHTML;
+}
+
+function userPassword(){
+    var f = document.getElementById("FormPassword");
+    var p1 = document.getElementById("pass1");
+    var p2 = document.getElementById("pass2");
+    var data = formData(f);
+
+    if (data.pass1.length < 3) {
+        Messanger("Username is to short");
+    }else if (data.pass1 != data.pass2) {
+        Messanger("Passwords don't match");
+    }else{
+        Messanger("Checking server ...",1);
+
+        var params = "a=Password&p=" + data.pass1 ;
+        Ajax(params);
+    }
+    p1.value = "";
+    p2.value = "";
+}
+
+
+function clearText(thefield){
+    if (thefield.defaultValue==thefield.value)
+    thefield.value = ""
+}
+
+function Messanger(text, no){
+    var e = document.getElementById("messanger");
+    e.innerText = e.textContent = text;
+
+    if (no === -1) {
+        e.style.display = 'none';
+        return
+    }
+
+    if (no === 1) {
+        e.className = "info";
+    }else{
+        e.className = "alert";
+    }
+    e.style.display = 'block';
+    setTimeout(function(){Messanger('',-1)},text.length * 100 + 3000);
+}
+
+function formData(form){
+    var r = {};
+    var i = 0;
+    for (i = 0; i < form.length; ++i) {
+        if (form[i] !== undefined) {
+            if (form[i].name) {
+                field_type = form[i].type.toLowerCase();
+                switch (field_type) {
+                    case "text":
+                        r[form[i].name] = form[i].value;
+                        break;
+                    case "password":
+                        r[form[i].name] = form[i].value;
+                        break;
+                    case "textarea":
+                        r[form[i].name] = form[i].value;
+                        break;
+                    case "radio":
+                    case "checkbox":
+                        r[form[i].name] = form[i].checked;
+                        break;
+                    case "select-one":
+                        r[form[i].name] = form[i].value;
+                    case "select-multi":
+                        r[form[i].name] = form[i].selectedIndex;
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    }
+    return r;
+}
+<?php
+
+session_start();
+require_once('config.php');
+require_once('Citadel.php');
+
+$cit = new Citadel();
+$cit->Socket(CITADEL_SOCKET);
+$cit->Timeout(1);
+$cit->Open();
+//$cit->Identify(12, 3, 1, "PHP Password");
+
+if( !isset($_REQUEST["a"]) ){
+	echo 'No Action';	
+}elseif($_REQUEST["a"] == 'Login'){
+	$username=$_REQUEST["u"];
+	$password=$_REQUEST["p"];
+	$_SESSION = $cit->Login($username, $password);
+	if( !empty($_SESSION) ) {
+		$_SESSION['Authenticated'] = TRUE;
+		$_SESSION['password'] = $password;
+	    setcookie("citadelAuth", 1, time()+3600, '/');
+	} else {
+	    echo "Login: " . $cit->Status();
+	}	
+}elseif($_REQUEST["a"] == 'Password') {
+
+	$cit->Login($_SESSION['username'], $_SESSION['password']);
+
+	$password=$_REQUEST["p"];
+
+	if(strlen($password) < 3){
+		echo 'Password is too short ';
+	}elseif($cit->Password($password)){
+	    echo "Password Successfull Changed";
+	} else {
+	    echo "Password: " . $cit->Status();
+	}	
+}
+
+
+?>

File citadel.png

Added
New image
+<?php
+
+
+date_default_timezone_set ('America/Los_Angeles');
+define('CITADEL_SOCKET', '/var/run/citadel/citadel.socket');
+?>