mirror of https://github.com/PurpleI2P/regi2p.git
R4SAS
4 years ago
commit
80bb08fbd2
43 changed files with 3710 additions and 0 deletions
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
Copyright 2021 © The PurpleI2P Project |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
Registry - reg.i2p |
||||
=== |
||||
|
||||
This is reg.i2p domain registry code |
||||
|
||||
|
||||
Requirements |
||||
--- |
||||
|
||||
* i2pd with enabled BOB protocol |
||||
* `verifyhost` binary built inside i2pd-tools project folder |
||||
* PHP and php-fpm 7.2+ |
||||
* Composer |
||||
* Corntab (for jobs) |
||||
|
||||
|
||||
Installation |
||||
--- |
||||
|
||||
1. Clone repository in your webserver working directory. Note: that must not be `/var/www/html` or something like this, your webserver must point to `public` directory of forked project. |
||||
2. Fetch requirements by runnning `composer update`. |
||||
3. Point your webserver to serving webpages from `public` directory. |
||||
4. Create cron jobs for quering and updating hosts lists. |
||||
|
||||
|
||||
nginx configuration |
||||
--- |
||||
|
||||
```nginx |
||||
server { |
||||
listen 127.0.0.1:8000; |
||||
|
||||
access_log /dev/null; |
||||
error_log /dev/null; |
||||
|
||||
root /home/www/reg.i2p/public; |
||||
index index.php index.html; |
||||
port_in_redirect off; |
||||
|
||||
location ~* ^/hosts\.txt$ { |
||||
if ($http_if_none_match) { |
||||
rewrite ^/hosts\.txt$ /export/hosts.txt break; |
||||
} |
||||
|
||||
rewrite ^/hosts\.txt$ /export/hosts-basic.txt break; |
||||
} |
||||
|
||||
location / { |
||||
try_files $uri $uri/ /index.php$request_uri; |
||||
} |
||||
|
||||
location ~ ^/index\.php(/|$) { |
||||
include fastcgi_params; |
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; |
||||
fastcgi_param SERVER_PORT 80; |
||||
fastcgi_pass unix:/home/www/reg.i2p/tmp/php-fpm.sock; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
php-fpm configuration |
||||
--- |
||||
|
||||
```conf |
||||
[reg.i2p] |
||||
|
||||
prefix = /home/www/$pool |
||||
user = www-data |
||||
group = www-data |
||||
listen = tmp/php-fpm.sock |
||||
|
||||
listen.owner = www-data |
||||
listen.group = www-data |
||||
listen.mode = 0660 |
||||
|
||||
pm = dynamic |
||||
pm.max_children = 5 |
||||
pm.start_servers = 1 |
||||
pm.min_spare_servers = 1 |
||||
pm.max_spare_servers = 2 |
||||
pm.max_requests = 500 |
||||
|
||||
chdir = public |
||||
|
||||
php_admin_flag[log_errors] = On |
||||
php_admin_flag[display_errors] = Off |
||||
php_admin_value[error_reporting] = E_ALL |
||||
php_admin_value[error_log] = /home/www/$pool/logs/php-error.log |
||||
|
||||
php_admin_value[upload_tmp_dir] = /home/www/$pool/tmp |
||||
php_admin_value[date.timezone] = Etc/UTC |
||||
|
||||
php_admin_value[session.save_path] = /home/www/$pool/sessions |
||||
php_admin_value[session.save_handler] = files |
||||
php_admin_value[session.gc_probability] = 2 |
||||
php_admin_value[session.gc_maxlifetime] = 1440 |
||||
|
||||
php_admin_value[memory_limit] = 32M |
||||
php_admin_value[upload_max_filesize] = "2M" |
||||
php_admin_value[max_execution_time] = 60 |
||||
``` |
||||
|
||||
Cron jobs |
||||
--- |
||||
|
||||
```crontab |
||||
5 * * * * php7.2 /home/www/reg.i2p/checker.php > /home/www/reg.i2p/logs/checker.log 2>&1 |
||||
45 */6 * * * php7.2 /home/www/reg.i2p/export.php > /home/www/reg.i2p/logs/export.log 2>&1 |
||||
50 23 * * * php7.2 /home/www/reg.i2p/fetch.php > /home/www/reg.i2p/logs/fetch.log 2>&1 |
||||
``` |
@ -0,0 +1,107 @@
@@ -0,0 +1,107 @@
|
||||
#include <iostream> |
||||
#include <fstream> |
||||
#include <sstream> |
||||
#include "Identity.h" |
||||
#include "Base.h" |
||||
|
||||
int main (int argc, char * argv[]) |
||||
{ |
||||
if (argc < 2) |
||||
{ |
||||
std::cout << "Usage: verifyhost '<host record>'" << std::endl; |
||||
return -1; |
||||
} |
||||
|
||||
i2p::crypto::InitCrypto (false, true, true, false); |
||||
|
||||
i2p::data::IdentityEx Identity, OldIdentity; |
||||
|
||||
std::string str (argv[1]); |
||||
std::size_t pos; |
||||
|
||||
// get record without command block after "#!"
|
||||
pos = str.find ("#!"); |
||||
std::string hostStr = str.substr (0, pos); |
||||
|
||||
// get host base64
|
||||
pos = hostStr.find ("="); |
||||
std::string hostBase64 = hostStr.substr (pos + 1); |
||||
|
||||
// load identity
|
||||
if (Identity.FromBase64 (hostBase64)) |
||||
{ |
||||
// get record without sig key and signature
|
||||
pos = str.find ("#sig="); |
||||
if (pos == std::string::npos) |
||||
{ |
||||
pos = str.find ("!sig="); |
||||
if (pos == std::string::npos) |
||||
{ |
||||
std::cout << "Destination signature not found." << std::endl; |
||||
return 1; |
||||
} |
||||
} |
||||
|
||||
int offset = (str[pos - 1] == '#' /* only sig in record */) ? 1 : 0; |
||||
|
||||
std::string hostNoSig = str.substr (0, pos - offset); |
||||
std::string sig = str.substr (pos + 5); // after "#sig=" till end
|
||||
|
||||
auto signatureLen = Identity.GetSignatureLen (); |
||||
uint8_t * signature = new uint8_t[signatureLen]; |
||||
|
||||
// validate signature
|
||||
i2p::data::Base64ToByteStream(sig.c_str (), sig.length(), signature, signatureLen); |
||||
if (!Identity.Verify ((uint8_t *)hostNoSig.c_str (), hostNoSig.length (), signature)) |
||||
{ |
||||
std::cout << "Invalid destination signature." << std::endl; |
||||
return 1; |
||||
} |
||||
|
||||
if (str.find ("olddest=") != std::string::npos) // if olddest present
|
||||
{ |
||||
// get olddest
|
||||
pos = str.find ("#olddest="); |
||||
std::string oldDestCut = str.substr (pos + 9); |
||||
pos = oldDestCut.find ("#"); |
||||
std::string oldDestBase64 = oldDestCut.substr (0, pos); |
||||
|
||||
// load identity
|
||||
if(!OldIdentity.FromBase64 (oldDestBase64)) |
||||
{ |
||||
std::cout << "Invalid old destination base64." << std::endl; |
||||
return 1; |
||||
} |
||||
|
||||
signatureLen = OldIdentity.GetSignatureLen (); |
||||
signature = new uint8_t[signatureLen]; |
||||
|
||||
// get record till oldsig key and oldsig
|
||||
pos = str.find ("#oldsig="); |
||||
std::string hostNoOldSig = str.substr (0, pos); |
||||
|
||||
std::string oldSigCut = str.substr (pos + 8); |
||||
pos = oldSigCut.find ("#"); |
||||
std::string oldSig = oldSigCut.substr (0, pos); |
||||
|
||||
// validate signature
|
||||
i2p::data::Base64ToByteStream(oldSig.c_str (), oldSig.length(), signature, signatureLen); |
||||
bool oldSignValid = OldIdentity.Verify ((uint8_t *)hostNoOldSig.c_str (), hostNoOldSig.length (), signature); |
||||
|
||||
if(!oldSignValid) |
||||
{ |
||||
std::cout << "Invalid old destination signature." << std::endl; |
||||
return 1; |
||||
} |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
std::cout << "Invalid destination base64." << std::endl; |
||||
return 1; |
||||
} |
||||
|
||||
i2p::crypto::TerminateCrypto (); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,70 @@
@@ -0,0 +1,70 @@
|
||||
<?php |
||||
require __DIR__ . '/vendor/autoload.php'; |
||||
require __DIR__ . '/config.php'; |
||||
|
||||
use App\BOB; |
||||
use App\DB; |
||||
use Amp\Loop; |
||||
use Amp\Parallel\Worker\DefaultPool; |
||||
|
||||
/* Disable execution limit */ |
||||
set_time_limit(0); |
||||
|
||||
$pdo = (new DB($options))->pdo; |
||||
|
||||
$results = []; |
||||
|
||||
$STH = $pdo->query("SELECT `host`, `base32` FROM `hosts`"); |
||||
$hosts = $STH->fetchAll(PDO::FETCH_KEY_PAIR); |
||||
|
||||
/* Start temporary BOB tunnel for checking */ |
||||
$bob = new BOB($options); |
||||
$bob->setnick(); |
||||
$bob->options(); |
||||
$bob->newkeys(); |
||||
$bob->intun(); |
||||
$bob->start(); |
||||
|
||||
/* Sleep 10 seconds awaitng tunnels get built */ |
||||
echo "BOB session started, awaiting 10 seconds for tunnels" . PHP_EOL; |
||||
sleep(10); |
||||
|
||||
/* Start async checker tasks */ |
||||
Loop::run(function () use (&$results, $hosts, $options) { |
||||
$pool = new DefaultPool(64); |
||||
|
||||
$coroutines = []; |
||||
|
||||
foreach ($hosts as $host => $base32) { |
||||
$coroutines[$host] = Amp\call(function () use ($pool, $base32, $options) { |
||||
$result = yield $pool->enqueue(new App\Checker($base32, $options)); |
||||
return $result; |
||||
}); |
||||
} |
||||
|
||||
$results = yield Amp\Promise\all($coroutines); |
||||
|
||||
return yield $pool->shutdown(); |
||||
}); |
||||
|
||||
/* Stop BOB tunnel and terminate */ |
||||
$bob->stop(); |
||||
$bob->clear(); |
||||
$bob = null; |
||||
|
||||
/* Update last seen time in DB */ |
||||
$i = 0; |
||||
$pdo->beginTransaction(); |
||||
|
||||
foreach ($results as $host => $result) |
||||
{ |
||||
if($result) |
||||
{ |
||||
$pdo->exec("UPDATE `hosts` SET `last_seen` = current_timestamp() WHERE `host` = '" . $host . "'"); |
||||
$i++; |
||||
} |
||||
} |
||||
|
||||
$pdo->commit(); |
||||
|
||||
echo "Result: Total hosts: " . count($results) . ", Alive: " . $i . PHP_EOL; |
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
{ |
||||
"name": "purplei2p/regi2p", |
||||
"description": "I2P domain registrar", |
||||
"type": "project", |
||||
"require": { |
||||
"php": "^7.2.5", |
||||
"twig/twig": "^3.0", |
||||
"amphp/parallel": "^1.4" |
||||
}, |
||||
"license": "MIT", |
||||
"authors": [ |
||||
{ |
||||
"name": "R4SAS", |
||||
"email": "r4sas@i2pmail.org" |
||||
} |
||||
], |
||||
"autoload": { |
||||
"files": [ |
||||
"lib/bob.php", |
||||
"lib/checker.php", |
||||
"lib/db.php", |
||||
"lib/router.php", |
||||
"lib/utils.php" |
||||
] |
||||
}, |
||||
"scripts": { |
||||
"checker": [ |
||||
"Composer\\Config::disableProcessTimeout", |
||||
"php checker.php" |
||||
], |
||||
"clearCache": "rm -rf cache/*", |
||||
"export": "php export.php", |
||||
"fetch": [ |
||||
"Composer\\Config::disableProcessTimeout", |
||||
"php fetch.php" |
||||
] |
||||
} |
||||
} |
@ -0,0 +1,718 @@
@@ -0,0 +1,718 @@
|
||||
{ |
||||
"_readme": [ |
||||
"This file locks the dependencies of your project to a known state", |
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", |
||||
"This file is @generated automatically" |
||||
], |
||||
"content-hash": "4053556a32d0a974795f8ab1ee625ae1", |
||||
"packages": [ |
||||
{ |
||||
"name": "amphp/amp", |
||||
"version": "v2.5.2", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/amphp/amp.git", |
||||
"reference": "efca2b32a7580087adb8aabbff6be1dc1bb924a9" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/amphp/amp/zipball/efca2b32a7580087adb8aabbff6be1dc1bb924a9", |
||||
"reference": "efca2b32a7580087adb8aabbff6be1dc1bb924a9", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=7" |
||||
}, |
||||
"require-dev": { |
||||
"amphp/php-cs-fixer-config": "dev-master", |
||||
"amphp/phpunit-util": "^1", |
||||
"ext-json": "*", |
||||
"jetbrains/phpstorm-stubs": "^2019.3", |
||||
"phpunit/phpunit": "^6.0.9 | ^7", |
||||
"psalm/phar": "^3.11@dev", |
||||
"react/promise": "^2" |
||||
}, |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "2.x-dev" |
||||
} |
||||
}, |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Amp\\": "lib" |
||||
}, |
||||
"files": [ |
||||
"lib/functions.php", |
||||
"lib/Internal/functions.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Daniel Lowrey", |
||||
"email": "rdlowrey@php.net" |
||||
}, |
||||
{ |
||||
"name": "Aaron Piotrowski", |
||||
"email": "aaron@trowski.com" |
||||
}, |
||||
{ |
||||
"name": "Bob Weinand", |
||||
"email": "bobwei9@hotmail.com" |
||||
}, |
||||
{ |
||||
"name": "Niklas Keller", |
||||
"email": "me@kelunik.com" |
||||
} |
||||
], |
||||
"description": "A non-blocking concurrency framework for PHP applications.", |
||||
"homepage": "http://amphp.org/amp", |
||||
"keywords": [ |
||||
"async", |
||||
"asynchronous", |
||||
"awaitable", |
||||
"concurrency", |
||||
"event", |
||||
"event-loop", |
||||
"future", |
||||
"non-blocking", |
||||
"promise" |
||||
], |
||||
"support": { |
||||
"irc": "irc://irc.freenode.org/amphp", |
||||
"issues": "https://github.com/amphp/amp/issues", |
||||
"source": "https://github.com/amphp/amp/tree/v2.5.2" |
||||
}, |
||||
"funding": [ |
||||
{ |
||||
"url": "https://github.com/amphp", |
||||
"type": "github" |
||||
} |
||||
], |
||||
"time": "2021-01-10T17:06:37+00:00" |
||||
}, |
||||
{ |
||||
"name": "amphp/byte-stream", |
||||
"version": "v1.8.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/amphp/byte-stream.git", |
||||
"reference": "f0c20cf598a958ba2aa8c6e5a71c697d652c7088" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/amphp/byte-stream/zipball/f0c20cf598a958ba2aa8c6e5a71c697d652c7088", |
||||
"reference": "f0c20cf598a958ba2aa8c6e5a71c697d652c7088", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"amphp/amp": "^2", |
||||
"php": ">=7.1" |
||||
}, |
||||
"require-dev": { |
||||
"amphp/php-cs-fixer-config": "dev-master", |
||||
"amphp/phpunit-util": "^1.4", |
||||
"friendsofphp/php-cs-fixer": "^2.3", |
||||
"jetbrains/phpstorm-stubs": "^2019.3", |
||||
"phpunit/phpunit": "^6 || ^7 || ^8", |
||||
"psalm/phar": "^3.11.4" |
||||
}, |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "1.x-dev" |
||||
} |
||||
}, |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Amp\\ByteStream\\": "lib" |
||||
}, |
||||
"files": [ |
||||
"lib/functions.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Aaron Piotrowski", |
||||
"email": "aaron@trowski.com" |
||||
}, |
||||
{ |
||||
"name": "Niklas Keller", |
||||
"email": "me@kelunik.com" |
||||
} |
||||
], |
||||
"description": "A stream abstraction to make working with non-blocking I/O simple.", |
||||
"homepage": "http://amphp.org/byte-stream", |
||||
"keywords": [ |
||||
"amp", |
||||
"amphp", |
||||
"async", |
||||
"io", |
||||
"non-blocking", |
||||
"stream" |
||||
], |
||||
"support": { |
||||
"irc": "irc://irc.freenode.org/amphp", |
||||
"issues": "https://github.com/amphp/byte-stream/issues", |
||||
"source": "https://github.com/amphp/byte-stream/tree/master" |
||||
}, |
||||
"time": "2020-06-29T18:35:05+00:00" |
||||
}, |
||||
{ |
||||
"name": "amphp/parallel", |
||||
"version": "v1.4.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/amphp/parallel.git", |
||||
"reference": "2c1039bf7ca137eae4d954b14c09a7535d7d4e1c" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/amphp/parallel/zipball/2c1039bf7ca137eae4d954b14c09a7535d7d4e1c", |
||||
"reference": "2c1039bf7ca137eae4d954b14c09a7535d7d4e1c", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"amphp/amp": "^2", |
||||
"amphp/byte-stream": "^1.6.1", |
||||
"amphp/parser": "^1", |
||||
"amphp/process": "^1", |
||||
"amphp/serialization": "^1", |
||||
"amphp/sync": "^1.0.1", |
||||
"php": ">=7.1" |
||||
}, |
||||
"require-dev": { |
||||
"amphp/php-cs-fixer-config": "dev-master", |
||||
"amphp/phpunit-util": "^1.1", |
||||
"phpunit/phpunit": "^8 || ^7" |
||||
}, |
||||
"type": "library", |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Amp\\Parallel\\": "lib" |
||||
}, |
||||
"files": [ |
||||
"lib/Context/functions.php", |
||||
"lib/Sync/functions.php", |
||||
"lib/Worker/functions.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Aaron Piotrowski", |
||||
"email": "aaron@trowski.com" |
||||
}, |
||||
{ |
||||
"name": "Stephen Coakley", |
||||
"email": "me@stephencoakley.com" |
||||
} |
||||
], |
||||
"description": "Parallel processing component for Amp.", |
||||
"homepage": "https://github.com/amphp/parallel", |
||||
"keywords": [ |
||||
"async", |
||||
"asynchronous", |
||||
"concurrent", |
||||
"multi-processing", |
||||
"multi-threading" |
||||
], |
||||
"support": { |
||||
"issues": "https://github.com/amphp/parallel/issues", |
||||
"source": "https://github.com/amphp/parallel/tree/master" |
||||
}, |
||||
"time": "2020-04-27T15:12:37+00:00" |
||||
}, |
||||
{ |
||||
"name": "amphp/parser", |
||||
"version": "v1.0.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/amphp/parser.git", |
||||
"reference": "f83e68f03d5b8e8e0365b8792985a7f341c57ae1" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/amphp/parser/zipball/f83e68f03d5b8e8e0365b8792985a7f341c57ae1", |
||||
"reference": "f83e68f03d5b8e8e0365b8792985a7f341c57ae1", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=7" |
||||
}, |
||||
"require-dev": { |
||||
"friendsofphp/php-cs-fixer": "^2.3", |
||||
"phpunit/phpunit": "^6" |
||||
}, |
||||
"type": "library", |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Amp\\Parser\\": "lib" |
||||
} |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Niklas Keller", |
||||
"email": "me@kelunik.com" |
||||
}, |
||||
{ |
||||
"name": "Aaron Piotrowski", |
||||
"email": "aaron@trowski.com" |
||||
} |
||||
], |
||||
"description": "A generator parser to make streaming parsers simple.", |
||||
"homepage": "https://github.com/amphp/parser", |
||||
"keywords": [ |
||||
"async", |
||||
"non-blocking", |
||||
"parser", |
||||
"stream" |
||||
], |
||||
"support": { |
||||
"issues": "https://github.com/amphp/parser/issues", |
||||
"source": "https://github.com/amphp/parser/tree/is-valid" |
||||
}, |
||||
"time": "2017-06-06T05:29:10+00:00" |
||||
}, |
||||
{ |
||||
"name": "amphp/process", |
||||
"version": "v1.1.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/amphp/process.git", |
||||
"reference": "355b1e561b01c16ab3d78fada1ad47ccc96df70e" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/amphp/process/zipball/355b1e561b01c16ab3d78fada1ad47ccc96df70e", |
||||
"reference": "355b1e561b01c16ab3d78fada1ad47ccc96df70e", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"amphp/amp": "^2", |
||||
"amphp/byte-stream": "^1.4", |
||||
"php": ">=7" |
||||
}, |
||||
"require-dev": { |
||||
"amphp/php-cs-fixer-config": "dev-master", |
||||
"amphp/phpunit-util": "^1", |
||||
"phpunit/phpunit": "^6" |
||||
}, |
||||
"type": "library", |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Amp\\Process\\": "lib" |
||||
}, |
||||
"files": [ |
||||
"lib/functions.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Bob Weinand", |
||||
"email": "bobwei9@hotmail.com" |
||||
}, |
||||
{ |
||||
"name": "Niklas Keller", |
||||
"email": "me@kelunik.com" |
||||
}, |
||||
{ |
||||
"name": "Aaron Piotrowski", |
||||
"email": "aaron@trowski.com" |
||||
} |
||||
], |
||||
"description": "Asynchronous process manager.", |
||||
"homepage": "https://github.com/amphp/process", |
||||
"support": { |
||||
"issues": "https://github.com/amphp/process/issues", |
||||
"source": "https://github.com/amphp/process/tree/master" |
||||
}, |
||||
"time": "2019-02-26T16:33:03+00:00" |
||||
}, |
||||
{ |
||||
"name": "amphp/serialization", |
||||
"version": "v1.0.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/amphp/serialization.git", |
||||
"reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", |
||||
"reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=7.1" |
||||
}, |
||||
"require-dev": { |
||||
"amphp/php-cs-fixer-config": "dev-master", |
||||
"phpunit/phpunit": "^9 || ^8 || ^7" |
||||
}, |
||||
"type": "library", |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Amp\\Serialization\\": "src" |
||||
}, |
||||
"files": [ |
||||
"src/functions.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Aaron Piotrowski", |
||||
"email": "aaron@trowski.com" |
||||
}, |
||||
{ |
||||
"name": "Niklas Keller", |
||||
"email": "me@kelunik.com" |
||||
} |
||||
], |
||||
"description": "Serialization tools for IPC and data storage in PHP.", |
||||
"homepage": "https://github.com/amphp/serialization", |
||||
"keywords": [ |
||||
"async", |
||||
"asynchronous", |
||||
"serialization", |
||||
"serialize" |
||||
], |
||||
"support": { |
||||
"issues": "https://github.com/amphp/serialization/issues", |
||||
"source": "https://github.com/amphp/serialization/tree/master" |
||||
}, |
||||
"time": "2020-03-25T21:39:07+00:00" |
||||
}, |
||||
{ |
||||
"name": "amphp/sync", |
||||
"version": "v1.4.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/amphp/sync.git", |
||||
"reference": "613047ac54c025aa800a9cde5b05c3add7327ed4" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/amphp/sync/zipball/613047ac54c025aa800a9cde5b05c3add7327ed4", |
||||
"reference": "613047ac54c025aa800a9cde5b05c3add7327ed4", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"amphp/amp": "^2.2", |
||||
"php": ">=7.1" |
||||
}, |
||||
"require-dev": { |
||||
"amphp/php-cs-fixer-config": "dev-master", |
||||
"amphp/phpunit-util": "^1.1", |
||||
"phpunit/phpunit": "^9 || ^8 || ^7" |
||||
}, |
||||
"type": "library", |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Amp\\Sync\\": "src" |
||||
}, |
||||
"files": [ |
||||
"src/functions.php", |
||||
"src/ConcurrentIterator/functions.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Aaron Piotrowski", |
||||
"email": "aaron@trowski.com" |
||||
}, |
||||
{ |
||||
"name": "Stephen Coakley", |
||||
"email": "me@stephencoakley.com" |
||||
} |
||||
], |
||||
"description": "Mutex, Semaphore, and other synchronization tools for Amp.", |
||||
"homepage": "https://github.com/amphp/sync", |
||||
"keywords": [ |
||||
"async", |
||||
"asynchronous", |
||||
"mutex", |
||||
"semaphore", |
||||
"synchronization" |
||||
], |
||||
"support": { |
||||
"issues": "https://github.com/amphp/sync/issues", |
||||
"source": "https://github.com/amphp/sync/tree/v1.4.0" |
||||
}, |
||||
"time": "2020-05-07T18:57:50+00:00" |
||||
}, |
||||
{ |
||||
"name": "symfony/polyfill-ctype", |
||||
"version": "v1.22.1", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/symfony/polyfill-ctype.git", |
||||
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", |
||||
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=7.1" |
||||
}, |
||||
"suggest": { |
||||
"ext-ctype": "For best performance" |
||||
}, |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-main": "1.22-dev" |
||||
}, |
||||
"thanks": { |
||||
"name": "symfony/polyfill", |
||||
"url": "https://github.com/symfony/polyfill" |
||||
} |
||||
}, |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Symfony\\Polyfill\\Ctype\\": "" |
||||
}, |
||||
"files": [ |
||||
"bootstrap.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Gert de Pagter", |
||||
"email": "BackEndTea@gmail.com" |
||||
}, |
||||
{ |
||||
"name": "Symfony Community", |
||||
"homepage": "https://symfony.com/contributors" |
||||
} |
||||
], |
||||
"description": "Symfony polyfill for ctype functions", |
||||
"homepage": "https://symfony.com", |
||||
"keywords": [ |
||||
"compatibility", |
||||
"ctype", |
||||
"polyfill", |
||||
"portable" |
||||
], |
||||
"support": { |
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" |
||||
}, |
||||
"funding": [ |
||||
{ |
||||
"url": "https://symfony.com/sponsor", |
||||
"type": "custom" |
||||
}, |
||||
{ |
||||
"url": "https://github.com/fabpot", |
||||
"type": "github" |
||||
}, |
||||
{ |
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony", |
||||
"type": "tidelift" |
||||
} |
||||
], |
||||
"time": "2021-01-07T16:49:33+00:00" |
||||
}, |
||||
{ |
||||
"name": "symfony/polyfill-mbstring", |
||||
"version": "v1.22.1", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/symfony/polyfill-mbstring.git", |
||||
"reference": "5232de97ee3b75b0360528dae24e73db49566ab1" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", |
||||
"reference": "5232de97ee3b75b0360528dae24e73db49566ab1", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=7.1" |
||||
}, |
||||
"suggest": { |
||||
"ext-mbstring": "For best performance" |
||||
}, |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-main": "1.22-dev" |
||||
}, |
||||
"thanks": { |
||||
"name": "symfony/polyfill", |
||||
"url": "https://github.com/symfony/polyfill" |
||||
} |
||||
}, |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Symfony\\Polyfill\\Mbstring\\": "" |
||||
}, |
||||
"files": [ |
||||
"bootstrap.php" |
||||
] |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"MIT" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Nicolas Grekas", |
||||
"email": "p@tchwork.com" |
||||
}, |
||||
{ |
||||
"name": "Symfony Community", |
||||
"homepage": "https://symfony.com/contributors" |
||||
} |
||||
], |
||||
"description": "Symfony polyfill for the Mbstring extension", |
||||
"homepage": "https://symfony.com", |
||||
"keywords": [ |
||||
"compatibility", |
||||
"mbstring", |
||||
"polyfill", |
||||
"portable", |
||||
"shim" |
||||
], |
||||
"support": { |
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" |
||||
}, |
||||
"funding": [ |
||||
{ |
||||
"url": "https://symfony.com/sponsor", |
||||
"type": "custom" |
||||
}, |
||||
{ |
||||
"url": "https://github.com/fabpot", |
||||
"type": "github" |
||||
}, |
||||
{ |
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony", |
||||
"type": "tidelift" |
||||
} |
||||
], |
||||
"time": "2021-01-22T09:19:47+00:00" |
||||
}, |
||||
{ |
||||
"name": "twig/twig", |
||||
"version": "v3.3.0", |
||||
"source": { |
||||
"type": "git", |
||||
"url": "https://github.com/twigphp/Twig.git", |
||||
"reference": "1f3b7e2c06cc05d42936a8ad508ff1db7975cdc5" |
||||
}, |
||||
"dist": { |
||||
"type": "zip", |
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/1f3b7e2c06cc05d42936a8ad508ff1db7975cdc5", |
||||
"reference": "1f3b7e2c06cc05d42936a8ad508ff1db7975cdc5", |
||||
"shasum": "" |
||||
}, |
||||
"require": { |
||||
"php": ">=7.2.5", |
||||
"symfony/polyfill-ctype": "^1.8", |
||||
"symfony/polyfill-mbstring": "^1.3" |
||||
}, |
||||
"require-dev": { |
||||
"psr/container": "^1.0", |
||||
"symfony/phpunit-bridge": "^4.4.9|^5.0.9" |
||||
}, |
||||
"type": "library", |
||||
"extra": { |
||||
"branch-alias": { |
||||
"dev-master": "3.3-dev" |
||||
} |
||||
}, |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Twig\\": "src/" |
||||
} |
||||
}, |
||||
"notification-url": "https://packagist.org/downloads/", |
||||
"license": [ |
||||
"BSD-3-Clause" |
||||
], |
||||
"authors": [ |
||||
{ |
||||
"name": "Fabien Potencier", |
||||
"email": "fabien@symfony.com", |
||||
"homepage": "http://fabien.potencier.org", |
||||
"role": "Lead Developer" |
||||
}, |
||||
{ |
||||
"name": "Twig Team", |
||||
"role": "Contributors" |
||||
}, |
||||
{ |
||||
"name": "Armin Ronacher", |
||||
"email": "armin.ronacher@active-4.com", |
||||
"role": "Project Founder" |
||||
} |
||||
], |
||||
"description": "Twig, the flexible, fast, and secure template language for PHP", |
||||
"homepage": "https://twig.symfony.com", |
||||
"keywords": [ |
||||
"templating" |
||||
], |
||||
"support": { |
||||
"issues": "https://github.com/twigphp/Twig/issues", |
||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.0" |
||||
}, |
||||
"funding": [ |
||||
{ |
||||
"url": "https://github.com/fabpot", |
||||
"type": "github" |
||||
}, |
||||
{ |
||||
"url": "https://tidelift.com/funding/github/packagist/twig/twig", |
||||
"type": "tidelift" |
||||
} |
||||
], |
||||
"time": "2021-02-08T09:54:36+00:00" |
||||
} |
||||
], |
||||
"packages-dev": [], |
||||
"aliases": [], |
||||
"minimum-stability": "stable", |
||||
"stability-flags": [], |
||||
"prefer-stable": false, |
||||
"prefer-lowest": false, |
||||
"platform": { |
||||
"php": "^7.2.5" |
||||
}, |
||||
"platform-dev": [], |
||||
"plugin-api-version": "2.0.0" |
||||
} |
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
<?php |
||||
/** |
||||
* Configuration file |
||||
*/ |
||||
|
||||
/* Service options */ |
||||
$options = [ |
||||
/* Database settings */ |
||||
'db_host' => '127.0.0.1', |
||||
'db_user' => 'regi2p', |
||||
'db_pass' => 'pass', |
||||
'db_name' => 'user', |
||||
|
||||
/* I2P settings */ |
||||
'bob_host' => '127.0.0.1', |
||||
'bob_port' => '2827', |
||||
'bob_options' => 'inbound.quantity=5 outbound.quantity=5 inbound.length=1 outbound.length=1 i2cp.leaseSetType=3', |
||||
'bob_nick' => 'hostchecker', |
||||
'http_proxy' => 'tcp://127.0.0.1:4444', // this is HTTP proxy, which must be specified as tcp protocol because we using stream context |
||||
|
||||
/* Service settings */ |
||||
'approval' => true, // require approval (check host for availability before publishing) |
||||
'fetcher' => true, // enable external subscriptions fetcher |
||||
'tableitems' => 30, // records limit on alive, all, search pages |
||||
|
||||
/* Records processing options */ |
||||
'approvedelay' => 24, // check host for availability before publishing for this time (hours) |
||||
'approveseen' => 3, // host must be seen lesser than this amount of hours for approving (hours) |
||||
|
||||
'newdays' => 7, // assume host as new for that amout of days |
||||
'delnewdays' => 3, // if new host not seen more than X days, disable it and disapprove |
||||
'delolddays' => 30, // same as above, but for old hosts |
||||
]; |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; |
||||
START TRANSACTION; |
||||
SET time_zone = "+00:00"; |
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; |
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; |
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; |
||||
/*!40101 SET NAMES utf8mb4 */; |
||||
|
||||
|
||||
CREATE TABLE `hosts` ( |
||||
`host` varchar(67) NOT NULL, |
||||
`base64` varchar(616) NOT NULL, |
||||
`base32` varchar(52) NOT NULL, |
||||
`description` varchar(128) NOT NULL DEFAULT '', |
||||
`add_date` timestamp NOT NULL DEFAULT current_timestamp(), |
||||
`last_seen` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', |
||||
`approved` tinyint(1) NOT NULL DEFAULT 0, |
||||
`initial` tinyint(1) NOT NULL DEFAULT 0 |
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; |
||||
|
||||
CREATE TABLE `subscriptions` ( |
||||
`name` varchar(67) NOT NULL, |
||||
`url` varchar(256) NOT NULL, |
||||
`etag` varchar(64) NOT NULL DEFAULT '', |
||||
`active` tinyint(1) NOT NULL DEFAULT 0 |
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; |
||||
|
||||
|
||||
ALTER TABLE `hosts` |
||||
ADD PRIMARY KEY (`host`); |
||||
|
||||
ALTER TABLE `subscriptions` |
||||
ADD PRIMARY KEY (`name`); |
||||
COMMIT; |
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; |
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; |
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; |
@ -0,0 +1,118 @@
@@ -0,0 +1,118 @@
|
||||
<?php |
||||
require __DIR__ . '/vendor/autoload.php'; |
||||
require __DIR__ . '/config.php'; |
||||
|
||||
set_time_limit (0); |
||||
date_default_timezone_set ('UTC'); |
||||
|
||||
$pdo = (new App\DB($options))->pdo; |
||||
$util = new App\Utils; |
||||
|
||||
$STH = $pdo->query ("SELECT `host`, `base64`, `base32`, `add_date`, `last_seen`, `approved`, `initial` FROM hosts"); |
||||
$hosts = $STH->fetchAll(PDO::FETCH_ASSOC); |
||||
|
||||
// for automatic approving |
||||
$approffs = date ("Y-m-d H:i:s", strtotime ("-" . $options["approvedelay"] . " hour")); // approval offset |
||||
$apprseen = date ("Y-m-d H:i:s", strtotime ("-" . $options["approveseen"] . " hour")); // approval maxseen offset |
||||
|
||||
$newregoffs = date ("Y-m-d H:i:s", strtotime ("-" . $options["newdays"] . " day")); |
||||
$newseenlim = date ("Y-m-d H:i:s", strtotime ("-" . $options["delnewdays"] . " day")); |
||||
$oldseenlim = date ("Y-m-d H:i:s", strtotime ("-" . $options["delolddays"] . " day")); |
||||
|
||||
$export_full = $export_live = $export_init = []; |
||||
$export_addr_full = $export_addr_live = $export_addr_init = []; |
||||
|
||||
foreach ($hosts as $host) |
||||
{ |
||||
/* Convert UFT-8 domains to Punycode */ |
||||
$domain = $util->isASCII ($host["host"]) ? $host["host"] : idn_to_ascii ($host["host"]); |
||||
|
||||
array_push($export_full, $domain . "=" . $host["base64"]); |
||||
array_push($export_addr_full, $domain . "," . $host["base32"]); |
||||
|
||||
if ( |
||||
($options["approval"] == false || $host["approved"] == 1) && ( |
||||
($host["add_date"] > $newregoffs && $host["last_seen"] > $newseenlim) || |
||||
($host["add_date"] < $newregoffs && $host["last_seen"] > $oldseenlim) |
||||
) |
||||
) |
||||
{ |
||||
array_push($export_live, $domain . "=" . $host["base64"]); |
||||
array_push($export_addr_live, $domain . "," . $host["base32"]); |
||||
|
||||
if ($host["initial"] == 1) |
||||
{ |
||||
array_push($export_init, $domain . "=" . $host["base64"]); |
||||
array_push($export_addr_init, $domain . "," . $host["base32"]); |
||||
} |
||||
} |
||||
else if ( |
||||
$host["approved"] == 0 && ( |
||||
($host["add_date"] < $approffs && $host["last_seen"] > $apprseen) // if host were registered more then X days ago and seen lesser than X hours ago |
||||
) |
||||
) |
||||
{ |
||||
$pdo->exec ("UPDATE `hosts` SET `approved` = 1 WHERE `host` = '" . $host["host"] . "'"); |
||||
array_push($export_live, $domain . "=" . $host["base64"]); |
||||
array_push($export_addr_live, $domain . "," . $host["base32"]); |
||||
} |
||||
} |
||||
|
||||
$STH = null; |
||||
$pdo = null; |
||||
|
||||
/* Sort records */ |
||||
sort ($export_full); |
||||
sort ($export_addr_full); |
||||
// |
||||
sort ($export_live); |
||||
sort ($export_addr_live); |
||||
// |
||||
sort ($export_init); |
||||
sort ($export_addr_init); |
||||
|
||||
|
||||
/* Export all records */ |
||||
$f = fopen (__DIR__ . "/public/export/hosts-all.txt", "w"); |
||||
foreach ($export_full as $l) { |
||||
$toWrite = print_r ($l, true); |
||||
fwrite ($f, $toWrite . "\n"); |
||||
} |
||||
fclose ($f); |
||||
|
||||
$f = fopen (__DIR__ . "/public/export/addresses-all.csv", "w"); |
||||
foreach ($export_addr_full as $l) { |
||||
$toWrite = print_r ($l, true); |
||||
fwrite ($f, $toWrite . "\n"); |
||||
} |
||||
fclose ($f); |
||||
|
||||
/* Export alive records */ |
||||
$f = fopen (__DIR__ . "/public/export/hosts.txt", "w"); |
||||
foreach ($export_live as $l) { |
||||
$toWrite = print_r ($l, true); |
||||
fwrite ($f, $toWrite . "\n"); |
||||
} |
||||
fclose ($f); |
||||
|
||||
$f = fopen (__DIR__ . "/public/export/addresses.csv", "w"); |
||||
foreach ($export_addr_live as $l) { |
||||
$toWrite = print_r ($l, true); |
||||
fwrite ($f, $toWrite . "\n"); |
||||
} |
||||
fclose ($f); |
||||
|
||||
/* Export initial records */ |
||||
$f = fopen (__DIR__ . "/public/export/hosts-basic.txt", "w"); |
||||
foreach ($export_init as $l) { |
||||
$toWrite = print_r ($l, true); |
||||
fwrite ($f, $toWrite . "\n"); |
||||
} |
||||
fclose ($f); |
||||
|
||||
$f = fopen (__DIR__ . "/public/export/addresses-basic.csv", "w"); |
||||
foreach ($export_addr_init as $l) { |
||||
$toWrite = print_r ($l, true); |
||||
fwrite ($f, $toWrite . "\n"); |
||||
} |
||||
fclose ($f); |
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
<?php |
||||
require __DIR__ . '/vendor/autoload.php'; |
||||
require __DIR__ . '/config.php'; |
||||
|
||||
$pdo = (new App\DB($options))->pdo; |
||||
$util = new App\Utils; |
||||
|
||||
$error = ""; |
||||
|
||||
$aContext = array( |
||||
'http' => array( |
||||
'method' => 'GET', |
||||
'proxy' => $options['http_proxy'], |
||||
'user_agent' => 'MYOB/6.66 (AN/ON)', |
||||
'request_fulluri' => true, |
||||
'timeout' => 120.0, |
||||
), |
||||
); |
||||
|
||||
|
||||
$STH = $pdo->query ("SELECT `name`, `url`, `etag` FROM `subscriptions` WHERE `active` = 1"); |
||||
$lists = $STH->fetchAll(PDO::FETCH_ASSOC); |
||||
|
||||
foreach ($lists as $list) { |
||||
echo "Processing " . $list['name'] . " subscription..." . PHP_EOL; |
||||
|
||||
if (!empty($list['etag'])) |
||||
$aContext['http']['header'] = 'If-None-Match: ' . $list['etag'] . '\r\n'; |
||||
|
||||
$cxContext = stream_context_create($aContext); |
||||
$f = fopen($list['url'], "r", false, $cxContext); |
||||
|
||||
if ($f) { |
||||
$f_meta = stream_get_meta_data($f); |
||||
|
||||
if (strpos($f_meta['wrapper_data'][0], "200") === false) { |
||||
continue; |
||||
} |
||||
|
||||
$etagHeader = array_filter($f_meta['wrapper_data'], function($el) { |
||||
return (strpos($el, "ETag") !== false); |
||||
}); |
||||
|
||||
if ($etagHeader) { |
||||
$etag = substr($etagHeader[array_keys($etagHeader)[0]], 6); |
||||
var_dump($etag); |
||||
$pdo->exec("UPDATE `subscriptions` SET `etag` = '" . $etag . "' WHERE `name` = '" . $list['name'] . "'"); |
||||
} |
||||
|
||||
while (($buffer = fgets($f, 4096)) !== false) { |
||||
$domain = ""; |
||||
$record = $util->parseHostRecord($buffer); |
||||
|
||||
if (!$util->isValidAddress($record['host'], $error)) { |
||||
echo "Error while validating " . $record['host'] . ": " . $error . PHP_EOL; |
||||
continue; |
||||
|
||||
} else { |
||||
if ($util->isPunycodeDomain($record['host'])) { |
||||
$domain = idn_to_utf8($record['host']); |
||||
|
||||
} else { |
||||
$domain = $record['host']; |
||||
} |
||||
} |
||||
|
||||
if (!$util->isValidBase64($record['b64'])) { |
||||
continue; |
||||
} |
||||
|
||||
if ((isset($record['commands']) && isset($record['commands']['action']) && $record['commands']['action'] == "adddest" && isset($record['commands']['olddest'])) && |
||||
$pdo->query("SELECT COUNT(*) FROM `hosts` WHERE `host` = '" . $domain . "' AND `base64` = '" . $record['commands']['olddest'] . "' LIMIT 1")->fetchColumn()) { |
||||
|
||||
$base32 = $util->b32from64($record['b64']); |
||||
$pdo->exec("UPDATE `hosts` SET `base64` = '" . $record["b64"] . "', `base32` = '" . $base32 . "' WHERE `host` = '" . $domain . "'"); |
||||
echo "Processed " . $domain . " (adddest)" . PHP_EOL; |
||||
continue; |
||||
} |
||||
|
||||
if ((isset($record['commands']) && isset($record['commands']['action']) && $record['commands']['action'] == "addsubdomain" && isset($record['commands']['oldname']) && isset($record['commands']['olddest'])) && |
||||
($pdo->query("SELECT COUNT(*) FROM `hosts` WHERE `host` = '" . $record['commands']['oldname'] . "' AND `base64` = '" . $record['commands']['olddest'] . "' LIMIT 1")->fetchColumn() && |
||||
!$pdo->query("SELECT COUNT(*) FROM `hosts` WHERE `host` = '" . $domain . "'")->fetchColumn())) { |
||||
|
||||
$base32 = $util->b32from64($record['b64']); |
||||
$pdo->exec("INSERT INTO `hosts` (`host`, `base64`, `base32`, `approved`) VALUES ('" . $domain . "', '" . $record["b64"] . "', '" . $base32 . "', 1)"); |
||||
echo "Processed " . $domain . " (addsubdomain)" . PHP_EOL; |
||||
continue; |
||||
} |
||||
|
||||
if(isset($record['commands']) && !$pdo->query("SELECT COUNT(*) FROM `hosts` WHERE `host` = '" . $domain . "' LIMIT 1")->fetchColumn()) { |
||||
|
||||
$base32 = $util->b32from64($record['b64']); |
||||
$pdo->exec("INSERT INTO `hosts` (`host`, `base64`, `base32`, `approved`) VALUES ('" . $domain . "', '" . $record["b64"] . "', '" . $base32 . "', 1)"); |
||||
echo "Processed " . $domain . PHP_EOL; |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
if (!feof($f)) { |
||||
echo "Error: fgets() ended earlier than needed." . PHP_EOL; |
||||
} |
||||
|
||||
fclose($f); |
||||
} else { |
||||
echo "Empty response while fetching update from " . $list['name']. "." . PHP_EOL; |
||||
} |
||||
} |
||||
|
||||
$pdo = null; |
||||
|
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
<?php |
||||
require __DIR__ . '/vendor/autoload.php'; |
||||
require __DIR__ . '/config.php'; |
||||
|
||||
$pdo = (new App\DB($options))->pdo; |
||||
$util = new App\Utils; |
||||
|
||||
$error = ""; |
||||
$f = @fopen("hosts.txt", "r"); |
||||
|
||||
if ($f) { |
||||
$pdo->beginTransaction(); |
||||
|
||||
$STH = $pdo->prepare("INSERT IGNORE INTO hosts (host, base64, base32) VALUES (:host, :base64, :base32)"); |
||||
$STH->bindParam('host', $domain); |
||||
$STH->bindParam('base64', $base64); |
||||
$STH->bindParam('base32', $base32); |
||||
|
||||
while (($buffer = fgets($f, 4096)) !== false) |
||||
{ |
||||
$domain = ""; |
||||
$record = $util->parseHostRecord($buffer); |
||||
|
||||
if (!$util->isValidAddress($record['host'], $error)) |
||||
{ |
||||
echo "Error while validating " . $record['host'] . ": " . $error . PHP_EOL; |
||||
continue; |
||||
} |
||||
else |
||||
{ |
||||
if($util->isPunycodeDomain($record['host'])) |
||||
{ |
||||
$domain = idn_to_utf8($record['host']); |
||||
} |
||||
else |
||||
{ |
||||
$domain = $record['host']; |
||||
} |
||||
} |
||||
|
||||
if (!$util->isValidBase64($record['b64'])) |
||||
{ |
||||
echo "Error while validating " . $record['host'] . ": incorrect base64: " . $record['b64'] . PHP_EOL; |
||||
continue; |
||||
} |
||||
|
||||
$base64 = $record['b64']; |
||||
$base32 = $util->b32from64($record['b64']); |
||||
|
||||
$STH->execute(); |
||||
} |
||||
if (!feof($f)) |
||||
{ |
||||
echo "Error: fgets() ended earlier than needed" . PHP_EOL; |
||||
} |
||||
|
||||
if (!$pdo->commit()) |
||||
{ |
||||
echo "Error while saving records to database"; |
||||
} |
||||
|
||||
fclose($f); |
||||
} |
||||
|
||||
$pdo = null; |
@ -0,0 +1,168 @@
@@ -0,0 +1,168 @@
|
||||
<?php |
||||
|
||||
namespace App; |
||||
|
||||
class BOB { |
||||
private $sock; |
||||
|
||||
protected $options = [ |
||||
"bob_host" => "127.0.0.1", |
||||
"bob_port" => "2827", |
||||
"bob_options" => "inbound.quantity=5 outbound.quantity=5 inbound.length=1 outbound.length=1 i2cp.leaseSetType=3", |
||||
"bob_nick" => "hostchecker", |
||||
]; |
||||
|
||||
public function __construct(array $options = []) |
||||
{ |
||||
ob_implicit_flush(); |
||||
|
||||
$this->options = array_merge($this->options, (array) $options); |
||||
|
||||
$this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); |
||||
if ($this->sock === false) |
||||
{ |
||||
throw new \ErrorException("socket_create() failed: reason: " . socket_strerror(socket_last_error())); |
||||
} |
||||
else |
||||
{ |
||||
$result = socket_connect($this->sock, $this->options["bob_host"], $this->options["bob_port"]); |
||||
if ($result === false) |
||||
{ |
||||
throw new \ErrorException("socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket))); |
||||
} |
||||
|
||||
/* Reading BOB greeting */ |
||||
$response = socket_read($this->sock, 1024); |
||||
|
||||
if(!preg_match('/OK/', $response)) |
||||
{ |
||||
throw new \ErrorException("BOB returned incorrect response on connect"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public function __destruct() |
||||
{ |
||||
$command = "quit\n"; |
||||
|
||||
socket_write($this->sock, $command, strlen($command)); |
||||
socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
|
||||
socket_close($this->sock); |
||||
} |
||||
|
||||
public function setnick() |
||||
{ |
||||
$command = "setnick " . $this->options["bob_nick"] . "\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
} |
||||
|
||||
public function options() |
||||
{ |
||||
$options = explode(" ", $this->options["bob_options"]); |
||||
|
||||
foreach($options as $option) |
||||
{ |
||||
$command = "option " . $option . "\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
} |
||||
} |
||||
|
||||
public function newkeys() |
||||
{ |
||||
$command = "newkeys\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
} |
||||
|
||||
public function start() |
||||
{ |
||||
$command = "start\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
} |
||||
|
||||
public function intun() |
||||
{ |
||||
$command = "inhost 127.0.0.1\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
|
||||
$command = "inport " . rand(1024, 65535) . "\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
} |
||||
|
||||
public function getnick() |
||||
{ |
||||
$command = "getnick " . $this->options["bob_nick"] . "\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
} |
||||
|
||||
public function lookup(string $address): bool |
||||
{ |
||||
$command = "lookup " . $address . "\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
|
||||
if(preg_match('/^OK/', $response)) |
||||
return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public function stop() |
||||
{ |
||||
$command = "stop\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
} |
||||
|
||||
public function clear() |
||||
{ |
||||
$command = "clear\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
} |
||||
|
||||
public function zap() |
||||
{ |
||||
$command = "zap\n"; |
||||
socket_write($this->sock, $command, strlen($command)); |
||||
|
||||
$response = socket_read($this->sock, 1024, PHP_NORMAL_READ); |
||||
if(!preg_match('/^OK/', $response)) |
||||
echo "BOB response: " . $response; |
||||
} |
||||
} |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
<?php |
||||
|
||||
namespace App; |
||||
|
||||
use App\BOB; |
||||
use Amp\Parallel\Worker\Environment; |
||||
use Amp\Parallel\Worker\Task; |
||||
use Amp\Parallel\Worker\TaskFailureError; |
||||
use Amp\Parallel\Worker\TaskFailureException; |
||||
use Amp\Parallel\Worker\TaskFailureThrowable; |
||||
|
||||
class Checker implements Task |
||||
{ |
||||
private $options = []; |
||||
private $base32; |
||||
|
||||
public function __construct($base32, $options = []) |
||||
{ |
||||
$this->options = array_merge($this->options, (array) $options); |
||||
$this->base32 = $base32; |
||||
} |
||||
|
||||
/** |
||||
* @param Environment $environment |
||||
* @return \Amp\Promise|\Generator|mixed |
||||
*/ |
||||
public function run(Environment $environment) |
||||
{ |
||||
$bob = new BOB($this->options); |
||||
|
||||
$bob->getnick(); |
||||
$result = $bob->lookup($this->base32 . ".b32.i2p"); |
||||
|
||||
$bob = null; |
||||
echo "Processed " . $this->base32 . ": " . ($result ? "online" : "offline") . PHP_EOL; |
||||
return $result; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
<?php |
||||
|
||||
namespace App; |
||||
|
||||
use PDO; |
||||
|
||||
class DB |
||||
{ |
||||
public $pdo; |
||||
|
||||
protected $options = [ |
||||
"db_host" => "127.0.0.1", |
||||
"db_user" => "regi2p", |
||||
"db_pass" => "password", |
||||
"db_name" => "regi2p", |
||||
]; |
||||
|
||||
public function __construct(array $options = []) |
||||
{ |
||||
$this->options = array_merge($this->options, (array) $options); |
||||
|
||||
try { |
||||
$this->pdo = new PDO("mysql:host=".$this->options["db_host"].";dbname=".$this->options["db_name"], $this->options["db_user"], $this->options["db_pass"]); |
||||
$this->pdo->setAttribute(PDO::ATTR_TIMEOUT, 20); |
||||
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
||||
$this->pdo->exec("set names utf8mb4"); |
||||
} |
||||
|
||||
catch(PDOException $e) { |
||||
exit("Database connection error: " . $e . PHP_EOL); |
||||
} |
||||
} |
||||
|
||||
public function __destruct() |
||||
{ |
||||
$pdo = null; |
||||
} |
||||
} |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
<?php |
||||
|
||||
namespace App; |
||||
|
||||
class Router |
||||
{ |
||||
private $routes; |
||||
private $errRoute; |
||||
|
||||
public function addRoute($pattern, $function) { |
||||
$this->routes['{'.$pattern.'}'] = $function; |
||||
} |
||||
|
||||
public function addErrorRoute($function) { |
||||
$this->errRoute = $function; |
||||
} |
||||
|
||||
public function run() { |
||||
foreach ($this->routes as $pattern => $function) { |
||||
if (preg_match($pattern, $_SERVER['REQUEST_URI'], $params)) { |
||||
array_shift($params); |
||||
array_unshift($params, $_SERVER['REQUEST_URI']); |
||||
return call_user_func_array($function, array_values($params)); |
||||
} |
||||
} |
||||
return call_user_func($this->errRoute); |
||||
} |
||||
} |
@ -0,0 +1,264 @@
@@ -0,0 +1,264 @@
|
||||
<?php |
||||
|
||||
namespace App; |
||||
|
||||
class Utils { |
||||
// I2P uses custom base64 alphabet, defining translation here |
||||
private const b64Trans = array("-" => "+", "~" => "/"); |
||||
|
||||
// base32 alpabet |
||||
private const b32alphabet = 'abcdefghijklmnopqrstuvwxyz234567'; |
||||
|
||||
/** |
||||
* base32 encoding function. |
||||
* @param string $data |
||||
* @return string |
||||
*/ |
||||
private static function b32encode(string $data): string |
||||
{ |
||||
if (empty($data)) { |
||||
return ""; |
||||
} |
||||
|
||||
/* Create binary string zeropadded to eight bits. */ |
||||
$data = str_split($data); |
||||
$binary = implode("", array_map(function ($character) { |
||||
return sprintf("%08b", ord($character)); |
||||
}, $data)); |
||||
|
||||
/* Split to five bit chunks and make sure last chunk has five bits. */ |
||||
$binary = str_split($binary, 5); |
||||
$last = array_pop($binary); |
||||
if (null !== $last) { |
||||
$binary[] = str_pad($last, 5, "0", STR_PAD_RIGHT); |
||||
} |
||||
|
||||
/* Convert each five bits to Base32 character. */ |
||||
$encoded = implode("", array_map(function ($fivebits) { |
||||
$index = bindec($fivebits); |
||||
return self::b32alphabet[$index]; |
||||
}, $binary)); |
||||
|
||||
return $encoded; |
||||
} |
||||
|
||||
/** |
||||
* I2P base64 decoding function. |
||||
* @param string $data |
||||
* @return string |
||||
*/ |
||||
private static function b64decode(string $data): string |
||||
{ |
||||
return base64_decode(strtr($data, static::b64Trans)); |
||||
} |
||||
|
||||
/** |
||||
* Checks if the given domain is in Punycode. |
||||
* @param string $domain The domain to check. |
||||
* @return bool Whether the domain is in Punycode. |
||||
*/ |
||||
public static function isPunycodeDomain($domain): bool |
||||
{ |
||||
$hasPunycode = false; |
||||
|
||||
foreach (explode('.', $domain) as $part) { |
||||
if (false === static::isAscii($part)) |
||||
return false; |
||||
|
||||
if (static::isPunycode($part)) |
||||
$hasPunycode = true; |
||||
} |
||||
|
||||
return $hasPunycode; |
||||
} |
||||
|
||||
/** |
||||
* Checks if the given value is in ASCII character encoding. |
||||
* @param string $value The value to check. |
||||
* @return bool Whether the value is in ASCII character encoding. |
||||
*/ |
||||
public static function isAscii($value): bool |
||||
{ |
||||
return ('ASCII' === mb_detect_encoding($value, 'ASCII', true)); |
||||
} |
||||
|
||||
/** |
||||
* Checks if the given value is in Punycode. |
||||
* @param string $value The value to check. |
||||
* @return bool Whether the value is in Punycode. |
||||
* @throws \LogicException If the string is not encoded by UTF-8. |
||||
*/ |
||||
public static function isPunycode($value): bool |
||||
{ |
||||
if (false === static::isAscii($value)) |
||||
return false; |
||||
|
||||
if ('UTF-8' !== mb_detect_encoding($value, 'UTF-8', true)) |
||||
throw new \LogicException('The string should be encoded by UTF-8 to do the right check.'); |
||||
|
||||
return (0 === mb_stripos($value, 'xn--', 0, 'UTF-8')); |
||||
} |
||||
|
||||
/** |
||||
* base64 validation function. |
||||
* @param string $data |
||||
* @return bool |
||||
*/ |
||||
public static function isValidBase64(string $data): bool |
||||
{ |
||||
$len = strlen($data); |
||||
|
||||
if($len < 516 || $len > 616) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/* .i2p in string */ |
||||
if(preg_match('/\.i2p/', $data)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/* DSA-SHA1. Will reject them because they are old (length 516) */ |
||||
if($len == 516 && preg_match('/^[a-zA-Z0-9\-~]+AA$/', $data)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
/* ECDSA or EdDSA */ |
||||
if($len == 524 && !preg_match('/^[a-zA-Z0-9\-~]+AEAA[Ec]AAA==$/', $data)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* I2P base64 to base32 conversion function. |
||||
* @param string $data |
||||
* @return string |
||||
*/ |
||||
public static function b32from64(string $data): string |
||||
{ |
||||
return self::b32encode(hash('sha256', self::b64decode($data), true)); |
||||
} |
||||
|
||||
/** |
||||
* Domain validation function for registration. |
||||
* @param string $data |
||||
* @return bool |
||||
*/ |
||||
public static function isValidAddress(string $data, string &$result): bool |
||||
{ |
||||
$len = strlen($data); |
||||
|
||||
if($len < 5 || $len > 67) |
||||
{ |
||||
$result = "Domain must be longer than 5 and lesser than 67 chars."; |
||||
return false; |
||||
} |
||||
|
||||
if(preg_match('/\.b32\.i2p$/', $data)) |
||||
{ |
||||
$result = "Domain can't end with .b32.i2p."; |
||||
return false; |
||||
} |
||||
|
||||
if(filter_var($data, FILTER_VALIDATE_DOMAIN) !== false && preg_match('/\.i2p$/', $data) |
||||
&& (static::isPunycodeDomain($data) || static::isAscii($data))) |
||||
{ |
||||
return true; |
||||
} |
||||
else |
||||
{ |
||||
$result = "Domain is not valid or is not in Punycode format."; |
||||
return false; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Domain validation function. |
||||
* @param string $data |
||||
* @return bool |
||||
*/ |
||||
public static function isValidDomain(string $data, string &$result): bool |
||||
{ |
||||
$len = strlen($data); |
||||
|
||||
if($len < 5 || $len > 67) |
||||
{ |
||||
$result = "Domain must be longer than 5 and lesser than 67 chars."; |
||||
return false; |
||||
} |
||||
|
||||
if(preg_match('/\.b32\.i2p$/', $data)) |
||||
{ |
||||
$result = "Domain can't end with .b32.i2p."; |
||||
return false; |
||||
} |
||||
|
||||
if(filter_var($data, FILTER_VALIDATE_DOMAIN) !== false && preg_match('/\.i2p$/', $data)) |
||||
{ |
||||
return true; |
||||
} |
||||
else |
||||
{ |
||||
$result = "Inputed data or domain is not valid."; |
||||
return false; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public static function verifyHostRecord(string $data, &$output): bool |
||||
{ |
||||
$retval = null; |
||||
|
||||
$cmd = dirname(__FILE__) . "/../bin/verifyhost '" . $data . "'"; |
||||
exec($cmd, $output, $retval); |
||||
|
||||
if (!$retval) |
||||
return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public static function parseHostRecord(string $data): array |
||||
{ |
||||
$record = []; |
||||
|
||||
/* Return empty array if no data provided */ |
||||
if(!strlen($data)) |
||||
return $record; |
||||
|
||||
$data = trim($data); |
||||
|
||||
/* Split addressbook host record and extended commands */ |
||||
$tmp = preg_split("/#!/", $data); |
||||
|
||||
/* Parse host record */ |
||||
$host = preg_split("/\=/", $tmp[0], 2); |
||||
$record["host"] = $host[0]; |
||||
$record["b64"] = $host[1]; |
||||
|
||||
/* Parse extended commands */ |
||||
if(isset($tmp[1])) |
||||
{ |
||||
$cmds = []; |
||||
|
||||
$cmdlist = preg_split("/#/", $tmp[1]); |
||||
foreach($cmdlist as $i) |
||||
{ |
||||
list($name,$value) = explode('=', $i, 2); |
||||
$cmds[$name] = $value; |
||||
} |
||||
|
||||
$record["commands"] = $cmds; |
||||
} |
||||
|
||||
return $record; |
||||
} |
||||
} |
@ -0,0 +1,642 @@
@@ -0,0 +1,642 @@
|
||||
body { |
||||
margin: 0; |
||||
margin-right: auto; |
||||
margin-left: auto; |
||||
background-color: #e6e6e6 |
||||
} |
||||
|
||||
main { |
||||
flex-grow: 1; |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
align-items: stretch; |
||||
justify-content: flex-start; |
||||
padding: 5px 0; |
||||
} |
||||
|
||||
.container_100vh { |
||||
flex-grow: 1; |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
align-items: stretch; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.container_main { |
||||
font-size: 16px; |
||||
} |
||||
|
||||
.container_main ul { |
||||
margin-bottom: 20px; |
||||
} |
||||
|
||||
.container_main li { |
||||
list-style-position: inside; |
||||
margin-bottom: 5px; |
||||
} |
||||
|
||||
.container_main b { |
||||
font-size: 18px; |
||||
margin-bottom: 5px; |
||||
} |
||||
|
||||
.container_main a { |
||||
color: #203070; |
||||
transition: .3s color linear; |
||||
} |
||||
|
||||
.container_main a:hover { |
||||
color: #4477ff; |
||||
} |
||||
* { |
||||
box-sizing: border-box; |
||||
font-family: monospace; |
||||
} |
||||
|
||||
button, input, ul, li { |
||||
padding: 0; |
||||
margin: 0; |
||||
border: 0 solid transparent; |
||||
} |
||||
|
||||
svg { |
||||
max-width: 100%; |
||||
} |
||||
|
||||
html { |
||||
background-color: #e6e6e6; |
||||
} |
||||
|
||||
body { |
||||
background-color: #dadada; |
||||
max-width: 1240px; |
||||
margin: 0 auto; |
||||
min-height: 100vh; |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
align-items: stretch; |
||||
justify-content: flex-start; |
||||
} |
||||
|
||||
.container { |
||||
width: 100%; |
||||
padding: 0 20px; |
||||
} |
||||
|
||||
.header { |
||||
position: sticky; |
||||
top: 0; |
||||
left: 0; |
||||
width: 100%; |
||||
} |
||||
|
||||
.header__top { |
||||
display: flex; |
||||
flex-flow: row nowrap; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
padding: 10px 20px; |
||||
background-color: #c1c1c1; |
||||
} |
||||
|
||||
.header__logo { |
||||
flex-basis: 220px; |
||||
} |
||||
|
||||
.logo { |
||||
font-size: 0; |
||||
} |
||||
|
||||
.logo__link { |
||||
display: inline-block; |
||||
font-size: 0; |
||||
width: 100%; |
||||
} |
||||
|
||||
.search { |
||||
display: flex; |
||||
flex-flow: row nowrap; |
||||
align-items: stretch; |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
.text-input { |
||||
padding: 0 10px; |
||||
border: 1px solid #e6e6e6; |
||||
background-color: #e6e6e6; |
||||
transition: .3s background-color linear, .3s border linear, .3s box-shadow linear; |
||||
outline: none; |
||||
} |
||||
|
||||
.search__text-input { |
||||
width: 260px; |
||||
margin-right: 5px; |
||||
} |
||||
|
||||
.text-input:hover { |
||||
background-color: #fff; |
||||
} |
||||
|
||||
.text-input:focus { |
||||
border: 1px solid #0054a6; |
||||
background-color: #fff; |
||||
box-shadow: 0 0px 6px 1px rgba(0, 84, 166, .6); |
||||
} |
||||
|
||||
.text-input::placeholder { |
||||
font-size: 16px; |
||||
font-weight: bold; |
||||
color: #777 |
||||
} |
||||
|
||||
.search__btn { |
||||
height: 100%; |
||||
padding: 5px 10px; |
||||
cursor: pointer; |
||||
background-color: #dadada; |
||||
transition: .3s background-color linear, .3s box-shadow linear; |
||||
} |
||||
|
||||
.search__btn img { |
||||
width: 20px; |
||||
} |
||||
|
||||
.search__btn svg path { |
||||
fill: #333; |
||||
transition: .3s fill linear; |
||||
} |
||||
|
||||
|
||||
.search__btn:hover { |
||||
background-color: #fff; |
||||
box-shadow: 0 0px 6px 1px rgba(0, 84, 166, .6) |
||||
} |
||||
|
||||
.search__btn:hover svg path { |
||||
fill: #0054a6; |
||||
} |
||||
|
||||
.main-menu__list { |
||||
display: flex; |
||||
flex-flow: row nowrap; |
||||
align-items: stretch; |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
.main-menu__item { |
||||
list-style: none; |
||||
font-size: 0; |
||||
flex-grow: 1; |
||||
margin-right: 5px; |
||||
} |
||||
|
||||
.main-menu__item:last-child{ |
||||
margin-right: 0px; |
||||
} |
||||
|
||||
.main-menu__link { |
||||
display: inline-block; |
||||
width: 100%; |
||||
font-size: 22px; |
||||
font-size: 22px; |
||||
font-weight: 400; |
||||
text-transform: uppercase; |
||||
text-align: center; |
||||
padding: 4px 0; |
||||
cursor: pointer; |
||||
background-color: #e6e6e6; |
||||
border: 1px solid #e6e6e6; |
||||
box-shadow: 0 4px 6px 2px rgba(0, 0, 0, .5); |
||||
text-decoration: none; |
||||
color: #444; |
||||
transition: .3s background-color linear, .3s border linear, .3s color linear, .3s box-shadow linear; |
||||
} |
||||
|
||||
.main-menu__link:hover { |
||||
background-color: #fff; |
||||
border: 1px solid #0054a6; |
||||
color: #0054a6; |
||||
box-shadow: 0 0px 12px 2px rgba(0, 84, 166, .8) |
||||
} |
||||
|
||||
.pagination__link:hover { |
||||
background-color: #fff; |
||||
border: 1px solid #808080; |
||||
color: #0054a6; |
||||
} |
||||
|
||||
.header__mobile-logo { |
||||
display: none; |
||||
} |
||||
|
||||
.header__mobile-search { |
||||
display: none; |
||||
} |
||||
|
||||
.footer { |
||||
margin-top: auto; |
||||
padding: 25px 0; |
||||
text-align: center; |
||||
font-size: 18px; |
||||
color: #000; |
||||
background-color: #a7a7a7; |
||||
} |
||||
|
||||
|
||||
.adder, .jumper { |
||||
font-size: 18px; |
||||
color: #000000; |
||||
} |
||||
|
||||
.title { |
||||
font-size: 24px; |
||||
text-align: center; |
||||
text-transform: uppercase; |
||||
font-weight: bold; |
||||
} |
||||
|
||||
.line { |
||||
margin-bottom: 10px; |
||||
} |
||||
|
||||
.line:last-child { |
||||
margin-bottom: 0; |
||||
|
||||
} |
||||
|
||||
.crazy-base64-span, .table__cell_long-ass { |
||||
word-break: break-all; |
||||
} |
||||
|
||||
.form__field { |
||||
margin-bottom: 20px; |
||||
display: flex; |
||||
flex-flow: row nowrap; |
||||
align-items: center; |
||||
justify-content: flex-start; |
||||
} |
||||
|
||||
.form__field:last-child { |
||||
margin-bottom: 0; |
||||
} |
||||
|
||||
.adder__text-input, .jumper__text-input, .search-page__text-input { |
||||
flex-grow: 1; |
||||
padding: 5px 10px; |
||||
margin-left: 15px; |
||||
} |
||||
|
||||
.btn { |
||||
height: 100%; |
||||
padding: 5px 10px; |
||||
cursor: pointer; |
||||
background-color: #dadada; |
||||
transition: .3s background-color linear, .3s color linear, .3s border linear, .3s box-shadow linear; |
||||
} |
||||
|
||||
.adder__btn, .jumper__btn, .search-page__btn { |
||||
display: block; |
||||
margin-left: auto; |
||||
margin-right: auto; |
||||
background-color: #fff; |
||||
text-transform: uppercase; |
||||
border: 1px solid #0054a6; |
||||
color: #0054a6; |
||||
font-size: 20px; |
||||
font-weight: bold; |
||||
} |
||||
|
||||
.adder__btn:hover, .jumper__btn:hover, .search-page__btn:hover { |
||||
background-color: #0054a6; |
||||
color: #ffffff; |
||||
border: 1px solid #fff; |
||||
box-shadow: 0 0px 23px 13px rgba(255, 255, 255, 1) |
||||
} |
||||
|
||||
.pagination__item.active .pagination__link, .pagination__item.active .pagination__link:hover { |
||||
background-color: #0054a6; |
||||
color: #ffffff; |
||||
box-shadow: 0px 0px 0px 0px transparent; |
||||
} |
||||
|
||||
.table { |
||||
font-size: 18px; |
||||
border-collapse: collapse; |
||||
width: 100%; |
||||
} |
||||
|
||||
.table__head { |
||||
font-weight: bold; |
||||
text-transform: uppercase; |
||||
} |
||||
|
||||
.table__head .table__row { |
||||
background-color: transparent !important; |
||||
color: #000 !important; |
||||
} |
||||
|
||||
.table__row { |
||||
margin-bottom: 10px; |
||||
background-color: #dadada; |
||||
color: #000; |
||||
} |
||||
|
||||
.table__row:nth-child(odd) { |
||||
background-color: #808080; |
||||
color: #fff; |
||||
} |
||||
|
||||
.table__row a { |
||||
color: #203070; |
||||
transition: .3s color linear; |
||||
} |
||||
.table__row:nth-child(odd) a { |
||||
color: #0054a6; |
||||
} |
||||
|
||||
.table__row a:hover { |
||||
color: #50a0ff |
||||
} |
||||
|
||||
.table__row:nth-child(odd) a:hover { |
||||
color: #ddddff; |
||||
} |
||||
|
||||
.table__cell { |
||||
padding: 10px; |
||||
} |
||||
|
||||
.pagination { |
||||
margin-top: 40px; |
||||
display: flex; |
||||
flex-flow: row nowrap; |
||||
align-items: stretch; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.pagination__item { |
||||
list-style: none; |
||||
width: 40px; |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
align-items: center; |
||||
justify-content: center; |
||||
font-size: 0; |
||||
margin-right: 5px; |
||||
} |
||||
|
||||
.pagination__item:last-child { |
||||
margin-right: 0; |
||||
} |
||||
|
||||
.pagination__link { |
||||
text-decoration: none; |
||||
text-transform: uppercase; |
||||
line-height: 1; |
||||
width: 100%; |
||||
height: 100%; |
||||
display: flex; |
||||
flex-flow: column nowrap; |
||||
align-items: center; |
||||
justify-content: center; |
||||
padding: 10px 0px; |
||||
cursor: pointer; |
||||
background-color: #dadada; |
||||
transition: .3s background-color linear, .3s color linear, .3s border linear, .3s box-shadow linear; |
||||
border: 1px solid transparent; |
||||
color: #0054a6; |
||||
font-size: 18px; |
||||
font-weight: bold; |
||||
} |
||||
|
||||
.pagination__link_arrow { |
||||
font-size: 0; |
||||
position: relative; |
||||
} |
||||
|
||||
.pagination__link_arrow::after { |
||||
content: ''; |
||||
position: absolute; |
||||
top: 50%; |
||||
left: 50%; |
||||
width: 5px; |
||||
height: 5px; |
||||
} |
||||
|
||||
.pagination__link_arrow_prev::after { |
||||
transform: translate(-50%, -50%) rotate(-45deg); |
||||
border-left: 1px solid #0054a6; |
||||
border-top: 1px solid #0054a6; |
||||
} |
||||
.pagination__link_arrow_next::after { |
||||
transform: translate(-50%, -50%) rotate(45deg); |
||||
border-right: 1px solid #0054a6; |
||||
border-top: 1px solid #0054a6; |
||||
} |
||||
|
||||
.error-msg { |
||||
text-align: center; |
||||
} |
||||
|
||||
.error-msg svg { |
||||
max-width: 300px; |
||||
} |
||||
|
||||
.hamburger { |
||||
display: none; |
||||
} |
||||
|
||||
.header-bottom__main-menu-switch { |
||||
display: none; |
||||
} |
||||
|
||||
.section-head { |
||||
margin-bottom: 40px; |
||||
} |
||||
|
||||
.disclaimer { |
||||
font-size: 16px; |
||||
margin-bottom: 15px; |
||||
} |
||||
|
||||
.disclaimer:last-child { |
||||
margin-bottom: 30px; |
||||
} |
||||
|
||||
@media screen and (max-width: 920px) { |
||||
.container { |
||||
padding: 0 8px; |
||||
} |
||||
|
||||
.header__logo { |
||||
flex-basis: auto; |
||||
margin-bottom: 5px; |
||||
} |
||||
|
||||
.logo svg { |
||||
max-width: 120px; |
||||
} |
||||
|
||||
.header__search .search__text-input { |
||||
max-width: unset; |
||||
flex-grow: 1; |
||||
margin: 0; |
||||
} |
||||
|
||||
.header__top { |
||||
display: none; |
||||
} |
||||
|
||||
.header__bottom { |
||||
/* background-color: rgba(30, 30, 80, .8); */ |
||||
padding: 3px 5px; |
||||
position: relative; |
||||
display: flex; |
||||
flex-flow: row nowrap; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
.header__bottom:before { |
||||
content: ""; |
||||
z-index: 0; |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
width: 100%; |
||||
height: 100%; |
||||
background-color: rgba(190, 190, 190, .6); |
||||
backdrop-filter: blur(1px); |
||||
-webkit-backdrop-filter: blur(1px); |
||||
} |
||||
|
||||
.header__mobile-logo { |
||||
display: block; |
||||
margin-left: 10px; |
||||
margin-right: auto; |
||||
width: 100px; |
||||
position: relative; |
||||
z-index: 1; |
||||
} |
||||
|
||||
.header__mobile-logo .st2 { |
||||
/* fill: #fff; */ |
||||
} |
||||
|
||||
.header__mobile-search { |
||||
display: flex; |
||||
margin-left: 10px; |
||||
position: relative; |
||||
z-index: 1; |
||||
} |
||||
|
||||
.header__mobile-search .search__btn { |
||||
padding: 3px 5px; |
||||
} |
||||
|
||||
.header__mobile-search svg { |
||||
width: 15px; |
||||
} |
||||
|
||||
.header__mobile-search .search__text-input { |
||||
width: 140px; |
||||
} |
||||
|
||||
.hamburger { |
||||
display: block; |
||||
} |
||||
|
||||
.hamburger__stripes { |
||||
border-top: 2px solid #333; |
||||
border-bottom: 2px solid #333; |
||||
width: 20px; |
||||
height: 14px; |
||||
position: relative |
||||
} |
||||
|
||||
.hamburger__stripes:after { |
||||
content: ""; |
||||
position: absolute; |
||||
height: 2px; |
||||
width: 100%; |
||||
top: 50%; |
||||
left: 0; |
||||
transform: translateY(-50%); |
||||
background-color: #333; |
||||
} |
||||
|
||||
.main-menu { |
||||
background-color: #fff; |
||||
position: absolute; |
||||
left: 0; |
||||
top: 100%; |
||||
height: calc(100vh - 32px); |
||||
background-color: #e6e6e6; |
||||
transform: translateX(-100%); |
||||
transition: .2s transform linear, .1s box-shadow linear; |
||||
box-shadow: none; |
||||
} |
||||
|
||||
.main-menu__list { |
||||
flex-flow: column nowrap; |
||||
align-items: stretch; |
||||
justify-content: flex-start; |
||||
} |
||||
|
||||
.main-menu__item { |
||||
margin-right: 0; |
||||
padding: 0 8px; |
||||
min-width: 30vw; |
||||
} |
||||
|
||||
.main-menu__link { |
||||
box-shadow: none; |
||||
border-bottom: 1px solid #aaa |
||||
} |
||||
|
||||
.header-bottom__main-menu-switch:checked ~ .main-menu { |
||||
transform: translateX(0); |
||||
box-shadow: 0px 0px 12px 4px rgba(0, 0, 0, .5) |
||||
} |
||||
|
||||
.title { |
||||
font-size: 16px; |
||||
} |
||||
|
||||
.table { |
||||
font-size: 14px; |
||||
} |
||||
|
||||
.table__cell { |
||||
padding: 6px 4px; |
||||
word-break: break-word; |
||||
min-width: 48px; |
||||
} |
||||
|
||||
.text-input { |
||||
padding: 0 6px; |
||||
font-size: 16px; |
||||
} |
||||
.text-input::placeholder { |
||||
font-size: 14px; |
||||
} |
||||
|
||||
.pagination__link { |
||||
padding: 3px 0px; |
||||
font-size: 14px; |
||||
} |
||||
|
||||
.adder__btn, .jumper__btn, .search-page__btn { |
||||
font-size: 16px; |
||||
} |
||||
|
||||
.footer { |
||||
font-size: 12px; |
||||
} |
||||
} |
||||
|
||||
@media screen and (max-width: 620px) { |
||||
.main-menu__item { |
||||
min-width: 45vw; |
||||
} |
||||
} |
After Width: | Height: | Size: 13 KiB |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
<?php |
||||
|
||||
use App\Router; |
||||
|
||||
require __DIR__ . '/../vendor/autoload.php'; |
||||
|
||||
/* Initialize pages rounting */ |
||||
$r = new Router(); |
||||
|
||||
$r->addRoute('^/$', function($url) { |
||||
require __DIR__ . '/../views/home.php'; |
||||
}); |
||||
|
||||
$r->addRoute('^/add/?$', function($url) { |
||||
require __DIR__ . '/../views/add.php'; |
||||
}); |
||||
|
||||
$r->addRoute('^/alive/?([0-9]+)?/?', function($url, $page = 1) { |
||||
require __DIR__ . '/../views/alive.php'; |
||||
}); |
||||
|
||||
$r->addRoute('^/all/?([0-9]+)?/?', function($url, $page = 1) { |
||||
require __DIR__ . '/../views/all.php'; |
||||
}); |
||||
|
||||
$r->addRoute('^/jump/?(.*)/?', function($url, $query = "") { |
||||
require __DIR__ . '/../views/jump.php'; |
||||
}); |
||||
|
||||
$r->addRoute('^/latest/?$', function($url) { |
||||
require __DIR__ . '/../views/latest.php'; |
||||
}); |
||||
|
||||
$r->addRoute('^/search/?(.*)/?', function($url, $query = "") { |
||||
require __DIR__ . '/../views/search.php'; |
||||
}); |
||||
|
||||
$r->addErrorRoute(function() { |
||||
require __DIR__ . '/../views/404.php'; |
||||
}); |
||||
|
||||
/* Process requests */ |
||||
$r->run(); |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
{% extends "_page.twig" %} |
||||
|
||||
{% block title %}404{% endblock %} |
||||
{% block content %} |
||||
<div class="container container_100vh"> |
||||
<div class="error-msg"> |
||||
<svg version="1.1" id="404" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
viewBox="0 0 386.1 242.8" style="enable-background:new 0 0 386.1 242.8;" xml:space="preserve"> |
||||
<style type="text/css"> |
||||
.st0{fill:#707070;} |
||||
</style> |
||||
<g> |
||||
<path class="st0" d="M97.5,102.3H116v24.6H97.5v34.6H67v-34.6H0.8l-1.2-19l67-105.7h30.9L97.5,102.3L97.5,102.3z M29.5,102.3H67 |
||||
V43.1l-2.9,5.3L29.5,102.3z"/> |
||||
<path class="st0" d="M246.8,100.9c0,10.3-1.2,19.4-3.6,27.2s-5.8,14.4-10.2,19.7s-9.9,9.3-16.3,12c-6.4,2.7-13.6,4-21.7,4 |
||||
c-8,0-15.1-1.4-21.6-4c-6.4-2.7-11.9-6.7-16.4-12s-8-11.9-10.4-19.7c-2.4-7.8-3.7-16.9-3.7-27.2V62.8c0-10.3,1.2-19.4,3.6-27.2 |
||||
s5.9-14.4,10.3-19.7s9.9-9.3,16.3-12c6.4-2.7,13.5-4.1,21.5-4.1c8,0,15.3,1.4,21.7,4.1s11.9,6.7,16.4,12 |
||||
c4.5,5.3,7.9,11.9,10.3,19.7s3.6,16.9,3.6,27.2L246.8,100.9L246.8,100.9z M173.7,87.7l42.5-31.6c-0.2-10.7-2.1-18.7-5.6-23.9 |
||||
c-3.5-5.2-8.8-7.8-15.8-7.8c-7.1,0-12.4,2.7-15.9,8.2s-5.3,13.9-5.3,25.3V87.7z M216.2,76.8l-42.5,31.5c0.4,10.5,2.3,18.3,5.8,23.4 |
||||
c3.5,5.1,8.7,7.6,15.5,7.6c7.1,0,12.5-2.8,15.9-8.3c3.5-5.5,5.2-14,5.2-25.5L216.2,76.8L216.2,76.8z"/> |
||||
<path class="st0" d="M366.4,102.3h18.5v24.6h-18.5v34.6H336v-34.6h-66.2l-1.2-19l67-105.7h30.9L366.4,102.3L366.4,102.3z |
||||
M298.5,102.3H336V43.1l-3,5.3L298.5,102.3z"/> |
||||
</g> |
||||
<g> |
||||
<path class="st0" d="M34.8,241.6h-9.9l-14.3-33.1v33.1h-10v-51.3h10l14.2,33l0-33h9.9V241.6z"/> |
||||
<path class="st0" d="M80.1,219.7c0,3.3-0.4,6.3-1.3,9.1c-0.9,2.8-2.1,5.2-3.8,7.2c-1.6,2-3.6,3.6-6,4.7s-5,1.7-8,1.7 |
||||
c-3,0-5.6-0.6-7.9-1.7s-4.2-2.7-5.8-4.7s-2.8-4.4-3.6-7.2s-1.2-5.8-1.2-9.1v-7.5c0-3.3,0.4-6.3,1.2-9.1c0.8-2.8,2-5.2,3.6-7.2 |
||||
c1.6-2,3.5-3.6,5.8-4.7c2.3-1.1,4.9-1.7,7.9-1.7c3,0,5.7,0.6,8,1.7s4.4,2.7,6,4.7c1.6,2,2.9,4.4,3.8,7.2c0.9,2.8,1.3,5.8,1.3,9.1 |
||||
L80.1,219.7L80.1,219.7z M70.1,212.2c0-2.1-0.2-4-0.5-5.8s-0.9-3.3-1.6-4.5c-0.7-1.3-1.7-2.3-2.8-3s-2.5-1.1-4.1-1.1 |
||||
c-1.6,0-2.9,0.4-4,1.1s-1.9,1.7-2.6,3c-0.7,1.3-1.1,2.8-1.4,4.5c-0.3,1.8-0.4,3.7-0.4,5.8v7.5c0,2.1,0.1,4,0.4,5.8 |
||||
c0.3,1.8,0.8,3.3,1.4,4.6c0.7,1.3,1.5,2.3,2.6,3s2.4,1.1,4.1,1.1c1.6,0,3-0.4,4.1-1.1s2.1-1.7,2.8-3c0.7-1.3,1.3-2.8,1.6-4.6 |
||||
s0.5-3.7,0.5-5.8L70.1,212.2L70.1,212.2z"/> |
||||
<path class="st0" d="M125.4,198.4h-15.8v43.3h-9.9v-43.3H84v-8.1h41.4V198.4z"/> |
||||
<path class="st0" d="M206.9,220.4h-22v21.3h-9.9v-51.3h34.7v8.1h-24.8v14h22V220.4z"/> |
||||
<path class="st0" d="M253.5,219.7c0,3.3-0.4,6.3-1.3,9.1c-0.9,2.8-2.1,5.2-3.8,7.2c-1.6,2-3.6,3.6-6,4.7s-5,1.7-8,1.7 |
||||
s-5.6-0.6-7.9-1.7s-4.2-2.7-5.8-4.7c-1.6-2-2.8-4.4-3.6-7.2c-0.8-2.8-1.2-5.8-1.2-9.1v-7.5c0-3.3,0.4-6.3,1.2-9.1 |
||||
c0.8-2.8,2-5.2,3.6-7.2c1.6-2,3.5-3.6,5.8-4.7c2.3-1.1,4.9-1.7,7.9-1.7c3,0,5.7,0.6,8,1.7s4.4,2.7,6,4.7c1.6,2,2.9,4.4,3.8,7.2 |
||||
c0.9,2.8,1.3,5.8,1.3,9.1L253.5,219.7L253.5,219.7z M243.4,212.2c0-2.1-0.2-4-0.5-5.8s-0.9-3.3-1.6-4.5s-1.7-2.3-2.8-3 |
||||
c-1.1-0.7-2.5-1.1-4.1-1.1c-1.6,0-2.9,0.4-4,1.1s-2,1.7-2.6,3c-0.7,1.3-1.1,2.8-1.4,4.5s-0.4,3.7-0.4,5.8v7.5c0,2.1,0.1,4,0.4,5.8 |
||||
s0.8,3.3,1.4,4.6c0.7,1.3,1.5,2.3,2.6,3s2.4,1.1,4,1.1c1.6,0,3-0.4,4.1-1.1c1.1-0.7,2.1-1.7,2.8-3s1.3-2.8,1.6-4.6s0.5-3.7,0.5-5.8 |
||||
L243.4,212.2L243.4,212.2z"/> |
||||
<path class="st0" d="M295.2,190.3l0,34c0,2.9-0.4,5.4-1.2,7.6c-0.8,2.2-1.9,4.1-3.4,5.7c-1.5,1.5-3.3,2.7-5.5,3.5s-4.6,1.2-7.4,1.2 |
||||
c-2.6,0-4.9-0.4-7-1.2s-3.9-2-5.3-3.5s-2.6-3.4-3.4-5.7c-0.8-2.2-1.2-4.8-1.2-7.7l0-34h9.9l0,34c0,3.3,0.6,5.8,1.8,7.5 |
||||
s2.9,2.5,5.2,2.5c2.4,0,4.3-0.9,5.5-2.5s1.9-4.2,2-7.5l0-34H295.2z"/> |
||||
<path class="st0" d="M338.1,241.6h-9.9L314,208.5v33.1h-10v-51.3h10l14.2,33l0-33h9.9L338.1,241.6L338.1,241.6z"/> |
||||
<path class="st0" d="M347.4,241.6v-51.3h13.3c3.4,0,6.5,0.6,9.3,1.8c2.8,1.2,5.1,2.8,7.1,4.9s3.5,4.6,4.5,7.5 |
||||
c1.1,2.9,1.6,6.1,1.6,9.5v4.2c0,3.5-0.5,6.6-1.6,9.5c-1.1,2.9-2.6,5.4-4.5,7.4c-1.9,2.1-4.2,3.7-7,4.9s-5.7,1.7-9,1.7L347.4,241.6 |
||||
L347.4,241.6z M357.4,198.3v35.3h3.8c1.9,0,3.5-0.4,5-1.1s2.7-1.7,3.7-3.1s1.8-3,2.3-4.9c0.5-1.9,0.8-4.1,0.8-6.5v-4.3 |
||||
c0-2.3-0.3-4.4-0.8-6.3s-1.3-3.5-2.4-4.9s-2.3-2.4-3.9-3.2s-3.3-1.1-5.3-1.1L357.4,198.3L357.4,198.3z"/> |
||||
</g> |
||||
</svg> |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
@ -0,0 +1,105 @@
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
{% block head %} |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<title>{% block title %}{% endblock %} - Registry</title> |
||||
<link rel="preload" href="/css/style.css" as="style"> |
||||
<link rel="preload" href="/img/logo.svg" as="image"> |
||||
<style> |
||||
@media screen and (max-width: 920px) { |
||||
.main-menu {transform: translateX(-100%);} |
||||
} |
||||
</style> |
||||
<link rel="stylesheet" href="/css/style.css"/> |
||||
{% endblock %} |
||||
</head> |
||||
<body> |
||||
<!-- <div id="loadOverlay" style="background-color:#333; position:absolute; top:0px; left:0px; width:100%; height:100%; z-index:2000;"></div> --> |
||||
<header class="header"> |
||||
<div class="header__top"> |
||||
<div class="logo header__logo"> |
||||
<a href="/" class="logo__link"> |
||||
<img class="svg" src="/img/logo.svg" /> |
||||
</a> |
||||
</div> |
||||
<form action="/search" method="post" class="search header__search"> |
||||
<input type="text" name="q" class="text-input search__text-input" maxlength="67" placeholder="Search address"> |
||||
<button type="submit" class="search__btn"> |
||||
<svg enable-background="new 0 0 32 32" id="search_svg" version="1.1" viewBox="0 0 32 32" width="20px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
||||
<g id="search_1_"> |
||||
<path d="M20,0.005c-6.627,0-12,5.373-12,12c0,2.026,0.507,3.933,1.395,5.608l-8.344,8.342l0.007,0.006 C0.406,26.602,0,27.49,0,28.477c0,1.949,1.58,3.529,3.529,3.529c0.985,0,1.874-0.406,2.515-1.059l-0.002-0.002l8.341-8.34 c1.676,0.891,3.586,1.4,5.617,1.4c6.627,0,12-5.373,12-12C32,5.378,26.627,0.005,20,0.005z M4.795,29.697 c-0.322,0.334-0.768,0.543-1.266,0.543c-0.975,0-1.765-0.789-1.765-1.764c0-0.498,0.21-0.943,0.543-1.266l-0.009-0.008l8.066-8.066 c0.705,0.951,1.545,1.791,2.494,2.498L4.795,29.697z M20,22.006c-5.522,0-10-4.479-10-10c0-5.522,4.478-10,10-10 c5.521,0,10,4.478,10,10C30,17.527,25.521,22.006,20,22.006z"/> |
||||
<path d="M20,5.005c-3.867,0-7,3.134-7,7c0,0.276,0.224,0.5,0.5,0.5s0.5-0.224,0.5-0.5c0-3.313,2.686-6,6-6 c0.275,0,0.5-0.224,0.5-0.5S20.275,5.005,20,5.005z" /> |
||||
</g> |
||||
</svg> |
||||
</button> |
||||
</form> |
||||
</div> |
||||
<div class="header__bottom"> |
||||
<div class="hamburger"> |
||||
<label for="header-bottom__main-menu-switch" class="hamburger__label"> |
||||
<div class="hamburger__stripes"> |
||||
</div> |
||||
</label> |
||||
</div> |
||||
<input type="checkbox" class="header-bottom__main-menu-switch" id="header-bottom__main-menu-switch"> |
||||
<nav class="main-menu"> |
||||
<ul class="main-menu__list"> |
||||
<li class="main-menu__item"> |
||||
<a href="/" class="main-menu__link"> |
||||
home |
||||
</a> |
||||
</li> |
||||
<li class="main-menu__item"> |
||||
<a href="/add" class="main-menu__link"> |
||||
add |
||||
</a> |
||||
</li> |
||||
<li class="main-menu__item"> |
||||
<a href="/jump" class="main-menu__link"> |
||||
jump |
||||
</a> |
||||
</li> |
||||
<li class="main-menu__item"> |
||||
<a href="/latest" class="main-menu__link"> |
||||
latest |
||||
</a> |
||||
</li> |
||||
<li class="main-menu__item"> |
||||
<a href="/alive" class="main-menu__link"> |
||||
alive |
||||
</a> |
||||
</li> |
||||
<li class="main-menu__item"> |
||||
<a href="/all" class="main-menu__link"> |
||||
all |
||||
</a> |
||||
</li> |
||||
</ul> |
||||
</nav> |
||||
<div class="logo header__mobile-logo"> |
||||
<a href="/" class="logo__link"> |
||||
<img src="/img/logo.svg" /> |
||||
</a> |
||||
</div> |
||||
<form action="/search" method="post" class="search header__mobile-search"> |
||||
<input type="text" name="q" class="text-input search__text-input" maxlength="67" placeholder="Search address"> |
||||
<button type="submit" class="search__btn"> |
||||
<svg enable-background="new 0 0 32 32" id="search_svg" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> |
||||
<g id="search_1_"> |
||||
<path d="M20,0.005c-6.627,0-12,5.373-12,12c0,2.026,0.507,3.933,1.395,5.608l-8.344,8.342l0.007,0.006 C0.406,26.602,0,27.49,0,28.477c0,1.949,1.58,3.529,3.529,3.529c0.985,0,1.874-0.406,2.515-1.059l-0.002-0.002l8.341-8.34 c1.676,0.891,3.586,1.4,5.617,1.4c6.627,0,12-5.373,12-12C32,5.378,26.627,0.005,20,0.005z M4.795,29.697 c-0.322,0.334-0.768,0.543-1.266,0.543c-0.975,0-1.765-0.789-1.765-1.764c0-0.498,0.21-0.943,0.543-1.266l-0.009-0.008l8.066-8.066 c0.705,0.951,1.545,1.791,2.494,2.498L4.795,29.697z M20,22.006c-5.522,0-10-4.479-10-10c0-5.522,4.478-10,10-10 c5.521,0,10,4.478,10,10C30,17.527,25.521,22.006,20,22.006z"/> |
||||
<path d="M20,5.005c-3.867,0-7,3.134-7,7c0,0.276,0.224,0.5,0.5,0.5s0.5-0.224,0.5-0.5c0-3.313,2.686-6,6-6 c0.275,0,0.5-0.224,0.5-0.5S20.275,5.005,20,5.005z" /> |
||||
</g> |
||||
</svg> |
||||
</button> |
||||
</form> |
||||
</div> |
||||
</header> |
||||
<main id="content"> |
||||
{% block content %}{% endblock %} |
||||
</main> |
||||
<footer style="color: #666666" class="footer" id="footer"> |
||||
Powered using i2pd by PurpleI2P team, {{ "now"|date("Y") == '2021' ? "now"|date("Y") : '2021-' ~ "now"|date("Y") }} |
||||
</footer> |
||||
</body> |
||||
</html> |
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
{% macro pagination(total, current, url, nearbyPagesLimit = 4) %} |
||||
|
||||
{# Create "main_url" variable with link for the first page #} |
||||
{% set foo = url|split('/') %} |
||||
{% set foo = foo|slice(0, -1) %} |
||||
{% set main_url = foo|join('/') ~ "/" %} |
||||
|
||||
{% apply spaceless %} |
||||
{% if total > 1 %} |
||||
<div class="row"> |
||||
<nav> |
||||
<ul class="pagination"> |
||||
{% if current > 1 %} |
||||
<li class="pagination__item pagination__item_arrow"> |
||||
<a class="prev pagination__link pagination__link_arrow pagination__link_arrow_prev" href="{{ (url ~ (current-1))|e }}">«</a> |
||||
</li> |
||||
{% endif %} |
||||
|
||||
{% for i in 1..total %} |
||||
{% if 0 == (current - nearbyPagesLimit) - loop.index %} |
||||
<li class="pagination__item"> |
||||
<a href="{{ (url ~ 1)|e }}" class="pagination__link">1 |
||||
</a> |
||||
</li> |
||||
{% if 1 != loop.index %} |
||||
<li class="pagination__item"> |
||||
<a href="#" class="pagination__link"><span>...</span> |
||||
</a> |
||||
</li> |
||||
{% endif %} |
||||
{% elseif 0 == (current + nearbyPagesLimit) - loop.index and (current + nearbyPagesLimit) < total %} |
||||
<li class="pagination__item pagination__item_see-more"><a href="#" class=" pagination__link_see-more pagination__link"><span>...</span></a></li> |
||||
{% elseif 0 < (current - nearbyPagesLimit) - loop.index %} |
||||
{% elseif 0 > (current + nearbyPagesLimit) - loop.index %} |
||||
{% else %} |
||||
{% if current == loop.index %} |
||||
<li class="active pagination__item pagination__item_active"><a href="#" class="pagination__link"><span aria-current="page">{{ loop.index }}</span></a></li> |
||||
{% else %} |
||||
{% if loop.index == 1 %} |
||||
<li class="pagination__item"><a href="{{ main_url }}" class="pagination__link">{{ loop.index }}</a></li> |
||||
{% else %} |
||||
<li class="pagination__item"><a href="{{ url ~ loop.index }}" class="pagination__link">{{ loop.index }}</a></li> |
||||
{% endif %} |
||||
{% endif %} |
||||
{% endif %} |
||||
{% endfor %} |
||||
{% if current != total and (current + nearbyPagesLimit) < total %} |
||||
<li class="pagination__item"><a href="{{ (url ~ total)|e }}" class="pagination__link">{{ total }}</a></li> |
||||
{% endif %} |
||||
{% if current < total %} |
||||
<li class="pagination__item pagination__item_arrow"><a class="next pagination__link pagination__link_arrow pagination__link_arrow_next" href="{{ (url ~ (current+1))|e }}">»</a></li> |
||||
{% endif %} |
||||
</ul> |
||||
</nav> |
||||
</div> |
||||
{% endif %} |
||||
{% endapply %} |
||||
{% endmacro %} |
||||
|
||||
{{ _self.pagination(total, current, url) }} |
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
{% extends "_page.twig" %} |
||||
|
||||
{% block title %}Add record{% endblock %} |
||||
{% block content %} |
||||
<div class="container"> |
||||
<div class="adder"> |
||||
{% if result|length > 0 %} |
||||
{% if result.error|length > 0 %} |
||||
<div class="adder__err"> |
||||
<h3 class="adder__title title">Error</h3> |
||||
<b style="color: red;">{{ result.error }}</b> |
||||
</div> |
||||
{% else %} |
||||
<div class="adder__succ"> |
||||
<div class="adder__succ-body"> |
||||
<h3 class="adder__title title">Domain successfuly added</h3> |
||||
<div class="adder__line line adder__line_reshost"> |
||||
<span> |
||||
Domain: |
||||
</span> |
||||
<span> |
||||
{{ result.host }} |
||||
</span> |
||||
</div> |
||||
<div class="adder__line line adder__line_adrhelper"> |
||||
<span> |
||||
Addresshelper: |
||||
</span> |
||||
<a href="http://{{ result.host }}/?i2paddresshelper={{ result.base64 }}">Go to site</a> |
||||
</div> |
||||
<div class="adder__line line adder__line_base32"> |
||||
<span> |
||||
Base32: |
||||
</span> |
||||
<span class="crazy-base64-span"> |
||||
{{ result.base32 }}.b32.i2p |
||||
</span> |
||||
</div> |
||||
<div class="adder__line line adder__line_base64"> |
||||
<span> |
||||
Base64: |
||||
</span> |
||||
<span class="crazy-base64-span"> |
||||
{{ result.base64 }} |
||||
</span> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{% endif %} |
||||
{% endif %} |
||||
<div class="adder__adder-itself"> |
||||
<p class="adder__title title important"> |
||||
Domain adding |
||||
</p> |
||||
<form action="/add" method="post" class="adder__form"> |
||||
<div class="form__field"> |
||||
<label for="record"> |
||||
<span> |
||||
Auth string: |
||||
</span> |
||||
</label> |
||||
<input class="text-input adder__text-input" type="text" id="record" name="record" placeholder="domain.i2p=base64#!sig=hash"{% if record|length > 0 %} value="{{ record }}"{% endif %} required> |
||||
</div> |
||||
<div class="form__field"> |
||||
<label for="desc">Description:</label> |
||||
<input class="text-input adder__text-input" type="text" id="desc" name="desc" placeholder="A bit information about service on domain"{% if desc|length > 0 %} value="{{ desc }}"{% endif %}> |
||||
</div> |
||||
<input class="adder__submit adder__btn btn" type="submit" value="Submit"> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
||||
|
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
{% extends "_page.twig" %} |
||||
|
||||
{% block title %}Alive hosts{% endblock %} |
||||
{% block content %} |
||||
<div class="container"> |
||||
<div class="alive-hosts"> |
||||
<p class="important title" align="center"> |
||||
Alive domains |
||||
</p> |
||||
|
||||
<table class="table"> |
||||
<thead class="table__head"> |
||||
<tr class="table__row"> |
||||
<th class="table__cell">Domain</th> |
||||
<th class="table__cell">Base32</th> |
||||
<th class="table__cell">Last seen</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody class="table__body"> |
||||
{% for host in hosts %} |
||||
<tr class="table__row"> |
||||
<td class="table__cell"><a href="http://{{ host.host }}/?i2paddresshelper={{ host.base64 }}">{{ host.host }}</a></td> |
||||
<td class="table__cell table__cell_long-ass"><a href="http://{{ host.base32 }}.b32.i2p/">{{ host.base32 }}.b32.i2p</a></td> |
||||
<td class="table__cell">{{ host.last_seen }}</td> |
||||
</tr> |
||||
{% endfor %} |
||||
</tbody> |
||||
</table> |
||||
|
||||
{% if total > 1 %} |
||||
{% include "_pagination.twig" with { |
||||
total, |
||||
current, |
||||
url: "/alive/" |
||||
} only %} |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
||||
|
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
{% extends "_page.twig" %} |
||||
|
||||
{% block title %}All hosts{% endblock %} |
||||
{% block content %} |
||||
<div class="container"> |
||||
<div class="all-hosts"> |
||||
|
||||
<p class="important title" align="center"> |
||||
All domains |
||||
</p> |
||||
|
||||
<table class="table"> |
||||
<thead class="table__head"> |
||||
<tr class="table__row"> |
||||
<th class="table__cell">Domain</th> |
||||
<th class="table__cell">Base32</th> |
||||
<th class="table__cell">Last seen</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody class="table__body"> |
||||
{% for host in hosts %} |
||||
<tr class="table__row"> |
||||
<td class="table__cell"><a href="http://{{ host.host }}/?i2paddresshelper={{ host.base64 }}">{{ host.host }}</a></td> |
||||
<td class="table__cell table__cell_long-ass"><a href="http://{{ host.base32 }}.b32.i2p/">{{ host.base32 }}.b32.i2p</a></td> |
||||
<td class="table__cell">{{ host.last_seen != '0000-00-00 00:00:00' ? host.last_seen : 'Never' }}</td> |
||||
</tr> |
||||
{% endfor %} |
||||
</tbody> |
||||
</table> |
||||
|
||||
{% if total > 1 %} |
||||
{% include "_pagination.twig" with { |
||||
total, |
||||
current, |
||||
url: "/all/" |
||||
} only %} |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
||||
|
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
{% extends "_page.twig" %} |
||||
|
||||
{% block title %}Home{% endblock %} |
||||
{% block content %} |
||||
<div class="container container_main"> |
||||
<p class="important title" align="center"> |
||||
This is reg.i2p registry service.<br> |
||||
</p> |
||||
|
||||
<p> |
||||
<b>Info:</b><br> |
||||
Service works, hosts check done every hour.<br> |
||||
Supported commands: |
||||
<ul> |
||||
<li>adding of hosts for 2LD domains (example.i2p)</li> |
||||
<li>adding subdomains (addsubdomain)</li> |
||||
<li>adding/changing destination (adddest, changedest) - destination will be replaced with new one, old will be purged</li> |
||||
</ul> |
||||
</p> |
||||
|
||||
<p> |
||||
<b>Subscription:</b><br> |
||||
For addressbook subscription use <a href="/hosts.txt">/hosts.txt</a> file.<br> |
||||
That link returns basic addressbook if request sent first time or without <code>If-Match-None</code> header.<br> |
||||
On next requests it will return full alive hosts list. |
||||
</p> |
||||
|
||||
<p> |
||||
<b>Static links to lists:</b><br> |
||||
Lists updated every 4 hours. |
||||
<ul> |
||||
<li>Basic list: <a href="/export/hosts-basic.txt">/export/hosts-basic.txt</a> - contains short list of alive popular services</li> |
||||
<li>Alive list: <a href="/export/hosts.txt">/export/hosts.txt</a></li> |
||||
<li>Whole list: <a href="/export/hosts-all.txt">/export/hosts-all.txt</a></li> |
||||
</ul> |
||||
</p> |
||||
|
||||
<p> |
||||
<b>Rules:</b><br> |
||||
Here no particular rules about registering domains, but we have some limitations about handling dead domain records.<br> |
||||
Your domain will be open for registration if it has been dead for: |
||||
<ul> |
||||
<li>{{ delnewdays }} days if registered less than {{ newdays }} days ago</li> |
||||
<li>{{ delolddays }} days if registered more than {{ newdays }} days ago</li> |
||||
</ul> |
||||
</p> |
||||
|
||||
{% if approval %} |
||||
<p> |
||||
<b>Approval:</b><br> |
||||
Domain will be approved after {{ apprdelay }} hours, but only if it has been alive not more than {{ apprseen }} hours ago when approval time is reached. |
||||
</p> |
||||
{% endif %} |
||||
|
||||
{% if fetcher and subscrs|length > 0 %} |
||||
<p> |
||||
<b>External subscriptions:</b><br> |
||||
Our service fetching updates from external subscriptions once a day. List of external services: |
||||
<ul> |
||||
{% for subscr in subscrs %} |
||||
<li>{{ subscr.name }}</li> |
||||
{% endfor %} |
||||
</ul> |
||||
</p> |
||||
</div> |
||||
{% endif %} |
||||
{% endblock %} |
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
{% extends "_page.twig" %} |
||||
|
||||
{% block title %}Jump to site{% endblock %} |
||||
{% block content %} |
||||
<div class="container"> |
||||
<div class="jumper"> |
||||
{% if result|length > 0 %} |
||||
{% if result.error|length > 0 %} |
||||
<div class="jumper__err"> |
||||
<h3 class="jumper__title title">Error</h3> |
||||
<b style="color: red;">{{ result.error }}</b> |
||||
</div> |
||||
{% else %} |
||||
<div class="jumper__succ"> |
||||
<h3 class="jumper__title title">Query result for "{{ result.host }}"</h3> |
||||
<div class="jumper__line line"> |
||||
<span> |
||||
Addresshelper: |
||||
</span> |
||||
<a href="http://{{ result.host }}/?i2paddresshelper={{ result.base64 }}">Go to site</a> |
||||
</div> |
||||
|
||||
<div class="jumper__line line"> |
||||
<span> |
||||
Base32: |
||||
</span> |
||||
<span class="crazy-base64-span"> |
||||
{{ result.base32 }}.b32.i2p |
||||
</span> |
||||
</div> |
||||
<div class="jumper__line line"> |
||||
<span> |
||||
Base64: |
||||
</span> |
||||
<span class="crazy-base64-span"> |
||||
{{ result.base64 }} |
||||
</span> |
||||
</div> |
||||
</div> |
||||
{% endif %} |
||||
{% endif %} |
||||
<div class="jumper__itself"> |
||||
<p class="important jumper__title title"> |
||||
Query domain |
||||
</p> |
||||
<form action="/jump" method="post" class="jumper__form"> |
||||
<div class="form__field"> |
||||
<label for="q">Domain:</label> |
||||
<input class="text-input jumper__text-input" type="text" id="q" name="q" maxlength="67" placeholder="domain.i2p" required> |
||||
</div> |
||||
<input type="submit" value="Submit" class="btn jumper__btn"> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
||||
|
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
{% extends "_page.twig" %} |
||||
|
||||
{% block title %}Latest hosts{% endblock %} |
||||
{% block content %} |
||||
<div class="container"> |
||||
<div class="latest-hosts"> |
||||
|
||||
<p class="important title" align="center"> |
||||
Latest added domains |
||||
</p> |
||||
|
||||
<table class="table"> |
||||
<thead class="table__head"> |
||||
<tr class="table__row"> |
||||
<th class="table__cell">Domain</th> |
||||
<th class="table__cell">Base32</th> |
||||
<th class="table__cell">Added</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody class="table__body"> |
||||
{% for host in hosts %} |
||||
<tr class="table__row"> |
||||
<td class="table__cell"><a href="http://{{ host.host }}/?i2paddresshelper={{ host.base64 }}">{{ host.host }}</a></td> |
||||
<td class="table__cell table__cell_long-ass"><a href="http://{{ host.base32 }}.b32.i2p/">{{ host.base32 }}.b32.i2p</a></td> |
||||
<td class="table__cell">{{ host.add_date }}</td> |
||||
</tr> |
||||
{% endfor %} |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
||||
|
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
{% extends "_page.twig" %} |
||||
|
||||
{% block title %}Search host{% endblock %} |
||||
{% block content %} |
||||
<div class="container"> |
||||
{% if result|length > 0 %} |
||||
<h3 class="title">Query result for "{{ query }}"</h3> |
||||
<div class="disclaimer"> |
||||
<span> |
||||
Note: only first {{ limit }} record(s) are shown |
||||
</span> |
||||
</div> |
||||
{% if result.error|length > 0 %} |
||||
<div class="disclaimer"> |
||||
<span> |
||||
<b>{{ result.error }}</b> |
||||
</span> |
||||
</div> |
||||
{% else %} |
||||
<table class="table"> |
||||
<thead class="table__head"> |
||||
<tr class="table__row"> |
||||
<td class="table__cell">Domain</td> |
||||
<td class="table__cell">Base32</td> |
||||
<td class="table__cell">Last seen</td> |
||||
</tr> |
||||
</thead> |
||||
<tbody class="table__body"> |
||||
{% for host in result %} |
||||
<tr class="table__row"> |
||||
<td class="table__cell"><a href="http://{{ host.host }}/?i2paddresshelper={{ host.base64 }}">{{ host.host }}</a></td> |
||||
<td class="table__cell table__cell_long-ass"><a href="http://{{ host.base32 }}.b32.i2p/">{{ host.base32 }}.b32.i2p</a></td> |
||||
<td class="table__cell">{{ host.last_seen != '0000-00-00 00:00:00' ? host.last_seen : 'Never' }}</td> |
||||
</tr> |
||||
{% endfor %} |
||||
</tbody> |
||||
</table> |
||||
{% endif %} |
||||
{% endif %} |
||||
|
||||
<p class="important title"> |
||||
Search domain |
||||
</p> |
||||
<form action="/search" method="post"> |
||||
<div class="form__field"> |
||||
<label for="q">Query:</label> |
||||
<input class="text-input search-page__text-input" type="text" id="q" name="q" maxlength="67" placeholder="domain.i2p" required> |
||||
</div> |
||||
<input type="submit" value="Submit" class="btn search-page__btn"> |
||||
</form> |
||||
</div> |
||||
{% endblock %} |
||||
|
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
<?php |
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php'; |
||||
require_once __DIR__ . '/../config.php'; |
||||
|
||||
/* Initialize Twig engine */ |
||||
$loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/../templates'); |
||||
$twig = new \Twig\Environment($loader, [ |
||||
'cache' => __DIR__ . '/../cache', |
||||
'auto_reload' => true, |
||||
]); |
||||
|
||||
$template = $twig->load('404.twig'); |
||||
echo $template->render(); |
||||
|
@ -0,0 +1,176 @@
@@ -0,0 +1,176 @@
|
||||
<?php |
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php'; |
||||
require_once __DIR__ . '/../config.php'; |
||||
|
||||
/* Initialize Twig engine */ |
||||
$loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/../templates'); |
||||
$twig = new \Twig\Environment($loader, [ |
||||
'cache' => __DIR__ . '/../cache', |
||||
'auto_reload' => true, |
||||
]); |
||||
|
||||
$record = ""; |
||||
$desc = ""; |
||||
|
||||
$error = ""; |
||||
$result = []; |
||||
|
||||
if (isset($_POST["record"]) && !empty($_POST["record"])) { |
||||
$record = (string) $_POST["record"]; |
||||
|
||||
if (isset($_POST["desc"]) && !empty($_POST["desc"])) { |
||||
$desc = (string) $_POST["desc"]; |
||||
} |
||||
|
||||
$pdo = (new App\DB($options))->pdo; |
||||
$util = new App\Utils; |
||||
|
||||
$parsed = $util->parseHostRecord($record); |
||||
|
||||
if (!$util->isValidAddress($parsed['host'], $error)) { |
||||
$result["error"] = "Error while validating: " . $error; |
||||
} else { |
||||
if ($util->isPunycodeDomain($parsed['host'])) { |
||||
$domain = idn_to_utf8($parsed['host']); |
||||
} else { |
||||
$domain = $parsed['host']; |
||||
} |
||||
|
||||
/* Check if such domain name already registered */ |
||||
$STH = $pdo->query("SELECT COUNT(*) FROM `hosts` WHERE `host` = '" . $domain . "' LIMIT 1"); |
||||
|
||||
if($STH->fetchColumn() == 1) { |
||||
$result["error"] = "Error while validating: That domain is already registered."; |
||||
|
||||
} else { |
||||
if (!isset($parsed["commands"]) || !isset($parsed["commands"]["sig"])) { |
||||
$result["error"] = "Error while validating: No extended record fields or signature is found."; |
||||
|
||||
} else if (!$util->verifyHostRecord($record, $error)) { |
||||
$result["error"] = "Error while validating: " . $error[0]; |
||||
|
||||
} else { |
||||
if (isset($parsed["commands"]["action"])) { |
||||
switch ($parsed["commands"]["action"]) { |
||||
case 'addsubdomain': |
||||
if (!isset($parsed["commands"]["oldname"]) || !isset($parsed["commands"]["olddest"]) || !isset($parsed["commands"]["oldsig"])) { |
||||
$result["error"] = "Error while validating: required fields not found. Re-check your registration string."; |
||||
|
||||
} else { |
||||
/* Getting domain at higher level (2LD for registering 3LD and etc.) and validating that domain is lower than 2LD. */ |
||||
$darr = explode(".", $domain); |
||||
$dtop = ""; |
||||
|
||||
for ($i = 1; $i < sizeof ($darr); $i++) { |
||||
$dtop .= $darr[$i]; |
||||
if ((sizeof ($darr) - 1) != $i) $dtop .= "."; |
||||
} |
||||
|
||||
if (sizeof($darr) < 3) { |
||||
$result["error"] = "Error while validating: you can't register second level domain (example.i2p) using addsubdomain action."; |
||||
|
||||
} else if ($dtop != $parsed["commands"]["oldname"]) { |
||||
$result["error"] = "Error while validating: oldname value is not same as your higher level domain."; |
||||
|
||||
} else if (!$pdo->query("SELECT COUNT(*) FROM `hosts` WHERE `host` = '" . $parsed["commands"]["oldname"] . "' AND `base64` = '" . $parsed["commands"]["olddest"] . "' LIMIT 1")->fetchColumn()) { |
||||
$result["error"] = "Error while validating: can't find higher level domain with values from oldname and olddest."; |
||||
|
||||
} else { |
||||
$base32 = $util->b32from64($parsed["b64"]); |
||||
if (!$pdo->exec("INSERT INTO `hosts` (`host`, `base64`, `base32`) VALUES ('" . $domain . "', '" . $parsed["b64"] . "', '" . $base32 . "')")) { |
||||
$result["error"] = "Error happened while inserting record to database. Please try again later."; |
||||
|
||||
} else { |
||||
$result["host"] = $domain; |
||||
$result["base64"] = $parsed["b64"]; |
||||
$result["base32"] = $base32; |
||||
} |
||||
} |
||||
} |
||||
break; |
||||
case 'adddest': |
||||
case 'changedest': |
||||
if (!isset($parsed["commands"]["olddest"]) || !isset($parsed["commands"]["oldsig"])) { |
||||
$result["error"] = "Error while validating: required fields not found. Re-check your registration string."; |
||||
|
||||
} else { |
||||
if (!$pdo->query("SELECT COUNT(*) FROM `hosts` WHERE `host` = '" . $domain . "' AND `base64` = '" . $parsed["commands"]["olddest"] . "' LIMIT 1")->fetchColumn()) { |
||||
$result["error"] = "Error while validating: old base64 and value in olddest field does not match.."; |
||||
|
||||
} else { |
||||
$base32 = $util->b32from64($parsed["b64"]); |
||||
if (!$pdo->exec("UPDATE `hosts` SET `base64` = '" . $parsed["b64"] . "', `base32` = '" . $base32 . "' WHERE `host` = '" . $domain . "'")) { |
||||
$result["error"] = "Error happened while updating record in database. Please try again later."; |
||||
|
||||
} else { |
||||
$result["host"] = $domain; |
||||
$result["base64"] = $parsed["b64"]; |
||||
$result["base32"] = $base32; |
||||
} |
||||
} |
||||
} |
||||
break; |
||||
case 'addname': |
||||
if (!isset($parsed["commands"]["olddest"]) || !isset($parsed["commands"]["oldsig"])) { |
||||
$result["error"] = "Error while validating: required fields not found. Re-check your registration string."; |
||||
|
||||
} else { |
||||
if (!$pdo->query("SELECT COUNT(*) FROM `hosts` WHERE `host` = '" . $domain . "' AND `base64` = '" . $parsed["commands"]["olddest"] . "' LIMIT 1")->fetchColumn()) { |
||||
$result["error"] = "Error while validating: old base64 and value in olddest field does not match.."; |
||||
|
||||
} else { |
||||
$base32 = $util->b32from64($parsed["b64"]); |
||||
if (!$pdo->exec("UPDATE `hosts` SET `base64` = '" . $parsed["b64"] . "', `base32` = '" . $base32 . "' WHERE `host` = '" . $domain . "'")) { |
||||
$result["error"] = "Error happened while updating record in database. Please try again later."; |
||||
|
||||
} else { |
||||
$result["host"] = $domain; |
||||
$result["base64"] = $parsed["b64"]; |
||||
$result["base32"] = $base32; |
||||
} |
||||
} |
||||
} |
||||
break; |
||||
default: |
||||
$result["error"] = "Error while validating: extended record fields are NOT supported for now."; |
||||
break; |
||||
} |
||||
|
||||
} else { |
||||
if (isset($parsed["commands"]["oldname"]) || isset($parsed["commands"]["olddest"]) || isset($parsed["commands"]["oldsig"])) { |
||||
$result["error"] = "Error while validating: unexpected fields found."; |
||||
|
||||
} else if (sizeof(explode(".", $domain)) > 2) { |
||||
$result["error"] = "Error while validating: you can't register subdomain without specific action field."; |
||||
|
||||
} else { |
||||
$base32 = $util->b32from64($parsed["b64"]); |
||||
|
||||
/* Adding to database 2LD domain */ |
||||
if (!$pdo->exec("INSERT INTO `hosts` (`host`, `base64`, `base32`) VALUES ('" . $domain . "', '" . $parsed["b64"] . "', '" . $base32 . "')")) { |
||||
$result["error"] = "Error happened while inserting record to database. Please try again later."; |
||||
|
||||
} else { |
||||
$result["host"] = $domain; |
||||
$result["base64"] = $parsed["b64"]; |
||||
$result["base32"] = $base32; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} else if (isset($_POST["desc"]) && !empty($_POST["desc"])) { |
||||
$result["error"] = "Authentication string is required for registering record."; |
||||
} |
||||
|
||||
if (!empty($result)) { |
||||
$record = $desc = ""; // clear them if any result present |
||||
} |
||||
|
||||
$pdo = null; |
||||
|
||||
$template = $twig->load('add.twig'); |
||||
echo $template->render(['record' => $record, 'desc' => $desc, 'result' => $result]); |
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
<?php |
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php'; |
||||
require_once __DIR__ . '/../config.php'; |
||||
|
||||
/* Initialize Twig engine */ |
||||
$loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/../templates'); |
||||
$twig = new \Twig\Environment($loader, [ |
||||
'cache' => __DIR__ . '/../cache', |
||||
'auto_reload' => true, |
||||
]); |
||||
|
||||
$offset = $options["tableitems"] * ($page - 1); |
||||
$newregoffs = date ("Y-m-d H:i:s", strtotime ("-7 day")); |
||||
$newseenlim = date ("Y-m-d H:i:s", strtotime ("-3 day")); |
||||
$oldseenlim = date ("Y-m-d H:i:s", strtotime ("-7 day")); |
||||
|
||||
$pdo = (new App\DB($options))->pdo; |
||||
|
||||
/* Get records amount */ |
||||
$STH = $pdo->query ("SELECT COUNT(*) as `count` FROM `hosts` " . |
||||
"WHERE `approved` = 1 AND (" . |
||||
" (`add_date` < '" . $newregoffs . "' AND `last_seen` > '" . $oldseenlim . "') OR" . |
||||
" (`add_date` > '" . $newregoffs . "' AND `last_seen` > '" . $newseenlim . "')" . |
||||
")"); |
||||
$STH->setFetchMode (PDO::FETCH_ASSOC); |
||||
$records = $STH->fetch()["count"]; |
||||
|
||||
$pages = intdiv($records, $options["tableitems"]) + 1; |
||||
|
||||
/* Get records with limit */ |
||||
$STH = $pdo->query ("SELECT `host`, `base64`, `base32`, `last_seen` FROM `hosts` " . |
||||
"WHERE `approved` = 1 AND (" . |
||||
" (`add_date` < '" . $newregoffs . "' AND `last_seen` > '" . $oldseenlim . "') OR" . |
||||
" (`add_date` > '" . $newregoffs . "' AND `last_seen` > '" . $newseenlim . "')" . |
||||
") LIMIT " . $offset . ", " . $options["tableitems"]); |
||||
$STH->setFetchMode(PDO::FETCH_ASSOC); |
||||
$rows = $STH->fetchAll(); |
||||
|
||||
$template = $twig->load('alive.twig'); |
||||
echo $template->render(['current' => $page, 'total' => $pages, 'hosts' => $rows]); |
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
<?php |
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php'; |
||||
require_once __DIR__ . '/../config.php'; |
||||
|
||||
/* Initialize Twig engine */ |
||||
$loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/../templates'); |
||||
$twig = new \Twig\Environment($loader, [ |
||||
'cache' => __DIR__ . '/../cache', |
||||
'auto_reload' => true, |
||||
]); |
||||
|
||||
$offset = $options["tableitems"] * ($page - 1); |
||||
$newregoffs = date ("Y-m-d H:i:s", strtotime ("-7 day")); |
||||
$newseenlim = date ("Y-m-d H:i:s", strtotime ("-3 day")); |
||||
$oldseenlim = date ("Y-m-d H:i:s", strtotime ("-7 day")); |
||||
|
||||
$pdo = (new App\DB($options))->pdo; |
||||
|
||||
/* Get records amount */ |
||||
$STH = $pdo->query ("SELECT COUNT(*) as `count` FROM `hosts`"); |
||||
$STH->setFetchMode (PDO::FETCH_ASSOC); |
||||
$records = $STH->fetch()["count"]; |
||||
|
||||
$pages = intdiv($records, $options["tableitems"]) + 1; |
||||
|
||||
/* Get records with limit */ |
||||
$STH = $pdo->query ("SELECT `host`, `base64`, `base32`, `last_seen` FROM `hosts` LIMIT " . $offset . ", " . $options["tableitems"]); |
||||
$STH->setFetchMode(PDO::FETCH_ASSOC); |
||||
$rows = $STH->fetchAll(); |
||||
|
||||
$template = $twig->load('all.twig'); |
||||
echo $template->render(['current' => $page, 'total' => $pages, 'hosts' => $rows]); |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
<?php |
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php'; |
||||
require_once __DIR__ . '/../config.php'; |
||||
|
||||
/* Initialize Twig engine */ |
||||
$loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/../templates'); |
||||
$twig = new \Twig\Environment($loader, [ |
||||
'cache' => __DIR__ . '/../cache', |
||||
'auto_reload' => true, |
||||
]); |
||||
|
||||
$subscrs = array(); |
||||
|
||||
if ($options['fetcher']) { |
||||
$pdo = (new App\DB($options))->pdo; |
||||
|
||||
$STH = $pdo->query ("SELECT `name` FROM `subscriptions` WHERE `active` = 1"); |
||||
$STH->setFetchMode(PDO::FETCH_ASSOC); |
||||
$subscrs = $STH->fetchAll(); |
||||
} |
||||
|
||||
$vars = array( |
||||
'approval' => $options['approval'], |
||||
'apprdelay' => $options['approvedelay'], |
||||
'apprseen' => $options['approveseen'], |
||||
'newdays' => $options['newdays'], |
||||
'delnewdays' => $options['delnewdays'], |
||||
'delolddays' => $options['delolddays'], |
||||
'fetcher' => $options['fetcher'], |
||||
'subscrs' => $subscrs |
||||
); |
||||
|
||||
$template = $twig->load('home.twig'); |
||||
echo $template->render($vars); |
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
<?php |
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php'; |
||||
require_once __DIR__ . '/../config.php'; |
||||
|
||||
/* Initialize Twig engine */ |
||||
$loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/../templates'); |
||||
$twig = new \Twig\Environment($loader, [ |
||||
'cache' => __DIR__ . '/../cache', |
||||
'auto_reload' => true, |
||||
]); |
||||
|
||||
$utils = new App\Utils; |
||||
|
||||
$domain = ""; |
||||
$result = []; |
||||
$error = ""; |
||||
if (isset($query) && strlen($query) > 67 || isset($_POST["q"]) && strlen($_POST["q"]) > 67) |
||||
$result["error"] = "Request is too long, max length is 67 chars"; |
||||
else if (isset($query) && !empty($query)) |
||||
$domain = htmlspecialchars($query); |
||||
else if (isset($_POST["q"])) |
||||
$domain = htmlspecialchars($_POST["q"]); |
||||
|
||||
if(!empty($domain) && !$utils->isValidDomain($domain, $error)) { |
||||
$domain = ""; |
||||
$result["error"] = 'Not valid query: ' . $error; |
||||
} |
||||
else if(!empty($domain) && $utils->isValidDomain($domain, $error)) { |
||||
$pdo = (new App\DB($options))->pdo; |
||||
|
||||
$STH = $pdo->query("SELECT `host`, `base64`, `base32`, `last_seen` FROM `hosts` WHERE `host` = '" . $domain . "' LIMIT 1"); |
||||
$STH->setFetchMode(PDO::FETCH_ASSOC); |
||||
$row = $STH->fetchAll(); |
||||
|
||||
if (empty($row)) |
||||
$result["error"] = "No such host is found"; |
||||
else |
||||
$result = array_merge($result, $row[0]); |
||||
} |
||||
|
||||
$template = $twig->load('jump.twig'); |
||||
echo $template->render(['domain' => $domain, 'result' => $result]); |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
<?php |
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php'; |
||||
require_once __DIR__ . '/../config.php'; |
||||
|
||||
/* Initialize Twig engine */ |
||||
$loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/../templates'); |
||||
$twig = new \Twig\Environment($loader, [ |
||||
'cache' => __DIR__ . '/../cache', |
||||
'auto_reload' => true, |
||||
]); |
||||
|
||||
$pdo = (new App\DB($options))->pdo; |
||||
|
||||
/* Get records with limit */ |
||||
$STH = $pdo->query ("SELECT `host`, `base64`, `base32`, `add_date` FROM `hosts` ORDER BY `add_date` DESC LIMIT " . $options["tableitems"]); |
||||
$STH->setFetchMode(PDO::FETCH_ASSOC); |
||||
$rows = $STH->fetchAll(); |
||||
|
||||
$template = $twig->load('latest.twig'); |
||||
echo $template->render(['limit' => $options["tableitems"], 'hosts' => $rows]); |
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
<?php |
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php'; |
||||
require_once __DIR__ . '/../config.php'; |
||||
|
||||
/* Initialize Twig engine */ |
||||
$loader = new \Twig\Loader\FilesystemLoader(__DIR__ . '/../templates'); |
||||
$twig = new \Twig\Environment($loader, [ |
||||
'cache' => __DIR__ . '/../cache', |
||||
'auto_reload' => true, |
||||
]); |
||||
|
||||
$utils = new App\Utils; |
||||
|
||||
$result = []; |
||||
$q = ""; |
||||
|
||||
if (isset($query) && strlen($query) > 67 || isset($_POST["q"]) && strlen($_POST["q"]) > 67) { |
||||
$result["error"] = "Request is too long, max length is 67 chars"; |
||||
} else if (isset($query) && !empty($query)) { |
||||
$q = htmlspecialchars($query); |
||||
} else if (isset($_POST["q"])) { |
||||
$q = htmlspecialchars($_POST["q"]); |
||||
} |
||||
|
||||
if(!empty($q)) { |
||||
$pdo = (new App\DB($options))->pdo; |
||||
|
||||
$STH = $pdo->query("SELECT `host`, `base64`, `base32`, `last_seen` FROM `hosts` WHERE `host` LIKE '%" . $q . "%' OR `base32` LIKE '%" . $q . "%' LIMIT " . $options["tableitems"]); |
||||
$STH->setFetchMode(PDO::FETCH_ASSOC); |
||||
$row = $STH->fetchAll(); |
||||
|
||||
if (empty($row)) |
||||
$result["error"] = "Nothing was found"; |
||||
else |
||||
$result = array_merge($result, $row); |
||||
} |
||||
|
||||
$template = $twig->load('search.twig'); |
||||
echo $template->render(['query' => $q ?: 'removed request', 'result' => $result, 'limit' => $options["tableitems"]]); |
Loading…
Reference in new issue