therselman 7 years ago
parent
commit
436d71909a
  1. 43
      public/.htaccess
  2. BIN
      public/favicon.ico
  3. 4
      public/index.php
  4. 99
      src/config/container.php
  5. 22
      src/config/demo.php
  6. 31
      src/config/my_server_name.php
  7. 57
      src/config/routes.php
  8. 14
      src/controllers/AdminController.php
  9. 28
      src/controllers/LoginController.php
  10. 17
      src/controllers/robots.txt.php
  11. 22
      src/controllers/sitemap.xml.php
  12. 0
      src/elements/404/init.php
  13. 6
      src/elements/404/view.php
  14. 5
      src/elements/admin/index/init.php
  15. 3
      src/elements/admin/index/view.php
  16. 0
      src/elements/footer/init.php
  17. 0
      src/elements/footer/view.php
  18. 6
      src/elements/header/init.php
  19. 0
      src/elements/header/view.php
  20. 6
      src/elements/index/init.php
  21. 0
      src/elements/index/view.php
  22. 33
      src/elements/login/init.php
  23. 199
      src/elements/login/post.php
  24. 68
      src/elements/login/view.php
  25. 60
      src/layouts/admin.php
  26. 60
      src/layouts/public.php

43
public/.htaccess

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
Order allow,deny
Allow from all
Require all granted
SetEnv COMPUTERNAME demo
Options -Indexes
IndexIgnore */*
php_value error_reporting -1
php_flag display_errors On
php_value max_execution_time 0
php_value default_charset "utf-8"
php_value session.use_trans_sid 0
php_value session.gc_maxlifetime 1440
php_value session.hash_function 0
php_value session.hash_bits_per_character 4
php_flag output_buffering Off
php_flag zlib.output_compression On
php_flag magic_quotes_gpc Off
php_flag register_globals Off
php_value sendmail_from no-reply@demo.com
php_value memory_limit 64M
php_value post_max_size 500M
php_value upload_max_filesize 500M
<Files *.log>
Order Deny,Allow
Deny from all
</Files>
<IfModule mod_headers.c>
Header unset ETag
<filesMatch "\.(js|css)$">
Header set Cache-Control "max-age=31536000, public"
</filesMatch>
</IfModule>
FileETag None
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /index.php [L]

BIN
public/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

4
public/index.php

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
<?php
require '../vendor/autoload.php';
(new \Twister\Container(require '../src/config/container.php'))->execute();

99
src/config/container.php

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
<?php
/**
* Pre-configured default properties for the master Container
* This is the default property array of the main Container class. It features some common properties, object instances and factory methods.
* The Container object itself essentially takes the place of any `global` variables. eg. configuration files, database connection etc.
* The great thing about this technique is that ALL these objects/properties are lazy loaded! ie. they are not instantiated until we need/call them!
* This technique is somewhat siliar to this pseudo-code: 'new Request(new DB(new Config()))' where 'config' will be resolved first!
* Maybe it can also be explained like this: '$c->request($c->db($c->config))' ... but this is just pseudo-code!
* These are anonymous functions that get called when one of the Container properties are accessed
* eg. A statement like `$c->db->query(...)`; will call the 'db' function handler code below (when it tries to resolve `$c->db` and automatically builds the `db` instance.
* The code will create the object instance internally,
* then assign the new instance value to the corresponding (same) location in the Container class at the end of the function,
* effectively overwriting/replacing the array member (anonymous function) with the new object instance,
* essentially building something like a singleton object instance,
* since calling `$c->db` the next time will return the new object and not the original function.
* If you require multiple instances, you can just skip assigning the value at the end and just return a new object each time eg. 'return $obj;' instead of 'return $c->obj = $obj;'
* If you require multiple arguments, like `$c->db('primary_conn')`, or `$c->myFunc($param1)`
* then you can just create a function like `function($c, $param1)`,
* the container object itself ($this) is always inserted as the first parameter in the parameter list before the function is called (with array_unshift()).
* Each one of these `properties` are called by the __get, __set and __call magic methods in the Container class!
* The `__get` method of the Container is actually called when we ask for '$c->db' (eg. $c->__get('db')) ... but the internal value of 'db' is actually the function below.
* Our `__get` method inside the Container class will recognize that the property is actually a callable function,
* and proceed to actually CALL the function, passing it the `$this` (container) value as the first parameter of the function call
*/
return [
'execute' => function($c)
{
/**
* Register an Exception Handler
*/
/**
* The Twister Exception Handler is just a slightly improved version of the default internal PHP handler
* new \Twister\ExceptionHandler();
* or use Whoops below!
*/
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
// $whoops->pushHandler(new \Whoops\Handler\JsonResponseHandler);
$whoops->register();
$c->request->execute_route();
},
'config' => function($c)
{
$config = require __DIR__ . '/' . strtolower(getenv('COMPUTERNAME')) . '.php';
$c->is_debug = $c->isDebug = $config['debug']; // shorthand
return $c->config = &$config;
},
'db' => function($c)
{
$dbc = $c->config['db'];
mysqli_report( MYSQLI_REPORT_ALL );
try {
$db = new DB($dbc['host'], $dbc['username'], $dbc['password'], $dbc['schema'], $dbc['port']);
} catch (mysqli_sql_exception $e) {
header('HTTP/1.1 503 Service Temporarily Unavailable');
header('Status: 503 Service Temporarily Unavailable');
header('Retry-After: 7200'); // seconds
die('<b>Website under scheduled maintenance!</b><br />' .
'We are aware of the situation and apologize for the inconvenience!<br />' .
'Normal operation will resume shortly, please be patient and try again later!<br />' . ($c->is_debug ? mysqli_connect_error() : null));
}
$db->set_charset($dbc['charset']);
$db->real_query('SET NAMES ' . $dbc['charset'] . ' COLLATE ' . $dbc['collation']);
return $c->db = &$db;
},
/**
* This Request object will instantiate the 'db' object internally, because the container is Injected into the Request object constructor,
* it includes the 'db' handler above, so when Request calls '$this->container->db->query(...)';
* it will (unknowingly) instantiate the 'db' object when the container tries to resolve the 'db' handler above,
* the 'db' handler is the function above, which returns the new 'db' object.
* Another way to do this is use a static variable inside the function, but then we have the unecessary function call overhead.
* Upon executing any 'db' queries, the 'db' object is automatically instantiated by the 'db' code above.
* Code like: `$db = new DB(...);` is never actually called anywhere else in code.
*/
'request' => function($c)
{
return $c->request = new Request($c);
},
'session' => function($c)
{
return $c->session = new Session($c->db);
/* // alternative: check if HTTPS is enabled
if ($c->request->is_https)
return $c->session = new Session($c->db);
else
return $c->session = new Session($c->db);
*/
},
'user' => function($c)
{
return $c->user = new User($c);
}
];

22
src/config/demo.php

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
<?php
/**
* This config file overwrites values in my_server_name.php
* my_server_name.php is not loaded in code except here
* We load config files by lowercase(COMPUTERNAME).php
* The COMPUTERNAME value has been set to 'demo' in .htaccess
* Change the `my_server_name` name to your server's name
*/
return array_merge(require __DIR__ . '/my_server_name.php',
[
'debug' => true,
'db' => [ 'host' => '127.0.0.1',
'username' => 'root',
'password' => '(-Y${K_c,Hg*3H|E2nu^XT?R3>68!@m:U]5eM&2#',
'schema' => 'fcm',
'port' => '33306',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'debug' => true
]
]);

31
src/config/my_server_name.php

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
<?php
/**
* This `my_server_name.php` file is not loaded directly in code, except in demo.php
* 'demo' represents the COMPUTERNAME value of a dev machine
* So config files are loaded by lowercase(COMPUTERNAME).php,
* which then loads and overwrites these default values
* For example, here we set 'debug' to false, but in our 'dev' machines we overwrite it
* Also, our dev machine(s) can overwrite the database connection values etc.
*/
return [
'debug' => false,
'maintenance' => false,
'email_from' => 'DEMO <noreply@example.com>',
'charset' => 'UTF-8',
'title' => 'Demo Application',
'lang' => 'en',
'languages' => ['en'],
'db' => [ 'host' => '127.0.0.1',
'username' => 'root',
'password' => 'XR7mswQKfgsgdfhgregGEzAedxYUDqk6iHBCtI3pcN',
'schema' => 'demo',
'port' => '3306',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci'
],
'google' => [ 'places' => '34fgdrrdg343t4dfgdfg34tdfsdfghg3q4tghert', // Google Places API server key ... SECRET!!!
'recaptcha-site' => 'hj345wehjqwereghjj56io54unety354khngdrg3', // `Use this in the HTML code your site serves to users.`
'recaptcha-secret' => 'hj345wehjqwereghjj56io54unety354khngdrg3' // `Use this for communication between your site and Google. Be sure to keep it a secret.`
]
];

57
src/config/routes.php

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
<?php // expressive, fast, flexible
return [
'patterns' => [ // official named patterns
'alnum' => '[A-Za-z0-9]',
'word' => '[A-Za-z0-9_]',
'alpha' => '[A-Za-z]',
'digit' => '[0-9]',
'lower' => '[a-z]',
'xdigit' => '[A-Fa-f0-9]',
// common
'any' => '[^/]+',
'string' => '[A-Za-z0-9_-]+',
'id' => '[0-9]+',
'int' => '[0-9]+',
'num' => '[0-9]+',
'i' => '[0-9]+',
'd' => '[0-9]',
'x' => '[A-Fa-f0-9]',
'l' => '[a-z]',
'u' => '[A-Z]',
'U' => '[A-Z]',
'hex' => '[A-Fa-f0-9]',
'date' => '\d{4}-\d{2}-\d{2}',
'day' => '0[1-9]|[12][0-9]|3[01]',
'month' => '0[1-9]|1[012]',
'year' => '[12][0-9]{3}',
'uuid' => '[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}',
// 'action' => 'index|show|add|create|edit|update|remove|del|delete|view|item',
],
'routes' => [ '' => [ null, '/', function(Container $c) { (new Response($c, 'public', 'index'))->render(); }, 'home' ],
'admin' => [ [ 'GET', '', 'AdminController::indexAction', 'admin' ],
[ 'GET', 'asset-groups', 'AdminController::assetGroupsAction', 'admin_asset_groups' ],
[ 'POST', 'asset-groups', 'AdminController::assetGroupsPostAction', 'admin_asset_groups_post' ],
[ 'GET', 'asset-servers', 'AdminController::assetServersAction', 'admin_asset_servers' ],
[ 'GET', 'assets', 'AdminController::assetsAction', 'admin_assets' ],
[ 'GET', 'asset/{id}', 'AdminController::assetAction', 'admin_asset' ],
[ 'GET', 'article/{id}', 'AdminController::articleAction', 'admin_article' ],
[ 'GET', 'articles', 'AdminController::articlesAction', 'admin_articles' ],
[ 'GET', 'languages', 'AdminController::languagesAction', 'admin_languages' ],
[ 'GET', 'club/{id}', 'AdminController::clubAction', 'admin_club' ],
],
'login' => [ null, null, 'LoginController::loginAction', 'login' ],
'register' => [ null, null, 'LoginController::registerAction', 'register' ],
'forgot-password' => [ null, null, 'LoginController::forgotPasswordAction', 'forgot_password' ],
'resend-verification' => [ null, null, 'LoginController::resendVerificationAction', 'resend_verification' ],
'logout' => [ null, null, 'LoginController::logoutAction', 'logout' ],
'robots.txt' => 'robots.txt',
'sitemap.xml' => 'sitemap.xml'
],
404 => function(Container $c) { (new Response($c, 'public', '404'))->render(); }
];

14
src/controllers/AdminController.php

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
<?php
class AdminController extends Controller
{
function indexAction()
{
}
function clubAction($id)
{
}
}

28
src/controllers/LoginController.php

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
<?php
/**
* This login controller does NOT extend the base/abstract Controller class
* For an example of one that does, look at the AdminController
* The router builds these action handlers (class methods) parameter lists dynamically
* So in the first action, I added DB $db (current database connection) (which is not needed inside the function, just an example)
* And the registerAction() asks for a non-existant parameter
* These functions can also receive parameters from the $_GET and $_POST arrays.
* eg. /search?q=term%20term => searchAction($q)
* value of $q above will be filled with $_GET['q'] value
*/
class LoginController
{
static function loginAction(DB $db, Container $c)
{
if ($c->request->isGet())
(new Response($c, 'public', 'login'))->render();
else
throw new \Exception('Process login action here ...');
}
static function registerAction(Container $c, $non_existing_param1 = null)
{
(new Response($c, 'public', 'register'))->render();
}
}

17
src/controllers/robots.txt.php

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
<?php
/**
* Example
*/
return function( Container $c )
{
header('Content-Type: text/plain');
?>
User-agent: *
Allow: /
Disallow: /admin/
Sitemap: http://<?php echo $c->request->uri->authority; ?>/sitemap.xml
<?php
};

22
src/controllers/sitemap.xml.php

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
<?php
/**
* Example
*/
return function( Container $c )
{
header('Content-type: text/xml');
?>
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>http://example.com/</loc>
<lastmod>2006-11-18</lastmod>
<changefreq>daily</changefreq>
<priority>0.8</priority>
</url>
</urlset>
<?php
};

0
src/elements/404/init.php

6
src/elements/404/view.php

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
Swigert: "Okay, Houston, we've had a problem here."<br />
Lousma: "This is Houston. Say again, please."<br />
Lovell: "Uh, Houston, we've had a problem."<br />
Lovell: "We've had a <b>404 Page Not Found</b>."<br />
Lousma: "Roger, 404 Not Found."<br />
Lousma: "Okay, stand by, Thirteen, we're looking at it."<br />

5
src/elements/admin/index/init.php

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
<?php
// if (empty($_SESSION['id'])) redirect(request::build_url(array('scheme' => 'https', 'path' => '/login')), array('message' => 'invalid permissions', 'next' => '/admin/'));
// if (empty($_SESSION['id'])) redirect(request::build_url('https'), 'invalid-permissions');
if (empty($_SESSION['id'])) request::redirect('https', '/login', ['message' => 'invalid permissions', 'next' => '/admin/']);

3
src/elements/admin/index/view.php

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
<?php
echo '<div>admin index view</div>';

0
src/elements/footer/init.php

0
src/elements/footer/view.php

6
src/elements/header/init.php

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
<?php
return function($response)
{
};

0
src/elements/header/view.php

6
src/elements/index/init.php

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
<?php
return function($response)
{
};

0
src/elements/index/view.php

33
src/elements/login/init.php

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
<?php
return function($response)
{
//$user = $response->container->user;
//echo $response->container->request->uri->getLeftPart(Uri::PARTIAL_AUTHORITY) . '/';
//echo $response->container->request->uri->getLeftPart(Uri::PARTIAL_PATH) . '/mdfg';
//echo $response->container->request->uri->withScheme(null);
if ( ! $response->container->request->is_https) // || ! isset($_COOKIE[session_name()])
$response->container->request->redirect('https');
$response->scripts['recaptcha'] = 'https://www.google.com/recaptcha/api.js';
// PROBLEM: if we only create session when the session cookie is set, it's only set (retrieved) on the SECOND page view!
// What I mean is, if you go immediately to the login page (no other page views), it will NOT have a session cookie stored yet!
// Actually, the cookie is stored on the client side, but it's not retrieveable yet, until the second page view!?!?
// Therefore, this session challenge will not be stored in the database if we first check to see if the cookie exists!?!?
// Maybe we should `fake` it on this page only, by manually setting the session cookie, so when the page ends, and the session must write,
// it will write the challenge. ie. We need to manually set `$_COOKIE[session_name()] = session_create_id();`
$_SESSION['challenge'] = md5(uniqid(microtime().mt_rand(), true)); // http://php.net/manual/en/function.mcrypt-create-iv.php
// HACK
// We might need to do this!?!?
// What we are doing here is CREATING a cookie that didn't exist before.
// ALTERNATIVE: We REDIRECT back to this page if the cookie didn't exist before!
// This `might` be necessary on this page only, to FORCE the session_write_close() function to write the session data (which includes the `challenge`) to the database!
$_COOKIE[session_name()] = session_id();
};

199
src/elements/login/post.php

@ -0,0 +1,199 @@ @@ -0,0 +1,199 @@
<?php
$email = (string) get('email');
$password = (string) get('password');
$challenge = (string) get('challenge');
$next = (string) get('next'); // get('next', '', false);
$persistent = (bool) get('persistent');
$response = (string) get('g-recaptcha-response');
/*
// DEBUGGING
print_pre('');
var_dump($_POST);
print_pre('');
var_dump($_SESSION);
print_pre('');
var_dump($_COOKIE);
print_pre('');
var_dump(session_id());
print_pre('');
var_dump($challenge);
*/
$from = '/login';
// Used in login & register scripts
function delay($msg)
{
global $next, $from;
// unset($_SESSION['challenge']); // should be destroyed with session_destroy() below!
session_regenerate_id(true);
// session_write_close(); // was originally using session_regenerate_id(true) + session_write_close() ... changed to session_regenerate_id(true) + session_destroy()
session_destroy();
db::conn()->close();
sleep(3);
redirect($from, array('message' => $msg, 'next' => $next));
}
//print_r($_POST);
//print_r($_SESSION);
if (empty($_SESSION['challenge']) || $challenge != $_SESSION['challenge']) delay('login-failed1');
if (empty($email)) delay('login-failed2');
if (empty($password)) delay('login-failed3');
if (empty($response)) delay('login-failed4');
//
// Verify reCAPTCHA
//
// https://www.google.com/recaptcha/admin#site/319890180?setup
// https://developers.google.com/recaptcha/docs/verify
require CODE_PATH . 'lib/curl.php';
$curl = new curl();
$curl->post('https://www.google.com/recaptcha/api/siteverify', 'secret=' . env::get('google')['recaptcha-secret'] . '&response=' . $response, array(CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_SSL_VERIFYPEER => 0));
//curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0); // I needed these to pass my self signed certificate. cURL complains about unable to verify local certificate or something!
//curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
if ($curl->response === false && env::isDebug())
{
var_dump($curl->response);
var_dump($curl->error());
var_dump($curl->errno());
var_dump($curl->info);
}
if ($curl->response === false)
delay('login-captcha-failed');
$response = json_decode($curl->response, true);
if (!isset($response['success']) || $response['success'] === false) // https://developers.google.com/recaptcha/docs/verify
delay('login-captcha-failed');
$user = db::lookup('SELECT id, email_verified, password, salt FROM users WHERE email_hash = 0x' . md5($email) . ' LIMIT 1');
if (empty($user) || hash_hmac('sha256', $password, $user['salt'], true) !== $user['password'])
delay('login-failed6');
unset($_SESSION['challenge']);
//var_dump($_COOKIE);
//var_dump($_SESSION);
/*
// Original code in session class
static function login($user_id, $persistent)
{
session_regenerate_id(true);
setcookie(session_name(), session_id(), $persistent ? 0x7fffffff : 0, '/');
$_SESSION['id'] = $user_id;
self::$_db->real_query('INSERT INTO sessions (id, timestamp, persistent, user_id, data) VALUES (0x' . session_id() . ', UNIX_TIMESTAMP(), ' . ($persistent ? 'UNIX_TIMESTAMP(), ' : '0, ') . $user_id . ', "")');
}
// call the function
session::login($user['id'], $persistent);
*/
// We do almost everything here, because I don't want to 'polute' the session class with functionality that's only called/used here!
session_regenerate_id(true);
//var_dump($_COOKIE);
//var_dump($_SESSION);
// This cookie is used to `force` (redirect) the browser to the HTTPS url of ALL pages due to a possible session timeout!
setrawcookie('HTTPS_ONLY', time(), 0x7fffffff, '/');
setrawcookie(session_name(), session_id(), $persistent ? 0x7fffffff : 0, '/', null, true, true); // $_SERVER["HTTP_HOST"] || null ??? ... This ALSO ensures that the session cookie will ONLY be sent over HTTPS!
$_SESSION['id'] = $user['id'];
// session::login($user['id'], $persistent); // This is the only thing we do in the session class, because it requires the session's DB connection ... HOW RETARDED!
//var_dump($_COOKIE);
//var_dump($_SESSION);
// We MUST lock the `sessions` table, just in-case the session garbage collector runs between the scripts and deletes some extra sessions!
db::real_query('LOCK TABLES sessions WRITE, user_sessions WRITE, user_session_history WRITE');
// ARCHIVE USER SESSIONS: `user_sessions` => `user_session_history`
db::real_query('INSERT INTO user_session_history (id, user_id, ip, cc, agent_id, forwarded_for_id, via_id, created, modified, last_request, requests, page_views, time_on_site, persistent) SELECT id, user_id, ip, cc, agent_id, forwarded_for_id, via_id, created, modified, last_request, requests, page_views, time_on_site, persistent FROM user_sessions WHERE id NOT IN (SELECT id FROM sessions)');
db::real_query('DELETE FROM user_sessions WHERE id NOT IN (SELECT id FROM sessions)');
// Create new session
if ($persistent) session::create_persistent_session(); // NEW!
session_write_close(); // might as well do this while we have a table lock !?!?
db::real_query('UNLOCK TABLES'); // I think it's important to unlock these tables early so other scripts can continue to execute !?!? However, this runs so infrequently it shouldn't be an issue!?!?
// Create a new entry in `user_sessions` ... most of the values can be empty, because we will UPDATE them on each page view!
db::real_query('INSERT INTO user_sessions (id, user_id, ip, cc, agent_id, forwarded_for_id, via_id, created, modified, last_request, requests, page_views, time_on_site, persistent) VALUES (0x' . session_id() . ',' . $user['id'] . ', 0x' . request::$ip2hex . ', "' . request::$cc . '", ' . request::$agent_id . ', ' . request::$forwarded_for_id . ', ' . request::$via_id . ', UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 1, 1, 0, ' . (int) $persistent . ')');
// ORIGINAL
/*
$sql = 'UPDATE users SET ' .
'previous_online = last_online,' .
'last_login = NOW(),' .
'last_online = NOW(),' .
//'last_domain_id = ' . $GLOBALS['FW']['domain']['id'] . ',' .
'locale = "' . $GLOBALS['FW']['locale'] . '",' .
'logins = logins + 1,' .
'ip = @ip,' .
'cc = @cc,' .
'for_id = ' . $logs['forwarder_id'] . ',' .
'via_id = ' . $logs['proxy_id'] . ',' .
'agent_id = ' . $logs['agent_id'] . ',' .
'referer_id = ' . $logs['referer_id'] . ',' .
'language_id = ' . $logs['language_id'] .
' WHERE id = ' . $_SESSION['id'];
*/
db::real_query('UPDATE users SET last_login = UNIX_TIMESTAMP(), logins = logins + 1 WHERE id = ' . $user['id']);
// session_write_close(); // moved to the session table lock above, can be moved back without side-effects!
db::close();
// ORIGINAL
// redirect('http://' . $fqdn . $next, array('error' => &$errors, 'warning' => &$warnings, 'message' => &$messages));
redirect(empty($next) ? '/dashboard/' : $next, array('message' => 'login-welcome'));
/*
// Generate the first password ...
$salt = microtime();
echo md5($salt) . '<br>';
echo hash_hmac('sha256', 'abc123', md5($salt, true)) . '<br>';
UPDATE users SET email_hash = UNHEX(MD5(email)), password = 0x9f37ce72aaad946e3a98b9dc6126e7c39855600a9d063058dfe79084cbf3a3e4, salt = 0x4c06ff520bc5198942657313a153ec09;
exit;
*/
/*
// ORIGINAL
$this->update_user_logs();
session_id(md5(uniqid(microtime().mt_rand(),true)));
//setrawcookie(session_name(), session_id(), 0x7fffffff, '/', '.iedb.net', false, true); // problem with this is that it doesn't override the previous session cookie. This is only applicable if we've deleted the cookies and refreshed the post page.
header('Set-Cookie: PHPSESSID=' . session_id() . ($persistent ? '; expires=Tue, 19-Jan-2038 03:14:07 GMT' : '') . '; path=/; httponly', true); // Later we can modify this to an SSL only cookie!
$DB->real_query('INSERT INTO sessions (id, timestamp, persistent, user_id, data) VALUES (0x' . session_id() . ', UNIX_TIMESTAMP(), ' . ($persistent ? 'UNIX_TIMESTAMP(), ' : '0, ') . $_SESSION['id'] . ', "")');
return true;
function update_user_logs() // try to run this in the framework after we have the $FW['domain']['id'] -- problem is: we don't know if the user has just "logged in"
{
$logs = $this->logs();
$GLOBALS['DB']->init_ipcc();
$sql = 'UPDATE users SET ' .
'previous_online = last_online,' .
'last_login = NOW(),' .
'last_online = NOW(),' .
//'last_domain_id = ' . $GLOBALS['FW']['domain']['id'] . ',' .
'locale = "' . $GLOBALS['FW']['locale'] . '",' .
'logins = logins + 1,' .
'ip = @ip,' .
'cc = @cc,' .
'for_id = ' . $logs['forwarder_id'] . ',' .
'via_id = ' . $logs['proxy_id'] . ',' .
'agent_id = ' . $logs['agent_id'] . ',' .
'referer_id = ' . $logs['referer_id'] . ',' .
'language_id = ' . $logs['language_id'] .
' WHERE id = ' . $_SESSION['id'];
$GLOBALS['DB']->real_query($sql);
}
*/

68
src/elements/login/view.php

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
<div class="panel panel-primary login-panel">
<div class="panel-heading">Login</div>
<div class="panel-body">
<form id="login" action="<?= request::build_url(['https', 'path' => '/login']) ?>" method="post" onsubmit="return validate_login(this);">
<input type="hidden" name="challenge" id="login_challenge" value="<?= $_SESSION['challenge'] ?>" />
<input type="hidden" name="next" id="login_next" value="<?= htmlentities(get('next')) ?>" />
<div class="form-group has-feedback">
<input type="text" class="form-control" id="login_email" name="email" placeholder="Email address" />
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input type="password" class="form-control" id="login_password" name="password" placeholder="Password" />
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="g-recaptcha" data-sitekey="<?= env::get('google')['recaptcha-site'] ?>"></div>
<br />
<div class="row">
<div class="col-xs-8">
<div class="checkbox icheck">
<label><input type="checkbox" name="persistent" value="1" /> Keep me logged in</label>
</div>
</div>
<!-- /.col -->
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat" id="login_submit">Login</button>
</div>
<!-- /.col -->
</div>
<span class="ajax-loading hide" id="login_loading"><img src="indicator.gif" alt="Loading" /> Loading...</span>
</form>
<br />
<p>By logging in you are agreeing to our <a>Terms of Use</a> and <a>Privacy Policy</a>.</p>
<hr />
<a href="forgot-password">Forgot your password?</a><br />
<a href="resend-verification">Resend Email Verification</a><br />
<hr />
<p>Not a member?</p>
<a href="register" class="btn btn-default btn-block">Create an account</a>
</div>
</div>
<script type="text/javascript">
function validate_login(form)
{
if (form.login_email.value && form.login_password.value)
{
//$("login_loading").className = "ajax-loading";
$("login_loading").removeClass("hide");
return true;
}
if (form.login_email.value == "")
alert("Please enter your email address!");
else
alert("Please enter your password!");
return false;
}
</script>

60
src/layouts/admin.php

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
<?php
return function($response)
{
$response->title = 'Admin';
$response->styles['font-awesome'] = 'https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'; // http://fontawesome.io/
$response->scripts['jquery'] = 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js';
$response->scripts['bootstrap'] = 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js';
$response->elements['content'] = null;
$response->elements['navbar'] = 'admin/navbar';
$response->elements['messages'] = 'admin/messages';
$response->robots = 'noindex,nofollow,noarchive';
$response->renderer = function() use ($response)
{
echo
'<!DOCTYPE html>' . PHP_EOL .
'<html lang="' . $response->lang . '">' .
'<head id="head>' .
'<meta charset="utf-8" />' .
'<meta http-equiv="x-ua-compatible" content="ie=edge" />' .
'<meta http-equiv="content-type" content="text/html; charset=utf-8" />' .
'<meta http-equiv="content-language" content="' . $response->lang . '" />' .
'<title>' . htmlspecialchars($response->title) . '</title>' .
'<meta name="robots" content="' . $response->robots . '" />' .
'<link type="image/x-icon" href="/favicon.ico" rel="icon" />' .
'<link type="image/x-icon" href="/favicon.ico" rel="shortcut icon" />' .
'<meta name="viewport" content="width=device-width, initial-scale=1" />';
foreach ($response->styles as &$url)
echo '<link rel="stylesheet" type="text/css" href="' . $url . '" />';
foreach ($response->scripts as &$url)
echo '<script type="text/javascript" src="' . $url . '"></script>';
echo
($response->script ? '<script type="text/javascript">' . $response->script . '</script>' : null) .
'</head>' .
'<body id="body">' .
'<div class="container fluid" style="min-height: 600px">';
// '<div class="container-fluid">';
// '<div class="admin-navbar">';
require __DIR__ . '/../elements/' . $response->elements['navbar'] . '/view.php';
echo
// '</div>' .
'<div class="admin-messages">';
require __DIR__ . '/../elements/' . $response->elements['messages'] . '/view.php';
echo
'</div>' .
'<div class="admin-content">';
require __DIR__ . '/../elements/' . $response->elements['content'] . '/view.php';
echo
'</div>' .
// '</div>' .
'</div>' .
'</body>' .
'</html>';
};
};

60
src/layouts/public.php

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
<?php
/**
* `layouts` are something like a template pre-processor.
* Mainly handling common code and default options for all code that use this layout!
*/
return function($response)
{
$response->title = $response->container->config['title'];
$response->styles['font-awesome'] = 'https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css'; // http://fontawesome.io/
$response->scripts['jquery'] = 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js';
$response->scripts['bootstrap'] = 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js';
$response->elements['content'] = null;
$response->elements['header'] = 'header';
$response->elements['footer'] = 'footer';
$response->meta['Play'] = '<link href="https://fonts.googleapis.com/css?family=Play:400,700" rel="stylesheet">';
$response->renderer = function() use ($response)
{
echo
'<!DOCTYPE html>' . PHP_EOL .
'<html lang="' . $response->lang . '">' .
'<head id="head>' .
'<meta charset="utf-8" />' .
'<meta http-equiv="x-ua-compatible" content="ie=edge" />' .
'<meta http-equiv="content-type" content="text/html; charset=utf-8" />' .
'<meta http-equiv="content-language" content="' . $response->lang . '" />' .
'<title>' . htmlspecialchars($response->title) . '</title>' .
'<meta name="description" content="' . htmlspecialchars($response->description) . '" />' .
'<meta name="keywords" content="' . htmlspecialchars(implode(',', $response->keywords)) . '" />' .
'<meta name="robots" content="' . $response->robots . '" />' .
'<link rel="canonical" href="' . htmlspecialchars($response->canonical) . '" />' .
'<link type="image/x-icon" href="/favicon.ico" rel="icon" />' .
'<link type="image/x-icon" href="/favicon.ico" rel="shortcut icon" />' .
'<meta name="viewport" content="width=device-width, initial-scale=1" />';
foreach ($response->styles as &$url)
echo '<link rel="stylesheet" type="text/css" href="' . $url . '" />';
foreach ($response->scripts as &$url)
echo '<script type="text/javascript" src="' . $url . '"></script>';
foreach ($response->meta as &$meta)
echo $meta;
echo
'</head>' .
'<body>' .
'<div class="container">';
require __DIR__ . '/../elements/' . $response->elements['header'] . '/view.php';
require __DIR__ . '/../elements/' . $response->elements['content'] . '/view.php';
require __DIR__ . '/../elements/' . $response->elements['footer'] . '/view.php';
echo
'</div>' .
'</body>' .
'</html>';
};
};
Loading…
Cancel
Save