diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..21709b7
--- /dev/null
+++ b/public/.htaccess
@@ -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
+
+ By logging in you are agreeing to our Terms of Use and Privacy Policy. Not a member?
' .
+ 'We are aware of the situation and apologize for the inconvenience!
' .
+ 'Normal operation will resume shortly, please be patient and try again later!
' . ($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);
+ }
+ ];
diff --git a/src/config/demo.php b/src/config/demo.php
new file mode 100644
index 0000000..7689fb3
--- /dev/null
+++ b/src/config/demo.php
@@ -0,0 +1,22 @@
+ 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
+ ]
+ ]);
diff --git a/src/config/my_server_name.php b/src/config/my_server_name.php
new file mode 100644
index 0000000..b6169ca
--- /dev/null
+++ b/src/config/my_server_name.php
@@ -0,0 +1,31 @@
+ false,
+ 'maintenance' => false,
+ 'email_from' => 'DEMO
+Lovell: "Uh, Houston, we've had a problem."
+Lovell: "We've had a 404 Page Not Found."
+Lousma: "Roger, 404 Not Found."
+Lousma: "Okay, stand by, Thirteen, we're looking at it."
diff --git a/src/elements/admin/index/init.php b/src/elements/admin/index/init.php
new file mode 100644
index 0000000..ba83e3f
--- /dev/null
+++ b/src/elements/admin/index/init.php
@@ -0,0 +1,5 @@
+ '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/']);
diff --git a/src/elements/admin/index/view.php b/src/elements/admin/index/view.php
new file mode 100644
index 0000000..2a40911
--- /dev/null
+++ b/src/elements/admin/index/view.php
@@ -0,0 +1,3 @@
+admin index view';
diff --git a/src/elements/footer/init.php b/src/elements/footer/init.php
new file mode 100644
index 0000000..e69de29
diff --git a/src/elements/footer/view.php b/src/elements/footer/view.php
new file mode 100644
index 0000000..e69de29
diff --git a/src/elements/header/init.php b/src/elements/header/init.php
new file mode 100644
index 0000000..52c0315
--- /dev/null
+++ b/src/elements/header/init.php
@@ -0,0 +1,6 @@
+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();
+ };
diff --git a/src/elements/login/post.php b/src/elements/login/post.php
new file mode 100644
index 0000000..01dd52d
--- /dev/null
+++ b/src/elements/login/post.php
@@ -0,0 +1,199 @@
+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) . '
';
+echo hash_hmac('sha256', 'abc123', md5($salt, true)) . '
';
+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);
+ }
+*/
diff --git a/src/elements/login/view.php b/src/elements/login/view.php
new file mode 100644
index 0000000..cfe5269
--- /dev/null
+++ b/src/elements/login/view.php
@@ -0,0 +1,68 @@
+
+
+
+ Forgot your password?
+ Resend Email Verification
+
+
+