Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F118277315
D1642.1775726301.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
37 KB
Referenced Files
None
Subscribers
None
D1642.1775726301.diff
View Options
diff --git a/src/app/Console/Command.php b/src/app/Console/Command.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Command.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace App\Console;
+
+class Command extends \Illuminate\Console\Command
+{
+ /**
+ * Find the domain.
+ *
+ * @param string $domain Domain ID or namespace
+ *
+ * @return \App\Domain|null
+ */
+ public function getDomain($domain)
+ {
+ return $this->getObject(\App\Domain::class, $domain, 'namespace');
+ }
+
+ /**
+ * Find an object.
+ *
+ * @param string $objectClass The name of the class
+ * @param string $objectIdOrTitle The name of a database field to match.
+ * @param string|null $objectTitle An additional database field to match.
+ *
+ * @return mixed
+ */
+ public function getObject($objectClass, $objectIdOrTitle, $objectTitle)
+ {
+ $object = $objectClass::find($objectIdOrTitle);
+
+ if (!$object && !empty($objectTitle)) {
+ $object = $objectClass::where($objectTitle, $objectIdOrTitle)->first();
+ }
+
+ return $object;
+ }
+
+ /**
+ * Find the user.
+ *
+ * @param string $user User ID or email
+ *
+ * @return \App\User|null
+ */
+ public function getUser($user)
+ {
+ return $this->getObject(\App\User::class, $user, 'email');
+ }
+
+ /**
+ * Find the wallet.
+ *
+ * @param string $wallet Wallet ID
+ *
+ * @return \App\Wallet|null
+ */
+ public function getWallet($wallet)
+ {
+ return $this->getObject(\App\Wallet::class, $wallet, null);
+ }
+
+ /**
+ * Return a string for output, with any additional attributes specified as well.
+ *
+ * @param mixed $entry An object
+ *
+ * @return string
+ */
+ protected function toString($entry)
+ {
+ /**
+ * Haven't figured out yet, how to test if this command implements an option for additional
+ * attributes.
+ if (!in_array('attr', $this->options())) {
+ return $entry->{$entry->getKeyName()};
+ }
+ */
+
+ $str = [
+ $entry->{$entry->getKeyName()}
+ ];
+
+ foreach ($this->option('attr') as $attr) {
+ if ($attr == $entry->getKeyName()) {
+ $this->warn("Specifying {$attr} is not useful.");
+ continue;
+ }
+
+ if (!array_key_exists($attr, $entry->toArray())) {
+ $this->error("Attribute {$attr} isn't available");
+ continue;
+ }
+
+ if (is_numeric($entry->{$attr})) {
+ $str[] = $entry->{$attr};
+ } else {
+ $str[] = !empty($entry->{$attr}) ? $entry->{$attr} : "null";
+ }
+ }
+
+ return implode(" ", $str);
+ }
+}
diff --git a/src/app/Console/Commands/DataCountries.php b/src/app/Console/Commands/Data/Import/CountriesCommand.php
rename from src/app/Console/Commands/DataCountries.php
rename to src/app/Console/Commands/Data/Import/CountriesCommand.php
--- a/src/app/Console/Commands/DataCountries.php
+++ b/src/app/Console/Commands/Data/Import/CountriesCommand.php
@@ -1,10 +1,11 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Data\Import;
-use Illuminate\Console\Command;
+use App\Console\Command;
+use Carbon\Carbon;
-class DataCountries extends Command
+class CountriesCommand extends Command
{
private $currency_fixes = [
// Country code => currency
@@ -16,14 +17,14 @@
*
* @var string
*/
- protected $signature = 'data:countries';
+ protected $signature = 'data:import:countries';
/**
* The console command description.
*
* @var string
*/
- protected $description = 'Fetches countries map from wikipedia';
+ protected $description = 'Fetches countries map from country.io';
/**
* Execute the console command.
@@ -32,50 +33,65 @@
*/
public function handle()
{
+ $today = Carbon::now()->toDateString();
+
$countries = [];
$currencies = [];
- $currencies_url = 'http://country.io/currency.json';
- $countries_url = 'http://country.io/names.json';
-
- $this->info("Fetching currencies from $currencies_url...");
+ $currencySource = 'http://country.io/currency.json';
+ $countrySource = 'http://country.io/names.json';
- // fetch currency table and create an index by country page url
- $currencies_json = file_get_contents($currencies_url);
-
- if (!$currencies_json) {
- $this->error("Failed to fetch currencies");
- return;
- }
+ //
+ // countries
+ //
+ $file = storage_path("countries-{$today}.json");
- $this->info("Fetching countries from $countries_url...");
+ \App\Utils::downloadFile($countrySource, $file);
- $countries_json = file_get_contents($countries_url);
+ $countryJson = file_get_contents($file);
- if (!$countries_json) {
+ if (!$countryJson) {
$this->error("Failed to fetch countries");
- return;
+ return 1;
}
- $currencies = json_decode($currencies_json, true);
- $countries = json_decode($countries_json, true);
+ $countries = json_decode($countryJson, true);
if (!is_array($countries) || empty($countries)) {
$this->error("Invalid countries data");
+ return 1;
+ }
+
+ //
+ // currencies
+ //
+ $file = storage_path("currencies-{$today}.json");
+
+ \App\Utils::downloadFile($currencySource, $file);
+
+ // fetch currency table and create an index by country page url
+ $currencyJson = file_get_contents($file);
+
+ if (!$currencyJson) {
+ $this->error("Failed to fetch currencies");
return;
}
+ $currencies = json_decode($currencyJson, true);
+
if (!is_array($currencies) || empty($currencies)) {
$this->error("Invalid currencies data");
- return;
+ return 1;
}
+ //
+ // export
+ //
$file = resource_path('countries.php');
- $this->info("Generating resource file $file...");
-
asort($countries);
$out = "<?php return [\n";
+
foreach ($countries as $code => $name) {
$currency = $currencies[$code] ?? null;
@@ -84,12 +100,13 @@
}
if (!$currency) {
- $this->error("Unknown currency for {$name} ({$code}). Skipped.");
+ $this->warn("Unknown currency for {$name} ({$code}). Skipped.");
continue;
}
$out .= sprintf(" '%s' => ['%s','%s'],\n", $code, $currency, addslashes($name));
}
+
$out .= "];\n";
file_put_contents($file, $out);
diff --git a/src/app/Console/Commands/Data/Import/IP4NetsCommand.php b/src/app/Console/Commands/Data/Import/IP4NetsCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Data/Import/IP4NetsCommand.php
@@ -0,0 +1,225 @@
+<?php
+
+namespace App\Console\Commands\Data\Import;
+
+use App\Console\Command;
+use Carbon\Carbon;
+
+class IP4NetsCommand extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'data:import:ip4nets';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Update IP4 Networks';
+
+ /**
+ * Create a new command instance.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return int
+ */
+ public function handle()
+ {
+ $rirs = [
+ 'afrinic' => 'http://ftp.afrinic.net/stats/afrinic/delegated-afrinic-latest',
+ 'apnic' => 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest',
+ 'arin' => 'http://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest',
+ 'lacnic' => 'http://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest',
+ 'ripencc' => 'https://ftp.ripe.net/ripe/stats/delegated-ripencc-latest'
+ ];
+
+ $today = Carbon::now()->toDateString();
+
+ foreach ($rirs as $rir => $url) {
+ $file = storage_path("{$rir}-{$today}");
+
+ \App\Utils::downloadFile($url, $file);
+
+ $serial = $this->serialFromStatsFile($file);
+
+ if (!$serial) {
+ \Log::error("Can not derive serial from {$file}");
+ continue;
+ }
+
+ $numLines = $this->countLines($file);
+
+ if (!$numLines) {
+ \Log::error("No relevant lines could be found in {$file}");
+ continue;
+ }
+
+ $bar = \App\Utils::createProgressBar(
+ $this->output,
+ $numLines,
+ "Importing IPv4 Networks from {$file}"
+ );
+
+ $fp = fopen($file, 'r');
+
+ $nets = [];
+
+ while (!feof($fp)) {
+ $line = trim(fgets($fp));
+
+ if ($line == "") {
+ continue;
+ }
+
+ if ((int)$line) {
+ continue;
+ }
+
+ if ($line[0] == "#") {
+ continue;
+ }
+
+ $items = explode('|', $line);
+
+ if (sizeof($items) < 7) {
+ continue;
+ }
+
+ if ($items[1] == "*") {
+ continue;
+ }
+
+ if ($items[2] != "ipv4") {
+ continue;
+ }
+
+ if ($items[5] == "00000000") {
+ $items[5] = "19700102";
+ }
+
+ if ($items[1] == "" || $items[1] == "ZZ") {
+ continue;
+ }
+
+ $bar->advance();
+
+ $mask = 32 - log($items[4], 2);
+
+ $net = \App\IP4Net::where(
+ [
+ 'net_number' => $items[3],
+ 'net_mask' => $mask,
+ 'net_broadcast' => long2ip((ip2long($items[3]) + 2 ** (32 - $mask)) - 1)
+ ]
+ )->first();
+
+ if ($net) {
+ if ($net->updated_at > Carbon::now()->subDays(1)) {
+ continue;
+ }
+
+ // don't use ->update() method because it doesn't update updated_at which we need for expiry
+ $net->rir_name = $rir;
+ $net->country = $items[1];
+ $net->serial = $serial;
+ $net->updated_at = Carbon::now();
+
+ $net->save();
+
+ continue;
+ }
+
+ $nets[] = [
+ 'rir_name' => $rir,
+ 'net_number' => $items[3],
+ 'net_mask' => $mask,
+ 'net_broadcast' => long2ip((ip2long($items[3]) + 2 ** (32 - $mask)) - 1),
+ 'country' => $items[1],
+ 'serial' => $serial,
+ 'created_at' => Carbon::parse($items[5], 'UTC'),
+ 'updated_at' => Carbon::now()
+ ];
+
+ if (sizeof($nets) >= 100) {
+ \App\IP4Net::insert($nets);
+ $nets = [];
+ }
+ }
+
+ if (sizeof($nets) > 0) {
+ \App\IP4Net::insert($nets);
+ $nets = [];
+ }
+
+ $bar->finish();
+
+ $this->info("DONE");
+ }
+
+ return 0;
+ }
+
+ private function countLines($file)
+ {
+ $numLines = 0;
+
+ $fh = fopen($file, 'r');
+
+ while (!feof($fh)) {
+ $line = trim(fgets($fh));
+
+ $items = explode('|', $line);
+
+ if (sizeof($items) < 3) {
+ continue;
+ }
+
+ if ($items[2] == "ipv4") {
+ $numLines++;
+ }
+ }
+
+ fclose($fh);
+
+ return $numLines;
+ }
+
+ private function serialFromStatsFile($file)
+ {
+ $serial = null;
+
+ $fh = fopen($file, 'r');
+
+ while (!feof($fh)) {
+ $line = trim(fgets($fh));
+
+ $items = explode('|', $line);
+
+ if (sizeof($items) < 2) {
+ continue;
+ }
+
+ if ((int)$items[2]) {
+ $serial = (int)$items[2];
+ break;
+ }
+ }
+
+ fclose($fh);
+
+ return $serial;
+ }
+}
diff --git a/src/app/Console/Commands/Data/Import/IP6NetsCommand.php b/src/app/Console/Commands/Data/Import/IP6NetsCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Data/Import/IP6NetsCommand.php
@@ -0,0 +1,223 @@
+<?php
+
+namespace App\Console\Commands\Data\Import;
+
+use App\Console\Command;
+use Carbon\Carbon;
+
+class IP6NetsCommand extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'data:import:ip6nets';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Import IP6 Networks.';
+
+ /**
+ * Create a new command instance.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return mixed
+ */
+ public function handle()
+ {
+ $rirs = [
+ 'afrinic' => 'http://ftp.afrinic.net/stats/afrinic/delegated-afrinic-latest',
+ 'apnic' => 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest',
+ 'arin' => 'http://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest',
+ 'lacnic' => 'http://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest',
+ 'ripencc' => 'https://ftp.ripe.net/ripe/stats/delegated-ripencc-latest'
+ ];
+
+ $today = Carbon::now()->toDateString();
+
+ foreach ($rirs as $rir => $url) {
+ $file = storage_path("{$rir}-{$today}");
+
+ \App\Utils::downloadFile($url, $file);
+
+ $serial = $this->serialFromStatsFile($file);
+
+ if (!$serial) {
+ \Log::error("Can not derive serial from {$file}");
+ continue;
+ }
+
+ $numLines = $this->countLines($file);
+
+ if (!$numLines) {
+ \Log::error("No relevant lines could be found in {$file}");
+ continue;
+ }
+
+ $bar = \App\Utils::createProgressBar(
+ $this->output,
+ $numLines,
+ "Importing IPv6 Networks from {$file}"
+ );
+
+ $fp = fopen($file, 'r');
+
+ $nets = [];
+
+ while (!feof($fp)) {
+ $line = trim(fgets($fp));
+
+ if ($line == "") {
+ continue;
+ }
+
+ if ((int)$line) {
+ continue;
+ }
+
+ if ($line[0] == "#") {
+ continue;
+ }
+
+ $items = explode('|', $line);
+
+ if (sizeof($items) < 7) {
+ continue;
+ }
+
+ if ($items[1] == "*") {
+ continue;
+ }
+
+ if ($items[2] != "ipv6") {
+ continue;
+ }
+
+ if ($items[5] == "00000000") {
+ $items[5] = "19700102";
+ }
+
+ if ($items[1] == "" || $items[1] == "ZZ") {
+ continue;
+ }
+
+ $bar->advance();
+
+ $broadcast = \App\Utils::ip6Broadcast($items[3], (int)$items[4]);
+
+ $net = \App\IP6Net::where(
+ [
+ 'net_number' => $items[3],
+ 'net_mask' => (int)$items[4],
+ 'net_broadcast' => $broadcast
+ ]
+ )->first();
+
+ if ($net) {
+ if ($net->updated_at > Carbon::now()->subDays(1)) {
+ continue;
+ }
+
+ // don't use ->update() method because it doesn't update updated_at which we need for expiry
+ $net->rir_name = $rir;
+ $net->country = $items[1];
+ $net->serial = $serial;
+ $net->updated_at = Carbon::now();
+
+ $net->save();
+
+ continue;
+ }
+
+ $nets[] = [
+ 'rir_name' => $rir,
+ 'net_number' => $items[3],
+ 'net_mask' => (int)$items[4],
+ 'net_broadcast' => $broadcast,
+ 'country' => $items[1],
+ 'serial' => $serial,
+ 'created_at' => Carbon::parse($items[5], 'UTC'),
+ 'updated_at' => Carbon::now()
+ ];
+
+ if (sizeof($nets) >= 100) {
+ \App\IP6Net::insert($nets);
+ $nets = [];
+ }
+ }
+
+ if (sizeof($nets) > 0) {
+ \App\IP6Net::insert($nets);
+ $nets = [];
+ }
+
+ $bar->finish();
+
+ $this->info("DONE");
+ }
+ }
+
+ private function countLines($file)
+ {
+ $numLines = 0;
+
+ $fh = fopen($file, 'r');
+
+ while (!feof($fh)) {
+ $line = trim(fgets($fh));
+
+ $items = explode('|', $line);
+
+ if (sizeof($items) < 3) {
+ continue;
+ }
+
+ if ($items[2] == "ipv6") {
+ $numLines++;
+ }
+ }
+
+ fclose($fh);
+
+ return $numLines;
+ }
+
+ private function serialFromStatsFile($file)
+ {
+ $serial = null;
+
+ $fh = fopen($file, 'r');
+
+ while (!feof($fh)) {
+ $line = trim(fgets($fh));
+
+ $items = explode('|', $line);
+
+ if (sizeof($items) < 2) {
+ continue;
+ }
+
+ if ((int)$items[2]) {
+ $serial = (int)$items[2];
+ break;
+ }
+ }
+
+ fclose($fh);
+
+ return $serial;
+ }
+}
diff --git a/src/app/Console/Commands/Data/ImportCommand.php b/src/app/Console/Commands/Data/ImportCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/Data/ImportCommand.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace App\Console\Commands\Data;
+
+use Illuminate\Console\Command;
+
+class ImportCommand extends Command
+{
+ /**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+ protected $signature = 'data:import';
+
+ /**
+ * The console command description.
+ *
+ * @var string
+ */
+ protected $description = 'Command description';
+
+ /**
+ * Create a new command instance.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return int
+ */
+ public function handle()
+ {
+ $commands = [
+ Import\CountriesCommand::class,
+ Import\IP4NetsCommand::class,
+ Import\IP6NetsCommand::class
+ ];
+
+ foreach ($commands as $command) {
+ $execution = new $command();
+ $execution->output = $this->output;
+ $execution->handle();
+ }
+
+ return 0;
+ }
+}
diff --git a/src/app/Console/Kernel.php b/src/app/Console/Kernel.php
--- a/src/app/Console/Kernel.php
+++ b/src/app/Console/Kernel.php
@@ -25,8 +25,18 @@
*/
protected function schedule(Schedule $schedule)
{
- // $schedule->command('inspire')
- // ->hourly();
+ // This command imports countries and the current set of IPv4 and IPv6 networks allocated to countries.
+ $schedule->command('data:import')->dailyAt('05:00');
+
+ $schedule->command('wallet:charge')->dailyAt('00:00');
+ $schedule->command('wallet:charge')->dailyAt('04:00');
+ $schedule->command('wallet:charge')->dailyAt('08:00');
+ $schedule->command('wallet:charge')->dailyAt('12:00');
+ $schedule->command('wallet:charge')->dailyAt('16:00');
+ $schedule->command('wallet:charge')->dailyAt('20:00');
+
+ // this is a laravel 8-ism
+ //$schedule->command('wallet:charge')->everyFourHours();
}
/**
diff --git a/src/app/IP4Net.php b/src/app/IP4Net.php
new file mode 100644
--- /dev/null
+++ b/src/app/IP4Net.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\DB;
+
+class IP4Net extends Model
+{
+ protected $table = "ip4nets";
+
+ protected $fillable = [
+ 'net_number',
+ 'net_mask',
+ 'net_broadcast',
+ 'country',
+ 'serial'
+ ];
+}
diff --git a/src/app/IP6Net.php b/src/app/IP6Net.php
new file mode 100644
--- /dev/null
+++ b/src/app/IP6Net.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Support\Facades\DB;
+
+class IP6Net extends Model
+{
+ protected $table = "ip6nets";
+
+ protected $fillable = [
+ 'rir_name',
+ 'net_number',
+ 'net_mask',
+ 'net_broadcast',
+ 'country',
+ 'serial',
+ 'created_at',
+ 'updated_at'
+ ];
+
+ public static function getNet($ip, $mask = 128)
+ {
+ $query = "
+ SELECT id FROM ip6nets
+ WHERE INET6_ATON(net_number) <= INET6_ATON(?)
+ AND INET6_ATON(net_broadcast) >= INET6_ATON(?)
+ ORDER BY INET6_ATON(net_number), net_mask DESC LIMIT 1
+ ";
+
+ $results = DB::select($query, [$ip, $ip]);
+
+ if (sizeof($results) == 0) {
+ return null;
+ }
+
+ return \App\IP6Net::find($results[0]->id);
+ }
+}
diff --git a/src/app/Observers/UserObserver.php b/src/app/Observers/UserObserver.php
--- a/src/app/Observers/UserObserver.php
+++ b/src/app/Observers/UserObserver.php
@@ -54,7 +54,7 @@
public function created(User $user)
{
$settings = [
- 'country' => 'CH',
+ 'country' => \App\Utils::countryForRequest(),
'currency' => 'CHF',
/*
'first_name' => '',
diff --git a/src/app/Utils.php b/src/app/Utils.php
--- a/src/app/Utils.php
+++ b/src/app/Utils.php
@@ -12,6 +12,98 @@
class Utils
{
/**
+ * Count the number of lines in a file.
+ *
+ * Useful for progress bars.
+ *
+ * @param string $file The filepath to count the lines of.
+ *
+ * @return int
+ */
+ public static function countLines($file)
+ {
+ $fh = fopen($file, 'rb');
+ $numLines = 0;
+
+ while (!feof($fh)) {
+ $numLines += substr_count(fread($fh, 8192), "\n");
+ }
+
+ fclose($fh);
+
+ return $numLines;
+ }
+
+ /**
+ * Return the country ISO code for an IP address.
+ *
+ * @return string
+ */
+ public static function countryForIP($ip)
+ {
+ if (strpos(':', $ip) === false) {
+ $query = "
+ SELECT country FROM ip4nets
+ WHERE INET_ATON(net_number) <= INET_ATON(?)
+ AND INET_ATON(net_broadcast) >= INET_ATON(?)
+ ORDER BY INET_ATON(net_number), net_mask DESC LIMIT 1
+ ";
+ } else {
+ $query = "
+ SELECT id FROM ip6nets
+ WHERE INET6_ATON(net_number) <= INET6_ATON(?)
+ AND INET6_ATON(net_broadcast) >= INET6_ATON(?)
+ ORDER BY INET6_ATON(net_number), net_mask DESC LIMIT 1
+ ";
+ }
+
+ $nets = \Illuminate\Support\Facades\DB::select($query, [$ip, $ip]);
+
+ if (sizeof($nets) > 0) {
+ return $nets[0]->country;
+ }
+
+ return 'CH';
+ }
+
+ /**
+ * Return the country ISO code for the current request.
+ */
+ public static function countryForRequest()
+ {
+ $request = \request();
+ $ip = $request->ip();
+
+ return self::countryForIP($ip);
+ }
+
+ /**
+ * Shortcut to creating a progress bar of a particular format with a particular message.
+ *
+ * @param \Illuminate\Console\OutputStyle $output Console output object
+ * @param int $count Number of progress steps
+ * @param string $message The description
+ *
+ * @return \Symfony\Component\Console\Helper\ProgressBar
+ */
+ public static function createProgressBar($output, $count, $message = null)
+ {
+ $bar = $output->createProgressBar($count);
+
+ $bar->setFormat(
+ '%current:7s%/%max:7s% [%bar%] %percent:3s%% %elapsed:7s%/%estimated:-7s% %message% '
+ );
+
+ if ($message) {
+ $bar->setMessage($message . " ...");
+ }
+
+ $bar->start();
+
+ return $bar;
+ }
+
+ /**
* Return the number of days in the month prior to this one.
*
* @return int
@@ -25,6 +117,100 @@
}
/**
+ * Download a file from the interwebz and store it locally.
+ *
+ * @param string $source The source location
+ * @param string $target The target location
+ * @param bool $force Force the download (and overwrite target)
+ *
+ * @return void
+ */
+ public static function downloadFile($source, $target, $force = false)
+ {
+ if (is_file($target) && !$force) {
+ return;
+ }
+
+ \Log::info("Retrieving {$source}");
+
+ $fp = fopen($target, 'w');
+
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $source);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FILE, $fp);
+ curl_exec($curl);
+
+ if (curl_errno($curl)) {
+ \Log::error("Request error on {$source}: " . curl_error($curl));
+
+ curl_close($curl);
+ fclose($fp);
+
+ unlink($target);
+ return;
+ }
+
+ curl_close($curl);
+ fclose($fp);
+ }
+
+ /**
+ * Calculate the broadcast address provided a net number and a prefix.
+ *
+ * @param string $net A valid IPv6 network number.
+ * @param int $prefix The network prefix.
+ *
+ * @return string
+ */
+ public static function ip6Broadcast($net, $prefix)
+ {
+ $netHex = bin2hex(inet_pton($net));
+
+ // Overwriting first address string to make sure notation is optimal
+ $net = inet_ntop(hex2bin($netHex));
+
+ // Calculate the number of 'flexible' bits
+ $flexbits = 128 - $prefix;
+
+ // Build the hexadecimal string of the last address
+ $lastAddrHex = $netHex;
+
+ // We start at the end of the string (which is always 32 characters long)
+ $pos = 31;
+ while ($flexbits > 0) {
+ // Get the character at this position
+ $orig = substr($lastAddrHex, $pos, 1);
+
+ // Convert it to an integer
+ $origval = hexdec($orig);
+
+ // OR it with (2^flexbits)-1, with flexbits limited to 4 at a time
+ $newval = $origval | (pow(2, min(4, $flexbits)) - 1);
+
+ // Convert it back to a hexadecimal character
+ $new = dechex($newval);
+
+ // And put that character back in the string
+ $lastAddrHex = substr_replace($lastAddrHex, $new, $pos, 1);
+
+ // We processed one nibble, move to previous position
+ $flexbits -= 4;
+ $pos -= 1;
+ }
+
+ // Convert the hexadecimal string to a binary string
+ # Using pack() here
+ # Newer PHP version can use hex2bin()
+ $lastaddrbin = pack('H*', $lastAddrHex);
+
+ // And create an IPv6 address from the binary string
+ $lastaddrstr = inet_ntop($lastaddrbin);
+
+ return $lastaddrstr;
+ }
+
+ /**
* Provide all unique combinations of elements in $input, with order and duplicates irrelevant.
*
* @param array $input The input array of elements.
diff --git a/src/composer.json b/src/composer.json
--- a/src/composer.json
+++ b/src/composer.json
@@ -16,22 +16,18 @@
"require": {
"php": "^7.1.3",
"barryvdh/laravel-dompdf": "^0.8.6",
- "doctrine/dbal": "^2.9",
+ "dyrynda/laravel-nullable-fields": "*",
"fideloper/proxy": "^4.0",
- "geoip2/geoip2": "^2.9",
- "iatstuti/laravel-nullable-fields": "*",
"kolab/net_ldap3": "dev-master",
"laravel/framework": "6.*",
"laravel/tinker": "^2.4",
"mollie/laravel-mollie": "^2.9",
"morrislaptop/laravel-queue-clear": "^1.2",
- "silviolleite/laravelpwa": "^1.0",
+ "silviolleite/laravelpwa": "^2.0",
"spatie/laravel-translatable": "^4.2",
"spomky-labs/otphp": "~4.0.0",
"stripe/stripe-php": "^7.29",
"swooletw/laravel-swoole": "^2.6",
- "torann/currency": "^1.0",
- "torann/geoip": "^1.0",
"tymon/jwt-auth": "^1.0"
},
"require-dev": {
diff --git a/src/config/geoip.php b/src/config/geoip.php
deleted file mode 100644
--- a/src/config/geoip.php
+++ /dev/null
@@ -1,165 +0,0 @@
-<?php
-
-return [
-
- /*
- |--------------------------------------------------------------------------
- | Logging Configuration
- |--------------------------------------------------------------------------
- |
- | Here you may configure the log settings for when a location is not found
- | for the IP provided.
- |
- */
-
- 'log_failures' => true,
-
- /*
- |--------------------------------------------------------------------------
- | Include Currency in Results
- |--------------------------------------------------------------------------
- |
- | When enabled the system will do it's best in deciding the user's currency
- | by matching their ISO code to a preset list of currencies.
- |
- */
-
- 'include_currency' => true,
-
- /*
- |--------------------------------------------------------------------------
- | Default Service
- |--------------------------------------------------------------------------
- |
- | Here you may specify the default storage driver that should be used
- | by the framework.
- |
- | Supported: "maxmind_database", "maxmind_api", "ipapi"
- |
- */
-
- 'service' => 'maxmind_database',
-
- /*
- |--------------------------------------------------------------------------
- | Storage Specific Configuration
- |--------------------------------------------------------------------------
- |
- | Here you may configure as many storage drivers as you wish.
- |
- */
-
- 'services' => [
-
- 'maxmind_database' => [
- 'class' => \Torann\GeoIP\Services\MaxMindDatabase::class,
- 'database_path' => storage_path('app/geoip.mmdb'),
- 'update_url' => 'https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz',
- 'locales' => ['en'],
- ],
-
- 'maxmind_api' => [
- 'class' => \Torann\GeoIP\Services\MaxMindWebService::class,
- 'user_id' => env('MAXMIND_USER_ID'),
- 'license_key' => env('MAXMIND_LICENSE_KEY'),
- 'locales' => ['en'],
- ],
-
- 'ipapi' => [
- 'class' => \Torann\GeoIP\Services\IPApi::class,
- 'secure' => true,
- 'key' => env('IPAPI_KEY'),
- 'continent_path' => storage_path('app/continents.json'),
- 'lang' => 'en',
- ],
-
- 'ipgeolocation' => [
- 'class' => \Torann\GeoIP\Services\IPGeoLocation::class,
- 'secure' => true,
- 'key' => env('IPGEOLOCATION_KEY'),
- 'continent_path' => storage_path('app/continents.json'),
- 'lang' => 'en',
- ],
-
- 'ipdata' => [
- 'class' => \Torann\GeoIP\Services\IPData::class,
- 'key' => env('IPDATA_API_KEY'),
- 'secure' => true,
- ],
-
- 'ipfinder' => [
- 'class' => \Torann\GeoIP\Services\IPFinder::class,
- 'key' => env('IPFINDER_API_KEY'),
- 'secure' => true,
- 'locales' => ['en'],
- ],
-
- ],
-
- /*
- |--------------------------------------------------------------------------
- | Default Cache Driver
- |--------------------------------------------------------------------------
- |
- | Here you may specify the type of caching that should be used
- | by the package.
- |
- | Options:
- |
- | all - All location are cached
- | some - Cache only the requesting user
- | none - Disable cached
- |
- */
-
- 'cache' => 'all',
-
- /*
- |--------------------------------------------------------------------------
- | Cache Tags
- |--------------------------------------------------------------------------
- |
- | Cache tags are not supported when using the file or database cache
- | drivers in Laravel. This is done so that only locations can be cleared.
- |
- */
-
- 'cache_tags' => false,
-
- /*
- |--------------------------------------------------------------------------
- | Cache Expiration
- |--------------------------------------------------------------------------
- |
- | Define how long cached location are valid.
- |
- */
-
- 'cache_expires' => 30,
-
- /*
- |--------------------------------------------------------------------------
- | Default Location
- |--------------------------------------------------------------------------
- |
- | Return when a location is not found.
- |
- */
-
- 'default_location' => [
- 'ip' => '127.0.0.0',
- 'iso_code' => 'CH',
- 'country' => 'Switzerland',
- 'city' => 'Zurich',
- 'state' => 'ZH',
- 'state_name' => 'Zurich',
- 'postal_code' => '8703',
- 'lat' => 47.30,
- 'lon' => 8.59,
- 'timezone' => 'Europe/Zurich',
- 'continent' => 'EU',
- 'default' => true,
- 'currency' => 'CHF',
- ],
-
-];
diff --git a/src/database/migrations/2020_06_04_140800_create_ip4nets_table.php b/src/database/migrations/2020_06_04_140800_create_ip4nets_table.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2020_06_04_140800_create_ip4nets_table.php
@@ -0,0 +1,43 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// phpcs:ignore
+class CreateIp4netsTable extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::create(
+ 'ip4nets',
+ function (Blueprint $table) {
+ $table->bigIncrements('id');
+ $table->string('rir_name', 8);
+ $table->string('net_number', 15)->index();
+ $table->tinyInteger('net_mask')->unsigned();
+ $table->string('net_broadcast', 15)->index();
+ $table->string('country', 2)->nullable();
+ $table->bigInteger('serial')->unsigned();
+ $table->timestamps();
+
+ $table->index(['net_number', 'net_mask', 'net_broadcast']);
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('ip4nets');
+ }
+}
diff --git a/src/database/migrations/2020_06_04_140800_create_ip6nets_table.php b/src/database/migrations/2020_06_04_140800_create_ip6nets_table.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2020_06_04_140800_create_ip6nets_table.php
@@ -0,0 +1,43 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+// phpcs:ignore
+class CreateIp6netsTable extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::create(
+ 'ip6nets',
+ function (Blueprint $table) {
+ $table->bigIncrements('id');
+ $table->string('rir_name', 8);
+ $table->string('net_number', 39)->index();
+ $table->tinyInteger('net_mask')->unsigned();
+ $table->string('net_broadcast', 39)->index();
+ $table->string('country', 2)->nullable();
+ $table->bigInteger('serial')->unsigned();
+ $table->timestamps();
+
+ $table->index(['net_number', 'net_mask', 'net_broadcast']);
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('ip6nets');
+ }
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Apr 9, 9:18 AM (13 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18834869
Default Alt Text
D1642.1775726301.diff (37 KB)
Attached To
Mode
D1642: Rely on our own IP4 network tables instead of any sort of GeoIP stuff
Attached
Detach File
Event Timeline