Browse Source

Added: PHP ClanCatsFramework

Mario Döring 11 years ago
parent
commit
5acc341bc1
100 changed files with 14768 additions and 0 deletions
  1. 3 0
      frameworks/PHP/php-clancatsframework/.bowerrc
  2. 12 0
      frameworks/PHP/php-clancatsframework/.gitignore
  3. 26 0
      frameworks/PHP/php-clancatsframework/.travis.yml
  4. 72 0
      frameworks/PHP/php-clancatsframework/CCF/app/App.php
  5. 35 0
      frameworks/PHP/php-clancatsframework/CCF/app/classes/User.php
  6. 15 0
      frameworks/PHP/php-clancatsframework/CCF/app/config/app.config.php
  7. 17 0
      frameworks/PHP/php-clancatsframework/CCF/app/config/auth.config.php
  8. 24 0
      frameworks/PHP/php-clancatsframework/CCF/app/config/database.config.php
  9. 27 0
      frameworks/PHP/php-clancatsframework/CCF/app/config/main.config.php
  10. 40 0
      frameworks/PHP/php-clancatsframework/CCF/app/config/router.config.php
  11. 78 0
      frameworks/PHP/php-clancatsframework/CCF/app/console/app.php
  12. 137 0
      frameworks/PHP/php-clancatsframework/CCF/app/controllers/AuthController.php
  13. 33 0
      frameworks/PHP/php-clancatsframework/CCF/app/controllers/BenchController.php
  14. 29 0
      frameworks/PHP/php-clancatsframework/CCF/app/controllers/ErrorController.php
  15. 34 0
      frameworks/PHP/php-clancatsframework/CCF/app/controllers/WelcomeController.php
  16. 10 0
      frameworks/PHP/php-clancatsframework/CCF/app/language/de-de/controller/auth.php
  17. 7 0
      frameworks/PHP/php-clancatsframework/CCF/app/language/de-de/model/user.php
  18. 10 0
      frameworks/PHP/php-clancatsframework/CCF/app/language/en-us/controller/auth.php
  19. 7 0
      frameworks/PHP/php-clancatsframework/CCF/app/language/en-us/model/user.php
  20. 18 0
      frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootpack/blueprint.json
  21. 36 0
      frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootpack/classes/Theme.php
  22. 65 0
      frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootpack/config/theme.config.php
  23. 95 0
      frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootpack/views/layout.php
  24. 18 0
      frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootstrap/blueprint.json
  25. 81 0
      frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootstrap/classes/Theme.php
  26. 29 0
      frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootstrap/config/theme.config.php
  27. 45 0
      frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootstrap/views/layout.php
  28. 48 0
      frameworks/PHP/php-clancatsframework/CCF/app/views/auth/sign_in.view.php
  29. 50 0
      frameworks/PHP/php-clancatsframework/CCF/app/views/auth/sign_up.view.php
  30. 24 0
      frameworks/PHP/php-clancatsframework/CCF/app/views/bench/fortune.php
  31. 6 0
      frameworks/PHP/php-clancatsframework/CCF/app/views/welcome.php
  32. 72 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/CCAuth.php
  33. 12 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/Exception.php
  34. 39 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/Group.php
  35. 476 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/Handler.php
  36. 84 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/User.php
  37. 409 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Builder.php
  38. 15 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Builder/Mysql.php
  39. 15 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Builder/Sqlite.php
  40. 230 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/DB.php
  41. 12 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Exception.php
  42. 43 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Expression.php
  43. 390 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Handler.php
  44. 83 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Handler/Driver.php
  45. 33 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Handler/Mysql.php
  46. 33 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Handler/Sqlite.php
  47. 416 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Migrator.php
  48. 537 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model.php
  49. 210 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model/Relation.php
  50. 44 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model/Relation/BelongsTo.php
  51. 30 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model/Relation/HasMany.php
  52. 24 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model/Relation/HasOne.php
  53. 12 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/ModelException.php
  54. 328 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query.php
  55. 28 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query/Delete.php
  56. 88 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query/Insert.php
  57. 436 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query/Select.php
  58. 64 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query/Update.php
  59. 12 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/QueryException.php
  60. 105 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Test/Case.php
  61. 70 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Test/Case/Database.php
  62. 438 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/CCMail.php
  63. 12 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Exception.php
  64. 3417 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/PHPMailer/class.phpmailer.php
  65. 397 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/PHPMailer/class.pop3.php
  66. 941 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/PHPMailer/class.smtp.php
  67. 148 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter.php
  68. 33 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/Array.php
  69. 33 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/File.php
  70. 23 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/Interface.php
  71. 108 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/PHPMailer.php
  72. 27 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/Sendmail.php
  73. 36 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/Smtp.php
  74. 124 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/CCSession.php
  75. 12 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Exception.php
  76. 358 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager.php
  77. 68 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Array.php
  78. 86 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Cookie.php
  79. 161 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Database.php
  80. 87 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/File.php
  81. 46 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Interface.php
  82. 83 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Json.php
  83. 109 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Alert.php
  84. 55 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Builder.php
  85. 99 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Builder/Bootstrap.php
  86. 44 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Builder/Interface.php
  87. 151 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/E.php
  88. 12 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Exception.php
  89. 369 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Form.php
  90. 135 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/HTML.php
  91. 125 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Table.php
  92. 68 0
      frameworks/PHP/php-clancatsframework/CCF/core/bundles/map.php
  93. 57 0
      frameworks/PHP/php-clancatsframework/CCF/core/classes/CCApp.php
  94. 455 0
      frameworks/PHP/php-clancatsframework/CCF/core/classes/CCArr.php
  95. 302 0
      frameworks/PHP/php-clancatsframework/CCF/core/classes/CCAsset.php
  96. 106 0
      frameworks/PHP/php-clancatsframework/CCF/core/classes/CCAsset/Holder.php
  97. 277 0
      frameworks/PHP/php-clancatsframework/CCF/core/classes/CCCli.php
  98. 69 0
      frameworks/PHP/php-clancatsframework/CCF/core/classes/CCColor.php
  99. 157 0
      frameworks/PHP/php-clancatsframework/CCF/core/classes/CCConfig.php
  100. 59 0
      frameworks/PHP/php-clancatsframework/CCF/core/classes/CCConfig/Array.php

+ 3 - 0
frameworks/PHP/php-clancatsframework/.bowerrc

@@ -0,0 +1,3 @@
+{
+  "directory": "public/assets/vendor"
+}

+ 12 - 0
frameworks/PHP/php-clancatsframework/.gitignore

@@ -0,0 +1,12 @@
+public/assets/packtacular/
+CCF/app/config/migrator.json
+CCF/vendor/
+storage/
+.DS_Store
+Thumbs.db
+composer.phar
+composer.lock
+phpunit.xml
+phpunit.phar
+report/
+run

+ 26 - 0
frameworks/PHP/php-clancatsframework/.travis.yml

@@ -0,0 +1,26 @@
+language: php
+
+php:
+ - 5.3
+ - 5.4
+ - 5.5
+ - 5.6
+ - hhvm
+
+env:
+ - DB=mysql
+
+before_script:
+ - mysql -e 'create database ccf2_phpunit_application'
+ - mysql -e 'create database ccf2_phpunit_database'
+ - composer self-update
+ - composer install --prefer-source --no-interaction --dev
+
+script:
+ - php cli phpunit::build
+ - php cli doctor::security_key
+ - phpunit --coverage-text
+
+matrix:
+ allow_failures:
+  - php: hhvm

+ 72 - 0
frameworks/PHP/php-clancatsframework/CCF/app/App.php

@@ -0,0 +1,72 @@
+<?php 
+/*
+ *---------------------------------------------------------------
+ * Application Object
+ *---------------------------------------------------------------
+ *
+ * This is your default application object.
+ */
+class App extends CCApp 
+{	
+	/**
+	 * The application name
+	 *
+	 * @var string
+	 */
+	public static $name = 'My Application';
+	
+	/**
+	 * App configuration
+	 *
+	 * @var CCConfig
+	 */
+	public static $config = null;
+	
+	/**
+	 * current user object
+	 *
+	 * @var User
+	 */
+	public static $user = null;
+	
+	/**
+	 * Application initialization.
+	 * do your inital stuff here like getting the current user object ect..
+	 * You can return a CCResponse wich will cancle all other actions 
+	 * if enebaled ( see. main.config -> send_app_wake_response )
+	 *
+	 * @return void | CCResponse
+	 */
+	public static function wake() 
+	{
+		/*
+		 * Start the session by adding the current uri
+		 */
+		//CCSession::set( 'uri', CCServer::uri() );
+		
+		/*
+		 * try to authenticate the user
+		 */
+		//static::$user =& CCAuth::handler()->user;
+		
+		/*
+		 * load the App configuration
+		 */
+		//static::$config = CCConfig::create( 'app' );
+	}
+	
+	/**
+	 * Environment wake
+	 * This is an environment wake they get called if your running
+	 * the application in a special environment like:
+	 *     wake_production
+	 *     wake_phpunit
+	 *     wake_test
+	 *
+	 * @return void | CCResponse
+	 */
+	public static function wake_development() 
+	{	
+		CCProfiler::check( 'App::development - Application finished wake events.' );
+	}
+}

+ 35 - 0
frameworks/PHP/php-clancatsframework/CCF/app/classes/User.php

@@ -0,0 +1,35 @@
+<?php
+/**
+ * User model
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class User extends Auth\User
+{	
+	/**
+	 * The users table
+	 */
+	protected static $_table = 'auth_users';
+	
+	/**
+	 * The user model defaults
+	 *
+	 * @var array
+	 */
+	protected static $_defaults = array(
+		'id'	,
+		'active'			=> array( 'bool', true ),
+		'group_id'		=> 0,
+		'email'			=> null,
+		'password'		=> null,
+		'storage'		=> array( 'json', array() ),
+		'last_login'		=> array( 'timestamp', 0 ),
+		'created_at'		=> array( 'timestamp' ),
+		'modified_at'	=> array( 'timestamp' ),
+	);
+}

+ 15 - 0
frameworks/PHP/php-clancatsframework/CCF/app/config/app.config.php

@@ -0,0 +1,15 @@
+<?php 
+/*
+ *---------------------------------------------------------------
+ * App configuration
+ *---------------------------------------------------------------
+ *
+ * This is your main application configuration file 
+ */
+return array(
+    
+    /*
+     * an description of your application
+     */
+    'description'      => 'ClanCats Framework, because your time is precious. HMVC PHP framework.',
+);

+ 17 - 0
frameworks/PHP/php-clancatsframework/CCF/app/config/auth.config.php

@@ -0,0 +1,17 @@
+<?php 
+/*
+ *---------------------------------------------------------------
+ * Auth configuration
+ *---------------------------------------------------------------
+ */
+return array(
+
+	/*
+	 * This is the default configuration for the main session
+	 */
+	'main' => array(
+
+		// The User model class
+		'user_model' => "\\User",
+	),
+);

+ 24 - 0
frameworks/PHP/php-clancatsframework/CCF/app/config/database.config.php

@@ -0,0 +1,24 @@
+<?php 
+/*
+ *---------------------------------------------------------------
+ * Database configuration
+ *---------------------------------------------------------------
+ */
+return array(
+	/*
+	 * the default database
+	 */
+	'main' =>  array(
+		// selected database
+		'db'	 => 'localhost',
+	
+		// driver
+		'driver' => 'mysql',
+	
+		// auth
+		'host'		=> '127.0.0.1',
+		'user' 		=> 'root',
+		'pass'		=> '',
+		'charset'	=> 'utf8'
+	),
+);

+ 27 - 0
frameworks/PHP/php-clancatsframework/CCF/app/config/main.config.php

@@ -0,0 +1,27 @@
+<?php 
+/*
+ *---------------------------------------------------------------
+ * Main CCF configuration
+ *---------------------------------------------------------------
+ *
+ * Take a look at the core main configuration file to see 
+ * what options are around. .../CCF/vendor/clancats/core/config/main.config.php
+ */
+return array(
+	
+	/*
+	 * URL configuration
+	 */
+	'url'	=> array(
+		// if not in the root directory set the path offset.
+		'path'		=> '/',
+	),
+	
+	/*
+	 * Security
+	 */
+	'security' => array(
+		// it is really important that you choose your own one!
+		'salt' => '7Q[„YI[œ1<-2S3Ck[%¼Sz59vQ!sl1aœÃ',
+	),
+);

+ 40 - 0
frameworks/PHP/php-clancatsframework/CCF/app/config/router.config.php

@@ -0,0 +1,40 @@
+<?php 
+/*
+ *---------------------------------------------------------------
+ * Router map configuration
+ *---------------------------------------------------------------
+ *
+ * You can use this configuration to set you default application
+ * routes.
+ */
+return array(
+	
+	/**
+	 * JSON response benchmark
+	 */
+	'json' => function() 
+	{
+		return CCResponse::create( json_encode(
+			array('message' => 'Hello, World!')
+		), 200 )->header( 'Content-Type', 'application/json' );
+	},
+	
+	/**
+	 * JSON response benchmark
+	 */
+	'db' => function() 
+	{		
+		$queries = CCIn::get( 'queries', 1 );
+		$worlds = array();
+		
+		for($i = 0; $i < $queries; ++$i) 
+		{
+			$worlds[] = DB::select( 'World' )->find( mt_rand(1, 10000) );
+		}
+		
+		return CCResponse::create( json_encode( $worlds ) )
+			->header( 'Content-Type', 'application/json' );
+	},
+	
+	'fortunes' => 'Bench@fortunes',
+);

+ 78 - 0
frameworks/PHP/php-clancatsframework/CCF/app/console/app.php

@@ -0,0 +1,78 @@
+<?php namespace CCConsole; use CCCli;
+/**
+ * App console
+ * stuff to maintain your application
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class app extends \CCConsoleController {
+
+	/**
+	 * return an array with information about the script
+	 */
+	public function help() 
+	{
+		return array(
+			'name'	=> 'App',
+			'desc'	=> 'stuff to maintain your application',
+			'actions'	=> array(
+				'info'	=> 'show information about this application.',
+			),
+		);
+	}
+
+	/**
+	 * print information about this application
+	 *
+	 * @param array 		$params 
+	 */
+	public function action_info( $params ) 
+	{
+		
+		$app = \ClanCats::runtime();
+		
+		// print the app name
+		$this->line( \CCForge_Php::make( 'comment', 
+			$app::$name.PHP_EOL.
+			"*".PHP_EOL.
+			"Running on ClanCatsFramework ".\ClanCats::VERSION.PHP_EOL.
+			"© 2010 - ".date('Y')." ClanCats GmbH".PHP_EOL
+		), 'cyan' );
+		
+		// list printer
+		$list = function( $array )
+		{
+			foreach( $array as $key => $value )
+			{
+				$this->line( $key.': '.CCCli::color( $value, 'green' ) );
+			}
+		};	
+		
+		// print info
+		$list( array(
+			'Runtime Class'		=> $app,
+			'CCF Version'		=> \ClanCats::version(),
+			'CCF Environment'	=> \ClanCats::environment(),
+			'Development env'	=> var_export( \ClanCats::in_development(), true ),
+			'File extention'		=> EXT,
+			'Core namespace'		=> CCCORE_NAMESPACE,
+		));
+		
+		// paths
+		$this->line( PHP_EOL."Paths", 'cyan' );
+		$list( \ClanCats::paths() );
+		
+		// paths
+		$this->line( PHP_EOL."Directories", 'cyan' );
+		$list( \ClanCats::directories() );
+		
+		// autoloader
+		$this->line( PHP_EOL."Autoloader - namespaces", 'cyan' );
+		$list( \CCFinder::$namespaces );
+	}
+}

+ 137 - 0
frameworks/PHP/php-clancatsframework/CCF/app/controllers/AuthController.php

@@ -0,0 +1,137 @@
+<?php
+/*
+ *---------------------------------------------------------------
+ * Basic Authentication Controller
+ *---------------------------------------------------------------
+ * 
+ * This is an example that should help to explain how CCAuth 
+ * and a basic controller works. Feel free to edit.
+ */
+class AuthController extends CCViewController 
+{	
+	/**
+	 * Sign in action
+	 *
+	 * @return CCResponse
+	 */
+	public function action_sign_in() 
+	{
+		// Here we set the page topic seen in the title tag
+		$this->theme->topic = __( ':action.topic' );
+		
+		// lets assign the view. Instead of getting the view directly
+		// using CCView::create( 'path/to/view' ) we get the view from the
+		// theme this allows us to have a diffrent sign_in for every theme.
+		// If the view does not exist in the theme it will load the view from 
+		// the default view folder.
+		$this->view = $this->theme->view( 'auth/sign_in.view' );
+		
+		$this->view->last_identifier = CCIn::post( 'identifier' );
+		
+		// By checking the HTTP method we figure out if this is a post request or not.
+		if ( CCIn::method( 'post' ) )
+		{
+			// Validate the data and get the user object.
+			// We use the key "identifier" because you can configure on
+			// what fields  the user is able to login. You could add for example
+			// the username or the customer number etc.
+			if ( $user = CCAuth::validate( CCIn::post( 'identifier' ), CCIn::post( 'password' ) ) )
+			{
+				// sign in the user with the current session.
+				CCAuth::sign_in( $user );
+				
+				// flash a success message to the user that he has been
+				// logged in succesfully.
+				UI\Alert::flash( 'success', __( ':action.message.success' ) );
+				
+				// Redirect the user back to the url where he came from
+				// this will only work when the next get parameter is set.
+				return CCRedirect::next();
+			}
+			
+			// If we could not recive a user object the login data were clearly invalid.
+			UI\Alert::add( 'danger', __( ':action.message.invalid' ) );
+		}
+	}
+	
+	/**
+	 * Sign up action
+	 *
+	 * @return CCResponse
+	 */
+	public function action_sign_up() 
+	{
+		// When the user is already authenticated we redirect him home.
+		if ( CCAuth::valid() )
+		{
+			return CCRedirect::to( '/' );
+		}
+		
+		$this->theme->topic = __( ':action.topic' );
+		$this->view = $this->theme->view( 'auth/sign_up.view' );
+		
+		// create a new user object as data holder
+		$user = new User;
+		
+		// bind the newly created user object to our view
+		$this->view->bind( 'user', $user );
+		
+		if ( CCIn::method( 'post' ) )
+		{
+			// Lets assign the email and the password to our 
+			// user object using the stirct assign method wich 
+			// will ignore all other post values in the assing process.
+			$user->strict_assign( array( 'email', 'password' ), CCIn::all( 'post' ) );
+			
+			$validator = CCValidator::post();
+			
+			// assign the labels to the validator this way we get 
+			// correct translated error messages.
+			$validator->label( array(
+				'email' => __( 'model/user.label.email' ),
+				'password' => __( 'model/user.label.password' ),
+				'password_match' => __( 'model/user.label.password_match' )
+			));
+			
+			// does the user already exist
+			$validator->set( 'same_email', User::find( 'email', $user->email ) );
+			$validator->message( __(':action.message.email_in_use'), 'negative', 'same_email' );
+			
+			// validate the other fields
+			$validator->rules( 'email', 'required', 'email' );
+			$validator->rules( 'password', 'required', 'min:6' );
+			$validator->rules( 'password_match', 'required', 'match:password' );
+			
+			// when the data passes the validation
+			if ( $validator->success() )
+			{
+				// because the user input is correct we can now save the 
+				// object to the database and sign the user in.
+				$user->save();
+				
+				CCAuth::sign_in( $user );
+				
+				UI\Alert::flash( 'success', __( ':action.message.success' ) );
+				
+				return CCRedirect::to( '/' );
+			}
+			else
+			{
+				UI\Alert::add( 'danger', $validator->errors() );
+			}
+		}
+	}
+	
+	/**
+	 * Sign out action
+	 */
+	public function action_sign_out() 
+	{
+		if ( !CCSession::valid_fingerprint() )
+		{
+			return CCRedirect::to( '/' );
+		}
+		
+		CCAuth::sign_out(); return CCRedirect::to( '/' );
+	}
+}

+ 33 - 0
frameworks/PHP/php-clancatsframework/CCF/app/controllers/BenchController.php

@@ -0,0 +1,33 @@
+<?php
+class BenchController extends CCController 
+{	
+	/**
+	 * Sign out action
+	 */
+	public function action_fortunes() 
+	{
+		$view = CCView::create( 'bench/fortune' );
+		
+		$fortunes = DB::select( 'Fortune' )->run();
+		
+		$runtimeFortune = new stdClass;
+		$runtimeFortune->id = 0;
+		$runtimeFortune->message = 'Additional fortune added at request time.';
+		
+		$fortunes[] = $runtimeFortune;
+		
+		usort($fortunes, function($left, $right) {
+			if ($left->message === $right->message) {
+				return 0;
+			} else if ($left->message > $right->message) {
+				return 1;
+			} else {
+				return -1;
+			}
+		});
+		
+		$view->fortunes = $fortunes;
+		
+		return CCResponse::create( $view->render() );
+	}
+}

+ 29 - 0
frameworks/PHP/php-clancatsframework/CCF/app/controllers/ErrorController.php

@@ -0,0 +1,29 @@
+<?php 
+/**
+ * ErrorController
+ **
+ * 
+ * @package       CCFApplication
+ * @author        Mario Döring <[email protected]>
+ * @version       1.0.0
+ * @copyright     2010 - 2014 ClanCats GmbH
+ *
+ */
+class ErrorController extends \CCController
+{
+	/**
+	 * Not found 404
+	 */
+	public function action_404() 
+	{
+		return CCResponse::create( CCView::create( 'Core::CCF/404' )->render(), 404 );
+	}
+	
+	/**
+	 * Server error 500
+	 */
+	public function action_500() 
+	{
+		return CCResponse::create( CCView::create( 'Core::CCF/500' )->render(), 500 );
+	}
+}

+ 34 - 0
frameworks/PHP/php-clancatsframework/CCF/app/controllers/WelcomeController.php

@@ -0,0 +1,34 @@
+<?php
+/**
+ * Welcome Controller
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class WelcomeController extends CCViewController 
+{	
+	/**
+	 * The default theme
+	 * if you wish you can render this controller with a special theme
+	 *
+	 * @var string
+	 */
+	protected $_theme = null;
+	
+	/**
+	 * the index function is just "function <controller_name>Index() {}" 
+	 */
+	public function action_index() 
+	{
+		$this->theme->topic = "Willkommen";
+		
+		$this->view = $this->theme->view( 'welcome', array(
+			'runtime_name'	=> ClanCats::runtime( 'name' ),
+			'environment'	=> ClanCats::environment(),
+		));
+	}
+}

+ 10 - 0
frameworks/PHP/php-clancatsframework/CCF/app/language/de-de/controller/auth.php

@@ -0,0 +1,10 @@
+<?php return array(
+	'sign_in.topic'	=> 'Einloggen',
+	'sign_in.message.invalid' => 'Der Benutzername und das Passwort stimmen nicht überein.',
+	'sign_in.message.success' => 'Willkommen zurück!',
+	
+	'sign_up.topic'	=> 'Registrieren',
+	'sign_up.submit'	=> 'Konto Erstellen',
+	'sign_up.message.email_in_use' => 'Diese E-Mail Adresse wird bereits verwendet.',
+	'sign_up.message.success' => 'Ihr Konto wurde erfolgreich erstellt.',
+);

+ 7 - 0
frameworks/PHP/php-clancatsframework/CCF/app/language/de-de/model/user.php

@@ -0,0 +1,7 @@
+<?php return array(
+	'label.name' => 'Name',
+	'label.email' => 'Email',
+	'label.password' => 'Passwort',
+	'label.password_match' => 'Wiederholen',
+	'label.retain' => 'Eingeloggt bleiben',
+);

+ 10 - 0
frameworks/PHP/php-clancatsframework/CCF/app/language/en-us/controller/auth.php

@@ -0,0 +1,10 @@
+<?php return array(
+	'sign_in.topic'	=> 'Sign In',
+	'sign_in.message.invalid' => 'Wrong email and password combination.',
+	'sign_in.message.success' => 'Welcome back!',
+	
+	'sign_up.topic'	=> 'Sign Up',
+	'sign_up.submit'	=> 'Create my account',
+	'sign_up.message.email_in_use' => 'This Email address is already in use.',
+	'sign_up.message.success' => 'Your account has been successfully created.',
+);

+ 7 - 0
frameworks/PHP/php-clancatsframework/CCF/app/language/en-us/model/user.php

@@ -0,0 +1,7 @@
+<?php return array(
+	'label.name' => 'Name',
+	'label.email' => 'Email',
+	'label.password' => 'Password',
+	'label.password_match' => 'Repeat',
+	'label.retain' => 'Remember me',
+);

+ 18 - 0
frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootpack/blueprint.json

@@ -0,0 +1,18 @@
+{
+	"name": "Twitter Bootstrap with packtacular",
+	"version": "3.0.3",
+	"description": "Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development.",
+	"homepage": "http://getbootstrap.com",
+	"keywords": [
+		"Twitter",
+		"Theme"
+	],
+	"license": "MIT",
+	"authors": [],
+	
+	"namespace": "Bootstrap",
+	
+	"wake": false,
+	"install": false,
+	"uninstall": false
+}

+ 36 - 0
frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootpack/classes/Theme.php

@@ -0,0 +1,36 @@
+<?php namespace Bootstrap;
+/**
+ * Bootstrap Theme
+ ** 
+ *
+ * @package		BootstrapTheme
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Theme extends \Packtacular\Theme
+{		
+	/**
+	 * Returns the current view namespace 
+	 * You need to implement this method in your theme subclass.
+	 *
+	 * @return string
+	 */
+	public static function view_namespace()
+	{
+		return __NAMESPACE__;	
+	}
+	
+	/**
+	 * This the current public namespace
+	 * By default its simply the PUBLICPATH/assets/<theme_namespace>
+	 * You need to implement this method in your theme subclass.
+	 *
+	 * @return string
+	 */
+	public static function public_namespace()
+	{
+		return "assets/".__NAMESPACE__.'/';
+	}
+}

+ 65 - 0
frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootpack/config/theme.config.php

@@ -0,0 +1,65 @@
+<?php
+/**
+ * CCFTheme default configuration
+ */
+return array(
+    
+    'default' => array(
+        
+        /*
+         * the topic gets added to the title
+         */
+        'topic'     => 'no title',
+        
+        /*
+         * the html title template
+         */
+        'title'     => '%s | '.ClanCats::runtime( 'name' ),
+        
+        /*
+         * the default html description
+         */
+        'description'   => 'Change your default description under theme.config -> defatul.description.',
+        
+        /*
+         * sidebar ( if false full container gets used )
+         */
+        'sidebar'	=> false,
+        
+        /*
+         * Footer appended scripts
+         * When you have a view specifc script you can append them to the js var just like:
+         *
+         *     $theme->capture_append( 'js', function() {
+	     *         echo "<script>console.log( 'hello world' );</script>";
+         *     });
+         */
+        'js' => '',
+    ), 
+    
+    /*
+     * Assets configuration
+     *
+     * you can pack files to gether:
+     * <holder>@<pack>
+     */
+	'assets' => array(
+		// load bootstrap core
+		'css/bootstrap.min.css'		=> 'theme@style',
+		'css/style.css'				=> 'theme@style',
+
+		// add mixins
+		'less/mixins/mixins.less'		=> 'theme@style',
+		'less/mixins/background.less'	=> 'theme@style',
+		'less/mixins/css3.less'			=> 'theme@style',
+		'less/mixins/transform.less'		=> 'theme@style',
+
+		// Main style
+		'less/style.less' 				=> 'theme@style',
+
+		// js core
+		'jquery.js'						=> 'vendor@lib',
+		'js/bootstrap.min.js'			=> 'theme@core',
+		'js/application.js'				=> 'theme@app',
+	)
+);

+ 95 - 0
frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootpack/views/layout.php

@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<meta charset="<?php echo ClanCats::$config->charset; ?>">
+	<meta name="viewport" content="width=device-width, initial-scale=1.0">
+	
+	<title><?php echo $title; ?></title>
+	<meta name="description" content="<?php echo $description; ?>">
+	
+	<!-- styling -->
+	<?php echo CCAsset::code( 'css', 'vendor' ); ?>
+	<?php echo CCAsset::code( 'css', 'theme' ); ?>
+	<?php echo CCAsset::code( 'css' ); ?>
+
+	<!--[if lt IE 9]>
+	  <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+	  <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+	<![endif]-->
+	
+	<!-- header scripts -->
+	<?php echo CCAsset::code( 'js', 'header' ); ?>
+</head>
+<body>
+<div id="header">
+	<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
+		<div class="container">
+			
+			<div class="navbar-header">
+				<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#ccf-navbar">
+					<span class="sr-only">Toggle navigation</span>
+					<span class="icon-bar"></span>
+					<span class="icon-bar"></span>
+					<span class="icon-bar"></span>
+				</button>
+				<a class="navbar-brand" href="<?php echo to('/'); ?>"><?php echo App::name(); ?></a>
+			</div>
+			
+			<div class="collapse navbar-collapse" id="ccf-navbar">
+				<ul class="nav navbar-nav">
+					<?php if ( CCAuth::valid() ) : ?>
+					<li>
+						<a href="<?php echo to( '@auth.sign_out' ); ?>">Sign Out</a>
+					</li>
+					<?php else : ?>
+					<li>
+						<a href="<?php echo to( '@auth.sign_in' ); ?>">Sign In</a>
+					</li>
+					<li>
+						<a href="<?php echo to( '@auth.sign_up' ); ?>">Sign Up</a>
+					</li>
+					<?php endif; ?>
+					
+					<?php if ( ClanCats::in_development() ) : ?>
+					<li class="dropdown">
+						<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dev Toolbox <b class="caret"></b></a>
+						<ul class="dropdown-menu">
+							<li><a href="<?php echo to('dev/session/'); ?>">Session Sandbox</a></li>
+							<li><a href="<?php echo to('dev/mail/'); ?>">Mail Sandbox</a></li>
+							<li><a href="<?php echo to('dev/common/phpinfo'); ?>">PHP Info</a></li>
+						</ul>
+					</li>
+					<?php endif; ?>
+				</ul>
+			</div><!-- /.navbar-collapse -->
+			
+		</div><!-- /.container-fluid -->
+	</nav>
+</div><!-- /#header -->
+
+<div id="main-container" class="container">
+	<?php echo UI\Alert::render(); ?>
+	<div>
+		<?php if ( $sidebar !== false ) : ?>
+		<div class="row">
+			<div class="col-md-3">
+				<?php echo $sidebar; ?>
+			</div>
+			<div class="col-md-9">
+				<?php echo $content; ?>
+			</div>
+		</div>
+		<?php else : ?>
+		<?php echo $content; ?>
+		<?php endif; ?>
+	</div>
+</div>
+
+<!-- footer scripts -->
+<?php echo CCAsset::code( 'js', 'vendor' ); ?>
+<?php echo CCAsset::code( 'js', 'theme' ); ?>
+<?php echo CCAsset::code( 'js' ); ?>
+<?php echo $js; ?>
+</body>
+
+</html>

+ 18 - 0
frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootstrap/blueprint.json

@@ -0,0 +1,18 @@
+{
+	"name": "Twitter Bootstrap",
+	"version": "3.0.3",
+	"description": "Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development.",
+	"homepage": "http://getbootstrap.com",
+	"keywords": [
+		"Twitter",
+		"Theme"
+	],
+	"license": "MIT",
+	"authors": [],
+	
+	"namespace": "Bootstrap",
+	
+	"wake": false,
+	"install": false,
+	"uninstall": false
+}

+ 81 - 0
frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootstrap/classes/Theme.php

@@ -0,0 +1,81 @@
+<?php namespace Bootstrap;
+/**
+ * Bootstrap Theme
+ ** 
+ *
+ * @package		BootstrapTheme
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Theme extends \CCTheme
+{	
+	/**
+	 * the theme configuration
+	 *
+	 * @var CCConfig
+	 */
+	public static $config = null;
+	
+	/**
+	 * static theme init
+	 *
+	 * @return void
+	 */
+	public static function _init()
+	{
+		static::$config = \CCConfig::create( __NAMESPACE__.'::theme' );
+	}
+	
+	/**
+	 * Returns the current view namespace 
+	 * You need to implement this method in your theme subclass.
+	 *
+	 * @return string
+	 */
+	public static function view_namespace()
+	{
+		return __NAMESPACE__;	
+	}
+	
+	/**
+	 * This the current public namespace
+	 * By default its simply the PUBLICPATH/assets/<theme_namespace>
+	 * You need to implement this method in your theme subclass.
+	 *
+	 * @return string
+	 */
+	public static function public_namespace()
+	{
+		return "assets/".__NAMESPACE__.'/';
+	}
+	
+	/**
+	 * custom theme render stuff
+	 *
+	 * @param string		$file
+	 * @return string
+	 */
+	public function render( $file = null ) 
+	{
+		foreach( static::$config->default as $key => $value )
+		{
+			$this->set( $key, $value );
+		}
+		
+		// assign the topic to the title
+		if ( $this->has( 'topic' ) && $this->has( 'title' ) )
+		{
+			$this->title = sprintf( $this->get( 'title' ), $this->get( 'topic' ) );
+		}
+		
+		// add default assets
+		\CCAsset::add( 'js/jquery/jquery.js' );
+		\CCAsset::add( 'js/bootstrap.min.js', 'theme' );
+		\CCAsset::add( 'css/bootstrap.min.css', 'theme' );
+		\CCAsset::add( 'css/style.css', 'theme' );
+		
+		return parent::render( $file );
+	}
+}

+ 29 - 0
frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootstrap/config/theme.config.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * CCFTheme default configuration
+ */
+return array(
+    
+    'default' => array(
+        
+        /*
+         * the topic gets added to the title
+         */
+        'topic'     => 'no title',
+        
+        /*
+         * the html title template
+         */
+        'title'     => '%s | '.ClanCats::runtime( 'name' ),
+        
+        /*
+         * the default html description
+         */
+        'description'   => 'Change your default description under theme.config -> defatul.description.',
+        
+        /*
+         * sidebar ( if false full container gets used )
+         */
+        'sidebar'	=> false,
+    ), 
+);

+ 45 - 0
frameworks/PHP/php-clancatsframework/CCF/app/themes/Bootstrap/views/layout.php

@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+  <head>
+	<meta charset="<?php echo ClanCats::$config->charset; ?>">
+	<meta name="viewport" content="width=device-width, initial-scale=1.0">
+	
+	<title><?php echo $title; ?></title>
+	<meta name="description" content="<?php echo $description; ?>">
+	
+	<!-- styling -->
+	<?php echo CCAsset::code( 'css', 'theme' ); ?>
+	<?php echo CCAsset::code( 'css' ); ?>
+
+	<!--[if lt IE 9]>
+	  <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+	  <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
+	<![endif]-->
+	
+	<!-- header scripts -->
+	<?php echo CCAsset::code( 'js', 'header' ); ?>
+  </head>
+  <body>
+	<div id="main-container" class="container">
+		<div>
+			<?php if ( $sidebar !== false ) : ?>
+			<div class="row">
+				<div class="col-md-3">
+					<?php echo $sidebar; ?>
+				</div>
+				<div class="col-md-9">
+					<?php echo $content; ?>
+				</div>
+			</div>
+			<?php else : ?>
+				<?php echo $content; ?>
+			<?php endif; ?>
+		</div>
+	</div>
+
+	<!-- footer scripts -->
+	<?php echo CCAsset::code( 'js', 'lib' ); ?>
+	<?php echo CCAsset::code( 'js', 'theme' ); ?>
+	<?php echo CCAsset::code( 'js' ); ?>
+  </body>
+</html>

+ 48 - 0
frameworks/PHP/php-clancatsframework/CCF/app/views/auth/sign_in.view.php

@@ -0,0 +1,48 @@
+{% use UI\Form; %}
+<div class="row">
+	<div class="col-md-6 col-md-offset-3">
+
+		<div class="row">
+			<div class="col-sm-offset-2 col-sm-10">
+				<h2>{{__( ':action.topic' )}}</h2>
+			</div>
+		</div>
+
+	<!-- sing in form -->
+	{{ Form::start( 'sign-in', array( 'method' => 'post', 'class' => 'form-horizontal' ) ) }}
+
+		<!-- identifier -->
+		<div class="form-group">
+			{{ Form::label( 'identifier', __( 'model/user.label.email' ) )->add_class( 'col-sm-2' ) }}
+			<div class="col-sm-10">
+		  		{{ Form::input( 'identifier', $last_identifier )
+		  			->placeholder( __( 'model/user.label.email' ) ) }}
+			</div>
+		</div>
+
+		<!-- password -->
+		<div class="form-group">
+			{{ Form::label( 'password', __( 'model/user.label.password' ) )->add_class( 'col-sm-2' ) }}
+			<div class="col-sm-10">
+		  		{{ Form::input( 'password', null, 'password' )
+		  			->placeholder( __( 'model/user.label.password' ) ) }}
+			</div>
+		</div>
+
+		<!-- remember me -->
+		<div class="form-group">
+			<div class="col-sm-offset-2 col-sm-10">
+				{{ Form::checkbox( 'retain', __( 'model/user.label.retain' ), true ) }}
+			</div>
+		</div>
+		
+		<!-- buttons -->
+		<div class="form-group">
+			<div class="col-sm-offset-2 col-sm-10">
+				<button type="submit" class="btn btn-primary">{{__( ':action.topic' )}}</button>
+			</div>
+		</div>
+		
+	{{ Form::end(); }}
+	</div>
+</div>

+ 50 - 0
frameworks/PHP/php-clancatsframework/CCF/app/views/auth/sign_up.view.php

@@ -0,0 +1,50 @@
+{% use UI\Form; %}
+<div class="row">
+	<div class="col-md-6 col-md-offset-3">
+
+		<div class="row">
+			<div class="col-sm-offset-2 col-sm-10">
+				<h2>{{__( ':action.topic' )}}</h2>
+			</div>
+		</div>
+
+	<!-- sing in form -->
+	{{ Form::start( 'sign-in', array( 'method' => 'post', 'class' => 'form-horizontal' ) ) }}
+
+		<!-- identifier -->
+		<div class="form-group">
+			{{ Form::label( 'email', __( 'model/user.label.email' ) )->add_class( 'col-sm-2' ); }}
+			<div class="col-sm-10">
+		  		{{ Form::input( 'email', $user->email, 'email' )
+		  			->placeholder( __( 'model/user.label.email' ) ); }}
+			</div>
+		</div>
+
+		<!-- password -->
+		<div class="form-group">
+			{{ Form::label( 'password', __( 'model/user.label.password' ) )->add_class( 'col-sm-2' ); }}
+			<div class="col-sm-10">
+		  		{{ Form::input( 'password', null, 'password' )
+		  			->placeholder( __( 'model/user.label.password' ) ); }}
+			</div>
+		</div>
+
+		<!-- password match -->
+		<div class="form-group">
+			{{ Form::label( 'password_match', __( 'model/user.label.password_match' ) )->add_class( 'col-sm-2' ); }}
+			<div class="col-sm-10">
+		  		{{ Form::input( 'password_match', null, 'password' )
+		  			->placeholder( __( 'model/user.label.password_match' ) ); }}
+			</div>
+		</div>
+
+		<!-- buttons -->
+		<div class="form-group">
+			<div class="col-sm-offset-2 col-sm-10">
+				<button type="submit" class="btn btn-primary">{{__( ':action.submit' )}}</button>
+			</div>
+		</div>
+
+	{{ Form::end() }}
+	</div>
+</div>

+ 24 - 0
frameworks/PHP/php-clancatsframework/CCF/app/views/bench/fortune.php

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>Fortunes</title>
+</head>
+<body>
+
+<table>
+	<tr>
+		<th>id</th>
+		<th>message</th>
+	</tr>
+
+	<?php foreach($fortunes as $fortune): ?>
+	<tr>
+		<td><?php echo $fortune->id; ?></td>
+		<td><?php echo $fortune->message; ?></td>
+	</tr>
+	<?php endforeach; ?>
+
+</table>
+
+</body>
+</html>

+ 6 - 0
frameworks/PHP/php-clancatsframework/CCF/app/views/welcome.php

@@ -0,0 +1,6 @@
+<h1>Wilkommen bei CCF 2.0</h1>
+<strong>Running: </strong><?php echo $runtime_name; ?>
+<br>
+<strong>Environment: </strong><?php echo $environment; ?>
+
+<hr>

+ 72 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/CCAuth.php

@@ -0,0 +1,72 @@
+<?php namespace Auth;
+/**
+ * Auth interface
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCAuth 
+{
+	/** 
+	 * Get an auth handler
+	 *
+	 * @param string		$name	The auth instance
+	 * @return bool
+	 */
+	public static function handler( $name = null )
+	{
+		return Handler::create( $name );
+	}
+	
+	/** 
+	 * Check if the login is valid
+	 *
+	 * @param string		$name	The auth instance
+	 * @return bool
+	 */
+	public static function valid( $name = null )
+	{
+		return Handler::create( $name )->valid();
+	}
+	
+	/** 
+	 * Validate user credentials
+	 *
+	 * @param mixed		$identifier
+	 * @param string		$password
+	 * @param string		$name			The auth instance
+	 * @return bool
+	 */
+	public static function validate( $identifier, $password, $name = null )
+	{
+		return Handler::create( $name )->validate( $identifier, $password );
+	}
+	
+	/** 
+	 * Sign in a user
+	 *
+	 * @param Auth\User		$user
+	 * @param string			$keep_loggedin
+	 * @param string			$name				The auth instance
+	 * @return bool
+	 */
+	public static function sign_in( \Auth\User $user, $keep_loggedin = true, $name = null )
+	{
+		return Handler::create( $name )->sign_in( $user, $keep_loggedin );
+	}
+	
+	/** 
+	 * Sign out a user
+	 *
+	 * @param string			$name				The auth instance
+	 * @return false
+	 */
+	public static function sign_out( $name = null )
+	{
+		return Handler::create( $name )->sign_out();
+	}
+}

+ 12 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/Exception.php

@@ -0,0 +1,12 @@
+<?php namespace Auth;
+/**
+ * Auth Exception
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Exception extends \Core\CCException {}

+ 39 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/Group.php

@@ -0,0 +1,39 @@
+<?php namespace Auth;
+/**
+ * Group
+ **
+ * 
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Group extends \DB\Model
+{
+	/**
+	 * The database table name
+	 * 
+	 * @var string
+	 */
+	protected static $_table = 'auth_groups';
+	
+	/**
+	 * Let the model automatically set created_at and modified_at 
+	 *
+	 * @var bool
+	 */
+	protected static $_timestamps = true;
+	
+	/**
+	 * The  default properties
+	 * 
+	 * @var string
+	 */
+	protected static $_defaults = array(
+		'id',
+		'name' => array( 'string', '' ),
+		'created_at' => array( 'int', 0 ),
+		'modified_at' => array( 'int', 0 )
+	);
+}

+ 476 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/Handler.php

@@ -0,0 +1,476 @@
+<?php namespace Auth;
+/**
+ * Auth instnace handler 
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+ 
+use Core\CCCookie;
+ 
+class Handler
+{
+	/**
+	 * Instance holder
+	 *
+	 * @var array
+	 */
+	protected static $_instances = array();
+
+	/**
+	 * Default auth instance name
+	 *
+	 * @var string
+	 */
+	private static $_default = 'main';
+
+	/**
+	 * Get an auth instance or create one
+	 *
+	 * @param string			$name
+	 * @param array 			$conf	You can pass optionally a configuration directly. This will overwrite.
+	 * @return Auth_Handler
+	 */
+	public static function create( $name = null, $conf = null ) 
+	{
+		if ( is_null( $name ) ) 
+		{
+			$name = static::$_default;
+		}
+		
+		if ( !is_null( $conf ) && is_array( $conf ) )
+		{
+			return static::$_instances[$name] = new static( $name, $conf );
+		}
+		
+		if ( !isset( static::$_instances[$name] ) )
+		{
+			static::$_instances[$name] = new static( $name );
+		}
+		
+		return static::$_instances[$name];
+	}
+	
+	/**
+	 * Kill an instance to force the handler to redo the construction
+	 *
+	 * @return void
+	 */
+	public static function kill_instance( $name )
+	{
+		if ( array_key_exists( $name, static::$_instances ) )
+		{
+			unset( static::$_instances[$name] );
+		}
+	}
+	
+	/**
+	 * the user object
+	 *
+	 * @var DB\Model
+	 */
+	public $user = null;
+	
+	/**
+	 * is the instance authenticated
+	 *
+	 * @var bool
+	 */
+	protected $authenticated = false;
+	
+	/**
+	 * The auth handler name
+	 *
+	 * @var string
+	 */
+	protected $name = null;
+	
+	/**
+	 * The auth config array
+	 *
+	 * @var string
+	 */
+	protected $config = null;
+	
+	/**
+	 * The used session manager
+	 *
+	 * @var string
+	 */
+	public $session = null;
+	
+	/**
+	 * Auth instance constructor
+	 *
+	 * @param string 		$name
+	 * @param array 			$config
+	 * @return void
+	 */
+	public function __construct( $name, $config = null ) 
+	{	
+		if ( is_null( $config ) )
+		{
+			$config = \CCConfig::create( 'auth' )->get( $name );
+			
+			// check for an alias. If you set a string 
+			// in your config file we use the config 
+			// with the passed key.
+			if ( is_string( $config ) ) 
+			{
+				$config = \CCConfig::create( 'auth' )->get( $config );
+			}
+		}
+		
+		if ( !is_array( $config ) )
+		{
+			throw new Exception( "Auth\\Handler::create - Invalid auth handler (".$name.")." );
+		}
+		
+		// also don't forget to set the name manager name becaue we need him later.
+		$this->name = $name;
+		
+		
+		// assign defaults and create the configuration object
+		$this->config = \CCDataObject::assign( \CCArr::merge( array(
+			
+			// Wich session manager should be used?
+			// null = default session manager
+			'session_manager' => null,
+			
+			// On wich field should the current logged in user
+			// id be saved in the session?
+			'session_key' => 'user_id',
+			
+			// On wich field do we select the user for 
+			// the authentification
+			'user_key' => 'id',
+			
+			// The User model class
+			'user_model' => "\\Auth\\User",
+			
+			// the identifiers wich fields should be allowed 
+			// to select the user object on validation.
+			'identifiers' => array(
+				'email'
+			),
+			
+			// Where to store the active logins
+			// how long do they stay active etc.
+			'logins' => array(
+			
+				// the logins db handlerw
+				'handler' => null,
+			
+				// the logins db table
+				'table' => 'auth_logins',
+			),
+			
+			// login restoring settings
+			'restore' => array(
+				
+				// the user id key cookie name
+				'id_cookie' => 'ccauth-restore-id',
+				
+				// the user restore token cookie name
+				'token_cookie' => 'ccauth-restore-token',
+				
+				// the restore key lifetime
+				'lifetime' => \CCDate::months(1),
+			),
+			
+		), $config ));
+				
+		// set the session handler
+		$this->session = \CCSession::manager( $this->config->session_manager );
+		
+		$user_model = $this->config->user_model;
+		
+		// set a empty default user object to avoid
+		// on a non object errors
+		$this->user = new $user_model;
+		
+		// do we already have a user id means are we
+		// logged in?
+		if ( !is_null( $session_key = $this->session_user_id() ) )
+		{
+			if ( $user = $user_model::find( $this->config->user_key, $session_key ) )
+			{
+				$this->user = $user; return $this->authenticated = true;
+			}
+		}
+		// When no session key / user id is given try to restore 
+		// the login using the login keepers
+		else 
+		{
+			$restore_id_cookie = $this->config->get( 'restore.id_cookie' );
+			$restore_token_cookie = $this->config->get( 'restore.token_cookie' );
+			
+			if 
+			(
+				CCCookie::has( $restore_id_cookie ) &&
+				CCCookie::has( $restore_token_cookie )
+			) 
+			{
+				// get the restore cookies
+				$restore_id = CCCookie::get( $restore_id_cookie );
+				$restore_token = CCCookie::get( $restore_token_cookie );
+	
+				// get the restore login
+				$login = $this->select_logins()
+					->where( 'restore_id', $restore_id )
+					->where( 'restore_token', $restore_token )
+					->limit( 1 );
+	
+				// if no login found kill the cookies and return
+				if ( !$login = $login->run() ) 
+				{
+					$this->kill_restore();
+					return $this->authenticated = false;
+				}
+	
+				// Invalid user? kill the cookies and return
+				if ( !$user = $user_model::find( $this->config->user_key, $restore_id ) )
+				{
+					$this->kill_restore();
+					return $this->authenticated = false;
+				}
+	
+				// validate the restore key if invalid 
+				// once again kill the cookies and return
+				if ( $login->restore_token != $this->restore_key( $user ) ) 
+				{
+					$this->kill_restore();
+					return $this->authenticated = false;
+				}
+	
+				// If everything is fine sign the user in and 
+				// update the restore keys
+				$this->sign_in( $user, true );
+				
+				return $this->authenticated = true;
+			}
+		}
+	
+		return $this->authenticated = false;
+	}
+	
+	/**
+	 * Kill the restore keys
+	 *
+	 * @return void
+	 */
+	public function kill_restore()
+	{
+		CCCookie::delete( $this->config->get( 'restore.id_cookie' ) );
+		CCCookie::delete( $this->config->get( 'restore.token_cookie' ) );
+	}
+	
+	/**
+	 * Is this login valid?
+	 *
+	 * @return bool
+	 */
+	public function valid()
+	{
+		return $this->authenticated;
+	}
+	
+	/**
+	 * Get the current user session user id 
+	 * 
+	 * @return mixed
+	 */
+	public function session_user_id() 
+	{
+		return $this->session->get( $this->config->session_key );
+	}
+	
+	/**
+	 * generate the current restore key
+	 *
+	 * @param User	$user
+	 * @return string
+	 */
+	public function restore_key( $user ) 
+	{
+		return \CCStr::hash( $user->password.'@'.$user->{$this->config->user_key}.'%'.\CCIn::client()->ip );
+	}
+	
+	/**
+	 * Select from logins
+	 *
+	 * @return DB\Query_Select
+	 */
+	private function select_logins()
+	{
+		return \DB::select( 
+			$this->config->get( 'logins.table' ), 
+			array(),
+			$this->config->get( 'logins.handler' )
+		);
+	}
+	
+	/**
+	 * Get the current login of the user
+	 *
+	 * @return stdObject|null
+	 */
+	public function login()
+	{
+		return $this->select_logins()
+			->where( 'restore_id', $this->user->{$this->config->user_key} )
+			->where( 'restore_token', $this->restore_key( $this->user ) )
+			->limit( 1 )
+			->run();
+	}
+	
+	/**
+	 * Validate an identifier with the password 
+	 * In other words is the login correct?
+	 *
+	 * @param string 	$identifier
+	 * @param string 	$password
+	 * @return mixed  	false on failure, user object on success
+	 */
+	public function validate( $identifier, $password ) 
+	{
+		$user = null;
+		$user_model = $this->config->user_model;
+		
+		foreach( $this->config->identifiers as $property ) 
+		{
+			if ( $user = $user_model::find( $property, $identifier ) ) 
+			{
+				break;
+			} 
+		}
+	
+		// when could not find a user matching the identifiers return false
+		if ( !$user ) 
+		{
+			return false;
+		}
+		
+		// when the passwords match return the user object
+		if ( \CCStr::verify_hash( $password, $user->password )) 
+		{
+			return $user;
+		}
+	
+		// otherwise return false
+		return false;
+	}
+	
+	/**
+	 * Sign the user and optinal also set the resore keys
+	 *
+	 * @param Auth\User  	$user	
+	 * @param bool			$keep_login
+	 * @return bool
+	 */
+	public function sign_in( \Auth\User $user, $keep_login = true ) 
+	{
+		// set the session key so the session knows we are logged in
+		$this->session->set( $this->config->session_key, $user->{$this->config->user_key} );
+		
+		// update the current user object
+		$this->user = $user;
+		
+		// update the last login timestamp
+		$this->user->last_login = time();
+		
+		// pass the user trough the events to allow modifications
+		// of the user object at sign in
+		$this->user = \CCEvent::pass( 'auth.sign_in', $this->user );
+		
+		// save the user object to the database 
+		$this->user->save();
+		
+		// set the restore keys to keep the login
+		// after the session ends
+		if ( $keep_login ) 
+		{
+			$restore_id_cookie = $this->config->get( 'restore.id_cookie' );
+			$restore_token_cookie = $this->config->get( 'restore.token_cookie' );
+			
+			$restore_lifetime = $this->config->get( 'restore.lifetime' );
+			
+			$restore_id = $this->session->get( $this->config->session_key );
+			$restore_token = $this->restore_key( $this->user );
+			
+			CCCookie::set( $restore_id_cookie, $restore_id, $restore_lifetime );
+			CCCookie::set( $restore_token_cookie, $restore_token, $restore_lifetime );
+			
+			// try to get the current login
+			$login = $this->select_logins()
+				->where( 'restore_id', $restore_id )
+				->where( 'restore_token', $restore_token );
+		
+			// prepare the login data
+			$login_data = array(
+				'restore_id' 		=> $restore_id,
+				'restore_token' 		=> $restore_token,
+				'last_login' 		=> time(),
+				'client_agent'		=> \CCIn::client()->agent,
+			);
+			
+			// pass the login data trough the events
+			$login_data = \CCEvent::pass( 'auth.store_login', $login_data );
+			
+			// if there is no such login create a new one
+			if ( !$login->run() ) 
+			{
+				\DB::insert( $this->config->get( 'logins.table' ), $login_data )
+					->run( $this->config->get( 'logins.handler' ) );
+			}
+			else 
+			{
+				\DB::update( $this->config->get( 'logins.table' ), $login_data )
+					->where( 'restore_id', $restore_id )
+					->where( 'restore_token', $restore_token )
+					->run( $this->config->get( 'logins.handler' ) );
+			}
+		}
+		
+		// and finally we are authenticated
+		return $this->authenticated = true;
+	}
+	
+	/**
+	 * Sign a user out
+	 *
+	 * @param id  				$user_id	
+	 * @param string				$name
+	 * @return false
+	 */
+	public function sign_out() {
+	
+		if ( !$this->authenticated ) 
+		{
+			return false;
+		}
+	
+		// remove the restore login
+		\DB::delete( $this->config->get( 'logins.table' ) )
+			->where( 'restore_token', $this->restore_key( $this->user ) )
+			->run();
+	
+		// logout the user
+		$this->session->delete( $this->config->session_key );
+
+		// pass the user object through all user hooks
+		$this->user = \CCEvent::pass( 'auth.sign_out', $this->user );
+		$this->user->save();
+	
+		$user_model = $this->config->user_model;
+		
+		// create new empty user
+		$this->user = new $user_model;
+	
+		return $this->authenticated = false;
+	}
+}

+ 84 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Auth/User.php

@@ -0,0 +1,84 @@
+<?php namespace Auth;
+/**
+ * User model
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class User extends \DB\Model
+{
+	/**
+	 * Hidden fields
+	 */
+	protected static $_hidden = array( 'password' );
+	
+	/*
+	 * Let the model automatically set created_at and modified_at 
+	 */
+	protected static $_timestamps = true;
+	
+	/**
+	 * The user model defaults
+	 *
+	 * @var array
+	 */
+	protected static $_defaults = array(
+		'id'	,
+		'active'			=> array( 'bool', true ),
+		'group_id'		=> 0,
+		'email'			=> null,
+		'password'		=> null,
+		'storage'		=> array( 'json', array() ),
+		'last_login'		=> array( 'timestamp', 0 ),
+		'created_at'		=> array( 'timestamp' ),
+		'modified_at'	=> array( 'timestamp' ),
+	);
+	
+	/**
+	 * Get the user group
+	 *
+	 * @return \Auth\Group
+	 */
+	public function group()
+	{
+		return $this->belongs_to( '\\Auth\\Group' );
+	}
+	
+	/**
+	 * Is the user an administrator?
+	 * We simply check the group_id in our default configuration
+	 * the user id of the admin group is 100.
+	 *
+	 * @return bool
+	 */
+	public function is_admin()
+	{
+		return $this->group_id == 100;
+	}
+	
+	/**
+	 * Always hash the passwort 
+	 * 
+	 * @param string 		$password
+	 * @return string
+	 */
+	protected function _set_modifier_password( $password )
+	{
+		return \CCStr::hash( $password );
+	}
+	
+	/**
+	 * The email should only contain lower case characters
+	 * 
+	 * @param string 		$email
+	 * @return string
+	 */
+	protected function _set_modifier_email( $email )
+	{
+		return strtolower( $email );
+	}
+}

+ 409 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Builder.php

@@ -0,0 +1,409 @@
+<?php namespace DB;
+/**
+ * The Query builder
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Builder
+{
+	/**
+	 * The query parameters 
+	 *
+	 * @var array
+	 */
+	public $parameters = array();
+	
+	/**
+	 * The escape pattern escapes table column names etc. 
+	 * select * from `table`...
+	 *
+	 * @var string
+	 */
+	protected $escape_pattern = '`%s`';
+	
+	/**
+	 * Clear all set parameters
+	 *
+	 * @return void
+	 */
+	public function clear_parameters()
+	{
+		$this->parameters = array();
+	}
+	
+	/**
+	 * Adds a parameter to the builder
+	 *
+	 * @return void
+	 */
+	public function add_parameter( $value )
+	{
+		$this->parameters[] = $value;
+	}
+	
+	/**
+	 * creates an parameter and adds it
+	 *
+	 * @param mixed 		$value
+	 * @return string
+	 */
+	public function param( $value )
+	{
+		if ( !\DB::is_expression( $value ) )
+		{
+			$this->add_parameter( $value ); return '?';
+		}
+		return $value;
+	}
+	
+	/**
+	 * Filters the parameters removes the keys and Expressions
+	 *
+	 * @param array 		$parameters
+	 * @return array
+	 */
+	public function filter_parameters( $parameters )
+	{
+		return array_values( array_filter( $parameters, function( $item ) {
+			return !\DB::is_expression( $item );
+		}));
+	}
+	
+	/**
+	 * Escape / wrap an string for sql
+	 *
+	 * @param string|Expression        $string
+	 */
+	public function escape( $string )
+	{	
+		if ( \DB::is_expression( $string ) ) 
+		{
+			return $string->value;
+		}
+		
+		// the string might contain an 'as' statement that we wil have to split.
+		if ( strpos( $string, ' as ' ) !== false )
+		{
+			$string = explode( ' as ', $string );
+			return $this->escape( trim( $string[0] ) ).' as '. $this->escape( trim( $string[1] ) );
+		}
+		
+		// it also might contain dott seperations we have to split
+		if ( strpos( $string, '.' ) !== false )
+		{
+			$string = explode( '.', $string );
+			
+			foreach( $string as $key => $item )
+			{
+				$string[$key] = $this->escape_string( $item );
+			}
+			
+			return implode( '.' , $string );
+		}
+		
+		return $this->escape_string( $string );
+	}
+	
+	/**
+	 * Escape a single string without checking for as and dots
+	 *
+	 * @param string 	$string
+	 * @return string
+	 */
+	public function escape_string( $string )
+	{
+		return sprintf( $this->escape_pattern, $string );
+	}
+	
+	/**
+	 * Escape an array of items an seprate them with a comma
+	 *
+	 * @param array 		$array
+	 * @return string
+	 */
+	public function escape_list( $array )
+	{
+		foreach( $array as $key => $item )
+		{
+			$array[$key] = $this->escape( $item );
+		}
+		
+		return implode( ', ', $array );
+	}
+	
+	/**
+	 * Escape the table 
+	 *
+	 * @param Query		$query
+	 * @return string
+	 */
+	public function escape_table( &$query )
+	{
+		$table = $query->table;
+		
+		if ( is_array( $table ) )
+		{
+			reset($table); $table = key($table).' as '.$table[key($table)];
+		}
+		
+		return $this->escape( $table );
+	}
+	
+	/**
+	 * Convert data to parameters and bind them to the query
+	 *
+	 * @param array 		$params
+	 * @return string
+	 */
+	public function parameterize( $params )
+	{
+		foreach( $params as $key => $param )
+		{
+			$params[$key] = $this->param( $param );
+		}
+		
+		return implode( ', ', $params );
+	}
+
+	/**
+	 * Build an insert query
+	 *
+	 * @param Query     $query
+	 * @return string
+	 */
+	public function compile_insert( &$query )
+	{
+		$build = ( $query->ignore ? 'insert ignore' : 'insert' ).' into '.$this->escape_table( $query ).' ';
+		
+		$value_collection = $query->values;
+		
+		// Get the array keys from the first array in the collection.
+		// We use them as insert keys.
+		$build .= '('.$this->escape_list( array_keys( reset( $value_collection ) ) ).') values ';
+		
+		// add the array values.
+		foreach( $value_collection as $values )
+		{
+			$build .= '('.$this->parameterize( $values ).'), ';
+		}
+		
+		// cut the last comma away
+		return substr( $build, 0, -2 );
+	}
+	
+	/**
+	 * Build an update query
+	 *
+	 * @param Query     $query
+	 * @return string
+	 */
+	public function compile_update( &$query )
+	{
+		$build = 'update '.$this->escape_table( $query ).' set ';
+		
+		// add the array values.
+		foreach( $query->values as $key => $value )
+		{
+			$build .= $this->escape( $key ).' = '.$this->param( $value ).', ';
+		}
+		
+		$build = substr( $build, 0, -2 );
+		$build .= $this->compile_where( $query );
+		$build .= $this->compile_limit( $query );
+		
+		// cut the last comma away
+		return $build;
+	}
+	
+	/**
+	 * Build an delete query
+	 *
+	 * @param Query     $query
+	 * @return string
+	 */
+	public function compile_delete( &$query )
+	{
+		$build = 'delete from '.$this->escape_table( $query );
+		
+		$build .= $this->compile_where( $query );
+		$build .= $this->compile_limit( $query );
+		
+		// cut the last comma away
+		return $build;
+	}
+	
+	/**
+	 * Build a select
+	 *
+	 * @param Query     $query
+	 * @return string
+	 */
+	public function compile_select( &$query )
+	{
+		$build = ( $query->distinct ? 'select distinct' : 'select' ).' ';
+		
+		if ( !empty( $query->fields ) )
+		{
+			foreach( $query->fields as $key => $field )
+			{
+				if ( !is_numeric( $key ) )
+				{
+					$build .= $this->escape( $key ).' as '.$this->escape( $field );
+				}
+				elseif ( is_array( $field ) )
+				{
+					$build .= $this->escape( $field[0] ).' as '.$this->escape( $field[1] );
+				}
+				else 
+				{
+					$build .= $this->escape( $field );
+				}
+				$build .= ', ';
+			}
+			
+			$build = substr( $build, 0, -2 );
+		}
+		else 
+		{
+			$build .= '*';
+		}
+		
+		// append the table
+		$build .= ' from '.$this->escape_table( $query );
+		
+		// build the where stuff
+		$build .= $this->compile_where( $query );
+		$build .= $this->compile_group( $query );
+		$build .= $this->compile_order( $query );
+		$build .= $this->compile_limit_with_offset( $query );
+		
+		return $build;
+	}
+	
+	/**
+	 * Build the where part
+	 *
+	 * @param Query 		$query
+	 * @return string
+	 */
+	public function compile_where( &$query )
+	{
+		$build = '';
+		
+		foreach( $query->wheres as $where ) 
+		{	
+			// to make nested wheres possible you can pass an closure 
+			// wich will create a new query where you can add your nested wheres
+			if ( !isset( $where[2] ) && is_closure( $where[1] ) )
+			{
+				$sub_query = new Query;
+				
+				call_user_func( $where[1], $sub_query );
+				
+				$build .= ' '.$where[0].' ( '.substr( $this->compile_where( $sub_query ), 7 ).' )';
+				
+				foreach( $sub_query->parameters as $param )
+				{
+					$this->add_parameter( $param );
+				}
+				
+				continue;
+			}
+			
+			// when we have an array as where values we have 
+			// to parameterize them
+			if ( is_array( $where[3] ) ) 
+			{
+				$where[3] = '('.$this->parameterize( $where[3] ).')';
+			}
+			else
+			{
+				$where[3] = $this->param( $where[3] );
+			}
+			
+			// we always need to escepe where 1 wich referrs to the key
+			$where[1] = $this->escape( $where[1] );
+			
+			// implode the beauty
+			$build .= ' '.implode( ' ', $where );
+		}
+		
+		return $build;
+	}
+	
+	/**
+	 * Build the limit and offset part
+	 *
+	 * @param Query 		$query
+	 * @return string
+	 */
+	public function compile_limit_with_offset( &$query ) 
+	{
+		if ( is_null( $query->limit ) )
+		{
+			return "";
+		}
+		
+		return ' limit '.( (int) $query->offset ).', '.( (int) $query->limit );
+	}
+	
+	/**
+	 * Build the limit and offset part
+	 *
+	 * @param Query 		$query
+	 * @return string
+	 */
+	public function compile_limit( &$query ) 
+	{
+		if ( is_null( $query->limit ) )
+		{
+			return "";
+		}
+		
+		return ' limit '.( (int) $query->limit );
+	}
+	
+	/**
+	 * Build the order by statement
+	 *
+	 * @param Query 		$query
+	 * @return string
+	 */
+	protected function compile_order( &$query ) 
+	{
+		if ( empty( $query->orders ) )
+		{
+			return '';
+		}
+		
+		$build = " order by ";
+		
+		foreach( $query->orders as $order ) 
+		{
+			$build .= $this->escape( $order[0] ).' '.$order[1].', ';
+		}
+		
+		return substr( $build, 0, -2 ); 
+	}
+	
+	/**
+	 * Build the gorup by statemnet
+	 *
+	 * @param Query 		$query
+	 * @return string
+	 */
+	protected function compile_group( &$query ) 
+	{
+		if ( empty( $query->groups ) )
+		{
+			return '';
+		}
+		
+		return ' group by '.$this->escape_list( $query->groups );
+	}
+}

+ 15 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Builder/Mysql.php

@@ -0,0 +1,15 @@
+<?php namespace DB;
+/**
+ * Handler wraps PDO and handles the final queries
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Builder_Mysql extends Builder
+{
+	
+}

+ 15 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Builder/Sqlite.php

@@ -0,0 +1,15 @@
+<?php namespace DB;
+/**
+ * Handler wraps PDO and handles the final queries
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Builder_Sqlite extends Builder
+{
+	
+}

+ 230 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/DB.php

@@ -0,0 +1,230 @@
+<?php namespace DB;
+/**
+ * Database interface
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class DB 
+{
+	/**
+	 * Connect to a database and return the connection status
+	 * 
+ 	 * @param string 	$handler
+ 	 * @param bool
+	 */
+	public static function connect( $handler = null ) 
+	{
+		return Handler::create( $handler )->connected();
+	}
+
+	/**
+	 * Get a database handler
+	 * 
+	 * @param string 	$handler
+	 * @param bool
+	 */
+	public static function handler( $handler = null, $conf = null ) 
+	{
+		return Handler::create( $handler, $conf );
+	}
+	
+	/**
+	 * Create an Database expression object wich will not be escaped
+	 *
+	 * @param mixed 		$value
+	 * @return Expression
+	 */
+	public static function raw( $value )
+	{
+		return new Expression( $value );
+	}
+	
+	/**
+	 * check if we have an db espression object
+	 *
+	 * @param string|Expession
+	 * @return bool
+	 */
+	public static function is_expression( $param )
+	{
+		return $param instanceof Expression;
+	}
+
+	/**
+	 * Return the logged queries
+	 * 
+	 * @param array
+	 */
+	public static function query_log() 
+	{
+		return Handler::log();
+	}
+	
+	/**
+	 * Returns the last executed query
+	 * 
+	 * @param string
+	 */
+	public static function last_query() 
+	{
+		return Handler::last_query();
+	}
+
+	/**
+	 * Fetch data from an sql query
+	 * Returns always an array of results
+	 *
+	 * @param string			$query
+	 * @param array 			$params
+	 * @return array
+	 */
+	public static function fetch( $query, $params = array(), $handler = null, $arguments = array( 'obj' ) ) 
+	{
+		return Handler::create( $handler )->fetch( $query, $params, $arguments );
+	}
+
+	/**
+	 * Run an sql statement will return the number of affected rows
+	 *
+	 * @param string			$query
+	 * @param array 			$params
+	 * @return mixed
+	 */
+	public static function run( $query, $params = array(), $handler = null ) 
+	{
+		return Handler::create( $handler )->run( $query, $params );
+	}
+	
+	/**
+	 * Select data from the database
+	 *
+	 * @param string		$table
+	 * @param array		$fields
+	 * @return mixed
+	 */
+	public static function select( $table, $fields = array(), $handler = null ) 
+	{
+		return Query::select( $table, $fields, $handler );
+	}
+	
+	/**
+	 * Create a select query with an model assignment
+	 *
+	 * @param string		$table
+	 * @param array		$fields
+	 * @return mixed
+	 */
+	public static function model( $model, $handler = null ) 
+	{
+		$model_data = call_user_func( $model.'::_model' );
+		return Query::select( $model_data['table'], null, $model_data['handler'] )
+			->fetch_handler( $model.'::_fetch_handler' );
+	}
+
+	/**
+	 * Find something, means select one record by key
+	 *
+	 * @param string 		$table
+	 * @param int			$id
+	 * @param string			$key
+	 * @param string			$handler
+	 * @return mixed
+	 */
+	public static function find( $table, $id, $key = null, $handler = null ) 
+	{
+		return Query::select( $table )->find( $id, $key, $handler );
+	}
+
+	/**
+	 * Get the first result by key
+	 *
+	 * @param string 		$table
+	 * @param string			$key
+	 * @param string			$handler
+	 * @return mixed
+	 */
+	public static function first( $table, $key = null, $handler = null ) 
+	{
+		return Query::select( $table )->first( $key, $handler );
+	}
+
+	/**
+	 * Get the last result by key
+	 *
+	 * @param string 		$table
+	 * @param string			$key
+	 * @param string			$handler
+	 * @return mixed
+	 */
+	public static function last( $table, $key = null, $handler = null ) 
+	{
+		return Query::select( $table )->last( $key, $handler );
+	}
+
+	/**
+	 * Just return the count result 
+	 *
+	 * @param string 		$table
+	 * @param string 		$handler
+	 * @return int
+	 */
+	public static function count( $table, $handler = null ) 
+	{
+		return Query::select( $table )->count( $handler );
+	}
+	
+	/**
+	 * Create an insert query object
+	 *
+	 * @param string		$table
+	 * @param array		$data
+	 * @return mixed
+	 */
+	public static function insert( $table, $values = array(), $handler = null ) 
+	{
+		return Query::insert( $table, $values, $handler );
+	}
+
+	/**
+	 * Create an update
+	 *
+	 * @param string			$table
+	 * @param array 			$values
+	 * @param string 		$handler
+	 * @return DB\Query
+	 */
+	public static function update( $table, $data = null, $handler = null ) 
+	{
+		return Query::update( $table, $data, $handler );
+	}
+
+	/**
+	 * Create delete query
+	 *
+	 * @param string			$table
+	 * @param string 		$handler
+	 * @return mixed
+	 */
+	public static function delete( $table, $handler = null ) 
+	{
+		return Query::delete( $table, $handler );
+	}
+	
+	/**
+	 * Attention with this one
+	 *
+	 * @param string			$table
+	 * @param array 			$values
+	 * @param string 		$handler
+	 * @return DB\Query
+	 */
+	public static function truncate( $table )
+	{
+		return DB::run( 'truncate table `'.$table.';' );
+	}
+}

+ 12 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Exception.php

@@ -0,0 +1,12 @@
+<?php namespace DB;
+/**
+ * Query Exception
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Exception extends \Core\CCException {}

+ 43 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Expression.php

@@ -0,0 +1,43 @@
+<?php namespace DB;
+/**
+ * DB Expression
+ * This class is just an value holder so we are able to identify 
+ * if a given string should not be escaped.
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Expression 
+{
+	/**
+	 * The value holder 
+	 *
+	 * @var string
+	 */
+	public $value = null;
+	
+	/**
+	 * The constructor that assigns our value
+	 *
+	 * @param string 		$value
+	 * @return void
+	 */
+	public function __construct( $value )
+	{
+		$this->value = $value;
+	}
+	
+	/**
+	 * To string magic in case we want to echo the expression
+	 *
+	 * @return string
+	 */
+	public function __toString()
+	{
+		return $this->value;
+	}
+}

+ 390 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Handler.php

@@ -0,0 +1,390 @@
+<?php namespace DB;
+/**
+ * Handler wraps PDO and handles the final queries
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Handler
+{
+	/**
+	 * Instance holder
+	 *
+	 * @var array
+	 */
+	protected static $_instances = array();
+
+	/**
+	 * Default database instance name
+	 *
+	 * @var string
+	 */
+	private static $_default = 'main';
+
+	/**
+	 * The query log if enabeled
+	 *
+	 * @var array
+	 */
+	private static $_query_log = array();
+
+	/**
+	 * Static init
+	 * When we are in development then we append the qurey log to body
+	 *
+	 * @codeCoverageIgnore
+	 *
+	 * @return void
+	 */
+	public static function _init() 
+	{	
+		if ( \ClanCats::in_development() ) 
+		{ 
+			// add a hook to the main resposne
+			\CCEvent::mind( 'response.output', function( $output ) {
+				
+				if ( strpos( $output, '</body>' ) === false ) 
+				{
+					return $output;
+				}
+				
+				$table = \UI\Table::create( array(
+					 'style'		=> array(
+					 	'width'	=> '100%',
+					 ),
+					 'cellpadding' 	=> '5',
+					 'class'			=> 'table debug-table debug-table-db',
+				));
+				
+				$table->header( array( '#', 'query' ) );
+				
+				foreach( static::log() as $key => $item )
+				{
+					$table->row( array( 
+						$key+1,
+						$item
+					));
+				}
+			
+				return str_replace( '</body>', $table."\n</body>", $output );
+			});
+		}
+	}
+
+	/**
+	 * Get a database handler instance
+	 * The objects get stored on runtime so you can use this function 
+	 * multipe times wihout reconecting to the database.
+	 *
+	 * @param string			$name
+	 * @param array 			$conf	You can pass optionally a configuration directly. This will overwrite.
+	 * @return DB_Handler
+	 */
+	public static function create( $name = null, $conf = null ) 
+	{
+		if ( is_null( $name ) ) 
+		{
+			$name = static::$_default;
+		}
+		
+		if ( !is_null( $conf ) && is_array( $conf ) )
+		{
+			return static::$_instances[$name] = new static( $conf );
+		}
+		
+		if ( !isset( static::$_instances[$name] ) )
+		{
+			static::$_instances[$name] = new static( $name );
+		}
+		
+		return static::$_instances[$name];
+	}
+	
+	/**
+	 * Kill all database connections
+	 *
+	 * @return void
+	 */
+	public static function kill_all_connections()
+	{
+		static::$_instances = array();
+	}
+
+	/**
+	 * Get the query log 
+	 * 
+	 * @return array
+	 */
+	public static function log()
+	{
+		return static::$_query_log;
+	}
+	
+	/**
+	 * Get the query log 
+	 * 
+	 * @return array
+	 */
+	public static function last_query()
+	{
+		return \CCArr::last( static::$_query_log );
+	}
+	
+	/**
+	 * The current connection
+	 *
+	 * @var DB\Handler_Driver
+	 */
+	protected $_driver;
+	
+	/**
+	 * The current query builder
+	 *
+	 * @var DB\Builder
+	 */
+	protected $_builder;
+
+	/**
+	 * Are we connected to default Database
+	 *
+	 * @var bool
+	 */
+	protected $_connected = false;
+
+	/** 
+	 * Handler constructor and connect to the database
+	 *
+	 * @param string		$name
+	 * @return void
+	 */
+	protected function __construct( $name ) 
+	{
+		$this->connect( $name );
+	}
+	
+	/**
+	 * Get the current driver
+	 *
+	 * @return DB\Handler_Driver
+	 */
+	public function driver()
+	{
+		return $this->_driver;
+	}
+	
+	/**
+	 * Set the current driver
+	 *
+	 * @param string		$driver		The full driver class ( DB\Handler_ExampleDriver )
+	 * @return void
+	 */
+	private function set_driver( $driver )
+	{
+		$this->_driver = new $driver;
+	}
+	
+	/**
+	 * Get the current query builder
+	 *
+	 * @return DB\Builder
+	 */
+	public function builder()
+	{
+		return $this->_builder;
+	}
+	
+	/**
+	 * Set the current builder
+	 *
+	 * @param string		$driver		The full driver class ( DB\Builder_ExampleDriver )
+	 * @return void
+	 */
+	private function set_builder( $driver )
+	{
+		$this->_builder = new $driver;
+	}
+
+	/**
+	 * Try to etablish a connetion to the database. Also assign the connection 
+	 * and query builder to the current DB driver.
+	 *
+	 * @param string|array 	$name	When passing an array it will be uesed as configuration.
+	 * @return void
+	 */
+	protected function connect( $name ) 
+	{
+		if ( $this->_connected ) 
+		{
+			return true; 
+		}
+		
+		// check if the name is an array. This way we can
+		// pass the configuration directly. We need this 
+		// to create for example an handler without having
+		// the configuration in the database conf file.
+		if ( is_array( $name ) )
+		{
+			$config = $name;
+		}
+		else 
+		{
+			$config = \CCConfig::create( 'database' )->get( $name );
+			
+			// check for an alias. If you set a string 
+			// in your config file we use the config 
+			// with the passed key.
+			if ( is_string( $config ) ) 
+			{
+				$config = \CCConfig::create( 'database' )->get( $config );
+			}
+		}
+
+		// Setup the driver class. We simply use name 
+		// from the confif file and make the first letter 
+		// capital. example: Handler_Mysql, Handler_Sqlite etc.
+		$driver_class = __NAMESPACE__."\\Handler_".ucfirst( $config['driver'] );
+		
+		if ( !class_exists( $driver_class ) )
+		{
+			throw new Exception( "DB\\Handler::connect - The driver (".$driver_class.") is invalid." );
+		}
+		
+		$this->set_driver( $driver_class );
+		
+		// setup the builder the same way as the handler.
+		$driver_class = __NAMESPACE__."\\Builder_".ucfirst( $config['driver'] );
+		
+		if ( !class_exists( $driver_class ) )
+		{
+			throw new Exception( "DB\\Handler::connect - The builder (".$driver_class.") is invalid." );
+		}
+		
+		$this->set_builder( $driver_class );
+		
+		// finally try to connect the driver with the databse
+		if ( $this->driver()->connect( $config ) ) 
+		{
+			return $this->_connected = true;
+		}
+
+		return $this->_connected = false;
+	}
+	
+	/**
+	 * Get the connected status
+	 *
+	 * @return bool
+	 */
+	public function connected()
+	{
+		return $this->_connected;
+	}
+	
+	/**
+	 * Run the query and return the PDO statement
+	 *
+	 * @param string			$query
+	 * @param array 			$params
+	 * @return array
+	 */
+	public function statement( $query, $params = array() )
+	{
+		// we alway prepare the query even if we dont have parameters
+		$sth = $this->driver()->connection()->prepare( $query );
+		
+		// because we set pdo the throw exception on db errors
+		// we catch them here to throw our own exception.
+		try 
+		{
+			$sth->execute( $params );
+		}
+		catch ( \PDOException $e ) 
+		{
+			throw new Exception( "DB\\Handler - PDOException: {$e->getMessage()} \n Query: {$query}" );
+		}
+		
+		// In development we alway log the query into an array.
+		if ( \ClanCats::in_development() ) 
+		{	
+			$keys = array();
+			
+			foreach ( $params as $key => $value ) 
+			{
+				if ( is_string( $key ) ) 
+				{
+					$keys[] = '/:'.$key.'/';
+				} else {
+					$keys[] = '/[?]/';
+				}
+			}
+			
+			static::$_query_log[] = preg_replace( $keys, $params, $query, 1 );
+		}
+		
+		return $sth;
+	}
+	
+	/**
+	 * Run the query and fetch the results
+	 *
+	 * You can pass arguments on the third parameter.
+	 * These parameter are just the normal PDO ones but in short.
+	 *
+	 *     obj = \PDO::FETCH_OBJ
+	 *     assoc = \PDO::FETCH_ASSOC
+	 *
+	 * @param string			$query
+	 * @param array 			$params
+	 * @return array
+	 */
+	public function fetch( $query, $params = array(), $arguments = array( 'obj' ) )
+	{
+		$sth = $this->statement( $query, $params );
+		
+		$args = null;
+		
+		foreach( $arguments as $argument )
+		{
+			$args |= constant( "\PDO::FETCH_".strtoupper( $argument ) );
+		}
+				
+		return $sth->fetchAll( $args );
+	}
+	
+	/**
+	 * Run the query and get the correct response
+	 *
+	 * INSERT -> last id
+	 * UPDATE -> affected rows
+	 * DELETE -> affected rows
+	 * etc...
+	 * 
+	 * @param string			$query
+	 * @param array 			$params
+	 * @return array
+	 */
+	public function run( $query, $params = array() )
+	{
+		$sth = $this->statement( $query, $params );
+		
+		$type = strtolower( substr( $query, 0, strpos( $query, ' ' ) ) );
+	
+		switch ( $type ) 
+		{
+			case 'update':
+			case 'delete':
+				return $sth->rowCount();
+			break;
+			
+			case 'insert':
+				return $this->driver()->connection()->lastInsertId();
+			break;
+		}
+		
+		return $sth;
+	}
+}

+ 83 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Handler/Driver.php

@@ -0,0 +1,83 @@
+<?php namespace DB;
+/**
+ * Handler wraps PDO and handles the final queries
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Handler_Driver
+{
+	/**
+	 * the raw connection object
+	 *
+	 * @var \PDO
+	 */
+	protected $connection = null;
+	
+	/**
+	 * the string used for the PDO connection
+	 *
+	 * @var string
+	 */
+	protected $connection_string = null;
+	
+	/**
+	 * connect to database
+	 *
+	 * @param array 		$conf
+	 * @return bool
+	 */
+	public function connect( $conf ) 
+	{	
+		$connection_params = array();
+		
+		foreach( $conf as $key => $value )
+		 {
+			if ( is_string( $value ) ) 
+			{
+				$connection_params[ '{'.$key.'}' ] = $value;
+			}
+		}
+		
+		$connection_string = \CCStr::replace( $this->connection_string, $connection_params );
+		
+		$this->connection = new \PDO( $connection_string, $conf['user'], $conf['pass'], $this->connection_attributes( $conf ) );
+		
+		// At the moment this will never happen because pdo is going to 
+		// trhow an exception when the connection fails
+		/*if ( !$this->connection )
+		{
+			return false;
+		}*/
+		
+		// let pdo throw exceptions
+		$this->connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+		
+		return true;
+	}
+	
+	/**
+	 * Get the current PDO conncection object
+	 *
+	 * @return \PDO
+	 */
+	public function connection()
+	{
+		return $this->connection;
+	}
+	
+	/**
+	 * return the connection attributes
+	 *
+	 * @param array 		$conf
+	 * @return array
+	 */
+	protected function connection_attributes( $conf ) 
+	{
+		return array();
+	}
+}

+ 33 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Handler/Mysql.php

@@ -0,0 +1,33 @@
+<?php namespace DB;
+/**
+ * Handler wraps PDO and handles the final queries
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Handler_Mysql extends Handler_Driver
+{
+	/**
+	 * the string used for the PDO connection
+	 *
+	 * @var string
+	 */
+	protected $connection_string = 'mysql:host={host};dbname={db}';
+	
+	/**
+	 * return the connection attributes
+	 *
+	 * @param array        $conf
+	 * @return array
+	 */
+	protected function connection_attributes( $conf ) 
+	{
+	    return array(
+	        \PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES ".$conf['charset']
+	    );
+	}
+}

+ 33 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Handler/Sqlite.php

@@ -0,0 +1,33 @@
+<?php namespace DB;
+/**
+ * Handler wraps PDO and handles the final queries
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Handler_Sqlite extends Handler_Driver
+{
+	/**
+	 * the string used for the PDO connection
+	 *
+	 * @var string
+	 */
+	protected $connection_string = 'sqlite:{path}';
+
+	/**
+	 * return the connection attributes
+	 *
+	 * @param array        $conf
+	 * @return array
+	 */
+	protected function connection_attributes( $conf ) 
+	{
+		return array(
+			\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES ".$conf['charset']
+		);
+	}
+}

+ 416 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Migrator.php

@@ -0,0 +1,416 @@
+<?php namespace DB;
+/**
+ * Database mirgrations
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Migrator 
+{
+	/**
+	 * The migration conifig file
+	 *
+	 * @var CCConfig
+	 */
+	protected static $config = null;
+	
+	/**
+	 * Init the migrator directory
+	 */
+	public static function _init()
+	{
+		\ClanCats::directories( array( 'migration' => 'database/' ) );
+		
+		// read the migration configuration
+		static::$config = \CCConfig::create( 'migrator', 'json' );
+	}
+	
+	/**
+	 * Get a migration path by name
+	 *
+	 * @param string 		$name
+	 * @return string
+	 */
+	public static function path( $name, $time = null )
+	{
+		if ( is_null( $time ) )
+		{
+			$time = time();
+		}
+		
+		$namespace = null;
+		
+		if ( strpos( $name, '::' ) !== false )
+		{
+			list( $namespace, $name ) = explode( '::', $name );
+		}
+		
+		$name = explode( '/', $name );
+		
+		foreach( $name as $key => $value )
+		{
+			$name[$key] = \CCStr::clean_url( $value, '_' );
+		}
+		
+		$name = implode( '/', $name );
+		
+		return \CCPath::get( ( $namespace ? $namespace.'::' : '' ) .$name.'_'.$time, \ClanCats::directory( 'migration' ), '.sql' );
+	}
+	
+	/**
+	 * Run all new migration
+	 *
+	 * @param bool			$silent 		In the silent mode the migrator ignores the migration file
+	 * 
+	 * @return void
+	 */
+	public static function migrate( $silent = false )
+	{
+		$migrations = $silent ? static::available() : static::unstaged();
+		
+		foreach( $migrations as $key => $value )
+		{
+			if ( empty( $value ) )
+			{
+				continue;
+			}
+			
+			if ( \ClanCats::is_cli() )
+			{
+				\CCCli::info( 'found new "'.$key.'" migrations.' );
+			}
+			
+			foreach( $value as $time => $path )
+			{
+				$migration = new static( $path );
+				
+				// run the migration
+				$migration->up();
+				
+				if ( \ClanCats::is_cli() )
+				{
+					\CCCli::success( 'migrated '.$migration->name() );
+				}
+			}
+			
+			static::$config->set( $key.'.revision', $time );
+		}
+		
+		if ( !$silent )
+		{
+			static::$config->write();
+		}
+	}
+	
+	/**
+	 * Revert the last migration
+	 *
+	 * @return void
+	 */
+	public static function rollback()
+	{
+		// first of all we have to filter only the already migrated versions
+		$available = static::available();
+		
+		foreach( $available as $key => $migrations )
+		{
+			foreach( $migrations as $time => $migration )
+			{
+				if ( $time > static::$config->get( $key.'.revision', 0 ) )
+				{
+					unset( $available[$key][$time] );
+				}
+			}
+		}
+		
+		$revisions = array();
+		
+		foreach( $available as $key => $value )
+		{
+			if ( empty( $value ) )
+			{
+				continue;
+			}
+			
+			foreach( $value as $name => $path )
+			{
+				$revisions[$name.'::'.$key] = $path;
+			}
+		}
+		
+		// nothing to rollback?
+		if ( empty( $revisions ) )
+		{
+			if ( \ClanCats::is_cli() )
+			{
+				\CCCli::warning( 'nothing to rollback to.' );
+			}
+			
+			return false;
+		}
+		
+		ksort( $revisions );
+		
+		end( $revisions ); 
+		list( $time, $key ) = explode( '::', key( $revisions ) );
+		
+		$migration = new static( array_pop( $revisions ) );
+		
+		// rollback the migration
+		$migration->down();
+		
+		// get the lastet migration from the group
+		$others = \CCArr::get( $key, $available );
+		
+		ksort( $others );
+		array_pop( $others );
+		end( $others );
+		
+		// update the config
+		static::$config->set( $key.'.revision', key( $others ) );
+		static::$config->write();
+		
+		return true;
+	}
+	
+	/**
+	 * The hard reset method deletes all tables from the database
+	 *
+	 * @param string 		$databse
+	 */
+	public static function hard_reset( $database = null )
+	{
+		$tables = DB::fetch( 'SHOW TABLES', array(), $database, array( 'assoc' ) );
+		
+		foreach( $tables as $table )
+		{
+			DB::run( 'DROP TABLE IF EXISTS '.reset( $table ), array(), $database );
+		}
+	}
+	
+	/**
+	 * Returns the available migrations
+	 *
+	 * @return array
+	 */
+	public static function available()
+	{
+		$bundles = array_merge( \CCFinder::$bundles, array( 
+			'app' => \CCPath::get( '', null ) 
+		));
+		
+		$available = array();
+		
+		foreach( $bundles as $name => $path )
+		{
+			$directory = $path.\ClanCats::directory( 'migration' );
+			
+			if ( is_dir( $directory ) )
+			{
+				$available[strtolower($name)] = static::get_migrations( $directory );
+			}
+		}
+		
+		return $available;
+	}
+	
+	/**
+	 * Returns the available migrations
+	 *
+	 * @param string 		$path
+	 * @return array
+	 */
+	public static function get_migrations( $path )
+	{
+		$objects = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $path ), \RecursiveIteratorIterator::SELF_FIRST );
+		
+		$files = array();
+		
+		foreach( $objects as $name => $object )
+		{
+			if ( \CCStr::extension( $name ) == 'sql' )
+			{
+				$files[\CCStr::cut( substr( basename( $name ), strrpos( basename( $name ),  '_' )+1 ), '.' )] = $name;
+			}
+		}
+		
+		ksort( $files );
+		
+		return $files;
+	}
+	
+	/**
+	 * Returns the unstaged migrations based on the configuration
+	 *
+	 * @return array
+	 */
+	public static function unstaged()
+	{
+		$available = static::available();
+		
+		foreach( $available as $key => $migrations )
+		{
+			foreach( $migrations as $time => $migration )
+			{
+				if ( $time <= static::$config->get( $key.'.revision', 0 ) )
+				{
+					unset( $available[$key][$time] );
+				}
+			}
+		}
+		
+		return $available;
+	}
+	
+	/**
+	 * The migration sql file
+	 *
+	 * @var string
+	 */
+	protected $path = null;
+	
+	/**
+	 * The migration name
+	 *
+	 * @var string
+	 */
+	protected $name = null;
+	
+	/**
+	 * Creates a new migrator instance 
+	 *
+	 * @param string 		$path 	The path of the migration
+	 * @return void
+	 */
+	protected function __construct( $path )
+	{
+		$this->path = $path;
+		$this->name = \CCStr::cut( substr( basename( $path ), 0, strrpos( basename( $path ), '_' ) ), '.' );	
+	}
+	
+	/**
+	 * Returns the name of the migration
+	 *
+	 * @return string
+	 */
+	public function name()
+	{
+		return $this->name;
+	}
+	
+	/**
+	 * Migrates the current migration up
+	 *
+	 * @return void
+	 */
+	public function up()
+	{
+		\CCCli::info( $this->name.'...', 'migrating' );
+		$this->run_queries( 'up' );
+	}
+	
+	/**
+	 * Migrates the current migration down
+	 *
+	 * @return void
+ 	 */
+	public function down()
+	{
+		\CCCli::info( $this->name.'...', 'reverting' );
+		$this->run_queries( 'down' );
+	}
+	
+	/**
+	 * Run all queries of specified group
+	 *
+	 * @param string 		$group
+	 * @return void
+	 */
+	private function run_queries( $group )
+	{
+		if ( !$handle = fopen( $this->path , "r") )
+		{
+			throw new Exception( "Could not read migration: ".$this->path );
+		}
+		
+		$mode = null;
+		$query  = '';
+		
+		while ( !feof( $handle ) ) 
+		{
+			$buffer = trim( fgets( $handle, 4096 ) );
+			
+			$this->parse_mode( $mode, $buffer );
+			
+			if ( $this->is_comment( $buffer ) )
+			{
+				continue;
+			}
+			
+			// only continue if we are in up mode
+			if ( $mode === $group )
+			{
+				$query .= $buffer;
+				
+				if ( substr( rtrim( $query ), -1 ) == ';' ) 
+				{
+					// run the query
+					DB::run( $query );
+					
+					// reset the query for the next one.
+					$query = '';
+				}
+			}
+			
+		}
+		
+		fclose($handle);
+	}
+	
+	/**
+	 * Try to parse the current mode
+	 *
+	 * @param string 		$mode
+	 * @param string			$buffer
+	 * @return bool
+	 */
+	private function parse_mode( &$mode, $buffer )
+	{
+		if ( substr( trim( $buffer ), 0, 6 ) === '# --->' )
+		{
+			$mode = trim( substr( $buffer, 6 ) );
+		}
+	}
+	
+	/**
+	 * Is this string an sql comment?
+	 *
+	 * @param string			$buffer
+	 * @return bool
+	 */
+	private function is_comment( $buffer )
+	{
+		if ( is_string( $buffer ) && strlen( $buffer ) >= 1 )
+		{
+			if ( $buffer[0] == '#' )
+			{
+				return true;
+			}
+			
+			if ( substr( $buffer, 0, 2 ) == '--' )
+			{
+				return true;
+			}
+		}
+		else
+		{
+			return true;
+		}
+		
+		return false;
+	}
+}

+ 537 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model.php

@@ -0,0 +1,537 @@
+<?php namespace DB;
+/**
+ * DB Model 
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Model extends \CCModel
+{
+	/*
+	 * The table
+	 */
+	// public static $_table = null;
+
+	/*
+	 * The primary key
+	 */
+	// public static $_primary_key = null;
+
+	/*
+	 * The find mofifier
+	 */
+	// protected static $_find_modifier = null;
+
+	/*
+	 * Let the model automatically set created_at and modified_at 
+	 */
+	// protected static $_timestamps = false;
+
+	/**
+ 	 * Prepare the model
+	 *
+	 * @param string 	$settings	The model properties
+	 * @param string 	$class 		The class name.
+	 * @return array
+	 */
+	protected static function _prepare( $settings, $class )
+	{
+		$settings = parent::_prepare( $settings, $class );
+
+		// Next step the table name. If not set we use the 
+		// class name appending an 's' in the hope that it 
+		// makes sense like Model_User = users
+		if ( property_exists( $class, '_table') ) 
+		{
+			$settings['table'] = static::$_table;
+		}
+		else 
+		{
+			$settings['table'] = strtolower( $class );
+
+			$settings['table'] = explode( "\\", $settings['table'] );
+
+			$last = array_pop( $settings['table'] );
+
+			// Often we have these model's in the Model folder, we 
+			// don't want this in the table name so we cut it out.
+			if ( substr( $last, 0, strlen( 'model_' ) ) == 'model_' )
+			{
+				$last = substr( $last, strlen( 'model_' ) );
+			}
+
+			$settings['table'][] = $last;
+
+			$settings['table'] = implode( '_', $settings['table'] ).'s';
+		}
+
+		// Next we would like to know the primary key used
+		// in this model for saving, finding etc.. if not set
+		// we use the on configured in the main configuration
+		if ( property_exists( $class, '_primary_key') ) 
+		{
+			$settings['primary_key'] = static::$_primary_key;
+		}
+		else 
+		{
+			$settings['primary_key'] = \ClanCats::$config->get( 'database.default_primary_key', 'id' );
+		}
+
+		// Do we should use a special DB handler?
+		if ( property_exists( $class, '_handler') ) 
+		{
+			$settings['handler'] = static::$_handler;
+		}
+		else 
+		{
+			$settings['handler'] = null;
+		}
+
+		// The find modifier allows you hijack every find executed
+		// on your model an pass setting's to the query. This allows
+		// you for example to defaultly order by something etc.
+		if ( property_exists( $class, '_find_modifier') ) 
+		{
+			$settings['find_modifier'] = static::$_find_modifier;
+		}
+		else
+		{
+			$settings['find_modifier'] = null;
+		}
+
+		// Enabling this options will set the created_at
+		// and modified at property on save
+		if ( property_exists( $class, '_timestamps') ) 
+		{
+			$settings['timestamps'] = (bool) static::$_timestamps;
+		}
+		else
+		{
+			$settings['timestamps'] = false;
+		}
+
+		return $settings;
+	}
+
+	/**
+	 * Fetch from the databse and created models out of the reults
+	 * 
+	 * @param DB\Query_Select		$query
+	 * @return array
+	 */
+	public static function _fetch_handler( &$query )
+	{
+		// because the model is an object we force the fetch
+		// arguments to obj so that we can still make use of
+		// the group by and forward key functions
+		$query->fetch_arguments = array( 'obj' );
+
+		// Run the query and assign the reults
+		// here we force the fetch arguments to assoc 
+		// without this step model::assign will fail
+		return static::assign( $query->handler->fetch( $query->build(), $query->handler->builder()->parameters, array( 'assoc' ) ) );
+	}
+
+	/**
+	 * Returns a query and assign's the current model to it
+	 *
+	 * @return DB\Query_Select
+	 */
+	public static function select() 
+	{
+		return DB::model( get_called_class() );
+	}
+
+	/**
+	 * Model finder 
+	 * This function allows you direct access to your records.
+	 *
+	 * @param mixed		$param
+	 * @param mixed		$param2
+	 * @return CCModel
+	 */
+	public static function find( $param = null, $param2 = null ) 
+	{
+		$settings = static::_model();
+
+		$query = DB::select( $settings['table'] );
+
+		// Do we have a find modifier?
+		if ( !is_null( $settings['find_modifier'] ) ) 
+		{
+			$callbacks = $settings['find_modifier'];
+
+			if ( !\CCArr::is_collection( $callbacks ) )
+			{
+				$callbacks = array( $callbacks );
+			}
+
+			foreach( $callbacks as $call )
+			{
+				if ( is_callable( $call ) ) 
+				{
+					call_user_func_array( $call, array( &$query ) );
+				}
+				else 
+				{
+					throw new ModelException( "Invalid Callback given to find modifiers." );
+				}
+			}
+		}
+
+		if ( !is_null( $param ) )
+		{
+			// Check if paramert 1 is a valid callback and not a string.
+			// Strings as function callback are not possible because
+			// the user might want to search by key like:
+			// Model::find( 'key', 'type' );
+			if ( is_callable( $param ) && !is_string( $param ) ) 
+			{
+				call_user_func_array( $param, array( &$query ) );
+			}
+			// When no param 2 isset we try to find the record by primary key
+			elseif ( is_null( $param2 ) ) 
+			{
+				$query->where( $settings['table'].'.'.$settings['primary_key'], $param )
+					->limit(1);
+			}
+			// When param one and two isset we try to find the record by
+			// the given key and value.
+			elseif ( !is_null( $param2 ) )
+			{
+				$query->where( $param, $param2 )
+					->limit(1);
+			}
+		}
+
+		// alway group the result
+		$query->forward_key( $settings['primary_key'] );
+
+		// and we have to fetch assoc
+		$query->fetch_arguments = array( 'assoc' );
+
+		// and assign
+		return static::assign( $query->run() );
+	}
+	
+	/**
+	 * Call a function as a property
+	 *
+	 * @param string 		$key
+	 * @return mixed
+	 */
+	public function __call_property( $key )
+	{
+		$result = parent::__call_property( $key );
+		
+		// when we recive a relation we execute it and save it
+		// to the data to avoid mutlitple queries
+		if ( $result instanceof Model_Relation )
+		{
+			return $this->_data_store[$key] = $result->run();
+		}
+		
+		return $result;
+	}
+
+	/**
+	 * Has one releationships
+	 *
+	 * Model Car:
+	 *     function engine()
+	 *     {
+	 *         return $this->has_one( 'Car_Engine', 'car_id', 'id' );	 
+	 *     }
+	 *
+	 * @param Model			$model
+	 * @param mixed			$foreign_key
+	 * @param mixed			$key
+	 * @return array
+	 */
+	protected function has_one( $model, $foreign_key = null, $local_key = null )
+	{
+		return new Model_Relation_HasOne( $this, $model, $foreign_key, $local_key );
+	}
+
+	/**
+	 * Has many releationships
+	 *
+	 * Model Car:
+	 *     function wheels()
+	 *     {
+	 *         return $this->has_many( 'Car_Wheel', 'car_id', 'id' );	 
+	 *     }
+	 *
+	 * @param Model			$model
+	 * @param mixed			$foreign_key
+	 * @param mixed			$key
+	 * @return array
+	 */
+	protected function has_many( $model, $foreign_key = null, $local_key = null )
+	{
+		return new Model_Relation_HasMany( $this, $model, $foreign_key, $local_key );
+	}
+
+	/**
+	 * Belongs To releationships
+	 *
+	 * Model Car_Engine:
+	 *     function car()
+	 *     {
+	 *         return $this->belongs_to( 'Car', 'id', 'car_id' );	 
+	 *     }
+	 *
+	 * @param Model			$model
+	 * @param mixed			$foreign_key
+	 * @param mixed			$key
+	 * @return array
+	 */
+	protected function belongs_to( $model, $foreign_key = null, $local_key = null )
+	{
+		return new Model_Relation_BelongsTo( $this, $model, $foreign_key, $local_key );
+	}
+
+	/**
+	 * find with an relationship
+	 *
+	 *     Person::with( 'cars' );
+	 *
+	 * @param array|string 			$with
+	 * @param callback				$callback
+	 * @return array
+	 */
+	public static function with( $with, $callback = null ) 
+	{	
+		if ( !is_array( $with ) )
+		{
+			$with = array( $with );
+		}
+		
+		$settings = static::_model();
+		
+		$query = DB::select( $settings['table'] );
+		
+		// run the callback
+		if ( !is_null( $callback ) )
+		{
+			call_user_func_array( $callback, array( &$query ) );
+		}
+		
+		// alway group the result and fetch assoc
+		$query->forward_key( $settings['primary_key'] );
+		$query->fetch_arguments = array( 'assoc' );
+		
+		// get the main result set
+		$results = static::assign( $query->run() );
+		$singleton = false;
+		
+		if ( !is_array( $results ) )
+		{
+			$results = array( $results );
+			$singleton = true;
+		}
+		
+		$ref_object = reset( $results );
+		
+		// we have to sort the relationships to make sure that
+		// select the relations in the right order.	
+		asort( $with );
+		
+		foreach( $with as $relation => $callback )
+		{
+			if ( is_int( $relation ) && is_string( $callback ) )
+			{
+				$relation = $callback;
+				$callback = null;
+			}
+			
+			if ( strpos( $relation, '.' ) !== false )
+			{	
+				$relation_layers = explode( '.', $relation );
+				
+				$relation_name = array_pop( $relation_layers );
+				
+				$relation_collection = array();
+				
+				foreach( $results as $key => &$item )
+				{
+					$curr_item = $item;
+					
+					foreach( $relation_layers as $layer )
+					{
+						$curr_item = $curr_item->raw( $layer );
+					}
+					
+					$relation_collection[] = $curr_item;
+				}
+				
+				$ref_object = reset( $relation_collection );
+				
+				$relation_object = call_user_func( array( $ref_object, $relation_name ) );
+				if ( $relation_object instanceof Model_Relation )
+				{
+					$relation_object->collection_assign( $relation_name, $relation_collection, $callback );
+				}
+			}
+			else
+			{
+				$relation_object = call_user_func( array( $ref_object, $relation ) );
+				if ( $relation_object instanceof Model_Relation )
+				{
+					$relation_object->collection_assign( $relation, $results, $callback );
+				}
+				
+			}
+		}
+		
+		if ( $singleton )
+		{
+			return reset( $results );
+		}
+		
+		// and assign
+		return $results;
+	}
+
+	/**
+	 * save an model
+	 *
+	 * @param mixed		$fields
+	 * @return self
+	 */
+	public function save( $fields = null ) 
+	{
+		$settings = static::_model();
+
+		// check if we should save just some fields
+		if ( is_null( $fields ) ) 
+		{
+			$fields = array_keys( $settings['defaults'] );
+		}
+		elseif ( !is_array( $fields ) ) 
+		{
+			$fields = array( $fields );
+		}
+
+		$pkey = $this->_data_store[$settings['primary_key']];
+		$data = array();
+
+		// Now we have to filter the data to the save g
+		foreach( $fields as $field )
+		{
+			$data[$field] = $this->_data_store[$field];
+		}
+
+		// We have to remove the primary key from our data
+		if ( array_key_exists( $settings['primary_key'], $data ) )
+		{
+			unset( $data[$settings['primary_key']] );
+		}
+
+		// We pass the data trough the before save callback.
+		// This is a local callback for performence reasons.
+		$data = $this->_before_save( $data );
+
+		// after the before save event,
+		// do we have to to something with the data type?
+		foreach( $data as $key => $value )
+		{
+			if ( array_key_exists( $key, $settings['types'] ) )
+			{
+				$data[$key] = $this->_type_assignment_set( $settings['types'][$key], $value );
+			}
+		}
+
+		// check if we have to set the timestamps automatically
+		if ( $settings['timestamps'] === true )
+		{
+			if ( array_key_exists( 'created_at', $data ) )
+			{
+				// check if created_at should be set
+				if ( $data['created_at'] < 1 )
+				{
+					$this->_data_store['created_at'] = $data['created_at'] = time();
+				}
+			}
+
+			if ( array_key_exists( 'modified_at', $data ) )
+			{
+				$this->_data_store['modified_at'] =$data['modified_at'] = time();
+			}
+		}
+
+		// When we already have a primary key we are going to 
+		// update our record instead of inserting a new one.
+		if ( !is_null( $pkey ) && $pkey > 0 ) 
+		{
+			$query = DB::update( $settings['table'], $data )
+				->where( $settings['primary_key'], $pkey );
+		}
+		// No primary key? Smells like an insert query. 
+		else 
+		{
+			$query = DB::insert( $settings['table'], $data );
+		}
+
+		// We check the query type to handle the response right
+		if ( $query instanceof Query_Insert ) 
+		{
+			$this->_data_store[$settings['primary_key']] = $query->run();
+		}
+		else 
+		{
+			$query->run();
+		}
+
+		// after save hookt
+		$this->_after_save();
+
+		// return self
+		return $this;
+	}
+
+	/**
+	 * Create a copy of the current model
+	 *
+	 * @return DB\Model
+	 */
+	public function copy() 
+	{
+		$clone = clone $this; $clone->_data_store[static::_model('primary_key')] = null; return $clone;
+	}
+
+	/**
+	 * Delete the current model from the database
+	 *
+	 * @return DB\Model
+	 */
+	public function delete() 
+	{
+		$settings = static::_model();
+
+		$result = DB::delete( $settings['table'] )
+			->where( $settings['primary_key'], $this->_data_store[$settings['primary_key']] )
+			->limit(1)
+			->run( $settings['handler'] );
+
+		$this->_data_store[$cache['primary_key']] = null;
+
+		return $result;
+	}
+
+	/**
+	 * Save data hook 
+	 * to modify your data before they get saved
+	 */
+	protected function _before_save( $data ) { return $data; }
+
+	/**
+	 * After save hook 
+	 * to modify your data after they get saved
+	 */
+	protected function _after_save() {}
+}

+ 210 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model/Relation.php

@@ -0,0 +1,210 @@
+<?php namespace DB;
+/**
+ * DB Model Relation 
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Model_Relation
+{
+	/**
+	 * The current relation query
+	 *
+	 * @var DB\Query
+	 */
+	public $query = null;
+	
+	/**
+	 * The key on the related model
+	 *
+	 * @var string
+	 */
+	public $foreign_key = null;
+	
+	/**
+	 * The key on the local model
+	 *
+	 * @var string
+	 */
+	public $local_key = null;
+	
+	/**
+	 * The related model
+	 *
+	 * @var DB\Model
+	 */
+	public $foreign_model = null;
+	
+	/**
+	 * The model where the request came from
+	 *
+	 * @var DB\Model
+	 */
+	public $local_model = null;
+	
+	/**
+	 * Should this relationship deliver a single item
+	 *
+	 * @var bool
+	 */ 
+	public $singleton = true;
+	
+	/**
+	 * Create new relationship instance
+	 *
+	 * @param DB\Model			$model
+	 * @param string				$foreign_key
+	 * @param string 			$key
+	 * @return void
+	 */
+	public function __construct( $local, $foreign, $foreign_key, $local_key )
+	{
+		$this->local_model = $local;
+		$this->foreign_model = $foreign;
+		
+		if ( !class_exists( $foreign ) )
+		{
+			throw new ModelException( 'Invalid class "'.$foreign.'" given for relationship.' );
+		}
+		
+		// set or create the for foreign_key
+		if ( !is_null( $foreign_key ) )
+		{
+			$this->foreign_key = $foreign_key;
+		} else {
+			$this->foreign_key = $this->get_foreign_key();
+		}
+		
+		// set or create the for local_key
+		if ( !is_null( $local_key ) )
+		{
+			$this->local_key = $local_key;
+		} else {
+			$this->local_key = $this->get_local_key();
+		}
+		
+		// create a basic query objcet		
+		$this->query = $this->create_query( $foreign );
+		
+		// prepare the query 
+		$this->prepare_query();
+	}
+	
+	/**
+	 * Get foreign key from the models
+	 *
+	 * @return void
+	 */
+	protected function get_foreign_key()
+	{
+		return basename( $this->local_model->model_settings( 'name' ) ).
+			'_'.$this->local_model->model_settings( 'primary_key' );
+	}
+	
+	/**
+	 * Get foreign key from the models
+	 *
+	 * @return void
+	 */
+	protected function get_local_key()
+	{
+		return call_user_func( $this->foreign_model.'::_model', 'primary_key' );
+	}
+	
+	/**
+	 * Creates a query object based on a given model
+	 *
+	 * @param DB\Model			$model
+	 * @return DB\Query
+	 */
+	protected function create_query( $model )
+	{
+		return call_user_func( $model.'::select' )->forward_key();
+	}
+	
+	/**
+	 * Prepare the query object
+	 *
+	 * @return void
+	 */
+	protected function prepare_query() {}
+	
+	/**
+	 * Prepare the query with collection select
+	 *
+	 * @param array 			$collection
+	 * @return void
+	 */
+	protected function collection_query( &$collection ) 
+	{
+		$local_keys = array();
+		
+		foreach( $collection as $item )
+		{
+			$local_keys[] = $item->raw( $this->local_key );
+		}
+		
+		// set the correct collection where
+		$this->query->wheres = array();
+		$this->query->where( $this->foreign_key, 'in', $local_keys );
+		$this->query->group_result( $this->foreign_key );
+		
+		$this->query->limit( null );
+	}
+	
+	/**
+	 * Prepare the query with collection select
+	 *
+	 * @param string 			$relation
+	 * @param array  			$collection
+	 * @param callback			$callback
+	 * @return void
+	 */
+	public function collection_assign( $relation, &$collection, $callback = null ) 
+	{
+		// make the query
+		$this->collection_query( $collection );
+		
+		call_user_func_array( $callback, array( &$this->query ) );
+		
+		// get the reults
+		$results = $this->query->run();
+		
+		foreach( $collection as $item )
+		{
+			if ( $this->singleton )
+			{
+				$item->raw_set( $relation, reset( $results[ $item->raw( $this->local_key ) ] ) );
+			}
+			else
+			{
+				$item->raw_set( $relation, $results[ $item->raw( $this->local_key ) ] );
+			}
+		}
+	}
+	
+	/**
+	 * Forward the query functionality
+	 *
+	 * @param string			$method
+	 * @param array 			$params
+	 * @return mixed
+	 */
+	public function __call( $method, $params )
+	{
+		$results = call_user_func_array( array( $this->query, $method ), $params );
+		
+		// because we want to keep the realtionship object
+		// we have to return this if its still the query
+		if ( $results === $this->query )
+		{
+			return $this;
+		}
+		
+		return $results;
+	}
+}

+ 44 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model/Relation/BelongsTo.php

@@ -0,0 +1,44 @@
+<?php namespace DB;
+/**
+ * DB Model Relation 
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Model_Relation_BelongsTo extends Model_Relation
+{
+	/**
+	 * Get foreign key from the models
+	 *
+	 * @return void
+	 */
+	protected function get_local_key()
+	{
+		return basename( call_user_func( $this->foreign_model.'::_model', 'name' ) ).'_'.call_user_func( $this->foreign_model.'::_model', 'primary_key' );
+	}
+	
+	/**
+	 * Get foreign key from the models
+	 *
+	 * @return void
+	 */
+	protected function get_foreign_key()
+	{
+		return call_user_func( $this->foreign_model.'::_model', 'primary_key' );
+	}
+	
+	/**
+	 * Prepare the query object
+	 *
+	 * @return void
+	 */
+	protected function prepare_query() 
+	{
+		$this->query->where( $this->foreign_key, $this->local_model->{$this->local_key} )
+			->limit( 1 );
+	}
+}

+ 30 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model/Relation/HasMany.php

@@ -0,0 +1,30 @@
+<?php namespace DB;
+/**
+ * DB Model Relation 
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Model_Relation_HasMany extends Model_Relation
+{
+	/**
+	 * Should this relationship deliver a single item
+	 *
+	 * @var bool
+	 */ 
+	public $singleton = false;
+	
+	/**
+	 * Prepare the query object
+	 *
+	 * @return void
+	 */
+	protected function prepare_query() 
+	{
+		$this->query->where( $this->foreign_key, $this->local_model->{$this->local_key} );
+	}
+}

+ 24 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Model/Relation/HasOne.php

@@ -0,0 +1,24 @@
+<?php namespace DB;
+/**
+ * DB Model Relation 
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Model_Relation_HasOne extends Model_Relation
+{
+	/**
+	 * Prepare the query object
+	 *
+	 * @return void
+	 */
+	protected function prepare_query() 
+	{
+		$this->query->where( $this->foreign_key, $this->local_model->{$this->local_key} )
+			->limit( 1 );
+	}
+}

+ 12 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/ModelException.php

@@ -0,0 +1,12 @@
+<?php namespace DB;
+/**
+ * Model Exception
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class ModelException extends Exception {}

+ 328 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query.php

@@ -0,0 +1,328 @@
+<?php namespace DB;
+/**
+ * The Query object
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Query
+{	
+	/**
+	 * The default primary key
+	 *
+	 * @var string
+	 */
+	protected static $_default_key = null;
+	
+	/**
+	 * Static init
+	 */
+	public static function _init()
+	{
+		static::$_default_key = \ClanCats::$config->get( 'database.default_primary_key', 'id' );
+	}
+	
+	/**
+	 * create a new query object with the default params
+	 *
+	 * @param string 		$table
+	 * @param string 		$handler
+	 *
+	 * @return Query
+	 */
+	protected static function create( $table, $handler = null )
+	{
+		return new static( $table, $handler );
+	}
+	
+	/**
+	 * Create an insert
+	 *
+	 * @param string			$table
+	 * @param array 			$values
+	 * @param string 		$handler
+	 * @return DB\Query
+	 */
+	public static function insert( $table, $values = array(), $handler = null )
+	{
+		return Query_Insert::create( $table, $handler )->values( $values );
+	}
+	
+	/**
+	 * Create an update
+	 *
+	 * @param string			$table
+	 * @param array 			$values
+	 * @param string 		$handler
+	 * @return DB\Query
+	 */
+	public static function update( $table, $values = array(), $handler = null )
+	{
+		return Query_Update::create( $table, $handler )->set( $values );
+	}
+	
+	/**
+	 * Create a delete
+	 *
+	 * @param string			$table
+	 * @param string 		$handler
+	 * @return DB\Query
+	 */
+	public static function delete( $table, $handler = null )
+	{
+		return Query_Delete::create( $table, $handler );
+	}
+	
+	/**
+	 * Select data from the database
+	 *
+	 * @param string			$table
+	 * @param array			$fields
+	 * @param string	 		$handler
+	 * @return mixed
+	 */
+	public static function select( $table, $fields = array(), $handler = null )
+	{
+		return Query_Select::create( $table, $handler )->fields( $fields );
+	}
+	
+	/**
+	 * The used table
+	 * 
+	 * @var string
+	 */
+	public $table = null;
+	
+	/**
+	 * The handler instance to execute the query
+	 * 
+	 * @var Handler
+	 */
+	public $handler = null;
+	
+	/**
+	 * The query where statements
+	 *
+	 * @var array
+	 */
+	public $wheres = array();
+	
+	/**
+	 * the query offset
+	 *
+	 * @var int
+	 */
+	public $offset = 0;
+	
+	/**
+	 * the query limit
+	 *
+	 * @var int
+	 */
+	public $limit = null;
+	
+	/**
+	 * Query constructor
+	 *
+	 * @param string			$table
+	 * @param string 		$handler
+	 * @return void
+	 */
+	public function __construct( $table = null, $handler = null )
+	{
+		$this->table = $table;
+		$this->handler( $handler );
+	}
+	
+	/**
+	 * Set the query table
+	 *
+	 * @param string 		$table
+	 * @return void
+	 */
+	public function table( $table )
+	{
+		$this->table = $table;
+	}
+	
+	/**
+	 * Set the query handler
+	 *
+	 * @param string 		$handler		The DB handler instance name
+	 * @return void
+	 */
+	public function handler( $handler )
+	{
+		$this->handler = Handler::create( $handler );
+	}
+	
+	/**
+	 * Create a where statement
+	 *
+	 * where query: <$column> <$param1> <$param2> <$type>
+	 * 
+	 * example:
+	 *     ->where( 'name', 'ladina' ) // name = landina
+	 *     ->where( 'age', '>', 18 )
+	 *     ->where( 'name', 'in', array( 'johanna', 'jennifer' ) )
+	 *
+	 * @param string		$column			The SQL column
+	 * @param mixed		$param1			
+	 * @param mixed		$param2
+	 * @param string		$type			The where type ( and, or )
+	 *
+	 * @return self
+	 */
+	public function where( $column, $param1 = null, $param2 = null, $type = 'and' ) 
+	{
+		// if this is the first where element we are going to change
+		// the where type to 'where'
+		if ( empty( $this->wheres ) )
+		{
+			$type = 'where';
+		}
+		
+		// when column is an array we assume to make a bulk and where.
+		// array( 'name' => 'ladina', 'language' => 'de' )
+		// where name = ladina and language = de
+		if ( is_array( $column ) ) 
+		{
+			foreach( $column as $key => $val ) 
+			{
+				$this->where( $key, $val, null, $type );	
+			}
+			
+			return $this;
+		}
+		
+		// to make nested wheres possible you can pass an closure 
+		// wich will create a new query where you can add your nested wheres
+		if ( is_closure( $column ) )
+		{
+			$this->wheres[] = array( $type, $column ); return $this;
+		}
+		
+		// when param2 is null we replace param2 with param one as the 
+		// value holder and make param1 to the = operator.
+		if ( is_null( $param2 ) ) 
+		{
+			$param2 = $param1; $param1 = '=';
+		}
+		
+		// if the param2 is an array we filter it. Im no more sure why 
+		// but it's there since 2 years so i think i had a reason.
+		if ( is_array( $param2 ) ) 
+		{
+			$param2 = array_unique( $param2 );
+		}
+		
+		$this->wheres[] = array( $type, $column, $param1, $param2 ); return $this;
+	}
+	
+	/**
+	 * Create an or where statement
+	 *
+	 * This is the same as the normal where just with a fixed type
+	 *
+	 * @param string		$column			The SQL column
+	 * @param mixed		$param1			
+	 * @param mixed		$param2
+	 *
+	 * @return self
+	 */
+	public function or_where( $column, $param1 = null, $param2 = null )
+	{
+		return $this->where( $column, $param1, $param2, 'or' );
+	}
+	
+	/**
+	 * Create an and where statement
+	 *
+	 * This is the same as the normal where just with a fixed type
+	 *
+	 * @param string		$column			The SQL column
+	 * @param mixed		$param1			
+	 * @param mixed		$param2
+	 *
+	 * @return self
+	 */
+	public function and_where( $column, $param1 = null, $param2 = null )
+	{
+		return $this->where( $column, $param1, $param2, 'and' );
+	}
+	
+	/**
+	 * Set the query limit
+	 *
+ 	 * @param int		$limit
+	 * @param int 		$limit2
+	 * @return void
+	 */
+	public function limit( $limit, $limit2 = null ) 
+	{
+		if ( !is_null( $limit2 ) ) 
+		{
+			$this->offset = (int) $limit;
+			$this->limit  = (int) $limit2;
+		}
+		else 
+		{
+			$this->limit = $limit;
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * Create an query limit based on a page and a page size
+	 *
+	 * @param int		$page
+	 * @param int 		$size
+	 * @return void
+	 */
+	public function page( $page, $size = 25 ) 
+	{	
+		// validate page
+		if ( (int) $page < 1 )
+		{
+			$page = 1;
+		}
+		
+		$this->limit = (int) $size;
+		$this->offset = (int) ( ( $size * $page ) - $size );
+		
+		return $this;
+	}
+	
+	/**
+	 * Build the query to a string
+	 *
+	 * @return string
+	 */
+	public function build()
+	{
+		$this->handler->builder()->clear_parameters();
+	}
+	
+	/**
+	 * Build and execute the current query
+	 * This wil run the local build function and pass the parameters
+	 * from the builder object in the handler
+	 *
+	 * @param string 		$handler
+	 * @return mixed
+	 */
+	public function run( $handler = null )
+	{
+		if ( !is_null( $handler ) )
+		{
+			$this->handler( $handler );
+		}
+		
+		return $this->handler->run( $this->build(), $this->handler->builder()->parameters );
+	}
+}

+ 28 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query/Delete.php

@@ -0,0 +1,28 @@
+<?php namespace DB;
+/**
+ * The Query object
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Query_Delete extends Query
+{
+	/**
+	 * Build the query to a string
+	 *
+	 * @return string
+	 */
+	public function build()
+	{
+		// Some task's like clearing the query parameters
+		// are handeld by the parent build function.
+		parent::build();
+
+		// Lets run the Builder by passing the current query object
+		return $this->handler->builder()->compile_delete( $this );
+	}
+}

+ 88 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query/Insert.php

@@ -0,0 +1,88 @@
+<?php namespace DB;
+/**
+ * The Query object
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Query_Insert extends Query
+{
+	/**
+	 * values container
+	 *
+	 * @var array 
+	 */
+	public $values = array();
+	
+	/**
+ 	 * make an insert ignore
+	 *
+	 * @var bool 
+	 */
+	public $ignore = false;
+	
+	/**
+	 * Insert ignore setter
+	 *
+	 * @param bool		$ignore
+	 * @return void
+	 */
+	public function ignore( $ignore = true )
+	{
+		$this->ignore = $ignore; return $this;
+	}
+	
+	/**
+	 * Add values to the insert
+	 *
+	 * @param array 		$values
+	 * @return void
+	 */
+	public function values( array $values )
+	{
+		// do nothing if we get nothing
+		if ( empty( $values ) )
+		{
+			return $this;
+		}
+		
+		// check if the the passed array is a collection.
+		// because we want to be able to insert bulk values.
+		if ( !\CCArr::is_collection( $values ) )
+		{
+			$values = array( $values );
+		}
+		
+		// because we could recive the arrays in diffrent order 
+		// we have to sort them by their key.
+		foreach( $values as $key => $value )
+		{
+			ksort( $value ); $values[$key] = $value;
+		}
+		
+		// merge the new values with the existing ones.
+		$this->values = array_merge( $this->values, $values ); 
+		
+		// return self so we can continue running the next function
+		return $this;
+	}
+	
+	/**
+	 * Build the query to a string
+	 *
+	 * @return string
+	 */
+	public function build()
+	{
+		// Some task's like clearing the query parameters
+		// are handeld by the parent build function.
+		parent::build();
+		
+		// Lets run the Builder by passing the current query object
+		return $this->handler->builder()->compile_insert( $this );
+	}
+}

+ 436 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query/Select.php

@@ -0,0 +1,436 @@
+<?php namespace DB;
+/**
+ * The Query object
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Query_Select extends Query
+{
+	/**
+	 * fields container
+	 *
+	 * @var array 
+	 */
+	public $fields = array();
+
+	/**
+ 	 * make an distinct
+	 *
+	 * @var bool 
+	 */
+	public $distinct = false;
+	
+	/**
+	 * order by container
+	 *
+	 * @var array 
+	 */
+	public $orders = array();
+	
+	/**
+	 * order by container
+	 *
+	 * @var array 
+	 */
+	public $groups = array();
+
+	/**
+	 * group the results
+	 *
+	 * @var false|string 
+	 */
+	public $group_result = false;
+	
+	/**
+	 * forward key
+	 *
+	 * @var false|string 
+	 */
+	public $forward_key = false;
+	
+	/**
+	 * the fetching arguments
+	 *
+	 * @var array
+	 */
+	public $fetch_arguments = array( 'obj' );
+	
+	/**
+	 * the fetching handler
+	 *
+	 * @var callback
+	 */
+	public $fetch_handler = null;
+
+	/**
+	 * Distinct select setter
+	 *
+	 * @param bool		$ignore
+	 * @return self
+	 */
+	public function distinct( $distinct = true )
+	{
+		$this->distinct = $distinct; return $this;
+	}
+
+	/**
+	 * Set the select fields
+	 *
+	 * @param array 		$values
+	 * @return self
+	 */
+	public function fields( $fields )
+	{
+		if ( !is_array( $fields ) && !is_null( $fields ) )
+		{
+			$fields = array( $fields );
+		}
+		
+		// do nothing if we get nothing
+		if ( empty( $fields ) || $fields == array( '*' ) )
+		{
+			$this->fields = array(); return $this;
+		}
+		
+		$this->fields = $fields;
+
+		// return self so we can continue running the next function
+		return $this;
+	}
+	
+	/**
+	 * Add select fields
+	 *
+	 * @param array 		$values
+	 * @return self
+	 */
+	public function add_fields( $fields )
+	{
+		if ( !is_array( $fields ) )
+		{
+			$fields = array( $fields );
+		}
+		
+		// merge the new values with the existing ones.
+		$this->fields = array_merge( $this->fields, $fields ); 
+	
+		// return self so we can continue running the next function
+		return $this;
+	}
+
+	/**
+	 * Set the order parameters
+	 *
+	 * @param mixed 	$cols
+	 * @param string	$order
+	 * @return self
+	 */
+	public function order_by( $cols, $order = 'asc' ) 
+	{
+		if ( !is_array( $cols ) ) 
+		{
+			$this->orders[] = array( $cols, $order ); return $this;
+		}
+		else
+		{
+			foreach( $cols as $key => $col ) 
+			{
+				if ( is_numeric( $key ) ) 
+				{
+					$this->orders[] = array( $col, $order );	
+				} 
+				else 
+				{
+					$this->orders[] = array( $key, $col );
+				}
+			}
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * Add group by stuff
+	 *
+	 * @param mixed 		$key		By passing an array you can add multiple groups
+	 * @return self
+	 */
+	public function group_by( $key ) 
+	{
+		if ( !is_array( $key ) )
+		{
+			$key = array( $key );	
+		}
+		
+		foreach( $key as $group_key )
+		{
+			$this->groups[] = $group_key;
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * Forward a result value as array key
+	 *
+	 * @param string|bool		$key
+	 * @return self
+	 */
+	public function forward_key( $key = true )
+	{
+		if ( $key === false )
+		{
+			$this->forward_key = false;
+		}
+		elseif ( $key === true )
+		{
+			$this->forward_key = \ClanCats::$config->get( 'database.default_primary_key', 'id' );
+		}
+		else
+		{
+			$this->forward_key = $key;
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * Group results by a column
+	 *
+	 * example:
+	 *     array(
+	 *         'name' => 'John', 'age' => 18,
+	 *     ),
+	 *     array(
+	 *         'name' => 'Jeff', 'age' => 32,
+	 *     ),
+	 *     array(
+	 *         'name' => 'Jenny', 'age' => 18,
+	 *     ),
+	 * To:
+	 *     '18' => array(
+	 *          array( 'name' => 'John', 'age' => 18 ),
+	 *          array( 'name' => 'Jenny', 'age' => 18 ),
+	 *     ),
+	 *     '32' => array(
+	 *          array( 'name' => 'Jeff', 'age' => 32 ),
+	 *     ),
+	 *
+	 * @param string|bool		$key
+	 * @return self
+	 */
+	public function group_result( $key )
+	{
+		if ( $key === false )
+		{
+			$this->group_result = false;
+		}
+		else
+		{
+			$this->group_result = $key;
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * Set the fetch handler for this query
+	 *
+	 * @param callback 		$callback
+	 * @return self
+	 */
+	public function fetch_handler( $callback )
+	{
+		$this->fetch_handler = $callback; return $this;
+	}
+	
+	/**
+	 * Build the query to a string
+	 *
+	 * @return string
+	 */
+	public function build()
+	{
+		// Some task's like clearing the query parameters
+		// are handeld by the parent build function.
+		parent::build();
+		
+		// Lets run the Builder by passing the current query object
+		return $this->handler->builder()->compile_select( $this );
+	}
+	
+	/**
+	 * Build and execute the current query
+	 * This wil run the local build function and pass the parameters
+	 * from the builder object in the handler
+	 *
+	 * @param string 		$handler
+	 * @return mixed
+	 */
+	public function run( $handler = null )
+	{
+		if ( !is_null( $handler ) )
+		{
+			$this->handler( $handler );
+		}
+		
+		// if there is a special fetch handler defined pass him all the 
+		// needed parameters and retrive the results
+		if ( !is_null( $this->fetch_handler ) )
+		{
+			$results = call_user_func_array( $this->fetch_handler, array(
+				&$this
+			));
+		}
+		// otherwise simply do the default select fetch
+		else 
+		{
+			$results = $this->handler->fetch( $this->build(), $this->handler->builder()->parameters, $this->fetch_arguments );
+		}
+		
+		// In case we should forward a key means using a value
+		// from every result as array key.
+		if ( $this->forward_key !== false )
+		{
+			$raw_results = $results;
+			$results = array();
+			
+			if ( in_array( 'assoc', $this->fetch_arguments ) )
+			{
+				foreach( $raw_results as $result )
+				{
+					$results[$result[$this->forward_key]] = $result;
+				}
+			}
+			else
+			{
+				foreach( $raw_results as $result )
+				{
+					$results[$result->{$this->forward_key}] = $result;
+				}
+			}
+		}
+		
+		// Group the results by a value
+		if ( $this->group_result !== false )
+		{
+			$raw_results = $results;
+			$results = array();
+			
+			if ( in_array( 'assoc', $this->fetch_arguments ) )
+			{
+				foreach( $raw_results as $key => $result )
+				{
+					$results[$result[$this->group_result]][$key] = $result;
+				}
+			}
+			else
+			{
+				foreach( $raw_results as $key => $result )
+				{
+					$results[$result->{$this->group_result}][$key] = $result;
+				}
+			}
+		}
+		
+		// when the limit is 1 we are going to return the 
+		// result directly
+		if ( $this->limit === 1 )
+		{
+			return reset( $results );
+		}
+		
+		return $results;
+	}
+	
+	/**
+	 * Get one result sets limit to 1 and executes
+	 *
+	 * @param string			$name
+	 * @return mixed
+	 */
+	public function one( $name = null ) 
+	{
+		return $this->limit( 0, 1 )->run( $name );
+	}
+	
+	/**
+	 * Find something, means select one record by key
+	 *
+	 * @param int			$id
+	 * @param string			$key
+	 * @param string			$name
+	 * @return mixed
+	 */
+	public function find( $id, $key = null, $name = null ) 
+	{
+		if ( is_null( $key ) )
+		{
+			$key = Query::$_default_key;
+		}
+		
+		return $this->where( $key, $id )->one( $name );
+	}
+	
+	/**
+	 * Get the first result by key
+	 *
+	 * @param string			$key
+	 * @param string			$name
+	 * @return mixed
+	 */
+	public function first( $key = null, $name = null ) 
+	{
+		if ( is_null( $key ) )
+		{
+			$key = Query::$_default_key;
+		}
+		
+		return $this->order_by( $key, 'asc' )->one( $name );
+	}
+	
+	/**
+	 * Get the last result by key
+	 *
+	 * @param string			$key
+	 * @param string			$name
+	 * @return mixed
+	 */
+	public function last( $key = null, $name = null ) 
+	{
+		if ( is_null( $key ) )
+		{
+			$key = Query::$_default_key;
+		}
+		
+		return $this->order_by( $key, 'desc' )->one( $name );
+	}
+	
+	/**
+	 * Just get a single value from the db
+	 *
+	 * @param string			$column
+	 * @param string			$name
+	 * @return mixed
+	 */
+	public function column( $column, $name = null ) 
+	{
+		return $this->fields( $column )->one( $name )->$column;
+	}
+	
+	/**
+	 * Just return the count result 
+	 *
+	 * @param string 	$db
+	 * @return int
+	 */
+	public function count( $name = null ) 
+	{
+		return (int) $this->column( DB::raw( "COUNT(*)" ), $name );
+	}
+}

+ 64 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Query/Update.php

@@ -0,0 +1,64 @@
+<?php namespace DB;
+/**
+ * The Query object
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Query_Update extends Query
+{
+	/**
+	 * values container
+	 *
+	 * @var array 
+	 */
+	public $values = array();
+	
+	/**
+	 * Add set values to the update query
+	 *
+	 * @param string|array 		$param1
+	 * @param mixed				$param2
+	 * @return self
+	 */
+	public function set( $param1, $param2 = null )
+	{
+		// do nothing if we get nothing
+		if ( empty( $param1 ) )
+		{
+			return $this;
+		}
+		
+		// when param 2 is not null we assume that only one set is passed
+		// like: set( 'name', 'Lu' ); instead of set( array( 'name' => 'Lu' ) );
+		if ( !is_null( $param2 ) )
+		{
+			$param1 = array( $param1 => $param2 );
+		}
+		
+		// merge the new values with the existing ones.
+		$this->values = array_merge( $this->values, $param1 ); 
+		
+		// return self so we can continue running the next function
+		return $this;
+	}
+	
+	/**
+	 * Build the query to a string
+	 *
+	 * @return string
+	 */
+	public function build()
+	{
+		// Some task's like clearing the query parameters
+		// are handeld by the parent build function.
+		parent::build();
+		
+		// Lets run the Builder by passing the current query object
+		return $this->handler->builder()->compile_update( $this );
+	}
+}

+ 12 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/QueryException.php

@@ -0,0 +1,12 @@
+<?php namespace DB;
+/**
+ * Query Exception
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class QueryException extends Exception {}

+ 105 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Test/Case.php

@@ -0,0 +1,105 @@
+<?php namespace DB;
+/**
+ * Database test case
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+
+use CCConfig;
+use CCPath;
+
+class Test_Case extends \PHPUnit_Framework_TestCase
+{
+	/**
+	 * Is a database configured?
+	 *
+	 * @var bool
+	 */
+	public static $dbtests = false;
+	
+	/**
+	 * Return a string of the database config name 
+	 * used for the test in this class
+	 *
+	 * @return string
+	 */
+	protected static function main_database()
+	{
+		// the normal test case uses the app database
+		return 'app';
+	}
+
+	/**
+	 * Check if DB test are possible
+	 *
+	 * @return void
+	 */
+	public static function setUpBeforeClass() 
+	{	
+		$current_datatbase = static::main_database();
+		
+		// lets make sure that we have an db configuration for phpunit
+		if ( CCConfig::create( 'database' )->has( $current_datatbase ) )
+		{	
+			// lets try to connect to that database if the conection
+			// fails we just return and continue the other tests
+			try { DB::connect( $current_datatbase ); }
+			catch ( \PDOException $e ) { return; }
+
+			// connection succeeded?
+			static::$dbtests = true;
+
+			// overwrite the main db
+			CCConfig::create( 'database' )->set( 'main', $current_datatbase );
+
+			// kill all connections
+			Handler::kill_all_connections();
+			
+			// check for any sql files that should be executed needed 
+			// for theses tests. We simply check if a file exists in the
+			// CCUnit bundle "db_<current_database>.sql"
+			if ( file_exists( CCPath::get( 'CCUnit::db_'.$current_datatbase.'.sql' ) ) )
+			{
+				$queries = explode( ';', file_get_contents( CCPath::get( 'CCUnit::db_'.$current_datatbase.'.sql' ) ) );
+				
+				foreach( $queries as $query )
+				{
+					$query = trim( $query );
+				
+					if ( !empty( $query ) )
+					{
+						DB::run( $query, array(), 'phpunit' );
+					}
+				}
+			}
+		}
+	}
+
+	public static function tearDownAfterClass() 
+	{	
+		// write the main database back to app
+		CCConfig::create( 'database' )->set( 'main', 'app' );
+		
+		// kill all connections
+		Handler::kill_all_connections();
+	}
+
+	/**
+	 * Check if we can execute db tests
+	 * And add a warning to phpunit that we skipped the test.
+	 *
+	 * @return void
+	 */
+	protected function setUp()
+	{
+		if ( !static::$dbtests )
+		{
+			$this->markTestSkipped( "Warning! Could not connect to phpunit DB. skipping DB unit test." );
+		}
+	}
+}

+ 70 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Database/Test/Case/Database.php

@@ -0,0 +1,70 @@
+<?php namespace DB;
+/**
+ * Database test case
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+
+use CCConfig;
+use CCPath;
+
+class Test_Case_Database extends Test_Case
+{
+	/**
+	 * people data bulk
+	 */
+	public static function people_provider_bulk() 
+	{
+		return array(
+			array(
+				array(
+					array( 'name' => 'Mario', 'age' => 20, 'library_id' => 1 ),
+					array( 'name' => 'Ladina', 'age' => 20, 'library_id' => 2 ),
+					array( 'name' => 'Johanna', 'age' => 18, 'library_id' => 1 ),
+					array( 'name' => 'Jenny', 'age' => 22, 'library_id' => 0 ),
+					array( 'name' => 'Melanie', 'age' => 19, 'library_id' => 0 ),
+					array( 'name' => 'Tarek', 'age' => 20, 'library_id' => 3 ),
+					array( 'name' => 'John', 'age' => 42, 'library_id' => 4 ),
+				),
+			),
+		);
+	}
+	
+	/**
+	 * people data
+	 */
+	public static function people_provider() 
+	{
+		return array(
+			array(
+				array( 'name' => 'Mario', 'age' => 20, 'library_id' => 1 ),
+			),
+			array(
+				array( 'name' => 'Ladina', 'library_id' => 2, 'age' => 20 ),
+			),
+			array(
+				array( 'name' => 'Johanna', 'age' => -18, 'library_id' => 1 ),
+			),
+			array(
+				array( 'age' => 22, 'library_id' => 0,  'name' => 'Jenny' ),
+			),
+		);
+	}
+	
+	/**
+	 * Return a string of the database config name 
+	 * used for the test in this class
+	 *
+	 * @return string
+	 */
+	protected static function main_database()
+	{
+		// these tests should use a seperate table
+		return 'phpunit';
+	}
+}

+ 438 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/CCMail.php

@@ -0,0 +1,438 @@
+<?php namespace Mail;
+/**
+ * CCMail 
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCMail 
+{	
+	/**
+	 * Create a new Mail instance
+	 *
+	 * @param string 			$transporter
+	 * @return CCMail
+	 */
+	public static function create( $transporter = null, $config = null ) 
+	{
+		return new static( $transporter, $config );
+	}
+	
+	/**
+	 * The mail transporter
+	 *
+	 * @var Mail\Transporter
+	 */
+	protected $transporter = null;
+	
+	/**
+	 * Is this a plaintext email
+	 *
+	 * @var bool
+	 */ 
+	protected $is_plaintext = false;
+	
+	/**
+	 * Mail recipients
+	 *
+	 * @var array
+	 */
+	protected $to = array();
+	
+	/**
+	 * Blind carbon copies
+	 *
+	 * @var array
+	 */
+	protected $bcc = array();
+	
+	/**
+	 * Carbon copies
+	 *
+	 * @var array
+	 */
+	protected $cc = array();
+	
+	/**
+	 * From email and name
+	 *
+	 * @var array[email => name]
+	 */
+	protected $from = array();
+	
+	/**
+	 * Attachemnts
+	 *
+	 * @var array
+	 */
+	protected $attachments = array();
+	
+	/**
+	 * Mail subject
+	 *
+	 * @var string
+	 */ 
+	protected $subject = "";
+	
+	/**
+	 * Plaintext message
+	 *
+	 * @var string
+	 */
+	protected $plaintext = null;
+	
+	/** 
+	 * HTML Message
+	 *
+	 * @var string|CCView
+	 */
+	public $message = "";
+	
+	/**
+	 * Mail layout view
+	 *
+	 * @var CCView
+	 */
+	public $layout = null;
+	
+	/**
+	 * Mail constructor
+	 *
+	 * @param string			$transporter
+	 * @return void
+	 */
+	public function __construct( $transporter = null, $config = null ) 
+	{
+		// prepare the transporter
+		$this->transporter = Transporter::create( $transporter, $config );
+		
+		// prepare the layout if we have one
+		if ( $layout = \CCConfig::create( 'mail' )->layout )
+		{
+			$this->layout = \CCView::create( $layout );
+		}
+		
+		// fill the from with the defaults
+		$this->from = \CCConfig::create( 'mail' )->from;
+	}
+	
+	/**
+	 * Add a recipient
+	 *
+	 *     $mail->to( '[email protected]' );
+	 *     $mail->to( '[email protected]', 'Jennifer Rostock' )
+	 *     $mail->to( array( '[email protected]' => 'Han Solo' ) );
+	 * 
+	 * @param string 		$email
+	 * @param string 		$name
+	 * @return self
+	 */
+	public function to( $email, $name = null ) 
+	{	
+		if ( !is_array( $email ) ) 
+		{
+			$email = array( $email => $name );
+		}
+		
+		foreach( $email as $address => $name )
+		{
+			if ( is_numeric( $address ) && is_string( $name ) )
+			{
+				$this->to[$name] = null;
+			}
+			else
+			{
+				$this->to[$address] = $name;
+			}
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * Add Blind carbon copies
+	 * 
+	 * Works like the 'to' function.
+	 * 
+	 * @param string 		$email
+	 * @param string 		$name
+	 * @return self
+	 */
+	public function bcc( $email, $name = null ) 
+	{	
+		if ( !is_array( $email ) ) 
+		{
+			$email = array( $email => $name );
+		}
+		
+		foreach( $email as $address => $name )
+		{
+			if ( is_numeric( $address ) && is_string( $name ) )
+			{
+				$this->bcc[$name] = null;
+			}
+			else
+			{
+				$this->bcc[$address] = $name;
+			}
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * Add Carbon copies
+	 * 
+	 * Works like the 'to' function.
+	 * 
+	 * @param string 		$email
+	 * @param string 		$name
+	 * @return self
+	 */
+	public function cc( $email, $name = null ) 
+	{	
+		if ( !is_array( $email ) ) 
+		{
+			$email = array( $email => $name );
+		}
+		
+		foreach( $email as $address => $name )
+		{
+			if ( is_numeric( $address ) && is_string( $name ) )
+			{
+				$this->cc[$name] = null;
+			}
+			else
+			{
+				$this->cc[$address] = $name;
+			}
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * Set the from email and name
+	 *
+	 * @param string 		$mail
+	 * @param string 		$name
+	 * @return self
+	 */
+	public function from( $email, $name = null ) 
+	{
+		$this->from = array( $email, $name ); return $this;
+	}
+	
+	/**
+	 * Add a recipient
+	 *
+	 *     $mail->attachment( '/path/to/my/file.zip' );
+	 *     $mail->attachment( '/some/image.jpg', 'your_photo.jpg' );
+	 *     $mail->attachment( array( '/some/other/image.jpg' => 'wallpaper.jpg' ) );
+	 * 
+	 * @param string 		$path		The path to your file
+	 * @param string 		$name
+	 * @return self
+	 */
+	public function attachment( $path, $name )
+	{
+		if ( !is_array( $path ) ) 
+		{
+			$path = array( $path => $name );
+		}
+		
+		foreach( $path as $file => $name )
+		{
+			if ( is_numeric( $file ) && is_string( $name ) )
+			{
+				$this->attachments[$name] = null;
+			}
+			else
+			{
+				$this->attachments[$file] = $name;
+			}
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * Set the mail subject
+	 *
+	 * @param string 		$subject
+	 * @return self
+	 */
+	public function subject( $subject ) 
+	{
+		$this->subject = $subject; return $this;
+	}
+	
+	/**
+	 * Set the mail plaintext message
+	 *
+	 * @param string 		$plaintext
+	 * @return self
+	 */
+	public function plaintext( $plaintext ) 
+	{
+		$this->plaintext = $plaintext; return $this;
+	}
+	
+	/**
+	 * Set the mail message
+	 *
+	 * @param string 		$message
+	 * @return self
+	 */
+	public function message( $message )
+	{
+		$this->message = $message; return $this;
+	}
+	
+	/**
+	 * Set a view as message
+	 *
+	 * @param string 		$view
+	 * @return self
+	 */
+	public function view( $view )
+	{
+		$this->message = \CCView::create( $view ); return $this;
+	}
+	
+	/** 
+	 * Is the current message just plaintext
+	 *
+	 * @param bool		$is
+	 * @return self
+	 */
+	public function is_plaintext( $is = true )
+	{
+		$this->is_plaintext = $is;
+	}
+	
+	/**
+	 * Render the message
+	 *
+	 * @return string
+	 */
+	public function render() 
+	{	
+		$message = $this->message;
+		
+		reset( $this->to );
+		
+		// default view parameters for the message and the layout
+		$params = array(
+			'mail' => $this,
+			'to_email' => key($this->to),
+			'to_name' => $this->to[ key($this->to) ],
+		);
+		
+		// if the message is a view
+		if ( $message instanceof \CCView ) 
+		{	
+			$message->_data = $message->_data + $params;
+			$message = $message->render();
+		}
+		
+		// prepare the layout
+		if ( $this->layout ) 
+		{
+			$this->layout->content = $message;
+			$this->layout->_data = $this->layout->_data + $params;
+			
+			$message = $this->layout->render();
+		}
+		
+		// return the renderd message
+		return $message;
+	}
+	
+	/**
+	 * Prepare the message for sending to the transport
+	 *
+	 * @return void 
+	 */
+	public function send()
+	{
+		// load the mail configuration
+		$config = \CCConfig::create( 'mail' );
+		
+		// when mailing is disabled do nothing just return
+		if ( $config->disabled === true )
+		{
+			return;
+		}
+		
+		// we cannot send a mail without recipients
+		if ( empty( $this->to ) )
+		{
+			throw new Exception( "Cannot send mail without recipients." );
+		}
+		
+		// is a catch all enabled?
+		if ( $config->get( 'catch_all.enabled' ) === true )
+		{
+			// to be able to modify the mail without removing
+			// the user options we have to clone the mail
+			$mail = clone $this;
+			
+			// we have to remove all recipients ( to, ccc, bcc ) and set them
+			// to our catch all recipients
+			$mail->to = array();
+			$mail->cc = array();
+			$mail->bcc = array();
+			
+			$mail->to( $config->get( 'catch_all.addresses' ) );
+			
+			// transport the cloned mail
+			return $mail->transport( $config->get( 'catch_all.transporter' ) );
+		}
+		
+		// transport the mail
+		$this->transport();
+	}
+	
+	/**
+	 * Transport the message
+	 *
+	 * @param string 		$transport 		Use a diffrent transporter
+	 * @return void
+	 */
+	protected function transport( $transporter = null )
+	{
+		if ( !is_null( $transporter ) )
+		{
+			$transporter = Transporter::create( $transporter );
+		}
+		else
+		{
+			$transporter = $this->transporter;
+		}
+		
+		// pass the current mail to the transporter
+		$transporter->send( $this );
+	}
+	
+	/**
+	 * Export the mail data 
+	 *
+	 * @return array
+	 */	
+	public function export_data()
+	{
+		$data = get_object_vars( $this ); 
+		
+		// render the message
+		$data['message'] = $this->render();
+		
+		unset( $data['transporter'] );
+		
+		return $data;
+	}
+}

+ 12 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Exception.php

@@ -0,0 +1,12 @@
+<?php namespace Mail;
+/**
+ * Mail Exception
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Exception extends \Core\CCException {}

+ 3417 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/PHPMailer/class.phpmailer.php

@@ -0,0 +1,3417 @@
+<?php namespace Mail\PHPMailer;
+/**
+ * PHPMailer - PHP email creation and transport class.
+ * PHP Version 5
+ * @package PHPMailer
+ * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
+ * @author Jim Jagielski (jimjag) <[email protected]>
+ * @author Andy Prevost (codeworxtech) <[email protected]>
+ * @author Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2014 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * PHPMailer - PHP email creation and transport class.
+ * @package PHPMailer
+ * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
+ * @author Jim Jagielski (jimjag) <[email protected]>
+ * @author Andy Prevost (codeworxtech) <[email protected]>
+ * @author Brent R. Matzelle (original founder)
+ */
+class PHPMailer
+{
+    /**
+     * The PHPMailer Version number.
+     * @type string
+     */
+    public $Version = '5.2.8';
+
+    /**
+     * Email priority.
+     * Options: 1 = High, 3 = Normal, 5 = low.
+     * @type integer
+     */
+    public $Priority = 3;
+
+    /**
+     * The character set of the message.
+     * @type string
+     */
+    public $CharSet = 'iso-8859-1';
+
+    /**
+     * The MIME Content-type of the message.
+     * @type string
+     */
+    public $ContentType = 'text/plain';
+
+    /**
+     * The message encoding.
+     * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
+     * @type string
+     */
+    public $Encoding = '8bit';
+
+    /**
+     * Holds the most recent mailer error message.
+     * @type string
+     */
+    public $ErrorInfo = '';
+
+    /**
+     * The From email address for the message.
+     * @type string
+     */
+    public $From = 'root@localhost';
+
+    /**
+     * The From name of the message.
+     * @type string
+     */
+    public $FromName = 'Root User';
+
+    /**
+     * The Sender email (Return-Path) of the message.
+     * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
+     * @type string
+     */
+    public $Sender = '';
+
+    /**
+     * The Return-Path of the message.
+     * If empty, it will be set to either From or Sender.
+     * @type string
+     * @deprecated Email senders should never set a return-path header;
+     * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
+     * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
+     */
+    public $ReturnPath = '';
+
+    /**
+     * The Subject of the message.
+     * @type string
+     */
+    public $Subject = '';
+
+    /**
+     * An HTML or plain text message body.
+     * If HTML then call isHTML(true).
+     * @type string
+     */
+    public $Body = '';
+
+    /**
+     * The plain-text message body.
+     * This body can be read by mail clients that do not have HTML email
+     * capability such as mutt & Eudora.
+     * Clients that can read HTML will view the normal Body.
+     * @type string
+     */
+    public $AltBody = '';
+
+    /**
+     * An iCal message part body.
+     * Only supported in simple alt or alt_inline message types
+     * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
+     * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
+     * @link http://kigkonsult.se/iCalcreator/
+     * @type string
+     */
+    public $Ical = '';
+
+    /**
+     * The complete compiled MIME message body.
+     * @access protected
+     * @type string
+     */
+    protected $MIMEBody = '';
+
+    /**
+     * The complete compiled MIME message headers.
+     * @type string
+     * @access protected
+     */
+    protected $MIMEHeader = '';
+
+    /**
+     * Extra headers that createHeader() doesn't fold in.
+     * @type string
+     * @access protected
+     */
+    protected $mailHeader = '';
+
+    /**
+     * Word-wrap the message body to this number of chars.
+     * @type integer
+     */
+    public $WordWrap = 0;
+
+    /**
+     * Which method to use to send mail.
+     * Options: "mail", "sendmail", or "smtp".
+     * @type string
+     */
+    public $Mailer = 'mail';
+
+    /**
+     * The path to the sendmail program.
+     * @type string
+     */
+    public $Sendmail = '/usr/sbin/sendmail';
+
+    /**
+     * Whether mail() uses a fully sendmail-compatible MTA.
+     * One which supports sendmail's "-oi -f" options.
+     * @type boolean
+     */
+    public $UseSendmailOptions = true;
+
+    /**
+     * Path to PHPMailer plugins.
+     * Useful if the SMTP class is not in the PHP include path.
+     * @type string
+     * @deprecated Should not be needed now there is an autoloader.
+     */
+    public $PluginDir = '';
+
+    /**
+     * The email address that a reading confirmation should be sent to.
+     * @type string
+     */
+    public $ConfirmReadingTo = '';
+
+    /**
+     * The hostname to use in Message-Id and Received headers
+     * and as default HELO string.
+     * If empty, the value returned
+     * by SERVER_NAME is used or 'localhost.localdomain'.
+     * @type string
+     */
+    public $Hostname = '';
+
+    /**
+     * An ID to be used in the Message-Id header.
+     * If empty, a unique id will be generated.
+     * @type string
+     */
+    public $MessageID = '';
+
+    /**
+     * The message Date to be used in the Date header.
+     * If empty, the current date will be added.
+     * @type string
+     */
+    public $MessageDate = '';
+
+    /**
+     * SMTP hosts.
+     * Either a single hostname or multiple semicolon-delimited hostnames.
+     * You can also specify a different port
+     * for each host by using this format: [hostname:port]
+     * (e.g. "smtp1.example.com:25;smtp2.example.com").
+     * Hosts will be tried in order.
+     * @type string
+     */
+    public $Host = 'localhost';
+
+    /**
+     * The default SMTP server port.
+     * @type integer
+     * @TODO Why is this needed when the SMTP class takes care of it?
+     */
+    public $Port = 25;
+
+    /**
+     * The SMTP HELO of the message.
+     * Default is $Hostname.
+     * @type string
+     * @see PHPMailer::$Hostname
+     */
+    public $Helo = '';
+
+    /**
+     * The secure connection prefix.
+     * Options: "", "ssl" or "tls"
+     * @type string
+     */
+    public $SMTPSecure = '';
+
+    /**
+     * Whether to use SMTP authentication.
+     * Uses the Username and Password properties.
+     * @type boolean
+     * @see PHPMailer::$Username
+     * @see PHPMailer::$Password
+     */
+    public $SMTPAuth = false;
+
+    /**
+     * SMTP username.
+     * @type string
+     */
+    public $Username = '';
+
+    /**
+     * SMTP password.
+     * @type string
+     */
+    public $Password = '';
+
+    /**
+     * SMTP auth type.
+     * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
+     * @type string
+     */
+    public $AuthType = '';
+
+    /**
+     * SMTP realm.
+     * Used for NTLM auth
+     * @type string
+     */
+    public $Realm = '';
+
+    /**
+     * SMTP workstation.
+     * Used for NTLM auth
+     * @type string
+     */
+    public $Workstation = '';
+
+    /**
+     * The SMTP server timeout in seconds.
+     * @type integer
+     */
+    public $Timeout = 10;
+
+    /**
+     * SMTP class debug output mode.
+     * Options:
+     *   0: no output
+     *   1: commands
+     *   2: data and commands
+     *   3: as 2 plus connection status
+     *   4: low level data output
+     * @type integer
+     * @see SMTP::$do_debug
+     */
+    public $SMTPDebug = 0;
+
+    /**
+     * How to handle debug output.
+     * Options:
+     *   'echo': Output plain-text as-is, appropriate for CLI
+     *   'html': Output escaped, line breaks converted to <br>, appropriate for browser output
+     *   'error_log': Output to error log as configured in php.ini
+     * @type string
+     * @see SMTP::$Debugoutput
+     */
+    public $Debugoutput = 'echo';
+
+    /**
+     * Whether to keep SMTP connection open after each message.
+     * If this is set to true then to close the connection
+     * requires an explicit call to smtpClose().
+     * @type boolean
+     */
+    public $SMTPKeepAlive = false;
+
+    /**
+     * Whether to split multiple to addresses into multiple messages
+     * or send them all in one message.
+     * @type boolean
+     */
+    public $SingleTo = false;
+
+    /**
+     * Storage for addresses when SingleTo is enabled.
+     * @type array
+     * @TODO This should really not be public
+     */
+    public $SingleToArray = array();
+
+    /**
+     * Whether to generate VERP addresses on send.
+     * Only applicable when sending via SMTP.
+     * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
+     * @link http://www.postfix.org/VERP_README.html Postfix VERP info
+     * @type boolean
+     */
+    public $do_verp = false;
+
+    /**
+     * Whether to allow sending messages with an empty body.
+     * @type boolean
+     */
+    public $AllowEmpty = false;
+
+    /**
+     * The default line ending.
+     * @note The default remains "\n". We force CRLF where we know
+     *        it must be used via self::CRLF.
+     * @type string
+     */
+    public $LE = "\n";
+
+    /**
+     * DKIM selector.
+     * @type string
+     */
+    public $DKIM_selector = '';
+
+    /**
+     * DKIM Identity.
+     * Usually the email address used as the source of the email
+     * @type string
+     */
+    public $DKIM_identity = '';
+
+    /**
+     * DKIM passphrase.
+     * Used if your key is encrypted.
+     * @type string
+     */
+    public $DKIM_passphrase = '';
+
+    /**
+     * DKIM signing domain name.
+     * @example 'example.com'
+     * @type string
+     */
+    public $DKIM_domain = '';
+
+    /**
+     * DKIM private key file path.
+     * @type string
+     */
+    public $DKIM_private = '';
+
+    /**
+     * Callback Action function name.
+     *
+     * The function that handles the result of the send email action.
+     * It is called out by send() for each email sent.
+     *
+     * Value can be any php callable: http://www.php.net/is_callable
+     *
+     * Parameters:
+     *   boolean $result        result of the send action
+     *   string  $to            email address of the recipient
+     *   string  $cc            cc email addresses
+     *   string  $bcc           bcc email addresses
+     *   string  $subject       the subject
+     *   string  $body          the email body
+     *   string  $from          email address of sender
+     * @type string
+     */
+    public $action_function = '';
+
+    /**
+     * What to use in the X-Mailer header.
+     * Options: null for default, whitespace for none, or a string to use
+     * @type string
+     */
+    public $XMailer = '';
+
+    /**
+     * An instance of the SMTP sender class.
+     * @type SMTP
+     * @access protected
+     */
+    protected $smtp = null;
+
+    /**
+     * The array of 'to' addresses.
+     * @type array
+     * @access protected
+     */
+    protected $to = array();
+
+    /**
+     * The array of 'cc' addresses.
+     * @type array
+     * @access protected
+     */
+    protected $cc = array();
+
+    /**
+     * The array of 'bcc' addresses.
+     * @type array
+     * @access protected
+     */
+    protected $bcc = array();
+
+    /**
+     * The array of reply-to names and addresses.
+     * @type array
+     * @access protected
+     */
+    protected $ReplyTo = array();
+
+    /**
+     * An array of all kinds of addresses.
+     * Includes all of $to, $cc, $bcc, $replyto
+     * @type array
+     * @access protected
+     */
+    protected $all_recipients = array();
+
+    /**
+     * The array of attachments.
+     * @type array
+     * @access protected
+     */
+    protected $attachment = array();
+
+    /**
+     * The array of custom headers.
+     * @type array
+     * @access protected
+     */
+    protected $CustomHeader = array();
+
+    /**
+     * The most recent Message-ID (including angular brackets).
+     * @type string
+     * @access protected
+     */
+    protected $lastMessageID = '';
+
+    /**
+     * The message's MIME type.
+     * @type string
+     * @access protected
+     */
+    protected $message_type = '';
+
+    /**
+     * The array of MIME boundary strings.
+     * @type array
+     * @access protected
+     */
+    protected $boundary = array();
+
+    /**
+     * The array of available languages.
+     * @type array
+     * @access protected
+     */
+    protected $language = array();
+
+    /**
+     * The number of errors encountered.
+     * @type integer
+     * @access protected
+     */
+    protected $error_count = 0;
+
+    /**
+     * The S/MIME certificate file path.
+     * @type string
+     * @access protected
+     */
+    protected $sign_cert_file = '';
+
+    /**
+     * The S/MIME key file path.
+     * @type string
+     * @access protected
+     */
+    protected $sign_key_file = '';
+
+    /**
+     * The S/MIME password for the key.
+     * Used only if the key is encrypted.
+     * @type string
+     * @access protected
+     */
+    protected $sign_key_pass = '';
+
+    /**
+     * Whether to throw exceptions for errors.
+     * @type boolean
+     * @access protected
+     */
+    protected $exceptions = false;
+
+    /**
+     * Error severity: message only, continue processing
+     */
+    const STOP_MESSAGE = 0;
+
+    /**
+     * Error severity: message, likely ok to continue processing
+     */
+    const STOP_CONTINUE = 1;
+
+    /**
+     * Error severity: message, plus full stop, critical error reached
+     */
+    const STOP_CRITICAL = 2;
+
+    /**
+     * SMTP RFC standard line ending
+     */
+    const CRLF = "\r\n";
+
+    /**
+     * Constructor
+     * @param boolean $exceptions Should we throw external exceptions?
+     */
+    public function __construct($exceptions = false)
+    {
+        $this->exceptions = ($exceptions == true);
+        // @CCF Fix
+        return;
+        
+        //Make sure our autoloader is loaded
+        if (version_compare(PHP_VERSION, '5.1.2', '>=')) {
+            $autoload = spl_autoload_functions();
+            if ($autoload === false or !in_array('PHPMailerAutoload', $autoload)) {
+                require 'PHPMailerAutoload.php';
+            }
+        }
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely
+            $this->smtpClose();
+        }
+    }
+
+    /**
+     * Call mail() in a safe_mode-aware fashion.
+     * Also, unless sendmail_path points to sendmail (or something that
+     * claims to be sendmail), don't pass params (not a perfect fix,
+     * but it will do)
+     * @param string $to To
+     * @param string $subject Subject
+     * @param string $body Message Body
+     * @param string $header Additional Header(s)
+     * @param string $params Params
+     * @access private
+     * @return boolean
+     */
+    private function mailPassthru($to, $subject, $body, $header, $params)
+    {
+        //Check overloading of mail function to avoid double-encoding
+        if (ini_get('mbstring.func_overload') & 1) {
+            $subject = $this->secureHeader($subject);
+        } else {
+            $subject = $this->encodeHeader($this->secureHeader($subject));
+        }
+        if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
+            $result = @mail($to, $subject, $body, $header);
+        } else {
+            $result = @mail($to, $subject, $body, $header, $params);
+        }
+        return $result;
+    }
+
+    /**
+     * Output debugging info via user-defined method.
+     * Only if debug output is enabled.
+     * @see PHPMailer::$Debugoutput
+     * @see PHPMailer::$SMTPDebug
+     * @param string $str
+     */
+    protected function edebug($str)
+    {
+        if (!$this->SMTPDebug) {
+            return;
+        }
+        switch ($this->Debugoutput) {
+            case 'error_log':
+                error_log($str);
+                break;
+            case 'html':
+                //Cleans up output a bit for a better looking display that's HTML-safe
+                echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet) . "<br>\n";
+                break;
+            case 'echo':
+            default:
+                echo $str."\n";
+        }
+    }
+
+    /**
+     * Sets message type to HTML or plain.
+     * @param boolean $isHtml True for HTML mode.
+     * @return void
+     */
+    public function isHTML($isHtml = true)
+    {
+        if ($isHtml) {
+            $this->ContentType = 'text/html';
+        } else {
+            $this->ContentType = 'text/plain';
+        }
+    }
+
+    /**
+     * Send messages using SMTP.
+     * @return void
+     */
+    public function isSMTP()
+    {
+        $this->Mailer = 'smtp';
+    }
+
+    /**
+     * Send messages using PHP's mail() function.
+     * @return void
+     */
+    public function isMail()
+    {
+        $this->Mailer = 'mail';
+    }
+
+    /**
+     * Send messages using $Sendmail.
+     * @return void
+     */
+    public function isSendmail()
+    {
+        $ini_sendmail_path = ini_get('sendmail_path');
+
+        if (!stristr($ini_sendmail_path, 'sendmail')) {
+            $this->Sendmail = '/usr/sbin/sendmail';
+        } else {
+            $this->Sendmail = $ini_sendmail_path;
+        }
+        $this->Mailer = 'sendmail';
+    }
+
+    /**
+     * Send messages using qmail.
+     * @return void
+     */
+    public function isQmail()
+    {
+        $ini_sendmail_path = ini_get('sendmail_path');
+
+        if (!stristr($ini_sendmail_path, 'qmail')) {
+            $this->Sendmail = '/var/qmail/bin/qmail-inject';
+        } else {
+            $this->Sendmail = $ini_sendmail_path;
+        }
+        $this->Mailer = 'qmail';
+    }
+
+    /**
+     * Add a "To" address.
+     * @param string $address
+     * @param string $name
+     * @return boolean true on success, false if address already used
+     */
+    public function addAddress($address, $name = '')
+    {
+        return $this->addAnAddress('to', $address, $name);
+    }
+
+    /**
+     * Add a "CC" address.
+     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
+     * @param string $address
+     * @param string $name
+     * @return boolean true on success, false if address already used
+     */
+    public function addCC($address, $name = '')
+    {
+        return $this->addAnAddress('cc', $address, $name);
+    }
+
+    /**
+     * Add a "BCC" address.
+     * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
+     * @param string $address
+     * @param string $name
+     * @return boolean true on success, false if address already used
+     */
+    public function addBCC($address, $name = '')
+    {
+        return $this->addAnAddress('bcc', $address, $name);
+    }
+
+    /**
+     * Add a "Reply-to" address.
+     * @param string $address
+     * @param string $name
+     * @return boolean
+     */
+    public function addReplyTo($address, $name = '')
+    {
+        return $this->addAnAddress('Reply-To', $address, $name);
+    }
+
+    /**
+     * Add an address to one of the recipient arrays.
+     * Addresses that have been added already return false, but do not throw exceptions
+     * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo'
+     * @param string $address The email address to send to
+     * @param string $name
+     * @throws phpmailerException
+     * @return boolean true on success, false if address already used or invalid in some way
+     * @access protected
+     */
+    protected function addAnAddress($kind, $address, $name = '')
+    {
+        if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {
+            $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);
+            $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);
+            if ($this->exceptions) {
+                throw new phpmailerException('Invalid recipient array: ' . $kind);
+            }
+            return false;
+        }
+        $address = trim($address);
+        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+        if (!$this->validateAddress($address)) {
+            $this->setError($this->lang('invalid_address') . ': ' . $address);
+            $this->edebug($this->lang('invalid_address') . ': ' . $address);
+            if ($this->exceptions) {
+                throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
+            }
+            return false;
+        }
+        if ($kind != 'Reply-To') {
+            if (!isset($this->all_recipients[strtolower($address)])) {
+                array_push($this->$kind, array($address, $name));
+                $this->all_recipients[strtolower($address)] = true;
+                return true;
+            }
+        } else {
+            if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
+                $this->ReplyTo[strtolower($address)] = array($address, $name);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Set the From and FromName properties.
+     * @param string $address
+     * @param string $name
+     * @param boolean $auto Whether to also set the Sender address, defaults to true
+     * @throws phpmailerException
+     * @return boolean
+     */
+    public function setFrom($address, $name = '', $auto = true)
+    {
+        $address = trim($address);
+        $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+        if (!$this->validateAddress($address)) {
+            $this->setError($this->lang('invalid_address') . ': ' . $address);
+            $this->edebug($this->lang('invalid_address') . ': ' . $address);
+            if ($this->exceptions) {
+                throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);
+            }
+            return false;
+        }
+        $this->From = $address;
+        $this->FromName = $name;
+        if ($auto) {
+            if (empty($this->Sender)) {
+                $this->Sender = $address;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Return the Message-ID header of the last email.
+     * Technically this is the value from the last time the headers were created,
+     * but it's also the message ID of the last sent message except in
+     * pathological cases.
+     * @return string
+     */
+    public function getLastMessageID()
+    {
+        return $this->lastMessageID;
+    }
+
+    /**
+     * Check that a string looks like an email address.
+     * @param string $address The email address to check
+     * @param string $patternselect A selector for the validation pattern to use :
+     * * `auto` Pick strictest one automatically;
+     * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
+     * * `pcre` Use old PCRE implementation;
+     * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; same as pcre8 but does not allow 'dotless' domains;
+     * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
+     * * `noregex` Don't use a regex: super fast, really dumb.
+     * @return boolean
+     * @static
+     * @access public
+     */
+    public static function validateAddress($address, $patternselect = 'auto')
+    {
+        if (!$patternselect or $patternselect == 'auto') {
+            if (defined('PCRE_VERSION')) { //Check this constant so it works when extension_loaded() is disabled
+                if (version_compare(PCRE_VERSION, '8.0') >= 0) {
+                    $patternselect = 'pcre8';
+                } else {
+                    $patternselect = 'pcre';
+                }
+            } else {
+                //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
+                if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+                    $patternselect = 'php';
+                } else {
+                    $patternselect = 'noregex';
+                }
+            }
+        }
+        switch ($patternselect) {
+            case 'pcre8':
+                /**
+                 * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
+                 * @link http://squiloople.com/2009/12/20/email-address-validation/
+                 * @copyright 2009-2010 Michael Rushton
+                 * Feel free to use and redistribute this code. But please keep this copyright notice.
+                 */
+                return (boolean)preg_match(
+                    '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
+                    '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
+                    '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
+                    '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
+                    '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
+                    '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
+                    '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
+                    '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
+                    '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
+                    $address
+                );
+            case 'pcre':
+                //An older regex that doesn't need a recent PCRE
+                return (boolean)preg_match(
+                    '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
+                    '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
+                    '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
+                    '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
+                    '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
+                    '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
+                    '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
+                    '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
+                    '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
+                    '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
+                    $address
+                );
+            case 'html5':
+                /**
+                 * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
+                 * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
+                 */
+                return (boolean)preg_match(
+                    '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
+                    '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
+                    $address
+                );
+            case 'noregex':
+                //No PCRE! Do something _very_ approximate!
+                //Check the address is 3 chars or longer and contains an @ that's not the first or last char
+                return (strlen($address) >= 3
+                    and strpos($address, '@') >= 1
+                    and strpos($address, '@') != strlen($address) - 1);
+            case 'php':
+            default:
+                return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
+        }
+    }
+
+    /**
+     * Create a message and send it.
+     * Uses the sending method specified by $Mailer.
+     * @throws phpmailerException
+     * @return boolean false on error - See the ErrorInfo property for details of the error.
+     */
+    public function send()
+    {
+        try {
+            if (!$this->preSend()) {
+                return false;
+            }
+            return $this->postSend();
+        } catch (phpmailerException $exc) {
+            $this->mailHeader = '';
+            $this->setError($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Prepare a message for sending.
+     * @throws phpmailerException
+     * @return boolean
+     */
+    public function preSend()
+    {
+        try {
+            $this->mailHeader = '';
+            if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
+                throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
+            }
+
+            // Set whether the message is multipart/alternative
+            if (!empty($this->AltBody)) {
+                $this->ContentType = 'multipart/alternative';
+            }
+
+            $this->error_count = 0; // reset errors
+            $this->setMessageType();
+            // Refuse to send an empty message unless we are specifically allowing it
+            if (!$this->AllowEmpty and empty($this->Body)) {
+                throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
+            }
+
+            $this->MIMEHeader = $this->createHeader();
+            $this->MIMEBody = $this->createBody();
+
+            // To capture the complete message when using mail(), create
+            // an extra header list which createHeader() doesn't fold in
+            if ($this->Mailer == 'mail') {
+                if (count($this->to) > 0) {
+                    $this->mailHeader .= $this->addrAppend('To', $this->to);
+                } else {
+                    $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
+                }
+                $this->mailHeader .= $this->headerLine(
+                    'Subject',
+                    $this->encodeHeader($this->secureHeader(trim($this->Subject)))
+                );
+            }
+
+            // Sign with DKIM if enabled
+            if (!empty($this->DKIM_domain)
+                && !empty($this->DKIM_private)
+                && !empty($this->DKIM_selector)
+                && !empty($this->DKIM_domain)
+                && file_exists($this->DKIM_private)) {
+                $header_dkim = $this->DKIM_Add(
+                    $this->MIMEHeader . $this->mailHeader,
+                    $this->encodeHeader($this->secureHeader($this->Subject)),
+                    $this->MIMEBody
+                );
+                $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
+                    str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
+            }
+            return true;
+
+        } catch (phpmailerException $exc) {
+            $this->setError($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Actually send a message.
+     * Send the email via the selected mechanism
+     * @throws phpmailerException
+     * @return boolean
+     */
+    public function postSend()
+    {
+        try {
+            // Choose the mailer and send through it
+            switch ($this->Mailer) {
+                case 'sendmail':
+                case 'qmail':
+                    return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
+                case 'smtp':
+                    return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
+                case 'mail':
+                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
+                default:
+                    $sendMethod = $this->Mailer.'Send';
+                    if (method_exists($this, $sendMethod)) {
+                        return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
+                    }
+
+                    return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
+            }
+        } catch (phpmailerException $exc) {
+            $this->setError($exc->getMessage());
+            $this->edebug($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Send mail using the $Sendmail program.
+     * @param string $header The message headers
+     * @param string $body The message body
+     * @see PHPMailer::$Sendmail
+     * @throws phpmailerException
+     * @access protected
+     * @return boolean
+     */
+    protected function sendmailSend($header, $body)
+    {
+        if ($this->Sender != '') {
+            if ($this->Mailer == 'qmail') {
+                $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
+            } else {
+                $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
+            }
+        } else {
+            if ($this->Mailer == 'qmail') {
+                $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
+            } else {
+                $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
+            }
+        }
+        if ($this->SingleTo === true) {
+            foreach ($this->SingleToArray as $toAddr) {
+                if (!@$mail = popen($sendmail, 'w')) {
+                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+                }
+                fputs($mail, 'To: ' . $toAddr . "\n");
+                fputs($mail, $header);
+                fputs($mail, $body);
+                $result = pclose($mail);
+                $this->doCallback(
+                    ($result == 0),
+                    array($toAddr),
+                    $this->cc,
+                    $this->bcc,
+                    $this->Subject,
+                    $body,
+                    $this->From
+                );
+                if ($result != 0) {
+                    throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+                }
+            }
+        } else {
+            if (!@$mail = popen($sendmail, 'w')) {
+                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+            }
+            fputs($mail, $header);
+            fputs($mail, $body);
+            $result = pclose($mail);
+            $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
+            if ($result != 0) {
+                throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Send mail using the PHP mail() function.
+     * @param string $header The message headers
+     * @param string $body The message body
+     * @link http://www.php.net/manual/en/book.mail.php
+     * @throws phpmailerException
+     * @access protected
+     * @return boolean
+     */
+    protected function mailSend($header, $body)
+    {
+        $toArr = array();
+        foreach ($this->to as $toaddr) {
+            $toArr[] = $this->addrFormat($toaddr);
+        }
+        $to = implode(', ', $toArr);
+
+        if (empty($this->Sender)) {
+            $params = ' ';
+        } else {
+            $params = sprintf('-f%s', $this->Sender);
+        }
+        if ($this->Sender != '' and !ini_get('safe_mode')) {
+            $old_from = ini_get('sendmail_from');
+            ini_set('sendmail_from', $this->Sender);
+        }
+        $result = false;
+        if ($this->SingleTo === true && count($toArr) > 1) {
+            foreach ($toArr as $toAddr) {
+                $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
+                $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
+            }
+        } else {
+            $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
+            $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
+        }
+        if (isset($old_from)) {
+            ini_set('sendmail_from', $old_from);
+        }
+        if (!$result) {
+            throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
+        }
+        return true;
+    }
+
+    /**
+     * Get an instance to use for SMTP operations.
+     * Override this function to load your own SMTP implementation
+     * @return SMTP
+     */
+    public function getSMTPInstance()
+    {
+        if (!is_object($this->smtp)) {
+            $this->smtp = new SMTP;
+        }
+        return $this->smtp;
+    }
+
+    /**
+     * Send mail via SMTP.
+     * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
+     * Uses the PHPMailerSMTP class by default.
+     * @see PHPMailer::getSMTPInstance() to use a different class.
+     * @param string $header The message headers
+     * @param string $body The message body
+     * @throws phpmailerException
+     * @uses SMTP
+     * @access protected
+     * @return boolean
+     */
+    protected function smtpSend($header, $body)
+    {
+        $bad_rcpt = array();
+
+        if (!$this->smtpConnect()) {
+            throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
+        }
+        $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender;
+        if (!$this->smtp->mail($smtp_from)) {
+            $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
+            throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
+        }
+
+        // Attempt to send to all recipients
+        foreach ($this->to as $to) {
+            if (!$this->smtp->recipient($to[0])) {
+                $bad_rcpt[] = $to[0];
+                $isSent = false;
+            } else {
+                $isSent = true;
+            }
+            $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
+        }
+        foreach ($this->cc as $cc) {
+            if (!$this->smtp->recipient($cc[0])) {
+                $bad_rcpt[] = $cc[0];
+                $isSent = false;
+            } else {
+                $isSent = true;
+            }
+            $this->doCallback($isSent, array(), array($cc[0]), array(), $this->Subject, $body, $this->From);
+        }
+        foreach ($this->bcc as $bcc) {
+            if (!$this->smtp->recipient($bcc[0])) {
+                $bad_rcpt[] = $bcc[0];
+                $isSent = false;
+            } else {
+                $isSent = true;
+            }
+            $this->doCallback($isSent, array(), array(), array($bcc[0]), $this->Subject, $body, $this->From);
+        }
+
+        // Only send the DATA command if we have viable recipients
+        if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
+            throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
+        }
+        if ($this->SMTPKeepAlive == true) {
+            $this->smtp->reset();
+        } else {
+            $this->smtp->quit();
+            $this->smtp->close();
+        }
+        if (count($bad_rcpt) > 0) { // Create error message for any bad addresses
+            throw new phpmailerException(
+                $this->lang('recipients_failed') . implode(', ', $bad_rcpt),
+                self::STOP_CONTINUE
+            );
+        }
+        return true;
+    }
+
+    /**
+     * Initiate a connection to an SMTP server.
+     * Returns false if the operation failed.
+     * @param array $options An array of options compatible with stream_context_create()
+     * @uses SMTP
+     * @access public
+     * @throws phpmailerException
+     * @return boolean
+     */
+    public function smtpConnect($options = array())
+    {
+        if (is_null($this->smtp)) {
+            $this->smtp = $this->getSMTPInstance();
+        }
+
+        // Already connected?
+        if ($this->smtp->connected()) {
+            return true;
+        }
+
+        $this->smtp->setTimeout($this->Timeout);
+        $this->smtp->setDebugLevel($this->SMTPDebug);
+        $this->smtp->setDebugOutput($this->Debugoutput);
+        $this->smtp->setVerp($this->do_verp);
+        $hosts = explode(';', $this->Host);
+        $lastexception = null;
+
+        foreach ($hosts as $hostentry) {
+            $hostinfo = array();
+            if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
+                // Not a valid host entry
+                continue;
+            }
+            // $hostinfo[2]: optional ssl or tls prefix
+            // $hostinfo[3]: the hostname
+            // $hostinfo[4]: optional port number
+            // The host string prefix can temporarily override the current setting for SMTPSecure
+            // If it's not specified, the default value is used
+            $prefix = '';
+            $tls = ($this->SMTPSecure == 'tls');
+            if ($hostinfo[2] == 'ssl' or ($hostinfo[2] == '' and $this->SMTPSecure == 'ssl')) {
+                $prefix = 'ssl://';
+                $tls = false; // Can't have SSL and TLS at once
+            } elseif ($hostinfo[2] == 'tls') {
+                $tls = true;
+                // tls doesn't use a prefix
+            }
+            $host = $hostinfo[3];
+            $port = $this->Port;
+            $tport = (integer)$hostinfo[4];
+            if ($tport > 0 and $tport < 65536) {
+                $port = $tport;
+            }
+            if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
+                try {
+                    if ($this->Helo) {
+                        $hello = $this->Helo;
+                    } else {
+                        $hello = $this->serverHostname();
+                    }
+                    $this->smtp->hello($hello);
+
+                    if ($tls) {
+                        if (!$this->smtp->startTLS()) {
+                            throw new phpmailerException($this->lang('connect_host'));
+                        }
+                        // We must resend HELO after tls negotiation
+                        $this->smtp->hello($hello);
+                    }
+                    if ($this->SMTPAuth) {
+                        if (!$this->smtp->authenticate(
+                            $this->Username,
+                            $this->Password,
+                            $this->AuthType,
+                            $this->Realm,
+                            $this->Workstation
+                        )
+                        ) {
+                            throw new phpmailerException($this->lang('authenticate'));
+                        }
+                    }
+                    return true;
+                } catch (phpmailerException $exc) {
+                    $lastexception = $exc;
+                    // We must have connected, but then failed TLS or Auth, so close connection nicely
+                    $this->smtp->quit();
+                }
+            }
+        }
+        // If we get here, all connection attempts have failed, so close connection hard
+        $this->smtp->close();
+        // As we've caught all exceptions, just report whatever the last one was
+        if ($this->exceptions and !is_null($lastexception)) {
+            throw $lastexception;
+        }
+        return false;
+    }
+
+    /**
+     * Close the active SMTP session if one exists.
+     * @return void
+     */
+    public function smtpClose()
+    {
+        if ($this->smtp !== null) {
+            if ($this->smtp->connected()) {
+                $this->smtp->quit();
+                $this->smtp->close();
+            }
+        }
+    }
+
+    /**
+     * Set the language for error messages.
+     * Returns false if it cannot load the language file.
+     * The default language is English.
+     * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
+     * @param string $lang_path Path to the language file directory, with trailing separator (slash)
+     * @return boolean
+     * @access public
+     */
+    public function setLanguage($langcode = 'en', $lang_path = '')
+    {
+        // Define full set of translatable strings in English
+        $PHPMAILER_LANG = array(
+            'authenticate' => 'SMTP Error: Could not authenticate.',
+            'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
+            'data_not_accepted' => 'SMTP Error: data not accepted.',
+            'empty_message' => 'Message body empty',
+            'encoding' => 'Unknown encoding: ',
+            'execute' => 'Could not execute: ',
+            'file_access' => 'Could not access file: ',
+            'file_open' => 'File Error: Could not open file: ',
+            'from_failed' => 'The following From address failed: ',
+            'instantiate' => 'Could not instantiate mail function.',
+            'invalid_address' => 'Invalid address',
+            'mailer_not_supported' => ' mailer is not supported.',
+            'provide_address' => 'You must provide at least one recipient email address.',
+            'recipients_failed' => 'SMTP Error: The following recipients failed: ',
+            'signing' => 'Signing Error: ',
+            'smtp_connect_failed' => 'SMTP connect() failed.',
+            'smtp_error' => 'SMTP server error: ',
+            'variable_set' => 'Cannot set or reset variable: '
+        );
+        if (empty($lang_path)) {
+            // Calculate an absolute path so it can work if CWD is not here
+            $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
+        }
+        $foundlang = true;
+        $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
+        if ($langcode != 'en') { // There is no English translation file
+            // Make sure language file path is readable
+            if (!is_readable($lang_file)) {
+                $foundlang = false;
+            } else {
+                // Overwrite language-specific strings.
+                // This way we'll never have missing translations.
+                $foundlang = include $lang_file;
+            }
+        }
+        $this->language = $PHPMAILER_LANG;
+        return ($foundlang == true); // Returns false if language not found
+    }
+
+    /**
+     * Get the array of strings for the current language.
+     * @return array
+     */
+    public function getTranslations()
+    {
+        return $this->language;
+    }
+
+    /**
+     * Create recipient headers.
+     * @access public
+     * @param string $type
+     * @param array $addr An array of recipient,
+     * where each recipient is a 2-element indexed array with element 0 containing an address
+     * and element 1 containing a name, like:
+     * array(array('[email protected]', 'Joe User'), array('[email protected]', 'Zoe User'))
+     * @return string
+     */
+    public function addrAppend($type, $addr)
+    {
+        $addresses = array();
+        foreach ($addr as $address) {
+            $addresses[] = $this->addrFormat($address);
+        }
+        return $type . ': ' . implode(', ', $addresses) . $this->LE;
+    }
+
+    /**
+     * Format an address for use in a message header.
+     * @access public
+     * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
+     *      like array('[email protected]', 'Joe User')
+     * @return string
+     */
+    public function addrFormat($addr)
+    {
+        if (empty($addr[1])) { // No name provided
+            return $this->secureHeader($addr[0]);
+        } else {
+            return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
+                $addr[0]
+            ) . '>';
+        }
+    }
+
+    /**
+     * Word-wrap message.
+     * For use with mailers that do not automatically perform wrapping
+     * and for quoted-printable encoded messages.
+     * Original written by philippe.
+     * @param string $message The message to wrap
+     * @param integer $length The line length to wrap to
+     * @param boolean $qp_mode Whether to run in Quoted-Printable mode
+     * @access public
+     * @return string
+     */
+    public function wrapText($message, $length, $qp_mode = false)
+    {
+        $soft_break = ($qp_mode) ? sprintf(' =%s', $this->LE) : $this->LE;
+        // If utf-8 encoding is used, we will need to make sure we don't
+        // split multibyte characters when we wrap
+        $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
+        $lelen = strlen($this->LE);
+        $crlflen = strlen(self::CRLF);
+
+        $message = $this->fixEOL($message);
+        if (substr($message, -$lelen) == $this->LE) {
+            $message = substr($message, 0, -$lelen);
+        }
+
+        $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE
+        $message = '';
+        for ($i = 0; $i < count($line); $i++) {
+            $line_part = explode(' ', $line[$i]);
+            $buf = '';
+            for ($e = 0; $e < count($line_part); $e++) {
+                $word = $line_part[$e];
+                if ($qp_mode and (strlen($word) > $length)) {
+                    $space_left = $length - strlen($buf) - $crlflen;
+                    if ($e != 0) {
+                        if ($space_left > 20) {
+                            $len = $space_left;
+                            if ($is_utf8) {
+                                $len = $this->utf8CharBoundary($word, $len);
+                            } elseif (substr($word, $len - 1, 1) == '=') {
+                                $len--;
+                            } elseif (substr($word, $len - 2, 1) == '=') {
+                                $len -= 2;
+                            }
+                            $part = substr($word, 0, $len);
+                            $word = substr($word, $len);
+                            $buf .= ' ' . $part;
+                            $message .= $buf . sprintf('=%s', self::CRLF);
+                        } else {
+                            $message .= $buf . $soft_break;
+                        }
+                        $buf = '';
+                    }
+                    while (strlen($word) > 0) {
+                        if ($length <= 0) {
+                            break;
+                        }
+                        $len = $length;
+                        if ($is_utf8) {
+                            $len = $this->utf8CharBoundary($word, $len);
+                        } elseif (substr($word, $len - 1, 1) == '=') {
+                            $len--;
+                        } elseif (substr($word, $len - 2, 1) == '=') {
+                            $len -= 2;
+                        }
+                        $part = substr($word, 0, $len);
+                        $word = substr($word, $len);
+
+                        if (strlen($word) > 0) {
+                            $message .= $part . sprintf('=%s', self::CRLF);
+                        } else {
+                            $buf = $part;
+                        }
+                    }
+                } else {
+                    $buf_o = $buf;
+                    $buf .= ($e == 0) ? $word : (' ' . $word);
+
+                    if (strlen($buf) > $length and $buf_o != '') {
+                        $message .= $buf_o . $soft_break;
+                        $buf = $word;
+                    }
+                }
+            }
+            $message .= $buf . self::CRLF;
+        }
+
+        return $message;
+    }
+
+    /**
+     * Find the last character boundary prior to $maxLength in a utf-8
+     * quoted (printable) encoded string.
+     * Original written by Colin Brown.
+     * @access public
+     * @param string $encodedText utf-8 QP text
+     * @param integer $maxLength   find last character boundary prior to this length
+     * @return integer
+     */
+    public function utf8CharBoundary($encodedText, $maxLength)
+    {
+        $foundSplitPos = false;
+        $lookBack = 3;
+        while (!$foundSplitPos) {
+            $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
+            $encodedCharPos = strpos($lastChunk, '=');
+            if ($encodedCharPos !== false) {
+                // Found start of encoded character byte within $lookBack block.
+                // Check the encoded byte value (the 2 chars after the '=')
+                $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
+                $dec = hexdec($hex);
+                if ($dec < 128) { // Single byte character.
+                    // If the encoded char was found at pos 0, it will fit
+                    // otherwise reduce maxLength to start of the encoded char
+                    $maxLength = ($encodedCharPos == 0) ? $maxLength :
+                        $maxLength - ($lookBack - $encodedCharPos);
+                    $foundSplitPos = true;
+                } elseif ($dec >= 192) { // First byte of a multi byte character
+                    // Reduce maxLength to split at start of character
+                    $maxLength = $maxLength - ($lookBack - $encodedCharPos);
+                    $foundSplitPos = true;
+                } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back
+                    $lookBack += 3;
+                }
+            } else {
+                // No encoded character found
+                $foundSplitPos = true;
+            }
+        }
+        return $maxLength;
+    }
+
+    /**
+     * Set the body wrapping.
+     * @access public
+     * @return void
+     */
+    public function setWordWrap()
+    {
+        if ($this->WordWrap < 1) {
+            return;
+        }
+
+        switch ($this->message_type) {
+            case 'alt':
+            case 'alt_inline':
+            case 'alt_attach':
+            case 'alt_inline_attach':
+                $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
+                break;
+            default:
+                $this->Body = $this->wrapText($this->Body, $this->WordWrap);
+                break;
+        }
+    }
+
+    /**
+     * Assemble message headers.
+     * @access public
+     * @return string The assembled headers
+     */
+    public function createHeader()
+    {
+        $result = '';
+
+        // Set the boundaries
+        $uniq_id = md5(uniqid(time()));
+        $this->boundary[1] = 'b1_' . $uniq_id;
+        $this->boundary[2] = 'b2_' . $uniq_id;
+        $this->boundary[3] = 'b3_' . $uniq_id;
+
+        if ($this->MessageDate == '') {
+            $this->MessageDate = self::rfcDate();
+        }
+        $result .= $this->headerLine('Date', $this->MessageDate);
+
+
+        // To be created automatically by mail()
+        if ($this->SingleTo === true) {
+            if ($this->Mailer != 'mail') {
+                foreach ($this->to as $toaddr) {
+                    $this->SingleToArray[] = $this->addrFormat($toaddr);
+                }
+            }
+        } else {
+            if (count($this->to) > 0) {
+                if ($this->Mailer != 'mail') {
+                    $result .= $this->addrAppend('To', $this->to);
+                }
+            } elseif (count($this->cc) == 0) {
+                $result .= $this->headerLine('To', 'undisclosed-recipients:;');
+            }
+        }
+
+        $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
+
+        // sendmail and mail() extract Cc from the header before sending
+        if (count($this->cc) > 0) {
+            $result .= $this->addrAppend('Cc', $this->cc);
+        }
+
+        // sendmail and mail() extract Bcc from the header before sending
+        if ((
+                $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
+            )
+            and count($this->bcc) > 0
+        ) {
+            $result .= $this->addrAppend('Bcc', $this->bcc);
+        }
+
+        if (count($this->ReplyTo) > 0) {
+            $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
+        }
+
+        // mail() sets the subject itself
+        if ($this->Mailer != 'mail') {
+            $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
+        }
+
+        if ($this->MessageID != '') {
+            $this->lastMessageID = $this->MessageID;
+        } else {
+            $this->lastMessageID = sprintf('<%s@%s>', $uniq_id, $this->ServerHostname());
+        }
+        $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);
+        $result .= $this->headerLine('X-Priority', $this->Priority);
+        if ($this->XMailer == '') {
+            $result .= $this->headerLine(
+                'X-Mailer',
+                'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'
+            );
+        } else {
+            $myXmailer = trim($this->XMailer);
+            if ($myXmailer) {
+                $result .= $this->headerLine('X-Mailer', $myXmailer);
+            }
+        }
+
+        if ($this->ConfirmReadingTo != '') {
+            $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');
+        }
+
+        // Add custom headers
+        for ($index = 0; $index < count($this->CustomHeader); $index++) {
+            $result .= $this->headerLine(
+                trim($this->CustomHeader[$index][0]),
+                $this->encodeHeader(trim($this->CustomHeader[$index][1]))
+            );
+        }
+        if (!$this->sign_key_file) {
+            $result .= $this->headerLine('MIME-Version', '1.0');
+            $result .= $this->getMailMIME();
+        }
+
+        return $result;
+    }
+
+    /**
+     * Get the message MIME type headers.
+     * @access public
+     * @return string
+     */
+    public function getMailMIME()
+    {
+        $result = '';
+        $ismultipart = true;
+        switch ($this->message_type) {
+            case 'inline':
+                $result .= $this->headerLine('Content-Type', 'multipart/related;');
+                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
+                break;
+            case 'attach':
+            case 'inline_attach':
+            case 'alt_attach':
+            case 'alt_inline_attach':
+                $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
+                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
+                break;
+            case 'alt':
+            case 'alt_inline':
+                $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
+                $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
+                break;
+            default:
+                // Catches case 'plain': and case '':
+                $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
+                $ismultipart = false;
+                break;
+        }
+        // RFC1341 part 5 says 7bit is assumed if not specified
+        if ($this->Encoding != '7bit') {
+            // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
+            if ($ismultipart) {
+                if ($this->Encoding == '8bit') {
+                    $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
+                }
+                // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
+            } else {
+                $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
+            }
+        }
+
+        if ($this->Mailer != 'mail') {
+            $result .= $this->LE;
+        }
+
+        return $result;
+    }
+
+    /**
+     * Returns the whole MIME message.
+     * Includes complete headers and body.
+     * Only valid post preSend().
+     * @see PHPMailer::preSend()
+     * @access public
+     * @return string
+     */
+    public function getSentMIMEMessage()
+    {
+        return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;
+    }
+
+
+    /**
+     * Assemble the message body.
+     * Returns an empty string on failure.
+     * @access public
+     * @throws phpmailerException
+     * @return string The assembled message body
+     */
+    public function createBody()
+    {
+        $body = '';
+
+        if ($this->sign_key_file) {
+            $body .= $this->getMailMIME() . $this->LE;
+        }
+
+        $this->setWordWrap();
+
+        $bodyEncoding = $this->Encoding;
+        $bodyCharSet = $this->CharSet;
+        if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
+            $bodyEncoding = '7bit';
+            $bodyCharSet = 'us-ascii';
+        }
+        $altBodyEncoding = $this->Encoding;
+        $altBodyCharSet = $this->CharSet;
+        if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
+            $altBodyEncoding = '7bit';
+            $altBodyCharSet = 'us-ascii';
+        }
+        switch ($this->message_type) {
+            case 'inline':
+                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->attachAll('inline', $this->boundary[1]);
+                break;
+            case 'attach':
+                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->attachAll('attachment', $this->boundary[1]);
+                break;
+            case 'inline_attach':
+                $body .= $this->textLine('--' . $this->boundary[1]);
+                $body .= $this->headerLine('Content-Type', 'multipart/related;');
+                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+                $body .= $this->LE;
+                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->attachAll('inline', $this->boundary[2]);
+                $body .= $this->LE;
+                $body .= $this->attachAll('attachment', $this->boundary[1]);
+                break;
+            case 'alt':
+                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= $this->LE . $this->LE;
+                if (!empty($this->Ical)) {
+                    $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
+                    $body .= $this->encodeString($this->Ical, $this->Encoding);
+                    $body .= $this->LE . $this->LE;
+                }
+                $body .= $this->endBoundary($this->boundary[1]);
+                break;
+            case 'alt_inline':
+                $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->textLine('--' . $this->boundary[1]);
+                $body .= $this->headerLine('Content-Type', 'multipart/related;');
+                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+                $body .= $this->LE;
+                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->attachAll('inline', $this->boundary[2]);
+                $body .= $this->LE;
+                $body .= $this->endBoundary($this->boundary[1]);
+                break;
+            case 'alt_attach':
+                $body .= $this->textLine('--' . $this->boundary[1]);
+                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
+                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+                $body .= $this->LE;
+                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->endBoundary($this->boundary[2]);
+                $body .= $this->LE;
+                $body .= $this->attachAll('attachment', $this->boundary[1]);
+                break;
+            case 'alt_inline_attach':
+                $body .= $this->textLine('--' . $this->boundary[1]);
+                $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
+                $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+                $body .= $this->LE;
+                $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+                $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->textLine('--' . $this->boundary[2]);
+                $body .= $this->headerLine('Content-Type', 'multipart/related;');
+                $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
+                $body .= $this->LE;
+                $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                $body .= $this->LE . $this->LE;
+                $body .= $this->attachAll('inline', $this->boundary[3]);
+                $body .= $this->LE;
+                $body .= $this->endBoundary($this->boundary[2]);
+                $body .= $this->LE;
+                $body .= $this->attachAll('attachment', $this->boundary[1]);
+                break;
+            default:
+                // catch case 'plain' and case ''
+                $body .= $this->encodeString($this->Body, $bodyEncoding);
+                break;
+        }
+
+        if ($this->isError()) {
+            $body = '';
+        } elseif ($this->sign_key_file) {
+            try {
+                if (!defined('PKCS7_TEXT')) {
+                    throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
+                }
+                // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
+                $file = tempnam(sys_get_temp_dir(), 'mail');
+                file_put_contents($file, $body); // @TODO check this worked
+                $signed = tempnam(sys_get_temp_dir(), 'signed');
+                if (@openssl_pkcs7_sign(
+                    $file,
+                    $signed,
+                    'file://' . realpath($this->sign_cert_file),
+                    array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
+                    null
+                )
+                ) {
+                    @unlink($file);
+                    $body = file_get_contents($signed);
+                    @unlink($signed);
+                } else {
+                    @unlink($file);
+                    @unlink($signed);
+                    throw new phpmailerException($this->lang('signing') . openssl_error_string());
+                }
+            } catch (phpmailerException $exc) {
+                $body = '';
+                if ($this->exceptions) {
+                    throw $exc;
+                }
+            }
+        }
+        return $body;
+    }
+
+    /**
+     * Return the start of a message boundary.
+     * @access protected
+     * @param string $boundary
+     * @param string $charSet
+     * @param string $contentType
+     * @param string $encoding
+     * @return string
+     */
+    protected function getBoundary($boundary, $charSet, $contentType, $encoding)
+    {
+        $result = '';
+        if ($charSet == '') {
+            $charSet = $this->CharSet;
+        }
+        if ($contentType == '') {
+            $contentType = $this->ContentType;
+        }
+        if ($encoding == '') {
+            $encoding = $this->Encoding;
+        }
+        $result .= $this->textLine('--' . $boundary);
+        $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
+        $result .= $this->LE;
+        // RFC1341 part 5 says 7bit is assumed if not specified
+        if ($encoding != '7bit') {
+            $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
+        }
+        $result .= $this->LE;
+
+        return $result;
+    }
+
+    /**
+     * Return the end of a message boundary.
+     * @access protected
+     * @param string $boundary
+     * @return string
+     */
+    protected function endBoundary($boundary)
+    {
+        return $this->LE . '--' . $boundary . '--' . $this->LE;
+    }
+
+    /**
+     * Set the message type.
+     * PHPMailer only supports some preset message types,
+     * not arbitrary MIME structures.
+     * @access protected
+     * @return void
+     */
+    protected function setMessageType()
+    {
+        $this->message_type = array();
+        if ($this->alternativeExists()) {
+            $this->message_type[] = 'alt';
+        }
+        if ($this->inlineImageExists()) {
+            $this->message_type[] = 'inline';
+        }
+        if ($this->attachmentExists()) {
+            $this->message_type[] = 'attach';
+        }
+        $this->message_type = implode('_', $this->message_type);
+        if ($this->message_type == '') {
+            $this->message_type = 'plain';
+        }
+    }
+
+    /**
+     * Format a header line.
+     * @access public
+     * @param string $name
+     * @param string $value
+     * @return string
+     */
+    public function headerLine($name, $value)
+    {
+        return $name . ': ' . $value . $this->LE;
+    }
+
+    /**
+     * Return a formatted mail line.
+     * @access public
+     * @param string $value
+     * @return string
+     */
+    public function textLine($value)
+    {
+        return $value . $this->LE;
+    }
+
+    /**
+     * Add an attachment from a path on the filesystem.
+     * Returns false if the file could not be found or read.
+     * @param string $path Path to the attachment.
+     * @param string $name Overrides the attachment name.
+     * @param string $encoding File encoding (see $Encoding).
+     * @param string $type File extension (MIME) type.
+     * @param string $disposition Disposition to use
+     * @throws phpmailerException
+     * @return boolean
+     */
+    public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
+    {
+        try {
+            if (!@is_file($path)) {
+                throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
+            }
+
+            // If a MIME type is not specified, try to work it out from the file name
+            if ($type == '') {
+                $type = self::filenameToType($path);
+            }
+
+            $filename = basename($path);
+            if ($name == '') {
+                $name = $filename;
+            }
+
+            $this->attachment[] = array(
+                0 => $path,
+                1 => $filename,
+                2 => $name,
+                3 => $encoding,
+                4 => $type,
+                5 => false, // isStringAttachment
+                6 => $disposition,
+                7 => 0
+            );
+
+        } catch (phpmailerException $exc) {
+            $this->setError($exc->getMessage());
+            $this->edebug($exc->getMessage());
+            if ($this->exceptions) {
+                throw $exc;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Return the array of attachments.
+     * @return array
+     */
+    public function getAttachments()
+    {
+        return $this->attachment;
+    }
+
+    /**
+     * Attach all file, string, and binary attachments to the message.
+     * Returns an empty string on failure.
+     * @access protected
+     * @param string $disposition_type
+     * @param string $boundary
+     * @return string
+     */
+    protected function attachAll($disposition_type, $boundary)
+    {
+        // Return text of body
+        $mime = array();
+        $cidUniq = array();
+        $incl = array();
+
+        // Add all attachments
+        foreach ($this->attachment as $attachment) {
+            // Check if it is a valid disposition_filter
+            if ($attachment[6] == $disposition_type) {
+                // Check for string attachment
+                $string = '';
+                $path = '';
+                $bString = $attachment[5];
+                if ($bString) {
+                    $string = $attachment[0];
+                } else {
+                    $path = $attachment[0];
+                }
+
+                $inclhash = md5(serialize($attachment));
+                if (in_array($inclhash, $incl)) {
+                    continue;
+                }
+                $incl[] = $inclhash;
+                $name = $attachment[2];
+                $encoding = $attachment[3];
+                $type = $attachment[4];
+                $disposition = $attachment[6];
+                $cid = $attachment[7];
+                if ($disposition == 'inline' && isset($cidUniq[$cid])) {
+                    continue;
+                }
+                $cidUniq[$cid] = true;
+
+                $mime[] = sprintf('--%s%s', $boundary, $this->LE);
+                $mime[] = sprintf(
+                    'Content-Type: %s; name="%s"%s',
+                    $type,
+                    $this->encodeHeader($this->secureHeader($name)),
+                    $this->LE
+                );
+                // RFC1341 part 5 says 7bit is assumed if not specified
+                if ($encoding != '7bit') {
+                    $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
+                }
+
+                if ($disposition == 'inline') {
+                    $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
+                }
+
+                // If a filename contains any of these chars, it should be quoted,
+                // but not otherwise: RFC2183 & RFC2045 5.1
+                // Fixes a warning in IETF's msglint MIME checker
+                // Allow for bypassing the Content-Disposition header totally
+                if (!(empty($disposition))) {
+                    if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) {
+                        $mime[] = sprintf(
+                            'Content-Disposition: %s; filename="%s"%s',
+                            $disposition,
+                            $this->encodeHeader($this->secureHeader($name)),
+                            $this->LE . $this->LE
+                        );
+                    } else {
+                        $mime[] = sprintf(
+                            'Content-Disposition: %s; filename=%s%s',
+                            $disposition,
+                            $this->encodeHeader($this->secureHeader($name)),
+                            $this->LE . $this->LE
+                        );
+                    }
+                } else {
+                    $mime[] = $this->LE;
+                }
+
+                // Encode as string attachment
+                if ($bString) {
+                    $mime[] = $this->encodeString($string, $encoding);
+                    if ($this->isError()) {
+                        return '';
+                    }
+                    $mime[] = $this->LE . $this->LE;
+                } else {
+                    $mime[] = $this->encodeFile($path, $encoding);
+                    if ($this->isError()) {
+                        return '';
+                    }
+                    $mime[] = $this->LE . $this->LE;
+                }
+            }
+        }
+
+        $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
+
+        return implode('', $mime);
+    }
+
+    /**
+     * Encode a file attachment in requested format.
+     * Returns an empty string on failure.
+     * @param string $path The full path to the file
+     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+     * @throws phpmailerException
+     * @see EncodeFile(encodeFile
+     * @access protected
+     * @return string
+     */
+    protected function encodeFile($path, $encoding = 'base64')
+    {
+        try {
+            if (!is_readable($path)) {
+                throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
+            }
+            $magic_quotes = get_magic_quotes_runtime();
+            if ($magic_quotes) {
+                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+                    set_magic_quotes_runtime(false);
+                } else {
+                    //Doesn't exist in PHP 5.4, but we don't need to check because
+                    //get_magic_quotes_runtime always returns false in 5.4+
+                    //so it will never get here
+                    ini_set('magic_quotes_runtime', 0);
+                }
+            }
+            $file_buffer = file_get_contents($path);
+            $file_buffer = $this->encodeString($file_buffer, $encoding);
+            if ($magic_quotes) {
+                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+                    set_magic_quotes_runtime($magic_quotes);
+                } else {
+                    ini_set('magic_quotes_runtime', ($magic_quotes?'1':'0'));
+                }
+            }
+            return $file_buffer;
+        } catch (Exception $exc) {
+            $this->setError($exc->getMessage());
+            return '';
+        }
+    }
+
+    /**
+     * Encode a string in requested format.
+     * Returns an empty string on failure.
+     * @param string $str The text to encode
+     * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+     * @access public
+     * @return string
+     */
+    public function encodeString($str, $encoding = 'base64')
+    {
+        $encoded = '';
+        switch (strtolower($encoding)) {
+            case 'base64':
+                $encoded = chunk_split(base64_encode($str), 76, $this->LE);
+                break;
+            case '7bit':
+            case '8bit':
+                $encoded = $this->fixEOL($str);
+                // Make sure it ends with a line break
+                if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
+                    $encoded .= $this->LE;
+                }
+                break;
+            case 'binary':
+                $encoded = $str;
+                break;
+            case 'quoted-printable':
+                $encoded = $this->encodeQP($str);
+                break;
+            default:
+                $this->setError($this->lang('encoding') . $encoding);
+                break;
+        }
+        return $encoded;
+    }
+
+    /**
+     * Encode a header string optimally.
+     * Picks shortest of Q, B, quoted-printable or none.
+     * @access public
+     * @param string $str
+     * @param string $position
+     * @return string
+     */
+    public function encodeHeader($str, $position = 'text')
+    {
+        $matchcount = 0;
+        switch (strtolower($position)) {
+            case 'phrase':
+                if (!preg_match('/[\200-\377]/', $str)) {
+                    // Can't use addslashes as we don't know the value of magic_quotes_sybase
+                    $encoded = addcslashes($str, "\0..\37\177\\\"");
+                    if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
+                        return ($encoded);
+                    } else {
+                        return ("\"$encoded\"");
+                    }
+                }
+                $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
+                break;
+            /** @noinspection PhpMissingBreakStatementInspection */
+            case 'comment':
+                $matchcount = preg_match_all('/[()"]/', $str, $matches);
+                // Intentional fall-through
+            case 'text':
+            default:
+                $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
+                break;
+        }
+
+        if ($matchcount == 0) { // There are no chars that need encoding
+            return ($str);
+        }
+
+        $maxlen = 75 - 7 - strlen($this->CharSet);
+        // Try to select the encoding which should produce the shortest output
+        if ($matchcount > strlen($str) / 3) {
+            // More than a third of the content will need encoding, so B encoding will be most efficient
+            $encoding = 'B';
+            if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
+                // Use a custom function which correctly encodes and wraps long
+                // multibyte strings without breaking lines within a character
+                $encoded = $this->base64EncodeWrapMB($str, "\n");
+            } else {
+                $encoded = base64_encode($str);
+                $maxlen -= $maxlen % 4;
+                $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
+            }
+        } else {
+            $encoding = 'Q';
+            $encoded = $this->encodeQ($str, $position);
+            $encoded = $this->wrapText($encoded, $maxlen, true);
+            $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
+        }
+
+        $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
+        $encoded = trim(str_replace("\n", $this->LE, $encoded));
+
+        return $encoded;
+    }
+
+    /**
+     * Check if a string contains multi-byte characters.
+     * @access public
+     * @param string $str multi-byte text to wrap encode
+     * @return boolean
+     */
+    public function hasMultiBytes($str)
+    {
+        if (function_exists('mb_strlen')) {
+            return (strlen($str) > mb_strlen($str, $this->CharSet));
+        } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
+            return false;
+        }
+    }
+
+    /**
+     * Does a string contain any 8-bit chars (in any charset)?
+     * @param string $text
+     * @return boolean
+     */
+    public function has8bitChars($text)
+    {
+        return (boolean)preg_match('/[\x80-\xFF]/', $text);
+    }
+
+    /**
+     * Encode and wrap long multibyte strings for mail headers
+     * without breaking lines within a character.
+     * Adapted from a function by paravoid
+     * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
+     * @access public
+     * @param string $str multi-byte text to wrap encode
+     * @param string $linebreak string to use as linefeed/end-of-line
+     * @return string
+     */
+    public function base64EncodeWrapMB($str, $linebreak = null)
+    {
+        $start = '=?' . $this->CharSet . '?B?';
+        $end = '?=';
+        $encoded = '';
+        if ($linebreak === null) {
+            $linebreak = $this->LE;
+        }
+
+        $mb_length = mb_strlen($str, $this->CharSet);
+        // Each line must have length <= 75, including $start and $end
+        $length = 75 - strlen($start) - strlen($end);
+        // Average multi-byte ratio
+        $ratio = $mb_length / strlen($str);
+        // Base64 has a 4:3 ratio
+        $avgLength = floor($length * $ratio * .75);
+
+        for ($i = 0; $i < $mb_length; $i += $offset) {
+            $lookBack = 0;
+            do {
+                $offset = $avgLength - $lookBack;
+                $chunk = mb_substr($str, $i, $offset, $this->CharSet);
+                $chunk = base64_encode($chunk);
+                $lookBack++;
+            } while (strlen($chunk) > $length);
+            $encoded .= $chunk . $linebreak;
+        }
+
+        // Chomp the last linefeed
+        $encoded = substr($encoded, 0, -strlen($linebreak));
+        return $encoded;
+    }
+
+    /**
+     * Encode a string in quoted-printable format.
+     * According to RFC2045 section 6.7.
+     * @access public
+     * @param string $string The text to encode
+     * @param integer $line_max Number of chars allowed on a line before wrapping
+     * @return string
+     * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
+     */
+    public function encodeQP($string, $line_max = 76)
+    {
+        if (function_exists('quoted_printable_encode')) { // Use native function if it's available (>= PHP5.3)
+            return $this->fixEOL(quoted_printable_encode($string));
+        }
+        // Fall back to a pure PHP implementation
+        $string = str_replace(
+            array('%20', '%0D%0A.', '%0D%0A', '%'),
+            array(' ', "\r\n=2E", "\r\n", '='),
+            rawurlencode($string)
+        );
+        $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
+        return $this->fixEOL($string);
+    }
+
+    /**
+     * Backward compatibility wrapper for an old QP encoding function that was removed.
+     * @see PHPMailer::encodeQP()
+     * @access public
+     * @param string $string
+     * @param integer $line_max
+     * @param boolean $space_conv
+     * @return string
+     * @deprecated Use encodeQP instead.
+     */
+    public function encodeQPphp(
+        $string,
+        $line_max = 76,
+        /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
+    ) {
+        return $this->encodeQP($string, $line_max);
+    }
+
+    /**
+     * Encode a string using Q encoding.
+     * @link http://tools.ietf.org/html/rfc2047
+     * @param string $str the text to encode
+     * @param string $position Where the text is going to be used, see the RFC for what that means
+     * @access public
+     * @return string
+     */
+    public function encodeQ($str, $position = 'text')
+    {
+        // There should not be any EOL in the string
+        $pattern = '';
+        $encoded = str_replace(array("\r", "\n"), '', $str);
+        switch (strtolower($position)) {
+            case 'phrase':
+                // RFC 2047 section 5.3
+                $pattern = '^A-Za-z0-9!*+\/ -';
+                break;
+            /** @noinspection PhpMissingBreakStatementInspection */
+            case 'comment':
+                // RFC 2047 section 5.2
+                $pattern = '\(\)"';
+                // intentional fall-through
+                // for this reason we build the $pattern without including delimiters and []
+            case 'text':
+            default:
+                // RFC 2047 section 5.1
+                // Replace every high ascii, control, =, ? and _ characters
+                $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
+                break;
+        }
+        $matches = array();
+        if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
+            // If the string contains an '=', make sure it's the first thing we replace
+            // so as to avoid double-encoding
+            $eqkey = array_search('=', $matches[0]);
+            if ($eqkey !== false) {
+                unset($matches[0][$eqkey]);
+                array_unshift($matches[0], '=');
+            }
+            foreach (array_unique($matches[0]) as $char) {
+                $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
+            }
+        }
+        // Replace every spaces to _ (more readable than =20)
+        return str_replace(' ', '_', $encoded);
+    }
+
+
+    /**
+     * Add a string or binary attachment (non-filesystem).
+     * This method can be used to attach ascii or binary data,
+     * such as a BLOB record from a database.
+     * @param string $string String attachment data.
+     * @param string $filename Name of the attachment.
+     * @param string $encoding File encoding (see $Encoding).
+     * @param string $type File extension (MIME) type.
+     * @param string $disposition Disposition to use
+     * @return void
+     */
+    public function addStringAttachment(
+        $string,
+        $filename,
+        $encoding = 'base64',
+        $type = '',
+        $disposition = 'attachment'
+    ) {
+        // If a MIME type is not specified, try to work it out from the file name
+        if ($type == '') {
+            $type = self::filenameToType($filename);
+        }
+        // Append to $attachment array
+        $this->attachment[] = array(
+            0 => $string,
+            1 => $filename,
+            2 => basename($filename),
+            3 => $encoding,
+            4 => $type,
+            5 => true, // isStringAttachment
+            6 => $disposition,
+            7 => 0
+        );
+    }
+
+    /**
+     * Add an embedded (inline) attachment from a file.
+     * This can include images, sounds, and just about any other document type.
+     * These differ from 'regular' attachmants in that they are intended to be
+     * displayed inline with the message, not just attached for download.
+     * This is used in HTML messages that embed the images
+     * the HTML refers to using the $cid value.
+     * @param string $path Path to the attachment.
+     * @param string $cid Content ID of the attachment; Use this to reference
+     *        the content when using an embedded image in HTML.
+     * @param string $name Overrides the attachment name.
+     * @param string $encoding File encoding (see $Encoding).
+     * @param string $type File MIME type.
+     * @param string $disposition Disposition to use
+     * @return boolean True on successfully adding an attachment
+     */
+    public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
+    {
+        if (!@is_file($path)) {
+            $this->setError($this->lang('file_access') . $path);
+            return false;
+        }
+
+        // If a MIME type is not specified, try to work it out from the file name
+        if ($type == '') {
+            $type = self::filenameToType($path);
+        }
+
+        $filename = basename($path);
+        if ($name == '') {
+            $name = $filename;
+        }
+
+        // Append to $attachment array
+        $this->attachment[] = array(
+            0 => $path,
+            1 => $filename,
+            2 => $name,
+            3 => $encoding,
+            4 => $type,
+            5 => false, // isStringAttachment
+            6 => $disposition,
+            7 => $cid
+        );
+        return true;
+    }
+
+    /**
+     * Add an embedded stringified attachment.
+     * This can include images, sounds, and just about any other document type.
+     * Be sure to set the $type to an image type for images:
+     * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
+     * @param string $string The attachment binary data.
+     * @param string $cid Content ID of the attachment; Use this to reference
+     *        the content when using an embedded image in HTML.
+     * @param string $name
+     * @param string $encoding File encoding (see $Encoding).
+     * @param string $type MIME type.
+     * @param string $disposition Disposition to use
+     * @return boolean True on successfully adding an attachment
+     */
+    public function addStringEmbeddedImage(
+        $string,
+        $cid,
+        $name = '',
+        $encoding = 'base64',
+        $type = '',
+        $disposition = 'inline'
+    ) {
+        // If a MIME type is not specified, try to work it out from the name
+        if ($type == '') {
+            $type = self::filenameToType($name);
+        }
+
+        // Append to $attachment array
+        $this->attachment[] = array(
+            0 => $string,
+            1 => $name,
+            2 => $name,
+            3 => $encoding,
+            4 => $type,
+            5 => true, // isStringAttachment
+            6 => $disposition,
+            7 => $cid
+        );
+        return true;
+    }
+
+    /**
+     * Check if an inline attachment is present.
+     * @access public
+     * @return boolean
+     */
+    public function inlineImageExists()
+    {
+        foreach ($this->attachment as $attachment) {
+            if ($attachment[6] == 'inline') {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if an attachment (non-inline) is present.
+     * @return boolean
+     */
+    public function attachmentExists()
+    {
+        foreach ($this->attachment as $attachment) {
+            if ($attachment[6] == 'attachment') {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if this message has an alternative body set.
+     * @return boolean
+     */
+    public function alternativeExists()
+    {
+        return !empty($this->AltBody);
+    }
+
+    /**
+     * Clear all To recipients.
+     * @return void
+     */
+    public function clearAddresses()
+    {
+        foreach ($this->to as $to) {
+            unset($this->all_recipients[strtolower($to[0])]);
+        }
+        $this->to = array();
+    }
+
+    /**
+     * Clear all CC recipients.
+     * @return void
+     */
+    public function clearCCs()
+    {
+        foreach ($this->cc as $cc) {
+            unset($this->all_recipients[strtolower($cc[0])]);
+        }
+        $this->cc = array();
+    }
+
+    /**
+     * Clear all BCC recipients.
+     * @return void
+     */
+    public function clearBCCs()
+    {
+        foreach ($this->bcc as $bcc) {
+            unset($this->all_recipients[strtolower($bcc[0])]);
+        }
+        $this->bcc = array();
+    }
+
+    /**
+     * Clear all ReplyTo recipients.
+     * @return void
+     */
+    public function clearReplyTos()
+    {
+        $this->ReplyTo = array();
+    }
+
+    /**
+     * Clear all recipient types.
+     * @return void
+     */
+    public function clearAllRecipients()
+    {
+        $this->to = array();
+        $this->cc = array();
+        $this->bcc = array();
+        $this->all_recipients = array();
+    }
+
+    /**
+     * Clear all filesystem, string, and binary attachments.
+     * @return void
+     */
+    public function clearAttachments()
+    {
+        $this->attachment = array();
+    }
+
+    /**
+     * Clear all custom headers.
+     * @return void
+     */
+    public function clearCustomHeaders()
+    {
+        $this->CustomHeader = array();
+    }
+
+    /**
+     * Add an error message to the error container.
+     * @access protected
+     * @param string $msg
+     * @return void
+     */
+    protected function setError($msg)
+    {
+        $this->error_count++;
+        if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
+            $lasterror = $this->smtp->getError();
+            if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {
+                $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n";
+            }
+        }
+        $this->ErrorInfo = $msg;
+    }
+
+    /**
+     * Return an RFC 822 formatted date.
+     * @access public
+     * @return string
+     * @static
+     */
+    public static function rfcDate()
+    {
+        // Set the time zone to whatever the default is to avoid 500 errors
+        // Will default to UTC if it's not set properly in php.ini
+        date_default_timezone_set(@date_default_timezone_get());
+        return date('D, j M Y H:i:s O');
+    }
+
+    /**
+     * Get the server hostname.
+     * Returns 'localhost.localdomain' if unknown.
+     * @access protected
+     * @return string
+     */
+    protected function serverHostname()
+    {
+        $result = 'localhost.localdomain';
+        if (!empty($this->Hostname)) {
+            $result = $this->Hostname;
+        } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
+            $result = $_SERVER['SERVER_NAME'];
+        } elseif (function_exists('gethostname') && gethostname() !== false) {
+            $result = gethostname();
+        } elseif (php_uname('n') !== false) {
+            $result = php_uname('n');
+        }
+        return $result;
+    }
+
+    /**
+     * Get an error message in the current language.
+     * @access protected
+     * @param string $key
+     * @return string
+     */
+    protected function lang($key)
+    {
+        if (count($this->language) < 1) {
+            $this->setLanguage('en'); // set the default language
+        }
+
+        if (isset($this->language[$key])) {
+            return $this->language[$key];
+        } else {
+            return 'Language string failed to load: ' . $key;
+        }
+    }
+
+    /**
+     * Check if an error occurred.
+     * @access public
+     * @return boolean True if an error did occur.
+     */
+    public function isError()
+    {
+        return ($this->error_count > 0);
+    }
+
+    /**
+     * Ensure consistent line endings in a string.
+     * Changes every end of line from CRLF, CR or LF to $this->LE.
+     * @access public
+     * @param string $str String to fixEOL
+     * @return string
+     */
+    public function fixEOL($str)
+    {
+        // Normalise to \n
+        $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
+        // Now convert LE as needed
+        if ($this->LE !== "\n") {
+            $nstr = str_replace("\n", $this->LE, $nstr);
+        }
+        return $nstr;
+    }
+
+    /**
+     * Add a custom header.
+     * $name value can be overloaded to contain
+     * both header name and value (name:value)
+     * @access public
+     * @param string $name Custom header name
+     * @param string $value Header value
+     * @return void
+     */
+    public function addCustomHeader($name, $value = null)
+    {
+        if ($value === null) {
+            // Value passed in as name:value
+            $this->CustomHeader[] = explode(':', $name, 2);
+        } else {
+            $this->CustomHeader[] = array($name, $value);
+        }
+    }
+
+    /**
+     * Create a message from an HTML string.
+     * Automatically makes modifications for inline images and backgrounds
+     * and creates a plain-text version by converting the HTML.
+     * Overwrites any existing values in $this->Body and $this->AltBody
+     * @access public
+     * @param string $message HTML message string
+     * @param string $basedir baseline directory for path
+     * @param boolean $advanced Whether to use the advanced HTML to text converter
+     * @return string $message
+     */
+    public function msgHTML($message, $basedir = '', $advanced = false)
+    {
+        preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
+        if (isset($images[2])) {
+            foreach ($images[2] as $imgindex => $url) {
+                // do not change urls for absolute images (thanks to corvuscorax)
+                if (!preg_match('#^[A-z]+://#', $url)) {
+                    $filename = basename($url);
+                    $directory = dirname($url);
+                    if ($directory == '.') {
+                        $directory = '';
+                    }
+                    $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
+                    if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
+                        $basedir .= '/';
+                    }
+                    if (strlen($directory) > 1 && substr($directory, -1) != '/') {
+                        $directory .= '/';
+                    }
+                    if ($this->addEmbeddedImage(
+                        $basedir . $directory . $filename,
+                        $cid,
+                        $filename,
+                        'base64',
+                        self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION))
+                    )
+                    ) {
+                        $message = preg_replace(
+                            '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
+                            $images[1][$imgindex] . '="cid:' . $cid . '"',
+                            $message
+                        );
+                    }
+                }
+            }
+        }
+        $this->isHTML(true);
+        // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
+        $this->Body = $this->normalizeBreaks($message);
+        $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
+        if (empty($this->AltBody)) {
+            $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
+                self::CRLF . self::CRLF;
+        }
+        return $this->Body;
+    }
+
+    /**
+     * Convert an HTML string into plain text.
+     * @param string $html The HTML text to convert
+     * @param boolean $advanced Should this use the more complex html2text converter or just a simple one?
+     * @return string
+     */
+    public function html2text($html, $advanced = false)
+    {
+        if ($advanced) {
+            require_once 'extras/class.html2text.php';
+            $htmlconverter = new html2text($html);
+            return $htmlconverter->get_text();
+        }
+        return html_entity_decode(
+            trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
+            ENT_QUOTES,
+            $this->CharSet
+        );
+    }
+
+    /**
+     * Get the MIME type for a file extension.
+     * @param string $ext File extension
+     * @access public
+     * @return string MIME type of file.
+     * @static
+     */
+    public static function _mime_types($ext = '')
+    {
+        $mimes = array(
+            'xl' => 'application/excel',
+            'hqx' => 'application/mac-binhex40',
+            'cpt' => 'application/mac-compactpro',
+            'bin' => 'application/macbinary',
+            'doc' => 'application/msword',
+            'word' => 'application/msword',
+            'class' => 'application/octet-stream',
+            'dll' => 'application/octet-stream',
+            'dms' => 'application/octet-stream',
+            'exe' => 'application/octet-stream',
+            'lha' => 'application/octet-stream',
+            'lzh' => 'application/octet-stream',
+            'psd' => 'application/octet-stream',
+            'sea' => 'application/octet-stream',
+            'so' => 'application/octet-stream',
+            'oda' => 'application/oda',
+            'pdf' => 'application/pdf',
+            'ai' => 'application/postscript',
+            'eps' => 'application/postscript',
+            'ps' => 'application/postscript',
+            'smi' => 'application/smil',
+            'smil' => 'application/smil',
+            'mif' => 'application/vnd.mif',
+            'xls' => 'application/vnd.ms-excel',
+            'ppt' => 'application/vnd.ms-powerpoint',
+            'wbxml' => 'application/vnd.wap.wbxml',
+            'wmlc' => 'application/vnd.wap.wmlc',
+            'dcr' => 'application/x-director',
+            'dir' => 'application/x-director',
+            'dxr' => 'application/x-director',
+            'dvi' => 'application/x-dvi',
+            'gtar' => 'application/x-gtar',
+            'php3' => 'application/x-httpd-php',
+            'php4' => 'application/x-httpd-php',
+            'php' => 'application/x-httpd-php',
+            'phtml' => 'application/x-httpd-php',
+            'phps' => 'application/x-httpd-php-source',
+            'js' => 'application/x-javascript',
+            'swf' => 'application/x-shockwave-flash',
+            'sit' => 'application/x-stuffit',
+            'tar' => 'application/x-tar',
+            'tgz' => 'application/x-tar',
+            'xht' => 'application/xhtml+xml',
+            'xhtml' => 'application/xhtml+xml',
+            'zip' => 'application/zip',
+            'mid' => 'audio/midi',
+            'midi' => 'audio/midi',
+            'mp2' => 'audio/mpeg',
+            'mp3' => 'audio/mpeg',
+            'mpga' => 'audio/mpeg',
+            'aif' => 'audio/x-aiff',
+            'aifc' => 'audio/x-aiff',
+            'aiff' => 'audio/x-aiff',
+            'ram' => 'audio/x-pn-realaudio',
+            'rm' => 'audio/x-pn-realaudio',
+            'rpm' => 'audio/x-pn-realaudio-plugin',
+            'ra' => 'audio/x-realaudio',
+            'wav' => 'audio/x-wav',
+            'bmp' => 'image/bmp',
+            'gif' => 'image/gif',
+            'jpeg' => 'image/jpeg',
+            'jpe' => 'image/jpeg',
+            'jpg' => 'image/jpeg',
+            'png' => 'image/png',
+            'tiff' => 'image/tiff',
+            'tif' => 'image/tiff',
+            'eml' => 'message/rfc822',
+            'css' => 'text/css',
+            'html' => 'text/html',
+            'htm' => 'text/html',
+            'shtml' => 'text/html',
+            'log' => 'text/plain',
+            'text' => 'text/plain',
+            'txt' => 'text/plain',
+            'rtx' => 'text/richtext',
+            'rtf' => 'text/rtf',
+            'vcf' => 'text/vcard',
+            'vcard' => 'text/vcard',
+            'xml' => 'text/xml',
+            'xsl' => 'text/xml',
+            'mpeg' => 'video/mpeg',
+            'mpe' => 'video/mpeg',
+            'mpg' => 'video/mpeg',
+            'mov' => 'video/quicktime',
+            'qt' => 'video/quicktime',
+            'rv' => 'video/vnd.rn-realvideo',
+            'avi' => 'video/x-msvideo',
+            'movie' => 'video/x-sgi-movie'
+        );
+        return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');
+    }
+
+    /**
+     * Map a file name to a MIME type.
+     * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
+     * @param string $filename A file name or full path, does not need to exist as a file
+     * @return string
+     * @static
+     */
+    public static function filenameToType($filename)
+    {
+        // In case the path is a URL, strip any query string before getting extension
+        $qpos = strpos($filename, '?');
+        if ($qpos !== false) {
+            $filename = substr($filename, 0, $qpos);
+        }
+        $pathinfo = self::mb_pathinfo($filename);
+        return self::_mime_types($pathinfo['extension']);
+    }
+
+    /**
+     * Multi-byte-safe pathinfo replacement.
+     * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
+     * Works similarly to the one in PHP >= 5.2.0
+     * @link http://www.php.net/manual/en/function.pathinfo.php#107461
+     * @param string $path A filename or path, does not need to exist as a file
+     * @param integer|string $options Either a PATHINFO_* constant,
+     *      or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
+     * @return string|array
+     * @static
+     */
+    public static function mb_pathinfo($path, $options = null)
+    {
+        $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
+        $pathinfo = array();
+        if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
+            if (array_key_exists(1, $pathinfo)) {
+                $ret['dirname'] = $pathinfo[1];
+            }
+            if (array_key_exists(2, $pathinfo)) {
+                $ret['basename'] = $pathinfo[2];
+            }
+            if (array_key_exists(5, $pathinfo)) {
+                $ret['extension'] = $pathinfo[5];
+            }
+            if (array_key_exists(3, $pathinfo)) {
+                $ret['filename'] = $pathinfo[3];
+            }
+        }
+        switch ($options) {
+            case PATHINFO_DIRNAME:
+            case 'dirname':
+                return $ret['dirname'];
+            case PATHINFO_BASENAME:
+            case 'basename':
+                return $ret['basename'];
+            case PATHINFO_EXTENSION:
+            case 'extension':
+                return $ret['extension'];
+            case PATHINFO_FILENAME:
+            case 'filename':
+                return $ret['filename'];
+            default:
+                return $ret;
+        }
+    }
+
+    /**
+     * Set or reset instance properties.
+     *
+     * Usage Example:
+     * $page->set('X-Priority', '3');
+     *
+     * @access public
+     * @param string $name
+     * @param mixed $value
+     * NOTE: will not work with arrays, there are no arrays to set/reset
+     * @throws phpmailerException
+     * @return boolean
+     * @TODO Should this not be using __set() magic function?
+     */
+    public function set($name, $value = '')
+    {
+        try {
+            if (isset($this->$name)) {
+                $this->$name = $value;
+            } else {
+                throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);
+            }
+        } catch (Exception $exc) {
+            $this->setError($exc->getMessage());
+            if ($exc->getCode() == self::STOP_CRITICAL) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Strip newlines to prevent header injection.
+     * @access public
+     * @param string $str
+     * @return string
+     */
+    public function secureHeader($str)
+    {
+        return trim(str_replace(array("\r", "\n"), '', $str));
+    }
+
+    /**
+     * Normalize line breaks in a string.
+     * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
+     * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
+     * @param string $text
+     * @param string $breaktype What kind of line break to use, defaults to CRLF
+     * @return string
+     * @access public
+     * @static
+     */
+    public static function normalizeBreaks($text, $breaktype = "\r\n")
+    {
+        return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
+    }
+
+
+    /**
+     * Set the public and private key files and password for S/MIME signing.
+     * @access public
+     * @param string $cert_filename
+     * @param string $key_filename
+     * @param string $key_pass Password for private key
+     */
+    public function sign($cert_filename, $key_filename, $key_pass)
+    {
+        $this->sign_cert_file = $cert_filename;
+        $this->sign_key_file = $key_filename;
+        $this->sign_key_pass = $key_pass;
+    }
+
+    /**
+     * Quoted-Printable-encode a DKIM header.
+     * @access public
+     * @param string $txt
+     * @return string
+     */
+    public function DKIM_QP($txt)
+    {
+        $line = '';
+        for ($i = 0; $i < strlen($txt); $i++) {
+            $ord = ord($txt[$i]);
+            if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
+                $line .= $txt[$i];
+            } else {
+                $line .= '=' . sprintf('%02X', $ord);
+            }
+        }
+        return $line;
+    }
+
+    /**
+     * Generate a DKIM signature.
+     * @access public
+     * @param string $signHeader
+     * @throws phpmailerException
+     * @return string
+     */
+    public function DKIM_Sign($signHeader)
+    {
+        if (!defined('PKCS7_TEXT')) {
+            if ($this->exceptions) {
+                throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');
+            }
+            return '';
+        }
+        $privKeyStr = file_get_contents($this->DKIM_private);
+        if ($this->DKIM_passphrase != '') {
+            $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
+        } else {
+            $privKey = $privKeyStr;
+        }
+        if (openssl_sign($signHeader, $signature, $privKey)) {
+            return base64_encode($signature);
+        }
+        return '';
+    }
+
+    /**
+     * Generate a DKIM canonicalization header.
+     * @access public
+     * @param string $signHeader Header
+     * @return string
+     */
+    public function DKIM_HeaderC($signHeader)
+    {
+        $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
+        $lines = explode("\r\n", $signHeader);
+        foreach ($lines as $key => $line) {
+            list($heading, $value) = explode(':', $line, 2);
+            $heading = strtolower($heading);
+            $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
+            $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
+        }
+        $signHeader = implode("\r\n", $lines);
+        return $signHeader;
+    }
+
+    /**
+     * Generate a DKIM canonicalization body.
+     * @access public
+     * @param string $body Message Body
+     * @return string
+     */
+    public function DKIM_BodyC($body)
+    {
+        if ($body == '') {
+            return "\r\n";
+        }
+        // stabilize line endings
+        $body = str_replace("\r\n", "\n", $body);
+        $body = str_replace("\n", "\r\n", $body);
+        // END stabilize line endings
+        while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
+            $body = substr($body, 0, strlen($body) - 2);
+        }
+        return $body;
+    }
+
+    /**
+     * Create the DKIM header and body in a new message header.
+     * @access public
+     * @param string $headers_line Header lines
+     * @param string $subject Subject
+     * @param string $body Body
+     * @return string
+     */
+    public function DKIM_Add($headers_line, $subject, $body)
+    {
+        $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
+        $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
+        $DKIMquery = 'dns/txt'; // Query method
+        $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
+        $subject_header = "Subject: $subject";
+        $headers = explode($this->LE, $headers_line);
+        $from_header = '';
+        $to_header = '';
+        $current = '';
+        foreach ($headers as $header) {
+            if (strpos($header, 'From:') === 0) {
+                $from_header = $header;
+                $current = 'from_header';
+            } elseif (strpos($header, 'To:') === 0) {
+                $to_header = $header;
+                $current = 'to_header';
+            } else {
+                if ($current && strpos($header, ' =?') === 0) {
+                    $current .= $header;
+                } else {
+                    $current = '';
+                }
+            }
+        }
+        $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
+        $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
+        $subject = str_replace(
+            '|',
+            '=7C',
+            $this->DKIM_QP($subject_header)
+        ); // Copied header fields (dkim-quoted-printable)
+        $body = $this->DKIM_BodyC($body);
+        $DKIMlen = strlen($body); // Length of body
+        $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
+        $ident = ($this->DKIM_identity == '') ? '' : ' i=' . $this->DKIM_identity . ';';
+        $dkimhdrs = 'DKIM-Signature: v=1; a=' .
+            $DKIMsignatureType . '; q=' .
+            $DKIMquery . '; l=' .
+            $DKIMlen . '; s=' .
+            $this->DKIM_selector .
+            ";\r\n" .
+            "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
+            "\th=From:To:Subject;\r\n" .
+            "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
+            "\tz=$from\r\n" .
+            "\t|$to\r\n" .
+            "\t|$subject;\r\n" .
+            "\tbh=" . $DKIMb64 . ";\r\n" .
+            "\tb=";
+        $toSign = $this->DKIM_HeaderC(
+            $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs
+        );
+        $signed = $this->DKIM_Sign($toSign);
+        return $dkimhdrs . $signed . "\r\n";
+    }
+
+    /**
+     * Allows for public read access to 'to' property.
+     * @access public
+     * @return array
+     */
+    public function getToAddresses()
+    {
+        return $this->to;
+    }
+
+    /**
+     * Allows for public read access to 'cc' property.
+     * @access public
+     * @return array
+     */
+    public function getCcAddresses()
+    {
+        return $this->cc;
+    }
+
+    /**
+     * Allows for public read access to 'bcc' property.
+     * @access public
+     * @return array
+     */
+    public function getBccAddresses()
+    {
+        return $this->bcc;
+    }
+
+    /**
+     * Allows for public read access to 'ReplyTo' property.
+     * @access public
+     * @return array
+     */
+    public function getReplyToAddresses()
+    {
+        return $this->ReplyTo;
+    }
+
+    /**
+     * Allows for public read access to 'all_recipients' property.
+     * @access public
+     * @return array
+     */
+    public function getAllRecipientAddresses()
+    {
+        return $this->all_recipients;
+    }
+
+    /**
+     * Perform a callback.
+     * @param boolean $isSent
+     * @param array $to
+     * @param array $cc
+     * @param array $bcc
+     * @param string $subject
+     * @param string $body
+     * @param string $from
+     */
+    protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
+    {
+        if (!empty($this->action_function) && is_callable($this->action_function)) {
+            $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
+            call_user_func_array($this->action_function, $params);
+        }
+    }
+}
+
+/**
+ * PHPMailer exception handler
+ * @package PHPMailer
+ */
+class phpmailerException extends \Exception
+{
+    /**
+     * Prettify error message output
+     * @return string
+     */
+    public function errorMessage()
+    {
+        $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n";
+        return $errorMsg;
+    }
+}

+ 397 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/PHPMailer/class.pop3.php

@@ -0,0 +1,397 @@
+<?php namespace Mail\PHPMailer;
+/**
+ * PHPMailer POP-Before-SMTP Authentication Class.
+ * PHP Version 5
+ * @package PHPMailer
+ * @link https://github.com/PHPMailer/PHPMailer/
+ * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
+ * @author Jim Jagielski (jimjag) <[email protected]>
+ * @author Andy Prevost (codeworxtech) <[email protected]>
+ * @author Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2014 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * PHPMailer POP-Before-SMTP Authentication Class.
+ * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication.
+ * Does not support APOP.
+ * @package PHPMailer
+ * @author Richard Davey (original author) <[email protected]>
+ * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
+ * @author Jim Jagielski (jimjag) <[email protected]>
+ * @author Andy Prevost (codeworxtech) <[email protected]>
+ */
+class POP3
+{
+    /**
+     * The POP3 PHPMailer Version number.
+     * @type string
+     * @access public
+     */
+    public $Version = '5.2.8';
+
+    /**
+     * Default POP3 port number.
+     * @type integer
+     * @access public
+     */
+    public $POP3_PORT = 110;
+
+    /**
+     * Default timeout in seconds.
+     * @type integer
+     * @access public
+     */
+    public $POP3_TIMEOUT = 30;
+
+    /**
+     * POP3 Carriage Return + Line Feed.
+     * @type string
+     * @access public
+     * @deprecated Use the constant instead
+     */
+    public $CRLF = "\r\n";
+
+    /**
+     * Debug display level.
+     * Options: 0 = no, 1+ = yes
+     * @type integer
+     * @access public
+     */
+    public $do_debug = 0;
+
+    /**
+     * POP3 mail server hostname.
+     * @type string
+     * @access public
+     */
+    public $host;
+
+    /**
+     * POP3 port number.
+     * @type integer
+     * @access public
+     */
+    public $port;
+
+    /**
+     * POP3 Timeout Value in seconds.
+     * @type integer
+     * @access public
+     */
+    public $tval;
+
+    /**
+     * POP3 username
+     * @type string
+     * @access public
+     */
+    public $username;
+
+    /**
+     * POP3 password.
+     * @type string
+     * @access public
+     */
+    public $password;
+
+    /**
+     * Resource handle for the POP3 connection socket.
+     * @type resource
+     * @access private
+     */
+    private $pop_conn;
+
+    /**
+     * Are we connected?
+     * @type boolean
+     * @access private
+     */
+    private $connected = false;
+
+    /**
+     * Error container.
+     * @type array
+     * @access private
+     */
+    private $errors = array();
+
+    /**
+     * Line break constant
+     */
+    const CRLF = "\r\n";
+
+    /**
+     * Simple static wrapper for all-in-one POP before SMTP
+     * @param $host
+     * @param boolean $port
+     * @param boolean $tval
+     * @param string $username
+     * @param string $password
+     * @param integer $debug_level
+     * @return boolean
+     */
+    public static function popBeforeSmtp(
+        $host,
+        $port = false,
+        $tval = false,
+        $username = '',
+        $password = '',
+        $debug_level = 0
+    ) {
+        $pop = new POP3;
+        return $pop->authorise($host, $port, $tval, $username, $password, $debug_level);
+    }
+
+    /**
+     * Authenticate with a POP3 server.
+     * A connect, login, disconnect sequence
+     * appropriate for POP-before SMTP authorisation.
+     * @access public
+     * @param string $host
+     * @param integer|boolean $port
+     * @param integer|boolean $tval
+     * @param string $username
+     * @param string $password
+     * @param integer $debug_level
+     * @return boolean
+     */
+    public function authorise($host, $port = false, $tval = false, $username = '', $password = '', $debug_level = 0)
+    {
+        $this->host = $host;
+        // If no port value provided, use default
+        if ($port === false) {
+            $this->port = $this->POP3_PORT;
+        } else {
+            $this->port = $port;
+        }
+        // If no timeout value provided, use default
+        if ($tval === false) {
+            $this->tval = $this->POP3_TIMEOUT;
+        } else {
+            $this->tval = $tval;
+        }
+        $this->do_debug = $debug_level;
+        $this->username = $username;
+        $this->password = $password;
+        //  Reset the error log
+        $this->errors = array();
+        //  connect
+        $result = $this->connect($this->host, $this->port, $this->tval);
+        if ($result) {
+            $login_result = $this->login($this->username, $this->password);
+            if ($login_result) {
+                $this->disconnect();
+                return true;
+            }
+        }
+        // We need to disconnect regardless of whether the login succeeded
+        $this->disconnect();
+        return false;
+    }
+
+    /**
+     * Connect to a POP3 server.
+     * @access public
+     * @param string $host
+     * @param integer|boolean $port
+     * @param integer $tval
+     * @return boolean
+     */
+    public function connect($host, $port = false, $tval = 30)
+    {
+        //  Are we already connected?
+        if ($this->connected) {
+            return true;
+        }
+
+        //On Windows this will raise a PHP Warning error if the hostname doesn't exist.
+        //Rather than suppress it with @fsockopen, capture it cleanly instead
+        set_error_handler(array($this, 'catchWarning'));
+
+        if ($port === false) {
+            $port = $this->POP3_PORT;
+        }
+
+        //  connect to the POP3 server
+        $this->pop_conn = fsockopen(
+            $host, //  POP3 Host
+            $port, //  Port #
+            $errno, //  Error Number
+            $errstr, //  Error Message
+            $tval
+        ); //  Timeout (seconds)
+        //  Restore the error handler
+        restore_error_handler();
+
+        //  Did we connect?
+        if ($this->pop_conn === false) {
+            //  It would appear not...
+            $this->setError(array(
+                'error' => "Failed to connect to server $host on port $port",
+                'errno' => $errno,
+                'errstr' => $errstr
+            ));
+            return false;
+        }
+
+        //  Increase the stream time-out
+        stream_set_timeout($this->pop_conn, $tval, 0);
+
+        //  Get the POP3 server response
+        $pop3_response = $this->getResponse();
+        //  Check for the +OK
+        if ($this->checkResponse($pop3_response)) {
+            //  The connection is established and the POP3 server is talking
+            $this->connected = true;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Log in to the POP3 server.
+     * Does not support APOP (RFC 2828, 4949).
+     * @access public
+     * @param string $username
+     * @param string $password
+     * @return boolean
+     */
+    public function login($username = '', $password = '')
+    {
+        if (!$this->connected) {
+            $this->setError('Not connected to POP3 server');
+        }
+        if (empty($username)) {
+            $username = $this->username;
+        }
+        if (empty($password)) {
+            $password = $this->password;
+        }
+
+        // Send the Username
+        $this->sendString("USER $username" . self::CRLF);
+        $pop3_response = $this->getResponse();
+        if ($this->checkResponse($pop3_response)) {
+            // Send the Password
+            $this->sendString("PASS $password" . self::CRLF);
+            $pop3_response = $this->getResponse();
+            if ($this->checkResponse($pop3_response)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Disconnect from the POP3 server.
+     * @access public
+     */
+    public function disconnect()
+    {
+        $this->sendString('QUIT');
+        //The QUIT command may cause the daemon to exit, which will kill our connection
+        //So ignore errors here
+        try {
+            @fclose($this->pop_conn);
+        } catch (Exception $e) {
+            //Do nothing
+        };
+    }
+
+    /**
+     * Get a response from the POP3 server.
+     * $size is the maximum number of bytes to retrieve
+     * @param integer $size
+     * @return string
+     * @access private
+     */
+    private function getResponse($size = 128)
+    {
+        $response = fgets($this->pop_conn, $size);
+        if ($this->do_debug >= 1) {
+            echo "Server -> Client: $response";
+        }
+        return $response;
+    }
+
+    /**
+     * Send raw data to the POP3 server.
+     * @param string $string
+     * @return integer
+     * @access private
+     */
+    private function sendString($string)
+    {
+        if ($this->pop_conn) {
+            if ($this->do_debug >= 2) { //Show client messages when debug >= 2
+                echo "Client -> Server: $string";
+            }
+            return fwrite($this->pop_conn, $string, strlen($string));
+        }
+        return 0;
+    }
+
+    /**
+     * Checks the POP3 server response.
+     * Looks for for +OK or -ERR.
+     * @param string $string
+     * @return boolean
+     * @access private
+     */
+    private function checkResponse($string)
+    {
+        if (substr($string, 0, 3) !== '+OK') {
+            $this->setError(array(
+                'error' => "Server reported an error: $string",
+                'errno' => 0,
+                'errstr' => ''
+            ));
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Add an error to the internal error store.
+     * Also display debug output if it's enabled.
+     * @param $error
+     */
+    private function setError($error)
+    {
+        $this->errors[] = $error;
+        if ($this->do_debug >= 1) {
+            echo '<pre>';
+            foreach ($this->errors as $error) {
+                print_r($error);
+            }
+            echo '</pre>';
+        }
+    }
+
+    /**
+     * POP3 connection error handler.
+     * @param integer $errno
+     * @param string $errstr
+     * @param string $errfile
+     * @param integer $errline
+     * @access private
+     */
+    private function catchWarning($errno, $errstr, $errfile, $errline)
+    {
+        $this->setError(array(
+            'error' => "Connecting to the POP3 server raised a PHP warning: ",
+            'errno' => $errno,
+            'errstr' => $errstr,
+            'errfile' => $errfile,
+            'errline' => $errline
+        ));
+    }
+}

+ 941 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/PHPMailer/class.smtp.php

@@ -0,0 +1,941 @@
+<?php namespace Mail\PHPMailer;
+/**
+ * PHPMailer RFC821 SMTP email transport class.
+ * PHP Version 5
+ * @package PHPMailer
+ * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
+ * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
+ * @author Jim Jagielski (jimjag) <[email protected]>
+ * @author Andy Prevost (codeworxtech) <[email protected]>
+ * @author Brent R. Matzelle (original founder)
+ * @copyright 2014 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * PHPMailer RFC821 SMTP email transport class.
+ * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
+ * @package PHPMailer
+ * @author Chris Ryan <[email protected]>
+ * @author Marcus Bointon <[email protected]>
+ */
+class SMTP
+{
+    /**
+     * The PHPMailer SMTP version number.
+     * @type string
+     */
+    const VERSION = '5.2.8';
+
+    /**
+     * SMTP line break constant.
+     * @type string
+     */
+    const CRLF = "\r\n";
+
+    /**
+     * The SMTP port to use if one is not specified.
+     * @type integer
+     */
+    const DEFAULT_SMTP_PORT = 25;
+
+    /**
+     * The maximum line length allowed by RFC 2822 section 2.1.1
+     * @type integer
+     */
+    const MAX_LINE_LENGTH = 998;
+
+    /**
+     * The PHPMailer SMTP Version number.
+     * @type string
+     * @deprecated Use the constant instead
+     * @see SMTP::VERSION
+     */
+    public $Version = '5.2.8';
+
+    /**
+     * SMTP server port number.
+     * @type integer
+     * @deprecated This is only ever used as a default value, so use the constant instead
+     * @see SMTP::DEFAULT_SMTP_PORT
+     */
+    public $SMTP_PORT = 25;
+
+    /**
+     * SMTP reply line ending.
+     * @type string
+     * @deprecated Use the constant instead
+     * @see SMTP::CRLF
+     */
+    public $CRLF = "\r\n";
+
+    /**
+     * Debug output level.
+     * Options:
+     * * `0` No output
+     * * `1` Commands
+     * * `2` Data and commands
+     * * `3` As 2 plus connection status
+     * * `4` Low-level data output
+     * @type integer
+     */
+    public $do_debug = 0;
+
+    /**
+     * How to handle debug output.
+     * Options:
+     * * `echo` Output plain-text as-is, appropriate for CLI
+     * * `html` Output escaped, line breaks converted to <br>, appropriate for browser output
+     * * `error_log` Output to error log as configured in php.ini
+     * @type string
+     */
+    public $Debugoutput = 'echo';
+
+    /**
+     * Whether to use VERP.
+     * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
+     * @link http://www.postfix.org/VERP_README.html Info on VERP
+     * @type boolean
+     */
+    public $do_verp = false;
+
+    /**
+     * The timeout value for connection, in seconds.
+     * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+     * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
+     * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
+     * @type integer
+     */
+    public $Timeout = 300;
+
+    /**
+     * The SMTP timelimit value for reads, in seconds.
+     * @type integer
+     */
+    public $Timelimit = 30;
+
+    /**
+     * The socket for the server connection.
+     * @type resource
+     */
+    protected $smtp_conn;
+
+    /**
+     * Error message, if any, for the last call.
+     * @type array
+     */
+    protected $error = array();
+
+    /**
+     * The reply the server sent to us for HELO.
+     * If null, no HELO string has yet been received.
+     * @type string|null
+     */
+    protected $helo_rply = null;
+
+    /**
+     * The most recent reply received from the server.
+     * @type string
+     */
+    protected $last_reply = '';
+
+    /**
+     * Output debugging info via a user-selected method.
+     * @param string $str Debug string to output
+     * @return void
+     */
+    protected function edebug($str)
+    {
+        switch ($this->Debugoutput) {
+            case 'error_log':
+                //Don't output, just log
+                error_log($str);
+                break;
+            case 'html':
+                //Cleans up output a bit for a better looking, HTML-safe output
+                echo htmlentities(
+                    preg_replace('/[\r\n]+/', '', $str),
+                    ENT_QUOTES,
+                    'UTF-8'
+                )
+                . "<br>\n";
+                break;
+            case 'echo':
+            default:
+                echo gmdate('Y-m-d H:i:s')."\t".trim($str)."\n";
+        }
+    }
+
+    /**
+     * Connect to an SMTP server.
+     * @param string $host SMTP server IP or host name
+     * @param integer $port The port number to connect to
+     * @param integer $timeout How long to wait for the connection to open
+     * @param array $options An array of options for stream_context_create()
+     * @access public
+     * @return boolean
+     */
+    public function connect($host, $port = null, $timeout = 30, $options = array())
+    {
+        static $streamok;
+        //This is enabled by default since 5.0.0 but some providers disable it
+        //Check this once and cache the result
+        if (is_null($streamok)) {
+            $streamok = function_exists('stream_socket_client');
+        }
+        // Clear errors to avoid confusion
+        $this->error = array();
+        // Make sure we are __not__ connected
+        if ($this->connected()) {
+            // Already connected, generate error
+            $this->error = array('error' => 'Already connected to a server');
+            return false;
+        }
+        if (empty($port)) {
+            $port = self::DEFAULT_SMTP_PORT;
+        }
+        // Connect to the SMTP server
+        if ($this->do_debug >= 3) {
+            $this->edebug("Connection: opening to $host:$port, t=$timeout, opt=".var_export($options, true));
+        }
+        $errno = 0;
+        $errstr = '';
+        if ($streamok) {
+            $socket_context = stream_context_create($options);
+            //Suppress errors; connection failures are handled at a higher level
+            $this->smtp_conn = @stream_socket_client(
+                $host . ":" . $port,
+                $errno,
+                $errstr,
+                $timeout,
+                STREAM_CLIENT_CONNECT,
+                $socket_context
+            );
+        } else {
+            //Fall back to fsockopen which should work in more places, but is missing some features
+            if ($this->do_debug >= 3) {
+                $this->edebug("Connection: stream_socket_client not available, falling back to fsockopen");
+            }
+            $this->smtp_conn = fsockopen(
+                $host,
+                $port,
+                $errno,
+                $errstr,
+                $timeout
+            );
+        }
+        // Verify we connected properly
+        if (!is_resource($this->smtp_conn)) {
+            $this->error = array(
+                'error' => 'Failed to connect to server',
+                'errno' => $errno,
+                'errstr' => $errstr
+            );
+            if ($this->do_debug >= 1) {
+                $this->edebug(
+                    'SMTP ERROR: ' . $this->error['error']
+                    . ": $errstr ($errno)"
+                );
+            }
+            return false;
+        }
+        if ($this->do_debug >= 3) {
+            $this->edebug('Connection: opened');
+        }
+        // SMTP server can take longer to respond, give longer timeout for first read
+        // Windows does not have support for this timeout function
+        if (substr(PHP_OS, 0, 3) != 'WIN') {
+            $max = ini_get('max_execution_time');
+            if ($max != 0 && $timeout > $max) { // Don't bother if unlimited
+                @set_time_limit($timeout);
+            }
+            stream_set_timeout($this->smtp_conn, $timeout, 0);
+        }
+        // Get any announcement
+        $announce = $this->get_lines();
+        if ($this->do_debug >= 2) {
+            $this->edebug('SERVER -> CLIENT: ' . $announce);
+        }
+        return true;
+    }
+
+    /**
+     * Initiate a TLS (encrypted) session.
+     * @access public
+     * @return boolean
+     */
+    public function startTLS()
+    {
+        if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
+            return false;
+        }
+        // Begin encrypted connection
+        if (!stream_socket_enable_crypto(
+            $this->smtp_conn,
+            true,
+            STREAM_CRYPTO_METHOD_TLS_CLIENT
+        )) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Perform SMTP authentication.
+     * Must be run after hello().
+     * @see hello()
+     * @param string $username    The user name
+     * @param string $password    The password
+     * @param string $authtype    The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5)
+     * @param string $realm       The auth realm for NTLM
+     * @param string $workstation The auth workstation for NTLM
+     * @access public
+     * @return boolean True if successfully authenticated.
+     */
+    public function authenticate(
+        $username,
+        $password,
+        $authtype = 'LOGIN',
+        $realm = '',
+        $workstation = ''
+    ) {
+        if (empty($authtype)) {
+            $authtype = 'LOGIN';
+        }
+        switch ($authtype) {
+            case 'PLAIN':
+                // Start authentication
+                if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
+                    return false;
+                }
+                // Send encoded username and password
+                if (!$this->sendCommand(
+                    'User & Password',
+                    base64_encode("\0" . $username . "\0" . $password),
+                    235
+                )
+                ) {
+                    return false;
+                }
+                break;
+            case 'LOGIN':
+                // Start authentication
+                if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
+                    return false;
+                }
+                if (!$this->sendCommand("Username", base64_encode($username), 334)) {
+                    return false;
+                }
+                if (!$this->sendCommand("Password", base64_encode($password), 235)) {
+                    return false;
+                }
+                break;
+            case 'NTLM':
+                /*
+                 * ntlm_sasl_client.php
+                 * Bundled with Permission
+                 *
+                 * How to telnet in windows:
+                 * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
+                 * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
+                 */
+                require_once 'extras/ntlm_sasl_client.php';
+                $temp = new stdClass();
+                $ntlm_client = new ntlm_sasl_client_class;
+                //Check that functions are available
+                if (!$ntlm_client->Initialize($temp)) {
+                    $this->error = array('error' => $temp->error);
+                    if ($this->do_debug >= 1) {
+                        $this->edebug(
+                            'You need to enable some modules in your php.ini file: '
+                            . $this->error['error']
+                        );
+                    }
+                    return false;
+                }
+                //msg1
+                $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
+
+                if (!$this->sendCommand(
+                    'AUTH NTLM',
+                    'AUTH NTLM ' . base64_encode($msg1),
+                    334
+                )
+                ) {
+                    return false;
+                }
+                //Though 0 based, there is a white space after the 3 digit number
+                //msg2
+                $challenge = substr($this->last_reply, 3);
+                $challenge = base64_decode($challenge);
+                $ntlm_res = $ntlm_client->NTLMResponse(
+                    substr($challenge, 24, 8),
+                    $password
+                );
+                //msg3
+                $msg3 = $ntlm_client->TypeMsg3(
+                    $ntlm_res,
+                    $username,
+                    $realm,
+                    $workstation
+                );
+                // send encoded username
+                return $this->sendCommand('Username', base64_encode($msg3), 235);
+            case 'CRAM-MD5':
+                // Start authentication
+                if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
+                    return false;
+                }
+                // Get the challenge
+                $challenge = base64_decode(substr($this->last_reply, 4));
+
+                // Build the response
+                $response = $username . ' ' . $this->hmac($challenge, $password);
+
+                // send encoded credentials
+                return $this->sendCommand('Username', base64_encode($response), 235);
+        }
+        return true;
+    }
+
+    /**
+     * Calculate an MD5 HMAC hash.
+     * Works like hash_hmac('md5', $data, $key)
+     * in case that function is not available
+     * @param string $data The data to hash
+     * @param string $key  The key to hash with
+     * @access protected
+     * @return string
+     */
+    protected function hmac($data, $key)
+    {
+        if (function_exists('hash_hmac')) {
+            return hash_hmac('md5', $data, $key);
+        }
+
+        // The following borrowed from
+        // http://php.net/manual/en/function.mhash.php#27225
+
+        // RFC 2104 HMAC implementation for php.
+        // Creates an md5 HMAC.
+        // Eliminates the need to install mhash to compute a HMAC
+        // Hacked by Lance Rushing
+
+        $bytelen = 64; // byte length for md5
+        if (strlen($key) > $bytelen) {
+            $key = pack('H*', md5($key));
+        }
+        $key = str_pad($key, $bytelen, chr(0x00));
+        $ipad = str_pad('', $bytelen, chr(0x36));
+        $opad = str_pad('', $bytelen, chr(0x5c));
+        $k_ipad = $key ^ $ipad;
+        $k_opad = $key ^ $opad;
+
+        return md5($k_opad . pack('H*', md5($k_ipad . $data)));
+    }
+
+    /**
+     * Check connection state.
+     * @access public
+     * @return boolean True if connected.
+     */
+    public function connected()
+    {
+        if (is_resource($this->smtp_conn)) {
+            $sock_status = stream_get_meta_data($this->smtp_conn);
+            if ($sock_status['eof']) {
+                // the socket is valid but we are not connected
+                if ($this->do_debug >= 1) {
+                    $this->edebug(
+                        'SMTP NOTICE: EOF caught while checking if connected'
+                    );
+                }
+                $this->close();
+                return false;
+            }
+            return true; // everything looks good
+        }
+        return false;
+    }
+
+    /**
+     * Close the socket and clean up the state of the class.
+     * Don't use this function without first trying to use QUIT.
+     * @see quit()
+     * @access public
+     * @return void
+     */
+    public function close()
+    {
+        $this->error = array();
+        $this->helo_rply = null;
+        if (is_resource($this->smtp_conn)) {
+            // close the connection and cleanup
+            fclose($this->smtp_conn);
+            if ($this->do_debug >= 3) {
+                $this->edebug('Connection: closed');
+            }
+        }
+    }
+
+    /**
+     * Send an SMTP DATA command.
+     * Issues a data command and sends the msg_data to the server,
+     * finializing the mail transaction. $msg_data is the message
+     * that is to be send with the headers. Each header needs to be
+     * on a single line followed by a <CRLF> with the message headers
+     * and the message body being separated by and additional <CRLF>.
+     * Implements rfc 821: DATA <CRLF>
+     * @param string $msg_data Message data to send
+     * @access public
+     * @return boolean
+     */
+    public function data($msg_data)
+    {
+        if (!$this->sendCommand('DATA', 'DATA', 354)) {
+            return false;
+        }
+        /* The server is ready to accept data!
+         * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
+         * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
+         * smaller lines to fit within the limit.
+         * We will also look for lines that start with a '.' and prepend an additional '.'.
+         * NOTE: this does not count towards line-length limit.
+         */
+
+        // Normalize line breaks before exploding
+        $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
+
+        /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
+         * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
+         * process all lines before a blank line as headers.
+         */
+
+        $field = substr($lines[0], 0, strpos($lines[0], ':'));
+        $in_headers = false;
+        if (!empty($field) && strpos($field, ' ') === false) {
+            $in_headers = true;
+        }
+
+        foreach ($lines as $line) {
+            $lines_out = array();
+            if ($in_headers and $line == '') {
+                $in_headers = false;
+            }
+            // ok we need to break this line up into several smaller lines
+            //This is a small micro-optimisation: isset($str[$len]) is equivalent to (strlen($str) > $len)
+            while (isset($line[self::MAX_LINE_LENGTH])) {
+                //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
+                //so as to avoid breaking in the middle of a word
+                $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
+                if (!$pos) { //Deliberately matches both false and 0
+                    //No nice break found, add a hard break
+                    $pos = self::MAX_LINE_LENGTH - 1;
+                    $lines_out[] = substr($line, 0, $pos);
+                    $line = substr($line, $pos);
+                } else {
+                    //Break at the found point
+                    $lines_out[] = substr($line, 0, $pos);
+                    //Move along by the amount we dealt with
+                    $line = substr($line, $pos + 1);
+                }
+                /* If processing headers add a LWSP-char to the front of new line
+                 * RFC822 section 3.1.1
+                 */
+                if ($in_headers) {
+                    $line = "\t" . $line;
+                }
+            }
+            $lines_out[] = $line;
+
+            // Send the lines to the server
+            foreach ($lines_out as $line_out) {
+                //RFC2821 section 4.5.2
+                if (!empty($line_out) and $line_out[0] == '.') {
+                    $line_out = '.' . $line_out;
+                }
+                $this->client_send($line_out . self::CRLF);
+            }
+        }
+
+        // Message data has been sent, complete the command
+        return $this->sendCommand('DATA END', '.', 250);
+    }
+
+    /**
+     * Send an SMTP HELO or EHLO command.
+     * Used to identify the sending server to the receiving server.
+     * This makes sure that client and server are in a known state.
+     * Implements RFC 821: HELO <SP> <domain> <CRLF>
+     * and RFC 2821 EHLO.
+     * @param string $host The host name or IP to connect to
+     * @access public
+     * @return boolean
+     */
+    public function hello($host = '')
+    {
+        // Try extended hello first (RFC 2821)
+        return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
+    }
+
+    /**
+     * Send an SMTP HELO or EHLO command.
+     * Low-level implementation used by hello()
+     * @see hello()
+     * @param string $hello The HELO string
+     * @param string $host The hostname to say we are
+     * @access protected
+     * @return boolean
+     */
+    protected function sendHello($hello, $host)
+    {
+        $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
+        $this->helo_rply = $this->last_reply;
+        return $noerror;
+    }
+
+    /**
+     * Send an SMTP MAIL command.
+     * Starts a mail transaction from the email address specified in
+     * $from. Returns true if successful or false otherwise. If True
+     * the mail transaction is started and then one or more recipient
+     * commands may be called followed by a data command.
+     * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF>
+     * @param string $from Source address of this message
+     * @access public
+     * @return boolean
+     */
+    public function mail($from)
+    {
+        $useVerp = ($this->do_verp ? ' XVERP' : '');
+        return $this->sendCommand(
+            'MAIL FROM',
+            'MAIL FROM:<' . $from . '>' . $useVerp,
+            250
+        );
+    }
+
+    /**
+     * Send an SMTP QUIT command.
+     * Closes the socket if there is no error or the $close_on_error argument is true.
+     * Implements from rfc 821: QUIT <CRLF>
+     * @param boolean $close_on_error Should the connection close if an error occurs?
+     * @access public
+     * @return boolean
+     */
+    public function quit($close_on_error = true)
+    {
+        $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
+        $err = $this->error; //Save any error
+        if ($noerror or $close_on_error) {
+            $this->close();
+            $this->error = $err; //Restore any error from the quit command
+        }
+        return $noerror;
+    }
+
+    /**
+     * Send an SMTP RCPT command.
+     * Sets the TO argument to $toaddr.
+     * Returns true if the recipient was accepted false if it was rejected.
+     * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF>
+     * @param string $toaddr The address the message is being sent to
+     * @access public
+     * @return boolean
+     */
+    public function recipient($toaddr)
+    {
+        return $this->sendCommand(
+            'RCPT TO',
+            'RCPT TO:<' . $toaddr . '>',
+            array(250, 251)
+        );
+    }
+
+    /**
+     * Send an SMTP RSET command.
+     * Abort any transaction that is currently in progress.
+     * Implements rfc 821: RSET <CRLF>
+     * @access public
+     * @return boolean True on success.
+     */
+    public function reset()
+    {
+        return $this->sendCommand('RSET', 'RSET', 250);
+    }
+
+    /**
+     * Send a command to an SMTP server and check its return code.
+     * @param string $command       The command name - not sent to the server
+     * @param string $commandstring The actual command to send
+     * @param integer|array $expect     One or more expected integer success codes
+     * @access protected
+     * @return boolean True on success.
+     */
+    protected function sendCommand($command, $commandstring, $expect)
+    {
+        if (!$this->connected()) {
+            $this->error = array(
+                'error' => "Called $command without being connected"
+            );
+            return false;
+        }
+        $this->client_send($commandstring . self::CRLF);
+
+        $reply = $this->get_lines();
+        $code = substr($reply, 0, 3);
+
+        if ($this->do_debug >= 2) {
+            $this->edebug('SERVER -> CLIENT: ' . $reply);
+        }
+
+        if (!in_array($code, (array)$expect)) {
+            $this->last_reply = null;
+            $this->error = array(
+                'error' => "$command command failed",
+                'smtp_code' => $code,
+                'detail' => substr($reply, 4)
+            );
+            if ($this->do_debug >= 1) {
+                $this->edebug(
+                    'SMTP ERROR: ' . $this->error['error'] . ': ' . $reply
+                );
+            }
+            return false;
+        }
+
+        $this->last_reply = $reply;
+        $this->error = array();
+        return true;
+    }
+
+    /**
+     * Send an SMTP SAML command.
+     * Starts a mail transaction from the email address specified in $from.
+     * Returns true if successful or false otherwise. If True
+     * the mail transaction is started and then one or more recipient
+     * commands may be called followed by a data command. This command
+     * will send the message to the users terminal if they are logged
+     * in and send them an email.
+     * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF>
+     * @param string $from The address the message is from
+     * @access public
+     * @return boolean
+     */
+    public function sendAndMail($from)
+    {
+        return $this->sendCommand('SAML', "SAML FROM:$from", 250);
+    }
+
+    /**
+     * Send an SMTP VRFY command.
+     * @param string $name The name to verify
+     * @access public
+     * @return boolean
+     */
+    public function verify($name)
+    {
+        return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
+    }
+
+    /**
+     * Send an SMTP NOOP command.
+     * Used to keep keep-alives alive, doesn't actually do anything
+     * @access public
+     * @return boolean
+     */
+    public function noop()
+    {
+        return $this->sendCommand('NOOP', 'NOOP', 250);
+    }
+
+    /**
+     * Send an SMTP TURN command.
+     * This is an optional command for SMTP that this class does not support.
+     * This method is here to make the RFC821 Definition complete for this class
+     * and _may_ be implemented in future
+     * Implements from rfc 821: TURN <CRLF>
+     * @access public
+     * @return boolean
+     */
+    public function turn()
+    {
+        $this->error = array(
+            'error' => 'The SMTP TURN command is not implemented'
+        );
+        if ($this->do_debug >= 1) {
+            $this->edebug('SMTP NOTICE: ' . $this->error['error']);
+        }
+        return false;
+    }
+
+    /**
+     * Send raw data to the server.
+     * @param string $data The data to send
+     * @access public
+     * @return integer|boolean The number of bytes sent to the server or false on error
+     */
+    public function client_send($data)
+    {
+        if ($this->do_debug >= 1) {
+            $this->edebug("CLIENT -> SERVER: $data");
+        }
+        return fwrite($this->smtp_conn, $data);
+    }
+
+    /**
+     * Get the latest error.
+     * @access public
+     * @return array
+     */
+    public function getError()
+    {
+        return $this->error;
+    }
+
+    /**
+     * Get the last reply from the server.
+     * @access public
+     * @return string
+     */
+    public function getLastReply()
+    {
+        return $this->last_reply;
+    }
+
+    /**
+     * Read the SMTP server's response.
+     * Either before eof or socket timeout occurs on the operation.
+     * With SMTP we can tell if we have more lines to read if the
+     * 4th character is '-' symbol. If it is a space then we don't
+     * need to read anything else.
+     * @access protected
+     * @return string
+     */
+    protected function get_lines()
+    {
+        // If the connection is bad, give up straight away
+        if (!is_resource($this->smtp_conn)) {
+            return '';
+        }
+        $data = '';
+        $endtime = 0;
+        stream_set_timeout($this->smtp_conn, $this->Timeout);
+        if ($this->Timelimit > 0) {
+            $endtime = time() + $this->Timelimit;
+        }
+        while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
+            $str = @fgets($this->smtp_conn, 515);
+            if ($this->do_debug >= 4) {
+                $this->edebug("SMTP -> get_lines(): \$data was \"$data\"");
+                $this->edebug("SMTP -> get_lines(): \$str is \"$str\"");
+            }
+            $data .= $str;
+            if ($this->do_debug >= 4) {
+                $this->edebug("SMTP -> get_lines(): \$data is \"$data\"");
+            }
+            // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
+            if ((isset($str[3]) and $str[3] == ' ')) {
+                break;
+            }
+            // Timed-out? Log and break
+            $info = stream_get_meta_data($this->smtp_conn);
+            if ($info['timed_out']) {
+                if ($this->do_debug >= 4) {
+                    $this->edebug(
+                        'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)'
+                    );
+                }
+                break;
+            }
+            // Now check if reads took too long
+            if ($endtime and time() > $endtime) {
+                if ($this->do_debug >= 4) {
+                    $this->edebug(
+                        'SMTP -> get_lines(): timelimit reached ('.
+                        $this->Timelimit . ' sec)'
+                    );
+                }
+                break;
+            }
+        }
+        return $data;
+    }
+
+    /**
+     * Enable or disable VERP address generation.
+     * @param boolean $enabled
+     */
+    public function setVerp($enabled = false)
+    {
+        $this->do_verp = $enabled;
+    }
+
+    /**
+     * Get VERP address generation mode.
+     * @return boolean
+     */
+    public function getVerp()
+    {
+        return $this->do_verp;
+    }
+
+    /**
+     * Set debug output method.
+     * @param string $method The function/method to use for debugging output.
+     */
+    public function setDebugOutput($method = 'echo')
+    {
+        $this->Debugoutput = $method;
+    }
+
+    /**
+     * Get debug output method.
+     * @return string
+     */
+    public function getDebugOutput()
+    {
+        return $this->Debugoutput;
+    }
+
+    /**
+     * Set debug output level.
+     * @param integer $level
+     */
+    public function setDebugLevel($level = 0)
+    {
+        $this->do_debug = $level;
+    }
+
+    /**
+     * Get debug output level.
+     * @return integer
+     */
+    public function getDebugLevel()
+    {
+        return $this->do_debug;
+    }
+
+    /**
+     * Set SMTP timeout.
+     * @param integer $timeout
+     */
+    public function setTimeout($timeout = 0)
+    {
+        $this->Timeout = $timeout;
+    }
+
+    /**
+     * Get SMTP timeout.
+     * @return integer
+     */
+    public function getTimeout()
+    {
+        return $this->Timeout;
+    }
+}

+ 148 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter.php

@@ -0,0 +1,148 @@
+<?php namespace Mail;
+/**
+ * CCMail Transporter
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Transporter 
+{
+	/**
+	 * Instance holder
+	 *
+	 * @var array
+	 */
+	protected static $_instances = array();
+	
+	/**
+	 * Default transporter instance name
+	 *
+	 * @var string
+	 */
+	private static $_default = 'main';
+	
+	/**
+	 * Get a transporter instance or create one
+	 *
+	 * @param string			$name
+	 * @param array 			$conf	You can pass optionally a configuration directly. This will overwrite.
+	 * @return Auth_Handler
+	 */
+	public static function create( $name = null, $conf = null ) 
+	{
+		if ( is_null( $name ) ) 
+		{
+			$name = static::$_default;
+		}
+		
+		if ( !is_null( $conf ) && is_array( $conf ) )
+		{
+			return static::$_instances[$name] = new static( $name, $conf );
+		}
+		
+		if ( !isset( static::$_instances[$name] ) )
+		{
+			static::$_instances[$name] = new static( $name );
+		}
+		
+		return static::$_instances[$name];
+	}
+	
+	/**
+	 * Kill an instance to force the transporter to redo the construction
+	 *
+	 * @return void
+	 */
+	public static function kill_instance( $name )
+	{
+		if ( array_key_exists( $name, static::$_instances ) )
+		{
+			unset( static::$_instances[$name] );
+		}
+	}
+	
+	/**
+	 * The transporter handler name
+	 *
+	 * @var string
+	 */
+	protected $name = null;
+	
+	/**
+	 * The transporter config array
+	 *
+	 * @var string
+	 */
+	protected $config = null;
+	
+	/**
+	 * The transporter driver
+	 *
+	 * @var Transporter_Driver
+	 */
+	protected $driver = null;
+	
+	/**
+	 * Transporter instance constructor
+	 *
+	 * @param string 		$name
+	 * @param array 			$config
+	 * @return void
+	 */
+	public function __construct( $name, $config = null ) 
+	{	
+		if ( is_null( $config ) )
+		{
+			$config = \CCConfig::create( 'mail' )->get( 'transporter.'.$name );
+			
+			// check for an alias. If you set a string 
+			// in your config file we use the config 
+			// with the passed key.
+			if ( is_string( $config ) ) 
+			{
+				$config = \CCConfig::create( 'mail' )->get( 'transporter.'.$config );
+			}
+		}
+		
+		if ( !is_array( $config ) )
+		{
+			throw new Exception( "Auth\\Handler::create - Invalid auth handler (".$name.")." );
+		}
+		
+		// also don't forget to set the name manager name becaue we need him later.
+		$this->name = $name;
+		
+		// assign defaults and create the configuration object
+		$this->config = \CCDataObject::assign( \CCArr::merge( array(
+			
+			// What driver should be used
+			'driver' => 'sendmail',
+		), $config ));
+		
+		
+		// load the driver
+		$driver_class = __NAMESPACE__.'\\Transporter_'.ucfirst( $this->config->driver );
+		
+		if ( !class_exists( $driver_class ) )
+		{
+			throw new Exception( "Invalid mail driver '".$this->config->driver."'" );
+		}
+		
+		$this->driver = new $driver_class( $this->config );
+	}
+	
+	/**
+	 * Forward the mail to the driver
+	 *
+	 * @param CCMail 		$mail
+	 * @return void
+	 */
+	public function send( CCMail $mail )
+	{
+		$this->driver->send( $mail );
+	}
+}

+ 33 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/Array.php

@@ -0,0 +1,33 @@
+<?php namespace Mail;
+/**
+ * Array Transporter
+ * This transporter is just for testing purposes
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Transporter_Array implements Transporter_Interface
+{
+	/**
+	 * Because this is just an array driver we
+	 * simply store all send email in this array.
+	 */
+	public static $store = array();
+	
+	/**
+	 * Send the mail
+	 *
+	 * @param CCMail 		$mail	The mail object.
+	 * @return void
+	 *
+	 * @throws Mail\Exception
+	 */ 
+	public function send( CCMail $mail )
+	{
+		static::$store[] = $mail->export_data();
+	}
+}

+ 33 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/File.php

@@ -0,0 +1,33 @@
+<?php namespace Mail;
+/**
+ * File Transporter
+ * The file transporter helps debugging in development
+ *
+ * The will not be sended but stored as file in the storage.
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Transporter_File implements Transporter_Interface
+{	
+	/**
+	 * Send the mail
+	 *
+	 * @param CCMail 		$mail	The mail object.
+	 * @return void
+	 *
+	 * @throws Mail\Exception
+	 */ 
+	public function send( CCMail $mail )
+	{	
+		$data = $mail->export_data();
+		
+		$filename = 'mails/'.date( 'Y-m' ).'/'.date( 'd' ).'/'.date("H-i-s").'-'.\CCStr::clean_url( $data['subject'] ).'.log';
+		
+		\CCFile::append( \CCStorage::path( $filename ), \CCJson::encode( $data, true ) );
+	}
+}

+ 23 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/Interface.php

@@ -0,0 +1,23 @@
+<?php namespace Mail;
+/**
+ * Transporter Interface
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+interface Transporter_Interface
+{
+	/**
+	 * Send the mail
+	 *
+	 * @param CCMail 		$mail	The mail object.
+	 * @return void
+	 *
+	 * @throws Mail\Exception
+	 */ 
+	public function send( CCMail $mail );
+}

+ 108 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/PHPMailer.php

@@ -0,0 +1,108 @@
+<?php namespace Mail;
+/**
+ * PHPMailer Transporter
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Transporter_PHPMailer implements Transporter_Interface
+{
+	/**
+	 * The Driver configuration
+	 *
+	 * @var CCConfig
+	 */
+	public $config; 
+	
+	/**
+	 * Recive and store the transporter configuration
+	 */
+	public function __construct( $config )
+	{
+		$this->config = $config;
+	}
+	
+	/**
+	 * Send the mail
+	 *
+	 * @param CCMail 		$mail	The mail object.
+	 * @return void
+	 *
+	 * @throws Mail\Exception
+	 */ 
+	public function send( CCMail $mail )
+	{
+		// create new phpmailer instance
+		$driver = new PHPMailer\PHPMailer();
+		
+		// set the charset
+		$driver->CharSet = 'utf-8'; 
+		
+		// set the xmailer tag
+		$driver->XMailer = "ClanCatsFramework 2.0 Mail / PHPMailer ".$driver->Version;
+		
+		// get the mail data as array
+		$mail_data = $mail->export_data();
+		
+		// from address and name
+		list( $from_email, $from_name ) = $mail_data['from'];
+		
+		$driver->From = $from_email;
+		if ( !is_null( $from_name ) )
+		{
+			$driver->FromName = $from_name;
+		}
+		
+		// set the recipients
+		foreach( $mail_data['to'] as $email => $name )
+		{
+			$driver->AddAddress( $email, $name );
+		}
+		
+		// set bcc
+		foreach( $mail_data['bcc'] as $email => $name )
+		{
+			$driver->addBCC( $email, $name );
+		}
+		
+		// set cc
+		foreach( $mail_data['cc'] as $email => $name )
+		{
+			$driver->addCC( $email, $name );
+		}
+		
+		// add attachments
+		foreach( $mail_data['attachments'] as $path => $name )
+		{
+			$driver->addAttachment( $path, $name );
+		}
+		
+		// set the subject
+		$driver->Subject = $mail_data['subject'];
+		
+		// set the message
+		$driver->Body = $mail_data['message'];
+		$driver->AltBody = $mail_data['plaintext'];
+		$driver->IsHTML( !$mail_data['is_plaintext'] );
+		
+		// setup the driver
+		$this->setup_driver( $driver );
+		
+		if( !$driver->Send() ) 
+		{
+			throw new Exception( "Mail failure: ". $driver->ErrorInfo );
+		}
+	}
+	
+	/**
+	 * Set the driver settings ( smtp / sendmail )
+	 *
+	 * @param PHPMailer 			$driver
+	 * @return void
+	 */
+	protected function setup_driver( &$driver ) {}
+}

+ 27 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/Sendmail.php

@@ -0,0 +1,27 @@
+<?php namespace Mail;
+/**
+ * Sendmail Transporter
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Transporter_Sendmail extends Transporter_PHPMailer
+{
+	/**
+	 * Set the driver settings ( smtp / sendmail )
+	 *
+	 * @param PHPMailer 			$driver
+	 * @return void
+	 */
+	protected function setup_driver( &$driver )
+	{
+		if ( !is_null( $this->config->path ) )
+		{
+			$driver->Sendmail = $this->config->path;
+		}
+	}
+}

+ 36 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Mail/Transporter/Smtp.php

@@ -0,0 +1,36 @@
+<?php namespace Mail;
+/**
+ * Sendmail Transporter
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Transporter_Smtp extends Transporter_PHPMailer
+{
+	/**
+	 * Set the driver settings ( smtp / sendmail )
+	 *
+	 * @param PHPMailer 			$driver
+	 * @return void
+	 */
+	protected function setup_driver( &$driver )
+	{
+		$driver->IsSMTP(); 
+		$driver->Host = $this->config->host;
+		
+		// smtp auth?
+		if ( $this->config->auth === true ) 
+		{
+			$driver->SMTPAuth = true;
+			$driver->Username = $this->config->user;
+			$driver->Password = $this->config->pass;
+		}
+		
+		$driver->SMTPSecure = $this->config->encryption;
+		$driver->Port = $this->config->port;
+	}
+}

+ 124 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/CCSession.php

@@ -0,0 +1,124 @@
+<?php namespace Session;
+/**
+ * Session handler
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCSession 
+{
+	/**
+	 * Get an instance of a session manager
+	 *
+	 * @param string 			$manager
+	 * @return Session\Manager
+	 */
+	public static function manager( $manager = null )
+	{
+		return Manager::create( $manager );
+	}
+	
+	/**
+	 * Get the fingerprint form an session
+	 *
+	 * @param string 			$manager
+	 * @return string
+	 */
+	public static function fingerprint( $name = null )
+	{
+		return Manager::create( $name )->fingerprint();
+	}
+	
+	/**
+	 * Check if the given fingerprint or the default fingerprint
+	 * parameter matches the curent session fingerprint.
+	 *
+	 * @param string 			$manager
+	 * @return string
+	 */
+	public static function valid_fingerprint( $fingerprint = null, $name = null )
+	{
+		return Manager::create( $name )->valid_fingerprint( $fingerprint );
+	}
+	
+	/**
+	 * Get a value from the session
+	 *
+	 * @param string				$key
+	 * @param string 			$default
+	 * @param string				$manager
+	 * @return Session\Manager
+	 */
+	public static function get( $key, $default, $manager = null )
+	{
+		return Manager::create( $manager )->get( $key, $default );
+	}
+	
+	/**
+	 * Get a value from the session and remove it afterwards
+	 *
+	 * @param string				$key
+	 * @param string 			$default
+	 * @param string				$manager
+	 * @return Session\Manager
+	 */
+	public static function once( $key, $default, $manager = null )
+	{
+		return Manager::create( $manager )->once( $key, $default );
+	}
+	
+	/**
+	 * Set a value on the session
+	 *
+	 * @param string				$key
+	 * @param string 			$value
+	 * @param string				$manager
+	 * @return Session\Manager
+	 */
+	public static function set( $key, $value, $manager = null )
+	{
+		return Manager::create( $manager )->set( $key, $value );
+	}
+	
+	/**
+	 * Similar to add but forces the element to be an array
+	 * and appends an item.
+	 *
+	 * @param string				$key
+	 * @param string 			$value
+	 * @param string				$manager
+	 * @return Session\Manager
+	 */
+	public static function add( $key, $value, $manager = null )
+	{
+		return Manager::create( $manager )->add( $key, $value );
+	}
+	
+	/**
+	 * Has a value on the session
+	 *
+	 * @param string				$key
+	 * @param string				$manager
+	 * @return Session\Manager
+	 */
+	public static function has( $key, $manager = null )
+	{
+		return Manager::create( $manager )->has( $key );
+	}
+	
+	/**
+	 * Delete a value on the session
+	 *
+	 * @param string				$key
+	 * @param string				$manager
+	 * @return Session\Manager
+	 */
+	public static function delete( $key, $manager = null )
+	{
+		return Manager::create( $manager )->delete( $key );
+	}
+}

+ 12 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Exception.php

@@ -0,0 +1,12 @@
+<?php namespace Session;
+/**
+ * Session Exceptions
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Exception extends \Exception {}

+ 358 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager.php

@@ -0,0 +1,358 @@
+<?php namespace Session;
+/**
+ * Session Manager
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Manager extends \CCDataObject
+{
+	/**
+	 * Instance holder
+	 *
+	 * @var array
+	 */
+	protected static $_instances = array();
+	
+	/**
+	 * Default session instance name
+	 *
+	 * @var string
+	 */
+	private static $_default = 'main';
+	
+	/**
+	 * Get a session instance manager
+	 *
+	 * @param string			$name
+	 * @param array 			$conf	You can pass optionally a configuration directly. This will overwrite.
+	 * @return Session\Manager
+	 */
+	public static function create( $name = null, $conf = null ) 
+	{
+		if ( is_null( $name ) ) 
+		{
+			$name = static::$_default;
+		}
+		
+		if ( !is_null( $conf ) && is_array( $conf ) )
+		{
+			return static::$_instances[$name] = new static( $name, $conf );
+		}
+		
+		if ( !isset( static::$_instances[$name] ) )
+		{
+			static::$_instances[$name] = new static( $name );
+		}
+		
+		return static::$_instances[$name];
+	}
+	
+	/**
+	 * Some default values for our session
+	 *
+	 * @return array
+	 */
+	public static function default_data_provider() 
+	{
+		return array(
+			'last_active'	=> time(),
+			'current_lang'	=> \CCLang::current(),
+			'client_agent'	=> \CCServer::client( 'agent' ),
+			'client_ip'		=> \CCServer::client( 'ip' ),
+			'client_port'	=> \CCServer::client( 'port' ),
+			'client_lang'	=> \CCServer::client( 'language' ),
+		);
+	}
+	
+	/**
+	 * The session manager name
+	 *
+	 * @var string
+	 */
+	protected $_name = null;
+	
+	/**
+	 * The session config array
+	 *
+	 * @var string
+	 */
+	protected $_config = null;
+	
+	/**
+	 * The session driver
+	 *
+	 * @var Manager_Driver
+	 */
+	protected $_driver = null;
+	
+	/**
+	 * Session ID
+	 *
+	 * @var	string
+	 */
+	public $id;
+	
+	/**
+	 * The Fingerprint
+	 *
+	 * @var string
+	 */
+	public $fingerprint;
+	
+	/**
+	 * Session constructor
+	 *
+	 * @param string 		$name
+	 * @param array 			$config
+	 */
+	protected function __construct( $name, $config = null ) 
+	{
+		if ( is_null( $config ) )
+		{
+			$config = \CCConfig::create( 'session' )->get( $name );
+			
+			// check for an alias. If you set a string 
+			// in your config file we use the config 
+			// with the passed key.
+			if ( is_string( $config ) ) 
+			{
+				$config = \CCConfig::create( 'session' )->get( $config );
+			}
+		}
+		
+		if ( empty( $config ) )
+		{
+			throw new Exception( "Session\\Manager::create - Invalid session manager (".$name.")." );
+		}
+		
+		// also don't forget to set the name manager name becaue we need him later.
+		$this->_name = $name;
+		
+		// keep the configuration array
+		$this->_config = $config;
+		
+		// Setup the driver class. We simply use name 
+		// from the confif file and make the first letter 
+		// capital. example: Handler_Mysql, Handler_Sqlite etc.
+		$driver_class = __NAMESPACE__."\\Manager_".ucfirst( $config['driver'] );
+		
+		if ( !class_exists( $driver_class ) )
+		{
+			throw new Exception( "Session\\Manager::create - The driver (".$driver_class.") is invalid." );
+		}
+		
+		$this->set_driver( $driver_class );
+		
+		// try to get the id from cookie
+		$this->id = $this->cookie_session_id();
+		
+		// set the fingerprint
+		$this->fingerprint = sha1( $this->id );
+		
+		// Before reading we might have to kill old sessions using 
+		// the Garbage collector
+		if ( \CCArr::get( 'gc.enabled', $this->_config, true ) )
+		{
+			if ( mt_rand( 1, \CCArr::get( 'gc.factor', $this->_config, 25 ) ) == 1 )
+			{
+				$this->gc();
+			}
+		}
+		
+		// Register a shutdown event to write the session down
+		// This should not happen on shutdown if we using command line
+		if ( !\ClanCats::is_cli() )
+		{
+			\CCEvent::mind( 'CCF.shutdown', array( $this, 'write' ) );
+		}
+		
+		// Now get the inital data from our driver
+		$this->read();
+	}
+	
+	/**
+	 * Get the current driver
+	 *
+	 * @return DB\Handler_Driver
+	 */
+	public function driver()
+	{
+		return $this->_driver;
+	}
+	
+	/**
+	 * Set the current driver
+	 *
+	 * @param string		$driver		The full driver class ( Session\Manager_ExampleDriver )
+	 * @return void
+	 */
+	private function set_driver( $driver )
+	{
+		$this->_driver = new $driver( $this->_name, $this->_config );
+	}
+	
+	/**
+	 * Return the default data for the session
+	 *
+	 * @return array
+	 */
+	protected function default_data()
+	{
+		return call_user_func( \ClanCats::$config->get( 'session.default_data_provider' ) );
+	}
+	
+	/**
+	 * Get the current cookie name
+	 *
+	 * @return string 
+	 */
+	protected function cookie_name()
+	{
+		return $this->_name.\CCArr::get( 'cookie_suffix', $this->_config, '-ccf-session' );
+	}
+	
+	/**
+	 * Get the current session id from the cookie
+	 *
+	 * @return string
+	 */
+	protected function cookie_session_id()
+	{
+		return \CCCookie::get( $this->cookie_name(), false );
+	}
+	
+	/**
+	 * Retrive the current session fingerprint
+	 *
+	 * @return string
+	 */
+	public function fingerprint()
+	{
+		return $this->fingerprint;
+	}
+	
+	/**
+	 * Does the current session fingerprint match a parameter
+	 *
+	 * When no parameter is given we use GET->s as default parameter
+	 *
+	 * @param string 		$fingerprint
+	 * @return string
+	 */
+	public function valid_fingerprint( $fingerprint = null )
+	{
+		if ( is_null( $fingerprint ) )
+		{
+			$fingerprint = \CCIn::get( \ClanCats::$config->get( 'session.default_fingerprint_parameter' ), false );
+		}
+		
+		return $this->fingerprint === $fingerprint;
+	}
+	
+	/**
+	 * Get a value from data and remove it afterwards
+	 *
+	 * @param string 	$key
+	 * @param mixed		$default
+	 * @return mixed
+	 */
+	public function once( $key, $default = null ) 
+	{
+		$value = \CCArr::get( $key, $this->_data, $default );
+		\CCArr::delete( $key, $this->_data );
+		return $value;
+	}
+	
+	/**
+	 * Read data from the session driver. This overwrite's 
+	 * any changes made on runtime.
+	 *
+	 * @return void 
+	 */
+	public function read() 
+	{
+		// Do we already have a session id if not we regenerate
+		// the session and assign the default data.
+		if ( $this->id ) 
+		{
+			if ( !$this->_data = $this->_driver->read( $this->id ) ) 
+			{
+				$this->regenerate();
+				$this->_data = array();
+			}
+	
+			if ( !is_array( $this->_data ) ) 
+			{
+				$this->_data = array();
+			}
+			
+			$this->_data = array_merge( $this->_data, $this->default_data() );
+		} 
+		else 
+		{
+			$this->regenerate();
+			$this->_data = $this->default_data();
+		}
+	}
+	
+	/**
+	 * Write the session to the driver
+	 *
+	 * @return void
+	 */
+	public function write() 
+	{
+		$this->_driver->write( $this->id, $this->_data );
+		
+		// We also have to set the cookie again to keep it alive
+		\CCCookie::set( $this->cookie_name(), $this->id, \CCArr::get( 'lifetime', $this->_config, \CCDate::minutes( 5 ) ) );
+	}
+	
+	/**
+	 * Generate a new session id and checks the dirver for dublicates.
+	 *
+	 * @return string	The new generated session id.
+	 */
+	public function regenerate() 
+	{
+		do 
+		{
+			$id = \CCStr::random( 32 );
+		}
+		while ( $this->_driver->has( $id ) );
+		
+		$this->fingerprint = sha1( $id );
+		return $this->id = $id;
+	}
+	
+	/**
+	 * Destory the session an create a new one
+	 */
+	public function destroy() 
+	{	
+		$this->_data = $this->default_data();
+		return $this->regenerate();
+	}
+		
+	/**
+	 * Garbage collection, delete all outdated sessions
+	 *
+	 * @return void
+	 */
+	public function gc() 
+	{
+		$lifetime = \CCArr::get( 'lifetime', $this->_config, \CCDate::minutes( 5 ) );
+		
+		if ( $lifetime < ( $min_lifetime = \CCArr::get( 'min_lifetime', $this->_config, \CCDate::minutes( 5 ) ) ) )
+		{
+			$lifetime = $min_lifetime;
+		}
+		
+		$this->_driver->gc( $lifetime );
+	}
+}

+ 68 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Array.php

@@ -0,0 +1,68 @@
+<?php namespace Session;
+/**
+ * Session Array test driver
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Manager_Array implements Manager_Interface
+{
+	/**
+	 * the data holder
+	 *
+	 * @var array
+	 */
+	private $data = array();
+	
+	/**
+	 * Read data from the session dirver
+	 *
+	 * @param string		$id		The session id key.
+	 * @return array
+	 */
+	public function read( $id )
+	{
+		if ( array_key_exists( $id, $this->data ) )
+		{
+			return $this->data[$id];
+		}
+	}
+	
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @return bool
+	 */
+	public function has( $id )
+	{
+		return array_key_exists( $id, $this->data );
+	}
+	
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @param array 		$data
+	 * @return bool
+	 */
+	public function write( $id, $data )
+	{
+		$this->data[$id] = $data;
+	}
+	
+	/**
+	 * Delete session that are older than the given time in secounds
+	 *
+	 * @param int		$time
+	 * @return void
+	 */
+	public function gc( $time )
+	{
+		$this->data = array();
+	}
+}

+ 86 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Cookie.php

@@ -0,0 +1,86 @@
+<?php namespace Session;
+/**
+ * Session Cookie Driver
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Manager_Cookie implements Manager_Interface
+{
+	/**
+	 * Cookie suffix string
+	 *
+	 * @var string
+	 */
+	private $cookie_suffix = null;
+	
+	/**
+	 * Salt for crypting the session 
+	 *
+	 * @var string
+	 */
+	private $crypt_salt = null;
+	
+	/**
+	 * Cookie driver constructor
+	 */
+	public function __construct( $name, $conf )
+	{
+		$this->cookie_suffix = \CCArr::get( 'cookie_suffix', $conf, '-session-store' );
+		$this->crypt_salt = \CCArr::get( 'crypt_salt', $conf );
+	}
+	
+	/**
+	 * Read data from the session dirver
+	 *
+	 * @param string		$id		The session id key.
+	 * @return array
+	 */
+	public function read( $id )
+	{
+	    if ( $this->has( $id ) )
+		{
+			return json_decode( \CCCrypter::decode( \CCCookie::get( $id.$this->cookie_suffix ), $this->crypt_salt ), true );
+		}
+		
+		return array();
+	}
+	
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @return boool
+	 */
+	public function has( $id )
+	{
+	    return (bool) \CCCookie::get( $id.$this->cookie_suffix, false );
+	}
+	
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @param array 		$data
+	 * @return bool
+	 */
+	public function write( $id, $data )
+	{
+		\CCCookie::set( $id.$this->cookie_suffix, \CCCrypter::encode( json_encode( $data ), $this->crypt_salt ) );
+	}
+	
+	/**
+	 * Delete session that are older than the given time in secounds
+	 *
+	 * @param int		$time
+	 * @return void
+	 */
+	public function gc( $time )
+	{
+		return false; // There is no garbage collection when using cookies
+	}
+}

+ 161 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Database.php

@@ -0,0 +1,161 @@
+<?php namespace Session;
+/**
+ * Session Database Driver
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Manager_Database implements Manager_Interface
+{
+	/**
+	 * The database instance used to store the sessions
+	 *
+	 * @var string
+	 */
+	private $database = null;
+
+	/**
+	 * The database table used
+	 *
+	 * @var string
+	 */
+	private $table = null;
+	
+	/**
+	 * An array of fields that get used as columns
+	 *
+	 * @var string
+	 */
+	private $index_fields = null;
+	
+	/**
+	 * The ID the session started with
+	 *
+	 * @var string
+	 */
+	private $inital_sesssion_id = null;
+
+	/**
+	 * Cookie driver constructor
+	 *
+	 * @param string		$name
+	 * @param array 		$conf
+	 */
+	public function __construct( $name, $conf )
+	{
+		$this->database = \CCArr::get( 'database', $conf );
+		$this->table = \CCArr::get( 'table', $conf, 'sessions' );
+		$this->index_fields = array_merge( \CCArr::get( 'index_fields', $conf, array() ), array_keys( Manager::default_data_provider() ) );
+	}
+
+	/**
+	 * Read data from the session dirver
+	 *
+	 * @param string		$id		The session id key.
+	 * @return array
+	 */
+	public function read( $id )
+	{
+		$this->inital_session_id = $id;
+		
+		if ( $data = \DB::select( $this->table )->where( 'id', $id )->one( $this->database ) )
+		{
+			$session_data = unserialize( $data->content );
+			
+			foreach( $this->index_fields as $field ) 
+			{
+				$session_data[$field] = $data->$field;
+			}
+			
+			return $session_data;
+		}
+		
+		return array();
+	}
+
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @return boool
+	 */
+	public function has( $id )
+	{
+		return \DB::select( $this->table )
+			->where( 'id', $id )
+			->column( 'id', $this->database ) == $id;
+	}
+
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @param array 		$data
+	 * @return bool
+	 */
+	public function write( $id, $data )
+	{
+		$columns = array();
+		
+		foreach( $this->index_fields as $field ) 
+		{
+			$columns[$field] = $data[$field]; unset( $data[$field] );
+		}
+		
+		$columns['content'] = serialize( $data );
+		
+		// When the session id didnt change we will do an update
+		if ( $id == $this->inital_session_id )
+		{
+			// because the we might already be in the shutdown process we 
+			// have to forward sql error directly to CCError
+			try {
+				\DB::update( $this->table, $columns )
+					->where( 'id', $id )
+					->run( $this->database );
+			} catch ( \Exception $e ) 
+			{
+				\CCError::exception( $e );
+			}
+		}
+		else
+		// else insert a new one
+		{
+			// add the id to the columns
+			$columns['id'] = $id;
+			
+			// because the we might already be in the shutdown process we 
+			// have to forward sql error directly to CCError
+			try {
+				\DB::insert( $this->table, $columns )->run( $this->database );
+			} catch ( \Exception $e ) 
+			{
+				\CCError::exception( $e );
+			}	
+		}
+	}
+
+	/**
+	 * Delete session that are older than the given time in secounds
+	 *
+	 * @param int		$time
+	 * @return void
+	 */
+	public function gc( $time )
+	{
+		// if we don't have the last active key we cannot execute 
+		// the garbage collection
+		if ( !in_array( 'last_active', $this->index_fields ) )
+		{
+			return false;
+		}
+		
+		\DB::delete( $this->table )
+			->where( 'last_active', '<', time() - $time )
+			->run( $this->database );
+	}
+}

+ 87 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/File.php

@@ -0,0 +1,87 @@
+<?php namespace Session;
+/**
+ * Session File Driver
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Manager_File implements Manager_Interface
+{
+	/**
+	 * Read data from the session dirver
+	 *
+	 * @param string		$id		The session id key.
+	 * @return array
+	 */
+	public function read( $id )
+	{
+	    if ( $this->has( $id ) )
+		{
+			return unserialize( \CCFile::read( $this->file_path( $id ) ) );
+		}
+		
+		return array();
+	}
+	
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @return boool
+	 */
+	public function has( $id )
+	{
+	    return file_exists( $this->file_path( $id ) );
+	}
+	
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @param array 		$data
+	 * @return bool
+	 */
+	public function write( $id, $data )
+	{
+		if ( !\CCFile::write( $this->file_path( $id ), serialize( $data ) ) )
+		{
+			\CCError::exception( new Exception( 'Could not write session file.' ) );
+		}
+	    return true;
+	}
+	
+	/**
+	 * Delete session that are older than the given time in secounds
+	 *
+	 * @param int		$time
+	 * @return void
+	 */
+	public function gc( $time )
+	{
+		foreach( \CCFile::ls( \CCStorage::path( 'sessions/*' ) ) as $file )
+		{
+			if ( ( filemtime( $file ) - ( time() - $time ) ) < 0 )
+			{
+				if ( !\CCFile::delete( $file ) )
+				{
+					throw new Exception( "Manager_File::gc - cannot delete session file." );
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Get the file path by id
+	 *
+	 * @param string			$id
+	 * @return string
+	 */
+	private function file_path( $id )
+	{
+		return \CCStorage::path( 'sessions/'.$id.'.session' );
+	}
+}

+ 46 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Interface.php

@@ -0,0 +1,46 @@
+<?php namespace Session;
+/**
+ * Session Manager Driver Interface
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+interface Manager_Interface
+{
+    /**
+     * Read data from the session dirver
+     *
+     * @param string		$id		The session id key.
+     * @return array
+     */
+    public function read( $id );
+    
+    /**
+     * Check if a session with the given key already exists
+     *
+     * @param string		$id		The session id key.
+     * @return boool
+     */
+    public function has( $id );
+    
+    /**
+     * Check if a session with the given key already exists
+     *
+     * @param string		$id		The session id key.
+     * @param array 		$data
+     * @return bool
+     */
+    public function write( $id, $data );
+    
+    /**
+     * Delete session that are older than the given time in secounds
+     *
+     * @param int		$time
+     * @return void
+     */
+    public function gc( $time );
+}

+ 83 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/Session/Manager/Json.php

@@ -0,0 +1,83 @@
+<?php namespace Session;
+/**
+ * Session File Driver
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Manager_Json implements Manager_Interface
+{
+	/**
+	 * Read data from the session dirver
+	 *
+	 * @param string		$id		The session id key.
+	 * @return array
+	 */
+	public function read( $id )
+	{
+	    if ( $this->has( $id ) )
+		{
+			return \CCJson::read( $this->file_path( $id ) );
+		}
+		
+		return array();
+	}
+	
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @return boool
+	 */
+	public function has( $id )
+	{
+	    return file_exists( $this->file_path( $id ) );
+	}
+	
+	/**
+	 * Check if a session with the given key already exists
+	 *
+	 * @param string		$id		The session id key.
+	 * @param array 		$data
+	 * @return bool
+	 */
+	public function write( $id, $data )
+	{
+	    \CCJson::write( $this->file_path( $id ), $data, true );
+	}
+	
+	/**
+	 * Delete session that are older than the given time in secounds
+	 *
+	 * @param int		$time
+	 * @return void
+	 */
+	public function gc( $time )
+	{
+		foreach( \CCFile::ls( \CCStorage::path( 'sessions/*' ) ) as $file )
+		{
+			if ( ( filemtime( $file ) - ( time() - $time ) ) < 0 )
+			{
+				if ( !\CCFile::delete( $file ) )
+				{
+					throw new Exception( "Manager_File::gc - cannot delete session file." );
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Get the file path by id
+	 *
+	 * @param string			$id
+	 * @return string
+	 */
+	private function file_path( $id )
+	{
+		return \CCStorage::path( 'sessions/'.$id.'.json' );
+	}
+}

+ 109 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Alert.php

@@ -0,0 +1,109 @@
+<?php namespace UI;
+/**
+ * Uiify component Builder
+ *
+ * @package 		Uiify
+ * @author     	Mario Döring <[email protected]>
+ * @version 		1.0
+ * @copyright 	2013 ClanCats GmbH
+ *
+ */
+class Alert
+{
+	/**
+	 * notification holder
+	 * 
+	 * @var array
+	 */
+	protected static $_alerts = array();
+	
+	/**
+	 * available alert types
+	 *
+	 * @var array
+	 */
+	protected static $_types = array(
+		'danger',
+		'warning',
+		'info',
+		'success',
+	);
+	
+	/**
+	 * When loading the alerts we going to add alerts
+	 * from the previous request stored in the session
+	 *
+	 * @return void
+	 */
+	public static function _init() 
+	{
+		if ( \CCSession::has( 'ui.alerts' ) ) 
+		{
+			static::$_alerts = \CCSession::once( 'ui.alerts', array() );
+		}
+	}
+	
+	/**
+	 * Validate the alert type and format the message
+	 *
+	 * @param string 			$type
+	 * @param string|array 		$message
+	 * @return array
+	 */
+	private static function prepare( $type, $message )
+	{
+		// to avoid typos and other mistakes we 
+		// validate the alert type
+		$type = strtolower( $type );
+		if ( !in_array( $type, static::$_types ) ) 
+		{
+			throw new Exception( "UI\Alert - Unknown alert type '{$type}'!" );
+		}
+		
+		// We always need to return an array
+		if ( !is_array( $message ) )
+		{
+			return array( $message );
+		}
+		return $message;
+	}
+	
+	/**
+	 * Flash an alert
+	 * This will add the alert to session so it gets displayed on the next request.
+	 *
+	 * @param string 			$type
+	 * @param string|array 		$message
+	 * @return void
+	 */
+	public static function flash( $type, $message ) 
+	{	
+		\CCSession::add( 'ui.alerts.'.$type, static::prepare( $type, $message ) );
+	}
+	
+	/**
+	 * Add an alert to the current queue
+	 *
+	 * @param string 			$type
+	 * @param string|array 		$message
+	 * @return void
+	 */
+	public static function add( $type, $message ) 
+	{
+		static::$_alerts[$type][] = static::prepare( $type, $message );
+	}
+	
+	/**
+	 * Render the alerts and reset the queue
+	 *
+	 * @return UI\HTML
+	 */
+	public static function render()
+	{
+		$html = Builder::handle( 'alert', static::$_alerts );
+		
+		static::$_alerts = array();
+		
+		return $html;
+	}
+}

+ 55 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Builder.php

@@ -0,0 +1,55 @@
+<?php namespace UI;
+/**
+ * Uiify component Builder
+ *
+ * @package 		Uiify
+ * @author     	Mario Döring <[email protected]>
+ * @version 		1.0
+ * @copyright 	2013 ClanCats GmbH
+ *
+ */
+class Builder 
+{
+	/**
+	 * The current Bulder object
+	 *
+	 * @var UI\Builder_Interface
+	 */
+	protected static $builder = null;
+	
+	/**
+	 * The user interface configuration
+	 *
+	 * @var UI\Builder_Interface
+	 */
+	public static $config = null;
+	
+	/**
+	 * Static init
+	 *
+	 * @return void
+	 */
+	public static function _init()
+	{
+		// select the current builder
+		$builder_class = \ClanCats::$config->get( 'ui.builder', "\\UI\\Builder_Bootstrap" );
+		static::$builder = new $builder_class;
+		
+		// load the ui configuration
+		static::$config = \CCConfig::create( 'ui' );
+	}
+	
+	/** 
+	 * Handle a build request
+	 *
+	 * @param string			$key
+	 * @param mixed ...
+	 */
+	public static function handle()
+	{
+		$args = func_get_args();
+		$key = array_shift( $args );
+		
+		return call_user_func_array( array( static::$builder, 'build_'.$key ), $args );
+	}
+}

+ 99 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Builder/Bootstrap.php

@@ -0,0 +1,99 @@
+<?php namespace UI;
+/**
+ * Uiify Bootstrap Builder class
+ *
+ * @package 		Uiify
+ * @author     	Mario Döring <[email protected]>
+ * @version 		0.1
+ * @copyright 	2013 ClanCats GmbH
+ *
+ */
+class Builder_Bootstrap implements Builder_Interface
+{
+	/**
+	 * Build an input form
+	 *
+	 * @param UI\HTML		$element
+	 * @return UI\HTML
+	 */
+	public function build_form_input( $element )
+	{
+		return $element->class( 'form-control' );
+	}
+	
+	/**
+	 * Build an input form
+	 *
+	 * @param UI\HTML		$element
+	 * @return UI\HTML
+	 */
+	public function build_form_textarea( $element )
+	{
+		return $element->class( 'form-control' );
+	}
+	
+	/**
+	 * Build an input label
+	 *
+	 * @param UI\HTML		$element
+	 * @return UI\HTML
+	 */
+	public function build_form_label( $element )
+	{
+		return $element->class( 'control-label' );
+	}
+	
+	/**
+	 * Build an input label
+	 *
+	 * @param UI\HTML		$element
+	 * @return UI\HTML
+	 */
+	public function build_form_checkbox( $element )
+	{
+		return HTML::tag( 'div', $element->render() )->class( 'checkbox' );
+	}
+	
+	/**
+	 * Build an input label
+	 *
+	 * @param UI\HTML		$element
+	 * @return UI\HTML
+	 */
+	public function build_form_select( $element )
+	{
+		return $element->class( 'form-control' );
+	}
+	
+	/**
+	 * Build the UI alerts
+	 *
+	 * @param array 		$alerts
+	 * @return UI\HTML
+	 */
+	public function build_alert( $alerts )
+	{	
+		return HTML::tag( 'div', function() use( $alerts ) 
+		{
+			// loop trough all alert types
+			foreach( $alerts as $type => $items ) 
+			{
+				foreach( $items as $alert ) 
+				{
+					$alert = implode( "<br>\n", $alert );
+					
+					// close button
+					$close = HTML::tag( 'button', '&times;' )
+						->add_class( 'close' )
+						->type( 'button' )
+						->data( 'dismiss', 'alert' );
+					
+					// alert div
+					echo HTML::tag( 'div', $close.$alert )
+						->add_class( 'alert fade in' )
+						->add_class( 'alert-'.$type );
+				}
+			}
+		})->add_class( 'ui-alert-container' );
+	}
+}

+ 44 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Builder/Interface.php

@@ -0,0 +1,44 @@
+<?php namespace UI;
+/**
+ * Uiify Builder Interface
+ *
+ * @package 		Uiify
+ * @author     	Mario Döring <[email protected]>
+ * @version 		1.0
+ * @copyright 	2013 ClanCats GmbH
+ *
+ */
+interface Builder_Interface
+{
+	/**
+	 * Build an input form
+	 *
+	 * @param UI\HTML		$element
+	 * @return UI\HTML
+	 */
+	public function build_form_input( $element );
+	
+	/**
+	 * Build an input label
+	 *
+	 * @param UI\HTML		$element
+	 * @return UI\HTML
+	 */
+	public function build_form_label( $element );
+	
+	/**
+	 * Build an input label
+	 *
+	 * @param UI\HTML		$element
+	 * @return UI\HTML
+	 */
+	public function build_form_checkbox( $element );
+	
+	/**
+	 * Build the UI alerts
+	 *
+	 * @param array 		$alerts
+	 * @return UI\HTML
+	 */
+	public function build_alert( $alerts );
+}

+ 151 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/E.php

@@ -0,0 +1,151 @@
+<?php namespace UI;
+/**
+ * Uiify Base Element
+ *
+ * @package 		Uiify
+ * @author     	Mario Döring <[email protected]>
+ * @version 		0.1
+ * @copyright 	2013 ClanCats GmbH
+ *
+ */
+class E {
+	
+	/**
+	 * generates an html tag
+	 *
+	 * @param string 	$name
+	 * @param mixed		$param1
+	 * @param mixed		$param2
+	 * @return UI\E
+	 */
+	public static function create( $name, $param1 = null, $param2 = null ) 
+	{
+		return new static( $name, $param1, $param2 );
+	}
+	
+	/**
+	 * Create elemnts by dynamic calls
+	 *
+	 * @param string 		$name
+	 * @param array 			$arguments
+	 * @return UI\E
+	 */
+	public static function __callStatic( $name, $arguments )
+	{
+		array_unshift( $arguments, $name );
+		return forward_static_call_array( array( "UI\\E", 'create' ), $arguments );
+	}
+	
+	/**
+	 * generates element attribute string
+	 *
+	 * @param array 	$attr
+	 */
+	public static function attr( $attr = array() ) {
+		$buffer = " ";
+		foreach( $attr as $key => $value ) {	
+			$buffer .= $key.'="'.$value.'" ';
+		}
+	
+		return substr( $buffer, 0, -1 );
+	}
+	
+	/*
+	 * The element value / content
+	 */
+	public $value = null;
+	
+	/*
+	 * the elements attributes
+	 */
+	public $attr = array();
+	
+	/*
+	 * the element name
+	 */
+	public $name = "";
+	
+	
+	/**
+	 * element constructor
+	 *
+	 * @param string 	$name
+	 * @param mixed		$param1
+	 * @param mixed		$param2
+	 */ 
+	public function __construct( $name, $param1 = null, $param2 = null ) {
+		/*
+		 * Dynamic params
+		 */
+		if ( is_array( $param1 ) ) {
+			$this->attr = $param1;
+			if ( is_string( $param2 ) || is_closure( $param2 ) ) {
+				$this->value = $param2;
+			}
+		}
+		elseif ( is_string( $param1 ) || is_closure( $param1 ) ) {
+			$this->value = $param1;
+			if ( is_array( $param2 ) ) {
+				$this->attr = $param2;
+			}
+		}
+		
+		$this->name = $name;
+	}
+	
+	/**
+	 * Set an attribute
+	 *
+	 * @param string	$attribute
+	 * @param mixed		$value
+	 * @return $this
+	 */
+	public function set_attr( $attribute, $value = null ) {
+		if ( is_null( $value ) || $value === false ) {
+			if ( array_key_exists( $attribute, $this->attr ) ) {
+				unset( $this->attr[$attribute] );
+			}
+		} else {
+			if ( $value === true ) {
+				$value = $attribute;
+			}
+			$this->attr[ $attribute ] = $value;
+		}
+		
+		return $this;
+	}
+	
+	/**
+	 * magic call to set attributes
+	 *
+	 * @param string	$method
+	 * @param array 	$args
+	 * @return $this
+	 */
+	function __call( $method, $args ) {
+		return $this->set_attr( $method, $args[key($args)] );
+	}
+	
+	/**
+	 * to string magic
+	 *
+	 * @return string
+	 */
+	public function __toString() {
+		return $this->render();
+	}
+	
+	/**
+	 * render this element
+	 *
+	 * @return string
+	 */
+	public function render() {
+		// execute callback if we have one first
+		if ( is_closure( $this->value ) ) {
+			ob_start(); $return = call_user_func( $this->value ); $this->value = ob_get_clean().$return;
+		}
+		return '<'.$this->name.static::attr( $this->attr ).
+			( !is_null( $this->value ) ? '>'.$this->value.'</'.$this->name.'>' : ' />' );
+	}
+}

+ 12 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Exception.php

@@ -0,0 +1,12 @@
+<?php namespace UI;
+/**
+ * UI Exception
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class Exception extends \Core\CCException {}

+ 369 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Form.php

@@ -0,0 +1,369 @@
+<?php namespace UI;
+/**
+ * Uiify Form Generator
+ *
+ * @package 		Uiify
+ * @author     	Mario Döring <[email protected]>
+ * @version 		0.1
+ * @copyright 	2013 ClanCats GmbH
+ *
+ */
+class Form 
+{	
+	/**
+	 * Current prefix
+	 *
+	 * @var string
+	 */
+	private static $id_prefix = null;
+	
+	/**
+	 * Registerd patterns
+	 *
+	 * @var array[callbacks]
+	 */
+	private static $macros = array();
+	
+	/**
+	 * Is the builder enabled
+	 *
+	 * @var bool
+	 */
+	private static $builder_enabled = false;
+	
+	/**
+	 * Static init
+	 * Here we register all inital macros
+	 */
+	public static function _init()
+	{
+		static::$builder_enabled = Builder::$config->get( 'form.builder_enabled' );
+	}
+	
+	/**
+	 * Create a new static pattern for the form generator.
+	 * I found this Macro idea in the wonderfull laravel framework thanks.
+	 *
+	 * @param string			$key
+	 * @param callback		$callback
+	 * @return void
+	 */
+	public static function macro( $key, $callback )
+	{
+		static::$macros[$key] = $callback;
+	}
+	
+	/**
+	 * Open a new form
+	 * This will set the current form to this one
+	 * 
+	 * @param string			$key
+	 * @param array 			$attr
+	 * @return string
+	 */
+	public static function start( $key, $attr = array() )
+	{
+		$attributes = array();
+		
+		// force the form role
+		$attributes['role'] = 'form';
+		
+		if ( !is_null( $key ) )
+		{
+			 static::$id_prefix = $attributes['id'] = static::form_id( 'form', $key );
+		}
+		
+		$attributes = array_merge( $attributes, $attr );
+		
+		return '<form'.HTML::attr( $attributes ).'>';
+	}
+	
+	/**
+	 * Closes the form and resest the current form
+	 * 
+	 * @param string			$key
+	 * @param array 			$attr
+	 * @return string
+	 */
+	public static function end()
+	{
+		static::$id_prefix = null; return '</form>';
+	}
+	
+	/**
+	 * Create a new from instance
+	 *
+	 * @param callback		$callback	
+	 * @param string			$key			The form key used for identification.
+	 * @param array 			$attr		The form dom attributes.
+	 * @return UI\Form
+	 */
+	public static function capture( $callback = null, $key = null, $attr = null ) 
+	{	
+		// we got some dynamics in the parameters here so in case
+		// of this shift stuff
+		if ( is_callable( $attr ) && !is_callable( $callback ) )
+		{
+			$new_attr = $key;
+			$key = $callback;
+			$callback = $attr;
+			$attr = $new_attr;
+		}
+		
+		$form = new static;
+		
+		if ( is_null( $callback ) )
+		{
+			throw new Exception( 'Cannot use capture without a callback or string given.' );
+		}
+		
+		// fix no array given
+		if ( !is_array( $attr ) )
+		{
+			$attr = array();
+		} 
+		
+		return static::start( $key, $attr ).\CCStr::capture( $callback, array( $form ) ).static::end();
+	}
+	
+	/**
+	 * Format an id by configartion
+	 *
+	 * @param string 		$type 	element, form etc..
+	 * @param string			$name
+	 * @return string
+	 */
+	public static function form_id( $type, $name )
+	{
+		return sprintf( Builder::$config->get( 'form.'.$type.'_id_format' ), $name );
+	}
+	
+	/**
+	 * Format an id by configartion with the current form prefix
+	 *
+	 * @param string 		$type 	element, form etc..
+	 * @param strgin			$name
+	 * @return string
+	 */
+	public static function build_id( $type, $name )
+	{
+		if ( !is_null( static::$id_prefix ) )
+		{
+			return static::$id_prefix.'-'.static::form_id( $type, $name );
+		}
+		return static::form_id( $type, $name );
+	}
+	
+	/**
+	 * Forward intance functions to static using the current instance
+	 *
+	 * @param string 		$method
+	 * @param array 			$args
+	 * @return mixed
+	 */
+	public static function __callStatic( $method, $args ) 
+	{
+		// take the first argument and add it again as the id
+		array_unshift( $args, static::build_id( $method, reset( $args ) ) );
+		
+		if ( array_key_exists( $method, static::$macros ) )
+		{
+			// execute the macro
+			return call_user_func_array( static::$macros[$method], $args );
+		}
+		
+		if ( method_exists( __CLASS__, 'make_'.$method ) )
+		{
+			return call_user_func_array( array( __CLASS__, 'make_'.$method ), $args );
+		}
+		
+		throw new Exception( "UI\\Form - Unknown macro '".$method."'." );
+	}
+	
+	/**
+	 * Simply forward to call static to allow execution from 
+	 * object context
+	 *
+	 * @param string 		$method
+	 * @param array 			$args
+	 * @return mixed
+	 */
+	public function __call( $method, $args )
+	{
+		return static::__callStatic( $method, $args );
+	}
+	
+	/**
+	 * make an input
+	 *
+	 * @param string		$id			The id that has been generated for us.
+	 * @param string 	$key			This is the name 
+	 * @param string		$type
+	 * @param array 		$attr
+	 */
+	public static function make_input( $id, $key, $value = null, $type = 'text', $attr = array() ) 
+	{
+		$element = HTML::tag( 'input', array_merge( array( 
+			'id' => $id, 
+			'name' => $key, 
+			'type' => $type 
+		), $attr ));
+		
+		if ( !is_null( $value ) )
+		{
+			$element->value( _e( $value ) );
+		}
+		
+		if ( !static::$builder_enabled )
+		{
+			 return $element;
+		}
+		
+		return Builder::handle( 'form_input', $element );
+	}
+	
+	/**
+	 * make a label
+	 *
+	 * @param string		$id			The id that has been generated for us.
+	 * @param string 	$key			This is the name 
+	 * @param string		$text
+	 * @param array 		$attr
+	 */
+	public static function make_label( $id, $key, $text = null, $attr = array() ) 
+	{
+		if ( is_null( $text ) )
+		{
+			$text = $key;
+		}
+		
+		$element = HTML::tag( 'label', $text, array_merge( array( 
+			'id' => $id, 
+			'for' => static::build_id( 'input', $key ) 
+		), $attr ));
+		
+		if ( !static::$builder_enabled )
+		{
+			 return $element;
+		}
+		
+		return Builder::handle( 'form_label', $element );
+	}
+	
+	/**
+	 * make a checkbox
+	 *
+	 * @param string		$id			The id that has been generated for us.
+	 * @param string 	$key			This is the name 
+	 * @param string		$text
+	 * @param bool		$active		Is the checkbox cheked
+	 * @param array 		$attr
+	 * @return string
+	 */
+	public static function make_checkbox( $id, $key, $text = '', $active = false, $attr = array() ) 
+	{
+		$element = HTML::tag( 'input', array_merge( array( 
+			'id' => $id,
+			'name' => $key, 
+			'type' => 'checkbox' 
+		), $attr ));
+		
+		$element->checked( (bool) $active );
+		
+		$element = HTML::tag( 'label', $element->render().' '.$text );
+		
+		if ( !static::$builder_enabled )
+		{
+			 return $element;
+		}
+		
+		return Builder::handle( 'form_checkbox', $element );
+	}
+	
+	/**
+	 * generate a textarea
+	 *
+	 * @param string		$id			The id that has been generated for us.
+	 * @param string 	$key			This is the name 
+	 * @param string		$value
+	 * @param array 		$attr
+	 * @return string
+	 */
+	public static function make_textarea( $id, $key, $value = '', $attr = array() ) 
+	{
+		$element = HTML::tag( 'textarea', _e( $value ), array_merge( array( 
+			'id' => $id, 
+			'name' => $key,
+		), $attr ));
+		
+		if ( !static::$builder_enabled )
+		{
+			 return $element;
+		}
+		
+		return Builder::handle( 'form_textarea', $element );
+	}
+	
+	/**
+	 * generate a file input
+	 *
+	 * @param string		$id			The id that has been generated for us.
+	 * @param string 	$key			This is the name
+	 * @param array 		$attr
+	 * @return string
+	 */
+	public static function make_file( $id, $key, $attr = array() ) 
+	{
+		return static::make_input( $id, $key, null, 'file', $attr );
+	}
+
+	/**
+	 * generate an select
+	 *
+	 *     Form::select( 'gender', array( 'F', 'M' ), 0 );
+	 *
+	 *     Form::select( 'gender', array( '1' => 'A', '2' => 'B' ), array( 1,2 ), 2 );
+	 *
+	 * @param string		$id			The id that has been generated for us.
+	 * @param string 	$name		This is the name
+	 * @param array		$options
+	 * @param array 		$selected
+	 * @param int		$size
+	 * @return string
+	 */
+	public static function make_select( $id, $name, array $options, $selected = array(), $size = 1 ) 
+	{	
+		if ( !is_array( $selected ) ) 
+		{
+			$selected = array( $selected );
+		}
+		
+		$buffer = "";
+		
+		foreach( $options as $key => $value )
+		{
+			$option = HTML::tag( 'option', $value )
+				->value( $key );
+			
+			if ( in_array( $key, $selected ) )
+			{
+				$option->selected( true );
+			}
+			
+			$buffer .= $option->render();
+		}
+		
+		$element = HTML::tag( 'select', $buffer, array(
+			'id' => $id,
+			'name' => $name,
+			'size' => $size,
+		) );
+		
+		if ( !static::$builder_enabled )
+		{
+			 return $element;
+		}
+		
+		return Builder::handle( 'form_select', $element );
+	}
+}

+ 135 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/HTML.php

@@ -0,0 +1,135 @@
+<?php namespace UI;
+/**
+ * Uiify Base HTML stuff
+ *
+ * @package 		Uiify
+ * @author     	Mario Döring <[email protected]>
+ * @version 		0.1
+ * @copyright 	2013 ClanCats GmbH
+ *
+ */
+class HTML extends E 
+{
+	/**
+	 * The maker is like a development shortcut to create
+	 * html elements on the go
+	 *
+	 * If param2 is set it wil be used as the content otherwise
+	 * the the first parameter will be splittet by the first space.
+	 *
+	 * @param string			$param
+	 * @param string			$param2
+	 */
+	public static function maker( $param, $param2 = null )
+	{
+		if ( !is_null( $param2 ) )
+		{
+			return static::create( $param, $param2 );
+		}
+		
+		$param = explode( ' ', $param );
+		
+		$element = array_shift( $param );
+		
+		return static::create( $element, implode( ' ', $param ) );
+	}
+	
+	/**
+	 * generates html attribute string
+	 *
+	 * @param array 	$attr
+	 */
+	public static function attr( $attr = array() ) {
+		$buffer = " ";
+		foreach( $attr as $key => $value ) {
+		
+			switch( $key ) {
+		
+				case 'class':
+				if ( is_array( $value ) ) { $value = implode( ' ', $value ); }
+				break;
+		
+				case 'style':
+				if ( is_array( $value ) ) {
+					$style = $value; $value = "";
+					foreach( $style as $k => $v ) {
+						$value .= $k.':'.$v.';';
+					}
+				}
+				break;
+			}
+		
+			$buffer .= $key.'="'.$value.'" ';
+		}
+		
+		return substr( $buffer, 0, -1 );
+	}
+	
+	/**
+	 * generates an html tag
+	 *
+	 * @param string 	$name
+	 * @param mixed		$param1
+	 * @param mixed		$param2
+	 */
+	public static function tag( $name, $param1 = null, $param2 = null ) 
+	{
+		return static::create( $name, $param1, $param2 );
+	}
+	
+	/**
+	 * set an data attribute
+	 *
+	 * @param string	$key
+	 * @param string	$value
+	 */
+	public function data( $key, $value ) {
+		return $this->set_attr( 'data-'.$key, $value );
+	}
+	
+	/**
+	 * add html class
+	 *
+	 * @param string 	$class
+	 */
+	public function add_class( $class ) {
+		if ( !isset( $this->attr['class'] ) || !is_array( $this->attr['class'] ) ) {
+			$this->_sanitize_class();
+		}
+		if ( strpos( $class, ' ' ) !== false ) {
+			$class = explode( ' ', $class );
+		}
+		if ( is_string( $class ) ) {
+			$class = array( $class );
+		}
+		foreach ( $class as $c ) {
+			$this->attr['class'][] = $c;
+		}
+		return $this;
+	}
+	
+	/**
+	 * remove html class
+	 *
+	 * @param string 	$class
+	 */
+	public function remove_class( $class ) {
+		if ( !isset( $this->attr['class'] ) || !is_array( $this->attr['class'] ) ) {
+			$this->_sanitize_class();
+		}
+		$this->attr['class'] = array_diff( $this->attr['class'], array( $class ) );
+		return $this;
+	}
+	
+	/**
+	 * clean the classes attribute
+	 */
+	private function _sanitize_class() {
+		if ( isset( $this->attr['class'] ) && is_string( $this->attr['class'] ) ) {
+			$this->attr['class'] = explode( ' ', $this->attr['class'] );
+		}
+		if ( !isset( $this->attr['class'] ) || !is_array( $this->attr['class'] ) ) {
+			$this->attr['class'] = array();
+		}
+	}
+}

+ 125 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/UI/Table.php

@@ -0,0 +1,125 @@
+<?php namespace UI;
+/**
+ * Uiify Table Generator
+ *
+ * @package 		Uiify
+ * @author     	Mario Döring <[email protected]>
+ * @version 		0.1
+ * @copyright 	2013 ClanCats GmbH
+ *
+ */
+class Table {
+	
+	/**
+	 * get an table instance
+	 */
+	public static function create( $attr = array() ) {
+		return new static( $attr );	
+	}
+	
+	/**
+	 * creates an cell
+	 */
+	public static function cell( $value, $attr = array(), $ele = 'td' ) {
+		return HTML::tag( $ele, $value, $attr );
+	}
+	
+	/*
+	 * The table element
+	 */
+	public $element = null;
+	
+	/*
+	 * the table header
+	 */
+	public $header = array();
+	
+	/*
+	 * data holder
+	 */
+	public $data = array();
+	
+	/**
+	 * generate
+	 */
+	public function __construct( $attr = array() ) {
+		$this->element = HTML::create( 'table', $attr );
+	}
+	
+	/**
+	 * add a new row
+	 */
+	public function row( $data = array(), $attr = array() ) {
+		$this->data[] = $this->_repair_row( $data, $attr ); return $this;
+	}
+	
+	/**
+	 * set the header
+	 */
+	public function header( $data = array(), $attr = array() ) {
+		$this->header = $this->_repair_row( $data, $attr ); return $this;
+	}
+	
+	/**
+	 * repair a row for rendering
+	 */
+	private function _repair_row( $data = array(), $attr = array() ) {
+		$row = array();
+		foreach( $data as $key => $value ) {
+			if ( !$value instanceof HTML || ( $value->name != 'td' && $value->name != 'th' ) ) {
+				$value = static::cell( (string) $value );
+			}
+			if ( is_string( $key ) ) {
+				$row[$key] = $value;
+			} else {
+				$row[] = $value;
+			}
+		}
+		return array( $row, $attr );
+	}
+	
+	/**
+	 * magic to string
+	 */
+	public function __toString() {
+		return $this->render();
+	}
+	
+	/**
+	 * generate the output
+	 */
+	public function render() {
+		
+		$buffer = ''; 
+		
+		if ( !empty( $this->header ) ) {
+			
+			$buffer .= '<tr'.HTML::attr( $this->header[1] ).'>';
+			foreach( $this->header[0] as $head ) {
+				$head->name = 'th';
+				$buffer .= $head->render();
+			}
+			$buffer .= '</tr>';
+			foreach( $this->data as $row ) {
+				$buffer .= '<tr'.HTML::attr( $row[1] ).'>';
+				foreach( $this->header[0] as $key => $value ) {
+					$buffer .= HTML::tag( 'td', $row[0][$key]->value, $row[0][$key]->attr );
+				}
+				$buffer .= '</tr>';
+			}
+		}
+		else {
+			foreach( $this->data as $row ) {
+				$buffer .= '<tr'.HTML::attr( $row[1] ).'>';
+				foreach( $row[0] as $value ) {
+					$buffer .= $value->render();
+				}
+				$buffer .= '</tr>';
+			}
+		}
+		
+		$this->element->value = $buffer;
+		
+		return $this->element->render();
+	}
+}

+ 68 - 0
frameworks/PHP/php-clancatsframework/CCF/core/bundles/map.php

@@ -0,0 +1,68 @@
+<?php
+/*
+ *---------------------------------------------------------------
+ * Database Bundle
+ *---------------------------------------------------------------
+ * 
+ * Here we define the database interface shadow and namespace
+ */
+// namepace
+\CCFinder::map( 'DB', COREPATH.'bundles/Database/' );
+
+// and the shdaow
+\CCFinder::shadow( 'DB', 'DB', COREPATH.'bundles/Database/DB'.EXT );
+
+/*
+ *---------------------------------------------------------------
+ * UI Bundle
+ *---------------------------------------------------------------
+ * 
+ * The UI Bundle contains some helpers to generate HTML.
+ */
+// namepace
+\CCFinder::map( 'UI', COREPATH.'bundles/UI/' );
+
+/*
+ *---------------------------------------------------------------
+ * Session Bundle
+ *---------------------------------------------------------------
+ * 
+ * Session managment bundle
+ */
+// namepace
+\CCFinder::map( 'Session', COREPATH.'bundles/Session/' );
+
+// and the shdaow
+\CCFinder::shadow( 'CCSession', 'Session', COREPATH.'bundles/Session/CCSession'.EXT );
+
+/*
+ *---------------------------------------------------------------
+ * Authentication Bundle
+ *---------------------------------------------------------------
+ * 
+ * The Authentication bundle for basic a basic user and login
+ */
+// namepace
+\CCFinder::map( 'Auth', COREPATH.'bundles/Auth/' );
+
+// and the shdaow
+\CCFinder::shadow( 'CCAuth', 'Auth', COREPATH.'bundles/Auth/CCAuth'.EXT );
+
+/*
+ *---------------------------------------------------------------
+ * Email Bundle
+ *---------------------------------------------------------------
+ * 
+ * The Email bundle mostly wraps phpmailer
+ */
+// namepace
+\CCFinder::map( 'Mail', COREPATH.'bundles/Mail/' );
+// phpmailer
+\CCFinder::bind( array(
+    "Mail\\PHPMailer\\PHPMailer" => COREPATH.'bundles/Mail/PHPMailer/class.phpmailer'.EXT,
+    "Mail\\PHPMailer\\POP3" => COREPATH.'bundles/Mail/PHPMailer/class.php3'.EXT,
+    "Mail\\PHPMailer\\SMTP" => COREPATH.'bundles/Mail/PHPMailer/class.smtp'.EXT,
+));
+
+// and the shdaow
+\CCFinder::shadow( 'CCMail', 'Mail', COREPATH.'bundles/Mail/CCMail'.EXT );

+ 57 - 0
frameworks/PHP/php-clancatsframework/CCF/core/classes/CCApp.php

@@ -0,0 +1,57 @@
+<?php namespace Core;
+/**
+ * App object 
+ * The application object implements some events, in the most
+ * cases your routes are app specific so return them in your App class.
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCApp 
+{	
+	/**
+	 * The application name
+	 *
+	 * @var string
+	 */
+	public static $name = 'CCF Application';
+	
+	/**
+	 * Get the applications name
+	 *
+	 * @return string
+	 */
+	public static function name() 
+	{
+		return static::$name;
+	}
+	
+	/**
+	 * This function should provide the application routes.
+	 * By default its going to use the router.config file.
+	 *
+	 * @return array
+	 */
+	public static function routes() 
+	{
+		return CCConfig::create( ClanCats::$config->get( 'router.map' ) )->raw();
+	}
+	
+	/**
+	 * Application initialization.
+	 *
+	 * do your inital stuff here like getting the current user object ect..
+	 * You can return a CCResponse wich will cancle all 
+	 * other actions if its enebaled ( see. main.config -> send_app_wake_response )
+	 *
+	 * @return void | CCResponse
+	 */
+	public static function wake() 
+	{
+		// do stuff
+	}
+}

+ 455 - 0
frameworks/PHP/php-clancatsframework/CCF/core/classes/CCArr.php

@@ -0,0 +1,455 @@
+<?php namespace Core;
+/**
+ * Array
+ * php array helpers
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCArr 
+{	
+	/**
+	 * Get the first element of an array
+	 *
+	 * @param array 		$array
+	 * @return mixed
+	 */
+	public static function first( $arr ) 
+	{
+		return array_shift( $arr );
+	}
+	
+	/**
+	 * Get the last element of an array
+	 *
+	 * @param array 		$array
+	 * @return mixed
+	 */
+	public static function last( $arr ) 
+	{
+		return array_pop( $arr );
+	}
+	
+	/**
+	 * Adds a single item or array of items at the end of the referenced array
+	 * If you want to combine multiple arrays recursivly or use key => value pairs, please use CCArr::merge()
+	 * 
+	 * Example:
+	 * 	$bar = array( 'bar' );
+	 * 	CCArr::push( 'foo', $bar ); // $bar = array( 'bar', 'foo' )
+	 * 	CCArr::push( array( 'foo', 'baz' ), $bar ); // $bar = array( 'bar', array( 'foo', 'baz' ) )
+	 * 	CCArr::push( array( 'foo', 'baz' ), $bar, true ); // $bar = array( 'bar', 'foo', 'baz' )
+	 *
+	 * @param mixed		$item		The item you would like to add to the array
+	 * @param array 	$array		The input array by reference
+	 * @param bool		$merge		If $merge is set to true, push will merge each element of $item into $array
+	 * @return array
+ 	 */
+	public static function push( $item, &$arr, $merge = false ) 
+	{	
+		if( !is_array( $arr ) ) 
+		{
+			throw new \InvalidArgumentException('CCArr::push - second argument has to be an array.');
+		}
+		
+		if ( $merge && is_array( $item ) ) 
+		{
+			foreach ( $item as $value ) 
+			{
+				$arr[] = $value;
+			}	
+			return $arr;
+		}
+		$arr[] = $item;
+		return $arr;
+	}
+	
+	/**
+	 * Adds an item to an element in the array
+	 * 
+	 * Example:
+	 *     CCArr::add( 'foo.bar', 'test' );
+	 * 
+	 * Results: 
+	 *     array( 'foo' => array( 'bar' => array( 'test' ) ) )
+	 *
+	 * @param string		$key 
+	 * @param mixed		$item		The item you would like to add to the array
+	 * @param array 		$array
+	 * @return array
+ 	 */
+	public static function add( $key, $item, &$arr ) 
+	{	
+		if( !is_array( $arr ) ) 
+		{
+			throw new \InvalidArgumentException('CCArr::add - second argument has to be an array.');
+		}
+		
+		if ( !is_array( static::get( $key, $arr ) ) )
+		{
+			return static::set( $key, array( $item ), $arr );
+		}
+		
+		return static::set( $key, static::push( $item, static::get( $key, $arr ) ), $arr );
+	}
+	
+	/**
+	 * get a special item from every array
+	 *
+	 * @param mixed			$key
+	 * @param array[array]	$arr
+	 * @return array
+	 */
+	public static function pick( $key, $arr ) 
+	{	
+		if( !is_array( $arr ) ) 
+		{
+			throw new \InvalidArgumentException('CCArr::pick - second argument has to be an array.');
+		}
+		
+		$return = array();
+		
+		foreach( $arr as $array ) 
+		{
+			$return[] = CCArr::get( $key, $array );
+		}
+		
+		return $return;
+	}
+	
+	/**
+	 * Check if an array is multidimensional
+	 * Elements with empty arrays doesn't count!
+	 *
+	 * Example:
+	 * 	CCArr::is_multi( array( 'foo', array( 'bar', 'baz' ) ) ) === true
+	 * 	CCArr::is_multi( array( array() ) ) === false
+	 * 	CCArr::is_multi( false ) === false
+	 * 
+	 * @param array 		$arr
+	 * @return bool
+	 */
+	public static function is_multi( $arr ) 
+	{
+		// if $arr isn't an array both count() will return useless values 0 (count(null)) or 1 (count(false)) and so the function will return false
+		if ( count( $arr ) == count( $arr, COUNT_RECURSIVE ) ) 
+		{
+			return false;
+		}
+		return true;
+	}
+	
+	/**
+	 * Check if first element of an array is an array
+	 *
+	 * Example:
+	 * 	CCArr::is_collection( array( 'foo', array( 'bar', 'baz' ) ) ) === false
+	 * 	CCArr::is_collection( array( array() ) ) === true
+	 * 	CCArr::is_collection( false ) // Exception
+	 *
+	 * @param array 		$arr
+	 * @return bool
+	 */
+	public static function is_collection( $arr ) 
+	{
+		return is_array( reset( $arr ) );
+	}
+	
+	/**
+	 * sum items in an array or use special item in the array
+	 *
+	 * @param array[array]	$arr
+	 * @param string			$key
+	 */
+	public static function sum( $arr, $key = null ) 
+	{	
+		if( !is_array( $arr ) ) 
+		{
+			throw new \InvalidArgumentException('CCArr::sum - first argument has to be an array.');
+		}
+		
+		$sum = 0;
+		
+		if ( is_string( $key ) && CCArr::is_multi( $arr ) ) 
+		{
+			$arr = CCArr::pick( $key, $arr );
+		}
+		
+		foreach ( $arr as $item ) 
+		{
+			if ( is_numeric( $item ) ) 
+			{
+				$sum += $item;	
+			}
+		}
+	
+		return $sum;
+	}
+	
+	/**
+	 * get the average of the items 
+	 *
+	 * @param array[array]	$arr
+	 * @param string			$key
+	 */
+	public static function average( $arr, $key = null ) 
+	{
+		if( !is_array( $arr ) ) 
+		{
+			throw new \InvalidArgumentException('CCArr::average - first argunent has to be an array.');
+		}
+		
+		if ( is_string( $key ) && CCArr::is_multi( $arr ) ) 
+		{
+			$arr = CCArr::pick( $key, $arr );
+		}
+		
+		return ( static::sum( $arr ) / count( $arr ) );
+	}
+	
+	/** 
+	 * create an object from an array
+	 *
+	 * @param array		$array
+	 * @return object
+	 */
+	public static function object( $arr ) 
+	{
+		if( !is_array( $arr ) ) 
+		{
+			throw new \InvalidArgumentException("CCArr::object - only arrays can be passed.");
+		}
+	   
+		$object = new \stdClass();
+		
+		if ( !empty( $arr ) ) 
+		{
+			foreach ( $arr as $name => $value) 
+			{
+				if ( is_array( $value ) ) 
+				{
+					$value = static::object( $value );
+				}
+				$object->$name = $value;
+			}
+		}
+		
+		return $object;
+	}
+	
+	/**
+	 * merge arrays recursivly together
+	 *
+	 * @param array 		$array ...
+	 * @return array
+	 */ 
+	public static function merge() 
+	{
+		// get all arguments
+		$arrs = func_get_args();
+		$return = array();
+	
+		foreach ( $arrs as $arr )
+		{
+			if ( !is_array( $arr ) ) 
+			{
+				throw new \InvalidArgumentException('CCArr::merge - all arguments have to be arrays.');
+			}
+	
+			foreach ( $arr as $key => $value ) 
+			{	
+				if ( array_key_exists( $key, $return ) ) 
+				{
+					if ( is_array( $value ) && is_array( $return[$key] ) ) 
+					{
+						$value = static::merge( $return[$key], $value );
+					}
+				}
+				$return[$key] = $value;
+			}
+		}
+		
+		return $return;
+	}
+	
+	/**
+	 * return an item from an array with dottet dimensions
+	 *
+	 * @param string		$key 
+	 * @param array		$arr
+	 * @param mixed		$default
+	 * @return mixed 
+	 */
+	public static function get( $key, $arr, $default = null ) 
+	{
+		if ( isset( $arr[$key] ) ) 
+		{
+			return $arr[$key];
+		}
+		
+		if ( strpos( $key, '.' ) !== false ) 
+		{
+			$kp = explode( '.', $key );
+			
+			switch ( count( $kp ) ) 
+			{
+				case 2:
+				if ( isset( $arr[$kp[0]][$kp[1]] ) ) 
+				{
+					return $arr[$kp[0]][$kp[1]]; 
+				}
+				break;
+				case 3:
+				if ( isset( $arr[$kp[0]][$kp[1]][$kp[2]] ) ) 
+				{
+					return $arr[$kp[0]][$kp[1]][$kp[2]]; 
+				}
+				break;	
+				case 4:
+				if ( isset( $arr[$kp[0]][$kp[1]][$kp[2]][$kp[3]] ) ) 
+				{
+					return $arr[$kp[0]][$kp[1]][$kp[2]][$kp[3]]; 
+				}
+				break;
+				
+				// if there are more then 4 parts loop trough them
+				default:
+					$curr = $arr;
+					foreach( $kp as $k ) 
+					{
+						if ( isset( $curr[$k] ) ) {
+							$curr = $curr[$k];
+						} else { 
+							return $default; 
+						}
+					}
+					return $curr;
+				break;
+			}	
+		}
+		
+		return $default;
+	}
+	
+	/**
+	 * checks if the array has an item with dottet dimensions
+	 *
+	 * @param string		$key 
+	 * @param array		$arr
+	 * @return bool
+	 */
+	public static function has( $key, $arr ) 
+	{	
+		if ( isset( $arr[$key] ) ) 
+		{
+			return true;
+		}
+		
+		if ( strpos( $key, '.' ) !== false ) 
+		{
+			$kp = explode( '.', $key );
+			
+			switch ( count( $kp ) ) {
+				case 2:
+					return isset( $arr[$kp[0]][$kp[1]] ); break;
+				case 3:
+					return isset( $arr[$kp[0]][$kp[1]][$kp[2]] ); break;	
+				case 4:
+					return isset( $arr[$kp[0]][$kp[1]][$kp[2]][$kp[3]] ); break;
+					
+				// if there are more then 4 parts loop trough them
+				default:
+					$curr = $arr;
+					foreach( $kp as $k ) 
+					{
+						if ( isset( $curr[$k] ) ) {
+							$curr = $curr[$k];
+						} else { 
+							return false; 
+						}
+					}
+					return true;
+				break;
+			}
+		}
+		return false;
+	}
+	
+	/**
+	 * sets an item from an array with dottet dimensions
+	 *
+	 * @param string	$key 
+	 * @param mixed		$value
+	 * @param array		$arr
+	 * @return array
+	 */
+	public static function set( $key, $value, &$arr ) 
+	{	
+		if ( strpos( $key, '.' ) === false ) 
+		{
+			$arr[$key] = $value;
+			
+		} 
+		else 
+		{
+			$kp = explode( '.', $key );
+			
+			switch ( count( $kp ) ) 
+			{
+				case 2:
+					$arr[$kp[0]][$kp[1]] = $value; break;
+				case 3:
+					$arr[$kp[0]][$kp[1]][$kp[2]] = $value; break;	
+				case 4:
+					$arr[$kp[0]][$kp[1]][$kp[2]][$kp[3]] = $value; break;
+					
+				// if there are more then 4 parts loop trough them
+				default:
+					$kp = array_reverse( $kp );
+					$curr = $value;
+					
+					foreach( $kp as $k ) 
+					{
+						$curr = array( $k => $curr );
+					}
+					
+					$arr = static::merge( $arr, $curr );
+				break;
+			}
+		}
+		return $arr;
+	}
+	
+	/**
+	 * deletes an item from an array with dottet dimensions
+	 *
+	 * @param string		$key 
+	 * @param array		$arr
+	 * @return void
+	 */
+	public static function delete( $key, &$arr ) 
+	{	
+		if ( isset( $arr[$key] ) ) 
+		{
+			unset( $arr[$key] ); return;
+		}
+		
+		if ( strpos( $key, '.' ) !== false ) 
+		{	
+			$kp = explode( '.', $key );
+			
+			switch ( count( $kp ) ) {
+				case 2:
+					unset( $arr[$kp[0]][$kp[1]] ); return; break;
+				case 3:
+					unset( $arr[$kp[0]][$kp[1]][$kp[2]] ); return; break;
+				case 4:
+					unset( $arr[$kp[0]][$kp[1]][$kp[2]][$kp[3]] ); return; break;
+			}
+		}
+	}
+}

+ 302 - 0
frameworks/PHP/php-clancatsframework/CCF/core/classes/CCAsset.php

@@ -0,0 +1,302 @@
+<?php namespace Core;
+/**
+ * Asset handler 
+ * This is helper your front end stuff like js file, images etc..
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCAsset 
+{	
+	/**
+	 * instance holder
+	 *
+	 * @var array
+	 */
+	protected static $instances = array();
+	
+	/**
+	 * default instance
+	 * 
+	 * @var string
+	 */
+	protected static $_default = 'main';
+	
+	/**
+	 * The macros holder
+	 *
+	 * @var array
+	 */
+	protected static $_macros = array();
+	
+	/**
+	 * CSS Macro - Generates a css stylesheet link
+	 * 
+	 * @param string 			$uri
+	 * @param string 			$holder
+	 * @return string
+	 */
+	protected static function macro_css( $uri, $holder = null )
+	{
+		return '<link type="text/css" rel="stylesheet" href="'.static::uri( $uri, $holder ).'" />';
+	}
+	
+	/**
+	 * JS Macro - Generates a js source link
+	 * 
+	 * @param string 			$uri
+	 * @param string 			$holder
+	 * @return string
+	 */
+	protected static function macro_js( $uri, $holder = null )
+	{
+		return '<script type="text/javascript" src="'.static::uri( $uri, $holder ).'"></script>';
+	}
+	
+	/**
+	 * LESS Macro - Generates a less stylesheet link
+	 * 
+	 * @param string 			$uri
+	 * @param string 			$holder
+	 * @return string
+	 */
+	protected static function macro_less( $uri, $holder = null )
+	{
+		return '<link type="text/css" rel="stylesheet/less" href="'.static::uri( $uri, $holder ).'" />';
+	}
+	
+	/**
+	 * IMG Macro - Generates a less stylesheet link
+	 * 
+	 * @param string 			$uri
+	 * @param string 			$holder
+	 * @return string
+	 */
+	protected static function macro_img( $uri, $holder = null )
+	{
+		return '<img src="'.static::uri( $uri, $holder ).'" />';
+	}
+	
+	/**
+	 * Default Macro - Simply returns the given string
+	 * 
+	 * @param string 			$string
+	 * @return string
+	 */
+	protected static function macro__( $string )
+	{
+		return $string;
+	}
+	
+	/**
+	 * OG Macro - Generates open grapth tags
+	 *
+	 *     // <meta property="og:type" content="video" />
+	 *     CCAsset::og( 'type', 'video' );
+	 *     
+	 *     // <meta property="og:type" content="video" />
+	 *     // <meta property="og:res" content="1080p" />
+	 *     CCAsset::og( array( 'type' => 'video', 'res' => '1080p' ));
+	 * 
+	 * @param array|string 		$tags
+	 * @param string 			$content
+	 * @return string
+	 */
+	protected static function macro_og( $tags, $content = null ) 
+	{
+		if ( !is_array( $tags ) )
+		{
+			$tags = array( $tags => $content );
+		}
+		
+		$buffer = "";
+		
+		foreach( $tags as $key => $content )
+		{
+			$buffer .= '<meta property="og:'.$key.'" content="'.$content.'" />';
+		}
+		
+		return $buffer;
+	}
+	
+	/**
+	 * Register an assets macro 
+	 * 
+	 * @param string					$key
+	 * @param callback|string		$callback
+	 * @return void
+	 */
+	public static function macro( $key, $callback )
+	{	
+		static::$_macros[$key] = $callback; 
+	}
+	
+	/**
+	 * Check for macros and execute them
+	 *
+	 * @param string 	$name
+	 * @param array 		$arguments
+	 * @return mixed
+	 */
+	public static function __callStatic( $name, $arguments )
+	{
+		if ( !array_key_exists( $name, static::$_macros ) )
+		{
+			// we always check if the global class has that marco this way we
+			// can easly extend CCAsset default macros.
+			if ( !method_exists( '\\CCAsset', 'macro_'.$name ) )
+			{
+				throw new \BadMethodCallException( "CCAsset::".$name." - No method or macro found." );
+			}
+			
+			return call_user_func_array( array( '\\CCAsset', 'macro_'.$name ), $arguments );
+		}
+		
+		// if we have a string handle the macro as replacement pattern.
+		if ( is_string( static::$_macros[$name] ) )
+		{
+			// in this case argument 1 is going to be the uri 
+			// and argument 2 the asset holder
+			list( $uri, $holder ) = $arguments;
+			
+			return str_replace( ':uri', static::uri( $uri, $holder ), static::$_macros[$name] );
+		}
+		
+		return call_user_func_array( static::$_macros[$name], $arguments );
+	}
+
+	/** 
+	 * Get the uri to an asset
+	 *
+	 *     // /assets/images/something.jpg
+	 *     CCAsset::uri( 'images/something.jpg' );
+	 *     
+	 *     // /assets/MyTheme/images/something.jpg
+	 *     CCAsset::uri( 'images/logo.png', 'theme' );
+	 *     
+	 *     // will not modify the given url
+	 *     CCAsset::uri( 'http://example.com/images/logo.jpg' );
+	 * 
+	 * @param string		$uri
+	 * @param string		$name 
+	 * @return string
+	 */
+	public static function uri( $uri, $name = null ) 
+	{
+		// when the uri conains a protocol we just return 
+		// the given uri.
+		if ( strpos( $uri, '://' ) === false ) 
+		{	
+			// If the path is relative and not absolute
+			// we prefix the holder path.
+			if ( substr( $uri, 0, 1 ) != '/' ) 
+			{
+				$uri = CCUrl::to( static::holder( $name )->path.$uri );
+			}
+			// When the given uri is absolute we remove the first
+			// slash and let CCUrl do the job.
+			else 
+			{
+				$uri = CCUrl::to( substr( $uri, 1 ) );
+			}
+		}
+		
+		return $uri;
+	}
+
+	/**
+	 * Get an asset holder instance.
+	 *
+	 *     $holder = CCAsset::holder();
+	 *     $holder = CCAsset::holder( 'footer' );
+	 *
+	 * @param string			$name
+	 * @return CCAsset
+	 */
+	public static function holder( $name = null ) 
+	{	
+		if ( !isset( $name ) ) 
+		{
+			$name = static::$_default;
+		}
+		
+		if ( !isset( static::$instances[$name] ) ) 
+		{
+			 static::$instances[$name] = new CCAsset_Holder();
+		}
+		
+		return static::$instances[$name];
+	}
+	
+	/**
+	 * Get all assets of a specific type in a holder and run them 
+	 * trough the maching macros.
+	 *
+	 *     CCAsset::code( 'css' );
+	 *     CCAsset::code( 'js', 'footer' ); 
+	 *
+	 * @param string		$type
+	 * @param string		$name
+	 */
+	public static function code( $type, $name = null ) 
+	{
+		$buffer = "";
+		
+		foreach( static::holder( $name )->get( $type ) as $item )
+		{
+			$buffer .= call_user_func( array( '\\CCAsset', $type ), $item, $name );
+		}
+		
+		return $buffer;
+	}
+	
+	/** 
+	 * Add an asset to the holder. Basically this method checks the 
+	 * file extension to sort them and generate the correct code using the macros.
+	 *
+	 *     CCAsset::add( 'jquery.js' );
+	 *     CCAsset::add( 'style.css' );
+	 *     CCAsset::add( '<script>document.write( "Hello World" );</script>', 'footer' );
+	 *
+	 * @param string			$item
+	 * @return void
+	 */
+	public static function add( $item, $name = null ) 
+	{
+		return static::holder( $name )->add( $item );
+	}
+	
+	/**
+	 * Get all assets by a specific macro / type
+	 *
+	 *     CCAsset::get();
+	 *     CCAsset::get( 'css', 'theme' );
+	 *
+	 * @param string			$type		By passing null all items from all types are returned
+	 * @return array
+	 */
+	public static function get( $type = null, $name = null ) 
+	{
+		return static::holder( $name )->get( $type );
+	}
+	
+	/**
+	 * Clear the asset holder, deletes all contained assets 
+	 * of a special type or if $type is null everything.
+	 *
+	 *     CCAsset::clear();
+	 *     CCAsset::clear( 'css' );
+	 *     CCAsset::clear( 'js', 'footer' );
+	 * 
+	 * @param string 		$type
+	 * @return void
+	 */
+	public static function clear( $type, $name = null ) 
+	{
+		return static::holder( $name )->clear( $type );
+	}	
+}

+ 106 - 0
frameworks/PHP/php-clancatsframework/CCF/core/classes/CCAsset/Holder.php

@@ -0,0 +1,106 @@
+<?php namespace Core;
+/**
+ * This is the Asset holder
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCAsset_Holder
+{	
+	/**
+	 * path modifier
+	 *
+	 * @var string
+	 */
+	public $path = 'assets/';
+	
+	/**
+	 * Content
+	 *
+	 * @var array
+	 */
+	public $assets = array();
+	
+	/** 
+	 * Add an asset to the holder. Basically this method checks the 
+	 * file extension to sort them and generate the correct code using the macros.
+	 *
+	 *     $holder->add( 'jquery.js' );
+	 *     $holder->add( 'style.css' );
+	 *     $holder->add( '<script>document.write( "Hello World" );</script>' );
+	 *
+	 * @param string			$item
+	 * @return void
+	 */
+	public function add( $item ) 
+	{
+		// when the first character is a "smaller than" we simply assume
+		// that a custom tag has been passed and not a filepath
+		if ( strpos( $item, "<" ) !== false ) 
+		{
+			$macro = '_';
+		} else {
+			$macro = CCStr::extension( $item );
+		}
+		
+		if ( !isset( $this->assets[$macro] ) )
+		{
+			$this->assets[$macro] = array();
+		}
+		
+		$this->assets[$macro][] = $item;
+	}
+	
+	/**
+	 * Get all assets by a specific macro / type
+	 *
+	 *     $holder->get();
+	 *     $holder->get( 'css' );
+	 *
+	 * @param string			$type		By passing null all items from all types are returned
+	 * @return array
+	 */
+	public function get( $type = null ) 
+	{
+		if ( is_null( $type ) )
+		{
+			return $this->assets;
+		}
+		
+		if ( !isset( $this->assets[$type] ) )
+		{
+			return array();
+		}
+		
+		return $this->assets[$type];
+	}
+	
+	/**
+	 * Clear the asset holder, deletes all contained assets 
+	 * of a special type or if $type is null everything.
+	 *
+	 *     $holder->clear();
+	 *     $holder->clear( 'js' );
+	 * 
+	 * @param string 		$type
+	 * @return void
+	 */
+	public function clear( $type = null ) 
+	{
+		if ( is_null( $type ) )
+		{
+			$this->assets = array();
+		}
+		else
+		{
+			if ( isset( $this->assets[$type] ) )
+			{
+				$this->assets[$type] = array();
+			}
+		}
+	}
+}

+ 277 - 0
frameworks/PHP/php-clancatsframework/CCF/core/classes/CCCli.php

@@ -0,0 +1,277 @@
+<?php namespace Core;
+/**
+ * Command line interface
+ * Some helpers when using the command line interface.
+ *
+ * FuelPHP helped me alot with this class thanks your awesome framework guys.
+ * Go check it out: http://fuelphp.com
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCCli 
+{
+	
+	/**
+	 * foreground colors
+	 * 
+	 * @var array
+	 */
+	protected static $colors = array(
+		'black'			=> '0;30',
+		'dark_gray'		=> '1;30',
+		'blue'			=> '0;34',
+		'dark_blue'		=> '1;34',
+		'light_blue'		=> '1;34',
+		'green'			=> '0;32',
+		'success'		=> '0;32',
+		'light_green'	=> '1;32',
+		'cyan'			=> '0;36',
+		'info'			=> '0;36',
+		'light_cyan'		=> '1;36',
+		'red'			=> '0;31',
+		'error'			=> '0;31',
+		'light_red'		=> '1;31',
+		'purple'			=> '0;35',
+		'light_purple'	=> '1;35',
+		'light_yellow'	=> '0;33',
+		'yellow'			=> '1;33',
+		'warning'		=> '1;33',
+		'light_gray'		=> '0;37',
+		'white'			=> '1;37',
+	);
+	
+	/**
+	 * background colors
+	 * 
+	 * @var array
+	 */
+	protected static $background_colors = array(
+		'black'			=> '40',
+		'red'			=> '41',
+		'green'			=> '42',
+		'yellow'			=> '43',
+		'blue'			=> '44',
+		'magenta'		=> '45',
+		'cyan'			=> '46',
+		'light_gray'		=> '47',
+	);
+	
+	/**
+	 * Check if php is running on windows cmd
+	 * 
+	 * @return bool
+	 */
+ 	public static function is_windows() 
+ 	{ 
+ 		return strtolower( substr( PHP_OS, 0, 3 ) ) === 'win';
+ 	}
+	
+	/**
+	 * Write to the standard output device
+	 * 
+	 * @param string		$str
+	 * @return void
+	 */
+	public static function write( $str, $color = null, $background = null ) 
+	{
+		if ( !is_null( $color ) ) 
+		{
+			$str = static::color( $str, $color, $background );
+		}
+		
+		fwrite( STDOUT, $str );
+	}
+	
+	/**
+	 * Write to the standard output device with an end of line.
+	 * 
+	 * @param string		$str
+	 * @return void
+	 */
+	public static function line( $str, $color = null, $background = null ) 
+	{
+		if ( is_array( $str ) ) 
+		{
+			$str = implode( PHP_EOL, $str );
+		}
+		
+		static::write( $str.PHP_EOL, $color, $background );
+	}
+	
+	/**
+	 * Display a success message
+	 *
+	 * @param string		$str
+	 * @param string		$prefix
+	 * @return void
+	 */ 
+	public static function success( $str, $prefix = 'success' )
+	{
+		static::line( static::color( $prefix, 'green' ).' '.$str );
+	}
+	
+	/**
+	 * Display an error message
+	 *
+	 * @param string		$str
+	 * @param string		$prefix
+	 * @return void
+	 */ 
+	public static function error( $str, $prefix = 'failure' )
+	{
+		static::line( static::color( $prefix, 'red' ).' '.$str );
+	}
+	
+	/**
+	 * Display a warning message
+	 *
+	 * @param string		$str
+	 * @param string		$prefix
+	 * @return void
+	 */ 
+	public static function warning( $str, $prefix = 'warning' )
+	{
+		static::line( static::color( $prefix, 'yellow' ).' '.$str );
+	}
+	
+	/**
+	 * Display an info message
+	 *
+	 * @param string		$str
+	 * @param string		$prefix
+	 * @return void
+	 */ 
+	public static function info( $str, $prefix = 'info' )
+	{
+		static::line( static::color( $prefix, 'cyan' ).' '.$str );
+	}
+	
+	/**
+	 * Write empty lines 
+	 * 
+	 * @param int		$count
+	 * @return void
+	 */
+	public static function new_line( $count = 1 ) 
+	{
+		for( $i=0; $i<$count; $i++ ) 
+		{
+			static::line( '' );
+		}
+	}
+	
+	/**
+	 * Add color to string
+	 * Check the available colors at the top $colors & $background_colors
+	 * 
+	 * @param string 	$str
+	 * @param string		$color
+	 * @param string		$background
+	 * @return string
+	 */
+	public static function color( $str, $color, $background = null ) 
+	{	
+		// ANSI escape codes are not available on Windows CMD
+		// So we return the string unmodified
+		if ( static::is_windows() ) 
+		{
+			return $str;
+		}
+		
+		$out = "\033[".static::$colors[$color]."m";
+		
+		if ( !is_null( $background ) ) 
+		{
+			$out .= "\033[".static::$background_colors[$background]."m";
+		}
+		
+		return $out.$str."\033[0m";
+	}
+	
+	/**
+	 * Clear the screen
+	 *
+	 * @return void
+	 */
+	public static function clear() 
+	{
+		if ( static::is_windows() ) 
+		{
+			static::new_line(40); return;
+		}
+		
+		static::write( chr(27)."[H".chr(27)."[2J" );
+	}
+	
+	/**
+	 * Get a bool value from the user
+	 * This will write the question down and waiting for a yes or no.
+	 *
+	 * When strict is true, it wil only accept yes or no. And not y, n, jep etc.
+	 *
+	 * @param string		$question
+	 * @param bool		$strict
+	 * @return bool
+	 */
+	public static function confirm( $question, $strict = false ) 
+	{
+		$question .= ' [yes/no]: ';
+		
+		if ( $strict ) 
+		{
+			do 
+			{
+				$res = strtolower( static::read( $question ) );
+			} 
+			while ( $res !== 'yes' && $res !== 'no' );
+			
+			return ( $res == 'yes' ) ? true : false;
+		}
+		
+		do 
+		{
+			$res = strtolower( static::read( $question ) );
+		} 
+		while ( empty( $res ) );
+		
+		$positives = array(
+			'yes', 'y', 'ya', 'ye', 'yeah', 'yup',
+			'jep', 'jap', 'ja', 
+			'si', 'sim',
+			'true',
+			'hai',
+			'oui',
+			'no problemo',
+		);
+		
+		return in_array( $res, $positives );
+	}
+
+	/**
+	 * Read from comman line 
+	 * Because windows does not support readline we use normal fgets in that case.
+	 * 
+	 * @param string	 		$prefix		The prefix will be printet before the user can respond.
+	 * @return string
+	 */
+	public static function read( $prefix = '' ) 
+	{
+		if ( !static::is_windows() ) 
+		{	
+			$line = readline( $prefix ); 
+			readline_add_history( $line );
+		} 
+		else 
+		{	
+			static::write( $prefix );
+			$line = trim( fgets( STDIN ) );
+		}
+		
+		return $line;
+	}
+}

+ 69 - 0
frameworks/PHP/php-clancatsframework/CCF/core/classes/CCColor.php

@@ -0,0 +1,69 @@
+<?php namespace Core;
+/**
+ * ClanCats color helper
+ *
+ * @package 			ClanCats-Framework
+ * @author     		Mario Döring <[email protected]>
+ * @version 			0.5
+ * @copyright 		2010 - 2013 ClanCats GmbH 
+ *
+ */
+class CCColor 
+{
+	/**
+	 * parse an color 
+	 * 
+	 * @param mixed 		$color 
+	 */
+	public static function create( $color ) 
+	{
+		// our return
+		$rgb = array();
+
+		if ( is_array( $color ) ) 
+		{
+			$color = array_values( $color );
+			$rgb[0] = $color[0];
+			$rgb[1] = $color[1];
+			$rgb[2] = $color[2];
+		}
+		// parse hex color 
+		elseif ( is_string( $color ) && substr( $color, 0, 1 ) == '#' ) 
+		{
+			$color = substr( $color, 1 );
+
+			if( strlen( $color ) == 3 ) 
+			{
+				$rgb[0] = hexdec( substr( $color, 0, 1 ) . substr( $color, 0, 1 ) );
+				$rgb[1] = hexdec( substr( $color, 1, 1 ) . substr( $color, 1, 1 ) );
+				$rgb[2] = hexdec( substr( $color, 2, 1 ) . substr( $color, 2, 1 ) );
+			}
+			elseif( strlen( $color ) == 6 ) 
+			{
+				$rgb[0] = hexdec( substr( $color, 0, 2 ) );
+				$rgb[1] = hexdec( substr( $color, 2, 2 ) );
+				$rgb[2] = hexdec( substr( $color, 4, 2 ) );
+			}
+		}
+		// could not be parsed 
+		else 
+		{
+			return false;
+		}
+
+		return new static( $rgb );
+	}
+
+	/*
+	 * our color holder
+	 */ 
+	public $RGB = array( 0, 0, 0 );
+
+	/**
+	 * init a new color instance
+	 */
+	public function __construct( $rgb ) 
+	{
+		$this->RGB = $rgb;
+	}
+}

+ 157 - 0
frameworks/PHP/php-clancatsframework/CCF/core/classes/CCConfig.php

@@ -0,0 +1,157 @@
+<?php namespace Core;
+/**
+ * Configuration handler
+ * load, access and create configuration files
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCConfig extends CCDataObject
+{	
+	/**
+	 * instance holder
+	 *
+	 * @var array
+	 */
+	protected static $instances = array();
+	
+	/**
+	 * Create a configuration instance
+	 *
+	 * @param string			$name
+	 * @return CCConfig
+	 */
+	public static function create( $name = null, $driver = 'file' ) 
+	{
+		if ( is_null( $name ) ) 
+		{
+			return new static( null, $driver );
+		}
+		
+		if ( !isset( static::$instances[$name] ) ) 
+		{
+			static::$instances[$name] = new static( $name, $driver );
+		}
+		
+		return static::$instances[$name];
+	}
+	
+	/*
+	 * current instance name
+	 */
+	protected $_instance_name = null;
+	
+	/*
+	 * current instance name
+	 */
+	protected $_driver = null;
+	
+	/**
+	 * Constructor  
+	 *
+	 * @param string		$name		The instance name used for writing
+	 * @param string		$driver	
+	 * @return void
+	 */
+	public function __construct( $name = null, $driver = 'file' ) 
+	{	
+		$this->_instance_name = $name;	
+		$this->driver( $driver );
+		
+		if ( !is_null( $name ) ) 
+		{
+			$this->read( $name );
+		}
+	}
+	
+	/**
+	 * Set the configuration dirver
+	 *
+	 * @param string			$driver
+	 * @return void
+	 */
+	public function driver( $driver )
+	{
+		$driver = CCCORE_NAMESPACE.'\\'.'CCConfig_'.ucfirst( $driver );
+		
+		if ( !class_exists( $driver ) ) 
+		{
+			throw new \InvalidArgumentException("CCConfig - Invalid driver '".$driver."'");
+		}
+		
+		$this->_driver = new $driver;
+	}
+	
+	/**
+	 * Name getter and setter
+	 *
+	 * @param string			$name
+	 * @return string
+	 */
+	public function name( $name = null )
+	{
+		if ( is_null( $name ) )
+		{
+			return $this->_instance_name;
+		}
+		
+		$this->_instance_name = $name;
+	}
+	
+	/**
+	 * Load a configuration file
+	 * This will load a configuration file and assign the data
+	 *
+	 * @param string			$name
+	 * @return void
+	 */
+	public function read( $name )
+	{
+		$this->_data = $this->_driver->read( $name );
+	}
+	
+	/**
+	 * save a configuration file
+	 * this method overwrites your configuration file!!
+	 *
+	 * @param string 	$name
+	 * @return bool
+	 */
+	public function write( $driver = null ) 
+	{	
+		if ( empty( $this->_instance_name ) ) 
+		{
+			throw new CCException("CCConfig::write - configuration name is missing.");
+		}
+		
+		// set the dirver
+		if ( !is_null( $driver ) )
+		{
+			$this->driver( $driver );
+		}
+		
+		// run write
+		$this->_driver->write( $this->_instance_name, $this->_data );
+	}
+	
+	/**
+	 * Delete the entire configuration
+	 * Attention with this one he can be evil!
+	 *
+	 * @param string			$name
+	 * @return void
+	 */
+	public function _delete()
+	{
+		if ( empty( $this->_instance_name ) ) 
+		{
+			throw new CCException("CCConfig::write - configuration name is missing.");
+		}
+		
+		return $this->_driver->delete( $this->_instance_name );
+	}
+}

+ 59 - 0
frameworks/PHP/php-clancatsframework/CCF/core/classes/CCConfig/Array.php

@@ -0,0 +1,59 @@
+<?php namespace Core;
+/**
+ * Configuration Driver
+ ** 
+ *
+ * @package		ClanCatsFramework
+ * @author		Mario Döring <[email protected]>
+ * @version		2.0
+ * @copyright 	2010 - 2014 ClanCats GmbH
+ *
+ */
+class CCConfig_Array implements CCConfig_Driver 
+{	
+	/**
+	 * Data holder
+	 */
+	protected static $data = array();
+	
+	/**
+	 * Read the configuration data 
+	 * Get the configuration data and return them as array
+	 *
+	 * @param string		$name
+	 * @return array
+	 */
+	public function read( $name )
+	{
+		if ( array_key_exists( $name, static::$data ) )
+		{
+			return static::$data[$name];
+		}
+		return array();
+	}
+	
+	/**
+	 * Write the configuration data
+	 *
+	 * @param string		$file
+	 * @return void
+	 */
+	public function write( $name, $data )
+	{
+		static::$data[$name] = $data;
+	}
+	
+	/**
+	 * delete the configuration data
+	 *
+	 * @param string		$file
+	 * @return void
+	 */
+	public function delete( $name )
+	{
+		if ( array_key_exists( $name, static::$data ) )
+		{
+			unset( static::$data[$name] );
+		}
+	}
+}

Some files were not shown because too many files changed in this diff