diff --git a/index.php b/index.php
index bddbf4d..3267e39 100644
--- a/index.php
+++ b/index.php
@@ -30,7 +30,8 @@ $routes = [
'/new' => 'new_twt.php',
'/add' => 'add_feed.php',
'/following' => 'following.php',
- '/refresh' => 'load_twt_files.php',
+ //'/refresh' => 'load_twt_files.php',
+ '/refresh' => 'refresh.php',
'/login' => 'login.php',
'/logout' => 'logout.php',
'/profile' => 'profile.php',
diff --git a/libs/TOTP.php b/libs/TOTP.php
new file mode 100644
index 0000000..1ef0173
--- /dev/null
+++ b/libs/TOTP.php
@@ -0,0 +1,166 @@
+\n";
+
+ for ($currentWindow = -$window; $currentWindow <= $window; $currentWindow++) {
+ $time = $timeSlice + $currentWindow;
+ $generatedCode = generateTOTP($secret, $digits, $time);
+ #echo "$time $generatedCode
\n";
+
+ if ($generatedCode === $enteredCodeInt) {
+ return true; // Code is valid within the window
+ }
+ }
+
+ return false; // Code is not valid
+}
+
+function generateTOTP($secret, $digits = 6, $timeSlice = null) {
+ if ($timeSlice === null) {
+ $timeSlice = floor(time() / 30);
+ }
+
+ $secret = base32Decode($secret);
+ $timeSlice = pack('N*', 0) . pack('N*', $timeSlice);
+
+ $hash = hash_hmac('sha1', $timeSlice, $secret, true);
+ $offset = ord($hash[19]) & 0xf;
+
+ $otp = (
+ (ord($hash[$offset + 0]) & 0x7f) << 24 |
+ (ord($hash[$offset + 1]) & 0xff) << 16 |
+ (ord($hash[$offset + 2]) & 0xff) << 8 |
+ (ord($hash[$offset + 3]) & 0xff)
+ );
+
+ #var_dump($otp);
+ #echo "
\n";
+
+ // When digits is 10, the padding was giving a wrong OTP
+ // Just return the calculated value
+ if ($digits === 10) {
+ return $otp;
+ }
+
+ $otp = $otp % pow(10, $digits);
+ return str_pad($otp, $digits, '0', STR_PAD_LEFT);
+}
+
+function base32Decode($base32) {
+ $base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
+ $base32charsFlipped = array_flip(str_split($base32chars));
+ $paddingChar = '=';
+ $paddingCharCount = 0;
+
+ if (strpos($base32, $paddingChar) !== false) {
+ $paddingCharCount = substr_count($base32, $paddingChar);
+ }
+
+ $allowedValues = array(6, 4, 3, 1, 0);
+
+ if (!in_array($paddingCharCount, $allowedValues)) {
+ return false;
+ }
+
+ for ($i = 0; $i < 4; ++$i) {
+ if ($paddingCharCount == $allowedValues[$i] &&
+ substr($base32, -$allowedValues[$i]) != str_repeat($paddingChar, $allowedValues[$i])
+ ) {
+ return false;
+ }
+ }
+
+ $base32 = str_replace($paddingChar, '', $base32);
+ $base32 = str_split($base32);
+ $binaryString = '';
+
+ foreach ($base32 as $char) {
+ if (!isset($base32charsFlipped[$char])) {
+ return false; // Invalid character found
+ }
+ $binaryString .= sprintf('%05b', $base32charsFlipped[$char]);
+ }
+
+ $length = strlen($binaryString);
+ $offset = 0;
+ $binaryData = '';
+
+ while ($offset < $length) {
+ $binaryChunk = substr($binaryString, $offset, 8);
+
+ if (strlen($binaryChunk) < 8) {
+ $binaryChunk = str_pad($binaryChunk, 8, '0', STR_PAD_RIGHT);
+ }
+
+ $decimalChunk = bindec($binaryChunk);
+ $binaryData .= pack('C', $decimalChunk);
+ $offset += 8;
+ }
+
+ return $binaryData;
+}
+
+function base32Encode($input) {
+ $base32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
+ $base32String = '';
+ $position = 0;
+ $carry = 0;
+ foreach (str_split($input) as $byte) {
+ $carry |= ord($byte) << $position;
+ $position += 8;
+ while ($position >= 5) {
+ $base32String .= $base32Chars[$carry & 31];
+ $carry >>= 5;
+ $position -= 5;
+ }
+ }
+ if ($position > 0) {
+ if ($carry & 31) {
+ $base32String .= $base32Chars[$carry & 31];
+ }
+ }
+ return $base32String;
+}
+
+function generateRandomSecret($length = 32) {
+ // https://medium.com/@nicola88/two-factor-authentication-with-totp-ccc5f828b6df
+ $bytes = random_bytes($length);
+ $base32 = base32Encode($bytes);
+
+ return substr($base32, 0, $length);
+}
+
+# Examples of usage (To move somewhere else)
+/*
+$randomSecret = generateRandomSecret();
+echo "Random Secret Key: $randomSecret
\n";
+
+$secret = 'K3OBZ7XPR6T4PTNXSNCQ';
+$enteredCode = '123456';
+$digits = 6;
+if (isset($_GET['c']) && isset($_GET['s'])) {
+ $enteredCode = $_GET['c'];
+ $secret = $_GET['s'];
+ $isCodeValid = verifyTOTP($secret, $digits, $enteredCode);
+
+ if ($isCodeValid) {
+ echo "Code $enteredCode is valid!
";
+ } else {
+ echo "Code $enteredCode is invalid!
";
+ }
+}
+
+$code = generateTOTP($secret);
+echo "TOTP code: $code
\n";
+
+$code = generateTOTP($secret, 8);
+echo "TOTP code: $code
\n";
+*/
diff --git a/libs/session.php b/libs/session.php
index 28ffa4a..bb9f9fc 100644
--- a/libs/session.php
+++ b/libs/session.php
@@ -1,18 +1,30 @@
+
-
-
+
+
+
= $title ?>
@@ -43,5 +45,3 @@ $profile = getTwtsFromTwtxtString($config['public_txt_url']);
-
-
diff --git a/twtxt_template.txt b/twtxt_template.txt
deleted file mode 100644
index 330a06d..0000000
--- a/twtxt_template.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-# | |___ _| |___ _| |_
-# | __\ \ /\ / / __\ \/ / __|
-# | |_ \ V V /| |_ > <| |_
-# \__| \_/\_/ \__/_/\_\\__|
-#
-# Twtxt is an open, distributed and decentralized microblogging platform
-# for hackers and friends that uses raw text files, easy to read by humans,
-# and with common protocols and free and open.
-#
-# Using twtxt-php from: https://github.com/eapl-gemugami/twtxt-php
-# Know more about twtxt here: https://github.com/buckket/twtxt
-# Using the following extensions:
-# https://github.com/eapl-gemugami/twtxt-php/blob/master/docs/02-metadata-extension.md
-#
-# nick = timeline
-# url = http://example.com/timeline/twtxt.txt
-# avatar = http://example.com/timeline/avatar.png
-# emoji = 👾
-# link =
-# lang =
-# description =
-# discovery =
-#
-# following = 123
-# follow = eapl.me https://eapl.me/twtxt.txt
-# follow = eapl.mx https://eapl.mx/twtxt.txt
-# follow = lyse https://lyse.isobeef.org/twtxt.txt
-# follow = prologic https://twtxt.net/user/prologic/twtxt.txt
-# follow = sorenpeter http://darch.dk/twtxt.txt
-# follow = stigatle https://yarn.stigatle.no/user/stigatle/twtxt.txt
-# follow = thecanine https://twtxt.net/user/thecanine/twtxt.txt
-#~~~#
-
-2023-09-10T18:55:27+02:00 Hello twtxt world!
diff --git a/views/load_twt_files.php b/views/load_twt_files.php
index 81abab6..96ce792 100644
--- a/views/load_twt_files.php
+++ b/views/load_twt_files.php
@@ -2,9 +2,13 @@
# Gets the followers from an URL and then gets all the Followers twtxt.txt files
# Intended to be run in the background
+/*
require_once("libs/session.php"); // TODO: Move all to base.php
require_once('libs/twtxt.php');
require_once('libs/hash.php');
+*/
+
+require_once("partials/base.php");
$config = parse_ini_file('private/config.ini');
@@ -13,6 +17,7 @@ if (!isset($_SESSION['password'])) {
exit();
}
+
$max_execution_time = intval($config['max_execution_time']);
if ($max_execution_time < 1) {
$max_execution_time = 1;
@@ -20,9 +25,13 @@ if ($max_execution_time < 1) {
ini_set('max_execution_time', $max_execution_time);
-#ob_start();
+//ob_start();
-$config = parse_ini_file('private/config.ini');
+//require_once 'partials/header.php';
+//ob_flush();
+
+
+//$config = parse_ini_file('private/config.ini');
$url = $config['public_txt_url'];
if (!empty($_GET['url'])) {
@@ -33,17 +42,20 @@ if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) {
die('Not a valid URL');
}
-echo "Loading URL: $url
\n
\n";
-#ob_flush();
+echo '
';
+echo '';
+
+ob_flush();
const DEBUG_TIME_SECS = 300;
const PRODUCTION_TIME_SECS = 5;
$fileContent = getCachedFileContentsOrUpdate($url, PRODUCTION_TIME_SECS);
$fileContent = mb_convert_encoding($fileContent, 'UTF-8');
-
$fileLines = explode("\n", $fileContent);
+// Build Following List
$twtFollowingList = [];
+
foreach ($fileLines as $currentLine) {
if (str_starts_with($currentLine, '#')) {
if (!is_null(getDoubleParameter('follow', $currentLine))) {
@@ -52,15 +64,32 @@ foreach ($fileLines as $currentLine) {
}
}
-# Load all the files
-# Save a flag to know it's loading files in the background
-foreach ($twtFollowingList as $following) {
- echo "Updating: $following[1]
\n";
- #ob_flush();
- updateCachedFile($following[1]);
-}
-echo 'Finished';
-#ob_flush();
-header('Location: /');
+/* Progress bar based on: https://github.com/w3shaman/php-progress-bar */
+
+$i = 1;
+$total = count($twtFollowingList);
+foreach ($twtFollowingList as $following) {
+ $float = $i/$total;
+ $percent = intval($float * 100)."%";
+
+ // Javascript for updating the progress bar and information
+ echo '';
+
+ updateCachedFile($following[1]);
+
+ ob_flush(); // Send output to browser immediately
+ $i++;
+}
+
+
+// Tell user that the process is completed
+echo '';
+
+//header('Location: /');
exit();
+
diff --git a/views/login.php b/views/login.php
index 484f670..6024384 100644
--- a/views/login.php
+++ b/views/login.php
@@ -5,23 +5,21 @@ $title = "Login - ".$title;
include 'partials/header.php';
?>
-
+
- Enter password:
+ Enter password or TOTP
diff --git a/views/refresh.php b/views/refresh.php
new file mode 100644
index 0000000..1eb7eea
--- /dev/null
+++ b/views/refresh.php
@@ -0,0 +1,96 @@
+
+
+
+ Loading feeds followed by:
+ = preg_replace('(^https?://)', '', $url) ?>
+
+
+
+
+document.getElementById("refreshInfo").innerHTML = "Updating feed from:"';
+
+foreach ($twtFollowingList as $following) {
+ //ob_start();
+ $float = $i/$total;
+ $percent = intval($float * 100)."%";
+ $feed = $following[1];
+ //$feed = preg_replace('(^https?://)', '', $feed);
+ //$feed = $following[0].'@'. parse_url($following[1], PHP_URL_HOST);
+ $feed = $following[0].' ('.$following[1].')';
+
+ // Javascript for updating the progress bar and information
+ echo '';
+
+ updateCachedFile($following[1]);
+ ob_flush(); // Send output to browser immediately
+ flush();
+ $i++;
+}
+
+// Tell user that the process is completed
+echo '';
+