diff --git a/application/classes/auth/ldap.php b/application/classes/auth/ldap.php
new file mode 100644
index 0000000..f6eced6
--- /dev/null
+++ b/application/classes/auth/ldap.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/block.php b/application/classes/block.php
new file mode 100644
index 0000000..bde7470
--- /dev/null
+++ b/application/classes/block.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/config.php b/application/classes/config.php
new file mode 100644
index 0000000..65daaae
--- /dev/null
+++ b/application/classes/config.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/controller/login.php b/application/classes/controller/login.php
new file mode 100644
index 0000000..f0c1b01
--- /dev/null
+++ b/application/classes/controller/login.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/controller/logout.php b/application/classes/controller/logout.php
new file mode 100644
index 0000000..b60b0ee
--- /dev/null
+++ b/application/classes/controller/logout.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/controller/media.php b/application/classes/controller/media.php
new file mode 100644
index 0000000..53bdf41
--- /dev/null
+++ b/application/classes/controller/media.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/controller/template.php b/application/classes/controller/template.php
new file mode 100644
index 0000000..1caabd9
--- /dev/null
+++ b/application/classes/controller/template.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/database/ldap.php b/application/classes/database/ldap.php
new file mode 100644
index 0000000..d9b3b1a
--- /dev/null
+++ b/application/classes/database/ldap.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/database/ldap/search.php b/application/classes/database/ldap/search.php
new file mode 100644
index 0000000..4a28484
--- /dev/null
+++ b/application/classes/database/ldap/search.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/database/ldap/search/builder/query.php b/application/classes/database/ldap/search/builder/query.php
new file mode 100644
index 0000000..6931b05
--- /dev/null
+++ b/application/classes/database/ldap/search/builder/query.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/htmlrender.php b/application/classes/htmlrender.php
new file mode 100644
index 0000000..ed9c6a0
--- /dev/null
+++ b/application/classes/htmlrender.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/meta.php b/application/classes/meta.php
new file mode 100644
index 0000000..8750207
--- /dev/null
+++ b/application/classes/meta.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/pla/auth/ldap.php b/application/classes/pla/auth/ldap.php
new file mode 100644
index 0000000..0b64e03
--- /dev/null
+++ b/application/classes/pla/auth/ldap.php
@@ -0,0 +1,79 @@
+_config['hash_key'])
+ return $str;
+ else
+ return parent::hash($str);
+ }
+
+ /**
+ * Logs a user in.
+ *
+ * @param string username
+ * @param string password
+ * @param boolean enable autologin (not supported)
+ * @return boolean
+ */
+ protected function _login($user, $password, $remember) {
+ if ( ! is_object($user)) {
+ $username = $user;
+
+ // Load the user
+ // @todo Get the server ID
+ $sid = 'default';
+
+ $user = Database_LDAP::instance($sid)->select_db('user')->connect();
+ $user->bind($username,$password);
+ }
+
+ // @todo Implement conditional logging based on memberships to groups or other criteria.
+ // @todo This check of user being logged in needs to be better
+ if (! $user->noconnect) {
+ /*
+ // @todo To implement
+ if ($remember === TRUE) {
+ // Token data
+ $data = array(
+ 'user_id'=>$user->id,
+ 'expires'=>time()+$this->_config['lifetime'],
+ 'user_agent'=>sha1(Request::$user_agent),
+ );
+
+ // Create a new autologin token
+ $token = ORM::factory('user_token')
+ ->values($data)
+ ->create();
+
+ // Set the autologin cookie
+ Cookie::set('authautologin', $token->token, $this->_config['lifetime']);
+ }
+ */
+
+ // Finish the login
+ $this->complete_login($user);
+
+ return TRUE;
+ }
+
+ // Login failed
+ return FALSE;
+ }
+}
+?>
diff --git a/application/classes/pla/block.php b/application/classes/pla/block.php
new file mode 100644
index 0000000..dc9e8d2
--- /dev/null
+++ b/application/classes/pla/block.php
@@ -0,0 +1,81 @@
+
|
';
+ protected static $_required_keys = array('body');
+
+ /**
+ * Add a block to be rendered
+ *
+ * @param array Block attributes
+ */
+ public static function add($block,$prepend=FALSE) {
+ parent::add($block);
+
+ // Detect any style sheets.
+ if (! empty($block['style']) && is_array($block['style']))
+ foreach ($block['style'] as $data=>$media)
+ Style::add(array(
+ 'type'=>'file',
+ 'data'=>$data,
+ 'media'=>$media,
+ ));
+ }
+
+ /**
+ * Return an instance of this class
+ *
+ * @return Block
+ */
+ public static function factory() {
+ return new Block;
+ }
+
+ /**
+ * Render this block
+ *
+ * @see HTMLRender::render()
+ */
+ protected function render() {
+ $output = '';
+ $styles = array();
+
+ $i = 0;
+ foreach (static::$_data as $value) {
+ if ($i++)
+ $output .= static::$_spacer;
+
+ $output .= '';
+
+ if (! empty($value['title']))
+ $output .= sprintf('%s |
',$value['title']);
+
+ if (! empty($value['subtitle']))
+ $output .= sprintf('%s |
',$value['subtitle']);
+
+ $output .= sprintf('%s |
',$value['body']);
+
+ if (! empty($value['footer']))
+ $output .= sprintf('',$value['footer']);
+
+ $output .= '
';
+ }
+
+ return $output;
+ }
+}
+?>
diff --git a/application/classes/pla/config.php b/application/classes/pla/config.php
new file mode 100644
index 0000000..2c0e5b4
--- /dev/null
+++ b/application/classes/pla/config.php
@@ -0,0 +1,105 @@
+'Production',
+ Kohana::STAGING=>'Staging',
+ Kohana::TESTING=>'Testing',
+ Kohana::DEVELOPMENT=>'Development',
+ );
+
+ return (! isset($modes[static::sitemode()])) ? 'Unknown' : $modes[static::sitemode()];
+ }
+
+ public static function submode() {
+ $submode = Kohana::Config('config.debug.submode');
+
+ return (isset($submode[Request::$client_ip])) ? $submode[Request::$client_ip] : FALSE;
+ }
+
+ public static function sitename() {
+ return Kohana::Config('config.site.name');
+ }
+
+ // Called in Invoice/Emailing to embed the file.
+ public static function logo_file() {
+ list ($path,$suffix) = explode('.',static::$logo);
+ return Kohana::find_file(sprintf('media/%s',Config::siteid()),$path,$suffix);
+ }
+
+ public static function logo_uri() {
+ list ($path,$suffix) = explode('.',static::$logo);
+ return URL::site(Route::get('default/media')->uri(array('file'=>$path.'.'.$suffix),array('alt'=>static::sitename())),'http');
+ }
+
+ public static function logo() {
+ return HTML::image(static::logo_uri(),array('class'=>'headlogo','alt'=>_('Logo')));
+ }
+
+ public static function login_uri() {
+ return ($ao = Auth::instance()->get_user()) ? $ao->name() : HTML::anchor('login',_('Login'));
+ }
+
+ public static function copywrite() {
+ return '(c) phpLDAPadmin Development Team';
+ }
+
+ /**
+ * Return our caching mechanism
+ */
+ public static function cachetype() {
+ return is_null(Kohana::config('config.cache_type')) ? 'file' : Kohana::config('config.cache_type');
+ }
+}
+?>
diff --git a/application/classes/pla/controller/login.php b/application/classes/pla/controller/login.php
new file mode 100644
index 0000000..a361c7b
--- /dev/null
+++ b/application/classes/pla/controller/login.php
@@ -0,0 +1,200 @@
+logged_in()!= 0) {
+ // Redirect to the user account
+ Request::current()->redirect('user/welcome');
+ }
+
+ // If there is a post and $_POST is not empty
+ if ($_POST) {
+//echo debug::vars(array('p'=>$_POST,'ai'=>Auth::instance()));die();
+ // Store our details in a session key
+ Session::instance()->set('login',$_POST['username']);
+ Session::instance()->set('password',$_POST['password']);
+
+ // If the post data validates using the rules setup in the user model
+ if (Auth::instance()->login($_POST['username'],$_POST['password'])) {
+ // Redirect to the user account
+ if ($redir = Session::instance()->get('afterlogin')) {
+ Session::instance()->delete('afterlogin');
+ Request::current()->redirect($redir);
+
+ } else
+ Request::current()->redirect('user/welcome');
+
+ } else {
+ SystemMessage::add(array(
+ 'title'=>_('Invalid username or password'),
+ 'type'=>'error',
+ 'body'=>_('The username or password was invalid.')
+ ));
+ }
+ }
+
+ Block::add(array(
+ 'title'=>_('Login to server'),
+ 'body'=>View::factory('login'),
+ 'style'=>array('css/login.css'=>'screen'),
+ ));
+
+ Script::add(array('type'=>'stdin','data'=>'
+ $(document).ready(function() {
+ $("#ajxbody").click(function() {$("#ajBODY").load("'.$this->request->uri().'/"); return false;});
+ });'
+ ));
+ }
+
+ public function action_register() {
+ // If user already signed-in
+ if (Auth::instance()->logged_in()!= 0) {
+ // Redirect to the user account
+ Request::current()->redirect('welcome/index');
+ }
+
+ // Instantiate a new user
+ $account = ORM::factory('account');
+
+ // If there is a post and $_POST is not empty
+ if ($_POST) {
+ // Check Auth
+ $status = $account->values($_POST)->check();
+
+ if (! $status) {
+ foreach ($account->validation()->errors('form/register') as $f => $r) {
+ // $r[0] has our reason for validation failure
+ switch ($r[0]) {
+ // Generic validation reason
+ default:
+ SystemMessage::add(array(
+ 'title'=>_('Validation failed'),
+ 'type'=>'error',
+ 'body'=>sprintf(_('The defaults on your submission were not valid for field %s (%s).'),$f,$r)
+ ));
+ }
+ }
+ }
+
+ $ido = ORM::factory('module')
+ ->where('name','=','account')
+ ->find();
+
+ $account->id = $ido->record_id->next_id($ido->id);
+ // Save the user details
+ if ($account->save()) {}
+
+ }
+
+ SystemMessage::add(array(
+ 'title'=>_('Already have an account?'),
+ 'type'=>'info',
+ 'body'=>_('If you already have an account, please login..')
+ ));
+
+ Block::add(array(
+ 'title'=>_('Register'),
+ 'body'=>View::factory('bregister')
+ ->set('account',$account)
+ ->set('errors',$account->validation()->errors('form/register')),
+ ));
+
+ $this->template->left = HTML::anchor('login','Login').'...';
+ }
+
+ /**
+ * Enable user password reset
+ */
+ public function action_reset() {
+ // If user already signed-in
+ if (Auth::instance()->logged_in()!= 0) {
+ // Redirect to the user account
+ Request::current()->redirect('welcome/index');
+ }
+
+ // If the user posted their details to reset their password
+ if ($_POST) {
+ // If the email address is correct, create a method token
+ if (! empty($_POST['email']) AND ($ao=ORM::factory('account',array('email'=>$_POST['email']))) AND $ao->loaded()) {
+ $mt = ORM::factory('module_method_token');
+
+ // Find out our password reset method id
+ // @todo move this to a more generic method, so that it can be called by other methods
+ $mo = ORM::factory('module',array('name'=>'account'));
+ $mmo = ORM::factory('module_method',array('name'=>'user_resetpassword','module_id'=>$mo->id));
+
+ // Check to see if there is already a token, if so, do nothing.
+ if ($mt->where('account_id','=',$ao->id)->and_where('method_id','=',$mmo->id)->find()) {
+ if ($mt->date_expire < time()) {
+ $mt->delete();
+ $mt->clear();
+ }
+ }
+
+ if (! $mt->loaded()) {
+ $mt->account_id = $ao->id;
+ $mt->method_id = $mmo->id;
+ $mt->date_expire = time() + 15*3600;
+ $mt->token = md5(sprintf('%s:%s:%s',$mt->account_id,$mt->method_id,$mt->date_expire));
+ $mt->save();
+
+ // Send our email with the token
+ $et = EmailTemplate::instance('account_reset_password');
+ $et->to = array($mt->account->email=>sprintf('%s %s',$mt->account->first_name,$mt->account->last_name));
+ $et->variables = array(
+ 'SITE'=>URL::base(TRUE,TRUE),
+ 'SITE_ADMIN'=>Config::sitename(),
+ 'SITE_NAME'=>Config::sitename(),
+ 'TOKEN'=>$mt->token,
+ 'USER_NAME'=>sprintf('%s %s',$mt->account->first_name,$mt->account->last_name),
+ );
+ $et->send();
+ }
+
+ // Redirect to our password reset, the Auth will validate the token.
+ } elseif (! empty($_REQUEST['token'])) {
+ Request::current()->redirect(sprintf('user/account/resetpassword?token=%s',$_REQUEST['token']));
+ }
+
+ // Show our token screen even if the email was invalid.
+ if (isset($_POST['email']))
+ Block::add(array(
+ 'title'=>_('Reset your password'),
+ 'body'=>View::factory('login_reset_sent'),
+ 'style'=>array('css/login.css'=>'screen'),
+ ));
+ else
+ Request::current()->redirect('login');
+
+ } else {
+ Block::add(array(
+ 'title'=>_('Reset your password'),
+ 'body'=>View::factory('login_reset'),
+ 'style'=>array('css/login.css'=>'screen'),
+ ));
+ }
+ }
+
+ public function action_noaccess() {
+ SystemMessage::add(array(
+ 'title'=>_('No access to requested resource'),
+ 'type'=>'error',
+ 'body'=>_('You do not have access to the requested resource, please contact your administrator.')
+ ));
+ }
+}
+?>
diff --git a/application/classes/pla/controller/logout.php b/application/classes/pla/controller/logout.php
new file mode 100644
index 0000000..ded76b5
--- /dev/null
+++ b/application/classes/pla/controller/logout.php
@@ -0,0 +1,26 @@
+logged_in()!= 0) {
+ Auth::instance()->logout();
+
+ Request::current()->redirect('login');
+ }
+
+ Request::current()->redirect('welcome/index');
+ }
+}
+?>
diff --git a/application/classes/pla/controller/media.php b/application/classes/pla/controller/media.php
new file mode 100644
index 0000000..9413a08
--- /dev/null
+++ b/application/classes/pla/controller/media.php
@@ -0,0 +1,59 @@
+request->param('file');
+
+ // Find the file extension
+ $ext = pathinfo($file,PATHINFO_EXTENSION);
+
+ // Remove the extension from the filename
+ $file = substr($file,0,-(strlen($ext)+1));
+ $f = '';
+
+ // If our file is pathed with session, our file is in our session.
+ if ($fd = Session::instance()->get_once($this->request->param('file'))) {
+ $this->response->body($fd);
+
+ // If not found try a default media file
+ } elseif ($f = Kohana::find_file('media/'.Kohana::Config('config.theme'),$file,$ext)) {
+ // Send the file content as the response
+ $this->response->body(file_get_contents($f));
+
+ // If not found try a default media file
+ } elseif ($f = Kohana::find_file('media',$file,$ext)) {
+ // Send the file content as the response
+ $this->response->body(file_get_contents($f));
+
+ } else {
+ // Return a 404 status
+ $this->response->status(404);
+ }
+
+ // Generate and check the ETag for this file
+ if (Kohana::$environment === Kohana::PRODUCTION)
+ $this->response->check_cache(NULL,$this->request);
+
+ // Set the proper headers to allow caching
+ $this->response->headers('Content-Type',File::mime_by_ext($ext));
+ $this->response->headers('Content-Length',(string)$this->response->content_length());
+ $this->response->headers('Last-Modified',date('r', $f ? filemtime($f) : time()));
+ }
+}
+?>
diff --git a/application/classes/pla/controller/template.php b/application/classes/pla/controller/template.php
new file mode 100644
index 0000000..4b189b1
--- /dev/null
+++ b/application/classes/pla/controller/template.php
@@ -0,0 +1,124 @@
+template = Kohana::Config('config.theme');
+
+ return parent::__construct($request,$response);
+ }
+
+ public function before() {
+ // Do not template media files
+ if ($this->request->action() === 'media') {
+ $this->auto_render = FALSE;
+ return;
+ }
+
+ parent::before();
+
+ // For AJAX calls, we dont need to render the complete page.
+ if ($this->request->is_ajax()) {
+ $this->auto_render = FALSE;
+ return;
+ }
+
+ $this->template->content = '';
+
+ // Setup the page template
+ $this->meta = new meta;
+ View::bind_global('meta',$this->meta);
+ }
+
+ public function after() {
+ if ($this->auto_render === TRUE) {
+ // Application Title
+ $this->meta->title = Kohana::Config('config.appname');
+
+ // Language
+ // @todo
+ $this->meta->language = '';
+
+ // Description
+ $this->meta->description = sprintf('%s::%s',$this->request->controller(),$this->request->action());
+
+ // Control Line
+ // @todo
+ $this->template->control = '';
+
+ // System Messages line
+ // @todo
+ $this->template->sysmsg = '';
+
+ // Left Item
+ // @todo
+ $this->template->left = '';
+ $this->template->right = '';
+ $this->template->center = '';
+
+ if (! $this->response->body())
+ $this->response->body((string)Block::factory());
+
+ if (empty($this->template->content))
+ $this->template->content = $this->response->body();
+
+ // Footer
+ // @todo
+ $this->template->footer = '';
+
+ // Our default script(s)
+ foreach (array('file'=>array_reverse(array(
+ 'js/jquery-1.6.4.min.js',
+ ))) as $type => $datas) {
+
+ foreach ($datas as $data) {
+ Script::add(array(
+ 'type'=>$type,
+ 'data'=>$data,
+ ),TRUE);
+ }
+ }
+
+ // Add our logo
+ Style::add(array(
+ 'type'=>'stdin',
+ 'data'=>'h1 span{background:url('.Config::logo_uri().') no-repeat;}',
+ ));
+
+ // For any ajax rendered actions, we'll need to capture the content and put it in the response
+ // @todo
+ } elseif ($this->request->is_ajax() && isset($this->template->content) && ! $this->response->body()) {
+ // @todo move this formatting to a view?
+ if ($s = $this->_sysmsg() AND (string)$s)
+ $this->response->body(sprintf('',$s));
+
+ // Since we are ajax, we should re-render the breadcrumb
+ Session::instance()->set('breadcrumb',(string)Breadcrumb::factory());
+ $this->response->bodyadd(Script::add(array('type'=>'stdin','data'=>'$().ready($("#ajCONTROL").load("'.URL::site('welcome/breadcrumb').'",null,function(x,s,r) {}));')));
+
+ // In case there any javascript for this render.
+ $this->response->bodyadd(Script::factory());
+
+ // Get the response body
+ $this->response->bodyadd(sprintf('',$this->template->content));
+ }
+
+ parent::after();
+
+ // Generate and check the ETag for this file
+ if (Kohana::$environment === Kohana::PRODUCTION)
+ $this->response->check_cache(NULL,$this->request);
+ }
+}
diff --git a/application/classes/pla/database/ldap.php b/application/classes/pla/database/ldap.php
new file mode 100644
index 0000000..e127284
--- /dev/null
+++ b/application/classes/pla/database/ldap.php
@@ -0,0 +1,186 @@
+index);
+ */
+
+ /*
+ // @todo To implement
+ if (function_exists('run_hook'))
+ run_hook('pre_connect',array('server_id'=>$this->index,'method'=>$method));
+ */
+
+ if (! empty($this->_config['port']))
+ $r = ldap_connect($this->_config['connection']['hostname'],$this->_config['port']);
+ else
+ $r = ldap_connect($this->_config['connection']['hostname']);
+
+ /*
+ // @todo To implement
+ if (DEBUG_ENABLED)
+ debug_log('LDAP Resource [%s], Host [%s], Port [%s]',16,0,__FILE__,__LINE__,__METHOD__,
+ $this->_r,$this->getValue('server','host'),$this->getValue('server','port'));
+ */
+
+ if (! is_resource($r))
+ throw Kohana_Exception('UNHANDLED, $r is not a resource');
+
+ // Go with LDAP version 3 if possible (needed for renaming and Novell schema fetching)
+ ldap_set_option($r,LDAP_OPT_PROTOCOL_VERSION,3);
+
+ /* Disabling this makes it possible to browse the tree for Active Directory, and seems
+ * to not affect other LDAP servers (tested with OpenLDAP) as phpLDAPadmin explicitly
+ * specifies deref behavior for each ldap_search operation. */
+ ldap_set_option($r,LDAP_OPT_REFERRALS,0);
+
+ /*
+ // @todo To implement
+ # Try to fire up TLS is specified in the config
+ if ($this->isTLSEnabled())
+ $this->startTLS($this->_r);
+ */
+
+ return $r;
+ }
+
+ private function _bind($r,$u,$p) {
+ if (! is_resource($r))
+ throw Kohana_Exception('UNHANDLED, $r is not a resource');
+
+ /*
+ // @todo To implement
+ # If SASL has been configured for binding, then start it now.
+ if ($this->isSASLEnabled())
+ $br = $this->startSASL($this->_r,$method);
+
+ # Normal bind...
+ else
+ */
+ $br = @ldap_bind($r,$u,$p);
+
+ /*
+ if ($debug)
+ debug_dump(array('method'=>$method,'bind'=>$bind,'USER'=>$_SESSION['USER']));
+
+ if (DEBUG_ENABLED)
+ debug_log('Resource [%s], Bind Result [%s]',16,0,__FILE__,__LINE__,__METHOD__,$this->_r,$bind);
+ */
+
+ if (! $br) {
+ /*
+ if (DEBUG_ENABLED)
+ debug_log('Leaving with FALSE, bind FAILed',16,0,__FILE__,__LINE__,__METHOD__);
+ */
+
+ $this->noconnect = true;
+
+ /*
+ // @todo To implement
+ system_message(array(
+ 'title'=>sprintf('%s %s',_('Unable to connect to LDAP server'),$this->getName()),
+ 'body'=>sprintf('%s: %s (%s) for %s',_('Error'),$this->getErrorMessage($method),$this->getErrorNum($method),$method),
+ 'type'=>'error'));
+ */
+
+ } else {
+ $this->noconnect = false;
+
+ /*
+ // @todo To implement
+ # If this is a proxy session, we need to switch to the proxy user
+ if ($this->isProxyEnabled() && $bind['id'] && $method != 'anon')
+ if (! $this->startProxy($this->_r,$method)) {
+ $this->noconnect = true;
+ $CACHE[$this->index][$method] = null;
+ }
+ */
+ }
+
+ /*
+ // @todo To implement
+ if (function_exists('run_hook'))
+ run_hook('post_connect',array('server_id'=>$this->index,'method'=>$method,'id'=>$bind['id']));
+ */
+
+ /*
+ // @todo To implement
+ if ($debug)
+ debug_dump(array($method=>$CACHE[$this->index][$method]));
+ */
+
+ return $br;
+ }
+
+ public function connect() {
+ if ($this->_r = $this->_connect())
+ return $this;
+ else
+ throw Kohana_Exception('Unable to connect to LDAP Server?');
+ }
+
+ public function bind($user,$pass) {
+ // If this is an anon query, then we return
+
+ // Do we need to do an anon search to find the DN
+ if (! empty($this->_config['login_attr']) AND strtoupper($this->_config['login_attr']) != 'DN') {
+ $u = $this->search()
+ ->scope('sub')
+ ->where($this->_config['login_attr'],'=',$user)
+ ->run();
+
+ if (! $u)
+ throw new Kohana_Exception('Unable to find user :user',array(':user'=>$user));
+
+ $u = array_shift($u);
+ $user = $u['dn'];
+ }
+
+ // Bind
+ if ($this->_bind($this->_r,$user,$pass))
+ return $this;
+ else
+ throw new Kohana_Exception('Unable to bind');
+ }
+
+ public function search() {
+ return new Database_LDAP_Search($this->_r);
+ }
+}
+?>
diff --git a/application/classes/pla/database/ldap/search.php b/application/classes/pla/database/ldap/search.php
new file mode 100644
index 0000000..04ac955
--- /dev/null
+++ b/application/classes/pla/database/ldap/search.php
@@ -0,0 +1,270 @@
+_r = $resource;
+ }
+
+ /**
+ * Handles pass-through to database methods. Calls to query methods
+ * (query, get, insert, update) are not allowed. Query builder methods
+ * are chainable.
+ *
+ * @param string $method Method name
+ * @param array $args Method arguments
+ * @return mixed
+ */
+ public function __call($method,array $args) {
+ if (in_array($method,Database_LDAP_Search::$_properties)) {
+ /*
+ // @todo To Implement
+ if ($method === 'validation')
+ {
+ if ( ! isset($this->_validation))
+ {
+ // Initialize the validation object
+ $this->_validation();
+ }
+ }
+ */
+
+ // Return the property
+ return $this->{'_'.$method};
+ }
+ elseif (in_array($method,Database_LDAP_Search::$_db_methods))
+ {
+ // Add pending database call which is executed after query type is determined
+ $this->_db_pending[] = array('name' => $method,'args' => $args);
+
+ return $this;
+ }
+ else
+ {
+ throw new Kohana_Exception('Invalid method :method called in :class',
+ array(':method' => $method,':class' => get_class($this)));
+ }
+ }
+
+ private function _build() {
+ $s = Database_LDAP_Search::Search();
+
+ // Process pending database method calls
+ foreach ($this->_db_pending as $method) {
+ $name = $method['name'];
+ $args = $method['args'];
+
+ $this->_db_applied[$name] = $name;
+
+ call_user_func_array(array($s,$name),$args);
+ }
+
+ return $s;
+ }
+
+ public static function Search($columns = NULL) {
+ return new Database_LDAP_Search_Builder_Query(func_get_args());
+ }
+
+ public static function instance($resource) {
+ return new Database_LDAP_Search($resource);
+ }
+
+ public function scope($val) {
+ switch ($val) {
+ case 'base':
+ case 'sub':
+ case 'one': $this->_scope = $val;
+ break;
+
+ default:
+ throw new Kohana_Exception('Unknown search scope :scope',array(':scope',$val));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Search the LDAP database
+ */
+ public function run() {
+ $query = array();
+
+ // Compile our query
+ if ($this->_db_pending)
+ $this->_filter = $this->_build();
+
+ /*
+ if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
+ debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
+ */
+
+ $attrs_only = 0;
+
+ $this->_base = 'o=Simpsons';
+ /*
+ // @todo To implement
+ if (! isset($query['base'])) {
+ $bases = $this->getBaseDN();
+ $query['base'] = array_shift($bases);
+ }
+ */
+
+ /*
+ // @todo To implement
+ if (! isset($query['deref']))
+ $query['deref'] = $_SESSION[APPCONFIG]->getValue('deref','search');
+ */
+ if (! isset($query['size_limit']))
+ $query['size_limit'] = 0;
+ if (! isset($query['time_limit']))
+ $query['time_limit'] = 0;
+
+ /*
+ if ($query['scope'] == 'base' && ! isset($query['baseok']))
+ system_message(array(
+ 'title'=>sprintf('Dont call %s',__METHOD__),
+ 'body'=>sprintf('Use getDNAttrValues for base queries [%s]',$query['base']),
+ 'type'=>'info'));
+ */
+
+ /*
+ if (is_array($query['base'])) {
+ system_message(array(
+ 'title'=>_('Invalid BASE for query'),
+ 'body'=>_('The query was cancelled because of an invalid base.'),
+ 'type'=>'error'));
+
+ return array();
+ }
+ */
+
+ /*
+ if (DEBUG_ENABLED)
+ debug_log('%s search PREPARE.',16,0,__FILE__,__LINE__,__METHOD__,$query['scope']);
+ */
+
+ /*
+ if ($debug)
+ debug_dump(array('query'=>$query,'server'=>$this->getIndex(),'con'=>$this->connect($method)));
+ */
+
+ //$resource = $this->connect($method,$debug);
+
+ switch ($this->_scope) {
+ case 'base':
+ $search = @ldap_read($this->_r,$this->_base,$this->_filter,$this->_attrs,$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
+ break;
+
+ case 'one':
+ $search = @ldap_list($this->_r,$this->_base,$this->_filter,$this->_attrs,$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
+ break;
+
+ case 'sub':
+ default:
+ $search = @ldap_search($this->_r,$this->_base,$this->_filter,$this->_attrs,$attrs_only,$query['size_limit'],$query['time_limit'],$query['deref']);
+ break;
+ }
+
+ /*
+ if ($debug)
+ debug_dump(array('method'=>$method,'search'=>$search,'error'=>$this->getErrorMessage()));
+ */
+
+ /*
+ if (DEBUG_ENABLED)
+ debug_log('Search scope [%s] base [%s] filter [%s] attrs [%s] COMPLETE (%s).',16,0,__FILE__,__LINE__,__METHOD__,
+ $query['scope'],$query['base'],$query['filter'],$query['attrs'],is_null($search));
+ */
+
+ if (! $search)
+ return array();
+
+ $return = array();
+
+ // Get the first entry identifier
+ if ($entries = ldap_get_entries($this->_r,$search)) {
+ # Remove the count
+ if (isset($entries['count']))
+ unset($entries['count']);
+
+ // Iterate over the entries
+ foreach ($entries as $a => $entry) {
+ /*
+ if (! isset($entry['dn']))
+ debug_dump_backtrace('No DN?',1);
+ */
+
+ // Remove the none entry references.
+ if (! is_array($entry)) {
+ unset($entries[$a]);
+ continue;
+ }
+
+ $dn = $entry['dn'];
+ unset($entry['dn']);
+
+ // Iterate over the attributes
+ foreach ($entry as $b => $attrs) {
+ // Remove the none entry references.
+ if (! is_array($attrs)) {
+ unset($entry[$b]);
+ continue;
+ }
+
+ // Remove the count
+ if (isset($entry[$b]['count']))
+ unset($entry[$b]['count']);
+ }
+
+ // Our queries always include the DN (the only value not an array).
+ $entry['dn'] = $dn;
+ $return[$dn] = $entry;
+ }
+
+ // Sort our results
+ foreach ($return as $key => $values)
+ ksort($return[$key]);
+ }
+
+ /*
+ if (DEBUG_ENABLED)
+ debug_log('Returning (%s)',17,0,__FILE__,__LINE__,__METHOD__,$return);
+ */
+
+ return $return;
+ }
+}
+?>
diff --git a/application/classes/pla/database/ldap/search/builder/query.php b/application/classes/pla/database/ldap/search/builder/query.php
new file mode 100644
index 0000000..63c1c21
--- /dev/null
+++ b/application/classes/pla/database/ldap/search/builder/query.php
@@ -0,0 +1,214 @@
+and_where($column,$op,$value);
+ }
+
+ /**
+ * Creates a new "AND WHERE" condition for the query.
+ *
+ * @param mixed column name or array($column,$alias) or object
+ * @param string logic operator
+ * @param mixed column value
+ * @return $this
+ */
+ public function and_where($column,$op,$value) {
+ $this->_where[] = array('AND' => array($column,$op,$value));
+
+ return $this;
+ }
+
+ /**
+ * Creates a new "OR WHERE" condition for the query.
+ *
+ * @param mixed column name or array($column,$alias) or object
+ * @param string logic operator
+ * @param mixed column value
+ * @return $this
+ */
+ public function or_where($column,$op,$value) {
+ $this->_where[] = array('OR' => array($column,$op,$value));
+
+ return $this;
+ }
+
+ /**
+ * Alias of and_where_open()
+ *
+ * @return $this
+ */
+ public function where_open() {
+ return $this->and_where_open();
+ }
+
+ /**
+ * Opens a new "AND WHERE (...)" grouping.
+ *
+ * @return $this
+ */
+ public function and_where_open() {
+ $this->_where[] = array('AND' => '(');
+
+ return $this;
+ }
+
+ /**
+ * Opens a new "OR WHERE (...)" grouping.
+ *
+ * @return $this
+ */
+ public function or_where_open() {
+ $this->_where[] = array('OR' => '(');
+
+ return $this;
+ }
+
+ public function compile(Database $db) {
+ $filter = '';
+
+ return $this->_compile_conditions($db,$this->_where);
+ }
+
+ /**
+ * Closes an open "AND WHERE (...)" grouping.
+ *
+ * @return $this
+ */
+ public function where_close() {
+ return $this->and_where_close();
+ }
+
+ /**
+ * Closes an open "AND WHERE (...)" grouping.
+ *
+ * @return $this
+ */
+ public function and_where_close() {
+ $this->_where[] = array('AND' => ')');
+
+ return $this;
+ }
+
+ /**
+ * Closes an open "OR WHERE (...)" grouping.
+ *
+ * @return $this
+ */
+ public function or_where_close() {
+ $this->_where[] = array('OR' => ')');
+
+ return $this;
+ }
+
+ /**
+ * Compiles an array of conditions into an LDAP filter.
+ *
+ * @param object Database instance
+ * @param array condition statements
+ * @return string
+ */
+ protected function _compile_conditions(Database $db,array $conditions,$index=0) {
+ $current_condition = $last_condition = NULL;
+
+ $filter = '';
+ $sub = 0;
+ foreach ($conditions as $key => $group) {
+ // If we have been called again, we need to skip ahead, or skip what has been processed
+ if ($key < $index OR $sub)
+ continue;
+
+ // Process groups of conditions
+ foreach ($group as $logic => $condition) {
+ if ($condition === '(') {
+ $filter .= $this->_compile_conditions($db,$conditions,$key+1);
+ $sub = 1;
+
+ } elseif ($condition === ')') {
+ if ($index) {
+ // As we return, we'll include our condition
+ switch ($current_condition) {
+ case 'AND':
+ return '(&'.$filter.')';
+
+ case 'OR':
+ return '(|'.$filter.')';
+
+ default:
+ throw new Kohana_Exception('Condition :condition not handled.',array(':condition'=>$condition));
+ }
+ }
+
+ $sub = 0;
+
+ } else {
+ // We currently cant handle when a condition changes, without brackets.
+ if ($filter AND $current_condition AND $current_condition != $logic)
+ throw new Kohana_Exception('Condition changed without brackets');
+
+ $current_condition = $logic;
+
+ // Split the condition
+ list($column,$op,$value) = $condition;
+
+ // Database operators are always uppercase
+ $op = strtoupper($op);
+
+ if ((is_string($value) AND array_key_exists($value,$this->_parameters)) === FALSE) {
+ // Quote the value, it is not a parameter
+ $value = $db->quote($value);
+ }
+
+ if ($column) {
+ // Apply proper quoting to the column
+ $column = $db->quote_column($column);
+ }
+
+ // Append the statement to the query
+ $filter .= trim('('.$column.$op.$value.')');
+ }
+
+ $last_condition = $condition;
+ }
+ }
+
+ switch ($current_condition) {
+ case 'AND':
+ return '(&'.$filter.')';
+
+ case 'OR':
+ return '(|'.$filter.')';
+
+ default:
+ throw new Kohana_Exception('Condition :condition not handled.',array(':condition'=>$condition));
+ }
+ }
+}
+?>
diff --git a/application/classes/pla/htmlrender.php b/application/classes/pla/htmlrender.php
new file mode 100644
index 0000000..5bf2100
--- /dev/null
+++ b/application/classes/pla/htmlrender.php
@@ -0,0 +1,92 @@
+get_called_class()));
+ }
+
+ /**
+ * Add an item to be rendered
+ *
+ * @param array Item to be added
+ */
+ public static function add($item,$prepend=FALSE) {
+ foreach (static::$_required_keys as $key)
+ if (! isset($item[$key]))
+ throw new Kohana_Exception('Missing key :key for image',array(':key'=>$key));
+
+ // Check for unique keys
+ if (static::$_unique_vals)
+ foreach (static::$_unique_vals as $v=>$u)
+ foreach (static::$_data as $d)
+ if (isset($d[$u]) && $d['data'] == $item['data'])
+ return;
+
+ if ($prepend)
+ array_unshift(static::$_data,$item);
+ else
+ array_push(static::$_data,$item);
+ }
+
+ /**
+ * Set the space used between rendering output
+ */
+ public static function setSpacer($spacer) {
+ static::$_spacer = $spacer;
+ }
+
+ /**
+ * Set the Kohana Media Path, used to determine where to find additional
+ * HTML content required for rendering.
+ */
+ public static function setMediaPath($path) {
+ static::$_media_path = $path;
+ }
+
+ /**
+ * Factory instance method must be declared by the child class
+ */
+ public static function factory() {
+ throw new Kohana_Exception(':class is calling :method, when it should have its own method',
+ array(':class'=>get_called_class(),':method'=>__METHOD__));
+ }
+
+ /**
+ * Return the HTML to render the header images
+ */
+ public function __toString() {
+ try {
+ return static::render();
+ }
+
+ // Display the exception message
+ catch (Exception $e) {
+ Kohana_Exception::handler($e);
+ }
+ }
+
+ /**
+ * Rendering must be declared by the child class
+ */
+ protected function render() {
+ throw new Kohana_Exception(':class is calling :method, when it should have its own method',
+ array(':class'=>get_called_class(),':method'=>__METHOD__));
+ }
+}
+?>
diff --git a/application/classes/pla/meta.php b/application/classes/pla/meta.php
new file mode 100644
index 0000000..f87c496
--- /dev/null
+++ b/application/classes/pla/meta.php
@@ -0,0 +1,34 @@
+_array_keys) && empty($this->_data[$key]))
+ return array();
+
+ if (empty($this->_data[$key]))
+ return null;
+ else
+ return $this->_data[$key];
+ }
+
+ public function __set($key,$value) {
+ if (in_array($key,$this->_array_keys) && ! is_array($value))
+ throw new Kohana_Exception('Key :key must be an array',array(':key'=>$key));
+
+ $this->_data[$key] = $value;
+ }
+}
+?>
diff --git a/application/classes/pla/script.php b/application/classes/pla/script.php
new file mode 100644
index 0000000..00d8839
--- /dev/null
+++ b/application/classes/pla/script.php
@@ -0,0 +1,53 @@
+'type');
+
+ /**
+ * Return an instance of this class
+ *
+ * @return Script
+ */
+ public static function factory() {
+ return new Script;
+ }
+
+ /**
+ * Render the script tag
+ *
+ * @see HTMLRender::render()
+ */
+ protected function render() {
+ $foutput = $soutput = '';
+ $mediapath = Route::get(static::$_media_path);
+
+ foreach (static::$_data as $value) {
+ switch ($value['type']) {
+ case 'file':
+ $foutput .= HTML::script($mediapath->uri(array('file'=>$value['data'])));
+ break;
+ case 'stdin':
+ $soutput .= sprintf("",$value['data']);
+ break;
+ default:
+ throw new Kohana_Exception('Unknown style type :type',array(':type'=>$value['type']));
+ }
+ }
+
+ return $foutput.static::$_spacer.$soutput;
+ }
+}
+?>
diff --git a/application/classes/pla/style.php b/application/classes/pla/style.php
new file mode 100644
index 0000000..29abd5e
--- /dev/null
+++ b/application/classes/pla/style.php
@@ -0,0 +1,54 @@
+'type');
+
+ /**
+ * Return an instance of this class
+ *
+ * @return Style
+ */
+ public static function factory() {
+ return new Style;
+ }
+
+ /**
+ * Render the style tag
+ *
+ * @see HTMLRender::render()
+ */
+ protected function render() {
+ $foutput = $soutput = '';
+ $mediapath = Route::get(static::$_media_path);
+
+ foreach (static::$_data as $value) {
+ switch ($value['type']) {
+ case 'file':
+ $foutput .= HTML::style($mediapath->uri(array('file'=>$value['data'])),
+ array('media'=>(! empty($value['media'])) ? $value['media'] : 'screen'),TRUE);
+ break;
+ case 'stdin':
+ $soutput .= sprintf("",$value['data']);
+ break;
+ default:
+ throw new Kohana_Exception('Unknown style type :type',array(':type'=>$value['type']));
+ }
+ }
+
+ return $foutput.static::$_spacer.$soutput;
+ }
+}
+?>
diff --git a/application/classes/script.php b/application/classes/script.php
new file mode 100644
index 0000000..676a71a
--- /dev/null
+++ b/application/classes/script.php
@@ -0,0 +1,4 @@
+
diff --git a/application/classes/style.php b/application/classes/style.php
new file mode 100644
index 0000000..e5a03bf
--- /dev/null
+++ b/application/classes/style.php
@@ -0,0 +1,4 @@
+
diff --git a/application/config/auth.php b/application/config/auth.php
new file mode 100644
index 0000000..6bc0cd8
--- /dev/null
+++ b/application/config/auth.php
@@ -0,0 +1,21 @@
+ 'LDAP',
+// 'hash_method' => '',
+ 'hash_key' => '', // LDAP passwords should be cleartext
+ 'lifetime' => 1209600,
+// 'session_key' => 'auth_user',
+// 'forced_key' => 'auth_forced',
+);
+?>
diff --git a/application/config/config.php b/application/config/config.php
new file mode 100644
index 0000000..034e13b
--- /dev/null
+++ b/application/config/config.php
@@ -0,0 +1,24 @@
+ 'phpLDAPadmin - LDAP Administration',
+ // Our mode level (PRODUCTION, STAGING, TESTING, DEVELOPMENT) - see [Kohana]
+ 'mode' => Kohana::PRODUCTION,
+ 'site' => array(
+ 'name'=>'phpLDAPadmin',
+ ),
+ // Our custom theme
+ 'theme' => 'original',
+);
+?>
diff --git a/application/config/database.php b/application/config/database.php
new file mode 100644
index 0000000..13d1068
--- /dev/null
+++ b/application/config/database.php
@@ -0,0 +1,70 @@
+ array
+ (
+ 'type' => 'ldap',
+ 'connection' => array(
+ /**
+ * The following options are available for MySQL:
+ *
+ * string hostname server hostname, or socket
+ * string database database name
+ * string username database username
+ * string password database password
+ * boolean persistent use persistent connections?
+ *
+ * Ports and sockets may be appended to the hostname.
+ */
+ 'hostname' => 'localhost',
+ 'database' => 'kohana',
+ 'username' => FALSE,
+ 'password' => FALSE,
+ 'persistent' => FALSE,
+ ),
+ 'table_prefix' => '',
+ 'charset' => 'utf8',
+ 'caching' => FALSE,
+ 'profiling' => TRUE,
+
+ 'login_attr'=>'uid',
+ ),
+ 'alternate' => array(
+ 'type' => 'pdo',
+ 'connection' => array(
+ /**
+ * The following options are available for PDO:
+ *
+ * string dsn Data Source Name
+ * string username database username
+ * string password database password
+ * boolean persistent use persistent connections?
+ */
+ 'dsn' => 'mysql:host=localhost;dbname=kohana',
+ 'username' => 'root',
+ 'password' => 'r00tdb',
+ 'persistent' => FALSE,
+ ),
+ /**
+ * The following extra options are available for PDO:
+ *
+ * string identifier set the escaping identifier
+ */
+ 'table_prefix' => '',
+ 'charset' => 'utf8',
+ 'caching' => FALSE,
+ 'profiling' => TRUE,
+ ),
+);