Compare commits
273 Commits
Author | SHA1 | Date | |
---|---|---|---|
673ed6aaed | |||
22ba6fe35c | |||
201a466ae3 | |||
8edc45fbd5 | |||
e2cd09ac98 | |||
c6458219bf | |||
99dc13b297 | |||
6284795e3b | |||
1461647159 | |||
3f0e17e20b | |||
128e333deb | |||
6f6b5fe2c4 | |||
3ca16f0d87 | |||
c49daadc5f | |||
b59317871a | |||
1656087ded | |||
9bc880520f | |||
2458ca2408 | |||
3797f3c7c5 | |||
bbc013a7a0 | |||
db34b2b641 | |||
b1eaec3271 | |||
52961e2403 | |||
c9eee48c6a | |||
8cc561ea2b | |||
35c5b3da8d | |||
3e202e34d2 | |||
29cbc80982 | |||
bf446e1186 | |||
aeea49abe7 | |||
2c1ab88bbd | |||
058b4ac4b9 | |||
b27d983fe6 | |||
5fe486b93c | |||
010f34f4f2 | |||
aa55d05ccb | |||
ae44732848 | |||
bcae8693a7 | |||
46cf488337 | |||
f4ee2e1a51 | |||
ec139b79a2 | |||
e9b5783945 | |||
e3c45dfd3d | |||
082b70e072 | |||
70f3a049f6 | |||
e7336a942b | |||
fd780d1756 | |||
8c8283503f | |||
b239f52719 | |||
c69cbe8746 | |||
1c35f71a4b | |||
a65e4b8bc3 | |||
6dde4cf910 | |||
5dbef2d9d1 | |||
3bc98047c3 | |||
37549a90b7 | |||
09a0139839 | |||
e9fde81bfb | |||
941f7b480e | |||
aa1a460836 | |||
6743e5bf73 | |||
e1ed446f3e | |||
445dd48c81 | |||
33a97ce5b3 | |||
2ea8bbd0b8 | |||
ce19985c2d | |||
0e1086c99f | |||
fc11700601 | |||
119f2cb6b9 | |||
d9817e1c2e | |||
08375f4995 | |||
d58ed8842b | |||
39034dbbb0 | |||
1296e3be40 | |||
9c828d65e6 | |||
810e620526 | |||
6c36f7f9aa | |||
38c68982ec | |||
a161b8fc5e | |||
b67ae28b98 | |||
521a9b0679 | |||
24144de193 | |||
aabc8b5a65 | |||
1aabd323bb | |||
bf3fce252d | |||
72ad1307c5 | |||
f0f2d74a14 | |||
ff4ecddb76 | |||
6c9f4facc6 | |||
67dad76bd1 | |||
016c1fb1b0 | |||
733f4621a1 | |||
bf4c282282 | |||
dacd8be4c2 | |||
2316663dac | |||
59af68dca8 | |||
e802fe5030 | |||
996b8cf99b | |||
9762d95cef | |||
21d3ff5918 | |||
1075cc0de4 | |||
c2197ecf7c | |||
b5d2479098 | |||
296740aead | |||
e7a94d906f | |||
60ea0afc98 | |||
dc2e84386f | |||
0bd2f6e82c | |||
80fa3e840b | |||
242f4013b1 | |||
3aeeed1686 | |||
f03533b62a | |||
3b7ce4b9ce | |||
1b228a58c9 | |||
0c51b17e6c | |||
f6134c0a98 | |||
a310f4190c | |||
6342fe28b2 | |||
881449a170 | |||
1002309614 | |||
2598203edb | |||
7c70c1f12d | |||
247d30505e | |||
1128bddcee | |||
4796dd9a6e | |||
d792bf8fe3 | |||
28b48e5bef | |||
d2d1094c1a | |||
271bf937cf | |||
179233385b | |||
4b309871ec | |||
afb8ad9d17 | |||
2132b4564c | |||
48d329e503 | |||
d20a62ad48 | |||
9c7d7bf932 | |||
a32e2e504a | |||
7e87481e05 | |||
670450387c | |||
967153c70a | |||
7da09a1a9e | |||
86a15872b8 | |||
eb61c5ea6e | |||
a6e911fb48 | |||
53c5488df7 | |||
944053972b | |||
6bba18fc5f | |||
d2d2f31664 | |||
8396866280 | |||
ac687efe51 | |||
676ba4a40b | |||
432a9bbf2b | |||
7ba037b76d | |||
a2cc1b6b8e | |||
3852e69fcc | |||
bf5d5bcd74 | |||
4bbb82690c | |||
03c6cadbf9 | |||
b1560015ae | |||
a3719b9186 | |||
f4f8e9fa94 | |||
38fd1539a6 | |||
32d31cea90 | |||
5e7bf0554a | |||
c76995fa00 | |||
caac9c21a8 | |||
43c573f641 | |||
b8f0b11c05 | |||
494cb317d1 | |||
bfab8c7d26 | |||
333e7e773d | |||
4501443a43 | |||
832b496b0b | |||
2d75c92afb | |||
67f0e3007e | |||
6974b9b885 | |||
5d9d8ed72a | |||
eafd203021 | |||
9d06b422e0 | |||
693e88bc01 | |||
2456402246 | |||
967aadf21d | |||
75f5424d4f | |||
e6c0511b33 | |||
7f95305d75 | |||
9d7455233b | |||
c70222fef8 | |||
2c4bf83250 | |||
84d9d271dc | |||
61dfadba5a | |||
616393edcc | |||
62a9139d14 | |||
e10ca8f18a | |||
c1e6d436e5 | |||
617901f7df | |||
4d7af7c7e3 | |||
fc930ba6c2 | |||
7f4540f5ec | |||
d25dae2427 | |||
cfb408766f | |||
b9603e4bb8 | |||
173446edbe | |||
3333679fc6 | |||
dcf34d9be2 | |||
df07d9978e | |||
f006d829f4 | |||
6374725e8e | |||
e2321f3acd | |||
d8c68bcaa2 | |||
a82927c1f6 | |||
5e162a3427 | |||
b62f18cf3d | |||
b88046d57e | |||
d3c8bc844a | |||
b891abb611 | |||
5f3db71451 | |||
9ed7171c9b | |||
298dfe6703 | |||
d3a9e36bb7 | |||
2f55de2ddf | |||
b34b046d3c | |||
7c23971e58 | |||
91d4cd0b2f | |||
db8475053c | |||
176ecb16a8 | |||
ad0ad73b0c | |||
11b7dc4229 | |||
8b4e2cb9f0 | |||
7bd192980f | |||
0a5374c743 | |||
1b2358b5a9 | |||
c9700fbd0c | |||
ef2e208fde | |||
c28392b2b6 | |||
180c620168 | |||
941117b342 | |||
df2873287c | |||
0304967e80 | |||
90c65fd5e1 | |||
a46ce7ff9e | |||
86995b03a8 | |||
61cfb773e2 | |||
ba0f643dca | |||
d6779e6e01 | |||
829fe1d7a9 | |||
b23d9351b5 | |||
153e4dc12b | |||
7c34a9f6c3 | |||
368198bc77 | |||
67377a2012 | |||
e692de7d7f | |||
95b6058020 | |||
a0b6df31f0 | |||
06c29d8750 | |||
614d5b877b | |||
742f0cd015 | |||
6b1cb8cd78 | |||
2509aa99e1 | |||
be8aec4751 | |||
efdb6b96ea | |||
1753111f8f | |||
27cdb02b06 | |||
434226f8bc | |||
8fb3a21fcd | |||
1d354da6e3 | |||
0bc3d4cf60 | |||
38795b83bf | |||
73cf421739 | |||
b5047c52f0 | |||
27fe3cd223 | |||
2590c2de71 | |||
31770241ec | |||
c414ebd053 |
65
.env.example
65
.env.example
@@ -1,55 +1,25 @@
|
|||||||
APP_NAME="Clearing Houz"
|
APP_NAME="Clearing Houz"
|
||||||
APP_ENV=production
|
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=false
|
APP_MAINTENANCE_DRIVER=cache
|
||||||
APP_URL=http://clrghouz
|
APP_MAINTENANCE_STORE=memcached
|
||||||
APP_TIMEZONE=
|
APP_TIMEZONE=
|
||||||
|
APP_URL=
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
AUTH_PASSWORD_RESET_TOKEN_TABLE=password_resets
|
||||||
LOG_LEVEL=info
|
|
||||||
|
CACHE_STORE=memcached
|
||||||
|
MEMCACHED_HOST=memcached
|
||||||
|
|
||||||
DB_CONNECTION=pgsql
|
DB_CONNECTION=pgsql
|
||||||
DB_HOST=postgres
|
DB_HOST=postgres
|
||||||
DB_PORT=5432
|
|
||||||
DB_DATABASE=clrghouz
|
DB_DATABASE=clrghouz
|
||||||
DB_USERNAME=clrghouz
|
DB_USERNAME=clrghouz
|
||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
#DB_SSLMODE=prefer
|
|
||||||
#DB_SSLROOTCERT=/var/www/html/config/ssl/ca.crt
|
|
||||||
#DB_SSLCERT=/var/www/html/config/ssl/client.crt
|
|
||||||
#DB_SSLKEY=/var/www/html/config/ssl/client.key
|
|
||||||
|
|
||||||
BROADCAST_DRIVER=log
|
|
||||||
MEMCACHED_HOST=memcached
|
|
||||||
CACHE_DRIVER=memcached
|
|
||||||
QUEUE_CONNECTION=database
|
QUEUE_CONNECTION=database
|
||||||
SESSION_DRIVER=file
|
SESSION_DRIVER=file
|
||||||
SESSION_LIFETIME=120
|
SESSION_LIFETIME=120
|
||||||
|
|
||||||
REDIS_HOST=127.0.0.1
|
|
||||||
REDIS_PASSWORD=
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|
||||||
MAIL_DRIVER=smtp
|
|
||||||
MAIL_HOST=mail.dege.lan
|
|
||||||
MAIL_PORT=25
|
|
||||||
MAIL_USERNAME=
|
|
||||||
MAIL_PASSWORD=
|
|
||||||
MAIL_ENCRYPTION=
|
|
||||||
MAIL_AUTO_EMBED_METHOD=base64
|
|
||||||
|
|
||||||
PUSHER_APP_ID=
|
|
||||||
PUSHER_APP_KEY=
|
|
||||||
PUSHER_APP_SECRET=
|
|
||||||
PUSHER_APP_CLUSTER=mt1
|
|
||||||
|
|
||||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
|
||||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
|
||||||
|
|
||||||
FIDO_DIR=fido
|
|
||||||
FIDO_PACKET_KEEP=
|
|
||||||
FIDO_STRICT=false
|
|
||||||
|
|
||||||
FILESYSTEM_DISK=s3
|
FILESYSTEM_DISK=s3
|
||||||
AWS_ACCESS_KEY_ID=
|
AWS_ACCESS_KEY_ID=
|
||||||
AWS_SECRET_ACCESS_KEY=
|
AWS_SECRET_ACCESS_KEY=
|
||||||
@@ -57,3 +27,24 @@ AWS_BUCKET=
|
|||||||
AWS_ENDPOINT=
|
AWS_ENDPOINT=
|
||||||
AWS_DEFAULT_REGION=home
|
AWS_DEFAULT_REGION=home
|
||||||
AWS_USE_PATH_STYLE_ENDPOINT=true
|
AWS_USE_PATH_STYLE_ENDPOINT=true
|
||||||
|
|
||||||
|
LOG_CHANNEL=daily
|
||||||
|
LOG_LEVEL=info
|
||||||
|
LOG_DAILY_DAYS=93
|
||||||
|
|
||||||
|
MAIL_MAILER=smtp
|
||||||
|
MAIL_HOST=smtp
|
||||||
|
MAIL_PORT=25
|
||||||
|
MAIL_USERNAME=
|
||||||
|
MAIL_PASSWORD=
|
||||||
|
MAIL_ENCRYPTION=
|
||||||
|
MAIL_AUTO_EMBED_METHOD=base64
|
||||||
|
|
||||||
|
SESSION_DRIVER=file
|
||||||
|
|
||||||
|
# Clrghouz configuration
|
||||||
|
FIDO_DNS_NS=
|
||||||
|
|
||||||
|
MATRIX_SERVER=
|
||||||
|
MATRIX_AS_TOKEN=
|
||||||
|
MATRIX_HS_TOKEN=
|
||||||
|
@@ -5,6 +5,8 @@ APP_DEBUG=true
|
|||||||
APP_URL=http://clrghouz
|
APP_URL=http://clrghouz
|
||||||
APP_TIMEZONE=Australia/Melbourne
|
APP_TIMEZONE=Australia/Melbourne
|
||||||
|
|
||||||
|
CACHE_STORE=array
|
||||||
|
|
||||||
LOG_CHANNEL=stderr
|
LOG_CHANNEL=stderr
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=debug
|
||||||
|
|
||||||
@@ -13,7 +15,7 @@ DB_HOST=postgres-test
|
|||||||
DB_PORT=5432
|
DB_PORT=5432
|
||||||
DB_DATABASE=test
|
DB_DATABASE=test
|
||||||
DB_USERNAME=test
|
DB_USERNAME=test
|
||||||
DB_PASSWORD=test
|
DB_PASSWORD=password
|
||||||
|
|
||||||
BROADCAST_DRIVER=log
|
BROADCAST_DRIVER=log
|
||||||
CACHE_DRIVER=file
|
CACHE_DRIVER=file
|
||||||
|
@@ -74,8 +74,7 @@ jobs:
|
|||||||
( dockerd --host=tcp://0.0.0.0:2375 --tls=false & ) && sleep 3
|
( dockerd --host=tcp://0.0.0.0:2375 --tls=false & ) && sleep 3
|
||||||
## Some debugging info
|
## Some debugging info
|
||||||
# docker info && docker version
|
# docker info && docker version
|
||||||
env|sort
|
# env|sort
|
||||||
echo "PRT: ${{ secrets.PKG_WRITE_TOKEN }}"
|
|
||||||
|
|
||||||
- name: Registry FQDN Setup
|
- name: Registry FQDN Setup
|
||||||
id: registry
|
id: registry
|
||||||
@@ -93,12 +92,10 @@ jobs:
|
|||||||
- name: Code Checkout
|
- name: Code Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Record version
|
- name: Record version and Delete Unnecessary files
|
||||||
run: |
|
run: |
|
||||||
pwd
|
|
||||||
ls -al
|
|
||||||
echo ${GITHUB_SHA::8} > VERSION
|
echo ${GITHUB_SHA::8} > VERSION
|
||||||
cat VERSION
|
rm -rf .git* tests/ storage/app/test/
|
||||||
|
|
||||||
- name: Build and Push Docker Image
|
- name: Build and Push Docker Image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
|
@@ -9,6 +9,7 @@ use Illuminate\Support\Facades\Log;
|
|||||||
|
|
||||||
use App\Classes\Dynamic;
|
use App\Classes\Dynamic;
|
||||||
use App\Models\Address;
|
use App\Models\Address;
|
||||||
|
use App\Traits\HubStats as HubStatsTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method will generate the hub status for an upstream Host/RC/ZC
|
* This method will generate the hub status for an upstream Host/RC/ZC
|
||||||
@@ -18,6 +19,8 @@ use App\Models\Address;
|
|||||||
*/
|
*/
|
||||||
class HubStats extends Dynamic
|
class HubStats extends Dynamic
|
||||||
{
|
{
|
||||||
|
use HubStatsTrait;
|
||||||
|
|
||||||
private const LOGKEY = 'DHS';
|
private const LOGKEY = 'DHS';
|
||||||
|
|
||||||
private string $name = '';
|
private string $name = '';
|
||||||
@@ -32,41 +35,8 @@ class HubStats extends Dynamic
|
|||||||
{
|
{
|
||||||
$date = Carbon::now()->yesterday()->endOfday();
|
$date = Carbon::now()->yesterday()->endOfday();
|
||||||
|
|
||||||
$r = Address::select([
|
$r = $this->HubStats($date)
|
||||||
'a.id',
|
->where('zones.id',$this->ao->zone_id);
|
||||||
'addresses.system_id',
|
|
||||||
'addresses.zone_id',
|
|
||||||
'addresses.region_id',
|
|
||||||
'addresses.host_id',
|
|
||||||
'addresses.node_id',
|
|
||||||
'addresses.point_id',
|
|
||||||
'addresses.hub_id',
|
|
||||||
'addresses.role',
|
|
||||||
DB::raw('sum(a.uncollected_echomail) as uncollected_echomail'),
|
|
||||||
DB::raw('sum(a.uncollected_netmail) as uncollected_netmail'),
|
|
||||||
DB::raw('sum(a.uncollected_files) as uncollected_files')
|
|
||||||
])
|
|
||||||
->from(
|
|
||||||
Address::UncollectedEchomailTotal()
|
|
||||||
->where('echomails.created_at','<',$date)
|
|
||||||
->union(Address::UncollectedNetmailTotal()
|
|
||||||
->where('netmails.created_at','<',$date)
|
|
||||||
)
|
|
||||||
->union(Address::UncollectedFilesTotal()
|
|
||||||
->where('files.created_at','<',$date)
|
|
||||||
),'a')
|
|
||||||
->where('systems.active',true)
|
|
||||||
->where('addresses.active',TRUE)
|
|
||||||
->where('zones.active',TRUE)
|
|
||||||
->where('domains.active',TRUE)
|
|
||||||
->where('zones.id',$this->ao->zone_id)
|
|
||||||
->join('addresses',['addresses.id'=>'a.id'])
|
|
||||||
->join('systems',['systems.id'=>'addresses.system_id'])
|
|
||||||
->join('zones',['zones.id'=>'addresses.zone_id'])
|
|
||||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
|
||||||
->ftnOrder()
|
|
||||||
->groupBy('addresses.system_id','a.id','addresses.zone_id','addresses.region_id','addresses.host_id','addresses.node_id','addresses.point_id','addresses.hub_id','addresses.role')
|
|
||||||
->with(['system','zone.domain']);
|
|
||||||
|
|
||||||
$header = "| %-12s | %4d | %3d | %3d | %16s | %5s | %5s |\r\n";
|
$header = "| %-12s | %4d | %3d | %3d | %16s | %5s | %5s |\r\n";
|
||||||
|
|
||||||
@@ -100,7 +70,7 @@ class HubStats extends Dynamic
|
|||||||
$o->uncollected_echomail ?? 0,
|
$o->uncollected_echomail ?? 0,
|
||||||
$o->uncollected_netmail ?? 0,
|
$o->uncollected_netmail ?? 0,
|
||||||
$o->uncollected_files ?? 0,
|
$o->uncollected_files ?? 0,
|
||||||
$o->system->last_session?->format('Y-m-d H:i'),
|
$o->system->last_seen?->format('Y-m-d H:i') ?: '-',
|
||||||
is_null($o->system->pollmode) ? 'HOLD' : ($o->system->pollmode ? 'CRASH' : 'DAILY'),
|
is_null($o->system->pollmode) ? 'HOLD' : ($o->system->pollmode ? 'CRASH' : 'DAILY'),
|
||||||
$o->system->autohold ? 'YES' : 'NO');
|
$o->system->autohold ? 'YES' : 'NO');
|
||||||
}
|
}
|
||||||
|
@@ -18,20 +18,16 @@ class NodelistSegment extends Dynamic
|
|||||||
{
|
{
|
||||||
private const LOGKEY = 'DNL';
|
private const LOGKEY = 'DNL';
|
||||||
|
|
||||||
private string $name = '';
|
private ?string $name;
|
||||||
private Address $our_address;
|
private Address $our_address;
|
||||||
private Carbon $now;
|
private Carbon $now;
|
||||||
|
|
||||||
public function __construct(Address $ao,Collection $arg)
|
public function __construct(Address $ao,Collection $arg)
|
||||||
{
|
{
|
||||||
$this->our_address = our_address($ao->zone->domain)->first();
|
$this->our_address = our_address($ao->zone->domain,FALSE)->first();
|
||||||
$this->now = Carbon::now();
|
$this->now = Carbon::now();
|
||||||
|
|
||||||
$this->name = sprintf('z%dn%d.%d',
|
$this->name = $arg->get('name','');
|
||||||
$this->our_address->zone->zone_id,
|
|
||||||
$this->our_address->host_id,
|
|
||||||
$this->now->format('z'),
|
|
||||||
);
|
|
||||||
|
|
||||||
Log::debug(sprintf('%s:- Generating Nodelist for [%s] from [%s] as [%s] with arguments',self::LOGKEY,$ao->ftn,$this->our_address->ftn,$this->our_address->role_name),['args'=>$arg]);
|
Log::debug(sprintf('%s:- Generating Nodelist for [%s] from [%s] as [%s] with arguments',self::LOGKEY,$ao->ftn,$this->our_address->ftn,$this->our_address->role_name),['args'=>$arg]);
|
||||||
}
|
}
|
||||||
@@ -59,7 +55,7 @@ class NodelistSegment extends Dynamic
|
|||||||
$result->push('CM');
|
$result->push('CM');
|
||||||
|
|
||||||
if ($ao->system->address) {
|
if ($ao->system->address) {
|
||||||
$result->push(sprintf('INA:%s',$ao->system->address));
|
$result->push(sprintf('INA:%s',our_address($ao->domain)->contains($ao->id) ? our_hostname($ao) : $ao->system->address));
|
||||||
|
|
||||||
if (($x=$ao->system->mailers->pluck('name')->search('BINKP')) !== FALSE)
|
if (($x=$ao->system->mailers->pluck('name')->search('BINKP')) !== FALSE)
|
||||||
$result->push(sprintf('IBN%s',(($y=$ao->system->mailers->get($x)->pivot->port) !== 24554) ? ':'.$y : ''));
|
$result->push(sprintf('IBN%s',(($y=$ao->system->mailers->get($x)->pivot->port) !== 24554) ? ':'.$y : ''));
|
||||||
@@ -149,7 +145,7 @@ class NodelistSegment extends Dynamic
|
|||||||
if ($oo->system_id == $so->id)
|
if ($oo->system_id == $so->id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$result->push($this->generate($oo) ?: $this->entry($oo));
|
$result->push($this->entry($oo));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result->join("\n");
|
return $result->join("\n");
|
||||||
@@ -157,6 +153,8 @@ class NodelistSegment extends Dynamic
|
|||||||
|
|
||||||
public function getName(): string
|
public function getName(): string
|
||||||
{
|
{
|
||||||
return $this->name;
|
return ($this->name ?: sprintf('z%dn%d',
|
||||||
|
$this->our_address->zone->zone_id,
|
||||||
|
$this->our_address->host_id)).'.'.sprintf('%03d',$this->now->format('z'));
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -17,7 +17,7 @@ abstract class FTN
|
|||||||
$this->fn,
|
$this->fn,
|
||||||
$this->ff,
|
$this->ff,
|
||||||
$this->fp,
|
$this->fp,
|
||||||
).($this->zone ? sprintf('@%s',$this->zone->domain->name) : '');
|
).((isset($this->zone) && $this->zone) ? sprintf('@%s',$this->zone->domain->name) : '');
|
||||||
|
|
||||||
case 'tftn_t':
|
case 'tftn_t':
|
||||||
return sprintf('%d:%d/%d.%d',
|
return sprintf('%d:%d/%d.%d',
|
||||||
@@ -25,7 +25,7 @@ abstract class FTN
|
|||||||
$this->tn,
|
$this->tn,
|
||||||
$this->tf,
|
$this->tf,
|
||||||
$this->tp,
|
$this->tp,
|
||||||
).($this->zone ? sprintf('@%s',$this->zone->domain->name) : '');
|
).((isset($this->zone) && $this->zone) ? sprintf('@%s',$this->zone->domain->name) : '');
|
||||||
|
|
||||||
case 'fftn':
|
case 'fftn':
|
||||||
return Address::findFTN($this->fftn_t);
|
return Address::findFTN($this->fftn_t);
|
||||||
|
@@ -8,6 +8,7 @@ use Illuminate\Support\Arr;
|
|||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Validation\Validator as ValidatorResult;
|
use Illuminate\Validation\Validator as ValidatorResult;
|
||||||
|
|
||||||
use App\Classes\FTN as FTNBase;
|
use App\Classes\FTN as FTNBase;
|
||||||
@@ -21,7 +22,31 @@ use App\Traits\ObjectIssetFix;
|
|||||||
* Represents the structure of a message in a packet
|
* Represents the structure of a message in a packet
|
||||||
*
|
*
|
||||||
* @note FTN packed echomail messages are ZONE agnostic.
|
* @note FTN packed echomail messages are ZONE agnostic.
|
||||||
* @package App\Classes
|
* @note FTN packed netmails may not have an INTL kludge
|
||||||
|
*
|
||||||
|
* We work out addresses using the following approach/priority:
|
||||||
|
* = By definition we should know the author node, because it's either ours or (will be) in the nodelist (but it might not be there yet)
|
||||||
|
* = The target node may not be in the nodelist (anymore)
|
||||||
|
*
|
||||||
|
* + Echomail - only has source addresses (MUST have an AREA: tag, otherwise its netmail)
|
||||||
|
* a Origin Line " * Origin: <some text> (z:f/n.p)
|
||||||
|
* b MSGID Kludge "MSGID: z:f/n.p<@domain> <sometext>
|
||||||
|
* c net/node from msg headers (dst should be to hub to be processed)
|
||||||
|
* d domain address from packet (2.2 only) (dst should be to hub to be processed)
|
||||||
|
* e point from packet (2+/2e/2.2) (dst should be to hub to be processed)
|
||||||
|
* f zone from (2/2+/2e/2.2) (dst should be to hub to be processed)
|
||||||
|
*
|
||||||
|
* RULES:
|
||||||
|
* + if a exists, c, e, f must match
|
||||||
|
* + if b exists, c, d (if present), e, f must match
|
||||||
|
*
|
||||||
|
* + Netmail
|
||||||
|
* a INTL kludge (may not exist)
|
||||||
|
* b FMPT/TOPT (points only)
|
||||||
|
* c src & dst net/node from msg headers
|
||||||
|
* d src domain address from packet (2.2 only) (dst is to next hop, not final destination)
|
||||||
|
* e src point from packet (2+/2e/2.2) (dst is to next hop, not final destination)
|
||||||
|
* f src zone from (2/2+/2e/2.2) (dst is to next hop, not final destination)
|
||||||
*/
|
*/
|
||||||
class Message extends FTNBase
|
class Message extends FTNBase
|
||||||
{
|
{
|
||||||
@@ -89,13 +114,10 @@ class Message extends FTNBase
|
|||||||
public const AREATAG_LEN = 35; //
|
public const AREATAG_LEN = 35; //
|
||||||
|
|
||||||
private array $header; // Message Header
|
private array $header; // Message Header
|
||||||
private int $tzutc = 0; // TZUTC that needs to be converted to be used by Carbon @see self::kludges
|
private Collection $kludges; // TZUTC that needs to be converted to be used by Carbon @see self::kludges
|
||||||
private Echomail|Netmail $mo; // The object storing this packet message
|
private Echomail|Netmail $mo; // The object storing this packet message
|
||||||
private Address $us; // Our address for this message
|
private Address $us; // Our address for this message
|
||||||
|
|
||||||
/** @deprecated Not sure why this is needed? */
|
|
||||||
public bool $packed = FALSE; // Has the message been packed successfully
|
|
||||||
|
|
||||||
// Convert characters into printable chars
|
// Convert characters into printable chars
|
||||||
// https://int10h.org/oldschool-pc-fonts/readme/#437_charset
|
// https://int10h.org/oldschool-pc-fonts/readme/#437_charset
|
||||||
private const CP437 = [
|
private const CP437 = [
|
||||||
@@ -144,7 +166,8 @@ class Message extends FTNBase
|
|||||||
|
|
||||||
public static function header_len(): int
|
public static function header_len(): int
|
||||||
{
|
{
|
||||||
return collect(static::HEADER)->sum(function($item) { return Arr::get($item,2); });
|
return collect(static::HEADER)
|
||||||
|
->sum(fn($item)=>Arr::get($item,2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -236,7 +259,7 @@ class Message extends FTNBase
|
|||||||
$o->mo->from = $o->header['user_from'];
|
$o->mo->from = $o->header['user_from'];
|
||||||
$o->mo->subject = $o->header['subject'];
|
$o->mo->subject = $o->header['subject'];
|
||||||
|
|
||||||
$o->mo->datetime = $o->datetime;
|
$o->mo->datetime = $o->datetime->clone()->utc();
|
||||||
$o->mo->tzoffset = $o->datetime->utcOffset();
|
$o->mo->tzoffset = $o->datetime->utcOffset();
|
||||||
$o->mo->flags = $o->header['flags'];
|
$o->mo->flags = $o->header['flags'];
|
||||||
$o->mo->cost = $o->header['cost'];
|
$o->mo->cost = $o->header['cost'];
|
||||||
@@ -297,6 +320,7 @@ class Message extends FTNBase
|
|||||||
public function __construct(Zone $zone)
|
public function __construct(Zone $zone)
|
||||||
{
|
{
|
||||||
$this->zone = $zone;
|
$this->zone = $zone;
|
||||||
|
$this->kludges = collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __get($key)
|
public function __get($key)
|
||||||
@@ -306,10 +330,11 @@ class Message extends FTNBase
|
|||||||
|
|
||||||
switch ($key) {
|
switch ($key) {
|
||||||
// From Addresses
|
// From Addresses
|
||||||
|
// @todo $this->src no longer appears to be defined
|
||||||
case 'fz': return (int)Arr::get($this->src,'z');
|
case 'fz': return (int)Arr::get($this->src,'z');
|
||||||
case 'fn': return (int)($x=$this->src) ? Arr::get($x,'n') : Arr::get($this->header,'onet');
|
case 'fn': return (int)($x=$this->src) ? Arr::get($x,'n') : Arr::get($this->header,'onet');
|
||||||
case 'ff': return (int)($x=$this->src) ? Arr::get($x,'f') : Arr::get($this->header,'onode');
|
case 'ff': return (int)($x=$this->src) ? Arr::get($x,'f') : Arr::get($this->header,'onode');
|
||||||
case 'fp': return (int)$this->mo->kludges->get('FMPT:') ?: Arr::get($this->src,'p',Arr::get($this->header,'opoint',0));
|
case 'fp': return (int)$this->mo->kludges->get('FMPT') ?: Arr::get($this->src,'p',Arr::get($this->header,'opoint',0));
|
||||||
case 'fd': return Arr::get($this->src,'d');
|
case 'fd': return Arr::get($this->src,'d');
|
||||||
|
|
||||||
case 'fzone':
|
case 'fzone':
|
||||||
@@ -326,6 +351,7 @@ class Message extends FTNBase
|
|||||||
return Zone::where('zone_id',$this->fz)
|
return Zone::where('zone_id',$this->fz)
|
||||||
->where('default',TRUE)
|
->where('default',TRUE)
|
||||||
->single();
|
->single();
|
||||||
|
|
||||||
case 'fdomain':
|
case 'fdomain':
|
||||||
// We'll use the zone's domain if this method class was called with a zone
|
// We'll use the zone's domain if this method class was called with a zone
|
||||||
if ($this->zone && (($this->zone->domain->name === Arr::get($this->src,'d')) || ! Arr::get($this->src,'d')))
|
if ($this->zone && (($this->zone->domain->name === Arr::get($this->src,'d')) || ! Arr::get($this->src,'d')))
|
||||||
@@ -343,7 +369,7 @@ class Message extends FTNBase
|
|||||||
case 'tz': return (int)Arr::get($this->isEchomail() ? $this->src : $this->dst,'z');
|
case 'tz': return (int)Arr::get($this->isEchomail() ? $this->src : $this->dst,'z');
|
||||||
case 'tn': return (int)Arr::get($this->header,'dnet');
|
case 'tn': return (int)Arr::get($this->header,'dnet');
|
||||||
case 'tf': return (int)Arr::get($this->header,'dnode');
|
case 'tf': return (int)Arr::get($this->header,'dnode');
|
||||||
case 'tp': return (int)$this->mo->kludges->get('TOPT:') ?: Arr::get($this->header,'dpoint',0);
|
case 'tp': return (int)$this->mo->kludges->get('TOPT') ?: Arr::get($this->header,'dpoint',0);
|
||||||
|
|
||||||
case 'tzone':
|
case 'tzone':
|
||||||
// Use the zone if this class was called with it.
|
// Use the zone if this class was called with it.
|
||||||
@@ -373,23 +399,6 @@ class Message extends FTNBase
|
|||||||
// Otherwise we'll assume the same as the source domain
|
// Otherwise we'll assume the same as the source domain
|
||||||
return $this->fdomain ?: NULL;
|
return $this->fdomain ?: NULL;
|
||||||
|
|
||||||
case 'fftn_t':
|
|
||||||
case 'fftn':
|
|
||||||
case 'tftn_t':
|
|
||||||
case 'tftn':
|
|
||||||
return parent::__get($key);
|
|
||||||
|
|
||||||
// For 5D we need to include the domain
|
|
||||||
/* @deprecated - is this required? */
|
|
||||||
case 'fboss':
|
|
||||||
return sprintf('%d:%d/%d',$this->fz,$this->fn,$this->ff).(($x=$this->fdomain) ? '@'.$x->name : '');
|
|
||||||
case 'tboss':
|
|
||||||
return sprintf('%d:%d/%d',$this->tz,$this->tn,$this->tf).(($x=$this->tdomain) ? '@'.$x->name : '');
|
|
||||||
case 'fboss_o':
|
|
||||||
return Address::findFTN($this->fboss);
|
|
||||||
case 'tboss_o':
|
|
||||||
return Address::findFTN($this->tboss);
|
|
||||||
|
|
||||||
// Convert our message (header[datetime]) with our TZUTC into a Carbon date
|
// Convert our message (header[datetime]) with our TZUTC into a Carbon date
|
||||||
case 'datetime':
|
case 'datetime':
|
||||||
try {
|
try {
|
||||||
@@ -489,19 +498,23 @@ class Message extends FTNBase
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'tzutc':
|
||||||
|
return $this->kludges->get($key);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new \Exception('Unknown key: '.$key);
|
return parent::__get($key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function __set(string $key,mixed $value): void
|
||||||
* When we serialise this object, we'll need to utf8_encode some values
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function __serialize(): array
|
|
||||||
{
|
{
|
||||||
return $this->encode();
|
switch ($key) {
|
||||||
|
case 'tzutc':
|
||||||
|
if (! is_numeric($value))
|
||||||
|
throw new InvalidPacketException('TZUTC is not numeric '.$value);
|
||||||
|
|
||||||
|
$this->kludges->put($key,$value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -512,8 +525,6 @@ class Message extends FTNBase
|
|||||||
*/
|
*/
|
||||||
public function __toString(): string
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
$s = Setup::findOrFail(config('app.id'));
|
|
||||||
|
|
||||||
$return = pack(collect(self::HEADER)->pluck(1)->join(''),
|
$return = pack(collect(self::HEADER)->pluck(1)->join(''),
|
||||||
$this->mo->fftn->node_id, // Originating Node
|
$this->mo->fftn->node_id, // Originating Node
|
||||||
$this->mo->tftn->node_id, // Destination Node
|
$this->mo->tftn->node_id, // Destination Node
|
||||||
@@ -524,15 +535,12 @@ class Message extends FTNBase
|
|||||||
$this->mo->date->format('d M y H:i:s'),
|
$this->mo->date->format('d M y H:i:s'),
|
||||||
);
|
);
|
||||||
|
|
||||||
$return .= $this->mo->to."\00";
|
$return .= Str::limit($this->mo->to,self::USER_TO_LEN,'')."\00";
|
||||||
$return .= $this->mo->from."\00";
|
$return .= Str::limit($this->mo->from,self::USER_FROM_LEN,'')."\00";
|
||||||
$return .= $this->mo->subject."\00";
|
$return .= Str::limit($this->mo->subject,self::SUBJECT_LEN-3)."\00";
|
||||||
|
|
||||||
if (($this->mo instanceof Netmail) && $this->mo->isFlagSet(self::FLAG_LOCAL)) {
|
|
||||||
// If there isnt an INTL kludge, we'll add it
|
|
||||||
if (! $this->mo->kludges->has('INTL'))
|
|
||||||
$this->mo->kludges->put('INTL',sprintf('%s %s',$this->mo->tftn->ftn3d,$this->mo->fftn->ftn3d));
|
|
||||||
|
|
||||||
|
// Add our FMPT/TOPT kludges for netmails to a point
|
||||||
|
if ($this->mo instanceof Netmail) {
|
||||||
if ((! $this->mo->kludges->has('FMPT')) && $this->mo->fftn->point_id)
|
if ((! $this->mo->kludges->has('FMPT')) && $this->mo->fftn->point_id)
|
||||||
$this->mo->kludges->put('FMPT',$this->mo->fftn->point_id);
|
$this->mo->kludges->put('FMPT',$this->mo->fftn->point_id);
|
||||||
|
|
||||||
@@ -540,12 +548,16 @@ class Message extends FTNBase
|
|||||||
$this->mo->kludges->put('TOPT',$this->mo->tftn->point_id);
|
$this->mo->kludges->put('TOPT',$this->mo->tftn->point_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->mo->kludges->put($this->mo->isFlagSet(self::FLAG_LOCAL) ? 'PID:' : 'TID:',sprintf('%s %s',Setup::PRODUCT_NAME_SHORT,$s->version));
|
$this->mo->kludges->put($this->mo->isFlagSet(self::FLAG_LOCAL) ? 'PID:' : 'TID:',sprintf('%s %s',Setup::PRODUCT_NAME_SHORT,Setup::version()));
|
||||||
$this->mo->kludges->put('DBID:',$this->mo->id);
|
$this->mo->kludges->put('DBID:',$this->mo->id);
|
||||||
|
|
||||||
if ($this->mo instanceof Echomail)
|
if ($this->mo instanceof Echomail)
|
||||||
$return .= sprintf("AREA:%s\r",strtoupper($this->mo->echoarea->name));
|
$return .= sprintf("AREA:%s\r",strtoupper($this->mo->echoarea->name));
|
||||||
|
|
||||||
|
// Rebuild the INTL kludge line
|
||||||
|
elseif ($this->mo instanceof Netmail)
|
||||||
|
$this->mo->kludges->put('INTL',sprintf('%s %s',$this->mo->tftn->ftn3d,$this->mo->fftn->ftn3d));
|
||||||
|
|
||||||
// Add some kludges
|
// Add some kludges
|
||||||
$return .= sprintf("\01TZUTC: %s\r",str_replace('+','',$this->mo->date->getOffsetString('')));
|
$return .= sprintf("\01TZUTC: %s\r",str_replace('+','',$this->mo->date->getOffsetString('')));
|
||||||
|
|
||||||
@@ -568,14 +580,19 @@ class Message extends FTNBase
|
|||||||
$return .= sprintf("\x01Via %s @%s.UTC %s %s\r",
|
$return .= sprintf("\x01Via %s @%s.UTC %s %s\r",
|
||||||
$this->us->ftn3d,
|
$this->us->ftn3d,
|
||||||
Carbon::now()->format('Ymd.His'),
|
Carbon::now()->format('Ymd.His'),
|
||||||
Setup::PRODUCT_NAME_SHORT,$s->version);
|
Setup::PRODUCT_NAME_SHORT,Setup::version());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// FTS-0004.001/FSC-0068.001 The message SEEN-BY lines
|
// FTS-0004.001/FSC-0068.001 The message SEEN-BY lines
|
||||||
// FTS-0004.001/FSC-0068.001 The message PATH lines
|
// FTS-0004.001/FSC-0068.001 The message PATH lines
|
||||||
|
|
||||||
// @todo This unique() function here shouldnt be required, but is while system generated messages are storing path/seenby
|
// @todo This unique() function here shouldnt be required, but is while system generated messages are storing path/seenby
|
||||||
$path = $this->mo->path->push($this->us)->unique('ftn')->filter(fn($item)=>($item->point_id === 0));
|
$path = $this
|
||||||
|
->mo
|
||||||
|
->path
|
||||||
|
->push($this->us)
|
||||||
|
->unique('ftn')
|
||||||
|
->filter(fn($item)=>is_null($item->point_id) || ($item->point_id === 0));
|
||||||
|
|
||||||
// Create our rogue seenby objects
|
// Create our rogue seenby objects
|
||||||
$seenby = $this->mo->seenby;
|
$seenby = $this->mo->seenby;
|
||||||
@@ -589,7 +606,7 @@ class Message extends FTNBase
|
|||||||
|
|
||||||
$seenby = $seenby
|
$seenby = $seenby
|
||||||
->push($this->us)
|
->push($this->us)
|
||||||
->filter(fn($item)=>($item->point_id === 0))
|
->filter(fn($item)=>is_null($item->point_id) || ($item->point_id === 0))
|
||||||
->unique('ftn')
|
->unique('ftn')
|
||||||
->sortBy(function($item) { return sprintf('%05d%05d',$item->host_id,$item->node_id);});
|
->sortBy(function($item) { return sprintf('%05d%05d',$item->host_id,$item->node_id);});
|
||||||
|
|
||||||
@@ -602,16 +619,6 @@ class Message extends FTNBase
|
|||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* When we unserialize, we'll restore (utf8_decode) some values
|
|
||||||
*
|
|
||||||
* @param array $values
|
|
||||||
*/
|
|
||||||
public function __unserialize(array $values): void
|
|
||||||
{
|
|
||||||
$this->decode($values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduce our PATH/SEEN-BY for messages as per FSC-0068
|
* Reduce our PATH/SEEN-BY for messages as per FSC-0068
|
||||||
*
|
*
|
||||||
@@ -672,8 +679,9 @@ class Message extends FTNBase
|
|||||||
* @param Echomail|Netmail $o
|
* @param Echomail|Netmail $o
|
||||||
* @return Echomail|Netmail
|
* @return Echomail|Netmail
|
||||||
* @throws InvalidPacketException
|
* @throws InvalidPacketException
|
||||||
|
* @todo Remove parsing $o as second object, make this private, and use $this->... instead of $o->...
|
||||||
*/
|
*/
|
||||||
private function unpackMessage(string $message,Echomail|Netmail $o): Echomail|Netmail
|
public function unpackMessage(string $message,Echomail|Netmail $o): Echomail|Netmail
|
||||||
{
|
{
|
||||||
// Remove DOS \n\r
|
// Remove DOS \n\r
|
||||||
$message = preg_replace("/\n\r/","\r",$message);
|
$message = preg_replace("/\n\r/","\r",$message);
|
||||||
@@ -682,136 +690,159 @@ class Message extends FTNBase
|
|||||||
// First find our kludge lines
|
// First find our kludge lines
|
||||||
$ptr_start = 0;
|
$ptr_start = 0;
|
||||||
|
|
||||||
while (substr($message,$ptr_start,1) === "\x01") {
|
try {
|
||||||
$ptr_end = strpos($message,"\r",$ptr_start);
|
while (substr($message,$ptr_start,1) === "\x01") {
|
||||||
|
$ptr_end = strpos($message,"\r",$ptr_start);
|
||||||
|
|
||||||
$m = [];
|
$m = [];
|
||||||
$kludge = ($x=substr($message,$ptr_start+1,$ptr_end-$ptr_start-1));
|
$kludge = ($x=substr($message,$ptr_start+1,$ptr_end-$ptr_start-1));
|
||||||
preg_match('/^([^\s]+:?)+\s+(.*)$/',$kludge,$m);
|
preg_match('/^([^\s]+:?)+\s+(.*)$/',$kludge,$m);
|
||||||
|
|
||||||
|
$ptr_start = $ptr_end+1;
|
||||||
|
|
||||||
|
if (! $m) {
|
||||||
|
Log::alert(sprintf('%s:! Invalid Kluge Line [%s]',self::LOGKEY,$x));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catch any kludges we need to process here
|
||||||
|
if (array_key_exists($m[1],self::kludges)) {
|
||||||
|
// Some earlier mystic message had a blank value for TZUTC
|
||||||
|
if ((($m[1]) === 'TZUTC:') && (! $m[2]))
|
||||||
|
$m[2] = '0000';
|
||||||
|
|
||||||
|
$this->{self::kludges[$m[1]]} = $m[2];
|
||||||
|
|
||||||
|
} else
|
||||||
|
$o->kludges = [$m[1],$m[2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next our message content ends with '\r * Origin: ... \r' or <soh>...
|
||||||
|
// FTS-0004.001
|
||||||
|
if ($ptr_end=strrpos($message,"\r * Origin: ",$ptr_start)) {
|
||||||
|
// Find the <cr>
|
||||||
|
$ptr_end = strpos($message,"\r",$ptr_end+1);
|
||||||
|
|
||||||
|
// If there is no ptr_end, then this is not an origin
|
||||||
|
if (! $ptr_end)
|
||||||
|
throw new InvalidPacketException('Couldnt find the end of the origin');
|
||||||
|
|
||||||
|
} elseif (! $ptr_end=strpos($message,"\r\x01",$ptr_start)) {
|
||||||
|
$ptr_end = strlen($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
$remaining = substr($message,$ptr_end+1);
|
||||||
|
|
||||||
|
// At this point, the remaining part of the message should start with \x01, PATH or SEEN-BY
|
||||||
|
if ((substr($remaining,0,9) !== 'SEEN-BY: ') && (substr($remaining,0,5) !== 'PATH:') && ($x=strpos($remaining,"\x01")) !== 0) {
|
||||||
|
if ($x)
|
||||||
|
$ptr_end += $x;
|
||||||
|
else
|
||||||
|
$ptr_end += strlen($remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the message content
|
||||||
|
if ($content=substr($message,$ptr_start,$ptr_end-$ptr_start)) {
|
||||||
|
$o->msg_src = $content;
|
||||||
|
$o->msg_crc = md5($content);
|
||||||
|
$ptr_content_start = 0;
|
||||||
|
|
||||||
|
// See if we have a tagline
|
||||||
|
if ($ptr_content_end=strrpos($content,"\r... ",$ptr_content_start)) {
|
||||||
|
$o->msg = substr($content,$ptr_content_start,$ptr_content_end+1);
|
||||||
|
|
||||||
|
$ptr_content_start = $ptr_content_end+5;
|
||||||
|
$ptr_content_end = strpos($content,"\r",$ptr_content_start);
|
||||||
|
|
||||||
|
// If there is no terminating "\r", then that's it
|
||||||
|
if (! $ptr_content_end) {
|
||||||
|
$o->set_tagline = substr($content,$ptr_content_start);
|
||||||
|
$ptr_content_start = strlen($content);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$o->set_tagline = substr($content,$ptr_content_start,$ptr_content_end-$ptr_content_start);
|
||||||
|
$ptr_content_start = $ptr_content_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we have a tearline
|
||||||
|
if ($ptr_content_end=strrpos($content,"\r--- ",$ptr_content_start)) {
|
||||||
|
if (! $ptr_content_start)
|
||||||
|
$o->msg = substr($content,$ptr_content_start,$ptr_content_end+1);
|
||||||
|
|
||||||
|
$ptr_content_start = $ptr_content_end+5;
|
||||||
|
$ptr_content_end = strpos($content,"\r",$ptr_content_start);
|
||||||
|
|
||||||
|
// If there is no terminating "\r", then that's it
|
||||||
|
if (! $ptr_content_end) {
|
||||||
|
$o->set_tearline = substr($content,$ptr_content_start);
|
||||||
|
$ptr_content_start = strlen($content);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$o->set_tearline = substr($content,$ptr_content_start,$ptr_content_end-$ptr_content_start);
|
||||||
|
$ptr_content_start = $ptr_content_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we have an origin
|
||||||
|
if ($ptr_content_end=strrpos($content,"\r * Origin: ",$ptr_content_start)) {
|
||||||
|
if (! $ptr_content_start)
|
||||||
|
$o->msg = substr($content,$ptr_content_start,$ptr_content_end);
|
||||||
|
|
||||||
|
$ptr_content_start = $ptr_content_end+12;
|
||||||
|
|
||||||
|
$ptr_content_end = strpos($content,"\r",$ptr_content_start);
|
||||||
|
|
||||||
|
// If there is no terminating "\r", then that's it
|
||||||
|
if (! $ptr_content_end) {
|
||||||
|
$o->set_origin = substr($content,$ptr_content_start);
|
||||||
|
$ptr_content_start = strlen($content);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$o->set_origin = substr($content,$ptr_content_start,$ptr_content_end-$ptr_content_start);
|
||||||
|
$ptr_content_start = $ptr_content_end+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there wasnt any tagline/tearline/origin, then the whole content is the message
|
||||||
|
if (! $ptr_content_start) {
|
||||||
|
$o->msg = $content;
|
||||||
|
$ptr_content_start = $ptr_end-$ptr_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim any right \r from the message
|
||||||
|
$o->msg = rtrim($o->msg,"\r");
|
||||||
|
|
||||||
|
// Quick validation that we are done
|
||||||
|
if ($ptr_content_start !== strlen($content)) {
|
||||||
|
Log::alert(sprintf('%s:! We failed parsing the message start [%d] content [%d]',self::LOGKEY,$ptr_content_start,strlen($content)));
|
||||||
|
$o->msg = substr($message,0,$ptr_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$ptr_start = $ptr_end+1;
|
$ptr_start = $ptr_end+1;
|
||||||
|
|
||||||
if (! $m) {
|
// Finally work out control kludges
|
||||||
Log::alert(sprintf('%s:! Invalid Kluge Line [%s]',self::LOGKEY,$x));
|
foreach (collect(explode("\r",substr($message,$ptr_start)))->filter() as $line) {
|
||||||
continue;
|
// If the line starts with <soh> ignore it
|
||||||
|
if (substr($line,0,1) === "\x01")
|
||||||
|
$line = ltrim($line,"\x01");
|
||||||
|
|
||||||
|
$m = [];
|
||||||
|
preg_match('/^([^\s]+:?)+\s+(.*)$/',$line,$m);
|
||||||
|
|
||||||
|
// Messages that originate from a point dont have anything in a PATH
|
||||||
|
if (count($m) === 2)
|
||||||
|
$o->kludges = [$m[1],$m[2]];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catch any kludges we need to process here
|
} catch (\Exception $e) {
|
||||||
if (array_key_exists($m[1],self::kludges))
|
Log::error(sprintf('%s:! Error parsing message, now at offset [0x%02x] (%s)',
|
||||||
$this->{self::kludges[$m[1]]} = $m[2];
|
self::LOGKEY,
|
||||||
else
|
$ptr_start,
|
||||||
$o->kludges = [$m[1],$m[2]];
|
$e->getMessage()),['dump'=>hex_dump($message),'line'=>$e->getLine(),'file'=>$e->getFile()]);
|
||||||
}
|
|
||||||
|
|
||||||
// Next our message content ends with '\r * Origin: ... \r' or <soh>...
|
throw new InvalidPacketException('Error parsing message');
|
||||||
// FTS-0004.001
|
|
||||||
if ($ptr_end=strrpos($message,"\r * Origin: ",$ptr_start)) {
|
|
||||||
// Find the <cr>
|
|
||||||
$ptr_end = strpos($message,"\r",$ptr_end+1);
|
|
||||||
|
|
||||||
// If there is no ptr_end, then this is not an origin
|
|
||||||
if (! $ptr_end)
|
|
||||||
throw new InvalidPacketException('Couldnt find the end of the origin');
|
|
||||||
|
|
||||||
} elseif (! $ptr_end=strpos($message,"\r\x01",$ptr_start)) {
|
|
||||||
throw new InvalidPacketException('Couldnt parse the end of the content');
|
|
||||||
}
|
|
||||||
|
|
||||||
$remaining = substr($message,$ptr_end+1);
|
|
||||||
|
|
||||||
// At this point, the remaining part of the message should start with \x01, PATH or SEEN-BY
|
|
||||||
if ((substr($remaining,0,9) !== 'SEEN-BY: ') && (substr($remaining,0,5) !== 'PATH:') && ($x=strpos($remaining,"\x01")) !== 0) {
|
|
||||||
if ($x)
|
|
||||||
$ptr_end += $x;
|
|
||||||
else
|
|
||||||
$ptr_end += strlen($remaining);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the message content
|
|
||||||
if ($content=substr($message,$ptr_start,$ptr_end-$ptr_start)) {
|
|
||||||
$o->msg_src = $content;
|
|
||||||
$o->msg_crc = md5($content);
|
|
||||||
$ptr_content_start = 0;
|
|
||||||
|
|
||||||
// See if we have a tagline
|
|
||||||
if ($ptr_content_end=strrpos($content,"\r\r... ",$ptr_content_start)) {
|
|
||||||
$o->msg = substr($content,$ptr_content_start,$ptr_content_end);
|
|
||||||
$ptr_content_start = $ptr_content_end+6;
|
|
||||||
|
|
||||||
$ptr_content_end = strpos($content,"\r",$ptr_content_start);
|
|
||||||
|
|
||||||
// If there is no terminating "\r", then that's it
|
|
||||||
if (! $ptr_content_end) {
|
|
||||||
$o->set_tagline = substr($content,$ptr_content_start);
|
|
||||||
$ptr_content_start = strlen($content);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$o->set_tagline = substr($content,$ptr_content_start,$ptr_content_end-$ptr_content_start);
|
|
||||||
$ptr_content_start = $ptr_content_end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if we have a tearline
|
|
||||||
if ($ptr_content_end=strrpos($content,"\r\r--- ",$ptr_content_start)) {
|
|
||||||
if (! $ptr_content_start)
|
|
||||||
$o->msg = substr($content,$ptr_content_start,$ptr_content_end);
|
|
||||||
$ptr_content_start = $ptr_content_end+6;
|
|
||||||
|
|
||||||
$ptr_content_end = strpos($content,"\r",$ptr_content_start);
|
|
||||||
|
|
||||||
// If there is no terminating "\r", then that's it
|
|
||||||
if (! $ptr_content_end) {
|
|
||||||
$o->set_tearline = substr($content,$ptr_content_start);
|
|
||||||
$ptr_content_start = strlen($content);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$o->set_tearline = substr($content,$ptr_content_start,$ptr_content_end-$ptr_content_start);
|
|
||||||
$ptr_content_start = $ptr_content_end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if we have an origin
|
|
||||||
if ($ptr_content_end=strrpos($content,"\r * Origin: ",$ptr_content_start)) {
|
|
||||||
if (! $ptr_content_start)
|
|
||||||
$o->msg = substr($content,$ptr_content_start,$ptr_content_end);
|
|
||||||
|
|
||||||
$ptr_content_start = $ptr_content_end+12;
|
|
||||||
|
|
||||||
$ptr_content_end = strpos($content,"\r",$ptr_content_start);
|
|
||||||
|
|
||||||
// If there is no terminating "\r", then that's it
|
|
||||||
if (! $ptr_content_end) {
|
|
||||||
$o->set_origin = substr($content,$ptr_content_start);
|
|
||||||
$ptr_content_start = strlen($content);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$o->set_origin = substr($content,$ptr_content_start,$ptr_content_end-$ptr_content_start);
|
|
||||||
$ptr_content_start = $ptr_content_end+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there wasnt any tagline/tearline/origin, then the whole content is the message
|
|
||||||
if (! $ptr_content_start) {
|
|
||||||
$o->msg = $content;
|
|
||||||
$ptr_content_start = $ptr_end-$ptr_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quick validation that we are done
|
|
||||||
if ($ptr_content_start !== strlen($content))
|
|
||||||
throw new InvalidPacketException('There is more data in the message content?');
|
|
||||||
}
|
|
||||||
|
|
||||||
$ptr_start = $ptr_end+1;
|
|
||||||
|
|
||||||
// Finally work out control kludges
|
|
||||||
foreach (collect(explode("\r",substr($message,$ptr_start)))->filter() as $line) {
|
|
||||||
// If the line starts with <soh> ignore it
|
|
||||||
if (substr($line,0,1) === "\x01")
|
|
||||||
$line = ltrim($line,"\x01");
|
|
||||||
|
|
||||||
$m = [];
|
|
||||||
preg_match('/^([^\s]+:?)+\s+(.*)$/',$line,$m);
|
|
||||||
$o->kludges = [$m[1],$m[2]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $o;
|
return $o;
|
||||||
@@ -847,9 +878,6 @@ class Message extends FTNBase
|
|||||||
'replyid' => 'sometimes|min:1',
|
'replyid' => 'sometimes|min:1',
|
||||||
'msg' => 'required|min:1', // @todo max message length?
|
'msg' => 'required|min:1', // @todo max message length?
|
||||||
'msg_crc' => 'required|size:32',
|
'msg_crc' => 'required|size:32',
|
||||||
'tagline' => 'sometimes|min:1|max:255',
|
|
||||||
'tearline' => 'sometimes|min:1|max:255',
|
|
||||||
'origin' => 'sometimes|min:1|max:255',
|
|
||||||
'local' => 'sometimes|boolean',
|
'local' => 'sometimes|boolean',
|
||||||
'fftn_id' => 'required|exists:App\Models\Address,id',
|
'fftn_id' => 'required|exists:App\Models\Address,id',
|
||||||
'tftn_id' => $this->isNetmail() ? 'required|exists:App\Models\Address,id' : 'prohibited',
|
'tftn_id' => $this->isNetmail() ? 'required|exists:App\Models\Address,id' : 'prohibited',
|
||||||
@@ -870,13 +898,26 @@ class Message extends FTNBase
|
|||||||
);
|
);
|
||||||
|
|
||||||
$validator->after(function($validator) {
|
$validator->after(function($validator) {
|
||||||
|
// @todo If the message has an INTL kludge, we send the message to our ZC, and if we are the ZC onto the dst ZC
|
||||||
|
// @todo So validate we can send it on
|
||||||
|
// @todo This validation below is incorrect - netmails only need to have an INTL if they are traversing zones
|
||||||
|
// @todo Without an INTL it is affecting our determination of a source zone/dst zone
|
||||||
|
if (($this->mo instanceof Netmail) && (! $this->mo->kludges->has('INTL')))
|
||||||
|
$validator->errors()->add('no-intl','Netmail message is missing INTL KLUDGE.');
|
||||||
|
|
||||||
if ($this->zone->domain->flatten) {
|
if ($this->zone->domain->flatten) {
|
||||||
if (! $this->zone->domain->zones->pluck('zone_id')->contains($this->fz))
|
if (! $this->zone->domain->zones->pluck('zone_id')->contains($this->fz))
|
||||||
$validator->errors()->add('invalid-zone',sprintf('Message zone [%d] doesnt match any zone in domain for packet zone [%d].',$this->fz,$this->zone->zone_id));
|
$validator->errors()->add('invalid-zone',sprintf('Message from zone [%d] doesnt match any zone in domain for packet zone [%d].',$this->fz,$this->zone->zone_id));
|
||||||
|
|
||||||
|
if (! $this->zone->domain->zones->pluck('zone_id')->contains($this->tz))
|
||||||
|
$validator->errors()->add('invalid-zone',sprintf('Message to zone [%d] doesnt match any zone in domain for packet zone [%d].',$this->fz,$this->zone->zone_id));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if ($this->zone->zone_id !== $this->fz)
|
if ($this->zone->zone_id !== $this->fz)
|
||||||
$validator->errors()->add('invalid-zone',sprintf('Message zone [%d] doesnt match packet zone [%d].',$this->fz,$this->zone->zone_id));
|
$validator->errors()->add('invalid-zone',sprintf('Message from zone [%d] doesnt match packet zone [%d].',$this->fz,$this->zone->zone_id));
|
||||||
|
|
||||||
|
if ($this->zone->zone_id !== $this->tz)
|
||||||
|
$validator->errors()->add('invalid-zone',sprintf('Message to zone [%d] doesnt match packet zone [%d].',$this->tz,$this->zone->zone_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $this->fftn)
|
if (! $this->fftn)
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Classes\FTN;
|
namespace App\Classes\FTN;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@@ -11,8 +12,8 @@ use Symfony\Component\HttpFoundation\File\File;
|
|||||||
|
|
||||||
use App\Classes\FTN as FTNBase;
|
use App\Classes\FTN as FTNBase;
|
||||||
use App\Exceptions\InvalidPacketException;
|
use App\Exceptions\InvalidPacketException;
|
||||||
use App\Models\{Address,Domain,Echomail,Netmail,Software,System,Zone};
|
use App\Models\{Address,Echomail,Netmail,Software,System,Zone};
|
||||||
use App\Notifications\Netmails\EchomailBadAddress;
|
use App\Notifications\Netmails\{EchomailBadAddress,NetmailBadAddress};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Fidonet Packet, that contains an array of messages.
|
* Represents a Fidonet Packet, that contains an array of messages.
|
||||||
@@ -51,9 +52,10 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
protected Address $fftn_p; // Address the packet is from (when packing messages)
|
protected Address $fftn_p; // Address the packet is from (when packing messages)
|
||||||
protected Address $tftn_p; // Address the packet is to (when packing messages)
|
protected Address $tftn_p; // Address the packet is to (when packing messages)
|
||||||
protected Collection $messages; // Messages in the Packet
|
protected Collection $messages; // Messages in the Packet
|
||||||
|
protected string $content; // Outgoing packet data
|
||||||
public Collection $errors; // Messages that fail validation
|
public Collection $errors; // Messages that fail validation
|
||||||
protected int $index; // Our array index
|
protected int $index; // Our array index
|
||||||
protected $pass_p = NULL; // Overwrite the packet password (when packing messages)
|
protected $pass_p = NULL; // Overwrite the packet password (when packing messages)
|
||||||
|
|
||||||
/* ABSTRACT */
|
/* ABSTRACT */
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
abstract public static function is_type(string $header): bool;
|
abstract public static function is_type(string $header): bool;
|
||||||
abstract protected function header(): string;
|
abstract protected function header(Collection $msgs): string;
|
||||||
|
|
||||||
/* STATIC */
|
/* STATIC */
|
||||||
|
|
||||||
@@ -86,11 +88,12 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
* @param mixed $f File handler returning packet data
|
* @param mixed $f File handler returning packet data
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param int $size
|
* @param int $size
|
||||||
* @param Domain|null $domain
|
* @param System|null $so - The system that sent us the packet, used to figure out domains if the packet is for a different zone
|
||||||
|
* @param bool $process
|
||||||
* @return Packet
|
* @return Packet
|
||||||
* @throws InvalidPacketException
|
* @throws InvalidPacketException
|
||||||
*/
|
*/
|
||||||
public static function process(mixed $f,string $name,int $size,Domain $domain=NULL): self
|
public static function process(mixed $f,string $name,int $size,System $so=NULL,bool $process=TRUE): self
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('%s:+ Opening Packet [%s] with size [%d]',self::LOGKEY,$name,$size));
|
Log::debug(sprintf('%s:+ Opening Packet [%s] with size [%d]',self::LOGKEY,$name,$size));
|
||||||
|
|
||||||
@@ -137,26 +140,41 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
} else
|
} else
|
||||||
throw new InvalidPacketException('Not a valid packet, not EOP or SOM:'.bin2hex($x));
|
throw new InvalidPacketException('Not a valid packet, not EOP or SOM:'.bin2hex($x));
|
||||||
|
|
||||||
Log::info(sprintf('%s:- Packet [%s] is a [%s] packet, dated [%s]',self::LOGKEY,$o->name,get_class($o),$o->date));
|
Log::info(sprintf('%s:- Packet [%s] is a [%s] packet',self::LOGKEY,$o->name,get_class($o)));
|
||||||
|
|
||||||
// Work out the packet zone
|
if ($o->fz && $o->fd) {
|
||||||
if ($o->fz && ($o->fd || $domain)) {
|
$o->zone = Zone::where('zone_id',$o->fz)
|
||||||
$o->zone = Zone::select('zones.*')
|
|
||||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||||
->where('zone_id',$o->fz)
|
->where('name',$o->fd)
|
||||||
->where('name',$o->fd ?: $domain->name)
|
|
||||||
->single();
|
->single();
|
||||||
|
|
||||||
|
} elseif ($o->fz && $so) {
|
||||||
|
Log::alert(sprintf('%s:! No domain in the packet, work it out from the system [%d] for zone [%d]',self::LOGKEY,$so->name,$o->fz));
|
||||||
|
|
||||||
|
if (($x=$so->zones->where('zone_id',$o->fz)->unique('domain_id'))->count() === 1) {
|
||||||
|
$o->zone = $x->pop();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::alert(sprintf('%s:! Node [%s] has two zones with [%d]',self::LOGKEY,$so->name,$o->fz));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If zone is not set, then we need to use a default zone - the messages may not be from this zone.
|
// If zone is not set, then we need to use a default zone - the messages may not be from this zone.
|
||||||
if (empty($o->zone)) {
|
if (empty($o->zone)) {
|
||||||
Log::alert(sprintf('%s:! We couldnt work out the packet zone, so we have fallen back to the default for [%d]',self::LOGKEY,$o->fz));
|
Log::alert(sprintf('%s:! We couldnt work out the packet zone, so we have fallen back to the default for [%d]',self::LOGKEY,$o->fz));
|
||||||
|
|
||||||
$o->zone = Zone::where('zone_id',$o->fz)
|
try {
|
||||||
->where('default',TRUE)
|
$o->zone = Zone::where('zone_id',$o->fz)
|
||||||
->singleOrFail();
|
->where('default',TRUE)
|
||||||
|
->sole();
|
||||||
|
|
||||||
|
} catch (ModelNotFoundException $e) {
|
||||||
|
throw new InvalidPacketException(sprintf('%s:! We couldnt work out the packet zone, and there isnt a default for [%d]',self::LOGKEY,$o->fz));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:- Packet Dated [%s] from [%s] to [%s]',self::LOGKEY,$o->date,$o->fftn_t,$o->tftn_t));
|
||||||
|
|
||||||
$message = ''; // Current message we are building
|
$message = ''; // Current message we are building
|
||||||
$msgbuf = '';
|
$msgbuf = '';
|
||||||
$leader = Message::header_len()+strlen(self::PACKED_MSG_LEAD);
|
$leader = Message::header_len()+strlen(self::PACKED_MSG_LEAD);
|
||||||
@@ -171,18 +189,19 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
|| (($end=strpos($msgbuf,"\x00".self::PACKED_END,$leader)) !== FALSE))
|
|| (($end=strpos($msgbuf,"\x00".self::PACKED_END,$leader)) !== FALSE))
|
||||||
{
|
{
|
||||||
// Parse our message
|
// Parse our message
|
||||||
$o->parseMessage(substr($msgbuf,0,$end));
|
Log::debug(sprintf('%s:- Message at offset [%d] in [%s]',self::LOGKEY,$read_ptr-strlen($readbuf),$name));
|
||||||
|
$o->parseMessage(substr($msgbuf,0,$end),$process);
|
||||||
|
|
||||||
$msgbuf = substr($msgbuf,$end+3);
|
$msgbuf = substr($msgbuf,$end+3);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If we have more to read
|
// If we have more to read
|
||||||
} elseif ($read_ptr < $size) {
|
} elseif ($read_ptr < $size) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here
|
// If we get here
|
||||||
throw new InvalidPacketException(sprintf('Cannot determine END of message/packet: %s|%s',get_class($o),hex_dump($message)));;
|
throw new InvalidPacketException(sprintf('Cannot determine END of message/packet: %s|%s',get_class($o),hex_dump($message)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($msgbuf)
|
if ($msgbuf)
|
||||||
@@ -250,7 +269,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
|
|
||||||
case 'software':
|
case 'software':
|
||||||
Software::unguard();
|
Software::unguard();
|
||||||
$o = Software::singleOrNew(['code'=>$this->product,'type'=>Software::SOFTWARE_TOSSER]);
|
$o = Software::firstOrNew(['code'=>$this->product,'type'=>Software::SOFTWARE_TOSSER]);
|
||||||
Software::reguard();
|
Software::reguard();
|
||||||
|
|
||||||
return $o;
|
return $o;
|
||||||
@@ -286,20 +305,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
*/
|
*/
|
||||||
public function __toString(): string
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
if (empty($this->messages))
|
return $this->content;
|
||||||
throw new InvalidPacketException('Refusing to make an empty packet');
|
|
||||||
|
|
||||||
if (empty($this->tftn_p) || empty($this->fftn_p))
|
|
||||||
throw new InvalidPacketException('Cannot generate a packet without a destination address');
|
|
||||||
|
|
||||||
$return = $this->header();
|
|
||||||
|
|
||||||
foreach ($this->messages as $o)
|
|
||||||
$return .= self::PACKED_MSG_LEAD.$o->packet($this->tftn_p);
|
|
||||||
|
|
||||||
$return .= "\00\00";
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* INTERFACE */
|
/* INTERFACE */
|
||||||
@@ -339,53 +345,6 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
|
|
||||||
/* METHODS */
|
/* METHODS */
|
||||||
|
|
||||||
/**
|
|
||||||
* When creating a new packet, set the header.
|
|
||||||
*
|
|
||||||
* @param Address $oo
|
|
||||||
* @param Address $o
|
|
||||||
* @param string|null $passwd Override the password used in the packet
|
|
||||||
* @deprecated Use Packet::generate(), which should generate a packet of the right type
|
|
||||||
*/
|
|
||||||
public function addressHeader(Address $oo,Address $o,string $passwd=NULL): void
|
|
||||||
{
|
|
||||||
Log::debug(sprintf('%s:+ Creating packet for [%s]',self::LOGKEY,$o->ftn));
|
|
||||||
|
|
||||||
$date = Carbon::now();
|
|
||||||
|
|
||||||
// Create Header
|
|
||||||
$this->header = [
|
|
||||||
'ozone' => $oo->zone->zone_id, // Orig Zone
|
|
||||||
'dzone' => $o->zone->zone_id, // Dest Zone
|
|
||||||
'onet' => $oo->host_id ?: $oo->region_id, // Orig Net
|
|
||||||
'dnet' => $o->host_id ?: $o->region_id, // Dest Net
|
|
||||||
'onode' => $oo->node_id, // Orig Node
|
|
||||||
'dnode' => $o->node_id, // Dest Node
|
|
||||||
'opoint' => $oo->point_id, // Orig Point
|
|
||||||
'dpoint' => $o->point_id, // Dest Point
|
|
||||||
'odomain' => $oo->zone->domain->name, // Orig Domain
|
|
||||||
'ddomain' => $o->zone->domain->name, // Dest Domain
|
|
||||||
'y' => $date->format('Y'), // Year
|
|
||||||
'm' => $date->format('m')-1, // Month
|
|
||||||
'd' => $date->format('d'), // Day
|
|
||||||
'H' => $date->format('H'), // Hour
|
|
||||||
'M' => $date->format('i'), // Minute
|
|
||||||
'S' => $date->format('s'), // Second
|
|
||||||
'password' => strtoupper((! is_null($passwd)) ? $passwd : $o->session('pktpass')), // Packet Password
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a message to this packet
|
|
||||||
*
|
|
||||||
* @param Message $o
|
|
||||||
* @deprecated No longer used when Address::class is updated
|
|
||||||
*/
|
|
||||||
public function addMail(Message $o): void
|
|
||||||
{
|
|
||||||
$this->messages->push($o);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function for(Address $ao): self
|
public function for(Address $ao): self
|
||||||
{
|
{
|
||||||
$this->tftn_p = $ao;
|
$this->tftn_p = $ao;
|
||||||
@@ -406,7 +365,20 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
|
|
||||||
public function mail(Collection $msgs): self
|
public function mail(Collection $msgs): self
|
||||||
{
|
{
|
||||||
$this->messages = $msgs;
|
if (! $msgs->count())
|
||||||
|
throw new InvalidPacketException('Refusing to make an empty packet');
|
||||||
|
|
||||||
|
if (empty($this->tftn_p) || empty($this->fftn_p))
|
||||||
|
throw new InvalidPacketException('Cannot generate a packet without a destination address');
|
||||||
|
|
||||||
|
$this->content = $this->header($msgs);
|
||||||
|
|
||||||
|
foreach ($msgs as $o)
|
||||||
|
$this->content .= self::PACKED_MSG_LEAD.$o->packet($this->tftn_p);
|
||||||
|
|
||||||
|
$this->content .= "\00\00";
|
||||||
|
|
||||||
|
$this->messages = $msgs->map(fn($item)=>$item->only(['id','datetime']));
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -415,25 +387,25 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
* Parse a message in a mail packet
|
* Parse a message in a mail packet
|
||||||
*
|
*
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @throws InvalidPacketException|\Exception
|
* @param bool $process
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
private function parseMessage(string $message): void
|
private function parseMessage(string $message,bool $process): void
|
||||||
{
|
{
|
||||||
Log::info(sprintf('%s:+ Processing packet message [%d] bytes',self::LOGKEY,strlen($message)));
|
Log::info(sprintf('%s:+ Processing packet message [%d] bytes',self::LOGKEY,strlen($message)));
|
||||||
|
|
||||||
$msg = Message::parseMessage($message,$this->zone);
|
$msg = Message::parseMessage($message,$this->zone);
|
||||||
|
|
||||||
// If the message is invalid, we'll ignore it
|
// If the message is invalid, we'll ignore it
|
||||||
if ($msg->errors->count()) {
|
if ($process && $msg->errors->count()) {
|
||||||
Log::info(sprintf('%s:- Message [%s] has [%d] errors',self::LOGKEY,$msg->msgid ?: 'No ID',$msg->errors->count()));
|
Log::info(sprintf('%s:- Message [%s] has [%d] errors',self::LOGKEY,$msg->msgid ?: 'No ID',$msg->errors->count()));
|
||||||
|
|
||||||
|
|
||||||
// If the messages is not for the right zone, we'll ignore it
|
// If the messages is not for the right zone, we'll ignore it
|
||||||
if ($msg->errors->has('invalid-zone')) {
|
if ($msg->errors->has('invalid-zone')) {
|
||||||
Log::alert(sprintf('%s:! Message [%s] is from an invalid zone [%s], packet is from [%s] - ignoring it',self::LOGKEY,$msg->msgid,$msg->fftn->zone->zone_id,$this->fftn->zone->zone_id));
|
Log::alert(sprintf('%s:! Message [%s] is from|to an invalid zone [%s|%s], packet is from [%s] - ignoring it',self::LOGKEY,$msg->msgid,$msg->get_fftn,$msg->get_tftn,$this->fz));
|
||||||
|
|
||||||
if (! $msg->kludges->get('RESCANNED'))
|
if (! $msg->kludges->get('RESCANNED'))
|
||||||
Notification::route('netmail',$this->fftn)->notify(new EchomailBadAddress($msg));
|
Notification::route('netmail',$this->fftn)->notify(($msg instanceof Echomail) ? new EchomailBadAddress($msg) : new NetmailBadAddress($msg));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -442,7 +414,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
if ($msg->errors->has('from') && $this->fftn && $this->fftn->zone_id) {
|
if ($msg->errors->has('from') && $this->fftn && $this->fftn->zone_id) {
|
||||||
Log::debug(sprintf('%s:^ From address [%s] doesnt exist, it needs to be created',self::LOGKEY,$msg->set->get('set_fftn')));
|
Log::debug(sprintf('%s:^ From address [%s] doesnt exist, it needs to be created',self::LOGKEY,$msg->set->get('set_fftn')));
|
||||||
|
|
||||||
$ao = Address::findFTN($msg->set->get('set_fftn'),TRUE);
|
$ao = Address::findFTN($msg->set->get('set_fftn'),TRUE,TRUE);
|
||||||
|
|
||||||
if ($ao?->exists && ($ao->zone?->domain_id !== $this->fftn->zone->domain_id)) {
|
if ($ao?->exists && ($ao->zone?->domain_id !== $this->fftn->zone->domain_id)) {
|
||||||
Log::alert(sprintf('%s:! From address [%s] domain [%d] doesnt match packet domain [%d]?',self::LOGKEY,$msg->set->get('set_fftn'),$ao->zone?->domain_id,$this->fftn->zone->domain_id));
|
Log::alert(sprintf('%s:! From address [%s] domain [%d] doesnt match packet domain [%d]?',self::LOGKEY,$msg->set->get('set_fftn'),$ao->zone?->domain_id,$this->fftn->zone->domain_id));
|
||||||
@@ -453,17 +425,37 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
if (! $ao) {
|
if (! $ao) {
|
||||||
$so = System::createUnknownSystem();
|
$so = System::createUnknownSystem();
|
||||||
$ao = Address::createFTN($msg->set->get('set_fftn'),$so);
|
$ao = Address::createFTN($msg->set->get('set_fftn'),$so);
|
||||||
|
|
||||||
|
Log::alert(sprintf('%s:- From FTN [%s] is not defined, created new entry for (%d)',self::LOGKEY,$msg->set->get('set_fftn'),$ao->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
$msg->fftn_id = $ao->id;
|
$msg->fftn_id = $ao->id;
|
||||||
Log::alert(sprintf('%s:- From FTN [%s] is not defined, created new entry for (%d)',self::LOGKEY,$msg->set->get('set_fftn'),$ao->id));
|
$msg->errors->forget('from');
|
||||||
|
$msg->errors->forget('fftn_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the $msg->tftn doesnt exist, we'll need to create it
|
// If the $msg->tftn doesnt exist, we'll need to create it
|
||||||
if ($msg->errors->has('to') && $this->tftn && $this->tftn->zone_id) {
|
if ($msg->errors->has('to') && $this->tftn && $this->tftn->zone_id) {
|
||||||
Log::debug(sprintf('%s:^ To address [%s] doesnt exist, it needs to be created',self::LOGKEY,$msg->set->get('set_tftn')));
|
$ao = Address::findFTN($msg->set->get('set_tftn'),TRUE,TRUE);
|
||||||
|
|
||||||
$ao = Address::findFTN($msg->set->get('set_tftn'),TRUE);
|
// If this is a netmail message, to a non existant address, we need to bounce it
|
||||||
|
if (($msg instanceof Netmail)) {
|
||||||
|
if ((! $ao) && our_address()->contains(Address::newFTN($msg->set_tftn)?->parent())) {
|
||||||
|
Log::alert(sprintf('%s:^ To address [%s] doesnt exist, netmail will be bounced',self::LOGKEY,$msg->set->get('set_tftn')));
|
||||||
|
|
||||||
|
$this->messages->push($msg);
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If this is a netmail message, to a non existant address, we need to bounce it
|
||||||
|
} elseif ($ao && (! $ao->active) && our_address()->contains($ao->parent())) {
|
||||||
|
Log::alert(sprintf('%s:^ To address [%s] isnt active, netmail will be bounced',self::LOGKEY,$msg->set->get('set_tftn')));
|
||||||
|
|
||||||
|
$this->messages->push($msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:^ To address [%s] doesnt exist, it needs to be created',self::LOGKEY,$msg->set->get('set_tftn')));
|
||||||
|
|
||||||
if ($ao?->exists && ($ao->zone?->domain_id !== $this->tftn->zone->domain_id)) {
|
if ($ao?->exists && ($ao->zone?->domain_id !== $this->tftn->zone->domain_id)) {
|
||||||
Log::alert(sprintf('%s:! To address [%s] domain [%d] doesnt match packet domain [%d]?',self::LOGKEY,$msg->set->get('set_tftn'),$ao->zone?->domain_id,$this->fftn->zone->domain_id));
|
Log::alert(sprintf('%s:! To address [%s] domain [%d] doesnt match packet domain [%d]?',self::LOGKEY,$msg->set->get('set_tftn'),$ao->zone?->domain_id,$this->fftn->zone->domain_id));
|
||||||
@@ -473,11 +465,14 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
|
|
||||||
if (! $ao) {
|
if (! $ao) {
|
||||||
$so = System::createUnknownSystem();
|
$so = System::createUnknownSystem();
|
||||||
$ao = Address::createFTN($msg->set->get('set_fftn'),$so);
|
$ao = Address::createFTN($msg->set->get('set_tftn'),$so);
|
||||||
|
|
||||||
|
Log::alert(sprintf('%s:- To FTN [%s] is not defined, created new entry for (%d)',self::LOGKEY,$msg->set->get('set_tftn'),$ao->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
$msg->tftn_id = $ao->id;
|
$msg->tftn_id = $ao->id;
|
||||||
Log::alert(sprintf('%s:- To FTN [%s] is not defined, created new entry for (%d)',self::LOGKEY,$msg->set->get('set_tftn'),$ao->id));
|
$msg->errors->forget('to');
|
||||||
|
$msg->errors->forget('tftn_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no fftn, then its from a system that we dont know about
|
// If there is no fftn, then its from a system that we dont know about
|
||||||
@@ -490,7 +485,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @todo If the message from domain (eg: $msg->fftn->zone->domain) is different to the packet address domain ($pkt->fftn->zone->domain), we'll skip this message
|
// @todo If the message from domain (eg: $msg->fftn->zone->domain) is different to the packet address domain ($pkt->fftn->zone->domain), we'll skip this message
|
||||||
Log::debug(sprintf('%s:^ Message [%s] - Packet from domain [%d], Message domain [%d]',self::LOGKEY,$msg->msgid,$this->fftn->zone->domain_id,$msg->fftn->zone->domain_id));
|
//Log::debug(sprintf('%s:^ Message [%s] - Packet from domain [%d], Message domain [%d]',self::LOGKEY,$msg->msgid,$this->fftn->zone->domain_id,$msg->fftn->zone->domain_id));
|
||||||
|
|
||||||
$this->messages->push($msg);
|
$this->messages->push($msg);
|
||||||
}
|
}
|
||||||
@@ -504,15 +499,8 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
|||||||
public function password(string $password=NULL): self
|
public function password(string $password=NULL): self
|
||||||
{
|
{
|
||||||
if ($password && (strlen($password) < 9))
|
if ($password && (strlen($password) < 9))
|
||||||
$this->pass_p = $password;
|
$this->pass_p = strtoupper($password);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated Is this used? */
|
|
||||||
public function pluck(string $key): Collection
|
|
||||||
{
|
|
||||||
throw new \Exception(sprintf('%s:! This function is deprecated - [%s]',self::LOGKEY,$key));
|
|
||||||
return $this->messages->pluck($key);
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Classes\FTN\Packet;
|
namespace App\Classes\FTN\Packet;
|
||||||
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
use App\Classes\FTN\Packet;
|
use App\Classes\FTN\Packet;
|
||||||
use App\Models\Setup;
|
use App\Models\Setup;
|
||||||
@@ -62,44 +63,39 @@ final class FSC39 extends Packet
|
|||||||
/**
|
/**
|
||||||
* Create our message packet header
|
* Create our message packet header
|
||||||
*/
|
*/
|
||||||
protected function header(): string
|
protected function header(Collection $msgs): string
|
||||||
{
|
{
|
||||||
$oldest = $this->messages->sortBy('datetime')->last();
|
$oldest = $this->messages->sortBy('date')->last();
|
||||||
|
|
||||||
try {
|
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
||||||
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
$this->fftn_p->node_id, // Orig Node
|
||||||
$this->fftn_p->node_id, // Orig Node
|
$this->tftn_p->node_id, // Dest Node
|
||||||
$this->tftn_p->node_id, // Dest Node
|
$oldest->datetime->format('Y'), // Year
|
||||||
$oldest->datetime->format('Y'), // Year
|
$oldest->datetime->format('m')-1, // Month
|
||||||
$oldest->datetime->format('m')-1, // Month
|
$oldest->datetime->format('d'), // Day
|
||||||
$oldest->datetime->format('d'), // Day
|
$oldest->datetime->format('H'), // Hour
|
||||||
$oldest->datetime->format('H'), // Hour
|
$oldest->datetime->format('i'), // Minute
|
||||||
$oldest->datetime->format('i'), // Minute
|
$oldest->datetime->format('s'), // Second
|
||||||
$oldest->datetime->format('s'), // Second
|
0, // Baud
|
||||||
0, // Baud
|
2, // Packet Version (should be 2)
|
||||||
2, // Packet Version (should be 2)
|
$this->fftn_p->host_id, // Orig Net
|
||||||
$this->fftn_p->host_id, // Orig Net
|
$this->tftn_p->host_id, // Dest Net
|
||||||
$this->tftn_p->host_id, // Dest Net
|
(Setup::PRODUCT_ID & 0xff), // Product Code Lo
|
||||||
(Setup::PRODUCT_ID & 0xff), // Product Code Lo
|
Setup::PRODUCT_VERSION_MAJ, // Product Version Major
|
||||||
Setup::PRODUCT_VERSION_MAJ, // Product Version Major
|
$this->pass_p ?: $this->tftn_p->pass_packet, // Packet Password
|
||||||
$this->pass_p ?: $this->tftn_p->session('pktpass'), // Packet Password
|
$this->fftn_p->zone->zone_id, // Orig Zone
|
||||||
$this->fftn_p->zone->zone_id, // Orig Zone
|
$this->tftn_p->zone->zone_id, // Dest Zone
|
||||||
$this->tftn_p->zone->zone_id, // Dest Zone
|
'', // Reserved
|
||||||
'', // Reserved
|
static::VERS, // fsc-0039.004 (copy of 0x2c)
|
||||||
static::VERS, // fsc-0039.004 (copy of 0x2c)
|
((Setup::PRODUCT_ID >> 8) & 0xff), // Product Code Hi
|
||||||
((Setup::PRODUCT_ID >> 8) & 0xff), // Product Code Hi
|
Setup::PRODUCT_VERSION_MIN, // Product Version Minor
|
||||||
Setup::PRODUCT_VERSION_MIN, // Product Version Minor
|
static::VERS, // Capability Word
|
||||||
static::VERS, // Capability Word
|
$this->fftn_p->zone->zone_id, // Orig Zone
|
||||||
$this->fftn_p->zone->zone_id, // Orig Zone
|
$this->tftn_p->zone->zone_id, // Dest Zone
|
||||||
$this->tftn_p->zone->zone_id, // Dest Zone
|
$this->fftn_p->point_id, // Orig Point
|
||||||
$this->fftn_p->point_id, // Orig Point
|
$this->tftn_p->point_id, // Dest Point
|
||||||
$this->tftn_p->point_id, // Dest Point
|
strtoupper(hexstr(Setup::PRODUCT_ID)), // ProdData
|
||||||
strtoupper(hexstr(Setup::PRODUCT_ID)), // ProdData
|
);
|
||||||
);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Classes\FTN\Packet;
|
namespace App\Classes\FTN\Packet;
|
||||||
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
use App\Classes\FTN\Packet;
|
use App\Classes\FTN\Packet;
|
||||||
use App\Models\Setup;
|
use App\Models\Setup;
|
||||||
@@ -54,32 +55,27 @@ final class FSC45 extends Packet
|
|||||||
/**
|
/**
|
||||||
* Create our message packet header
|
* Create our message packet header
|
||||||
*/
|
*/
|
||||||
protected function header(): string
|
protected function header(Collection $msgs): string
|
||||||
{
|
{
|
||||||
try {
|
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
||||||
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
$this->fftn_p->node_id, // Orig Node
|
||||||
$this->fftn_p->node_id, // Orig Node
|
$this->tftn_p->node_id, // Dest Node
|
||||||
$this->tftn_p->node_id, // Dest Node
|
$this->fftn_p->point_id, // Orig Point
|
||||||
$this->fftn_p->point_id, // Orig Point
|
$this->tftn_p->point_id, // Dest Point
|
||||||
$this->tftn_p->point_id, // Dest Point
|
'', // Reserved
|
||||||
'', // Reserved
|
2, // Sub Version (should be 2)
|
||||||
2, // Sub Version (should be 2)
|
2, // Packet Version (should be 2)
|
||||||
2, // Packet Version (should be 2)
|
$this->fftn_p->host_id, // Orig Net
|
||||||
$this->fftn_p->host_id, // Orig Net
|
$this->tftn_p->host_id, // Dest Net
|
||||||
$this->tftn_p->host_id, // Dest Net
|
(Setup::PRODUCT_ID & 0xff), // Product Code
|
||||||
(Setup::PRODUCT_ID & 0xff), // Product Code
|
Setup::PRODUCT_VERSION_MAJ, // Product Version
|
||||||
Setup::PRODUCT_VERSION_MAJ, // Product Version
|
$this->pass_p ?: $this->tftn_p->pass_packet, // Packet Password
|
||||||
$this->pass_p ?: $this->tftn_p->session('pktpass'), // Packet Password
|
$this->fftn_p->zone->zone_id, // Orig Zone
|
||||||
$this->fftn_p->zone->zone_id, // Orig Zone
|
$this->tftn_p->zone->zone_id, // Dest Zone
|
||||||
$this->tftn_p->zone->zone_id, // Dest Zone
|
$this->fftn_p->zone->domain->name, // Orig Domain
|
||||||
$this->fftn_p->zone->domain->name, // Orig Domain
|
$this->tftn_p->zone->domain->name, // Dest Domain
|
||||||
$this->tftn_p->zone->domain->name, // Dest Domain
|
strtoupper(hexstr(Setup::PRODUCT_ID)), // ProdData
|
||||||
strtoupper(hexstr(Setup::PRODUCT_ID)), // ProdData
|
);
|
||||||
);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Classes\FTN\Packet;
|
namespace App\Classes\FTN\Packet;
|
||||||
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
use App\Classes\FTN\Packet;
|
use App\Classes\FTN\Packet;
|
||||||
use App\Models\Setup;
|
use App\Models\Setup;
|
||||||
@@ -54,6 +55,10 @@ final class FSC48 extends Packet
|
|||||||
case 'capability':
|
case 'capability':
|
||||||
return sprintf('%016b',Arr::get($this->header,'capword'));
|
return sprintf('%016b',Arr::get($this->header,'capword'));
|
||||||
|
|
||||||
|
case 'fn':
|
||||||
|
// If the packet is from a point, then onet will be 0xffff
|
||||||
|
return ($x=Arr::get($this->header,'onet')) === 0xffff ? Arr::get($this->header,'auxnet') : $x;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return parent::__get($key);
|
return parent::__get($key);
|
||||||
}
|
}
|
||||||
@@ -62,44 +67,39 @@ final class FSC48 extends Packet
|
|||||||
/**
|
/**
|
||||||
* Create our message packet header
|
* Create our message packet header
|
||||||
*/
|
*/
|
||||||
protected function header(): string
|
protected function header(Collection $msgs): string
|
||||||
{
|
{
|
||||||
$oldest = $this->messages->sortBy('datetime')->last();
|
$oldest = $msgs->sortBy('date')->last();
|
||||||
|
|
||||||
try {
|
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
||||||
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
$this->fftn_p->node_id, // Orig Node
|
||||||
$this->fftn_p->node_id, // Orig Node
|
$this->tftn_p->node_id, // Dest Node
|
||||||
$this->tftn_p->node_id, // Dest Node
|
$oldest->datetime->format('Y'), // Year
|
||||||
$oldest->datetime->format('Y'), // Year
|
$oldest->datetime->format('m')-1, // Month
|
||||||
$oldest->datetime->format('m')-1, // Month
|
$oldest->datetime->format('d'), // Day
|
||||||
$oldest->datetime->format('d'), // Day
|
$oldest->datetime->format('H'), // Hour
|
||||||
$oldest->datetime->format('H'), // Hour
|
$oldest->datetime->format('i'), // Minute
|
||||||
$oldest->datetime->format('i'), // Minute
|
$oldest->datetime->format('s'), // Second
|
||||||
$oldest->datetime->format('s'), // Second
|
0, // Baud
|
||||||
0, // Baud
|
2, // Packet Version (should be 2)
|
||||||
2, // Packet Version (should be 2)
|
$this->fftn_p->point_id ? 0xffff : $this->fftn_p->host_id, // Orig Net (0xFFFF when OrigPoint != 0)
|
||||||
$this->fftn_p->point_id ? 0xffff : $this->fftn_p->host_id, // Orig Net (0xFFFF when OrigPoint != 0)
|
$this->tftn_p->host_id, // Dest Net
|
||||||
$this->tftn_p->host_id, // Dest Net
|
(Setup::PRODUCT_ID & 0xff), // Product Code Lo
|
||||||
(Setup::PRODUCT_ID & 0xff), // Product Code Lo
|
Setup::PRODUCT_VERSION_MAJ, // Product Version Major
|
||||||
Setup::PRODUCT_VERSION_MAJ, // Product Version Major
|
$this->pass_p ?: $this->tftn_p->pass_packet, // Packet Password
|
||||||
$this->pass_p ?: $this->tftn_p->session('pktpass'), // Packet Password
|
$this->fftn_p->zone->zone_id, // Orig Zone
|
||||||
$this->fftn_p->zone->zone_id, // Orig Zone
|
$this->tftn_p->zone->zone_id, // Dest Zone
|
||||||
$this->tftn_p->zone->zone_id, // Dest Zone
|
$this->fftn_p->point_id ? $this->fftn_p->host_id : 0x00, // Aux Net
|
||||||
$this->fftn_p->point_id ? $this->fftn_p->host_id : 0x00, // Aux Net
|
static::VERS, // fsc-0039.004 (copy of 0x2c)
|
||||||
static::VERS, // fsc-0039.004 (copy of 0x2c)
|
((Setup::PRODUCT_ID >> 8) & 0xff), // Product Code Hi
|
||||||
((Setup::PRODUCT_ID >> 8) & 0xff), // Product Code Hi
|
Setup::PRODUCT_VERSION_MIN, // Product Version Minor
|
||||||
Setup::PRODUCT_VERSION_MIN, // Product Version Minor
|
static::VERS, // Capability Word
|
||||||
static::VERS, // Capability Word
|
$this->fftn_p->zone->zone_id, // Orig Zone
|
||||||
$this->fftn_p->zone->zone_id, // Orig Zone
|
$this->tftn_p->zone->zone_id, // Dest Zone
|
||||||
$this->tftn_p->zone->zone_id, // Dest Zone
|
$this->fftn_p->point_id, // Orig Point
|
||||||
$this->fftn_p->point_id, // Orig Point
|
$this->tftn_p->point_id, // Dest Point
|
||||||
$this->tftn_p->point_id, // Dest Point
|
strtoupper(hexstr(Setup::PRODUCT_ID)), // ProdData
|
||||||
strtoupper(hexstr(Setup::PRODUCT_ID)), // ProdData
|
);
|
||||||
);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Classes\FTN\Packet;
|
namespace App\Classes\FTN\Packet;
|
||||||
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
use App\Classes\FTN\Packet;
|
use App\Classes\FTN\Packet;
|
||||||
use App\Models\Setup;
|
use App\Models\Setup;
|
||||||
@@ -55,35 +56,30 @@ final class FTS1 extends Packet
|
|||||||
/**
|
/**
|
||||||
* Create our message packet header
|
* Create our message packet header
|
||||||
*/
|
*/
|
||||||
protected function header(): string
|
protected function header(Collection $msgs): string
|
||||||
{
|
{
|
||||||
$oldest = $this->messages->sortBy('datetime')->last();
|
$oldest = $this->messages->sortBy('datetime')->last();
|
||||||
|
|
||||||
try {
|
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
||||||
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
$this->fftn_p->node_id, // Orig Node
|
||||||
$this->fftn_p->node_id, // Orig Node
|
$this->tftn_p->node_id, // Dest Node
|
||||||
$this->tftn_p->node_id, // Dest Node
|
$oldest->datetime->format('Y'), // Year
|
||||||
$oldest->datetime->format('Y'), // Year
|
$oldest->datetime->format('m')-1, // Month
|
||||||
$oldest->datetime->format('m')-1, // Month
|
$oldest->datetime->format('d'), // Day
|
||||||
$oldest->datetime->format('d'), // Day
|
$oldest->datetime->format('H'), // Hour
|
||||||
$oldest->datetime->format('H'), // Hour
|
$oldest->datetime->format('i'), // Minute
|
||||||
$oldest->datetime->format('i'), // Minute
|
$oldest->datetime->format('s'), // Second
|
||||||
$oldest->datetime->format('s'), // Second
|
0, // Baud
|
||||||
0, // Baud
|
2, // Packet Version (should be 2)
|
||||||
2, // Packet Version (should be 2)
|
$this->fftn_p->host_id, // Orig Net
|
||||||
$this->fftn_p->host_id, // Orig Net
|
$this->tftn_p->host_id, // Dest Net
|
||||||
$this->tftn_p->host_id, // Dest Net
|
(Setup::PRODUCT_ID & 0xff), // Product Code Lo
|
||||||
(Setup::PRODUCT_ID & 0xff), // Product Code Lo
|
Setup::PRODUCT_VERSION_MAJ, // Product Version Major
|
||||||
Setup::PRODUCT_VERSION_MAJ, // Product Version Major
|
$this->pass_p ?: $this->tftn_p->pass_packet, // Packet Password
|
||||||
$this->pass_p ?: $this->tftn_p->session('pktpass'), // Packet Password
|
$this->fftn_p->zone->zone_id, // Orig Zone
|
||||||
$this->fftn_p->zone->zone_id, // Orig Zone
|
$this->tftn_p->zone->zone_id, // Dest Zone
|
||||||
$this->tftn_p->zone->zone_id, // Dest Zone
|
'', // Reserved
|
||||||
'', // Reserved
|
);
|
||||||
);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return $e->getMessage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -11,7 +11,7 @@ abstract class Process
|
|||||||
{
|
{
|
||||||
public static function canProcess(Echoarea $eao): bool
|
public static function canProcess(Echoarea $eao): bool
|
||||||
{
|
{
|
||||||
return $eao->automsgs ? TRUE : FALSE;
|
return (bool)$eao->automsgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Classes\FTN\Process\Netmail;
|
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Notification;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
use App\Classes\FTN\Process;
|
|
||||||
use App\Models\{Echomail,Netmail};
|
|
||||||
use App\Notifications\Netmails\Areafix as AreafixNotification;
|
|
||||||
use App\Notifications\Netmails\Areafix\NotConfiguredHere as AreafixNotConfiguredHereNotification;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process messages to Ping
|
|
||||||
*
|
|
||||||
* @package App\Classes\FTN\Process
|
|
||||||
*/
|
|
||||||
final class Areafix extends Process
|
|
||||||
{
|
|
||||||
private const LOGKEY = 'RP-';
|
|
||||||
|
|
||||||
public static function handle(Echomail|Netmail $mo): bool
|
|
||||||
{
|
|
||||||
if (strtolower($mo->to) !== 'areafix')
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
Log::info(sprintf('%s:- Processing AREAFIX message from (%s) [%s]',self::LOGKEY,$mo->from,$mo->fftn));
|
|
||||||
|
|
||||||
// If this is not a node we manage, then respond with a sorry can help you
|
|
||||||
if ($mo->fftn->system->sessions->count())
|
|
||||||
Notification::route('netmail',$mo->fftn)->notify(new AreafixNotification($mo));
|
|
||||||
else
|
|
||||||
Notification::route('netmail',$mo->fftn)->notify(new AreafixNotConfiguredHereNotification($mo));
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
50
app/Classes/FTN/Process/Netmail/Robot.php
Normal file
50
app/Classes/FTN/Process/Netmail/Robot.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process;
|
||||||
|
use App\Models\{Echomail,Netmail};
|
||||||
|
use App\Notifications\Netmails\Areafix\{InvalidPassword,NotConfiguredHere};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process messages to Ping
|
||||||
|
*
|
||||||
|
* @package App\Classes\FTN\Process
|
||||||
|
*/
|
||||||
|
abstract class Robot extends Process
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'RPR';
|
||||||
|
|
||||||
|
public static function handle(Echomail|Netmail $mo): bool
|
||||||
|
{
|
||||||
|
if (((strtolower($mo->to) !== 'areafix') && (strtolower($mo->to) !== 'filefix')) || (! ($mo instanceof Netmail)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:- Processing *FIX [%s] message from (%s) [%s]',self::LOGKEY,$mo->to,$mo->from,$mo->fftn->ftn));
|
||||||
|
|
||||||
|
// If this is not a node we manage, then respond with a sorry can help you
|
||||||
|
if (! $mo->fftn->system->sessions->count()) {
|
||||||
|
Notification::route('netmail',$mo->fftn)->notify(new NotConfiguredHere($mo));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this nodes password is not correct
|
||||||
|
if ($mo->fftn->pass_fix !== strtoupper($mo->subject)) {
|
||||||
|
Notification::route('netmail',$mo->fftn)->notify(new InvalidPassword($mo));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((strtolower($mo->to) === 'areafix'))
|
||||||
|
return static::areafix($mo);
|
||||||
|
|
||||||
|
if ((strtolower($mo->to) === 'filefix'))
|
||||||
|
return static::filefix($mo);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
104
app/Classes/FTN/Process/Netmail/Robot/Areafix.php
Normal file
104
app/Classes/FTN/Process/Netmail/Robot/Areafix.php
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot;
|
||||||
|
use App\Models\{Echomail,Netmail};
|
||||||
|
use App\Notifications\Netmails\Areafix\CommandsProcessed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process messages to Ping
|
||||||
|
*
|
||||||
|
* @package App\Classes\FTN\Process
|
||||||
|
*/
|
||||||
|
final class Areafix extends Robot
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'RPA';
|
||||||
|
|
||||||
|
public const commands = 'App\\Classes\\FTN\\Process\\Netmail\\Robot\\Areafix\\';
|
||||||
|
|
||||||
|
public static function handle(Echomail|Netmail $mo): bool
|
||||||
|
{
|
||||||
|
if ((strtolower($mo->to) !== 'areafix') || (! ($mo instanceof Netmail)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:- Processing AREAFIX [%s] message from (%s) [%s]',self::LOGKEY,$mo->to,$mo->from,$mo->fftn->ftn));
|
||||||
|
|
||||||
|
return parent::handle($mo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function areafix(Netmail $mo): bool
|
||||||
|
{
|
||||||
|
$result = collect();
|
||||||
|
$result->push('--> BEGIN <--');
|
||||||
|
|
||||||
|
foreach ($mo->body_lines as $command) {
|
||||||
|
Log::debug(sprintf('%s:* Processing command [%s]',self::LOGKEY,$command));
|
||||||
|
|
||||||
|
// Skip empty lines
|
||||||
|
if (! $command || preg_match('/^\s+$/',$command))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$command = explode(' ',strtoupper(rtrim($command)));
|
||||||
|
Log::debug(sprintf('%s:* Processing command',self::LOGKEY),['command'=>$command]);
|
||||||
|
|
||||||
|
// If command starts with '...' or '---', its a tear/tag line, and we have reached the end
|
||||||
|
if (str_starts_with($command[0],'...') || str_starts_with($command[0],'---')) {
|
||||||
|
Log::info(sprintf('%s:= We got a tearline/tagline, end of processing',self::LOGKEY));
|
||||||
|
|
||||||
|
$result->push('--> END OF PROCESSING - TEARLINE/TAGLINE <--');
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Lines starting with a space, we'll abort
|
||||||
|
} elseif (! $command[0]) {
|
||||||
|
Log::info(sprintf('%s:= Got a new line with a space, end of processing',self::LOGKEY));
|
||||||
|
|
||||||
|
$result->push('--> END OF PROCESSING - SPACE DETECTED <--');
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// If command doesnt start with %, its an area
|
||||||
|
} elseif (! str_starts_with($command[0],'%')) {
|
||||||
|
Log::info(sprintf('%s:= Assuming command [%s] is an AREA command',self::LOGKEY,$command[0]));
|
||||||
|
|
||||||
|
array_unshift($command,'%AREA');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some commands are reserved words
|
||||||
|
switch ($x=strtolower(substr($command[0],1))) {
|
||||||
|
case 'list':
|
||||||
|
$class = self::commands.'AreaList';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Parse the message body and pluck out the commands on each line
|
||||||
|
$class = self::commands.ucfirst($x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! class_exists($class)) {
|
||||||
|
$result->push(sprintf('%-25s <-- **COMMAND UNKNOWN**',join(' ',$command)));
|
||||||
|
Log::info(sprintf('%s:! Command UNKNOWN [%s] ',self::LOGKEY,join('|',$command)),['class'=>$class]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop the command from the array, the rest are arguments
|
||||||
|
array_shift($command);
|
||||||
|
|
||||||
|
// Refresh our echoareas
|
||||||
|
$mo->fftn->load('echoareas');
|
||||||
|
|
||||||
|
$o = new $class($mo,$command);
|
||||||
|
$result->push($o->process());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply with a confirmation of what commands were processed
|
||||||
|
Notification::route('netmail',$mo->fftn)->notify(new CommandsProcessed($mo,$result));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
147
app/Classes/FTN/Process/Netmail/Robot/Areafix/Area.php
Normal file
147
app/Classes/FTN/Process/Netmail/Robot/Areafix/Area.php
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Jobs\AreafixRescan;
|
||||||
|
|
||||||
|
// Echoarea Processing Command
|
||||||
|
class Area extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AFA';
|
||||||
|
|
||||||
|
private const command = '%AREA';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command.' [-|+]<ECHOAREA> [R|D=<DAYS>]',
|
||||||
|
' Use the area command to subscribe (+) or unsubscribe (-) to an ECHOAREA',
|
||||||
|
' Arguments:',
|
||||||
|
' - ECHOAREA (required) name of area to subscribe or unsubscribe',
|
||||||
|
' - D=DAYS (optional) number of days to resend mail from this area that you',
|
||||||
|
' havent already received (useful if you are resubscribing to an area and',
|
||||||
|
' have received mail in the past)',
|
||||||
|
' - R=DAYS (optional) number of days to resend mail from this area (even if',
|
||||||
|
' it was sent to you previously)',
|
||||||
|
' Notes:',
|
||||||
|
' * "+" is optional, and is implied if "-" is not used',
|
||||||
|
' * "R" and "D" options only apply to subscribing',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
$command = self::command.' '.join(' ',$this->arguments);
|
||||||
|
|
||||||
|
if (! ($area=Arr::get($this->arguments,0,NULL)))
|
||||||
|
return sprintf('%-25s <-- INVALID, AN AREA IS REQUIRED',$command);
|
||||||
|
|
||||||
|
// If command starts with '-', its to unsubscribe
|
||||||
|
if (str_starts_with($area,'-')) {
|
||||||
|
$sub = FALSE;
|
||||||
|
$area = substr($area,1);
|
||||||
|
|
||||||
|
} elseif (str_starts_with($area,'+')) {
|
||||||
|
$sub = TRUE;
|
||||||
|
$area = substr($area,1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$sub = TRUE;
|
||||||
|
$area = $area;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- Processing [%s] for [%s]',self::LOGKEY,$sub ? 'ADD' : 'REMOVE',$area));
|
||||||
|
|
||||||
|
// Drop the area from the arguments, the rest are options
|
||||||
|
array_shift($this->arguments);
|
||||||
|
|
||||||
|
// Area exists
|
||||||
|
if ($ea=$this->mo->fftn->domain->echoareas->where('name',$area)->pop()) {
|
||||||
|
// If already subscribed
|
||||||
|
if ($nea=$this->mo->fftn->echoareas->where('name',$area)->pop()) {
|
||||||
|
// requesting to subscribe "You already are since..., arguments ignored
|
||||||
|
if ($sub) {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] ALREADY subscribed to [%s] since [%s]',self::LOGKEY,$this->mo->fftn->ftn,$area,$nea->pivot->subscribed));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- ALREADY subscribed since %s',$command,$nea->pivot->subscribed);
|
||||||
|
|
||||||
|
// requesting to unsubscribe
|
||||||
|
} else {
|
||||||
|
$this->mo->fftn->echoareas()->detach($ea->id);
|
||||||
|
|
||||||
|
// Remove sub, clear queue
|
||||||
|
$x = DB::table('echomail_seenby')
|
||||||
|
->where('address_id',$this->mo->fftn->id)
|
||||||
|
->join('echomails',['echomails.id'=>'echomail_seenby.echomail_id'])
|
||||||
|
->where('echoarea_id',$nea->id)
|
||||||
|
->whereNotNull('export_at')
|
||||||
|
->whereNull('sent_at')
|
||||||
|
->orderBy('echomails.datetime')
|
||||||
|
->skip($this->mo->fftn->system->pkt_msgs) // Might already being sent in this session
|
||||||
|
->delete();
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] UNSUBSCRIBED from [%s] clearing [%s]',self::LOGKEY,$this->mo->fftn->ftn,$area,$x));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- UNSUBSCRIBED, CLEARED [%d] MSGS from queue',$command,$x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not subscribed
|
||||||
|
} else {
|
||||||
|
// requesting to subscribe, subsubsribe and rescan if arguments
|
||||||
|
if ($sub) {
|
||||||
|
$this->mo->fftn->echoareas()->attach([$ea->id=>['subscribed'=>Carbon::now()]]);
|
||||||
|
|
||||||
|
// If we have arguments, they are to rescan
|
||||||
|
if (count($this->arguments) === 1) {
|
||||||
|
$m = [];
|
||||||
|
if (preg_match('/^([DR])=([0-9]+)$/',$this->arguments[0],$m)) {
|
||||||
|
switch ($m[1]) {
|
||||||
|
// Scan
|
||||||
|
case 'D':
|
||||||
|
AreafixRescan::dispatch($this->mo->fftn,$ea,$m[2])
|
||||||
|
->onQueue('mail');
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED, RESCAN [%d] DAYS queued',$command,$m[2]);
|
||||||
|
|
||||||
|
// Scan
|
||||||
|
case 'R':
|
||||||
|
AreafixRescan::dispatch($this->mo->fftn,$ea,$m[2],TRUE)
|
||||||
|
->onQueue('mail');
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED, FORCE RESCAN [%d] DAYS queued',$command,$m[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED, INVALID OPTIONS',$command);
|
||||||
|
|
||||||
|
} elseif (count($this->arguments) > 1) {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] subscribed to [%s], extra commands [%s] ignored',self::LOGKEY,$this->mo->fftn->ftn,$area,join('|',$this->arguments)));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED, OPTIONS IGNORED',$command);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] subscribed to [%s]',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED',$command);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not subscribed, "you arent subscribed, arguments ignored"
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] is NOT subscribed to [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- NOT subscribed, NO ACTION taken',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] area UNKNOWN [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- AREA UNKNOWN, NO ACTION TAKEN',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
app/Classes/FTN/Process/Netmail/Robot/Areafix/AreaList.php
Normal file
38
app/Classes/FTN/Process/Netmail/Robot/Areafix/AreaList.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
|
use App\Notifications\Netmails\Areafix\AreaList as AreaListNotification;
|
||||||
|
|
||||||
|
// LIST - List echoareas in a domain
|
||||||
|
class AreaList extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AFS';
|
||||||
|
private const command = '%LIST';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command,
|
||||||
|
' List the available echoareas in this network',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s:- Areafix [%s] for [%s] for [%s]',self::LOGKEY,self::command,$this->mo->fftn->ftn,join('|',$this->arguments)));
|
||||||
|
|
||||||
|
if (count($this->arguments) > 1)
|
||||||
|
return sprintf('%-25s <-- INVALID COMMAND',self::command);
|
||||||
|
|
||||||
|
else {
|
||||||
|
Notification::route('netmail',$this->mo->fftn)
|
||||||
|
->notify(new AreaListNotification($this->mo));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- COMMAND PROCESSED',self::command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
app/Classes/FTN/Process/Netmail/Robot/Areafix/Base.php
Normal file
26
app/Classes/FTN/Process/Netmail/Robot/Areafix/Base.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Models\Netmail;
|
||||||
|
|
||||||
|
// Our base areafix commands class
|
||||||
|
abstract class Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AB-';
|
||||||
|
|
||||||
|
protected Netmail $mo;
|
||||||
|
protected array $arguments;
|
||||||
|
|
||||||
|
public function __construct(Netmail $mo,array $arguments) {
|
||||||
|
Log::debug(sprintf('%s:- Areafix [%s] command with arguments [%s] for [%s]',self::LOGKEY,get_class($this),implode('|',$arguments),$mo->fftn->ftn));
|
||||||
|
|
||||||
|
$this->mo = $mo;
|
||||||
|
$this->arguments = $arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public static function help(): array;
|
||||||
|
abstract public function process(): string;
|
||||||
|
}
|
48
app/Classes/FTN/Process/Netmail/Robot/Areafix/Help.php
Normal file
48
app/Classes/FTN/Process/Netmail/Robot/Areafix/Help.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||||
|
use App\Notifications\Netmails\Areafix\Help as HelpNotification;
|
||||||
|
|
||||||
|
// A Help Index Command
|
||||||
|
class Help extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AFH';
|
||||||
|
private const areafix_classes = 'app/Classes/FTN/Process/Netmail/Robot/Areafix';
|
||||||
|
private const command = '%HELP';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command,
|
||||||
|
' This message!',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s:- Processing [%s] for [%s]',self::LOGKEY,self::command,$this->mo->fftn->ftn));
|
||||||
|
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
foreach (preg_grep('/^([^.])/',scandir(self::areafix_classes)) as $file) {
|
||||||
|
if (($file === 'Base.php') || (! str_ends_with(strtolower($file),'.php')))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$class = Areafix::commands.preg_replace('/\.php$/','',$file);
|
||||||
|
if ($result->count())
|
||||||
|
$result->push('');
|
||||||
|
|
||||||
|
$result = $result
|
||||||
|
->merge($class::help());
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification::route('netmail',$this->mo->fftn)->notify(new HelpNotification($this->mo,$result));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- COMMAND PROCESSED',self::command);
|
||||||
|
}
|
||||||
|
}
|
65
app/Classes/FTN/Process/Netmail/Robot/Areafix/Rescan.php
Normal file
65
app/Classes/FTN/Process/Netmail/Robot/Areafix/Rescan.php
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Jobs\AreafixRescan;
|
||||||
|
|
||||||
|
// RESCAN - Resend echomail
|
||||||
|
class Rescan extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AFR';
|
||||||
|
private const command = '%RESCAN';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command.' <ECHOAREA> [<DAYS>]',
|
||||||
|
' Use the rescan command to resend mail from an echoarea.',
|
||||||
|
' This is will resend mail again, even if you have received it in the past.',
|
||||||
|
' Arguments:',
|
||||||
|
' - ECHOAREA (required) name of area to subscribe or unsubscribe',
|
||||||
|
' - DAYS (optional) number of days to resend mail from this area that you',
|
||||||
|
' If DAYS is omitted, the default is 30.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s:- Areafix [%s] for [%s] for [%s]',self::LOGKEY,self::command,$this->mo->fftn->ftn,join('|',$this->arguments)));
|
||||||
|
|
||||||
|
$command = self::command.' '.join(' ',$this->arguments);
|
||||||
|
|
||||||
|
if (! ($area=Arr::get($this->arguments,0)))
|
||||||
|
return sprintf('%-25s <-- INVALID, AN AREA IS REQUIRED',$command);
|
||||||
|
|
||||||
|
if (! is_numeric($days=Arr::get($this->arguments,1,30)))
|
||||||
|
return sprintf('%-25s <-- INVALID, DAYS [%s] NOT NUMERIC',$command,$this->arguments[1]);
|
||||||
|
|
||||||
|
// Area exists
|
||||||
|
if ($ea=$this->mo->fftn->domain->echoareas->where('name',$area)->pop()) {
|
||||||
|
// If already subscribed
|
||||||
|
if ($this->mo->fftn->echoareas->pluck('name')->contains($area)) {
|
||||||
|
AreafixRescan::dispatch($this->mo->fftn,$ea,$days,TRUE)
|
||||||
|
->onQueue('mail');
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] RESCAN [%s] DAYS [%d]',self::LOGKEY,$this->mo->fftn->ftn,$area,$days));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- RESCAN [%d] DAYS queued',$command,$days);
|
||||||
|
|
||||||
|
// If not subscribed
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] is NOT subscribed to [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- NOT subscribed, NO ACTION taken',$command);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] area UNKNOWN [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- AREA UNKNOWN, NO ACTION TAKEN',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
app/Classes/FTN/Process/Netmail/Robot/Areafix/Scan.php
Normal file
66
app/Classes/FTN/Process/Netmail/Robot/Areafix/Scan.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Jobs\AreafixRescan;
|
||||||
|
|
||||||
|
// SCAN - Send unsent echomail
|
||||||
|
class Scan extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AFS';
|
||||||
|
private const command = '%SCAN';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command.' <ECHOAREA> [<DAYS>]',
|
||||||
|
' Use the scan command to resend mail that you havent received yet from an',
|
||||||
|
' echoarea. This is useful if you are rejoining an echoarea, and only want',
|
||||||
|
' to get mail that you dont already have.',
|
||||||
|
' Arguments:',
|
||||||
|
' - ECHOAREA (required) name of area to subscribe or unsubscribe',
|
||||||
|
' - DAYS (optional) number of days to resend mail from this area that you',
|
||||||
|
' If DAYS is omitted, the default is 30.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s:- Areafix [%s] for [%s] for [%s]',self::LOGKEY,self::command,$this->mo->fftn->ftn,join('|',$this->arguments)));
|
||||||
|
|
||||||
|
$command = self::command.' '.join(' ',$this->arguments);
|
||||||
|
|
||||||
|
if (! ($area=Arr::get($this->arguments,0)))
|
||||||
|
return sprintf('%-25s <-- INVALID, AN AREA IS REQUIRED',$command);
|
||||||
|
|
||||||
|
if (! is_numeric($days=Arr::get($this->arguments,1,30)))
|
||||||
|
return sprintf('%-25s <-- INVALID, DAYS [%s] NOT NUMERIC',$command,$this->arguments[1]);
|
||||||
|
|
||||||
|
// Area exists
|
||||||
|
if ($ea=$this->mo->fftn->domain->echoareas->where('name',$area)->pop()) {
|
||||||
|
// If already subscribed
|
||||||
|
if ($this->mo->fftn->echoareas->pluck('name')->contains($area)) {
|
||||||
|
AreafixRescan::dispatch($this->mo->fftn,$ea,$days)
|
||||||
|
->onQueue('mail');
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] SCAN [%s] DAYS [%d]',self::LOGKEY,$this->mo->fftn->ftn,$area,$days));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SCAN [%d] DAYS queued',$command,$days);
|
||||||
|
|
||||||
|
// If not subscribed
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] is NOT subscribed to [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- NOT subscribed, NO ACTION taken',$command);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] area UNKNOWN [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- AREA UNKNOWN, NO ACTION TAKEN',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
app/Classes/FTN/Process/Netmail/Robot/Filefix.php
Normal file
104
app/Classes/FTN/Process/Netmail/Robot/Filefix.php
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot;
|
||||||
|
use App\Models\{Echomail,Netmail};
|
||||||
|
use App\Notifications\Netmails\Filefix\CommandsProcessed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process messages to Ping
|
||||||
|
*
|
||||||
|
* @package App\Classes\FTN\Process
|
||||||
|
*/
|
||||||
|
final class Filefix extends Robot
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'RPF';
|
||||||
|
|
||||||
|
public const commands = 'App\\Classes\\FTN\\Process\\Netmail\\Robot\\Filefix\\';
|
||||||
|
|
||||||
|
public static function handle(Echomail|Netmail $mo): bool
|
||||||
|
{
|
||||||
|
if ((strtolower($mo->to) !== 'filefix') || (! ($mo instanceof Netmail)))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:- Processing FILEFIX [%s] message from (%s) [%s]',self::LOGKEY,$mo->to,$mo->from,$mo->fftn->ftn));
|
||||||
|
|
||||||
|
return parent::handle($mo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function filefix(Netmail $mo): bool
|
||||||
|
{
|
||||||
|
$result = collect();
|
||||||
|
$result->push('--> BEGIN <--');
|
||||||
|
|
||||||
|
foreach ($mo->body_lines as $command) {
|
||||||
|
Log::debug(sprintf('%s:* Processing command [%s]',self::LOGKEY,$command));
|
||||||
|
|
||||||
|
// Skip empty lines
|
||||||
|
if (! $command || preg_match('/^\s+$/',$command))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$command = explode(' ',strtoupper(rtrim($command)));
|
||||||
|
Log::debug(sprintf('%s:* Processing command',self::LOGKEY),['command'=>$command]);
|
||||||
|
|
||||||
|
// If command starts with '...' or '---', its a tear/tag line, and we have reached the end
|
||||||
|
if (str_starts_with($command[0],'...') || str_starts_with($command[0],'---')) {
|
||||||
|
Log::info(sprintf('%s:= We got a tearline/tagline, end of processing',self::LOGKEY));
|
||||||
|
|
||||||
|
$result->push('--> END OF PROCESSING - TEARLINE/TAGLINE <--');
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Lines starting with a space, we'll abort
|
||||||
|
} elseif (! $command[0]) {
|
||||||
|
Log::info(sprintf('%s:= Got a new line with a space, end of processing',self::LOGKEY));
|
||||||
|
|
||||||
|
$result->push('--> END OF PROCESSING - SPACE DETECTED <--');
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// If command doesnt start with %, its an area
|
||||||
|
} elseif (! str_starts_with($command[0],'%')) {
|
||||||
|
Log::info(sprintf('%s:= Assuming command [%s] is an AREA command',self::LOGKEY,$command[0]));
|
||||||
|
|
||||||
|
array_unshift($command,'%AREA');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some commands are reserved words
|
||||||
|
switch ($x=strtolower(substr($command[0],1))) {
|
||||||
|
case 'list':
|
||||||
|
$class = self::commands.'AreaList';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Parse the message body and pluck out the commands on each line
|
||||||
|
$class = self::commands.ucfirst($x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! class_exists($class)) {
|
||||||
|
$result->push(sprintf('%-25s <-- **COMMAND UNKNOWN**',join(' ',$command)));
|
||||||
|
Log::info(sprintf('%s:! Command UNKNOWN [%s] ',self::LOGKEY,join('|',$command)),['class'=>$class]);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop the command from the array, the rest are arguments
|
||||||
|
array_shift($command);
|
||||||
|
|
||||||
|
// Refresh our echoareas
|
||||||
|
$mo->fftn->load('fileareas');
|
||||||
|
|
||||||
|
$o = new $class($mo,$command);
|
||||||
|
$result->push($o->process());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply with a confirmation of what commands were processed
|
||||||
|
Notification::route('netmail',$mo->fftn)->notify(new CommandsProcessed($mo,$result));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
148
app/Classes/FTN/Process/Netmail/Robot/Filefix/Area.php
Normal file
148
app/Classes/FTN/Process/Netmail/Robot/Filefix/Area.php
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Filefix;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot\Areafix\Base;
|
||||||
|
use App\Jobs\FilefixRescan;
|
||||||
|
|
||||||
|
// Filearea Processing Command
|
||||||
|
class Area extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'FFA';
|
||||||
|
|
||||||
|
private const command = '%AREA';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command.' [-|+]<FILEAREA> [R|D=<DAYS>]',
|
||||||
|
' Use the area command to subscribe (+) or unsubscribe (-) to a FILEAREA',
|
||||||
|
' Arguments:',
|
||||||
|
' - FILEAREA (required) name of area to subscribe or unsubscribe',
|
||||||
|
' - D=DAYS (optional) number of days to resend files from this area that you',
|
||||||
|
' havent already received (useful if you are resubscribing to an area and',
|
||||||
|
' have received files in the past)',
|
||||||
|
' - R=DAYS (optional) number of days to resend files from this area (even if',
|
||||||
|
' it was sent to you previously)',
|
||||||
|
' Notes:',
|
||||||
|
' * "+" is optional, and is implied if "-" is not used',
|
||||||
|
' * "R" and "D" options only apply to subscribing',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
$command = self::command.' '.join(' ',$this->arguments);
|
||||||
|
|
||||||
|
if (! ($area=Arr::get($this->arguments,0,NULL)))
|
||||||
|
return sprintf('%-25s <-- INVALID, AN AREA IS REQUIRED',$command);
|
||||||
|
|
||||||
|
// If command starts with '-', its to unsubscribe
|
||||||
|
if (str_starts_with($area,'-')) {
|
||||||
|
$sub = FALSE;
|
||||||
|
$area = substr($area,1);
|
||||||
|
|
||||||
|
} elseif (str_starts_with($area,'+')) {
|
||||||
|
$sub = TRUE;
|
||||||
|
$area = substr($area,1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$sub = TRUE;
|
||||||
|
$area = $area;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- Processing [%s] for [%s]',self::LOGKEY,$sub ? 'ADD' : 'REMOVE',$area));
|
||||||
|
|
||||||
|
// Drop the area from the arguments, the rest are options
|
||||||
|
array_shift($this->arguments);
|
||||||
|
|
||||||
|
// Area exists
|
||||||
|
if ($fa=$this->mo->fftn->domain->fileareas->where('name',$area)->pop()) {
|
||||||
|
// If already subscribed
|
||||||
|
if ($nea=$this->mo->fftn->fileareas->where('name',$area)->pop()) {
|
||||||
|
// requesting to subscribe "You already are since..., arguments ignored
|
||||||
|
if ($sub) {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] ALREADY subscribed to [%s] since [%s]',self::LOGKEY,$this->mo->fftn->ftn,$area,$nea->pivot->subscribed));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- ALREADY subscribed since %s',$command,$nea->pivot->subscribed);
|
||||||
|
|
||||||
|
// requesting to unsubscribe
|
||||||
|
} else {
|
||||||
|
$this->mo->fftn->fileareas()->detach($fa->id);
|
||||||
|
|
||||||
|
// Remove sub, clear queue
|
||||||
|
$x = DB::table('file_seenby')
|
||||||
|
->where('address_id',$this->mo->fftn->id)
|
||||||
|
->join('files',['files.id'=>'file_seenby.file_id'])
|
||||||
|
->where('filearea_id',$nea->id)
|
||||||
|
->whereNotNull('export_at')
|
||||||
|
->whereNull('sent_at')
|
||||||
|
->orderBy('files.datetime')
|
||||||
|
->skip(1) // Might already being sent in this session
|
||||||
|
->delete();
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] UNSUBSCRIBED from [%s] clearing [%s]',self::LOGKEY,$this->mo->fftn->ftn,$area,$x));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- UNSUBSCRIBED, CLEARED [%d] FILES from queue',$command,$x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not subscribed
|
||||||
|
} else {
|
||||||
|
// requesting to subscribe, subsubsribe and rescan if arguments
|
||||||
|
if ($sub) {
|
||||||
|
$this->mo->fftn->fileareas()->attach([$fa->id=>['subscribed'=>Carbon::now()]]);
|
||||||
|
|
||||||
|
// If we have arguments, they are to rescan
|
||||||
|
if (count($this->arguments) === 1) {
|
||||||
|
$m = [];
|
||||||
|
if (preg_match('/^([DR])=([0-9]+)$/',$this->arguments[0],$m)) {
|
||||||
|
switch ($m[1]) {
|
||||||
|
// Scan
|
||||||
|
case 'D':
|
||||||
|
FilefixRescan::dispatch($this->mo->fftn,$fa,$m[2])
|
||||||
|
->onQueue('mail');
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED, RESCAN [%d] DAYS queued',$command,$m[2]);
|
||||||
|
|
||||||
|
// Scan
|
||||||
|
case 'R':
|
||||||
|
FilefixRescan::dispatch($this->mo->fftn,$fa,$m[2],TRUE)
|
||||||
|
->onQueue('mail');
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED, FORCE RESCAN [%d] DAYS queued',$command,$m[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED, INVALID OPTIONS',$command);
|
||||||
|
|
||||||
|
} elseif (count($this->arguments) > 1) {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] subscribed to [%s], extra commands [%s] ignored',self::LOGKEY,$this->mo->fftn->ftn,$area,join('|',$this->arguments)));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED, OPTIONS IGNORED',$command);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] subscribed to [%s]',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SUBSCRIBED',$command);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not subscribed, "you arent subscribed, arguments ignored"
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] is NOT subscribed to [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- NOT subscribed, NO ACTION taken',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FILE [%s] area UNKNOWN [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- AREA UNKNOWN, NO ACTION TAKEN',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
app/Classes/FTN/Process/Netmail/Robot/Filefix/AreaList.php
Normal file
39
app/Classes/FTN/Process/Netmail/Robot/Filefix/AreaList.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Filefix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot\Areafix\Base;
|
||||||
|
use App\Notifications\Netmails\Filefix\AreaList as AreaListNotification;
|
||||||
|
|
||||||
|
// LIST - List fileareas in a domain
|
||||||
|
class AreaList extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AFS';
|
||||||
|
private const command = '%LIST';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command,
|
||||||
|
' List the available fileareas in this network',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s:- Filefix [%s] for [%s] for [%s]',self::LOGKEY,self::command,$this->mo->fftn->ftn,join('|',$this->arguments)));
|
||||||
|
|
||||||
|
if (count($this->arguments) > 1)
|
||||||
|
return sprintf('%-25s <-- INVALID COMMAND',self::command);
|
||||||
|
|
||||||
|
else {
|
||||||
|
Notification::route('netmail',$this->mo->fftn)
|
||||||
|
->notify(new AreaListNotification($this->mo));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- COMMAND PROCESSED',self::command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
app/Classes/FTN/Process/Netmail/Robot/Filefix/Filelist.php
Normal file
71
app/Classes/FTN/Process/Netmail/Robot/Filefix/Filelist.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Filefix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot\Areafix\Base;
|
||||||
|
use App\Notifications\Netmails\Filefix\Filelist as FilelistNotification;
|
||||||
|
|
||||||
|
// FILELIST - List files in an area
|
||||||
|
class Filelist extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AFR';
|
||||||
|
private const command = '%FILELIST';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command.' [-|+]<FILEAREA> [<DAYS>]',
|
||||||
|
' Use the filelist command to list files from an filearea.',
|
||||||
|
' This is will resend files again, even if you have received them in the',
|
||||||
|
' past.',
|
||||||
|
' Arguments:',
|
||||||
|
' - FILEAREA (required) name of area',
|
||||||
|
' - DAYS (optional) number of days to resend mail from this area that you',
|
||||||
|
' If DAYS is omitted, the default is 30. The maximum is 365.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s:- Filefix [%s] for [%s] for [%s]',self::LOGKEY,self::command,$this->mo->fftn->ftn,join('|',$this->arguments)));
|
||||||
|
|
||||||
|
$command = self::command.' '.join(' ',$this->arguments);
|
||||||
|
|
||||||
|
if (! is_numeric($days=Arr::get($this->arguments,1,30)))
|
||||||
|
return sprintf('%-25s <-- INVALID, DAYS [%s] NOT NUMERIC',$command,$this->arguments[1]);
|
||||||
|
|
||||||
|
if ($days > 365)
|
||||||
|
$days = 365;
|
||||||
|
|
||||||
|
if (! ($area=Arr::get($this->arguments,0)))
|
||||||
|
return sprintf('%-25s <-- INVALID, AN AREA IS REQUIRED',$command);
|
||||||
|
|
||||||
|
// Area exists
|
||||||
|
if ($fa=$this->mo->fftn->domain->fileareas->where('name',$area)->pop()) {
|
||||||
|
// If already subscribed
|
||||||
|
if ($this->mo->fftn->fileareas->pluck('name')->contains($area)) {
|
||||||
|
Notification::route('netmail',$this->mo->fftn)
|
||||||
|
->notify(new FileListNotification($this->mo,$fa,$days));
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] FILELIST [%s] DAYS [%d]',self::LOGKEY,$this->mo->fftn->ftn,$area,$days));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- FILELIST [%d] DAYS',$command,$days);
|
||||||
|
|
||||||
|
// If not subscribed
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] is NOT subscribed to [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- NOT subscribed, NO ACTION taken',$command);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FILE [%s] area UNKNOWN [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- AREA UNKNOWN, NO ACTION TAKEN',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
app/Classes/FTN/Process/Netmail/Robot/Filefix/Help.php
Normal file
50
app/Classes/FTN/Process/Netmail/Robot/Filefix/Help.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Filefix;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot\Areafix\Base;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot\Filefix;
|
||||||
|
use App\Notifications\Netmails\Filefix\Help as HelpNotification;
|
||||||
|
|
||||||
|
// A Help Index Command
|
||||||
|
class Help extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'FFH';
|
||||||
|
|
||||||
|
private const filefix_classes = 'app/Classes/FTN/Process/Netmail/Robot/Filefix';
|
||||||
|
private const command = '%HELP';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command,
|
||||||
|
' This message!',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s:- Processing [%s] for [%s]',self::LOGKEY,self::command,$this->mo->fftn->ftn));
|
||||||
|
|
||||||
|
$result = collect();
|
||||||
|
|
||||||
|
foreach (preg_grep('/^([^.])/',scandir(self::filefix_classes)) as $file) {
|
||||||
|
if (($file === 'Base.php') || (! str_ends_with(strtolower($file),'.php')))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$class = Filefix::commands.preg_replace('/\.php$/','',$file);
|
||||||
|
if ($result->count())
|
||||||
|
$result->push('');
|
||||||
|
|
||||||
|
$result = $result
|
||||||
|
->merge($class::help());
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification::route('netmail',$this->mo->fftn)->notify(new HelpNotification($this->mo,$result));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- COMMAND PROCESSED',self::command);
|
||||||
|
}
|
||||||
|
}
|
70
app/Classes/FTN/Process/Netmail/Robot/Filefix/Rescan.php
Normal file
70
app/Classes/FTN/Process/Netmail/Robot/Filefix/Rescan.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Filefix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot\Areafix\Base;
|
||||||
|
use App\Jobs\FilefixRescan;
|
||||||
|
|
||||||
|
// RESCAN - Resend echomail
|
||||||
|
class Rescan extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AFR';
|
||||||
|
private const command = '%RESCAN';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command.' <FILEAREA> [<DAYS>]',
|
||||||
|
' Use the rescan command to resend files from a filearea.',
|
||||||
|
' This is will resend files again, even if you have received them in the',
|
||||||
|
' past.',
|
||||||
|
' Arguments:',
|
||||||
|
' - FILEAREA (required) name of area to subscribe or unsubscribe',
|
||||||
|
' - DAYS (optional) number of days to resend mail from this area that you',
|
||||||
|
' If DAYS is omitted, the default is 30. The maximum is 365.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s:- Filefix [%s] for [%s] for [%s]',self::LOGKEY,self::command,$this->mo->fftn->ftn,join('|',$this->arguments)));
|
||||||
|
|
||||||
|
$command = self::command.' '.join(' ',$this->arguments);
|
||||||
|
|
||||||
|
if (! ($area=Arr::get($this->arguments,0)))
|
||||||
|
return sprintf('%-25s <-- INVALID, AN AREA IS REQUIRED',$command);
|
||||||
|
|
||||||
|
if (! is_numeric($days=Arr::get($this->arguments,1,30)))
|
||||||
|
return sprintf('%-25s <-- INVALID, DAYS [%s] NOT NUMERIC',$command,$this->arguments[1]);
|
||||||
|
|
||||||
|
if ($days > 365)
|
||||||
|
$days = 365;
|
||||||
|
|
||||||
|
// Area exists
|
||||||
|
if ($fa=$this->mo->fftn->domain->fileareas->where('name',$area)->pop()) {
|
||||||
|
// If already subscribed
|
||||||
|
if ($this->mo->fftn->fileareas->pluck('name')->contains($area)) {
|
||||||
|
FilefixRescan::dispatch($this->mo->fftn,$fa,$days,TRUE)
|
||||||
|
->onQueue('mail');
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] RESCAN [%s] DAYS [%d]',self::LOGKEY,$this->mo->fftn->ftn,$area,$days));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- RESCAN [%d] DAYS queued',$command,$days);
|
||||||
|
|
||||||
|
// If not subscribed
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] is NOT subscribed to [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- NOT subscribed, NO ACTION taken',$command);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FILE [%s] area UNKNOWN [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- AREA UNKNOWN, NO ACTION TAKEN',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
app/Classes/FTN/Process/Netmail/Robot/Filefix/Resend.php
Normal file
79
app/Classes/FTN/Process/Netmail/Robot/Filefix/Resend.php
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Filefix;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot\Areafix\Base;
|
||||||
|
|
||||||
|
// Resend a file
|
||||||
|
class Resend extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'FFA';
|
||||||
|
|
||||||
|
private const command = '%RESEND';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command.' <FILEAREA> <FILENAME>',
|
||||||
|
' Resend a file from a file area',
|
||||||
|
' Arguments:',
|
||||||
|
' - FILEAREA (required) name of area to subscribe or unsubscribe',
|
||||||
|
' - FILENAME (required) number of file to resend',
|
||||||
|
' Notes:',
|
||||||
|
' * You can obtain a list of files in an area with %FILELIST <FILEAREA>',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
$command = self::command.' '.join(' ',$this->arguments);
|
||||||
|
|
||||||
|
if (count($this->arguments) < 2)
|
||||||
|
return sprintf('%-25s <-- INVALID, NOT ENOUGH ARGUMENTS',$command);
|
||||||
|
elseif (count($this->arguments) > 2)
|
||||||
|
return sprintf('%-25s <-- INVALID, TOO MANY ARGUMENTS',$command);
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- Resending [%s] from [%s] to [%s]',self::LOGKEY,$this->arguments[1],$this->arguments[0],$this->mo->fftn->ftn));
|
||||||
|
|
||||||
|
// Area exists
|
||||||
|
if ($fa=$this->mo->fftn->domain->fileareas->where('name',$this->arguments[0])->pop()) {
|
||||||
|
// If already subscribed
|
||||||
|
if ($nea=$this->mo->fftn->fileareas->where('name',$fa->name)->pop()) {
|
||||||
|
// Check the file is in the area
|
||||||
|
if ($fo=$nea->files()->where('name','ilike',$this->arguments[1])->single()) {
|
||||||
|
// File hasnt been exported before
|
||||||
|
if (! $fo->seenby->where('id',$this->mo->fftn_id)->count()) {
|
||||||
|
Log::info(sprintf('Exported [%d] FILE (%s) dated (%s) to [%s]',$fo->id,$fo->name,$fo->datetime->format('Y-m-d H:i:s'),$this->mo->fftn->ftn3d));
|
||||||
|
$fo->seenby()->attach($this->mo->fftn_id,['export_at'=>Carbon::now()]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('Re-exported [%d] FILE (%s) dated (%s) to [%s]',$fo->id,$fo->name,$fo->datetime,$this->mo->fftn->ftn3d));
|
||||||
|
$fo->seenby()->updateExistingPivot($this->mo->fftn_id,['export_at'=>Carbon::now(),'sent_at'=>NULL]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- FILE QUEUED TO RESEND',$command);
|
||||||
|
|
||||||
|
// No file in area
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] doesnt have a file [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$this->arguments[1]));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- FILE NOT FOUND, NO ACTION taken',$command);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not subscribed
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] is NOT subscribed to [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$this->arguments[0]));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- NOT subscribed, NO ACTION taken',$command);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FILE [%s] area UNKNOWN [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$this->arguments[0]));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- AREA UNKNOWN, NO ACTION TAKEN',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
app/Classes/FTN/Process/Netmail/Robot/Filefix/Scan.php
Normal file
70
app/Classes/FTN/Process/Netmail/Robot/Filefix/Scan.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\FTN\Process\Netmail\Robot\Filefix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\FTN\Process\Netmail\Robot\Areafix\Base;
|
||||||
|
use App\Jobs\FilefixRescan;
|
||||||
|
|
||||||
|
// SCAN - Send unsent echomail
|
||||||
|
class Scan extends Base
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'AFS';
|
||||||
|
private const command = '%SCAN';
|
||||||
|
|
||||||
|
public static function help(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::command.' <FILEAREA> [<DAYS>]',
|
||||||
|
' Use the scan command to resend files that you havent received yet from an',
|
||||||
|
' filearea. This is useful if you are rejoining an filearea, and only want',
|
||||||
|
' to get files that you dont already have.',
|
||||||
|
' Arguments:',
|
||||||
|
' - FILEAREA (required) name of area to subscribe or unsubscribe',
|
||||||
|
' - DAYS (optional) number of days to resend mail from this area that you',
|
||||||
|
' If DAYS is omitted, the default is 30. The maximum is 365.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(): string
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s:- Filefix [%s] for [%s] for [%s]',self::LOGKEY,self::command,$this->mo->fftn->ftn,join('|',$this->arguments)));
|
||||||
|
|
||||||
|
$command = self::command.' '.join(' ',$this->arguments);
|
||||||
|
|
||||||
|
if (! ($area=Arr::get($this->arguments,0)))
|
||||||
|
return sprintf('%-25s <-- INVALID, AN AREA IS REQUIRED',$command);
|
||||||
|
|
||||||
|
if (! is_numeric($days=Arr::get($this->arguments,1,30)))
|
||||||
|
return sprintf('%-25s <-- INVALID, DAYS [%s] NOT NUMERIC',$command,$this->arguments[1]);
|
||||||
|
|
||||||
|
if ($days > 365)
|
||||||
|
$days = 365;
|
||||||
|
|
||||||
|
// Area exists
|
||||||
|
if ($fa=$this->mo->fftn->domain->fileareas->where('name',$area)->pop()) {
|
||||||
|
// If already subscribed
|
||||||
|
if ($this->mo->fftn->fileareas->pluck('name')->contains($area)) {
|
||||||
|
FilefixRescan::dispatch($this->mo->fftn,$fa,$days)
|
||||||
|
->onQueue('mail');
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] SCAN [%s] DAYS [%d]',self::LOGKEY,$this->mo->fftn->ftn,$area,$days));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- SCAN [%d] DAYS queued',$command,$days);
|
||||||
|
|
||||||
|
// If not subscribed
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FTN [%s] is NOT subscribed to [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- NOT subscribed, NO ACTION taken',$command);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf('%s:- FILE [%s] area UNKNOWN [%s], NO ACTION taken',self::LOGKEY,$this->mo->fftn->ftn,$area));
|
||||||
|
|
||||||
|
return sprintf('%-25s <-- AREA UNKNOWN, NO ACTION TAKEN',$command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -106,7 +106,7 @@ class Tic extends FTNBase
|
|||||||
$result->put('REPLACES',$this->file->replaces);
|
$result->put('REPLACES',$this->file->replaces);
|
||||||
$result->put('AREA',$this->file->filearea->name);
|
$result->put('AREA',$this->file->filearea->name);
|
||||||
$result->put('AREADESC',$this->file->filearea->description);
|
$result->put('AREADESC',$this->file->filearea->description);
|
||||||
if ($x=strtoupper($this->to->session('ticpass')))
|
if ($x=$this->to->pass_tic)
|
||||||
$result->put('PW',$x);
|
$result->put('PW',$x);
|
||||||
$result->put('CRC',sprintf("%X",$this->file->crc));
|
$result->put('CRC',sprintf("%X",$this->file->crc));
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ class Tic extends FTNBase
|
|||||||
*/
|
*/
|
||||||
public function isNodelist(): bool
|
public function isNodelist(): bool
|
||||||
{
|
{
|
||||||
Log::critical(sprintf('%s:D fo_nodelist_file_area [%d], fo_filearea_domain_filearea_id [%d], regex [%s] name [%s]',
|
Log::info(sprintf('%s:D fo_nodelist_file_area [%d], fo_filearea_domain_filearea_id [%d], regex [%s] name [%s]',
|
||||||
self::LOGKEY,
|
self::LOGKEY,
|
||||||
$this->file->nodelist_filearea_id,
|
$this->file->nodelist_filearea_id,
|
||||||
$this->file->filearea->domain->filearea_id,
|
$this->file->filearea->domain->filearea_id,
|
||||||
@@ -219,6 +219,8 @@ class Tic extends FTNBase
|
|||||||
case 'from':
|
case 'from':
|
||||||
if (($ao=Address::findFTN($m[2])) && ((! $aid) || ($ao->zone->domain_id === Address::findOrFail(hexdec($aid))->zone->domain_id)))
|
if (($ao=Address::findFTN($m[2])) && ((! $aid) || ($ao->zone->domain_id === Address::findOrFail(hexdec($aid))->zone->domain_id)))
|
||||||
$this->file->fftn_id = $ao->id;
|
$this->file->fftn_id = $ao->id;
|
||||||
|
elseif ($aid && ($x=Address::findOrFail(hexdec($aid))) && (($y=$x->system->akas->search(fn($item)=>str_starts_with($item->ftn,$m[2]))) !== FALSE))
|
||||||
|
$this->file->fftn_id = $x->system->akas->get($y)->id;
|
||||||
else
|
else
|
||||||
throw new ModelNotFoundException(sprintf('FTN Address [%s] not found or sender mismatch',$m[2]));
|
throw new ModelNotFoundException(sprintf('FTN Address [%s] not found or sender mismatch',$m[2]));
|
||||||
|
|
||||||
@@ -347,11 +349,11 @@ class Tic extends FTNBase
|
|||||||
// @todo Add notification back to the system if no replaces line and the file already exists
|
// @todo Add notification back to the system if no replaces line and the file already exists
|
||||||
|
|
||||||
// Validate Size
|
// Validate Size
|
||||||
if ($this->file->size !== ($y=$fs->size($file)))
|
if ((! is_null($this->file->size)) && ($this->file->size !== ($y=$fs->size($file))))
|
||||||
throw new SizeMismatchException(sprintf('TIC file size [%d] doesnt match file [%s] (%d)',$this->file->size,$fs->path($rel_path_name),$y));
|
throw new SizeMismatchException(sprintf('TIC file size [%d] doesnt match file [%s] (%d)',$this->file->size,$fs->path($rel_path_name),$y));
|
||||||
|
|
||||||
// Validate Password
|
// Validate Password
|
||||||
if (strtoupper($pw) !== ($y=strtoupper($this->file->fftn->session('ticpass'))))
|
if (strtoupper($pw) !== ($y=$this->file->fftn->pass_tic))
|
||||||
throw new InvalidPasswordException(sprintf('TIC file PASSWORD [%s] doesnt match system [%s] (%s)',$pw,$this->file->fftn->ftn,$y));
|
throw new InvalidPasswordException(sprintf('TIC file PASSWORD [%s] doesnt match system [%s] (%s)',$pw,$this->file->fftn->ftn,$y));
|
||||||
|
|
||||||
// Validate Sender is linked
|
// Validate Sender is linked
|
||||||
@@ -367,6 +369,10 @@ class Tic extends FTNBase
|
|||||||
if (! $this->file->datetime)
|
if (! $this->file->datetime)
|
||||||
$this->file->datetime = Carbon::createFromTimestamp($fs->lastModified($file));
|
$this->file->datetime = Carbon::createFromTimestamp($fs->lastModified($file));
|
||||||
|
|
||||||
|
// If the file size was omitted, we'll use the file's size
|
||||||
|
if (is_null($this->file->size))
|
||||||
|
$this->file->size = $fs->size($file);
|
||||||
|
|
||||||
$this->file->src_file = $file;
|
$this->file->src_file = $file;
|
||||||
$this->file->recv_tic = $filename;
|
$this->file->recv_tic = $filename;
|
||||||
|
|
||||||
|
@@ -22,24 +22,28 @@ class File extends FileBase implements \Iterator
|
|||||||
{
|
{
|
||||||
parent::__construct($path,$checkPath);
|
parent::__construct($path,$checkPath);
|
||||||
|
|
||||||
switch($x=$this->guessExtension()) {
|
if ($this->getExtension() === 'pkt')
|
||||||
case 'zip':
|
$this->canHandle = TRUE;
|
||||||
$this->canHandle = TRUE;
|
|
||||||
$this->isArchive = TRUE;
|
|
||||||
$this->z = new \ZipArchive;
|
|
||||||
$this->z->open($this->getRealPath());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NULL:
|
else
|
||||||
case 'bin':
|
switch ($x=$this->guessExtension()) {
|
||||||
if ($this->isPacket() || ($path instanceof UploadedFile && (strcasecmp($path->getClientOriginalExtension(),'pkt') === 0))) {
|
case 'zip':
|
||||||
$this->canHandle = TRUE;
|
$this->canHandle = TRUE;
|
||||||
|
$this->isArchive = TRUE;
|
||||||
|
$this->z = new \ZipArchive;
|
||||||
|
$this->z->open($this->getRealPath());
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
case NULL:
|
||||||
Log::alert(sprintf('%s:? Unknown file received: %s (%s) [%s]',self::LOGKEY,$x,$this->getExtension(),$path instanceof UploadedFile ? $path->getClientOriginalExtension() : '-'));
|
case 'bin':
|
||||||
}
|
if ($this->isPacket() || ($path instanceof UploadedFile && (strcasecmp($path->getClientOriginalExtension(),'pkt') === 0))) {
|
||||||
|
$this->canHandle = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log::alert(sprintf('%s:? Unknown file received: %s (%s) [%s]',self::LOGKEY,$x,$this->getExtension(),$path instanceof UploadedFile ? $path->getClientOriginalExtension() : '-'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ITERATOR */
|
/* ITERATOR */
|
||||||
@@ -83,6 +87,11 @@ class File extends FileBase implements \Iterator
|
|||||||
|
|
||||||
/* METHODS */
|
/* METHODS */
|
||||||
|
|
||||||
|
public function isArchive(): bool
|
||||||
|
{
|
||||||
|
return $this->isArchive;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the file is a mail packet
|
* Determine if the file is a mail packet
|
||||||
*
|
*
|
||||||
|
@@ -11,7 +11,7 @@ use Illuminate\Support\Collection;
|
|||||||
* When sending, we can queue up a list of items, and mark one active (the one being sent) at a time.
|
* When sending, we can queue up a list of items, and mark one active (the one being sent) at a time.
|
||||||
*
|
*
|
||||||
* + Netmail/Echomail/TIC
|
* + Netmail/Echomail/TIC
|
||||||
* + name is dynamically calculated, based on timew() of the youngest item in the mail bundle
|
* + name is the hex ID of the youngest item in the mail bundle
|
||||||
* + size is dynamically calculated based on the size of the bundle
|
* + size is dynamically calculated based on the size of the bundle
|
||||||
* + mtime is dynamically calculated, based on the age of the youngest item
|
* + mtime is dynamically calculated, based on the age of the youngest item
|
||||||
* + sendas (nameas) is name + [.pkt|.tic]
|
* + sendas (nameas) is name + [.pkt|.tic]
|
||||||
|
@@ -41,7 +41,6 @@ final class File extends Send
|
|||||||
return $this->f->datetime->timestamp;
|
return $this->f->datetime->timestamp;
|
||||||
|
|
||||||
case 'name':
|
case 'name':
|
||||||
case 'size':
|
|
||||||
return $this->f->{$key};
|
return $this->f->{$key};
|
||||||
|
|
||||||
case 'type':
|
case 'type':
|
||||||
@@ -84,22 +83,21 @@ final class File extends Send
|
|||||||
*/
|
*/
|
||||||
public function open(string $compress=''): bool
|
public function open(string $compress=''): bool
|
||||||
{
|
{
|
||||||
|
$this->size = $this->f->size;
|
||||||
|
|
||||||
// If sending file is a File::class, then our file is s3
|
// If sending file is a File::class, then our file is s3
|
||||||
if ($this->nameas && $this->f instanceof FileModel) {
|
$this->fd = ($this->nameas && $this->f instanceof FileModel)
|
||||||
$this->fd = Storage::readStream($this->f->rel_name);
|
? Storage::readStream($this->f->rel_name)
|
||||||
|
: fopen($this->full_name,'rb');
|
||||||
|
|
||||||
} else {
|
if (! $this->fd) {
|
||||||
$this->fd = fopen($this->full_name,'rb');
|
Log::error(sprintf('%s:! Unable to open file [%s] for reading',self::LOGKEY,$this->full_name));
|
||||||
|
|
||||||
if (! $this->fd) {
|
return FALSE;
|
||||||
Log::error(sprintf('%s:! Unable to open file [%s] for reading',self::LOGKEY,$this->full_name));
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info(sprintf('%s:= File [%s] opened with size [%d]',self::LOGKEY,$this->full_name,$this->size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:= File [%s] opened with size [%d]',self::LOGKEY,$this->full_name,$this->size));
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +108,6 @@ final class File extends Send
|
|||||||
|
|
||||||
public function seek(int $pos): bool
|
public function seek(int $pos): bool
|
||||||
{
|
{
|
||||||
return (fseek($this->f,$pos,SEEK_SET) === 0);
|
return (fseek($this->fd,$pos,SEEK_SET) === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -4,6 +4,7 @@ namespace App\Classes\File;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
@@ -36,16 +37,13 @@ final class Mail extends Send
|
|||||||
return $this->f->messages->pluck('id');
|
return $this->f->messages->pluck('id');
|
||||||
|
|
||||||
case 'name':
|
case 'name':
|
||||||
return sprintf('%08x',timew($this->youngest()));
|
return sprintf('%08x',$this->youngest_id());
|
||||||
|
|
||||||
case 'nameas':
|
case 'nameas':
|
||||||
return sprintf('%s.pkt',$this->name);
|
return sprintf('%s.pkt',$this->name);
|
||||||
|
|
||||||
case 'mtime':
|
case 'mtime':
|
||||||
return $this->youngest()->timestamp;
|
return $this->youngest_date()->timestamp;
|
||||||
|
|
||||||
case 'size':
|
|
||||||
return strlen($this->f);
|
|
||||||
|
|
||||||
case 'type':
|
case 'type':
|
||||||
return ($this->ftype&0xff00)>>8;
|
return ($this->ftype&0xff00)>>8;
|
||||||
@@ -60,7 +58,7 @@ final class Mail extends Send
|
|||||||
if ($successful) {
|
if ($successful) {
|
||||||
$this->complete = TRUE;
|
$this->complete = TRUE;
|
||||||
|
|
||||||
Log::debug(sprintf('%s:- Successful close for [%d] - updating [%d] records.',self::LOGKEY,$this->type,$this->dbids->count()),['dbids'=>$this->dbids,'authd'=>$node->aka_remote_authed->pluck('id')]);
|
Log::info(sprintf('%s:- Successful close for [%d] - updating [%d] records.',self::LOGKEY,$this->type,$this->dbids->count()),['dbids'=>$this->dbids,'authd'=>$node->aka_remote_authed->pluck('id')]);
|
||||||
|
|
||||||
// Update netmail table
|
// Update netmail table
|
||||||
if (($this->type === Send::T_NETMAIL)
|
if (($this->type === Send::T_NETMAIL)
|
||||||
@@ -87,6 +85,7 @@ final class Mail extends Send
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->content = NULL;
|
$this->content = NULL;
|
||||||
|
$this->f = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +97,7 @@ final class Mail extends Send
|
|||||||
public function open(string $compress=''): bool
|
public function open(string $compress=''): bool
|
||||||
{
|
{
|
||||||
$this->content = (string)$this->f;
|
$this->content = (string)$this->f;
|
||||||
|
$this->size = strlen($this->content);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +115,18 @@ final class Mail extends Send
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function youngest(): Carbon
|
private function youngest(): array
|
||||||
{
|
{
|
||||||
return $this->f->messages->pluck('date')->sort()->last();
|
return $this->f->messages->sortBy(fn($item)=>Arr::get($item,'datetime'))->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function youngest_id(): int
|
||||||
|
{
|
||||||
|
return Arr::get($this->youngest(),'id',0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function youngest_date(): Carbon
|
||||||
|
{
|
||||||
|
return Arr::get($this->youngest(),'datetime',Carbon::now());
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -128,9 +128,9 @@ class Receive extends Base
|
|||||||
// If packet is greater than a size, lets queue it
|
// If packet is greater than a size, lets queue it
|
||||||
if ($this->queue || ($this->receiving->size > config('fido.queue_size',0))) {
|
if ($this->queue || ($this->receiving->size > config('fido.queue_size',0))) {
|
||||||
Log::info(sprintf('%s:- Packet [%s] will be sent to the queue for processing because its [%d] size, or queue forced',self::LOGKEY,$this->receiving->full_name,$this->receiving->size));
|
Log::info(sprintf('%s:- Packet [%s] will be sent to the queue for processing because its [%d] size, or queue forced',self::LOGKEY,$this->receiving->full_name,$this->receiving->size));
|
||||||
PacketProcess::dispatch($this->receiving->rel_name,$this->ao->zone->domain,FALSE,$rcvd_time);
|
PacketProcess::dispatch($this->receiving->rel_name,$this->ao->system,FALSE,$rcvd_time);
|
||||||
} else
|
} else
|
||||||
PacketProcess::dispatchSync($this->receiving->rel_name,$this->ao->zone->domain,TRUE,$rcvd_time);
|
PacketProcess::dispatchSync($this->receiving->rel_name,$this->ao->system,TRUE,$rcvd_time);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::error(sprintf('%s:! Got error dispatching packet [%s] (%d:%s-%s).',self::LOGKEY,$this->receiving->rel_name,$e->getLine(),$e->getFile(),$e->getMessage()));
|
Log::error(sprintf('%s:! Got error dispatching packet [%s] (%d:%s-%s).',self::LOGKEY,$this->receiving->rel_name,$e->getLine(),$e->getFile(),$e->getMessage()));
|
||||||
|
@@ -34,6 +34,7 @@ class Send extends Base
|
|||||||
public const T_ECHOMAIL = (1<<2);
|
public const T_ECHOMAIL = (1<<2);
|
||||||
|
|
||||||
private string $comp_data;
|
private string $comp_data;
|
||||||
|
protected int $size = 0;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@@ -124,6 +125,9 @@ class Send extends Base
|
|||||||
if ($successful) {
|
if ($successful) {
|
||||||
$end = time()-$this->start;
|
$end = time()-$this->start;
|
||||||
Log::info(sprintf('%s:- Closing [%s], sent in [%d] with [%s] items',self::LOGKEY,$this->sending->nameas,$end,$this->sending->dbids->count()));
|
Log::info(sprintf('%s:- Closing [%s], sent in [%d] with [%s] items',self::LOGKEY,$this->sending->nameas,$end,$this->sending->dbids->count()));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::alert(sprintf('%s:- Closing [%s], file NOT SENT successfully',self::LOGKEY,$this->sending->nameas));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->sending->close($successful,$node);
|
$this->sending->close($successful,$node);
|
||||||
@@ -199,8 +203,8 @@ class Send extends Base
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Files
|
// Files
|
||||||
if (($x=$ao->filesWaiting())->count()) {
|
if (($x=$ao->getFiles())->count()) {
|
||||||
Log::debug(sprintf('%s:- [%d] Files(s) added for sending to [%s]',self::LOGKEY,$x->count(),$ao->ftn));
|
Log::info(sprintf('%s:- [%d] Files(s) added for sending to [%s]',self::LOGKEY,$x->count(),$ao->ftn));
|
||||||
|
|
||||||
// Add Files
|
// Add Files
|
||||||
foreach ($x as $fo) {
|
foreach ($x as $fo) {
|
||||||
@@ -223,12 +227,12 @@ class Send extends Base
|
|||||||
*/
|
*/
|
||||||
public function open(string $compress=''): bool
|
public function open(string $compress=''): bool
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('%s:+ Opening file to send',self::LOGKEY));
|
Log::debug(sprintf('%s:+ File Open to send',self::LOGKEY));
|
||||||
|
|
||||||
if ((($this->index=$this->list->search(function($item) { return $item->complete === FALSE; })) !== FALSE)
|
if ((($this->index=$this->list->search(function($item) { return $item->complete === FALSE; })) !== FALSE)
|
||||||
&& $this->sending->open())
|
&& $this->sending->open())
|
||||||
{
|
{
|
||||||
Log::info(sprintf('%s:- Sending item [%d] (%s)',self::LOGKEY,$this->index,$this->sending->nameas));
|
Log::info(sprintf('%s:- Content Send item [#%d] (%s) with size [%d]',self::LOGKEY,$this->index,$this->sending->nameas,$this->sending->size));
|
||||||
|
|
||||||
$this->pos = 0;
|
$this->pos = 0;
|
||||||
$this->start = time();
|
$this->start = time();
|
||||||
@@ -241,7 +245,10 @@ class Send extends Base
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Exception('No files to open');
|
Log::error(sprintf('%s:- No files to open',self::LOGKEY));
|
||||||
|
$this->index = NULL;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,7 +309,7 @@ class Send extends Base
|
|||||||
|
|
||||||
$this->pos += strlen($data);
|
$this->pos += strlen($data);
|
||||||
|
|
||||||
Log::debug(sprintf('%s:- Read [%d] bytes, file pos now [%d]',self::LOGKEY,strlen($data),$this->pos));
|
Log::debug(sprintf('%s:- Content Read [%d] bytes, pos now [%d]',self::LOGKEY,strlen($data),$this->pos));
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
@@ -322,7 +329,7 @@ class Send extends Base
|
|||||||
if ($this->sending->seek($pos)) {
|
if ($this->sending->seek($pos)) {
|
||||||
$this->pos = $pos;
|
$this->pos = $pos;
|
||||||
|
|
||||||
Log::debug(sprintf('%s:= Seeked to [%d]',self::LOGKEY,$this->pos));
|
Log::debug(sprintf('%s:= Content Seek to [%d]',self::LOGKEY,$this->pos));
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Classes\File\Send;
|
namespace App\Classes\File\Send;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
use App\Classes\File\Send;
|
use App\Classes\File\Send;
|
||||||
use App\Classes\Node;
|
use App\Classes\Node;
|
||||||
@@ -47,7 +48,7 @@ final class Dynamic extends Send
|
|||||||
return $this->sent->timestamp;
|
return $this->sent->timestamp;
|
||||||
|
|
||||||
case 'size':
|
case 'size':
|
||||||
return strlen($this->buffer);
|
return $this->{$key};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -56,6 +57,8 @@ final class Dynamic extends Send
|
|||||||
|
|
||||||
public function close(bool $successful,Node $node): void
|
public function close(bool $successful,Node $node): void
|
||||||
{
|
{
|
||||||
|
Log::debug(sprintf('%s:- Close [%s] - %s',self::LOGKEY,$this->nameas,$successful ? 'SUCCESSFUL' : 'FAILED'));
|
||||||
|
|
||||||
if ($successful) {
|
if ($successful) {
|
||||||
$this->complete = TRUE;
|
$this->complete = TRUE;
|
||||||
|
|
||||||
@@ -74,24 +77,35 @@ final class Dynamic extends Send
|
|||||||
$this->do->next_at = $next_at
|
$this->do->next_at = $next_at
|
||||||
->addDay();
|
->addDay();
|
||||||
|
|
||||||
|
while ($this->do->next_at->isPast())
|
||||||
|
$this->do->next_at = $this->do->next_at->addDay();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'WEEKLY':
|
case 'WEEKLY':
|
||||||
$this->do->next_at = $next_at
|
$this->do->next_at = $next_at
|
||||||
->addWeek();
|
->addWeek();
|
||||||
|
|
||||||
|
while ($this->do->next_at->isPast())
|
||||||
|
$this->do->next_at = $this->do->next_at->addWeek();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'MONTHLY':
|
case 'MONTHLY':
|
||||||
$this->do->next_at = $next_at
|
$this->do->next_at = $next_at
|
||||||
->addMonth();
|
->addMonth();
|
||||||
|
|
||||||
|
while ($this->do->next_at->isPast())
|
||||||
|
$this->do->next_at = $this->do->next_at->addMonth();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new \Exception(sprintf('%s:! Unknown frequency [%s] for [%d]',self::LOGKEY,$this->do->frequency,$this->do->id));
|
throw new \Exception(sprintf('%s:! Unknown frequency [%s] for [%d]',self::LOGKEY,$this->do->frequency,$this->do->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: - Frequency [%s], UPDATE next_at [%s]',self::LOGKEY,$this->do->frequency,$next_at->format('Y-m-d H:i:s')));
|
||||||
|
|
||||||
$this->do->save();
|
$this->do->save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,6 +118,7 @@ final class Dynamic extends Send
|
|||||||
public function open(string $compress=''): bool
|
public function open(string $compress=''): bool
|
||||||
{
|
{
|
||||||
$this->buffer = (string)$this->item;
|
$this->buffer = (string)$this->item;
|
||||||
|
$this->size = strlen($this->buffer);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@@ -43,9 +43,6 @@ final class Tic extends Send
|
|||||||
case 'mtime':
|
case 'mtime':
|
||||||
return $this->f->datetime->timestamp;
|
return $this->f->datetime->timestamp;
|
||||||
|
|
||||||
case 'size':
|
|
||||||
return strlen($this->tic);
|
|
||||||
|
|
||||||
case 'type':
|
case 'type':
|
||||||
return ($this->ftype&0xff00)>>8;
|
return ($this->ftype&0xff00)>>8;
|
||||||
|
|
||||||
@@ -67,6 +64,8 @@ final class Tic extends Send
|
|||||||
|
|
||||||
public function open(string $compress=''): bool
|
public function open(string $compress=''): bool
|
||||||
{
|
{
|
||||||
|
$this->size = strlen($this->tic);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
201
app/Classes/Fonts/Ansitex.php
Normal file
201
app/Classes/Fonts/Ansitex.php
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\Fonts;
|
||||||
|
|
||||||
|
use App\Classes\Font;
|
||||||
|
|
||||||
|
final class Ansitex extends Font
|
||||||
|
{
|
||||||
|
protected const FONT = [
|
||||||
|
'a' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xb3,0xdf,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'b' => [
|
||||||
|
[0xb3,0xc4,0xdc],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'c' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xb3,0x5f,0xdc],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'd' => [
|
||||||
|
[0xda,0xc4,0xdb],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'e' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xc3,0x5f,0xdc],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'f' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xc3,0x20,0x20],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'g' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0x20,0xc4,0xdf],
|
||||||
|
],
|
||||||
|
'h' => [
|
||||||
|
[0xde,0xc4,0xdc],
|
||||||
|
[0xde,0x20,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'i' => [
|
||||||
|
[0x20,0xdf],
|
||||||
|
[0xde,0xdb],
|
||||||
|
[0x20,0x20],
|
||||||
|
],
|
||||||
|
'j' => [
|
||||||
|
[0x20,0xdf],
|
||||||
|
[0xde,0xdb],
|
||||||
|
[0xc4,0xdf],
|
||||||
|
],
|
||||||
|
'k' => [
|
||||||
|
[0xb3,0x20,0xdc],
|
||||||
|
[0xb3,0x60,0xdc],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'l' => [
|
||||||
|
[0xb3,0x20],
|
||||||
|
[0xb3,0xdc],
|
||||||
|
[0x20,0x20],
|
||||||
|
],
|
||||||
|
'm' => [
|
||||||
|
[0xda,0xda,0xdc],
|
||||||
|
[0xb3,0xb3,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'n' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xb3,0x20,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'o' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'p' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0xc0,0x20,0x20],
|
||||||
|
],
|
||||||
|
'q' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0x20,0x20,0xdf],
|
||||||
|
],
|
||||||
|
'r' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xb3,0xc1,0x5c],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
's' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0x2e,0x5c,0xdc],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
't' => [
|
||||||
|
[0xda,0xc2,0xdc],
|
||||||
|
[0x20,0xb3,0x20],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'u' => [
|
||||||
|
[0xda,0x20,0xdc],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'v' => [
|
||||||
|
[0xda,0x20,0xdc],
|
||||||
|
[0x60,0x5c,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'w' => [
|
||||||
|
[0xda,0xda,0xdb],
|
||||||
|
[0x60,0x5c,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'x' => [
|
||||||
|
[0xda,0x20,0xdc],
|
||||||
|
[0xda,0xdf,0xdc],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'y' => [
|
||||||
|
[0xda,0x20,0xdc],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0x20,0xc4,0xdf],
|
||||||
|
],
|
||||||
|
'z' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0x2e,0x2f,0xdc],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'1' => [
|
||||||
|
[0xc4,0xdc],
|
||||||
|
[0x20,0xdb],
|
||||||
|
[0x20,0x20],
|
||||||
|
],
|
||||||
|
'2' => [
|
||||||
|
[0xfe,0xc4,0xdc],
|
||||||
|
[0xda,0x2f,0xdc],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'3' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xda,0xf0,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'4' => [
|
||||||
|
[0xde,0x20,0xdc],
|
||||||
|
[0xc0,0xc4,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'5' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0x2e,0x2d,0xdc],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'6' => [
|
||||||
|
[0xde,0xc4,0xbf],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'7' => [
|
||||||
|
[0xfe,0x2d,0xbf],
|
||||||
|
[0x20,0xde,0x20],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'8' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xc3,0xf0,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'9' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xd4,0xc4,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'0' => [
|
||||||
|
[0xda,0xc4,0xdc],
|
||||||
|
[0xb3,0x5f,0xdb],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'#' => [
|
||||||
|
[0xdc,0xba,0xdc],
|
||||||
|
[0xfe,0xba,0xfe],
|
||||||
|
[0x20,0x20,0x20],
|
||||||
|
],
|
||||||
|
'!' => [
|
||||||
|
[0xdb],
|
||||||
|
[0xfe],
|
||||||
|
[0x20],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
@@ -38,6 +38,7 @@ class Node
|
|||||||
private Collection $ftns_authed; // The FTNs we have validated
|
private Collection $ftns_authed; // The FTNs we have validated
|
||||||
private Collection $ftns_other; // Other FTN addresses presented
|
private Collection $ftns_other; // Other FTN addresses presented
|
||||||
private bool $authed; // Have we authenticated the remote.
|
private bool $authed; // Have we authenticated the remote.
|
||||||
|
private Address $originate; // When we originate a call, this is who we are after
|
||||||
|
|
||||||
private int $options; // This nodes capabilities/options
|
private int $options; // This nodes capabilities/options
|
||||||
|
|
||||||
@@ -84,11 +85,15 @@ class Node
|
|||||||
|
|
||||||
// The nodes password
|
// The nodes password
|
||||||
case 'password':
|
case 'password':
|
||||||
|
// If we are originating a session, we'll use that password.
|
||||||
|
if (isset($this->originate))
|
||||||
|
return $this->originate->pass_session;
|
||||||
|
|
||||||
// If we have already authed, we'll use that password.
|
// If we have already authed, we'll use that password.
|
||||||
if ($this->ftns_authed->count())
|
if ($this->ftns_authed->count())
|
||||||
return $this->ftns_authed->first()->session('sespass');
|
return $this->ftns_authed->first()->pass_session;
|
||||||
else
|
else
|
||||||
return ($this->ftns->count() && ($x=$this->ftns->first()->session('sespass'))) ? $x : '-';
|
return ($this->ftns->count() && ($x=$this->ftns->first()->pass_session)) ? $x : '-';
|
||||||
|
|
||||||
// Return how long our session has been connected
|
// Return how long our session has been connected
|
||||||
case 'session_time':
|
case 'session_time':
|
||||||
@@ -194,7 +199,9 @@ class Node
|
|||||||
throw new Exception('Already authed');
|
throw new Exception('Already authed');
|
||||||
|
|
||||||
foreach ($this->ftns as $o) {
|
foreach ($this->ftns as $o) {
|
||||||
if (! $sespass=$o->session('sespass'))
|
Log::debug(sprintf('%s:- Attempting to authenticate [%s] with [%s]',self::LOGKEY,$o->ftn,$o->pass_session));
|
||||||
|
|
||||||
|
if (! $sespass=$o->pass_session)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// If we have challenge, then we are doing MD5
|
// If we have challenge, then we are doing MD5
|
||||||
@@ -269,7 +276,8 @@ class Node
|
|||||||
*/
|
*/
|
||||||
public function originate(Address $o): void
|
public function originate(Address $o): void
|
||||||
{
|
{
|
||||||
$this->ftns_authed->push($o);
|
$this->originate = $o;
|
||||||
|
$this->ftns_authed = $o->system->match($o->zone,Address::NODE_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -283,19 +291,11 @@ class Node
|
|||||||
if ($this->authed)
|
if ($this->authed)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
if ($this->ftns_authed->count() !== 1 || ! $this->ftns->count())
|
Log::debug(sprintf('%s:- Making sure we called [%s] from [%s]',self::LOGKEY,$this->originate->ftn,$this->ftns->pluck('ftn')->join(',')));
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
$ftn = $this->ftns_authed->first()->ftn;
|
$this->authed = $this->ftns->pluck('ftn')->contains($this->originate->ftn);
|
||||||
|
|
||||||
return $this->ftns->search(function($item) use ($ftn) {
|
return $this->authed;
|
||||||
if ($item->ftn === $ftn) {
|
|
||||||
$item->system->last_session = Carbon::now();
|
|
||||||
$item->system->save();
|
|
||||||
$this->authed = TRUE;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}) !== FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function optionClear(int $key): void
|
public function optionClear(int $key): void
|
||||||
|
@@ -332,13 +332,13 @@ class Page
|
|||||||
$subtext = substr($this->text,$current_pos,$space_pos);
|
$subtext = substr($this->text,$current_pos,$space_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the reset of the string will fit on the current line
|
// If the rest of the string will fit on the current line
|
||||||
} elseif ($text_length-$current_pos < static::MSG_WIDTH-$buffer) {
|
} elseif ($text_length-$current_pos < static::MSG_WIDTH-$this->x-$buffer) {
|
||||||
$subtext = substr($this->text,$current_pos);
|
$subtext = substr($this->text,$current_pos);
|
||||||
|
|
||||||
// Get the next lines worth of chars, breaking on a space
|
// Get the next lines worth of chars, breaking on a space
|
||||||
} else {
|
} else {
|
||||||
$subtext = $this->text_substr(substr($this->text,$current_pos),static::MSG_WIDTH-$buffer);
|
$subtext = $this->text_substr(substr($this->text,$current_pos),static::MSG_WIDTH-$this->x-$buffer);
|
||||||
|
|
||||||
// Include the text up to the last space
|
// Include the text up to the last space
|
||||||
if (substr($this->text,$current_pos+strlen($subtext),1) !== ' ')
|
if (substr($this->text,$current_pos+strlen($subtext),1) !== ' ')
|
||||||
|
@@ -6,9 +6,9 @@ use Illuminate\Support\Collection;
|
|||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
use App\Classes\File\{Receive,Send};
|
use App\Classes\File\{Receive,Send};
|
||||||
use App\Classes\Protocol\EMSI;
|
use App\Classes\Protocol\{Binkp,DNS,EMSI,Zmodem};
|
||||||
|
use App\Classes\Sock\Exception\SocketException;
|
||||||
use App\Classes\Sock\SocketClient;
|
use App\Classes\Sock\SocketClient;
|
||||||
use App\Classes\Sock\SocketException;
|
|
||||||
use App\Models\{Address,Mailer,Setup,System,SystemLog};
|
use App\Models\{Address,Mailer,Setup,System,SystemLog};
|
||||||
|
|
||||||
// @todo after receiving a mail packet/file, dont acknowledge it until we can validate that we can read it properly.
|
// @todo after receiving a mail packet/file, dont acknowledge it until we can validate that we can read it properly.
|
||||||
@@ -97,17 +97,17 @@ abstract class Protocol
|
|||||||
|
|
||||||
// File transfer status
|
// File transfer status
|
||||||
|
|
||||||
public const FOP_OK = 0;
|
public const FOP_OK = 0;
|
||||||
public const FOP_CONT = 1;
|
public const FOP_CONT = 1;
|
||||||
public const FOP_SKIP = 2;
|
public const FOP_SKIP = 2;
|
||||||
public const FOP_ERROR = 3;
|
public const FOP_ERROR = 3;
|
||||||
public const FOP_SUSPEND = 4;
|
public const FOP_SUSPEND = 4;
|
||||||
public const FOP_GOT = 5;
|
public const FOP_GOT = 5;
|
||||||
|
|
||||||
public const TCP_SPEED = 115200;
|
public const TCP_SPEED = 115200;
|
||||||
|
|
||||||
protected SocketClient $client; /* Our socket details */
|
protected SocketClient $client; /* Our socket details */
|
||||||
protected ?Setup $setup; /* Our setup */
|
protected Setup $setup; /* Our setup */
|
||||||
protected Node $node; /* The node we are communicating with */
|
protected Node $node; /* The node we are communicating with */
|
||||||
/** The list of files we are sending */
|
/** The list of files we are sending */
|
||||||
protected Send $send;
|
protected Send $send;
|
||||||
@@ -122,7 +122,12 @@ abstract class Protocol
|
|||||||
protected array $capability; // @todo make private
|
protected array $capability; // @todo make private
|
||||||
/** @var bool Are we originating a connection */
|
/** @var bool Are we originating a connection */
|
||||||
protected bool $originate;
|
protected bool $originate;
|
||||||
/** Our comms details */
|
|
||||||
|
/** @var bool Is the application down for maintenance */
|
||||||
|
protected bool $down = FALSE;
|
||||||
|
|
||||||
|
/** @var int Our mailer ID for logging purposes */
|
||||||
|
private int $mailer_id;
|
||||||
|
|
||||||
private array $comms;
|
private array $comms;
|
||||||
|
|
||||||
@@ -132,12 +137,31 @@ abstract class Protocol
|
|||||||
|
|
||||||
abstract protected function protocol_session(bool $force_queue=FALSE): int;
|
abstract protected function protocol_session(bool $force_queue=FALSE): int;
|
||||||
|
|
||||||
public function __construct(Setup $o=NULL)
|
/**
|
||||||
|
* @param Setup $setup
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function __construct(Setup $setup)
|
||||||
{
|
{
|
||||||
if ($o && ! $o->system->akas->count())
|
$this->setup = $setup;
|
||||||
throw new \Exception('We dont have any ACTIVE FTN addresses assigned');
|
|
||||||
|
|
||||||
$this->setup = $o;
|
// Some initialisation details
|
||||||
|
switch (get_class($this)) {
|
||||||
|
case Binkp::class:
|
||||||
|
$this->mailer_id = Mailer::where('name','BINKP')->sole()->id;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DNS::class:
|
||||||
|
case Zmodem::class:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EMSI::class:
|
||||||
|
$this->mailer_id = Mailer::where('name','EMSI')->sole()->id;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('not handled'.get_class($this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,20 +270,29 @@ abstract class Protocol
|
|||||||
* Incoming Protocol session
|
* Incoming Protocol session
|
||||||
*
|
*
|
||||||
* @param SocketClient $client
|
* @param SocketClient $client
|
||||||
* @return int|null
|
* @return int
|
||||||
* @throws SocketException
|
* @throws SocketException
|
||||||
*/
|
*/
|
||||||
public function onConnect(SocketClient $client): ?int
|
public function onConnect(SocketClient $client): int
|
||||||
{
|
{
|
||||||
$pid = pcntl_fork();
|
$pid = pcntl_fork();
|
||||||
|
|
||||||
if ($pid === -1)
|
if ($pid === -1)
|
||||||
throw new SocketException(SocketException::CANT_ACCEPT,'Could not fork process');
|
throw new SocketException(SocketException::CANT_ACCEPT,'Could not fork process');
|
||||||
|
|
||||||
|
// If our parent returns a PID, we've forked
|
||||||
if ($pid)
|
if ($pid)
|
||||||
Log::info(sprintf('%s:+ New connection, thread [%d] created',self::LOGKEY,$pid));
|
Log::info(sprintf('%s:+ New connection from [%s], thread [%d] created',self::LOGKEY,$client->address_remote,$pid));
|
||||||
|
|
||||||
|
// This is the new thread
|
||||||
|
else {
|
||||||
|
Log::withContext(['pid'=>getmypid()]);
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:* Client session starting',self::LOGKEY));
|
||||||
|
|
||||||
|
$this->session($client,(new Address));
|
||||||
|
}
|
||||||
|
|
||||||
// Parent return ready for next connection
|
|
||||||
return $pid;
|
return $pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +335,7 @@ abstract class Protocol
|
|||||||
* Our addresses to send to the remote
|
* Our addresses to send to the remote
|
||||||
*
|
*
|
||||||
* @return Collection
|
* @return Collection
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
protected function our_addresses(): Collection
|
protected function our_addresses(): Collection
|
||||||
{
|
{
|
||||||
@@ -316,7 +350,7 @@ abstract class Protocol
|
|||||||
Log::debug(sprintf('%s:- Presenting limited AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
Log::debug(sprintf('%s:- Presenting limited AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$addresses = $this->setup->system->akas;
|
$addresses = our_address();
|
||||||
|
|
||||||
Log::debug(sprintf('%s:- Presenting ALL our AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
Log::debug(sprintf('%s:- Presenting ALL our AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
||||||
}
|
}
|
||||||
@@ -325,15 +359,14 @@ abstract class Protocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise our Session
|
* Setup a session with a remote client
|
||||||
*
|
*
|
||||||
* @param Mailer $mo
|
* @param SocketClient $client Socket details of session
|
||||||
* @param SocketClient $client
|
* @param Address $o If we have an address, we originated a session to this Address
|
||||||
* @param Address|null $o
|
|
||||||
* @return int
|
* @return int
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function session(Mailer $mo,SocketClient $client,Address $o=NULL): int
|
public function session(SocketClient $client,Address $o): int
|
||||||
{
|
{
|
||||||
if ($o->exists)
|
if ($o->exists)
|
||||||
Log::withContext(['ftn'=>$o->ftn]);
|
Log::withContext(['ftn'=>$o->ftn]);
|
||||||
@@ -364,13 +397,17 @@ abstract class Protocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We are an IP node
|
// We are an IP node
|
||||||
$this->optionSet(self::O_TCP);
|
|
||||||
$this->client = $client;
|
|
||||||
|
|
||||||
switch ($mo->name) {
|
$this->client = $client;
|
||||||
case 'EMSI':
|
// @todo This appears to be a bug in laravel? Need to call app()->isDownForMaintenance() twice?
|
||||||
|
app()->isDownForMaintenance();
|
||||||
|
$this->down = app()->isDownForMaintenance();
|
||||||
|
|
||||||
|
switch (get_class($this)) {
|
||||||
|
case EMSI::class:
|
||||||
Log::debug(sprintf('%s:- Starting EMSI',self::LOGKEY));
|
Log::debug(sprintf('%s:- Starting EMSI',self::LOGKEY));
|
||||||
|
|
||||||
|
$this->optionSet(self::O_TCP);
|
||||||
$rc = $this->protocol_init();
|
$rc = $this->protocol_init();
|
||||||
if ($rc < 0) {
|
if ($rc < 0) {
|
||||||
Log::error(sprintf('%s:! Unable to start EMSI [%d]',self::LOGKEY,$rc));
|
Log::error(sprintf('%s:! Unable to start EMSI [%d]',self::LOGKEY,$rc));
|
||||||
@@ -382,21 +419,23 @@ abstract class Protocol
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'BINKP':
|
case Binkp::class:
|
||||||
Log::debug(sprintf('%s:- Starting BINKP',self::LOGKEY));
|
Log::debug(sprintf('%s:- Starting BINKP',self::LOGKEY));
|
||||||
|
|
||||||
|
$this->optionSet(self::O_TCP);
|
||||||
$rc = $this->protocol_session($this->originate);
|
$rc = $this->protocol_session($this->originate);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DNS::class:
|
||||||
|
return $this->protocol_session();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Log::error(sprintf('%s:! Unsupported session type [%d]',self::LOGKEY,$mo->id));
|
Log::error(sprintf('%s:! Unsupported session type [%s]',self::LOGKEY,get_class($this)));
|
||||||
|
|
||||||
return self::S_FAILURE;
|
return self::S_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo Unlock outbounds
|
|
||||||
|
|
||||||
// @todo These flags determine when we connect to the remote.
|
// @todo These flags determine when we connect to the remote.
|
||||||
// If the remote indicated that they dont support file requests (NRQ) or temporarily hold them (HRQ)
|
// If the remote indicated that they dont support file requests (NRQ) or temporarily hold them (HRQ)
|
||||||
if (($this->node->optionGet(self::O_NRQ) && (! $this->setup->optionGet(EMSI::F_IGNORE_NRQ,'emsi_options'))) || $this->node->optionGet(self::O_HRQ))
|
if (($this->node->optionGet(self::O_NRQ) && (! $this->setup->optionGet(EMSI::F_IGNORE_NRQ,'emsi_options'))) || $this->node->optionGet(self::O_HRQ))
|
||||||
@@ -426,8 +465,9 @@ abstract class Protocol
|
|||||||
|
|
||||||
if ($so && $so->exists) {
|
if ($so && $so->exists) {
|
||||||
foreach ($this->node->aka_other as $aka)
|
foreach ($this->node->aka_other as $aka)
|
||||||
if (! Address::findFTN($aka)) {
|
// @todo For disabled zones, we shouldnt refuse to record the address
|
||||||
$oo = Address::createFTN($aka,$so);
|
// @todo If the system hasnt presented an address for a configured period (eg: 30 days) assume it no longer carries it
|
||||||
|
if ((! Address::findFTN($aka)) && ($oo=Address::createFTN($aka,$so))) {
|
||||||
$oo->validated = TRUE;
|
$oo->validated = TRUE;
|
||||||
$oo->save();
|
$oo->save();
|
||||||
}
|
}
|
||||||
@@ -438,7 +478,7 @@ abstract class Protocol
|
|||||||
$slo->items_sent_size = $this->send->total_sent_bytes;
|
$slo->items_sent_size = $this->send->total_sent_bytes;
|
||||||
$slo->items_recv = $this->recv->total_recv;
|
$slo->items_recv = $this->recv->total_recv;
|
||||||
$slo->items_recv_size = $this->recv->total_recv_bytes;
|
$slo->items_recv_size = $this->recv->total_recv_bytes;
|
||||||
$slo->mailer_id = $mo->id;
|
$slo->mailer_id = $this->mailer_id;
|
||||||
$slo->sessiontime = $this->node->session_time;
|
$slo->sessiontime = $this->node->session_time;
|
||||||
$slo->result = ($rc & self::S_MASK);
|
$slo->result = ($rc & self::S_MASK);
|
||||||
$slo->originate = $this->originate;
|
$slo->originate = $this->originate;
|
||||||
@@ -452,12 +492,6 @@ abstract class Protocol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo Optional after session execution event
|
|
||||||
// if ($this->node->start_time && $this->setup->cfg('CFG_AFTERSESSION')) {}
|
|
||||||
|
|
||||||
// @todo Optional after session includes mail event
|
|
||||||
// if ($this->node->start_time && $this->setup->cfg('CFG_AFTERMAIL')) {}
|
|
||||||
|
|
||||||
return $rc;
|
return $rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,10 +11,9 @@ use League\Flysystem\UnreadableFileEncountered;
|
|||||||
use App\Classes\Crypt;
|
use App\Classes\Crypt;
|
||||||
use App\Classes\Node;
|
use App\Classes\Node;
|
||||||
use App\Classes\Protocol as BaseProtocol;
|
use App\Classes\Protocol as BaseProtocol;
|
||||||
use App\Classes\Sock\SocketClient;
|
use App\Classes\Sock\Exception\SocketException;
|
||||||
use App\Classes\Sock\SocketException;
|
|
||||||
use App\Exceptions\{FileGrewException,InvalidFTNException};
|
use App\Exceptions\{FileGrewException,InvalidFTNException};
|
||||||
use App\Models\{Address,Mailer};
|
use App\Models\{Address,Setup};
|
||||||
|
|
||||||
final class Binkp extends BaseProtocol
|
final class Binkp extends BaseProtocol
|
||||||
{
|
{
|
||||||
@@ -142,36 +141,26 @@ final class Binkp extends BaseProtocol
|
|||||||
*/
|
*/
|
||||||
private Crypt $crypt_out;
|
private Crypt $crypt_out;
|
||||||
|
|
||||||
/**
|
|
||||||
* Incoming BINKP session
|
|
||||||
*
|
|
||||||
* @param SocketClient $client
|
|
||||||
* @return int|null
|
|
||||||
* @throws SocketException
|
|
||||||
*/
|
|
||||||
public function onConnect(SocketClient $client): ?int
|
|
||||||
{
|
|
||||||
// If our parent returns a PID, we've forked
|
|
||||||
if (! parent::onConnect($client)) {
|
|
||||||
Log::withContext(['pid'=>getmypid()]);
|
|
||||||
|
|
||||||
$this->session(Mailer::where('name','BINKP')->singleOrFail(),$client,(new Address));
|
|
||||||
$this->client->close();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BINKD handshake
|
* BINKD handshake
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
private function binkp_hs(): void
|
private function binkp_hs(): bool
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('%s:+ Starting BINKP handshake',self::LOGKEY));
|
Log::debug(sprintf('%s:+ Starting BINKP handshake',self::LOGKEY));
|
||||||
|
|
||||||
|
if (! $this->originate && $this->down) {
|
||||||
|
Log::info(sprintf('%s:! System down for maintenance',self::LOGKEY));
|
||||||
|
|
||||||
|
$this->msgs(self::BPM_BSY,'RETRY 0600: Down for maintenance, back soon...');
|
||||||
|
|
||||||
|
// @note Sometimes the remote drops the connection when we send the busy
|
||||||
|
while (($this->tx_left || $this->mqueue->count()) && $this->binkp_send()) {}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (! $this->originate && $this->capGet(self::F_MD,self::O_WANT)) {
|
if (! $this->originate && $this->capGet(self::F_MD,self::O_WANT)) {
|
||||||
$random_key = random_bytes(8);
|
$random_key = random_bytes(8);
|
||||||
$this->md_challenge = md5($random_key,TRUE);
|
$this->md_challenge = md5($random_key,TRUE);
|
||||||
@@ -184,7 +173,7 @@ final class Binkp extends BaseProtocol
|
|||||||
$this->msgs(self::BPM_NUL,sprintf('NDL %d,TCP,BINKP',$this->client->speed));
|
$this->msgs(self::BPM_NUL,sprintf('NDL %d,TCP,BINKP',$this->client->speed));
|
||||||
$this->msgs(self::BPM_NUL,sprintf('TIME %s',Carbon::now()->toRfc2822String()));
|
$this->msgs(self::BPM_NUL,sprintf('TIME %s',Carbon::now()->toRfc2822String()));
|
||||||
$this->msgs(self::BPM_NUL,
|
$this->msgs(self::BPM_NUL,
|
||||||
sprintf('VER %s-%s %s/%s',config('app.name'),$this->setup->version,self::PROT,self::VERSION));
|
sprintf('VER %s/%s %s/%s',Setup::PRODUCT_NAME_SHORT,Setup::version(),self::PROT,self::VERSION));
|
||||||
|
|
||||||
if ($this->originate) {
|
if ($this->originate) {
|
||||||
$opt = $this->capGet(self::F_NOREL,self::O_WANT) ? ' NR' : '';
|
$opt = $this->capGet(self::F_NOREL,self::O_WANT) ? ' NR' : '';
|
||||||
@@ -206,6 +195,8 @@ final class Binkp extends BaseProtocol
|
|||||||
|
|
||||||
$this->msgs(self::BPM_ADR,$addresses->pluck('ftn')->join(' '));
|
$this->msgs(self::BPM_ADR,$addresses->pluck('ftn')->join(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -380,11 +371,13 @@ final class Binkp extends BaseProtocol
|
|||||||
|
|
||||||
if ($this->capGet(self::F_CRYPT,self::O_YES)) {
|
if ($this->capGet(self::F_CRYPT,self::O_YES)) {
|
||||||
Log::debug(sprintf('%s:%% Decrypting data from remote.',self::LOGKEY));
|
Log::debug(sprintf('%s:%% Decrypting data from remote.',self::LOGKEY));
|
||||||
$this->rx_buf .= $this->crypt_in->decrypt($rx_buf);
|
$this->rx_buf .= ($x=$this->crypt_in->decrypt($rx_buf));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$this->rx_buf .= $rx_buf;
|
$this->rx_buf .= ($x=$rx_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- We read [%d] chars from remote',self::LOGKEY,strlen($x)),['rx_buf'=>hex_dump($x)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::debug(sprintf('%s:- Read buffer has [%d] chars to process.',self::LOGKEY,strlen($this->rx_buf)));
|
Log::debug(sprintf('%s:- Read buffer has [%d] chars to process.',self::LOGKEY,strlen($this->rx_buf)));
|
||||||
@@ -421,7 +414,7 @@ final class Binkp extends BaseProtocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (static::DEBUG)
|
if (static::DEBUG)
|
||||||
Log::debug(sprintf('%s: - binkp_recv BUFFER [%d]',self::LOGKEY,strlen($this->rx_buf)));
|
Log::debug(sprintf('%s:- rx_buf size [%d]',self::LOGKEY,strlen($this->rx_buf)));
|
||||||
|
|
||||||
$msg = ord(substr($this->rx_buf,0,1));
|
$msg = ord(substr($this->rx_buf,0,1));
|
||||||
|
|
||||||
@@ -474,6 +467,11 @@ final class Binkp extends BaseProtocol
|
|||||||
$rc = $this->M_get($data);
|
$rc = $this->M_get($data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case self::BPM_SKIP:
|
||||||
|
Log::debug(sprintf('%s:- SKIP:Remote requested to skip file [%s]',self::LOGKEY,$data));
|
||||||
|
$rc = $this->M_skip($data);
|
||||||
|
break;
|
||||||
|
|
||||||
case self::BPM_GOTSKIP:
|
case self::BPM_GOTSKIP:
|
||||||
Log::debug(sprintf('%s:- GOT:Remote received, or already has a file [%s]',self::LOGKEY,$data));
|
Log::debug(sprintf('%s:- GOT:Remote received, or already has a file [%s]',self::LOGKEY,$data));
|
||||||
$rc = $this->M_gotskip($data);
|
$rc = $this->M_gotskip($data);
|
||||||
@@ -643,7 +641,7 @@ final class Binkp extends BaseProtocol
|
|||||||
$offs = (int)$this->strsep($str,' ');
|
$offs = (int)$this->strsep($str,' ');
|
||||||
$flags = $this->strsep($str,' ');
|
$flags = $this->strsep($str,' ');
|
||||||
|
|
||||||
if ($name && $size && $time) {
|
if ($name && is_numeric($size) && $time) {
|
||||||
return [
|
return [
|
||||||
'file'=>['name'=>$name,'size'=>$size,'mtime'=>$time],
|
'file'=>['name'=>$name,'size'=>$size,'mtime'=>$time],
|
||||||
'offs'=>$offs,
|
'offs'=>$offs,
|
||||||
@@ -698,7 +696,7 @@ final class Binkp extends BaseProtocol
|
|||||||
|
|
||||||
// If we only present limited AKAs dont validate password against akas outside of the domains we present
|
// If we only present limited AKAs dont validate password against akas outside of the domains we present
|
||||||
} elseif (is_null(our_address($o))) {
|
} elseif (is_null(our_address($o))) {
|
||||||
Log::alert(sprintf('%s:/ AKA domain [%s] is not in our domain(s) [%s] - ignoring',self::LOGKEY,$o->zone->domain->name,our_address()->pluck('zone.domain.name')->unique()->join(',')));
|
Log::debug(sprintf('%s:/ AKA domain [%s] is not in our domain(s) [%s] - ignoring',self::LOGKEY,$o->zone->domain->name,our_address()->pluck('zone.domain.name')->unique()->join(',')));
|
||||||
|
|
||||||
$this->node->ftn_other = $rem_aka;
|
$this->node->ftn_other = $rem_aka;
|
||||||
continue;
|
continue;
|
||||||
@@ -713,6 +711,7 @@ final class Binkp extends BaseProtocol
|
|||||||
Log::info(sprintf('%s:- Got AKA [%s]',self::LOGKEY,$rem_aka));
|
Log::info(sprintf('%s:- Got AKA [%s]',self::LOGKEY,$rem_aka));
|
||||||
|
|
||||||
// We'll update this address status
|
// We'll update this address status
|
||||||
|
// @todo this shouldnt be here, since we havent authenticated the node
|
||||||
$o->validated = TRUE;
|
$o->validated = TRUE;
|
||||||
$o->role &= ~(Address::NODE_HOLD|Address::NODE_DOWN);
|
$o->role &= ~(Address::NODE_HOLD|Address::NODE_DOWN);
|
||||||
$o->save();
|
$o->save();
|
||||||
@@ -873,16 +872,13 @@ final class Binkp extends BaseProtocol
|
|||||||
//if ($this->recv->fd)
|
//if ($this->recv->fd)
|
||||||
// $this->recv->close();
|
// $this->recv->close();
|
||||||
|
|
||||||
|
// If we cannot understand the file, we'll send back a SKIP
|
||||||
if (! ($file=$this->file_parse($buf))) {
|
if (! ($file=$this->file_parse($buf))) {
|
||||||
Log::error(sprintf('%s:! UNPARSABLE file info [%s]',self::LOGKEY,$buf));
|
Log::error(sprintf('%s:! UNPARSABLE file info [%s]',self::LOGKEY,$buf));
|
||||||
$this->msgs(self::BPM_ERR,sprintf('M_FILE: unparsable file info: "%s", what are you on?',$buf));
|
$this->msgs(self::BPM_ERR,sprintf('M_FILE: unparsable file info: "%s", what are you on?',$buf));
|
||||||
|
$this->msgs(self::BPM_SKIP,$buf);
|
||||||
|
|
||||||
if ($this->sessionGet(self::SE_SENDFILE))
|
return TRUE;
|
||||||
$this->send->close(FALSE,$this->node);
|
|
||||||
|
|
||||||
$this->rc = self::S_FAILURE;
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// In NR mode, when we got -1 for the file offsite, the reply to our get will confirm our requested offset.
|
// In NR mode, when we got -1 for the file offsite, the reply to our get will confirm our requested offset.
|
||||||
@@ -900,6 +896,18 @@ final class Binkp extends BaseProtocol
|
|||||||
|
|
||||||
$this->recv->new($file['file'],$this->node->address,$this->force_queue);
|
$this->recv->new($file['file'],$this->node->address,$this->force_queue);
|
||||||
|
|
||||||
|
// If the file is zero byte size, we'll skip it
|
||||||
|
if ($this->recv->recvsize === 0) {
|
||||||
|
Log::alert(sprintf('%s:! SKIPPING zero byte file info [%s]',self::LOGKEY,$this->recv->nameas));
|
||||||
|
|
||||||
|
$this->msgs(self::BPM_SKIP,$this->recv->name_size_time);
|
||||||
|
|
||||||
|
// Close the file, since we are skipping it.
|
||||||
|
$this->recv->close();
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch ($this->recv->open($file['offs']<0,$file['flags'])) {
|
switch ($this->recv->open($file['offs']<0,$file['flags'])) {
|
||||||
case self::FOP_ERROR:
|
case self::FOP_ERROR:
|
||||||
@@ -993,7 +1001,46 @@ final class Binkp extends BaseProtocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* M_GOT/M_SKIP commands
|
* M_SKIP commands
|
||||||
|
*
|
||||||
|
* @param string $buf
|
||||||
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
|
* @todo We need to not add more files this session if a node skips a file
|
||||||
|
*/
|
||||||
|
private function M_skip(string $buf): bool
|
||||||
|
{
|
||||||
|
Log::alert(sprintf('%s:+ Remote request to skip the file for now [%s]',self::LOGKEY,$buf));
|
||||||
|
|
||||||
|
if ($file = $this->file_parse($buf)) {
|
||||||
|
if ($this->send->nameas
|
||||||
|
&& ! strncasecmp(Arr::get($file,'file.name'),$this->send->nameas,self::MAX_PATH)
|
||||||
|
&& $this->send->mtime === Arr::get($file,'file.mtime')
|
||||||
|
&& $this->send->size === Arr::get($file,'file.size'))
|
||||||
|
{
|
||||||
|
if ((! $this->sessionGet(self::SE_SENDFILE)) && (! $this->sessionGet(self::SE_WAITGOT))) {
|
||||||
|
Log::error(sprintf('%s:! M_skip for unknown file [%s]',self::LOGKEY,$buf));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::info(sprintf('%s:= Packet/File [%s], type [%d] skipped.',self::LOGKEY,$this->send->nameas,$this->send->type));
|
||||||
|
$this->sessionClear(self::SE_WAITGOT|self::SE_SENDFILE);
|
||||||
|
|
||||||
|
$this->send->close(FALSE,$this->node);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::error(sprintf('%s:! M_skip not for our file? [%s]',self::LOGKEY,$buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::error(sprintf('%s:! UNPARSABLE file info [%s]',self::LOGKEY,$buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* M_GOTSKIP command
|
||||||
*
|
*
|
||||||
* @param string $buf
|
* @param string $buf
|
||||||
* @return bool
|
* @return bool
|
||||||
@@ -1217,9 +1264,13 @@ final class Binkp extends BaseProtocol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->optionGet(self::O_PWD))
|
if ($this->optionGet(self::O_PWD)) {
|
||||||
Log::info(sprintf('%s:- SECURE',self::LOGKEY));
|
Log::info(sprintf('%s:- SECURE',self::LOGKEY));
|
||||||
|
|
||||||
|
// @todo Since we have connected, if the node was marked down/hold reset that
|
||||||
|
// Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations()));
|
||||||
|
}
|
||||||
|
|
||||||
return $this->binkp_hsdone();
|
return $this->binkp_hsdone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1315,6 +1366,9 @@ final class Binkp extends BaseProtocol
|
|||||||
if ($this->node->aka_authed) {
|
if ($this->node->aka_authed) {
|
||||||
$this->msgs(self::BPM_OK,sprintf('%ssecure',$have_pwd ? '' : 'non-'));
|
$this->msgs(self::BPM_OK,sprintf('%ssecure',$have_pwd ? '' : 'non-'));
|
||||||
|
|
||||||
|
// @todo Since we have connected, if the node was marked down/hold reset that
|
||||||
|
// Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations()));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$this->msgs(self::OK,'non-secure');
|
$this->msgs(self::OK,'non-secure');
|
||||||
}
|
}
|
||||||
@@ -1341,7 +1395,8 @@ final class Binkp extends BaseProtocol
|
|||||||
return self::S_FAILURE;
|
return self::S_FAILURE;
|
||||||
|
|
||||||
$this->force_queue = $force_queue;
|
$this->force_queue = $force_queue;
|
||||||
$this->binkp_hs();
|
if (! $this->binkp_hs())
|
||||||
|
return self::S_FAILURE;
|
||||||
|
|
||||||
while (TRUE) {
|
while (TRUE) {
|
||||||
if ((! $this->sessionGet(self::SE_INIT))
|
if ((! $this->sessionGet(self::SE_INIT))
|
||||||
@@ -1496,12 +1551,7 @@ final class Binkp extends BaseProtocol
|
|||||||
Log::info(sprintf('%s:- We have authed these AKAs [%s]',self::LOGKEY,$node->aka_remote_authed->pluck('ftn')->join(',')));
|
Log::info(sprintf('%s:- We have authed these AKAs [%s]',self::LOGKEY,$node->aka_remote_authed->pluck('ftn')->join(',')));
|
||||||
|
|
||||||
foreach ($node->aka_remote_authed as $ao) {
|
foreach ($node->aka_remote_authed as $ao) {
|
||||||
Log::debug(sprintf('%s:- Checking for any new mail and files to [%s]',self::LOGKEY,$ao->ftn));
|
Log::info(sprintf('%s:- Checking for any new mail and files to [%s]',self::LOGKEY,$ao->ftn));
|
||||||
|
|
||||||
if (! $ao->validated) {
|
|
||||||
Log::alert(sprintf('%s:! Address [%s] is not validated, so we wont bundle mail for it',self::LOGKEY,$ao->ftn));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->send->mail($ao);
|
$this->send->mail($ao);
|
||||||
$this->send->files($ao);
|
$this->send->files($ao);
|
||||||
@@ -1518,33 +1568,14 @@ final class Binkp extends BaseProtocol
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info(sprintf('%s:- We have [%d] items to send to [%s]',self::LOGKEY,$this->send->togo_count,$ao->system->name));
|
Log::info(sprintf('%s:- We have [%d] items to send to [%s]',self::LOGKEY,$this->send->togo_count,$ao->ftn));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// @todo We should only send netmail if unauthenticated - netmail that is direct to this node (no routing)
|
// @todo We should only send netmail if unauthenticated - netmail that is direct to this node (no routing)
|
||||||
Log::debug(sprintf('%s:- Not AUTHed so not looking for mail, but we know these akas [%s]',self::LOGKEY,$node->aka_remote->pluck('ftn')->join(',')));
|
Log::alert(sprintf('%s:- Not AUTHed so not looking for mail, but we know these akas [%s]',self::LOGKEY,$node->aka_remote->pluck('ftn')->join(',')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Strip blanks at the beginning of a string
|
|
||||||
*
|
|
||||||
* @param string $str
|
|
||||||
* @return string
|
|
||||||
* @throws \Exception
|
|
||||||
* @deprecated - use ltrim instead
|
|
||||||
*/
|
|
||||||
private function skip_blanks(string $str): string
|
|
||||||
{
|
|
||||||
$c = 0;
|
|
||||||
|
|
||||||
if ($str != NULL)
|
|
||||||
while ($this->isSpace(substr($str,$c,1)))
|
|
||||||
$c++;
|
|
||||||
|
|
||||||
return substr($str,$c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the string delimited by char and shorten the input to the remaining characters
|
* Return the string delimited by char and shorten the input to the remaining characters
|
||||||
*
|
*
|
||||||
@@ -1565,20 +1596,4 @@ final class Binkp extends BaseProtocol
|
|||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the string is a space
|
|
||||||
*
|
|
||||||
* @param string $str
|
|
||||||
* @return bool
|
|
||||||
* @throws \Exception
|
|
||||||
* @deprecated No longer required since we are using ltrim
|
|
||||||
*/
|
|
||||||
private function isSpace(string $str):bool
|
|
||||||
{
|
|
||||||
if (strlen($str) > 1)
|
|
||||||
throw new \Exception('String is more than 1 char');
|
|
||||||
|
|
||||||
return $str && in_array($str,[' ',"\n","\r","\v","\f","\t"]);
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -6,7 +6,6 @@ use Illuminate\Support\Facades\Log;
|
|||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
use App\Classes\Protocol as BaseProtocol;
|
use App\Classes\Protocol as BaseProtocol;
|
||||||
use App\Classes\Sock\SocketClient;
|
|
||||||
use App\Models\{Address,Domain,Mailer};
|
use App\Models\{Address,Domain,Mailer};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,22 +63,6 @@ final class DNS extends BaseProtocol
|
|||||||
public const DNS_TYPE_OPT = 41; // OPT Records
|
public const DNS_TYPE_OPT = 41; // OPT Records
|
||||||
public const DNS_TYPE_DS = 43; // DS Records (Delegation signer RFC 4034)
|
public const DNS_TYPE_DS = 43; // DS Records (Delegation signer RFC 4034)
|
||||||
|
|
||||||
public function onConnect(SocketClient $client): ?int
|
|
||||||
{
|
|
||||||
// If our parent returns a PID, we've forked
|
|
||||||
if (! parent::onConnect($client)) {
|
|
||||||
Log::withContext(['pid'=>getmypid()]);
|
|
||||||
|
|
||||||
$this->client = $client;
|
|
||||||
$this->protocol_session();
|
|
||||||
|
|
||||||
Log::info(sprintf('%s:= onConnect - Connection closed [%s]',self::LOGKEY,$client->address_remote));
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function protocol_init(): int
|
protected function protocol_init(): int
|
||||||
{
|
{
|
||||||
// N/A
|
// N/A
|
||||||
@@ -110,7 +93,7 @@ final class DNS extends BaseProtocol
|
|||||||
$this->query = new BaseProtocol\DNS\Query($this->client->read(0,512));
|
$this->query = new BaseProtocol\DNS\Query($this->client->read(0,512));
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::error(sprintf('%s:! Ignoring bad DNS query (%s)',self::LOGKEY,$e->getMessage()));
|
Log::notice(sprintf('%s:! Ignoring bad DNS query (%s)',self::LOGKEY,$e->getMessage()));
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@@ -119,7 +102,7 @@ final class DNS extends BaseProtocol
|
|||||||
|
|
||||||
// If the wrong class
|
// If the wrong class
|
||||||
if ($this->query->class !== self::DNS_QUERY_IN) {
|
if ($this->query->class !== self::DNS_QUERY_IN) {
|
||||||
Log::error(sprintf('%s:! We only service Internet queries [%d]',self::LOGKEY,$this->query->class));
|
Log::notice(sprintf('%s:! We only service Internet queries [%d]',self::LOGKEY,$this->query->class));
|
||||||
return $this->reply(self::DNS_NOTIMPLEMENTED,[],$this->soa());
|
return $this->reply(self::DNS_NOTIMPLEMENTED,[],$this->soa());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +150,7 @@ final class DNS extends BaseProtocol
|
|||||||
case self::DNS_TYPE_AAAA:
|
case self::DNS_TYPE_AAAA:
|
||||||
case self::DNS_TYPE_SRV:
|
case self::DNS_TYPE_SRV:
|
||||||
case self::DNS_TYPE_TXT:
|
case self::DNS_TYPE_TXT:
|
||||||
Log::info(sprintf('%s:= Looking for record [%s] for [%s]',self::LOGKEY,$this->query->type,$this->query->domain));
|
Log::debug(sprintf('%s:= Looking for record [%s] for [%s]',self::LOGKEY,$this->query->type,$this->query->domain));
|
||||||
|
|
||||||
$labels = clone($this->query->labels);
|
$labels = clone($this->query->labels);
|
||||||
$mailer = '';
|
$mailer = '';
|
||||||
@@ -179,11 +162,11 @@ final class DNS extends BaseProtocol
|
|||||||
|
|
||||||
switch ($labels->first()) {
|
switch ($labels->first()) {
|
||||||
case '_binkp':
|
case '_binkp':
|
||||||
$mailer = Mailer::where('name','BINKP')->singleOrFail();
|
$mailer = Mailer::where('name','BINKP')->sole();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '_ifcico':
|
case '_ifcico':
|
||||||
$mailer = Mailer::where('name','EMSI')->singleOrFail();
|
$mailer = Mailer::where('name','EMSI')->sole();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -232,14 +215,15 @@ final class DNS extends BaseProtocol
|
|||||||
// Check we have the right record
|
// Check we have the right record
|
||||||
if ((! $ao) || (($rootdn !== self::TLD) && ((! $ao->zone->domain->dnsdomain) || ($ao->zone->domain->dnsdomain !== $rootdn)))) {
|
if ((! $ao) || (($rootdn !== self::TLD) && ((! $ao->zone->domain->dnsdomain) || ($ao->zone->domain->dnsdomain !== $rootdn)))) {
|
||||||
Log::alert(sprintf('%s:= No DNS record for [%d:%d/%d.%d@%s]',self::LOGKEY,$z,$n,$f,$p,$d));
|
Log::alert(sprintf('%s:= No DNS record for [%d:%d/%d.%d@%s]',self::LOGKEY,$z,$n,$f,$p,$d));
|
||||||
|
|
||||||
return $this->nameerr();
|
return $this->nameerr();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($this->query->type) {
|
switch ($this->query->type) {
|
||||||
case self::DNS_TYPE_SRV:
|
case self::DNS_TYPE_SRV:
|
||||||
Log::info(sprintf('%s:= Returning [%s] for DNS query [%s]',self::LOGKEY,$ao->system->address,$ao->ftn));
|
|
||||||
|
|
||||||
if (($ao->system->address) && ($xx=$ao->system->mailers->where('id',$mailer->id)->pop())) {
|
if (($ao->system->address) && ($xx=$ao->system->mailers->where('id',$mailer->id)->pop())) {
|
||||||
|
Log::info(sprintf('%s:= Returning [%s] for DNS query [%s]',self::LOGKEY,$ao->system->address,$ao->ftn));
|
||||||
|
|
||||||
return $this->reply(
|
return $this->reply(
|
||||||
self::DNS_NOERROR,
|
self::DNS_NOERROR,
|
||||||
[serialize([
|
[serialize([
|
||||||
@@ -250,6 +234,8 @@ final class DNS extends BaseProtocol
|
|||||||
]) => self::DNS_TYPE_SRV]);
|
]) => self::DNS_TYPE_SRV]);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
Log::alert(sprintf('%s:! No/incomplete hostname/port details for [%d] for DNS query [%s]',self::LOGKEY,$ao->system->id,$ao->ftn));
|
||||||
|
|
||||||
return $this->nodata();
|
return $this->nodata();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +247,7 @@ final class DNS extends BaseProtocol
|
|||||||
[serialize($ao->system->name) => self::DNS_TYPE_TXT]);
|
[serialize($ao->system->name) => self::DNS_TYPE_TXT]);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Log::info(sprintf('%s:= Returning [%s] for DNS query [%s]',self::LOGKEY,$ao->system->address,$ao->ftn));
|
Log::info(sprintf('%s:= Returning [%s] for DNS query [%s]',self::LOGKEY,$ao->system->address ?: 'NO ADDRESS',$ao->ftn));
|
||||||
|
|
||||||
return (! $ao->system->address)
|
return (! $ao->system->address)
|
||||||
? $this->nodata()
|
? $this->nodata()
|
||||||
@@ -272,7 +258,7 @@ final class DNS extends BaseProtocol
|
|||||||
|
|
||||||
// Other attributes return NOTIMPL
|
// Other attributes return NOTIMPL
|
||||||
default:
|
default:
|
||||||
Log::error(sprintf('%s:! We dont support DNS query types [%d]',self::LOGKEY,$this->query->type));
|
Log::notice(sprintf('%s:! We dont support DNS query types [%d]',self::LOGKEY,$this->query->type));
|
||||||
|
|
||||||
return $this->reply(self::DNS_NOTIMPLEMENTED,[],$this->soa());
|
return $this->reply(self::DNS_NOTIMPLEMENTED,[],$this->soa());
|
||||||
}
|
}
|
||||||
@@ -309,14 +295,14 @@ final class DNS extends BaseProtocol
|
|||||||
|
|
||||||
private function nameerr(): int
|
private function nameerr(): int
|
||||||
{
|
{
|
||||||
Log::error(sprintf('%s:! DNS query for a resource we dont manage [%s]',self::LOGKEY,$this->query->domain));
|
Log::notice(sprintf('%s:! DNS query for a resource we dont manage [%s]',self::LOGKEY,$this->query->domain));
|
||||||
|
|
||||||
return $this->reply(self::DNS_NAMEERR,[],$this->soa());
|
return $this->reply(self::DNS_NAMEERR,[],$this->soa());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function nodata(): int
|
private function nodata(): int
|
||||||
{
|
{
|
||||||
Log::error(sprintf('%s:! DNS query for a resource we dont manage [%s] in our zone(s)',self::LOGKEY,$this->query->domain));
|
Log::notice(sprintf('%s:! DNS query for a resource we dont manage [%s] in our zone(s)',self::LOGKEY,$this->query->domain));
|
||||||
|
|
||||||
return $this->reply(self::DNS_NOERROR,[],$this->soa());
|
return $this->reply(self::DNS_NOERROR,[],$this->soa());
|
||||||
}
|
}
|
||||||
|
@@ -6,12 +6,11 @@ use Carbon\Carbon;
|
|||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
use App\Classes\Protocol as BaseProtocol;
|
use App\Classes\Protocol as BaseProtocol;
|
||||||
use App\Classes\Sock\SocketClient;
|
use App\Classes\Sock\Exception\SocketException;
|
||||||
use App\Classes\Sock\SocketException;
|
|
||||||
use App\Exceptions\InvalidFTNException;
|
use App\Exceptions\InvalidFTNException;
|
||||||
use App\Models\{Address,Mailer,Setup};
|
|
||||||
use App\Interfaces\CRC as CRCInterface;
|
use App\Interfaces\CRC as CRCInterface;
|
||||||
use App\Interfaces\Zmodem as ZmodemInterface;
|
use App\Interfaces\Zmodem as ZmodemInterface;
|
||||||
|
use App\Models\{Address,Setup};
|
||||||
use App\Traits\CRC as CRCTrait;
|
use App\Traits\CRC as CRCTrait;
|
||||||
|
|
||||||
// http://ftsc.org/docs/fsc-0056.001
|
// http://ftsc.org/docs/fsc-0056.001
|
||||||
@@ -82,27 +81,6 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
|||||||
'1'=>self::P_ZMODEM,
|
'1'=>self::P_ZMODEM,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* Incoming EMSI session
|
|
||||||
*
|
|
||||||
* @param SocketClient $client
|
|
||||||
* @return int|null
|
|
||||||
* @throws SocketException
|
|
||||||
*/
|
|
||||||
public function onConnect(SocketClient $client): ?int
|
|
||||||
{
|
|
||||||
// If our parent returns a PID, we've forked
|
|
||||||
if (! parent::onConnect($client)) {
|
|
||||||
Log::withContext(['pid'=>getmypid()]);
|
|
||||||
|
|
||||||
$this->session(Mailer::where('name','EMSI')->singleOrFail(),$client,(new Address));
|
|
||||||
$this->client->close();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send our welcome banner
|
* Send our welcome banner
|
||||||
*
|
*
|
||||||
@@ -207,8 +185,8 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
|||||||
// Mailer Details
|
// Mailer Details
|
||||||
$makedata .= sprintf('{%s}{%s}{%s}{%s}',
|
$makedata .= sprintf('{%s}{%s}{%s}{%s}',
|
||||||
Setup::product_id(),
|
Setup::product_id(),
|
||||||
config('app.name'),
|
Setup::PRODUCT_NAME_SHORT,
|
||||||
$this->setup->version,
|
Setup::version(),
|
||||||
'#000000' // Serial Numbers
|
'#000000' // Serial Numbers
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -930,16 +908,135 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
|||||||
|
|
||||||
$this->client->rx_purge();
|
$this->client->rx_purge();
|
||||||
$this->client->tx_purge();
|
$this->client->tx_purge();
|
||||||
|
|
||||||
|
if ($this->down) {
|
||||||
|
Log::info(sprintf('%s:! System down for maintenance',self::LOGKEY));
|
||||||
|
$this->client->buffer_add(self::EMSI_NAK.'Sorry down for maintenance, call back again after a few minutes'.self::CR.self::CR);
|
||||||
|
$this->client->buffer_flush(5);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
$this->emsi_banner();
|
$this->emsi_banner();
|
||||||
|
|
||||||
$t1 = $this->client->timer_set(self::EMSI_HSTIMEOUT);
|
$t1 = $this->client->timer_set(self::EMSI_HSTIMEOUT);
|
||||||
$t2 = $this->client->timer_set(self::EMSI_RESEND_TO);
|
$t2 = $this->client->timer_set(self::EMSI_RESEND_TO);
|
||||||
|
|
||||||
|
$c = 0;
|
||||||
while (! $this->client->timer_expired($t1)) {
|
while (! $this->client->timer_expired($t1)) {
|
||||||
$ch = $this->client->read_ch(max( 1,min($this->client->timer_rest($t1),$this->client->timer_rest($t2))));
|
try {
|
||||||
|
$ch = $this->client->read_ch(max( 1,min($this->client->timer_rest($t1),$this->client->timer_rest($t2))));
|
||||||
|
|
||||||
|
} catch (SocketException $e) {
|
||||||
|
if ($c++ > 2)
|
||||||
|
return self::TIMEOUT;
|
||||||
|
else
|
||||||
|
$ch = -2;
|
||||||
|
}
|
||||||
|
|
||||||
if (static::DEBUG)
|
if (static::DEBUG)
|
||||||
Log::debug(sprintf('%s:- Got [%x] (%c)',self::LOGKEY,$ch,$ch));
|
Log::debug(sprintf('%s:- Got [%x] (%c)',self::LOGKEY,$ch,$ch));
|
||||||
|
|
||||||
|
// Look for Telnet IAC, if binary mode we'll need to handle IAC IAC => IAC
|
||||||
|
if ($ch === 0xff) {
|
||||||
|
Log::info(sprintf('%s:- TELNET IAC',self::LOGKEY));
|
||||||
|
|
||||||
|
$iaccmd = NULL;
|
||||||
|
// Peek for the next chars
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
$iac = $this->client->read(1,1,MSG_PEEK);
|
||||||
|
if (static::DEBUG)
|
||||||
|
Log::debug(sprintf('%s: - IAC LOOP',self::LOGKEY),['iac'=>ord($iac),'cmd'=>$iaccmd]);
|
||||||
|
|
||||||
|
switch (ord($iac)) {
|
||||||
|
// Binary Mode
|
||||||
|
case 0x00:
|
||||||
|
if ($iaccmd === 0xfb) {
|
||||||
|
Log::debug(sprintf('%s: - IAC WILL BINARY [%02x]',self::LOGKEY,ord($iac)));
|
||||||
|
|
||||||
|
// Config with DO
|
||||||
|
$this->client->send(chr(0xff).chr(0xfd).$iac,10);
|
||||||
|
|
||||||
|
} elseif ($iaccmd === 0xfd) {
|
||||||
|
Log::debug(sprintf('%s: - IAC DO BINARY [%02x]',self::LOGKEY,ord($iac)));
|
||||||
|
|
||||||
|
// Config with WILL
|
||||||
|
if (! $this->client->iac_bin) {
|
||||||
|
$this->client->send(chr(0xff).chr(0xfb).$iac,10);
|
||||||
|
$this->client->iac_bin = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$iaccmd = NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Suppress Go Ahead
|
||||||
|
case 0x03:
|
||||||
|
if ($iaccmd === 0xfb) {
|
||||||
|
Log::debug(sprintf('%s: - IAC WILL SUPPRESS-GO-AHEAD [%02x]',self::LOGKEY,ord($iac)));
|
||||||
|
|
||||||
|
// Config with DO
|
||||||
|
$this->client->send(chr(0xff).chr(0xfd).$iac,10);
|
||||||
|
|
||||||
|
} elseif ($iaccmd === 0xfd) {
|
||||||
|
Log::debug(sprintf('%s: - IAC DO SUPPRESS-GO-AHEAD [%02x]',self::LOGKEY,ord($iac)));
|
||||||
|
|
||||||
|
// Config with WILL
|
||||||
|
$this->client->send(chr(0xff).chr(0xfb).$iac,10);
|
||||||
|
}
|
||||||
|
|
||||||
|
$iaccmd = NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Will
|
||||||
|
case 0xfb:
|
||||||
|
if (static::DEBUG)
|
||||||
|
Log::debug(sprintf('%s: - IAC WILL [%02x]',self::LOGKEY,ord($iac)));
|
||||||
|
|
||||||
|
$iaccmd = ord($iac);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Do
|
||||||
|
case 0xfd:
|
||||||
|
if (static::DEBUG)
|
||||||
|
Log::debug(sprintf('%s: - IAC DO [%02x]',self::LOGKEY,ord($iac)));
|
||||||
|
|
||||||
|
$iaccmd = ord($iac);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// IAC
|
||||||
|
case 0xff:
|
||||||
|
if (static::DEBUG)
|
||||||
|
Log::debug(sprintf('%s: - IAC [%02x]',self::LOGKEY,ord($iac)));
|
||||||
|
|
||||||
|
$iaccmd = ord($iac);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log::alert(sprintf('%s: - IAC Unhandled [%02x]',self::LOGKEY,ord($iac)),['iac'=>$iac,'iaccmd'=>$iaccmd,'ch'=>ord($iac)]);
|
||||||
|
|
||||||
|
$ch = ord($iac);
|
||||||
|
$iac = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($iaccmd) {
|
||||||
|
$iac = ord($this->client->read_ch(10));
|
||||||
|
$ch = NULL;
|
||||||
|
|
||||||
|
} elseif (is_null($ch)) {
|
||||||
|
$ch = ord($this->client->read_ch(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (SocketException $e) {
|
||||||
|
Log::debug(sprintf('%s:! SocketException: %s',self::LOGKEY,$e->getMessage()),['class'=>get_class($e),'code'=>$e->getCode()]);
|
||||||
|
$iac = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (! is_null($iac));
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- Leaving IAC with [%02x]',self::LOGKEY,$ch),['ch'=>serialize($ch)]);
|
||||||
|
}
|
||||||
|
|
||||||
if (($ch != self::TIMEOUT) && ($ch < 0))
|
if (($ch != self::TIMEOUT) && ($ch < 0))
|
||||||
return $ch;
|
return $ch;
|
||||||
|
|
||||||
@@ -1053,6 +1150,9 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
|||||||
return (self::S_REDIAL|self::S_ADDTRY);
|
return (self::S_REDIAL|self::S_ADDTRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @todo Since we have connected, if the node was marked down/hold reset that
|
||||||
|
// Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations()));
|
||||||
|
|
||||||
// @todo Lock Node AKAs
|
// @todo Lock Node AKAs
|
||||||
|
|
||||||
Log::info(sprintf('%s:- We have [%lu%s] mail, [%lu%s] files',self::LOGKEY,$this->send->mail_size,'b',$this->send->files_size,'b'));
|
Log::info(sprintf('%s:- We have [%lu%s] mail, [%lu%s] files',self::LOGKEY,$this->send->mail_size,'b',$this->send->files_size,'b'));
|
||||||
@@ -1204,7 +1304,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
|||||||
Log::debug(sprintf('%s:+ Start WAZOO Receive',self::LOGKEY));
|
Log::debug(sprintf('%s:+ Start WAZOO Receive',self::LOGKEY));
|
||||||
|
|
||||||
// @todo If the node is not defined in the DB node->address is NULL. Need to figure out how to handle those nodes.
|
// @todo If the node is not defined in the DB node->address is NULL. Need to figure out how to handle those nodes.
|
||||||
$rc = (new Zmodem)->zmodem_receive($this->client,$zap,$this->recv,$this->node->address,$this->force_queue);
|
$rc = (new Zmodem($this->setup))->zmodem_receive($this->client,$zap,$this->recv,$this->node->address,$this->force_queue);
|
||||||
|
|
||||||
return ($rc === self::RCDO || $rc === self::ERROR);
|
return ($rc === self::RCDO || $rc === self::ERROR);
|
||||||
}
|
}
|
||||||
@@ -1226,14 +1326,9 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
|||||||
// Add our mail to the queue if we have authenticated
|
// Add our mail to the queue if we have authenticated
|
||||||
if ($this->node->aka_authed)
|
if ($this->node->aka_authed)
|
||||||
foreach ($this->node->aka_remote_authed as $ao) {
|
foreach ($this->node->aka_remote_authed as $ao) {
|
||||||
if (! $ao->validated) {
|
|
||||||
Log::alert(sprintf('%s:! Address [%s] is not validated, so we wont bundle mail for it',self::LOGKEY,$ao->ftn));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send mail
|
// Send mail
|
||||||
while ($this->send->mail($ao)) {
|
while ($this->send->mail($ao)) {
|
||||||
$z = new Zmodem;
|
$z = new Zmodem($this->setup);
|
||||||
|
|
||||||
if (! $z->zmodem_sendinit($this->client,$zap) && $this->send->togo_count)
|
if (! $z->zmodem_sendinit($this->client,$zap) && $this->send->togo_count)
|
||||||
$z->zmodem_sendfile($this->send,$this->node);
|
$z->zmodem_sendfile($this->send,$this->node);
|
||||||
@@ -1241,7 +1336,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
|||||||
|
|
||||||
// Send files
|
// Send files
|
||||||
while ($this->send->files($ao)) {
|
while ($this->send->files($ao)) {
|
||||||
$z = new Zmodem;
|
$z = new Zmodem($this->setup);
|
||||||
|
|
||||||
if (! $z->zmodem_sendinit($this->client,$zap) && $this->send->togo_count)
|
if (! $z->zmodem_sendinit($this->client,$zap) && $this->send->togo_count)
|
||||||
$z->zmodem_sendfile($this->send,$this->node);
|
$z->zmodem_sendfile($this->send,$this->node);
|
||||||
|
@@ -5,12 +5,12 @@ namespace App\Classes\Protocol;
|
|||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
use App\Classes\{Node,Protocol};
|
use App\Classes\{Node,Protocol};
|
||||||
use App\Classes\Protocol\Zmodem as ZmodemClass;
|
|
||||||
use App\Classes\File\{Receive,Send};
|
use App\Classes\File\{Receive,Send};
|
||||||
use App\Classes\Sock\{SocketClient,SocketException};
|
use App\Classes\Sock\Exception\SocketException;
|
||||||
|
use App\Classes\Sock\SocketClient;
|
||||||
use App\Interfaces\CRC as CRCInterface;
|
use App\Interfaces\CRC as CRCInterface;
|
||||||
use App\Interfaces\Zmodem as ZmodemInterface;
|
use App\Interfaces\Zmodem as ZmodemInterface;
|
||||||
use App\Models\{Address,Mailer};
|
use App\Models\Address;
|
||||||
use App\Traits\CRC as CRCTrait;
|
use App\Traits\CRC as CRCTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -202,27 +202,6 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
|||||||
private string $rxbuf = '';
|
private string $rxbuf = '';
|
||||||
private string $txbuf = '';
|
private string $txbuf = '';
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SocketClient $client
|
|
||||||
* @return null
|
|
||||||
* @throws SocketException
|
|
||||||
*/
|
|
||||||
public function onConnect(SocketClient $client): ?int
|
|
||||||
{
|
|
||||||
// If our parent returns a PID, we've forked
|
|
||||||
if (! parent::onConnect($client)) {
|
|
||||||
Log::withContext(['pid'=>getmypid()]);
|
|
||||||
|
|
||||||
$this->session(Mailer::where('name','ZMODEM')->singleOrFail(),$client);
|
|
||||||
$this->client->close();
|
|
||||||
|
|
||||||
Log::info(sprintf('%s:= onConnect - Connection closed [%s]',self::LOGKEY,$client->address_remote));
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise our session
|
* Initialise our session
|
||||||
*/
|
*/
|
||||||
@@ -510,7 +489,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
|||||||
* @param Send $send
|
* @param Send $send
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function zmodem_sendfile(Send $send,Node $node): int
|
public function zmodem_sendfile(Send $send,Node $node): void
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('%s:+ zmodem_sendfile',self::LOGKEY));
|
Log::debug(sprintf('%s:+ zmodem_sendfile',self::LOGKEY));
|
||||||
|
|
||||||
@@ -534,14 +513,16 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $rc;
|
return;
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::error(sprintf('%s:! Error [%s]',self::LOGKEY,$e->getMessage()));
|
Log::error(sprintf('%s:! Error [%s]',self::LOGKEY,$e->getMessage()),['rc'=>$rc ?? '-UNDEFINED-']);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::OK;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1166,7 +1147,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
|||||||
private function ls_zrecvdata32(string &$data,int &$len,int $timeout): int
|
private function ls_zrecvdata32(string &$data,int &$len,int $timeout): int
|
||||||
{
|
{
|
||||||
if (static::DEBUG)
|
if (static::DEBUG)
|
||||||
Log::debug(sprintf('%s:+ ls_zrecvdata32',self::LOGKEY),['d'=>$data]);
|
Log::debug(sprintf('%s:+ ls_zrecvdata32',self::LOGKEY),['d'=>$data,'len'=>$len,'timeout'=>$timeout]);
|
||||||
|
|
||||||
$got = 0; /* Bytes total got */
|
$got = 0; /* Bytes total got */
|
||||||
$crc = 0; /* Received CRC */
|
$crc = 0; /* Received CRC */
|
||||||
@@ -1184,6 +1165,9 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
|||||||
return self::LSZ_BADCRC;
|
return self::LSZ_BADCRC;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
if (static::DEBUG)
|
||||||
|
Log::debug(sprintf('%s:- ls_zrecvdata32 c>32 [%x] (%c)',self::LOGKEY,$c,($c<31 ? 32 : $c)),['c'=>serialize($c)]);
|
||||||
|
|
||||||
switch ($c) {
|
switch ($c) {
|
||||||
case self::LSZ_CRCE:
|
case self::LSZ_CRCE:
|
||||||
case self::LSZ_CRCG:
|
case self::LSZ_CRCG:
|
||||||
@@ -1296,6 +1280,8 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case self::LSZ_BADCRC:
|
case self::LSZ_BADCRC:
|
||||||
|
$this->rxbuf = '';
|
||||||
|
|
||||||
case self::TIMEOUT:
|
case self::TIMEOUT:
|
||||||
if ($this->ls_rxAttnStr) {
|
if ($this->ls_rxAttnStr) {
|
||||||
$this->client->buffer_add($this->ls_rxAttnStr);
|
$this->client->buffer_add($this->ls_rxAttnStr);
|
||||||
@@ -1324,6 +1310,9 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
|||||||
$needzdata = 1;
|
$needzdata = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (static::DEBUG)
|
||||||
|
Log::debug(sprintf('%s:- ls_zrecvfile RC [%s]',self::LOGKEY,$rc),['needzdata'=>$needzdata]);
|
||||||
|
|
||||||
/* We need new position -- ZDATA (and may be ZEOF) */
|
/* We need new position -- ZDATA (and may be ZEOF) */
|
||||||
} else {
|
} else {
|
||||||
Log::debug(sprintf('%s:- ls_zrecvfile Want ZDATA/ZEOF at [%d]',self::LOGKEY,$rxpos));
|
Log::debug(sprintf('%s:- ls_zrecvfile Want ZDATA/ZEOF at [%d]',self::LOGKEY,$rxpos));
|
||||||
@@ -1354,7 +1343,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
|||||||
return self::OK;
|
return self::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::debug(sprintf('%s:- ls_zrecvfile ZDATA',self::LOGKEY));
|
Log::debug(sprintf('%s:- ls_zrecvfile ZDATA',self::LOGKEY),['newpos'=>$newpos]);
|
||||||
$needzdata = 0;
|
$needzdata = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1947,6 +1936,9 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
|||||||
$this->ls_zsendhhdr(self::ZNAK,$this->ls_storelong(0));
|
$this->ls_zsendhhdr(self::ZNAK,$this->ls_storelong(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sleep between tries
|
||||||
|
sleep(5);
|
||||||
|
|
||||||
} while (++$trys < 10);
|
} while (++$trys < 10);
|
||||||
|
|
||||||
Log::error(sprintf('%s:? ls_zrecvnewpos Something strange or timeout [%d]',self::LOGKEY,$rc));
|
Log::error(sprintf('%s:? ls_zrecvnewpos Something strange or timeout [%d]',self::LOGKEY,$rc));
|
||||||
|
6
app/Classes/Sock/Exception/HAproxyException.php
Normal file
6
app/Classes/Sock/Exception/HAproxyException.php
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\Sock\Exception;
|
||||||
|
|
||||||
|
final class HAproxyException extends \Exception {
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Classes\Sock;
|
namespace App\Classes\Sock\Exception;
|
||||||
|
|
||||||
// @todo Can we change this to use socket_strerr() && socket_last_error()
|
// @todo Can we change this to use socket_strerr() && socket_last_error()
|
||||||
final class SocketException extends \Exception {
|
final class SocketException extends \Exception {
|
||||||
@@ -11,6 +11,7 @@ final class SocketException extends \Exception {
|
|||||||
public const CANT_CONNECT = 5;
|
public const CANT_CONNECT = 5;
|
||||||
public const SOCKET_ERROR = 6;
|
public const SOCKET_ERROR = 6;
|
||||||
public const SOCKET_EAGAIN = 11;
|
public const SOCKET_EAGAIN = 11;
|
||||||
|
public const SOCKET_TIMEOUT = 15;
|
||||||
public const SOCKET_READ = 22;
|
public const SOCKET_READ = 22;
|
||||||
public const CONNECTION_RESET = 104;
|
public const CONNECTION_RESET = 104;
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ final class SocketException extends \Exception {
|
|||||||
self::CANT_CONNECT => 'Can\'t connect: "%s"',
|
self::CANT_CONNECT => 'Can\'t connect: "%s"',
|
||||||
self::SOCKET_ERROR => 'Socket Error: "%s"',
|
self::SOCKET_ERROR => 'Socket Error: "%s"',
|
||||||
self::SOCKET_EAGAIN => 'Socket Resource Temporarily Unavailable - Try again',
|
self::SOCKET_EAGAIN => 'Socket Resource Temporarily Unavailable - Try again',
|
||||||
|
self::SOCKET_TIMEOUT => 'Timeout reached "%d"',
|
||||||
self::SOCKET_READ => 'Unable to read from socket',
|
self::SOCKET_READ => 'Unable to read from socket',
|
||||||
self::CONNECTION_RESET => 'Connection reset by peer',
|
self::CONNECTION_RESET => 'Connection reset by peer',
|
||||||
];
|
];
|
@@ -6,6 +6,8 @@ use Illuminate\Support\Arr;
|
|||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
use App\Classes\Sock\Exception\{HAproxyException,SocketException};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class SocketClient
|
* Class SocketClient
|
||||||
*
|
*
|
||||||
@@ -46,7 +48,8 @@ final class SocketClient {
|
|||||||
/** @var string Data in the RX buffer */
|
/** @var string Data in the RX buffer */
|
||||||
private string $rx_buf = '';
|
private string $rx_buf = '';
|
||||||
|
|
||||||
public function __construct (\Socket $connection) {
|
public function __construct (\Socket $connection,bool $originate=FALSE)
|
||||||
|
{
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
|
|
||||||
if ($this->type === SOCK_STREAM) {
|
if ($this->type === SOCK_STREAM) {
|
||||||
@@ -54,133 +57,163 @@ final class SocketClient {
|
|||||||
socket_getpeername($connection,$this->address_remote,$this->port_remote);
|
socket_getpeername($connection,$this->address_remote,$this->port_remote);
|
||||||
|
|
||||||
// If HAPROXY is used, work get the clients address
|
// If HAPROXY is used, work get the clients address
|
||||||
if (config('fido.haproxy')) {
|
if ((! $originate) && config('fido.haproxy')) {
|
||||||
Log::debug(sprintf('%s:+ HAPROXY connection host [%s] on port [%d] (%s)',self::LOGKEY,$this->address_remote,$this->port_remote,$this->type));
|
Log::debug(sprintf('%s:+ HAPROXY connection host [%s] on port [%d] (%s)',self::LOGKEY,$this->address_remote,$this->port_remote,$this->type));
|
||||||
|
|
||||||
if ($this->read(5,12) !== "\x0d\x0a\x0d\x0a\x00\x0d\x0aQUIT\x0a") {
|
if (($x=$this->read(5,6)) === 'PROXY ')
|
||||||
Log::error(sprintf('%s:! Failed to initialise HAPROXY connection',self::LOGKEY));
|
$vers = 1;
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,'Failed to initialise HAPROXY connection');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version/Command
|
elseif (($x === "\x0d\x0a\x0d\x0a\x00\x0d") && ($this->read(5,6) === "\x0aQUIT\x0a"))
|
||||||
$vc = $this->read_ch(5);
|
$vers = 2;
|
||||||
|
|
||||||
if (($x=($vc>>4)&0x7) !== 2) {
|
else
|
||||||
Log::error(sprintf('%s:! HAPROXY version [%d] is not handled',self::LOGKEY,$x));
|
throw new HAproxyException('Failed to initialise HAPROXY connection');
|
||||||
|
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY version');
|
switch ($vers) {
|
||||||
}
|
|
||||||
|
|
||||||
switch ($x=($vc&0x7)) {
|
|
||||||
// HAPROXY internal
|
|
||||||
case 0:
|
|
||||||
Log::debug(sprintf('%s:! HAPROXY internal health-check',self::LOGKEY));
|
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,'Healthcheck');
|
|
||||||
|
|
||||||
// PROXY connection
|
|
||||||
case 1:
|
case 1:
|
||||||
|
// Protocol/Address Family
|
||||||
|
switch ($x=$this->read(5,5)) {
|
||||||
|
case 'TCP4 ':
|
||||||
|
$p = 4;
|
||||||
|
break;
|
||||||
|
case 'TCP6 ':
|
||||||
|
$p = 6;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new HAproxyException(sprintf('HAPROXY protocol [%d] is not handled',$x));
|
||||||
|
}
|
||||||
|
|
||||||
|
$read = $this->read(5,104-11);
|
||||||
|
|
||||||
|
// IPv4
|
||||||
|
if (($p === 4) || ($p === 6)) {
|
||||||
|
$parse = collect(sscanf($read,'%s %s %s %s'));
|
||||||
|
|
||||||
|
$src = Arr::get($parse,0);
|
||||||
|
$dst = Arr::get($parse,1);
|
||||||
|
$src_port = (int)Arr::get($parse,2);
|
||||||
|
$dst_port = (int)Arr::get($parse,3);
|
||||||
|
$len = $parse->map(fn($item)=>strlen($item))->sum()+3;
|
||||||
|
|
||||||
|
// The last 2 chars should be "\r\n"
|
||||||
|
if (($x=substr($read,$len)) !== "\r\n")
|
||||||
|
throw new HAproxyException(sprintf('HAPROXY parsing failed for version [%d] [%s] (%s)',$p,$read,hex_dump($x)));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new HAproxyException(sprintf('HAPROXY version [%d] is not handled [%s]',$p,$read));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->port_remote = $src_port;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// Version/Command
|
||||||
|
$vc = $this->read_ch(5);
|
||||||
|
|
||||||
|
if (($x=($vc>>4)&0x7) !== 2)
|
||||||
|
throw new HAproxyException(sprintf('Unknown HAPROXY version [%d]',$x));
|
||||||
|
|
||||||
|
switch ($x=($vc&0x7)) {
|
||||||
|
// HAPROXY internal
|
||||||
|
case 0:
|
||||||
|
throw new HAproxyException('HAPROXY internal health-check');
|
||||||
|
|
||||||
|
// PROXY connection
|
||||||
|
case 1:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new HAproxyException(sprintf('HAPROXY command [%d] is not handled',$x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol/Address Family
|
||||||
|
$pa = $this->read_ch(5);
|
||||||
|
|
||||||
|
switch ($x=($pa>>4)&0x7) {
|
||||||
|
case 1: // AF_INET
|
||||||
|
$p = 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // AF_INET6
|
||||||
|
$p = 6;
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($x=($pa&0x7)) {
|
||||||
|
case 1: // STREAM
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new HAproxyException(sprintf('HAPROXY address family [%d] is not handled',$x));
|
||||||
|
}
|
||||||
|
|
||||||
|
$len = Arr::get(unpack('n',$this->read(5,2)),1);
|
||||||
|
|
||||||
|
// IPv4
|
||||||
|
if (($p === 4) && ($len === 12)) {
|
||||||
|
$src = inet_ntop($this->read(5,4));
|
||||||
|
$dst = inet_ntop($this->read(5,4));
|
||||||
|
|
||||||
|
} elseif (($p === 6) && ($len === 36)) {
|
||||||
|
$src = inet_ntop($this->read(5,16));
|
||||||
|
$dst = inet_ntop($this->read(5,16));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new HAproxyException(sprintf('HAPROXY address len [%d:%d] is not handled',$p,$len));
|
||||||
|
}
|
||||||
|
|
||||||
|
$src_port = unpack('n',$this->read(5,2));
|
||||||
|
$dst_port = Arr::get(unpack('n',$this->read(5,2)),1);
|
||||||
|
|
||||||
|
$this->port_remote = Arr::get($src_port,1);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Log::error(sprintf('%s:! HAPROXY command [%d] is not handled',self::LOGKEY,$x));
|
throw new HAproxyException('Failed to initialise HAPROXY connection');
|
||||||
|
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY command');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protocol/Address Family
|
|
||||||
$pa = $this->read_ch(5);
|
|
||||||
$p = NULL;
|
|
||||||
|
|
||||||
switch ($x=($pa>>4)&0x7) {
|
|
||||||
case 1: // AF_INET
|
|
||||||
$p = 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // AF_INET6
|
|
||||||
$p = 6;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Log::error(sprintf('%s:! HAPROXY protocol [%d] is not handled',self::LOGKEY,$x));
|
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY protocol');
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($x=($pa&0x7)) {
|
|
||||||
case 1: // STREAM
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Log::error(sprintf('%s:! HAPROXY address family [%d] is not handled',self::LOGKEY,$x));
|
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY address family');
|
|
||||||
}
|
|
||||||
|
|
||||||
$len = Arr::get(unpack('n',$this->read(5,2)),1);
|
|
||||||
|
|
||||||
// IPv4
|
|
||||||
if (($p === 4) && ($len === 12)) {
|
|
||||||
$src = inet_ntop($this->read(5,4));
|
|
||||||
$dst = inet_ntop($this->read(5,4));
|
|
||||||
|
|
||||||
} elseif (($p === 6) && ($len === 36)) {
|
|
||||||
$src = inet_ntop($this->read(5,16));
|
|
||||||
$dst = inet_ntop($this->read(5,16));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log::error(sprintf('%s:! HAPROXY address len [%d:%d] is not handled',self::LOGKEY,$p,$len));
|
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY address length');
|
|
||||||
}
|
|
||||||
|
|
||||||
$src_port = unpack('n',$this->read(5,2));
|
|
||||||
$dst_port = unpack('n',$this->read(5,2));
|
|
||||||
|
|
||||||
$this->address_remote = $src;
|
$this->address_remote = $src;
|
||||||
$this->port_remote = Arr::get($src_port,1);
|
|
||||||
|
|
||||||
Log::info(sprintf('%s:! HAPROXY src [%s:%d] dst [%s:%d]',
|
Log::debug(sprintf('%s:- HAPROXY src [%s:%d] dst [%s:%d]',
|
||||||
self::LOGKEY,
|
self::LOGKEY,
|
||||||
$this->address_remote,
|
$this->address_remote,
|
||||||
$this->port_remote,
|
$this->port_remote,
|
||||||
$dst,
|
$dst,
|
||||||
Arr::get($dst_port,1),
|
$dst_port,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info(sprintf('%s:+ Connection host [%s] on port [%d] (%s)',self::LOGKEY,$this->address_remote,$this->port_remote,$this->type));
|
Log::debug(sprintf('%s:+ Connection host [%s] on port [%d] (%s)',self::LOGKEY,$this->address_remote,$this->port_remote,$this->type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __get($key) {
|
public function __get(string $key): mixed
|
||||||
switch ($key) {
|
{
|
||||||
case 'address_remote':
|
return match ($key) {
|
||||||
case 'port_remote':
|
'address_remote', 'port_remote' => $this->{$key},
|
||||||
return $this->{$key};
|
'cps', 'speed' => Arr::get($this->session,$key),
|
||||||
|
'iac_bin' => Arr::get($this->session,$key),
|
||||||
case 'cps':
|
'rx_free' => self::RX_BUF_SIZE-$this->rx_left,
|
||||||
case 'speed':
|
'rx_left' => strlen($this->rx_buf),
|
||||||
return Arr::get($this->session,$key);
|
'tx_free' => self::TX_BUF_SIZE-strlen($this->tx_buf),
|
||||||
|
'type' => socket_get_option($this->connection,SOL_SOCKET,SO_TYPE),
|
||||||
case 'rx_free':
|
default => throw new \Exception(sprintf('%s:! Unknown key [%s]:',self::LOGKEY, $key)),
|
||||||
return self::RX_BUF_SIZE-$this->rx_left;
|
};
|
||||||
|
|
||||||
case 'rx_left':
|
|
||||||
return strlen($this->rx_buf);
|
|
||||||
|
|
||||||
case 'tx_free':
|
|
||||||
return self::TX_BUF_SIZE-strlen($this->tx_buf);
|
|
||||||
|
|
||||||
case 'type':
|
|
||||||
return socket_get_option($this->connection,SOL_SOCKET,SO_TYPE);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new \Exception(sprintf('%s:! Unknown key [%s]:',self::LOGKEY,$key));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __set($key,$value) {
|
public function __set(string $key,mixed $value): void
|
||||||
|
{
|
||||||
switch ($key) {
|
switch ($key) {
|
||||||
case 'cps':
|
case 'cps':
|
||||||
case 'speed':
|
case 'speed':
|
||||||
return $this->session[$key] = $value;
|
case 'iac_bin':
|
||||||
|
$this->session[$key] = $value;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new \Exception(sprintf('%s:! Unknown key [%s]:',self::LOGKEY,$key));
|
throw new \Exception(sprintf('%s:! Unknown key [%s]:',self::LOGKEY,$key));
|
||||||
@@ -193,13 +226,14 @@ final class SocketClient {
|
|||||||
* @param string $address
|
* @param string $address
|
||||||
* @param int $port
|
* @param int $port
|
||||||
* @return static
|
* @return static
|
||||||
* @throws SocketException
|
* @throws SocketException|HAproxyException
|
||||||
*/
|
*/
|
||||||
public static function create(string $address,int $port): self
|
public static function create(string $address,int $port): self
|
||||||
{
|
{
|
||||||
Log::info(sprintf('%s:+ Creating connection to [%s:%d]',self::LOGKEY,$address,$port));
|
Log::info(sprintf('%s:+ Creating connection to [%s:%d]',self::LOGKEY,$address,$port));
|
||||||
|
|
||||||
$sort = collect(['AAAA','A']);
|
$type = collect(config('fido.ip'))
|
||||||
|
->filter(fn($item)=>$item['enabled']);
|
||||||
|
|
||||||
if (filter_var($address,FILTER_VALIDATE_IP))
|
if (filter_var($address,FILTER_VALIDATE_IP))
|
||||||
$resolved = collect([[
|
$resolved = collect([[
|
||||||
@@ -208,14 +242,15 @@ final class SocketClient {
|
|||||||
]]);
|
]]);
|
||||||
else
|
else
|
||||||
// We only look at AAAA/A records
|
// We only look at AAAA/A records
|
||||||
$resolved = collect(dns_get_record($address,DNS_AAAA|DNS_A))
|
$resolved = collect(dns_get_record($address,$type->map(fn($item)=>$item['type'])->sum()))
|
||||||
->filter(function($item) use ($sort) { return $sort->search(Arr::get($item,'type')) !== FALSE; })
|
->filter(fn($item)=>$type->has(Arr::get($item,'type')))
|
||||||
->sort(function($item) use ($sort) { return $sort->search(Arr::get($item,'type')); });
|
->sort(fn($a,$b)=>$type->get(Arr::get($a,'type'))['order'] < $type->get(Arr::get($b,'type'))['order']);
|
||||||
|
|
||||||
if (! $resolved->count())
|
if (! $resolved->count())
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,sprintf('%s doesnt resolved to an IPv4/IPv6 address',$address));
|
throw new SocketException(SocketException::CANT_CONNECT,sprintf('%s doesnt resolved to an IPv4/IPv6 address',$address));
|
||||||
|
|
||||||
$result = FALSE;
|
$result = FALSE;
|
||||||
|
$socket = NULL;
|
||||||
|
|
||||||
foreach ($resolved as $address) {
|
foreach ($resolved as $address) {
|
||||||
try {
|
try {
|
||||||
@@ -245,7 +280,7 @@ final class SocketClient {
|
|||||||
if ($result === FALSE)
|
if ($result === FALSE)
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,socket_strerror(socket_last_error($socket)));
|
throw new SocketException(SocketException::CANT_CONNECT,socket_strerror(socket_last_error($socket)));
|
||||||
|
|
||||||
return new self($socket);
|
return new self($socket,TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -306,7 +341,7 @@ final class SocketClient {
|
|||||||
while (strlen($this->tx_buf)) {
|
while (strlen($this->tx_buf)) {
|
||||||
$tv = $this->timer_rest($tm);
|
$tv = $this->timer_rest($tm);
|
||||||
|
|
||||||
if (($rc=$this->canSend($tv)) > 0) {
|
if ($rc=$this->canSend($tv)) {
|
||||||
if (self::DEBUG)
|
if (self::DEBUG)
|
||||||
Log::debug(sprintf('%s:- Chars to send [%d]',self::LOGKEY,strlen($this->tx_buf)));
|
Log::debug(sprintf('%s:- Chars to send [%d]',self::LOGKEY,strlen($this->tx_buf)));
|
||||||
|
|
||||||
@@ -334,14 +369,14 @@ final class SocketClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $timeout
|
* @param int $timeout
|
||||||
* @return int
|
* @return bool
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function canSend(int $timeout): int
|
public function canSend(int $timeout): bool
|
||||||
{
|
{
|
||||||
$write = [$this->connection];
|
$write = [$this->connection];
|
||||||
|
|
||||||
return $this->socketSelect(NULL,$write,NULL,$timeout);
|
return $this->socketSelect(NULL,$write,NULL,$timeout) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -361,21 +396,21 @@ final class SocketClient {
|
|||||||
Log::error(sprintf('%s:! Closing socket [%s]',self::LOGKEY,$e->getMessage()));
|
Log::error(sprintf('%s:! Closing socket [%s]',self::LOGKEY,$e->getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info(sprintf('%s:= Connection closed with [%s]',self::LOGKEY,$this->address_remote));
|
Log::debug(sprintf('%s:= Connection closed with [%s]',self::LOGKEY,$this->address_remote));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We have data in the buffer or on the socket
|
* We have data in the buffer or on the socket
|
||||||
*
|
*
|
||||||
* @param int $timeout
|
* @param int $timeout
|
||||||
* @return int
|
* @return bool
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function hasData(int $timeout): int
|
public function hasData(int $timeout): bool
|
||||||
{
|
{
|
||||||
$read = [$this->connection];
|
$read = [$this->connection];
|
||||||
|
|
||||||
return $this->rx_left ?: $this->socketSelect($read,NULL,NULL,$timeout);
|
return ($this->rx_left ?: $this->socketSelect($read,NULL,NULL,$timeout)) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -383,10 +418,11 @@ final class SocketClient {
|
|||||||
*
|
*
|
||||||
* @param int $timeout How long to wait for data
|
* @param int $timeout How long to wait for data
|
||||||
* @param int $len The amount of data we want
|
* @param int $len The amount of data we want
|
||||||
|
* @param int $flags
|
||||||
* @return string|null
|
* @return string|null
|
||||||
* @throws SocketException
|
* @throws SocketException
|
||||||
*/
|
*/
|
||||||
public function read(int $timeout,int $len=1024): ?string
|
public function read(int $timeout,int $len=1024,int $flags=MSG_DONTWAIT): ?string
|
||||||
{
|
{
|
||||||
// We have data in our buffer
|
// We have data in our buffer
|
||||||
if ($this->rx_left >= $len) {
|
if ($this->rx_left >= $len) {
|
||||||
@@ -394,24 +430,58 @@ final class SocketClient {
|
|||||||
Log::debug(sprintf('%s:- Returning [%d] chars from the RX buffer',self::LOGKEY,$len));
|
Log::debug(sprintf('%s:- Returning [%d] chars from the RX buffer',self::LOGKEY,$len));
|
||||||
|
|
||||||
$result = substr($this->rx_buf,0,$len);
|
$result = substr($this->rx_buf,0,$len);
|
||||||
$this->rx_buf = substr($this->rx_buf,strlen($result));
|
if ($flags !== MSG_PEEK)
|
||||||
|
$this->rx_buf = substr($this->rx_buf,strlen($result));
|
||||||
|
|
||||||
return $result;
|
// In case we are in Telnet Binary Mode
|
||||||
|
if ($this->iac_bin) {
|
||||||
|
if (self::DEBUG)
|
||||||
|
Log::debug(sprintf('%s:- Telnet IAC Binary Mode, looking for ff ff',self::LOGKEY),['result'=>hex_dump($result)]);
|
||||||
|
|
||||||
|
// if the last char is ff, we need to get the next char
|
||||||
|
if (str_ends_with($result,"\xff")) {
|
||||||
|
if (self::DEBUG)
|
||||||
|
Log::debug(sprintf('%s: - We have a hit',self::LOGKEY));
|
||||||
|
|
||||||
|
// If we have it in our buffer, just get it
|
||||||
|
if ($this->rx_left) {
|
||||||
|
$result .= substr($this->rx_buf,0,1);
|
||||||
|
$this->rx_buf = substr($this->rx_buf,1);
|
||||||
|
|
||||||
|
// Else put everything back into rx_buf, and increase len by 1
|
||||||
|
} else {
|
||||||
|
$this->rx_buf = $result;
|
||||||
|
$len++;
|
||||||
|
$result = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($result) > 1)
|
||||||
|
$result = str_replace("\xff\xff","\xff",$result);
|
||||||
|
|
||||||
|
if (strlen($result))
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
} else
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($timeout AND ($this->hasData($timeout) === 0))
|
if (self::DEBUG)
|
||||||
return NULL;
|
Log::debug(sprintf('%s:- Buffer doesnt have [%d] chars, it only has [%d], or it ends with 0xff',self::LOGKEY,$len,strlen($this->rx_buf)),['rx_buf'=>hex_dump($this->rx_buf)]);
|
||||||
|
|
||||||
|
if ($timeout && (! $this->hasData($timeout)))
|
||||||
|
throw new SocketException(SocketException::SOCKET_TIMEOUT,$timeout);
|
||||||
|
|
||||||
$buf = '';
|
$buf = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch ($this->type) {
|
switch ($this->type) {
|
||||||
case SOCK_STREAM:
|
case SOCK_STREAM:
|
||||||
$recv = socket_recv($this->connection,$buf,self::RX_SIZE,MSG_DONTWAIT);
|
$recv = socket_recv($this->connection,$buf,self::RX_SIZE,$flags);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SOCK_DGRAM:
|
case SOCK_DGRAM:
|
||||||
$recv = socket_recvfrom($this->connection,$buf,self::RX_SIZE,MSG_DONTWAIT,$this->address_remote,$this->port_remote);
|
$recv = socket_recvfrom($this->connection,$buf,self::RX_SIZE,$flags,$this->address_remote,$this->port_remote);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -452,13 +522,19 @@ final class SocketClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($flags === MSG_PEEK) {
|
||||||
|
Log::debug(sprintf('%s:- Returning [%d] chars as a result of a PEEK operation, buffer would have [%d], but still has [%d]',self::LOGKEY,$len,strlen($this->rx_buf.$buf),strlen($this->rx_buf)),['rx_buf'=>hex_dump($this->rx_buf),'buf'=>hex_dump($buf)]);
|
||||||
|
|
||||||
|
return substr($this->rx_buf.$buf,0,$len);
|
||||||
|
}
|
||||||
|
|
||||||
$this->rx_buf .= $buf;
|
$this->rx_buf .= $buf;
|
||||||
|
|
||||||
if (self::DEBUG)
|
if (self::DEBUG)
|
||||||
Log::debug(sprintf('%s:- Added [%d] chars to the RX buffer',self::LOGKEY,strlen($buf)),['rx_buf'=>hex_dump($this->rx_buf)]);
|
Log::debug(sprintf('%s:- Added [%d] chars to the RX buffer',self::LOGKEY,strlen($buf)),['rx_buf'=>hex_dump($this->rx_buf)]);
|
||||||
|
|
||||||
// Loop again and return the data, now that it is in the RX buffer
|
// Loop again and return the data, now that it is in the RX buffer
|
||||||
return $this->read($timeout,$len);
|
return $this->read($timeout,$len,$flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -471,12 +547,14 @@ final class SocketClient {
|
|||||||
*/
|
*/
|
||||||
public function read_ch(int $timeout): int
|
public function read_ch(int $timeout): int
|
||||||
{
|
{
|
||||||
if ($this->hasData($timeout) > 0) {
|
if ($this->hasData($timeout))
|
||||||
$ch = $this->read($timeout,1);
|
$ch = $this->read($timeout,1);
|
||||||
|
|
||||||
} else {
|
else
|
||||||
return self::TIMEOUT;
|
throw new SocketException(SocketException::SOCKET_TIMEOUT,$timeout);
|
||||||
}
|
|
||||||
|
if (self::DEBUG)
|
||||||
|
Log::debug(sprintf('%s:+ read_ch [%c] (%x)',self::LOGKEY,$ch,ord($ch)));
|
||||||
|
|
||||||
return ord($ch);
|
return ord($ch);
|
||||||
}
|
}
|
||||||
@@ -505,17 +583,22 @@ final class SocketClient {
|
|||||||
*
|
*
|
||||||
* @param string $message
|
* @param string $message
|
||||||
* @param int $timeout
|
* @param int $timeout
|
||||||
* @return int|false
|
* @return int|bool
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function send(string $message,int $timeout): int|false
|
public function send(string $message,int $timeout): int|bool
|
||||||
{
|
{
|
||||||
if ($timeout AND (! $rc=$this->canSend($timeout)))
|
if ($timeout && (! $rc=$this->canSend($timeout)))
|
||||||
return $rc;
|
return $rc;
|
||||||
|
|
||||||
if (self::DEBUG)
|
if (self::DEBUG)
|
||||||
Log::debug(sprintf('%s:- Sending [%d] chars [%s]',self::LOGKEY,strlen($message),Str::limit($message,15)));
|
Log::debug(sprintf('%s:- Sending [%d] chars [%s]',self::LOGKEY,strlen($message),Str::limit($message,15)));
|
||||||
|
|
||||||
|
if ($this->iac_bin) {
|
||||||
|
Log::debug(sprintf('%s:- IAC_BIN mode, looking for 0xff',self::LOGKEY));
|
||||||
|
$message = str_replace("\xff","\xff\xff",$message);
|
||||||
|
}
|
||||||
|
|
||||||
switch ($this->type) {
|
switch ($this->type) {
|
||||||
case SOCK_STREAM:
|
case SOCK_STREAM:
|
||||||
return socket_write($this->connection,$message,strlen($message));
|
return socket_write($this->connection,$message,strlen($message));
|
||||||
|
@@ -4,6 +4,8 @@ namespace App\Classes\Sock;
|
|||||||
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Sock\Exception\{HAproxyException,SocketException};
|
||||||
|
|
||||||
final class SocketServer {
|
final class SocketServer {
|
||||||
private const LOGKEY = 'SS-';
|
private const LOGKEY = 'SS-';
|
||||||
|
|
||||||
@@ -125,16 +127,27 @@ final class SocketServer {
|
|||||||
if (($accept = socket_accept($this->server)) === FALSE)
|
if (($accept = socket_accept($this->server)) === FALSE)
|
||||||
throw new SocketException(SocketException::CANT_ACCEPT,socket_strerror(socket_last_error($this->server)));
|
throw new SocketException(SocketException::CANT_ACCEPT,socket_strerror(socket_last_error($this->server)));
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:* TCP Loop Start',self::LOGKEY));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$r = new SocketClient($accept);
|
$r = new SocketClient($accept);
|
||||||
|
|
||||||
|
} catch (HAproxyException $e) {
|
||||||
|
Log::notice(sprintf('%s:! HAPROXY Exception [%s]',self::LOGKEY,$e->getMessage()));
|
||||||
|
socket_close($accept);
|
||||||
|
continue;
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
Log::error(sprintf('%s:! Creating Socket client failed? [%s]',self::LOGKEY,$e->getMessage()));
|
Log::notice(sprintf('%s:! Creating Socket client failed? [%s]',self::LOGKEY,$e->getMessage()));
|
||||||
socket_close($accept);
|
socket_close($accept);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->handler[0]->{$this->handler[1]}($r);
|
// If the handler returns a value, then that is the main thread
|
||||||
|
if (! $this->handler[0]->{$this->handler[1]}($r)) {
|
||||||
|
$r->close();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +157,8 @@ final class SocketServer {
|
|||||||
$r = new SocketClient($this->server);
|
$r = new SocketClient($this->server);
|
||||||
|
|
||||||
if ($r->hasData(30)) {
|
if ($r->hasData(30)) {
|
||||||
$this->handler[0]->{$this->handler[1]}($r);
|
if (! ($this->handler[0]->{$this->handler[1]}($r)))
|
||||||
|
exit(0);
|
||||||
|
|
||||||
// Sleep so our thread has a chance to pick up the data from our connection
|
// Sleep so our thread has a chance to pick up the data from our connection
|
||||||
usleep(50000);
|
usleep(50000);
|
||||||
|
42
app/Console/Commands/AddressClearQueue.php
Normal file
42
app/Console/Commands/AddressClearQueue.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
use App\Jobs\AddressClearQueue as Job;
|
||||||
|
use App\Models\Address;
|
||||||
|
|
||||||
|
class AddressClearQueue extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'address:clear:queue'
|
||||||
|
.' {ftn : FTN}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Clear up anything queued for an FTN';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
$ao = Address::findFTN($this->argument('ftn'),TRUE,TRUE);
|
||||||
|
|
||||||
|
if (! $ao) {
|
||||||
|
$this->error('FTN not found: '.$this->argument('ftn'));
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Job::dispatchSync($ao);
|
||||||
|
}
|
||||||
|
}
|
@@ -30,7 +30,7 @@ class AddressIdle extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$do = Domain::where('name',$this->argument('domain'))->singleOrFail();
|
$do = Domain::where('name',$this->argument('domain'))->sole();
|
||||||
|
|
||||||
return Job::dispatchSync($do,$this->option('ftn') ? Address::findFTN($this->option('ftn')) : NULL);
|
return Job::dispatchSync($do,$this->option('ftn') ? Address::findFTN($this->option('ftn')) : NULL);
|
||||||
}
|
}
|
||||||
|
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands\Areafix;
|
namespace App\Console\Commands\Areafix;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
use App\Models\{Address,Echoarea,Echomail};
|
use App\Jobs\AreafixRescan;
|
||||||
|
use App\Models\{Address,Echoarea};
|
||||||
|
|
||||||
class Rescan extends Command
|
class Rescan extends Command
|
||||||
{
|
{
|
||||||
@@ -14,7 +14,13 @@ class Rescan extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'areafix:rescan {ftn} {area} {days?}';
|
protected $signature = 'areafix:rescan'
|
||||||
|
.' {ftn : FTN Address}'
|
||||||
|
.' {area : Echoarea Tag}'
|
||||||
|
.' {days? : Limit to messages authored days ago}'
|
||||||
|
.' {--j|queue : Queue the Job}'
|
||||||
|
.' {--Q|queuename=default : Queue on queue}'
|
||||||
|
.' {--R|export : Re-export previously sent messages}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -43,50 +49,15 @@ class Rescan extends Command
|
|||||||
if (! $this->argument('area'))
|
if (! $this->argument('area'))
|
||||||
throw new \Exception('Areaname is required');
|
throw new \Exception('Areaname is required');
|
||||||
|
|
||||||
$eao = Echoarea::where('name',$this->argument('area'))->singleOrFail();
|
$eo = Echoarea::where('name',$this->argument('area'))->sole();
|
||||||
if ($eao->domain_id !== $ao->zone->domain_id)
|
|
||||||
throw new \Exception(sprintf('Echo area [%s] is not in domain [%s] for FTN [%s]',$eao->name,$ao->zone->domain->name,$ao->ftn));
|
|
||||||
|
|
||||||
// Check that the user is subscribed
|
if ($eo->domain_id !== $ao->zone->domain_id)
|
||||||
if (! $ao->echoareas->contains($eao->id))
|
throw new \Exception(sprintf('Echo area [%s] is not in domain [%s] for FTN [%s]',$eo->name,$ao->zone->domain->name,$ao->ftn));
|
||||||
throw new \Exception(sprintf('FTN [%s] is not subscribed to [%s]',$ao->ftn,$eao->name));
|
|
||||||
|
|
||||||
// Check that an FTN can read the area
|
if ($this->option('queue'))
|
||||||
if (! $eao->can_read($ao->security))
|
AreafixRescan::dispatch($ao,$eo,$this->argument('days'))->onQueue($this->option('queuename'));
|
||||||
throw new \Exception(sprintf('FTN [%s] doesnt have permission to receive [%s]',$ao->ftn,$eao->name));
|
else
|
||||||
|
AreafixRescan::dispatchSync($ao,$eo,$this->argument('days'));
|
||||||
foreach (Echomail::select('id')
|
|
||||||
->where('echoarea_id',$eao->id)
|
|
||||||
->when($this->argument('days'),function($query) {
|
|
||||||
return $query->where('created_at','>=',Carbon::now()->subDays($this->argument('days'))->startOfDay());
|
|
||||||
})
|
|
||||||
->orderBy('datetime')
|
|
||||||
->cursor() as $eo) {
|
|
||||||
|
|
||||||
// Echomail hasnt been exported before
|
|
||||||
if (! $eo->seenby->count()) {
|
|
||||||
$eo->seenby()->attach($ao->id,['export_at'=>Carbon::now()]);
|
|
||||||
$this->info(sprintf('Exported [%d] to [%s]',$eo->id,$ao->ftn3d));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$export = $eo->seenby->where('id',$ao->id)->pop();
|
|
||||||
|
|
||||||
// Echomail is pending export
|
|
||||||
if ($export && $export->pivot->export_at && is_null($export->pivot->sent_at) && is_null($export->pivot->sent_pkt)) {
|
|
||||||
$this->warn(sprintf('Not exporting [%d] already queued for [%s]',$eo->id,$ao->ftn3d));
|
|
||||||
|
|
||||||
// Echomail has been exported
|
|
||||||
} elseif ($export) {
|
|
||||||
$eo->seenby()->updateExistingPivot($ao,['export_at'=>Carbon::now(),'sent_at'=>NULL,'sent_pkt'=>NULL]);
|
|
||||||
$this->info(sprintf('Re-exported [%d] to [%s]',$eo->id,$ao->ftn3d));
|
|
||||||
|
|
||||||
// Echomail has not been exported
|
|
||||||
} else {
|
|
||||||
$eo->seenby()->attach($ao,['export_at'=>Carbon::now(),'sent_at'=>NULL,'sent_pkt'=>NULL]);
|
|
||||||
$this->info(sprintf('Exported [%d] to [%s]',$eo->id,$ao->ftn3d));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
return self::SUCCESS;
|
||||||
}
|
}
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
use App\Classes\Protocol\Binkp;
|
|
||||||
use App\Classes\Sock\SocketException;
|
|
||||||
use App\Classes\Sock\SocketServer;
|
|
||||||
use App\Models\Setup;
|
|
||||||
|
|
||||||
class CommBinkpReceive extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'comm:binkp:receive';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'BINKP receive';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
* @throws SocketException
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
Log::info('Listening for BINKP connections...');
|
|
||||||
$o = Setup::findOrFail(config('app.id'));
|
|
||||||
|
|
||||||
$server = new SocketServer($o->binkp_port,$o->binkp_bind);
|
|
||||||
$server->handler = [new Binkp($o),'onConnect'];
|
|
||||||
|
|
||||||
try {
|
|
||||||
$server->listen();
|
|
||||||
|
|
||||||
} catch (SocketException $e) {
|
|
||||||
if ($e->getMessage() === 'Can\'t accept connections: "Success"')
|
|
||||||
Log::debug('Server Terminated');
|
|
||||||
else
|
|
||||||
Log::emergency('Uncaught Message: '.$e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -16,7 +16,9 @@ class CommBinkpSend extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'comm:binkp:send {ftn : FTN to Send to}';
|
protected $signature = 'comm:binkp:send'
|
||||||
|
.' {--N|now : Dont queue}'
|
||||||
|
.' {ftn : FTN to Send to}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -25,14 +27,12 @@ class CommBinkpSend extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'BINKP send';
|
protected $description = 'BINKP send';
|
||||||
|
|
||||||
private const ID = 'BINKP';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function handle(): void
|
public function handle()
|
||||||
{
|
{
|
||||||
$ao = Address::findFTN($this->argument('ftn'));
|
$ao = Address::findFTN($this->argument('ftn'));
|
||||||
if (! $ao)
|
if (! $ao)
|
||||||
@@ -40,8 +40,13 @@ class CommBinkpSend extends Command
|
|||||||
|
|
||||||
Log::info(sprintf('CBS:- Call BINKP send for %s',$ao->ftn));
|
Log::info(sprintf('CBS:- Call BINKP send for %s',$ao->ftn));
|
||||||
|
|
||||||
$mo = Mailer::where('name',self::ID)->singleOrFail();
|
$mo = Mailer::where('name','BINKP')->sole();
|
||||||
|
|
||||||
Job::dispatch($ao,$mo);
|
if ($this->option('now'))
|
||||||
|
Job::dispatchSync($ao,$mo);
|
||||||
|
else
|
||||||
|
Job::dispatch($ao,$mo);
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
use App\Classes\Protocol\EMSI;
|
|
||||||
use App\Classes\Sock\SocketException;
|
|
||||||
use App\Classes\Sock\SocketServer;
|
|
||||||
use App\Models\Setup;
|
|
||||||
|
|
||||||
class CommEMSIReceive extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'comm:emsi:receive';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'EMSI receive';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
Log::info('Listening for EMSI connections...');
|
|
||||||
$o = Setup::findOrFail(config('app.id'));
|
|
||||||
|
|
||||||
$server = new SocketServer($o->emsi_port,$o->emsi_bind);
|
|
||||||
$server->handler = [new EMSI($o),'onConnect'];
|
|
||||||
|
|
||||||
try {
|
|
||||||
$server->listen();
|
|
||||||
|
|
||||||
} catch (SocketException $e) {
|
|
||||||
if ($e->getMessage() === 'Can\'t accept connections: "Success"')
|
|
||||||
Log::debug('Server Terminated');
|
|
||||||
else
|
|
||||||
Log::emergency('Uncaught Message: '.$e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -16,7 +16,9 @@ class CommEMSISend extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'comm:emsi:send {ftn : FTN to Send to}';
|
protected $signature = 'comm:emsi:send'
|
||||||
|
.' {--N|now : Dont queue}'
|
||||||
|
.' {ftn : FTN to Send to}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -25,14 +27,12 @@ class CommEMSISend extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'EMSI send';
|
protected $description = 'EMSI send';
|
||||||
|
|
||||||
private const ID = 'EMSI';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function handle(): void
|
public function handle()
|
||||||
{
|
{
|
||||||
$ao = Address::findFTN($this->argument('ftn'));
|
$ao = Address::findFTN($this->argument('ftn'));
|
||||||
if (! $ao)
|
if (! $ao)
|
||||||
@@ -40,8 +40,13 @@ class CommEMSISend extends Command
|
|||||||
|
|
||||||
Log::info(sprintf('CES:- Call EMSI send for %s',$ao->ftn));
|
Log::info(sprintf('CES:- Call EMSI send for %s',$ao->ftn));
|
||||||
|
|
||||||
$mo = Mailer::where('name',self::ID)->singleOrFail();
|
$mo = Mailer::where('name','EMSI')->sole();
|
||||||
|
|
||||||
Job::dispatch($ao,$mo);
|
if ($this->option('now'))
|
||||||
|
Job::dispatchSync($ao,$mo);
|
||||||
|
else
|
||||||
|
Job::dispatch($ao,$mo);
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ class AddressCheck extends Command
|
|||||||
|
|
||||||
$this->info(sprintf('Address: %s (%s)',$o->ftn,$o->role_name));
|
$this->info(sprintf('Address: %s (%s)',$o->ftn,$o->role_name));
|
||||||
$this->info(sprintf("Children: \n- %s",$o->children()->pluck('ftn4d')->join("\n- ")));
|
$this->info(sprintf("Children: \n- %s",$o->children()->pluck('ftn4d')->join("\n- ")));
|
||||||
$this->info(sprintf("Downstream: \n- %s",$o->downstream()->pluck('ftn4d')->join("\n- ")));
|
$this->info(sprintf("Downlinks: \n- %s",$o->downlinks()->pluck('ftn4d')->join("\n- ")));
|
||||||
$this->info(sprintf('Uplink: %s (Parent: %s)',$o->uplink()?->ftn,$o->parent()?->ftn));
|
$this->info(sprintf('Uplink: %s (Parent: %s)',$o->uplink()?->ftn,$o->parent()?->ftn));
|
||||||
$this->info(sprintf('Our Address: %s',our_address($o)?->ftn));
|
$this->info(sprintf('Our Address: %s',our_address($o)?->ftn));
|
||||||
$this->info(sprintf('- Domain Addresses: %s',our_address($o->zone->domain)->pluck('ftn4d')->join(',')));
|
$this->info(sprintf('- Domain Addresses: %s',our_address($o->zone->domain)->pluck('ftn4d')->join(',')));
|
||||||
|
64
app/Console/Commands/Debug/AddressCheckNode.php
Normal file
64
app/Console/Commands/Debug/AddressCheckNode.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands\Debug;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
use App\Models\Address;
|
||||||
|
|
||||||
|
class AddressCheckNode extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'debug:address:check:nodes'
|
||||||
|
.' {ftn? : FTN}'
|
||||||
|
.' {--N|node : Node Order}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Check all addresses we use for nodes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
$ao = NULL;
|
||||||
|
|
||||||
|
if ($this->argument('ftn')) {
|
||||||
|
$ao = Address::findFTN($this->argument('ftn'));
|
||||||
|
|
||||||
|
if (! $ao) {
|
||||||
|
$this->error('FTN not found: ' .$this->argument('ftn'));
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info('our address:'.our_address($ao)->ftn);
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->table(['System','Node','Ours'],
|
||||||
|
our_nodes($ao ? $ao->domain : NULL)
|
||||||
|
->sortBy(fn($item)=>$this->option('node')
|
||||||
|
? sprintf('%s:%s',$item->system->name,$item->domain->name)
|
||||||
|
: sprintf('%s',$item->domain->name))
|
||||||
|
->map(fn($item)=>
|
||||||
|
[
|
||||||
|
'System'=>$item->system->name,
|
||||||
|
'Node'=>$item->ftn.' '.($item->echoareas->count() ? '^' : '').($item->fileareas->count() ? '*' : ''),
|
||||||
|
'Ours'=>our_address($item)?->ftn,
|
||||||
|
]));
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
@@ -12,8 +12,7 @@ use App\Models\Dynamic as DynamicModel;
|
|||||||
class DynamicItem extends Command
|
class DynamicItem extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'debug:dynamic:item'
|
protected $signature = 'debug:dynamic:item'
|
||||||
.' {name : Dynamic Item}'
|
.' {name : Dynamic Item}';
|
||||||
.' {ftn : FTN Address}';
|
|
||||||
|
|
||||||
protected $description = 'Generate a dynamic item';
|
protected $description = 'Generate a dynamic item';
|
||||||
|
|
||||||
@@ -24,12 +23,12 @@ class DynamicItem extends Command
|
|||||||
if (! $do)
|
if (! $do)
|
||||||
throw new \Exception(sprintf('Dynamic Item [%s] doesnt exist?',$this->argument('name')));
|
throw new \Exception(sprintf('Dynamic Item [%s] doesnt exist?',$this->argument('name')));
|
||||||
|
|
||||||
$ao = Address::findFTN($this->argument('ftn'));
|
$d = new Dynamic($do,$do->address,Send::T_FILE);
|
||||||
|
|
||||||
$d = new Dynamic($do,$ao,Send::T_FILE);
|
|
||||||
|
|
||||||
$d->open();
|
$d->open();
|
||||||
echo $d->read($d->size);
|
echo $d->read($d->size)."\n";
|
||||||
|
|
||||||
|
$this->alert('File sent as:'.$d->nameas);
|
||||||
|
|
||||||
return self::SUCCESS;
|
return self::SUCCESS;
|
||||||
}
|
}
|
||||||
|
@@ -16,8 +16,7 @@ class PacketDump extends Command
|
|||||||
protected $signature = 'debug:packet:dump'.
|
protected $signature = 'debug:packet:dump'.
|
||||||
' {type : Type of packet, netmail|echomail }'.
|
' {type : Type of packet, netmail|echomail }'.
|
||||||
' {ftn : FTN}'.
|
' {ftn : FTN}'.
|
||||||
' {file? : filename}'.
|
' {file? : filename}';
|
||||||
' {--dump : Dump packet}';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -50,11 +49,16 @@ class PacketDump extends Command
|
|||||||
throw new \Exception('Unknown type: '.$this->argument('type'));
|
throw new \Exception('Unknown type: '.$this->argument('type'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->option('dump')) {
|
if (is_null($pkt)) {
|
||||||
|
$this->info(sprintf('No packet for [%s] of type [%s]',$this->argument('ftn'),$this->argument('type')));
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->argument('file')) {
|
||||||
$this->info('Item Name:'.$pkt->name);
|
$this->info('Item Name:'.$pkt->name);
|
||||||
$this->info('Item Type:'.get_class($pkt));
|
$this->info('Item Type:'.get_class($pkt));
|
||||||
$this->info('Dump:');
|
$this->info('Dump:');
|
||||||
echo hex_dump($pkt);
|
echo hex_dump((string)$pkt);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$f = fopen($this->argument('file'),'w+');
|
$f = fopen($this->argument('file'),'w+');
|
||||||
|
@@ -16,7 +16,7 @@ class ZoneCheck extends Command
|
|||||||
|
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$do = Domain::where('name',$this->argument('domain'))->singleOrFail();
|
$do = Domain::where('name',$this->argument('domain'))->sole();
|
||||||
|
|
||||||
foreach ($do->zones->sortby('zone_id') as $zo) {
|
foreach ($do->zones->sortby('zone_id') as $zo) {
|
||||||
if ($this->option('zone') && ($this->option('zone') != $zo->zone_id))
|
if ($this->option('zone') && ($this->option('zone') != $zo->zone_id))
|
||||||
@@ -25,21 +25,22 @@ class ZoneCheck extends Command
|
|||||||
$this->warn('Zone: '.$zo->zone_id);
|
$this->warn('Zone: '.$zo->zone_id);
|
||||||
$this->info(sprintf('- Our address(es): %s',our_address($do)->pluck('ftn4d')->join(',')));
|
$this->info(sprintf('- Our address(es): %s',our_address($do)->pluck('ftn4d')->join(',')));
|
||||||
|
|
||||||
$this->table(['id','ftn','role','parent','children','downlinks','uplink','send from','region_id','system','notes'],$zo->addresses()->FTNorder()->active()->with(['system'])->dontCache()->get()->transform(function($item) {
|
$this->table(['id','region_id','ftn','role','parent','children','downlinks','uplink','send from','system','notes'],
|
||||||
return [
|
$zo->addresses()->FTN()->active()->with(['system','nodes_hub'])->get()->transform(function($item) {
|
||||||
'id'=>$item->id,
|
return [
|
||||||
'ftn'=>$item->ftn4d,
|
'id'=>$item->id,
|
||||||
'role'=>$item->role_name,
|
'region_id'=>$item->region_id,
|
||||||
'parent'=>$item->parent()?->ftn4d,
|
'ftn'=>$item->ftn4d,
|
||||||
'children'=>$item->children()->count(),
|
'role'=>$item->role_name,
|
||||||
'downlinks'=>$item->downlinks()->count(),
|
'parent'=>$item->parent()?->ftn4d,
|
||||||
'uplink'=>($x=$item->uplink())?->ftn4d,
|
'children'=>$item->children()->count(),
|
||||||
'send from'=>$x ? our_address($item->uplink())?->ftn4d : '',
|
'downlinks'=>$item->downlinks()->count(),
|
||||||
'region_id'=>$item->region_id,
|
'uplink'=>($x=$item->uplink())?->ftn4d,
|
||||||
'system'=>$item->system->name,
|
'send from'=>$x ? our_address($item->uplink())?->ftn4d : '',
|
||||||
'notes'=>$item->isRoleOverride() ? 'Role Override' : '',
|
'system'=>$item->system->name,
|
||||||
];
|
'notes'=>$item->isRoleOverride() ? 'Role Override' : '',
|
||||||
}));
|
];
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::SUCCESS;
|
return self::SUCCESS;
|
||||||
|
@@ -34,7 +34,8 @@ class EchoareaImport extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$do = Domain::where('name',strtolower($this->argument('domain')))->singleOrFail();
|
$do = Domain::where('name',strtolower($this->argument('domain')))->single();
|
||||||
return Job::dispatchSync($this->argument('file'),$do,$this->option('prefix'),$this->option('unlink'));
|
|
||||||
|
return Job::dispatchSync($this->argument('file'),$do,$this->option('prefix') ?: '',$this->option('unlink'));
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -34,7 +34,8 @@ class FileareaImport extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$do = Domain::where('name',strtolower($this->argument('domain')))->singleOrFail();
|
$do = Domain::where('name',strtolower($this->argument('domain')))->sole();
|
||||||
return Job::dispatchSync($this->argument('file'),$do,$this->option('prefix'),$this->option('unlink'));
|
|
||||||
|
return Job::dispatchSync($this->argument('file'),$do,$this->option('prefix') ?: '',$this->option('unlink'));
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands\Filefix;
|
namespace App\Console\Commands\Filefix;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
use App\Models\{Address,Filearea,File};
|
use App\Jobs\FilefixRescan;
|
||||||
|
use App\Models\{Address,Filearea};
|
||||||
|
|
||||||
class Rescan extends Command
|
class Rescan extends Command
|
||||||
{
|
{
|
||||||
@@ -14,7 +14,13 @@ class Rescan extends Command
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'filefix:rescan {ftn} {area} {file?}';
|
protected $signature = 'filefix:rescan'
|
||||||
|
.' {ftn : FTN Address}'
|
||||||
|
.' {area : Echoarea Tag}'
|
||||||
|
.' {days? : Limit to files received days ago}'
|
||||||
|
.' {--j|queue : Queue the Job}'
|
||||||
|
.' {--Q|queuename=default : Queue on queue}'
|
||||||
|
.' {--R|export : Re-export previously sent files}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@@ -31,6 +37,9 @@ class Rescan extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
|
if (($this->argument('days')) && (! is_numeric($this->argument('days'))))
|
||||||
|
throw new \Exception('Days must be numeric: '.$this->argument('days'));
|
||||||
|
|
||||||
$ao = Address::findFtn($this->argument('ftn'));
|
$ao = Address::findFtn($this->argument('ftn'));
|
||||||
|
|
||||||
if (! $ao)
|
if (! $ao)
|
||||||
@@ -40,50 +49,15 @@ class Rescan extends Command
|
|||||||
if (! $this->argument('area'))
|
if (! $this->argument('area'))
|
||||||
throw new \Exception('Areaname is required');
|
throw new \Exception('Areaname is required');
|
||||||
|
|
||||||
$fao = Filearea::where('name',$this->argument('area'))->singleOrFail();
|
$fao = Filearea::where('name',$this->argument('area'))->sole();
|
||||||
|
|
||||||
if ($fao->domain_id !== $ao->zone->domain_id)
|
if ($fao->domain_id !== $ao->zone->domain_id)
|
||||||
throw new \Exception(sprintf('File area [%s] is not in domain [%s] for FTN [%s]',$fao->name,$ao->zone->domain->name,$ao->ftn));
|
throw new \Exception(sprintf('File area [%s] is not in domain [%s] for FTN [%s]',$fao->name,$ao->zone->domain->name,$ao->ftn));
|
||||||
|
|
||||||
// Check that the user is subscribed
|
if ($this->option('queue'))
|
||||||
if (! $ao->fileareas->contains($fao->id))
|
FilefixRescan::dispatch($ao,$fao,$this->argument('days'))->onQueue($this->option('queuename'));
|
||||||
throw new \Exception(sprintf('FTN [%s] is not subscribed to [%s]',$ao->ftn,$fao->name));
|
else
|
||||||
|
FilefixRescan::dispatchSync($ao,$fao,$this->argument('days'));
|
||||||
// Check that an FTN can read the area
|
|
||||||
if (! $fao->can_read($ao->security))
|
|
||||||
throw new \Exception(sprintf('FTN [%s] doesnt have permission to receive [%s]',$ao->ftn,$fao->name));
|
|
||||||
|
|
||||||
foreach (File::select('id')
|
|
||||||
->where('filearea_id',$fao->id)
|
|
||||||
->when($this->argument('file'),function($query) {
|
|
||||||
return $query->where('name','=',$this->argument('days'));
|
|
||||||
})
|
|
||||||
->orderBy('datetime')
|
|
||||||
->cursor() as $fo) {
|
|
||||||
|
|
||||||
// File hasnt been exported before
|
|
||||||
if (! $fo->seenby->count()) {
|
|
||||||
$fo->seenby()->attach($ao->id,['export_at'=>Carbon::now()]);
|
|
||||||
$this->info(sprintf('Exported [%d] to [%s]',$fo->id,$ao->ftn3d));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$export = $fo->seenby->where('id',$ao->id)->pop();
|
|
||||||
|
|
||||||
// File is pending export
|
|
||||||
if ($export && $export->pivot->export_at && is_null($export->pivot->sent_at) && is_null($export->pivot->sent_pkt)) {
|
|
||||||
$this->warn(sprintf('Not exporting [%d] already queued for [%s]',$fo->id,$ao->ftn3d));
|
|
||||||
|
|
||||||
// File has been exported
|
|
||||||
} elseif ($export) {
|
|
||||||
$fo->seenby()->updateExistingPivot($ao,['export_at'=>Carbon::now(),'sent_at'=>NULL]);
|
|
||||||
$this->info(sprintf('Re-exported [%d] to [%s]',$fo->id,$ao->ftn3d));
|
|
||||||
|
|
||||||
// File has not been exported
|
|
||||||
} else {
|
|
||||||
$fo->seenby()->attach($ao,['export_at'=>Carbon::now(),'sent_at'=>NULL]);
|
|
||||||
$this->info(sprintf('Exported [%d] to [%s]',$fo->id,$ao->ftn3d));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::SUCCESS;
|
return self::SUCCESS;
|
||||||
}
|
}
|
||||||
|
@@ -45,12 +45,12 @@ class MailList extends Command
|
|||||||
'from' => 'FROM',
|
'from' => 'FROM',
|
||||||
'to' => 'TO',
|
'to' => 'TO',
|
||||||
'subject' => 'SUBJECT',
|
'subject' => 'SUBJECT',
|
||||||
],$ao->netmailWaiting()->map(function($item) {
|
],$ao->netmailWaiting()->get()->map(function($item) {
|
||||||
return [
|
return [
|
||||||
'id'=>$item->id,
|
'id'=>$item->id,
|
||||||
'msgid'=>$item->msgid,
|
'msgid'=>$item->msgid,
|
||||||
'from'=>$item->from,
|
'from'=>sprintf('%s (%s)',$item->from,$item->fftn->ftn3d),
|
||||||
'to'=>$item->to,
|
'to'=>sprintf('%s (%s)',$item->to,$item->tftn->ftn3d),
|
||||||
'subject'=>$item->subject,
|
'subject'=>$item->subject,
|
||||||
];
|
];
|
||||||
}));
|
}));
|
||||||
@@ -63,7 +63,7 @@ class MailList extends Command
|
|||||||
'to' => 'TO',
|
'to' => 'TO',
|
||||||
'subject' => 'SUBJECT',
|
'subject' => 'SUBJECT',
|
||||||
'area' => 'AREA',
|
'area' => 'AREA',
|
||||||
],$ao->echomailWaiting()->map(function($item) {
|
],$ao->echomailWaiting()->get()->map(function($item) {
|
||||||
return [
|
return [
|
||||||
'id'=>$item->id,
|
'id'=>$item->id,
|
||||||
'msgid'=>$item->msgid,
|
'msgid'=>$item->msgid,
|
||||||
|
@@ -36,22 +36,15 @@ class NodelistImport extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle():int
|
public function handle():int
|
||||||
{
|
{
|
||||||
try {
|
return Job::dispatchSync(
|
||||||
return Job::dispatchSync(
|
is_numeric($x=$this->argument('file'))
|
||||||
is_numeric($x=$this->argument('file'))
|
? File::findOrFail($x)
|
||||||
? File::findOrFail($x)
|
: sprintf('%s/%s',config('fido.dir'),$this->argument('file')),
|
||||||
: sprintf('%s/%s',config('fido.dir'),$this->argument('file')),
|
$this->argument('domain'),
|
||||||
$this->argument('domain'),
|
$this->option('delete'),
|
||||||
$this->option('delete'),
|
$this->option('unlink'),
|
||||||
$this->option('unlink'),
|
$this->option('test'),
|
||||||
$this->option('test'),
|
$this->option('ignorecrc'),
|
||||||
$this->option('ignorecrc'),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error($e->getMessage());
|
|
||||||
|
|
||||||
return self::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
50
app/Console/Commands/NodesNew.php
Normal file
50
app/Console/Commands/NodesNew.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Address;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
use App\Jobs\NodesNew as Job;
|
||||||
|
use App\Models\Domain;
|
||||||
|
|
||||||
|
class NodesNew extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'nodes:new'
|
||||||
|
.' {domain : Domain}'
|
||||||
|
.' {--date= : From a specific date (default 1 since last Saturday)}'
|
||||||
|
.' {--netmail= : Send a Netmail to FTN}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'List new nodes since last Saturday (or a specific date)';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
$do = Domain::where('name',$this->argument('domain'))->sole();
|
||||||
|
$ao = NULL;
|
||||||
|
|
||||||
|
if ($this->option('netmail')) {
|
||||||
|
$ao = Address::findFTN($this->option('netmail'));
|
||||||
|
|
||||||
|
if (! $ao) {
|
||||||
|
$this->error('Address not found: '.$this->option('netmail'));
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Job::dispatchSync($do,$this->option('date') ? Carbon::parse($this->option('date')) : Carbon::parse('last saturday'),$ao);
|
||||||
|
}
|
||||||
|
}
|
@@ -50,7 +50,7 @@ class PacketInfo extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($f as $packet) {
|
foreach ($f as $packet) {
|
||||||
$pkt = Packet::process($packet,$x=$f->itemName(),$f->itemSize(),$a?->zone->domain);
|
$pkt = Packet::process($packet,$x=$f->itemName(),$f->itemSize(),$a?->system);
|
||||||
|
|
||||||
$this->alert(sprintf('File Name: %s',$x));
|
$this->alert(sprintf('File Name: %s',$x));
|
||||||
|
|
||||||
@@ -68,7 +68,8 @@ class PacketInfo extends Command
|
|||||||
echo "\n";
|
echo "\n";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->warn(sprintf('- Date : %s (%s)',$msg->datetime,$msg->datetime->tz->toOffsetName()));
|
$this->warn(sprintf('- TYPE : %s',get_class($msg)));
|
||||||
|
$this->warn(sprintf(' - Date : %s (%s)',$msg->date,$msg->date->tz->toOffsetName()));
|
||||||
$this->warn(sprintf(' - Errors : %s',$msg->errors->count() ? 'YES' : 'No'));
|
$this->warn(sprintf(' - Errors : %s',$msg->errors->count() ? 'YES' : 'No'));
|
||||||
$this->warn(sprintf(' - Flags : %s',$msg->flags()->keys()->join(', ')));
|
$this->warn(sprintf(' - Flags : %s',$msg->flags()->keys()->join(', ')));
|
||||||
$this->warn(sprintf(' - Cost : %d',$msg->cost));
|
$this->warn(sprintf(' - Cost : %d',$msg->cost));
|
||||||
@@ -76,7 +77,7 @@ class PacketInfo extends Command
|
|||||||
if ($msg instanceof Echomail)
|
if ($msg instanceof Echomail)
|
||||||
$this->warn(sprintf(' - To : %s',$msg->to));
|
$this->warn(sprintf(' - To : %s',$msg->to));
|
||||||
else
|
else
|
||||||
$this->warn(sprintf(' - To : %s (%s)',$msg->to,$msg->tftn->ftn));
|
$this->warn(sprintf(' - To : %s (%s)',$msg->to,$msg->tftn?->ftn ?: $msg->set_tftn));
|
||||||
$this->warn(sprintf(' - Subject: %s',$msg->subject));
|
$this->warn(sprintf(' - Subject: %s',$msg->subject));
|
||||||
if ($msg instanceof Echomail)
|
if ($msg instanceof Echomail)
|
||||||
$this->warn(sprintf(' - Area : %s',$msg->echoarea->name));
|
$this->warn(sprintf(' - Area : %s',$msg->echoarea->name));
|
||||||
@@ -97,7 +98,7 @@ class PacketInfo extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($pkt->errors as $msg) {
|
foreach ($pkt->errors as $msg) {
|
||||||
$this->error(sprintf('- Date: %s',$msg->date));
|
$this->error(sprintf('- Date: %s',$msg->datetime));
|
||||||
$this->error(sprintf(' - FLAGS: %s',$msg->flags()->filter()->keys()->join(', ')));
|
$this->error(sprintf(' - FLAGS: %s',$msg->flags()->filter()->keys()->join(', ')));
|
||||||
$this->error(sprintf(' - From: %s (%s)',$msg->from,$msg->fftn));
|
$this->error(sprintf(' - From: %s (%s)',$msg->from,$msg->fftn));
|
||||||
$this->error(sprintf(' - To: %s (%s)',$msg->to,$msg->tftn));
|
$this->error(sprintf(' - To: %s (%s)',$msg->to,$msg->tftn));
|
||||||
|
@@ -34,6 +34,8 @@ use App\Models\Address;
|
|||||||
* - To areafix (processed)
|
* - To areafix (processed)
|
||||||
* - To ping (respond)
|
* - To ping (respond)
|
||||||
* - With trace turned on (respond)
|
* - With trace turned on (respond)
|
||||||
|
*
|
||||||
|
* @todo Enable force processing packets when the password is wrong
|
||||||
*/
|
*/
|
||||||
class PacketProcess extends Command
|
class PacketProcess extends Command
|
||||||
{
|
{
|
||||||
@@ -78,7 +80,7 @@ class PacketProcess extends Command
|
|||||||
return self::FAILURE;
|
return self::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Job::dispatchSync($rel_name,$ao->zone->domain,$this->option('dontqueue'));
|
Job::dispatchSync($rel_name,$ao->system,$this->option('dontqueue'));
|
||||||
|
|
||||||
return self::SUCCESS;
|
return self::SUCCESS;
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,12 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
use App\Classes\Protocol\{Binkp,DNS,EMSI};
|
use App\Classes\Protocol\{Binkp,DNS,EMSI};
|
||||||
use App\Classes\Sock\{SocketException,SocketServer};
|
use App\Classes\Sock\Exception\SocketException;
|
||||||
use App\Models\Setup;
|
use App\Classes\Sock\SocketServer;
|
||||||
|
|
||||||
class ServerStart extends Command
|
class ServerStart extends Command
|
||||||
{
|
{
|
||||||
@@ -36,7 +37,11 @@ class ServerStart extends Command
|
|||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
Log::info(sprintf('%s:+ Server Starting (%d)',self::LOGKEY,getmypid()));
|
Log::info(sprintf('%s:+ Server Starting (%d)',self::LOGKEY,getmypid()));
|
||||||
$o = Setup::findOrFail(config('app.id'));
|
|
||||||
|
if (! our_address()->count())
|
||||||
|
throw new \Exception('We dont have any ACTIVE FTN addresses assigned');
|
||||||
|
|
||||||
|
$o = Config::get('setup');
|
||||||
|
|
||||||
$start = collect();
|
$start = collect();
|
||||||
|
|
||||||
@@ -61,7 +66,7 @@ class ServerStart extends Command
|
|||||||
'address'=>$o->dns_bind,
|
'address'=>$o->dns_bind,
|
||||||
'port'=>$o->dns_port,
|
'port'=>$o->dns_port,
|
||||||
'proto'=>SOCK_DGRAM,
|
'proto'=>SOCK_DGRAM,
|
||||||
'class'=>new DNS(),
|
'class'=>new DNS($o),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$children = collect();
|
$children = collect();
|
||||||
|
@@ -25,7 +25,7 @@ class UserCodeSend extends Command
|
|||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$ao = Address::findFTN($this->argument('ftn'));
|
$ao = Address::findFTN($this->argument('ftn'));
|
||||||
$uo = User::where('email',$this->argument('email'))->singleOrFail();
|
$uo = User::where('email',$this->argument('email'))->sole();
|
||||||
|
|
||||||
Notification::route('netmail',$ao->uplink())->notify(new AddressLink($uo));
|
Notification::route('netmail',$ao->uplink())->notify(new AddressLink($uo));
|
||||||
|
|
||||||
|
@@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console;
|
|
||||||
|
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
|
||||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
|
||||||
|
|
||||||
use App\Jobs\{AddressIdleDomain,MailSend,SystemHeartbeat};
|
|
||||||
|
|
||||||
class Kernel extends ConsoleKernel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The Artisan commands provided by your application.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $commands = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the application's command schedule.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function schedule(Schedule $schedule)
|
|
||||||
{
|
|
||||||
$schedule->job(new MailSend(TRUE))->everyMinute()->withoutOverlapping();
|
|
||||||
$schedule->job(new MailSend(FALSE))->twiceDaily(1,13);
|
|
||||||
$schedule->job(new SystemHeartbeat)->hourly();
|
|
||||||
$schedule->job(new AddressIdleDomain)->weeklyOn(0,'01:00');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the commands for the application.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function commands()
|
|
||||||
{
|
|
||||||
$this->load(__DIR__.'/Commands');
|
|
||||||
|
|
||||||
require base_path('routes/console.php');
|
|
||||||
}
|
|
||||||
}
|
|
21
app/Events/Echomail.php
Normal file
21
app/Events/Echomail.php
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
use App\Models\Echomail as EchomailModel;
|
||||||
|
|
||||||
|
class Echomail
|
||||||
|
{
|
||||||
|
use Dispatchable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*/
|
||||||
|
public function __construct(public EchomailModel $eo)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
64
app/Events/Matrix/Base.php
Normal file
64
app/Events/Matrix/Base.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events\Matrix;
|
||||||
|
|
||||||
|
use Illuminate\Http\Client\ConnectionException;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Models\Echoarea;
|
||||||
|
|
||||||
|
abstract class Base
|
||||||
|
{
|
||||||
|
protected $_data = [];
|
||||||
|
|
||||||
|
public function __construct(array $request)
|
||||||
|
{
|
||||||
|
Log::info(sprintf('EMb:- Event Initialised [%s]',get_class($this)));
|
||||||
|
|
||||||
|
$this->_data = json_decode(json_encode($request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable getting values for keys in the response
|
||||||
|
*
|
||||||
|
* @note: This method is limited to certain values to ensure integrity reasons
|
||||||
|
* @note: Classes should return:
|
||||||
|
* + channel_id,
|
||||||
|
* + team_id,
|
||||||
|
* + ts,
|
||||||
|
* + user_id
|
||||||
|
* @param string $key
|
||||||
|
* @return mixed|object
|
||||||
|
* @throws ConnectionException
|
||||||
|
*/
|
||||||
|
public function __get(string $key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'echoarea':
|
||||||
|
$rooms = collect(config('matrix.rooms'));
|
||||||
|
|
||||||
|
return Echoarea::where('name',$rooms->get($this->room_id))->single();
|
||||||
|
|
||||||
|
case 'room':
|
||||||
|
$room_alias = Http::withToken(config('matrix.as_token'))
|
||||||
|
->get(sprintf('%s/_matrix/client/v3/rooms/%s/state/m.room.canonical_alias',config('matrix.server'),$this->room_id));
|
||||||
|
|
||||||
|
return $room_alias->json('alias',$this->room_id);
|
||||||
|
|
||||||
|
case 'topic':
|
||||||
|
$subject = Http::withToken(config('matrix.as_token'))
|
||||||
|
->get(sprintf('%s/_matrix/client/v3/rooms/%s/state/m.room.topic',config('matrix.server'),$this->room_id));
|
||||||
|
|
||||||
|
return $subject->json('topic','Message from Matrix');
|
||||||
|
|
||||||
|
case 'ts':
|
||||||
|
return object_get($this->_data,'origin_server_ts');
|
||||||
|
|
||||||
|
case 'event_id':
|
||||||
|
case 'room_id':
|
||||||
|
default:
|
||||||
|
return object_get($this->_data,$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
app/Events/Matrix/Factory.php
Normal file
41
app/Events/Matrix/Factory.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events\Matrix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class Factory {
|
||||||
|
private const LOGKEY = 'EMf';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array event type to event class mapping
|
||||||
|
*/
|
||||||
|
public const map = [
|
||||||
|
'm.room.message' => Message::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns new event instance
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param array $request
|
||||||
|
* @return Base
|
||||||
|
*/
|
||||||
|
public static function create(string $type,array $request): Base
|
||||||
|
{
|
||||||
|
$class = Arr::get(self::map,$type,Unknown::class);
|
||||||
|
Log::debug(sprintf('%s:- Working out Event Class for [%s] as [%s]',static::LOGKEY,$type,$class));
|
||||||
|
|
||||||
|
if (App::environment() == 'local')
|
||||||
|
file_put_contents('/tmp/event.'.$type,print_r($request,TRUE));
|
||||||
|
|
||||||
|
return new $class($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function make(array $request): Base
|
||||||
|
{
|
||||||
|
return self::create(Arr::get($request,'type','unknown'),$request);
|
||||||
|
}
|
||||||
|
}
|
49
app/Events/Matrix/Message.php
Normal file
49
app/Events/Matrix/Message.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events\Matrix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A matrix message event
|
||||||
|
*
|
||||||
|
* Array
|
||||||
|
* (
|
||||||
|
* [age] => 37
|
||||||
|
* [content] => Array
|
||||||
|
* (
|
||||||
|
* [body] => This is my text
|
||||||
|
* [m.mentions] => Array
|
||||||
|
* (
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* [msgtype] => m.text
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* [event_id] => $fkpvy3qDkAGlB55nvqcH8mUfSxzELtaJ9TKJs6GP9us
|
||||||
|
* [origin_server_ts] => 1717917709298
|
||||||
|
* [room_id] => !bbXofZepRYOhKjihLH:matrix.dege.au
|
||||||
|
* [sender] => @deon:matrix.dege.au
|
||||||
|
* [type] => m.room.message
|
||||||
|
* [unsigned] => Array
|
||||||
|
* (
|
||||||
|
* [age] => 37
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* [user_id] => @deon:matrix.dege.au
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
class Message extends Base
|
||||||
|
{
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'message':
|
||||||
|
return object_get($this->_data,'content.body');
|
||||||
|
|
||||||
|
case 'sender':
|
||||||
|
return object_get($this->_data,$key);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return parent::__get($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
app/Events/Matrix/Unknown.php
Normal file
20
app/Events/Matrix/Unknown.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events\Matrix;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Catch all unknown events that we havent specifically programmed for.
|
||||||
|
*
|
||||||
|
* @package Slack\Event
|
||||||
|
*/
|
||||||
|
class Unknown extends Base
|
||||||
|
{
|
||||||
|
public function __construct(array $request)
|
||||||
|
{
|
||||||
|
Log::notice(sprintf('EMU:? UNKNOWN Event received [%s]',get_class($this)));
|
||||||
|
|
||||||
|
parent::__construct($request);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Exceptions;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class Handler extends ExceptionHandler
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A list of the exception types that are not reported.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $dontReport = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of the inputs that are never flashed for validation exceptions.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $dontFlash = [
|
|
||||||
'password',
|
|
||||||
'password_confirmation',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register the exception handling callbacks for the application.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function register()
|
|
||||||
{
|
|
||||||
$this->reportable(function (Throwable $e) {
|
|
||||||
//
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
212
app/Helpers/PageAssets.php
Normal file
212
app/Helpers/PageAssets.php
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Useful tools (js/css) used when rendering pages
|
||||||
|
*/
|
||||||
|
class PageAssets
|
||||||
|
{
|
||||||
|
// Types that we can handle
|
||||||
|
private const array types = [
|
||||||
|
'css',
|
||||||
|
'js',
|
||||||
|
];
|
||||||
|
|
||||||
|
public const array assets = [
|
||||||
|
'datatables' => [
|
||||||
|
'base' => [
|
||||||
|
'css' => [
|
||||||
|
'//cdn.datatables.net/1.10.25/css/dataTables.bootstrap5.css',
|
||||||
|
//'//cdn.datatables.net/2.1.2/css/dataTables.dataTables.min.css',
|
||||||
|
'/plugin/dataTables/dataTables.bootstrap5.css',
|
||||||
|
],
|
||||||
|
'js' => [
|
||||||
|
'//cdn.datatables.net/1.10.25/js/jquery.dataTables.min.js',
|
||||||
|
'//cdn.datatables.net/1.10.25/js/dataTables.bootstrap5.min.js',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'buttons' => [
|
||||||
|
'css' => [
|
||||||
|
'//cdn.datatables.net/buttons/3.1.0/css/buttons.bootstrap4.min.css',
|
||||||
|
//'//cdn.datatables.net/buttons/3.1.0/css/buttons.dataTables.min.css',
|
||||||
|
],
|
||||||
|
'js' => [
|
||||||
|
'//cdn.datatables.net/buttons/3.1.0/js/dataTables.buttons.min.js',
|
||||||
|
//'//cdn.datatables.net/buttons/3.1.0/js/buttons.dataTables.min.js',
|
||||||
|
'//cdn.datatables.net/buttons/3.1.0/js/buttons.bootstrap4.min.js',
|
||||||
|
'//cdnjs.cloudflare.com/ajax/libs/jszip/3.2.0/jszip.min.js',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'conditionalpaging' => [
|
||||||
|
'js' => [
|
||||||
|
//'//cdn.datatables.net/plug-ins/2.0.5/features/conditionalPaging/dataTables.conditionalPaging.min.js',
|
||||||
|
'/plugin/dataTables/dataTables.conditionalPaging.js',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'fixedheader' => [
|
||||||
|
'css' => [
|
||||||
|
'//cdn.datatables.net/fixedheader/4.0.1/css/fixedHeader.bootstrap4.min.css',
|
||||||
|
//'//cdn.datatables.net/fixedheader/4.0.1/css/fixedHeader.dataTables.min.css',
|
||||||
|
],
|
||||||
|
'js' => [
|
||||||
|
'//cdn.datatables.net/fixedheader/4.0.1/js/dataTables.fixedHeader.min.js',
|
||||||
|
//'//cdn.datatables.net/fixedheader/4.0.1/js/fixedHeader.dataTables.min.js',
|
||||||
|
'//cdn.datatables.net/fixedheader/4.0.1/js/fixedHeader.bootstrap4.min.js',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'responsive' => [
|
||||||
|
'css' => [
|
||||||
|
'//cdn.datatables.net/responsive/3.0.2/css/responsive.bootstrap4.min.css',
|
||||||
|
//'//cdn.datatables.net/responsive/3.0.2/css/responsive.dataTables.min.css',
|
||||||
|
],
|
||||||
|
'js' => [
|
||||||
|
'//cdn.datatables.net/responsive/3.0.2/js/dataTables.responsive.min.js',
|
||||||
|
//'//cdn.datatables.net/responsive/3.0.2/js/responsive.bootstrap.min.js',
|
||||||
|
'//cdn.datatables.net/responsive/3.0.2/js/responsive.bootstrap4.min.js',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'rowgroup' => [
|
||||||
|
'css' => [
|
||||||
|
'//cdn.datatables.net/rowgroup/1.1.2/css/rowGroup.bootstrap4.min.css',
|
||||||
|
//'//cdn.datatables.net/rowgroup/1.5.0/css/rowGroup.dataTables.min.css',
|
||||||
|
],
|
||||||
|
'js' => [
|
||||||
|
'//cdn.datatables.net/rowgroup/1.1.2/js/dataTables.rowGroup.min.js',
|
||||||
|
//'//cdn.datatables.net/rowgroup/1.5.0/js/rowGroup.dataTables.min.js',
|
||||||
|
'//cdn.datatables.net/rowgroup/1.1.2/js/rowGroup.bootstrap5.min.js',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'searchpanes' => [
|
||||||
|
'css' => [
|
||||||
|
'//cdn.datatables.net/searchpanes/2.3.1/css/searchPanes.bootstrap4.min.css',
|
||||||
|
//'//cdn.datatables.net/searchpanes/2.3.1/css/searchPanes.dataTables.min.css',
|
||||||
|
],
|
||||||
|
'js' => [
|
||||||
|
'//cdn.datatables.net/searchpanes/2.3.1/js/dataTables.searchPanes.min.js',
|
||||||
|
//'//cdn.datatables.net/searchpanes/2.3.1/js/searchPanes.dataTables.min.js',
|
||||||
|
'//cdn.datatables.net/searchpanes/2.3.1/js/searchPanes.bootstrap4.min.js',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'searchpanes-left' => [
|
||||||
|
'css' => [
|
||||||
|
'/plugin/dataTables/leftSearchPanes.css',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'select' => [
|
||||||
|
'css' => [
|
||||||
|
'//cdn.datatables.net/select/2.0.3/css/select.bootstrap4.min.css',
|
||||||
|
//'//cdn.datatables.net/select/2.0.3/css/select.dataTables.min.css',
|
||||||
|
],
|
||||||
|
'js' => [
|
||||||
|
'//cdn.datatables.net/select/2.0.3/js/dataTables.select.min.js',
|
||||||
|
//'//cdn.datatables.net/select/2.0.3/js/select.dataTables.min.js',
|
||||||
|
'//cdn.datatables.net/select/2.0.3/js/select.bootstrap4.min.js',
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'select2' => [
|
||||||
|
'base' => [
|
||||||
|
'css' => [
|
||||||
|
'//cdn.jsdelivr.net/npm/select2@4.0.13/dist/css/select2.min.css',
|
||||||
|
'//cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css',
|
||||||
|
],
|
||||||
|
'js' => [
|
||||||
|
'//cdn.jsdelivr.net/npm/select2@4.0.13/dist/js/select2.full.min.js',
|
||||||
|
'/plugin/select2/fix-autofocus.js',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'simplemde' => [
|
||||||
|
'base' => [
|
||||||
|
'css' => [
|
||||||
|
'//cdn.jsdelivr.net/simplemde/latest/simplemde.min.css',
|
||||||
|
],
|
||||||
|
'js' => [
|
||||||
|
'//cdn.jsdelivr.net/simplemde/latest/simplemde.min.js',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Items to manage
|
||||||
|
public static Collection $items;
|
||||||
|
|
||||||
|
// Add an item to the list
|
||||||
|
public static function add(string $type,Collection|array|string $asset): void
|
||||||
|
{
|
||||||
|
if (! in_array($type,self::types))
|
||||||
|
throw new \Exception('Invalid type: '.$type);
|
||||||
|
|
||||||
|
if (! isset(self::$items))
|
||||||
|
self::init();
|
||||||
|
|
||||||
|
if (is_string($asset))
|
||||||
|
self::$items
|
||||||
|
->get($type)
|
||||||
|
->push($asset)
|
||||||
|
->unique();
|
||||||
|
else
|
||||||
|
self::$items->put($type,
|
||||||
|
self::$items
|
||||||
|
->get($type)
|
||||||
|
->merge($asset->values())
|
||||||
|
->unique());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a predefined asset
|
||||||
|
public static function asset(string $id): void
|
||||||
|
{
|
||||||
|
if (! isset(self::$items))
|
||||||
|
self::init();
|
||||||
|
|
||||||
|
if (str_contains($id,',')) {
|
||||||
|
[$item,$arguments] = explode(',',$id,2);
|
||||||
|
$arguments = collect(explode('|',$arguments));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$item = $id;
|
||||||
|
$arguments = collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
$arguments = $arguments->prepend('base');
|
||||||
|
$asset = collect(Arr::get(self::assets,$item))->only($arguments);
|
||||||
|
|
||||||
|
foreach (self::types as $type)
|
||||||
|
if ($x=$asset->pluck($type)->filter()->flatten())
|
||||||
|
self::add($type,$x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the CSS items
|
||||||
|
public static function css(): string
|
||||||
|
{
|
||||||
|
return isset(self::$items)
|
||||||
|
? self::$items
|
||||||
|
->get('css')
|
||||||
|
->map(fn($item)=>sprintf('<link rel="stylesheet" href="%s">',$item))
|
||||||
|
->join('')
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function init(): void
|
||||||
|
{
|
||||||
|
self::$items = collect([
|
||||||
|
'js' => collect(),
|
||||||
|
'css' => collect(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the JS items
|
||||||
|
public static function js(): string
|
||||||
|
{
|
||||||
|
return isset(self::$items)
|
||||||
|
? self::$items
|
||||||
|
->get('js')
|
||||||
|
->map(fn($item)=>sprintf('<script type="text/javascript" src="%s"></script>',$item))
|
||||||
|
->join('')
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
}
|
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Providers\RouteServiceProvider;
|
|
||||||
use Illuminate\Foundation\Auth\ConfirmsPasswords;
|
use Illuminate\Foundation\Auth\ConfirmsPasswords;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
class ConfirmPasswordController extends Controller
|
class ConfirmPasswordController extends Controller
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -26,7 +25,7 @@ class ConfirmPasswordController extends Controller
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
protected $redirectTo = '/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* Create a new controller instance.
|
||||||
|
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Password;
|
use Illuminate\Support\Facades\Password;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
class ForgotPasswordController extends Controller
|
class ForgotPasswordController extends Controller
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@@ -8,7 +8,6 @@ use Illuminate\Http\Request;
|
|||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Providers\RouteServiceProvider;
|
|
||||||
|
|
||||||
class LoginController extends Controller
|
class LoginController extends Controller
|
||||||
{
|
{
|
||||||
@@ -30,7 +29,7 @@ class LoginController extends Controller
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
protected $redirectTo = '/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* Create a new controller instance.
|
||||||
@@ -39,40 +38,41 @@ class LoginController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->middleware('guest')
|
$this->middleware('guest')->except('logout');
|
||||||
->except('logout');
|
$this->middleware('auth')->only('logout');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login(Request $request)
|
public function login(Request $request)
|
||||||
{
|
{
|
||||||
$this->validateLogin($request);
|
$this->validateLogin($request);
|
||||||
|
|
||||||
if (Auth::attempt(array_merge($this->credentials($request),['active'=>TRUE]),TRUE)) {
|
if (Auth::attempt(array_merge($this->credentials($request),['active'=>TRUE]),TRUE)) {
|
||||||
$request->session()->regenerate();
|
$request->session()->regenerate();
|
||||||
|
|
||||||
return $this->sendLoginResponse($request);
|
return $this->sendLoginResponse($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->sendFailedLoginResponse($request);
|
return $this->sendFailedLoginResponse($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function authenticated(Request $request, $user)
|
// Record our last logged in time
|
||||||
{
|
protected function authenticated(Request $request, $user)
|
||||||
$user->last_on = Carbon::now();
|
{
|
||||||
$user->save();
|
$user->last_on = Carbon::now();
|
||||||
}
|
$user->save();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show our themed login page
|
* Show our themed login page
|
||||||
*/
|
*/
|
||||||
public function showLoginForm()
|
public function showLoginForm()
|
||||||
{
|
{
|
||||||
$login_note = '';
|
$login_note = '';
|
||||||
|
|
||||||
if (file_exists('login_note.txt'))
|
if (file_exists('login_note.txt'))
|
||||||
$login_note = file_get_contents('login_note.txt');
|
$login_note = file_get_contents('login_note.txt');
|
||||||
|
|
||||||
return view('auth.login')
|
return view('auth.login')
|
||||||
->with('login_note',$login_note);
|
->with('login_note',$login_note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,6 @@ use Illuminate\Support\Facades\Hash;
|
|||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Providers\RouteServiceProvider;
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
class RegisterController extends Controller
|
class RegisterController extends Controller
|
||||||
@@ -30,7 +29,7 @@ class RegisterController extends Controller
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
protected $redirectTo = '/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* Create a new controller instance.
|
||||||
|
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Providers\RouteServiceProvider;
|
|
||||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
class ResetPasswordController extends Controller
|
class ResetPasswordController extends Controller
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -26,15 +26,5 @@ class ResetPasswordController extends Controller
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
protected $redirectTo = '/';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new controller instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->middleware('guest');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Providers\RouteServiceProvider;
|
|
||||||
use Illuminate\Foundation\Auth\VerifiesEmails;
|
use Illuminate\Foundation\Auth\VerifiesEmails;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
class VerificationController extends Controller
|
class VerificationController extends Controller
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -26,7 +26,7 @@ class VerificationController extends Controller
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $redirectTo = RouteServiceProvider::HOME;
|
protected $redirectTo = '/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new controller instance.
|
* Create a new controller instance.
|
||||||
|
@@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
|
||||||
class Controller extends BaseController
|
abstract class Controller extends \Illuminate\Routing\Controller
|
||||||
{
|
{
|
||||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
use AuthorizesRequests,ValidatesRequests;
|
||||||
}
|
}
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
@@ -14,7 +15,7 @@ class DomainController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Daily stats as shown on the about page
|
* Daily stats as shown on the about page
|
||||||
*
|
*
|
||||||
* @param Domain $o
|
* @param Request $request
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function api_daily_stats(Request $request): Collection
|
public function api_daily_stats(Request $request): Collection
|
||||||
@@ -25,7 +26,7 @@ class DomainController extends Controller
|
|||||||
->sortBy('date')
|
->sortBy('date')
|
||||||
->groupBy('date')
|
->groupBy('date')
|
||||||
->transform(function($item,$key) { return [
|
->transform(function($item,$key) { return [
|
||||||
'x'=>\Carbon\Carbon::createFromFormat('Y-m-d',$key)->timestamp,
|
'x'=>Carbon::createFromFormat('Y-m-d',$key)->timestamp*1000,
|
||||||
'y'=>$item->sum('count')]; } )
|
'y'=>$item->sum('count')]; } )
|
||||||
->values();
|
->values();
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ class EchoareaController extends Controller
|
|||||||
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'domain_id' => 'required|exists:domains,id',
|
'domain_id' => 'required|exists:domains,id',
|
||||||
'name' => 'required|min:4|max:35|regex:/^[a-zA-Z0-9\-_~]{4,}$/|unique:echoareas,name,'.($o->exists ? $o->id : 0),
|
'name' => 'required|min:4|max:35|regex:/^[a-zA-Z0-9\-_~.]{4,}$/|unique:echoareas,name,'.($o->exists ? $o->id : 0),
|
||||||
'description' => 'required',
|
'description' => 'required',
|
||||||
'active' => 'required|boolean',
|
'active' => 'required|boolean',
|
||||||
'show' => 'required|boolean',
|
'show' => 'required|boolean',
|
||||||
|
@@ -10,13 +10,16 @@ use Illuminate\Support\Facades\DB;
|
|||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
use App\Classes\File;
|
use App\Classes\File;
|
||||||
use App\Classes\FTN\Packet;
|
use App\Classes\FTN\{Message,Packet};
|
||||||
use App\Http\Requests\SetupRequest;
|
use App\Http\Requests\SetupRequest;
|
||||||
use App\Models\File as FileModel;
|
use App\Models\File as FileModel;
|
||||||
use App\Models\{Address,Echomail,Netmail,Setup,System};
|
use App\Models\{Address,Echomail,Netmail,Setup,System};
|
||||||
|
use App\Traits\HubStats;
|
||||||
|
|
||||||
class HomeController extends Controller
|
class HomeController extends Controller
|
||||||
{
|
{
|
||||||
|
use HubStats;
|
||||||
|
|
||||||
public function home()
|
public function home()
|
||||||
{
|
{
|
||||||
return redirect(Auth::check() ? 'dashboard' : 'about');
|
return redirect(Auth::check() ? 'dashboard' : 'about');
|
||||||
@@ -104,7 +107,7 @@ class HomeController extends Controller
|
|||||||
$f = new File($file);
|
$f = new File($file);
|
||||||
|
|
||||||
foreach ($f as $packet)
|
foreach ($f as $packet)
|
||||||
$pkt->push([$f->itemName()=>Packet::process($packet,$f->itemName(),$f->itemSize())]);
|
$pkt->push([$f->itemName()=>Packet::process($packet,$f->itemName(),$f->itemSize(),NULL,FALSE)]);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return redirect()->back()->withErrors(sprintf('%s (%s:%d)',$e->getMessage(),$e->getFile(),$e->getLine()));
|
return redirect()->back()->withErrors(sprintf('%s (%s:%d)',$e->getMessage(),$e->getFile(),$e->getLine()));
|
||||||
@@ -169,7 +172,7 @@ class HomeController extends Controller
|
|||||||
->orWhere('replyid','like','%'.$request->query('term').'%')
|
->orWhere('replyid','like','%'.$request->query('term').'%')
|
||||||
->get() as $o)
|
->get() as $o)
|
||||||
{
|
{
|
||||||
$result->push(['id'=>$o->id,'name'=>sprintf('%s (%s)',$o->from,$o->fftn->ftn3d),'value'=>url('echomail/view',[$o->id]),'category'=>'Echomail']);
|
$result->push(['id'=>$o->id,'name'=>sprintf('%s (%s)',Message::tr($o->from),$o->fftn->ftn3d),'value'=>url('echomail/view',[$o->id]),'category'=>'Echomail']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for Netmail
|
// Look for Netmail
|
||||||
@@ -224,6 +227,7 @@ class HomeController extends Controller
|
|||||||
|
|
||||||
$options->put('options',collect($request->post('options'))->sum());
|
$options->put('options',collect($request->post('options'))->sum());
|
||||||
$options->put('msgs_pkt',$request->post('msgs_pkt'));
|
$options->put('msgs_pkt',$request->post('msgs_pkt'));
|
||||||
|
$options->put('pkt_passwds',$request->post('pkt_passwds') === "1");
|
||||||
|
|
||||||
$o->servers = $servers;
|
$o->servers = $servers;
|
||||||
$o->options = $options;
|
$o->options = $options;
|
||||||
@@ -245,49 +249,8 @@ class HomeController extends Controller
|
|||||||
{
|
{
|
||||||
$date = Carbon::now()->yesterday()->endOfday();
|
$date = Carbon::now()->yesterday()->endOfday();
|
||||||
|
|
||||||
$r = Address::select([
|
|
||||||
'a.id',
|
|
||||||
'a.system_id',
|
|
||||||
'a.zone_id',
|
|
||||||
'addresses.region_id',
|
|
||||||
'a.host_id',
|
|
||||||
'a.node_id',
|
|
||||||
'a.point_id',
|
|
||||||
'addresses.hub_id',
|
|
||||||
'addresses.role',
|
|
||||||
DB::raw('sum(a.uncollected_echomail) as uncollected_echomail'),
|
|
||||||
DB::raw('sum(a.uncollected_netmail) as uncollected_netmail'),
|
|
||||||
DB::raw('sum(a.uncollected_files) as uncollected_files')
|
|
||||||
])
|
|
||||||
->from(
|
|
||||||
Address::UncollectedEchomailTotal()
|
|
||||||
->where('echomails.created_at','<',$this->yesterdayEOD())
|
|
||||||
->union(Address::UncollectedNetmailTotal()
|
|
||||||
->where('netmails.created_at','<',$this->yesterdayEOD())
|
|
||||||
)
|
|
||||||
->union(Address::UncollectedFilesTotal()
|
|
||||||
->where('files.created_at','<',$this->yesterdayEOD())
|
|
||||||
),'a')
|
|
||||||
->where('systems.active',TRUE)
|
|
||||||
->where('addresses.active',TRUE)
|
|
||||||
->where('zones.active',TRUE)
|
|
||||||
->where('domains.active',TRUE)
|
|
||||||
->when(! ($x=Auth::user()) || (! $x->isAdmin()),function($query) { return $query->where('domains.public',TRUE); })
|
|
||||||
->join('addresses',['addresses.id'=>'a.id'])
|
|
||||||
->join('systems',['systems.id'=>'a.system_id'])
|
|
||||||
->join('zones',['zones.id'=>'addresses.zone_id'])
|
|
||||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
|
||||||
->ftnOrder()
|
|
||||||
->groupBy('a.system_id','a.id','a.zone_id','addresses.region_id','a.host_id','a.node_id','a.point_id','addresses.hub_id','addresses.role')
|
|
||||||
->with(['system','zone.domain']);
|
|
||||||
|
|
||||||
return view('status')
|
return view('status')
|
||||||
->with('date',$date)
|
->with('date',$date)
|
||||||
->with('uncollected',$r->get());
|
->with('uncollected',$this->HubStats($date)->get());
|
||||||
}
|
|
||||||
|
|
||||||
private function yesterdayEOD(): Carbon
|
|
||||||
{
|
|
||||||
return Carbon::now()->yesterday()->endOfday();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
31
app/Http/Controllers/MatrixController.php
Normal file
31
app/Http/Controllers/MatrixController.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Events\Matrix\Factory as MatrixEventFactory;
|
||||||
|
use App\Events\Matrix\Message;
|
||||||
|
|
||||||
|
final class MatrixController extends Controller
|
||||||
|
{
|
||||||
|
private const LOGKEY = 'CMC';
|
||||||
|
|
||||||
|
public function webhook(Request $request): mixed
|
||||||
|
{
|
||||||
|
$event = MatrixEventFactory::make(Arr::get($request->events,0,[]));
|
||||||
|
|
||||||
|
// Catch our messages that we've posted
|
||||||
|
if (($event instanceof Message) && preg_match('#^.*\^[0-9]+_[0-9]+/[0-9]+(\.[0-9]+)?:#',$event->sender)) {
|
||||||
|
Log::info(sprintf('%s:- Ignoring Matrix Message event, probably from us [%s]',static::LOGKEY,$event->sender));
|
||||||
|
return response(['result'=>'OK']);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info(sprintf('%s:- Dispatching Matrix Event [%s]',static::LOGKEY,get_class($event)));
|
||||||
|
event($event);
|
||||||
|
|
||||||
|
return response(['result'=>'OK']);
|
||||||
|
}
|
||||||
|
}
|
@@ -13,7 +13,6 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Notification;
|
use Illuminate\Support\Facades\Notification;
|
||||||
use Illuminate\Support\ViewErrorBag;
|
|
||||||
|
|
||||||
use App\Classes\FTN\Message;
|
use App\Classes\FTN\Message;
|
||||||
use App\Http\Requests\{AddressAdd,AddressMerge,AreafixRequest,SystemEchoareaRequest,SystemRegisterRequest,SystemSessionRequest};
|
use App\Http\Requests\{AddressAdd,AddressMerge,AreafixRequest,SystemEchoareaRequest,SystemRegisterRequest,SystemSessionRequest};
|
||||||
@@ -31,12 +30,12 @@ class SystemController extends Controller
|
|||||||
public function add_edit(SystemRegisterRequest $request, System $o)
|
public function add_edit(SystemRegisterRequest $request, System $o)
|
||||||
{
|
{
|
||||||
if ($request->validated()) {
|
if ($request->validated()) {
|
||||||
foreach (['name','location','phone','address','port','active','method','pkt_type'] as $key)
|
foreach (['name','location','phone','address','port','active','method','pkt_msgs','pkt_type'] as $key)
|
||||||
$o->{$key} = $request->validated($key);
|
$o->{$key} = $request->validated($key);
|
||||||
|
|
||||||
// Sometimes items
|
// Sometimes items
|
||||||
foreach (['sysop','hold','notes','zt_id','heartbeat'] as $key)
|
foreach (['sysop','hold','notes','zt_id','heartbeat'] as $key)
|
||||||
if ($request->validated($key))
|
if ($request->has($key))
|
||||||
$o->{$key} = $request->validated($key);
|
$o->{$key} = $request->validated($key);
|
||||||
|
|
||||||
switch ($request->validated('pollmode')) {
|
switch ($request->validated('pollmode')) {
|
||||||
@@ -45,9 +44,13 @@ class SystemController extends Controller
|
|||||||
default: $o->pollmode = NULL;
|
default: $o->pollmode = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$o->active = (! is_null($x=$request->validated('active'))) && $x;
|
||||||
$o->autohold = FALSE;
|
$o->autohold = FALSE;
|
||||||
$o->save();
|
$o->save();
|
||||||
|
|
||||||
|
if ($o->wasRecentlyCreated)
|
||||||
|
$o->users()->attach(Auth::id());
|
||||||
|
|
||||||
$mailers = collect($request->post('mailer_details'))
|
$mailers = collect($request->post('mailer_details'))
|
||||||
->filter(function($item) { return $item['port']; })
|
->filter(function($item) { return $item['port']; })
|
||||||
->transform(function($item) { $item['active'] = Arr::get($item,'active',FALSE); return $item; });
|
->transform(function($item) { $item['active'] = Arr::get($item,'active',FALSE); return $item; });
|
||||||
@@ -60,10 +63,22 @@ class SystemController extends Controller
|
|||||||
$o->users()->detach();
|
$o->users()->detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->to('system');
|
return redirect()
|
||||||
|
->to('system/addedit/'.$o->id)
|
||||||
|
->with('saved',TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
$o->load(['addresses.zone.domain','addresses.nodes_hub','addresses.system','sessions.domain','sessions.systems']);
|
$o->loadMissing([
|
||||||
|
'zcs',
|
||||||
|
// For 'ftn' to work
|
||||||
|
'addresses.zone:id,zone_id,domain_id,active',
|
||||||
|
'addresses.zone.domain:id,name,active',
|
||||||
|
// For 'role'
|
||||||
|
'addresses.system:id,address',
|
||||||
|
// For system addedit
|
||||||
|
'sessions.domain:id,name,active',
|
||||||
|
'sessions.systems:id',
|
||||||
|
]);
|
||||||
|
|
||||||
return view('system.addedit')
|
return view('system.addedit')
|
||||||
->with('action',$o->exists ? 'update_nn' : 'create')
|
->with('action',$o->exists ? 'update_nn' : 'create')
|
||||||
@@ -83,7 +98,10 @@ class SystemController extends Controller
|
|||||||
$oo = Address::findOrNew($request->validated('submit'));
|
$oo = Address::findOrNew($request->validated('submit'));
|
||||||
$oo->zone_id = $request->validated('zone_id');
|
$oo->zone_id = $request->validated('zone_id');
|
||||||
$oo->security = $request->validated('security');
|
$oo->security = $request->validated('security');
|
||||||
$oo->active = TRUE;
|
|
||||||
|
// Only change to active if it is new
|
||||||
|
if (! $oo->exists)
|
||||||
|
$oo->active = TRUE;
|
||||||
|
|
||||||
switch ($request->validated('action')) {
|
switch ($request->validated('action')) {
|
||||||
case 'region':
|
case 'region':
|
||||||
@@ -157,7 +175,9 @@ class SystemController extends Controller
|
|||||||
|
|
||||||
// Make sure that no other system has this address active.
|
// Make sure that no other system has this address active.
|
||||||
if ($o->role_id === Address::NODE_NN)
|
if ($o->role_id === Address::NODE_NN)
|
||||||
return redirect()->back()->withErrors(['address'=>sprintf('%s cannot be demoted any more',$o->ftn3D)]);
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withErrors(['address'=>sprintf('%s cannot be demoted any more',$o->ftn3D)]);
|
||||||
|
|
||||||
$off = $o->role_id;
|
$off = $o->role_id;
|
||||||
$o->role &= ~$off;
|
$o->role &= ~$off;
|
||||||
@@ -165,7 +185,8 @@ class SystemController extends Controller
|
|||||||
|
|
||||||
$o->save();
|
$o->save();
|
||||||
|
|
||||||
return redirect()->to(sprintf('system/addedit/%d',$o->system_id));
|
return redirect()
|
||||||
|
->to(sprintf('system/addedit/%d',$o->system_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function address_merge(AddressMerge $request,int $id)
|
public function address_merge(AddressMerge $request,int $id)
|
||||||
@@ -207,6 +228,9 @@ class SystemController extends Controller
|
|||||||
// Find all netmails
|
// Find all netmails
|
||||||
$x = DB::update('update netmails set fftn_id=? where fftn_id=?',[$request->dst,$request->src]);
|
$x = DB::update('update netmails set fftn_id=? where fftn_id=?',[$request->dst,$request->src]);
|
||||||
|
|
||||||
|
// Find all netmails
|
||||||
|
$x = DB::update('update netmails set sent_id=? where sent_id=?',[$request->dst,$request->src]);
|
||||||
|
|
||||||
// Find all netmails
|
// Find all netmails
|
||||||
$x = DB::update('update netmails set tftn_id=? where tftn_id=?',[$request->dst,$request->src]);
|
$x = DB::update('update netmails set tftn_id=? where tftn_id=?',[$request->dst,$request->src]);
|
||||||
|
|
||||||
@@ -305,6 +329,7 @@ class SystemController extends Controller
|
|||||||
|
|
||||||
if (Arr::get($validated,'remove')) {
|
if (Arr::get($validated,'remove')) {
|
||||||
$so->sessions()->detach($o->zone);
|
$so->sessions()->detach($o->zone);
|
||||||
|
$so->logs()->delete();
|
||||||
$so->mailers()->detach();
|
$so->mailers()->detach();
|
||||||
$so->users()->detach();
|
$so->users()->detach();
|
||||||
$so->delete();
|
$so->delete();
|
||||||
@@ -334,8 +359,10 @@ class SystemController extends Controller
|
|||||||
session()->flash('accordion','address');
|
session()->flash('accordion','address');
|
||||||
|
|
||||||
// Make sure that no other system has this address active.
|
// Make sure that no other system has this address active.
|
||||||
if ($o->role_id === Address::NODE_NC)
|
if ($o->role_id === Address::NODE_HC)
|
||||||
return redirect()->back()->withErrors(['address'=>sprintf('%s cannot be promoted any more',$o->ftn3D)]);
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withErrors(['address'=>sprintf('%s cannot be promoted any more',$o->ftn3D)]);
|
||||||
|
|
||||||
$off = $o->role_id;
|
$off = $o->role_id;
|
||||||
$o->role &= ~$off;
|
$o->role &= ~$off;
|
||||||
@@ -343,7 +370,8 @@ class SystemController extends Controller
|
|||||||
|
|
||||||
$o->save();
|
$o->save();
|
||||||
|
|
||||||
return redirect()->to(sprintf('system/addedit/%d',$o->system_id));
|
return redirect()
|
||||||
|
->to(sprintf('system/addedit/%d',$o->system_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -484,10 +512,16 @@ class SystemController extends Controller
|
|||||||
public function api_autohold_toggle(Request $request,string $state): array
|
public function api_autohold_toggle(Request $request,string $state): array
|
||||||
{
|
{
|
||||||
$o = System::findOrFail($request->id);
|
$o = System::findOrFail($request->id);
|
||||||
$o->autohold = $state === 'off' ? FALSE : TRUE;
|
|
||||||
$o->save();
|
|
||||||
|
|
||||||
Log::debug(sprintf('%s:- Autohold set to [%s]',self::LOGKEY,$o->autohold ? 'ON' : 'OFF'));
|
if ($request->user()->can('update_nn',$o)) {
|
||||||
|
$o->autohold = !($state === 'off');
|
||||||
|
$o->save();
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s:- Autohold set to [%s]',self::LOGKEY,$o->autohold ? 'ON' : 'OFF'));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
return ['autohold'=>$o->autohold];
|
return ['autohold'=>$o->autohold];
|
||||||
}
|
}
|
||||||
@@ -507,7 +541,7 @@ class SystemController extends Controller
|
|||||||
$no->flags = (Message::FLAG_LOCAL|Message::FLAG_PRIVATE|Message::FLAG_CRASH);
|
$no->flags = (Message::FLAG_LOCAL|Message::FLAG_PRIVATE|Message::FLAG_CRASH);
|
||||||
$no->cost = 0;
|
$no->cost = 0;
|
||||||
|
|
||||||
$no->tearline = sprintf('%s (%04X)',Setup::PRODUCT_NAME,Setup::PRODUCT_ID);
|
$no->set_tearline = sprintf('%s (%04X)',Setup::PRODUCT_NAME,Setup::PRODUCT_ID);
|
||||||
$no->save();
|
$no->save();
|
||||||
|
|
||||||
Log::info(sprintf('%s:= Areafix to [%s], scheduling a poll',self::LOGKEY,$no->tftn->ftn));
|
Log::info(sprintf('%s:= Areafix to [%s], scheduling a poll',self::LOGKEY,$no->tftn->ftn));
|
||||||
@@ -571,7 +605,7 @@ class SystemController extends Controller
|
|||||||
session()->flash('accordion','filearea');
|
session()->flash('accordion','filearea');
|
||||||
|
|
||||||
// Ensure we have session details for this address.
|
// Ensure we have session details for this address.
|
||||||
if (! $ao->session('sespass'))
|
if (! $ao->pass_session)
|
||||||
return redirect()->back()->withErrors('System doesnt belong to this network');
|
return redirect()->back()->withErrors('System doesnt belong to this network');
|
||||||
|
|
||||||
$ao->fileareas()->syncWithPivotValues($request->get('id',[]),['subscribed'=>Carbon::now()]);
|
$ao->fileareas()->syncWithPivotValues($request->get('id',[]),['subscribed'=>Carbon::now()]);
|
||||||
@@ -601,58 +635,19 @@ class SystemController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function register(SystemRegisterRequest $request)
|
public function register(SystemRegisterRequest $request)
|
||||||
{
|
{
|
||||||
// Step 1, show the user a form to select an existing defined system
|
// During the BBS linking process, if the user selected a system will link to confirm it
|
||||||
if ($request->isMethod('GET'))
|
if ($request->action === 'register' && $request->system_id && is_numeric($request->system_id))
|
||||||
return view('user.system.register');
|
return redirect()
|
||||||
|
->to(sprintf('user/system/register_confirm/%d',$request->system_id));
|
||||||
|
|
||||||
if ($request->action === 'register' && $request->name && is_numeric($request->name))
|
// Re-flash our previously input data, we must be creating a new system
|
||||||
return view('user.system.widget.register_confirm')
|
session()->flashInput([
|
||||||
->with('o',System::findOrFail($request->name));
|
'name'=>$request->system_id,
|
||||||
|
'sysop'=>Auth::user()->name,
|
||||||
|
]);
|
||||||
|
|
||||||
$o = System::findOrNew(is_numeric($request->system_id) ? $request->system_id : NULL);
|
return redirect()
|
||||||
|
->to('system/addedit');
|
||||||
// If the system exists, and we are 'register', we'll start the address claim process
|
|
||||||
if ($o->exists && $request->action === 'Link') {
|
|
||||||
$validate = Setup::findOrFail(config('app.id'))->system->inMyZones($o->addresses);
|
|
||||||
|
|
||||||
// If we have addresses, we'll trigger the routed netmail
|
|
||||||
if ($validate->count()) {
|
|
||||||
Notification::route('netmail',$x=$validate->first())->notify(new AddressLink(Auth::user()));
|
|
||||||
AddressPoll::dispatch($x)->delay(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
return view('user.system.widget.register_send')
|
|
||||||
->with('validate',$validate)
|
|
||||||
->with('o',$o);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the system doesnt exist, we'll create it
|
|
||||||
if (! $o->exist) {
|
|
||||||
$o->sysop = Auth::user()->name;
|
|
||||||
|
|
||||||
foreach (['name','zt_id','location','phone','method','address','port'] as $item)
|
|
||||||
if ($request->{$item})
|
|
||||||
$o->{$item} = $request->{$item};
|
|
||||||
|
|
||||||
$o->active = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($request->post('submit')) {
|
|
||||||
Auth::user()->systems()->save($o);
|
|
||||||
|
|
||||||
// @todo if the system already exists and part of one of our networks, we'll need to send the registration email to confirm the address.
|
|
||||||
// @todo mark the system (or addresses) as "pending" at this stage until it is confirmed
|
|
||||||
return redirect()->to(url('system/addedit',$o->id));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-flash our previously input data
|
|
||||||
if ($request->old)
|
|
||||||
session()->flashInput($request->old);
|
|
||||||
|
|
||||||
return view('system.widget.system')
|
|
||||||
->with('action',$request->action)
|
|
||||||
->with('o',$o)
|
|
||||||
->with('errors',new ViewErrorBag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -694,9 +689,16 @@ class SystemController extends Controller
|
|||||||
$this->authorize('update_nn',$o);
|
$this->authorize('update_nn',$o);
|
||||||
session()->flash('accordion','session');
|
session()->flash('accordion','session');
|
||||||
|
|
||||||
|
// Remove the subscription to file/echo areas for each address affected
|
||||||
|
foreach ($o->akas->where('zone_id',$zo->id) as $ao) {
|
||||||
|
$ao->echoareas()->detach();
|
||||||
|
$ao->fileareas()->detach();
|
||||||
|
}
|
||||||
|
|
||||||
$o->sessions()->detach($zo);
|
$o->sessions()->detach($zo);
|
||||||
|
|
||||||
return redirect()->to(sprintf('system/addedit/%d',$o->id));
|
return redirect()
|
||||||
|
->to(sprintf('system/addedit/%d',$o->id));
|
||||||
}
|
}
|
||||||
// @todo Can this be consolidated with system_register()
|
// @todo Can this be consolidated with system_register()
|
||||||
|
|
||||||
|
@@ -64,10 +64,17 @@ class UserController extends Controller
|
|||||||
'code', 'Invalid Code!'
|
'code', 'Invalid Code!'
|
||||||
);
|
);
|
||||||
|
|
||||||
return back()->withErrors($validator);
|
return back()
|
||||||
|
->withInput()
|
||||||
|
->withErrors($validator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('user.link');
|
return view('user.link');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function whoami(): User
|
||||||
|
{
|
||||||
|
return Auth::user();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -10,7 +10,7 @@ use App\Models\{Address,Zone};
|
|||||||
class ZoneController extends Controller
|
class ZoneController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Add or edit a node
|
* Add or edit a zone
|
||||||
*/
|
*/
|
||||||
public function add_edit(Request $request,Zone $o)
|
public function add_edit(Request $request,Zone $o)
|
||||||
{
|
{
|
||||||
@@ -69,7 +69,7 @@ class ZoneController extends Controller
|
|||||||
$o->save();
|
$o->save();
|
||||||
$zo = Zone::where('zone_id',$request->zone_id)
|
$zo = Zone::where('zone_id',$request->zone_id)
|
||||||
->where('domain_id',$request->domain_id)
|
->where('domain_id',$request->domain_id)
|
||||||
->singleOrFail();
|
->sole();
|
||||||
|
|
||||||
// Find the zones 0/0 address, and assign it to this host.
|
// Find the zones 0/0 address, and assign it to this host.
|
||||||
$ao = Address::where('zone_id',$zo->id)
|
$ao = Address::where('zone_id',$zo->id)
|
||||||
|
@@ -1,83 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
|
||||||
|
|
||||||
class Kernel extends HttpKernel
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The application's global HTTP middleware stack.
|
|
||||||
*
|
|
||||||
* These middleware are run during every request to your application.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $middleware = [
|
|
||||||
\App\Http\Middleware\CheckForMaintenanceMode::class,
|
|
||||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
|
||||||
\App\Http\Middleware\TrimStrings::class,
|
|
||||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
|
||||||
\App\Http\Middleware\TrustProxies::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The application's route middleware groups.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $middlewareGroups = [
|
|
||||||
'web' => [
|
|
||||||
\App\Http\Middleware\EncryptCookies::class,
|
|
||||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
|
||||||
\Illuminate\Session\Middleware\StartSession::class,
|
|
||||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
|
||||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
|
||||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
|
||||||
\App\Http\Middleware\AddUserToView::class,
|
|
||||||
],
|
|
||||||
|
|
||||||
'api' => [
|
|
||||||
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
|
||||||
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
|
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The application's route middleware.
|
|
||||||
*
|
|
||||||
* These middleware may be assigned to groups or used individually.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $routeMiddleware = [
|
|
||||||
'activeuser' => \App\Http\Middleware\ActiveUser::class,
|
|
||||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
|
||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
|
||||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
|
||||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
|
||||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
|
||||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
|
||||||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
|
||||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
|
||||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The priority-sorted list of middleware.
|
|
||||||
*
|
|
||||||
* This forces the listed middleware to always be in the given order.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $middlewarePriority = [
|
|
||||||
\Illuminate\Session\Middleware\StartSession::class,
|
|
||||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
|
||||||
\App\Http\Middleware\Authenticate::class,
|
|
||||||
\Illuminate\Session\Middleware\AuthenticateSession::class,
|
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
|
||||||
\Illuminate\Auth\Middleware\Authorize::class,
|
|
||||||
];
|
|
||||||
}
|
|
@@ -5,6 +5,9 @@ namespace App\Http\Middleware;
|
|||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
use Illuminate\Contracts\Auth\Authenticatable;
|
use Illuminate\Contracts\Auth\Authenticatable;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
|
||||||
|
use App\Models\Setup;
|
||||||
|
|
||||||
class AddUserToView
|
class AddUserToView
|
||||||
{
|
{
|
||||||
@@ -43,7 +46,10 @@ class AddUserToView
|
|||||||
*/
|
*/
|
||||||
public function handle($request, Closure $next)
|
public function handle($request, Closure $next)
|
||||||
{
|
{
|
||||||
|
Config::set('setup',$x=Setup::find(config('app.id'))->load('system'));
|
||||||
|
|
||||||
$this->factory->share('user',$this->user);
|
$this->factory->share('user',$this->user);
|
||||||
|
$this->factory->share('setup',$x);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
|
||||||
|
|
||||||
class Authenticate extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Get the path the user should be redirected to when they are not authenticated.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function redirectTo($request)
|
|
||||||
{
|
|
||||||
if (! $request->expectsJson()) {
|
|
||||||
return route('login');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
|
|
||||||
|
|
||||||
class CheckForMaintenanceMode extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The URIs that should be reachable while maintenance mode is enabled.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $except = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Middleware;
|
|
||||||
|
|
||||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
|
||||||
|
|
||||||
class EncryptCookies extends Middleware
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The names of the cookies that should not be encrypted.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $except = [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user