Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
e65e664792 | |||
4f6e1e90c6 |
61
.env.example
61
.env.example
@ -1,25 +1,55 @@
|
||||
APP_NAME="Clearing Houz"
|
||||
APP_ENV=production
|
||||
APP_KEY=
|
||||
APP_MAINTENANCE_DRIVER=cache
|
||||
APP_MAINTENANCE_STORE=memcached
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://clrghouz
|
||||
APP_TIMEZONE=
|
||||
APP_URL=
|
||||
|
||||
AUTH_PASSWORD_RESET_TOKEN_TABLE=password_resets
|
||||
|
||||
CACHE_STORE=memcached
|
||||
MEMCACHED_HOST=memcached
|
||||
LOG_CHANNEL=stack
|
||||
LOG_LEVEL=info
|
||||
|
||||
DB_CONNECTION=pgsql
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_DATABASE=clrghouz
|
||||
DB_USERNAME=clrghouz
|
||||
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
|
||||
SESSION_DRIVER=file
|
||||
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
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
@ -28,23 +58,6 @@ AWS_ENDPOINT=
|
||||
AWS_DEFAULT_REGION=home
|
||||
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=
|
||||
|
@ -74,7 +74,8 @@ jobs:
|
||||
( dockerd --host=tcp://0.0.0.0:2375 --tls=false & ) && sleep 3
|
||||
## Some debugging info
|
||||
# docker info && docker version
|
||||
# env|sort
|
||||
env|sort
|
||||
echo "PRT: ${{ secrets.PKG_WRITE_TOKEN }}"
|
||||
|
||||
- name: Registry FQDN Setup
|
||||
id: registry
|
||||
@ -92,10 +93,12 @@ jobs:
|
||||
- name: Code Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Record version and Delete Unnecessary files
|
||||
- name: Record version
|
||||
run: |
|
||||
pwd
|
||||
ls -al
|
||||
echo ${GITHUB_SHA::8} > VERSION
|
||||
rm -rf .git* tests/ storage/app/test/
|
||||
cat VERSION
|
||||
|
||||
- name: Build and Push Docker Image
|
||||
uses: docker/build-push-action@v5
|
||||
|
@ -1,9 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Casts;
|
||||
namespace App\Casts;
|
||||
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class CollectionOrNull implements CastsAttributes
|
||||
@ -11,13 +10,13 @@ class CollectionOrNull implements CastsAttributes
|
||||
/**
|
||||
* Cast the given value.
|
||||
*
|
||||
* @param Model $model
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return Collection
|
||||
*/
|
||||
public function get(Model $model,string $key,$value,array $attributes): Collection
|
||||
public function get($model,string $key,$value,array $attributes): Collection
|
||||
{
|
||||
return collect(json_decode($value, true));
|
||||
}
|
||||
@ -31,7 +30,7 @@ class CollectionOrNull implements CastsAttributes
|
||||
* @param array $attributes
|
||||
* @return string|null
|
||||
*/
|
||||
public function set(Model $model,string $key,$value,array $attributes): ?string
|
||||
public function set($model,string $key,$value,array $attributes): ?string
|
||||
{
|
||||
return ($value->count()) ? json_encode($value) : NULL;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Casts;
|
||||
namespace App\Casts;
|
||||
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Casts;
|
||||
namespace App\Casts;
|
||||
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
@ -100,7 +100,7 @@ class HubStats extends Dynamic
|
||||
$o->uncollected_echomail ?? 0,
|
||||
$o->uncollected_netmail ?? 0,
|
||||
$o->uncollected_files ?? 0,
|
||||
$o->system->last_seen?->format('Y-m-d H:i') ?: '-',
|
||||
$o->system->last_session?->format('Y-m-d H:i'),
|
||||
is_null($o->system->pollmode) ? 'HOLD' : ($o->system->pollmode ? 'CRASH' : 'DAILY'),
|
||||
$o->system->autohold ? 'YES' : 'NO');
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class NodelistSegment extends Dynamic
|
||||
$result->push('CM');
|
||||
|
||||
if ($ao->system->address) {
|
||||
$result->push(sprintf('INA:%s',our_address($ao->domain)->contains($ao->id) ? our_hostname($ao) : $ao->system->address));
|
||||
$result->push(sprintf('INA:%s',$ao->system->address));
|
||||
|
||||
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 : ''));
|
||||
|
@ -17,7 +17,7 @@ abstract class FTN
|
||||
$this->fn,
|
||||
$this->ff,
|
||||
$this->fp,
|
||||
).((isset($this->zone) && $this->zone) ? sprintf('@%s',$this->zone->domain->name) : '');
|
||||
).($this->zone ? sprintf('@%s',$this->zone->domain->name) : '');
|
||||
|
||||
case 'tftn_t':
|
||||
return sprintf('%d:%d/%d.%d',
|
||||
@ -25,7 +25,7 @@ abstract class FTN
|
||||
$this->tn,
|
||||
$this->tf,
|
||||
$this->tp,
|
||||
).((isset($this->zone) && $this->zone) ? sprintf('@%s',$this->zone->domain->name) : '');
|
||||
).($this->zone ? sprintf('@%s',$this->zone->domain->name) : '');
|
||||
|
||||
case 'fftn':
|
||||
return Address::findFTN($this->fftn_t);
|
||||
|
@ -89,7 +89,7 @@ class Message extends FTNBase
|
||||
public const AREATAG_LEN = 35; //
|
||||
|
||||
private array $header; // Message Header
|
||||
private Collection $kludges; // TZUTC that needs to be converted to be used by Carbon @see self::kludges
|
||||
private int $tzutc = 0; // 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 Address $us; // Our address for this message
|
||||
|
||||
@ -233,7 +233,7 @@ class Message extends FTNBase
|
||||
$o->mo->from = $o->header['user_from'];
|
||||
$o->mo->subject = $o->header['subject'];
|
||||
|
||||
$o->mo->datetime = $o->datetime->clone()->utc();
|
||||
$o->mo->datetime = $o->datetime;
|
||||
$o->mo->tzoffset = $o->datetime->utcOffset();
|
||||
$o->mo->flags = $o->header['flags'];
|
||||
$o->mo->cost = $o->header['cost'];
|
||||
@ -294,7 +294,6 @@ class Message extends FTNBase
|
||||
public function __construct(Zone $zone)
|
||||
{
|
||||
$this->zone = $zone;
|
||||
$this->kludges = collect();
|
||||
}
|
||||
|
||||
public function __get($key)
|
||||
@ -307,7 +306,7 @@ class Message extends FTNBase
|
||||
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 '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 'fzone':
|
||||
@ -324,7 +323,6 @@ class Message extends FTNBase
|
||||
return Zone::where('zone_id',$this->fz)
|
||||
->where('default',TRUE)
|
||||
->single();
|
||||
|
||||
case 'fdomain':
|
||||
// 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')))
|
||||
@ -342,7 +340,7 @@ class Message extends FTNBase
|
||||
case 'tz': return (int)Arr::get($this->isEchomail() ? $this->src : $this->dst,'z');
|
||||
case 'tn': return (int)Arr::get($this->header,'dnet');
|
||||
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':
|
||||
// Use the zone if this class was called with it.
|
||||
@ -477,25 +475,11 @@ class Message extends FTNBase
|
||||
|
||||
break;
|
||||
|
||||
case 'tzutc':
|
||||
return $this->kludges->get($key);
|
||||
|
||||
default:
|
||||
throw new \Exception('Unknown key: '.$key);
|
||||
}
|
||||
}
|
||||
|
||||
public function __set(string $key,mixed $value): void
|
||||
{
|
||||
switch ($key) {
|
||||
case 'tzutc':
|
||||
if (! is_numeric($value))
|
||||
throw new InvalidPacketException('TZUTC is not numeric '.$value);
|
||||
|
||||
$this->kludges->put($key,$value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export an FTN message, ready for sending.
|
||||
*
|
||||
@ -504,6 +488,8 @@ class Message extends FTNBase
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
$s = Setup::findOrFail(config('app.id'));
|
||||
|
||||
$return = pack(collect(self::HEADER)->pluck(1)->join(''),
|
||||
$this->mo->fftn->node_id, // Originating Node
|
||||
$this->mo->tftn->node_id, // Destination Node
|
||||
@ -519,6 +505,9 @@ class Message extends FTNBase
|
||||
$return .= $this->mo->subject."\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));
|
||||
|
||||
if ((! $this->mo->kludges->has('FMPT')) && $this->mo->fftn->point_id)
|
||||
$this->mo->kludges->put('FMPT',$this->mo->fftn->point_id);
|
||||
@ -527,16 +516,12 @@ class Message extends FTNBase
|
||||
$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,Setup::version()));
|
||||
$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('DBID:',$this->mo->id);
|
||||
|
||||
if ($this->mo instanceof Echomail)
|
||||
$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
|
||||
$return .= sprintf("\01TZUTC: %s\r",str_replace('+','',$this->mo->date->getOffsetString('')));
|
||||
|
||||
@ -559,19 +544,14 @@ class Message extends FTNBase
|
||||
$return .= sprintf("\x01Via %s @%s.UTC %s %s\r",
|
||||
$this->us->ftn3d,
|
||||
Carbon::now()->format('Ymd.His'),
|
||||
Setup::PRODUCT_NAME_SHORT,Setup::version());
|
||||
Setup::PRODUCT_NAME_SHORT,$s->version);
|
||||
|
||||
} else {
|
||||
// FTS-0004.001/FSC-0068.001 The message SEEN-BY 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
|
||||
$path = $this
|
||||
->mo
|
||||
->path
|
||||
->push($this->us)
|
||||
->unique('ftn')
|
||||
->filter(fn($item)=>is_null($item->point_id) || ($item->point_id === 0));
|
||||
$path = $this->mo->path->push($this->us)->unique('ftn')->filter(fn($item)=>($item->point_id === 0));
|
||||
|
||||
// Create our rogue seenby objects
|
||||
$seenby = $this->mo->seenby;
|
||||
@ -585,7 +565,7 @@ class Message extends FTNBase
|
||||
|
||||
$seenby = $seenby
|
||||
->push($this->us)
|
||||
->filter(fn($item)=>is_null($item->point_id) || ($item->point_id === 0))
|
||||
->filter(fn($item)=>($item->point_id === 0))
|
||||
->unique('ftn')
|
||||
->sortBy(function($item) { return sprintf('%05d%05d',$item->host_id,$item->node_id);});
|
||||
|
||||
@ -667,9 +647,7 @@ class Message extends FTNBase
|
||||
|
||||
// First find our kludge lines
|
||||
$ptr_start = 0;
|
||||
$ptr_end = 0;
|
||||
|
||||
try {
|
||||
while (substr($message,$ptr_start,1) === "\x01") {
|
||||
$ptr_end = strpos($message,"\r",$ptr_start);
|
||||
|
||||
@ -812,15 +790,6 @@ class Message extends FTNBase
|
||||
$o->kludges = [$m[1],$m[2]];
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('%s:! Error parsing message, now at offset [0x%02x] (%s)',
|
||||
self::LOGKEY,
|
||||
$ptr_start,
|
||||
$e->getMessage()),['dump'=>hex_dump($message)]);
|
||||
|
||||
throw new InvalidPacketException('Error parsing message');
|
||||
}
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
@ -876,17 +845,11 @@ class Message extends FTNBase
|
||||
$validator->after(function($validator) {
|
||||
if ($this->zone->domain->flatten) {
|
||||
if (! $this->zone->domain->zones->pluck('zone_id')->contains($this->fz))
|
||||
$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));
|
||||
$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));
|
||||
|
||||
} else {
|
||||
if ($this->zone->zone_id !== $this->fz)
|
||||
$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));
|
||||
$validator->errors()->add('invalid-zone',sprintf('Message zone [%d] doesnt match packet zone [%d].',$this->fz,$this->zone->zone_id));
|
||||
}
|
||||
|
||||
if (! $this->fftn)
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace App\Classes\FTN;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@ -12,8 +12,8 @@ use Symfony\Component\HttpFoundation\File\File;
|
||||
|
||||
use App\Classes\FTN as FTNBase;
|
||||
use App\Exceptions\InvalidPacketException;
|
||||
use App\Models\{Address,Echomail,Netmail,Software,System,Zone};
|
||||
use App\Notifications\Netmails\{EchomailBadAddress,NetmailBadAddress};
|
||||
use App\Models\{Address,Domain,Echomail,Netmail,Software,System,Zone};
|
||||
use App\Notifications\Netmails\EchomailBadAddress;
|
||||
|
||||
/**
|
||||
* Represents a Fidonet Packet, that contains an array of messages.
|
||||
@ -88,11 +88,11 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
||||
* @param mixed $f File handler returning packet data
|
||||
* @param string $name
|
||||
* @param int $size
|
||||
* @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 Domain|null $domain
|
||||
* @return Packet
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public static function process(mixed $f,string $name,int $size,System $so=NULL): self
|
||||
public static function process(mixed $f,string $name,int $size,Domain $domain=NULL): self
|
||||
{
|
||||
Log::debug(sprintf('%s:+ Opening Packet [%s] with size [%d]',self::LOGKEY,$name,$size));
|
||||
|
||||
@ -139,34 +139,25 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
||||
} else
|
||||
throw new InvalidPacketException('Not a valid packet, not EOP or SOM:'.bin2hex($x));
|
||||
|
||||
Log::info(sprintf('%s:- Packet [%s] is a [%s] packet',self::LOGKEY,$o->name,get_class($o)));
|
||||
Log::info(sprintf('%s:- Packet [%s] is a [%s] packet, dated [%s]',self::LOGKEY,$o->name,get_class($o),$o->date));
|
||||
|
||||
if ($o->fz && ($o->fd || $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));
|
||||
}
|
||||
// Work out the packet zone
|
||||
if ($o->fz && ($o->fd || $domain)) {
|
||||
$o->zone = Zone::select('zones.*')
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->where('zone_id',$o->fz)
|
||||
->where('name',$o->fd ?: $domain->name)
|
||||
->single();
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
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));
|
||||
|
||||
try {
|
||||
$o->zone = Zone::where('zone_id',$o->fz)
|
||||
->where('default',TRUE)
|
||||
->singleOrFail();
|
||||
|
||||
} 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
|
||||
$msgbuf = '';
|
||||
@ -182,7 +173,6 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
||||
|| (($end=strpos($msgbuf,"\x00".self::PACKED_END,$leader)) !== FALSE))
|
||||
{
|
||||
// Parse our message
|
||||
Log::debug(sprintf('%s:- Message at offset [%d] in [%s]',self::LOGKEY,$read_ptr-strlen($readbuf),$name));
|
||||
$o->parseMessage(substr($msgbuf,0,$end));
|
||||
|
||||
$msgbuf = substr($msgbuf,$end+3);
|
||||
@ -194,7 +184,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
||||
}
|
||||
|
||||
// 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)
|
||||
@ -371,7 +361,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
||||
|
||||
$this->content .= "\00\00";
|
||||
|
||||
$this->messages = $msgs->map(fn($item)=>$item->only(['id','datetime']));
|
||||
$this->messages = $msgs->map(fn($item)=>$item->only(['id','date']));
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -394,10 +384,10 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
||||
|
||||
// If the messages is not for the right zone, we'll ignore it
|
||||
if ($msg->errors->has('invalid-zone')) {
|
||||
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));
|
||||
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));
|
||||
|
||||
if (! $msg->kludges->get('RESCANNED'))
|
||||
Notification::route('netmail',$this->fftn)->notify(($msg instanceof Echomail) ? new EchomailBadAddress($msg) : new NetmailBadAddress($msg));
|
||||
Notification::route('netmail',$this->fftn)->notify(new EchomailBadAddress($msg));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -406,7 +396,7 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
||||
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')));
|
||||
|
||||
$ao = Address::findFTN($msg->set->get('set_fftn'),TRUE,TRUE);
|
||||
$ao = Address::findFTN($msg->set->get('set_fftn'),TRUE);
|
||||
|
||||
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));
|
||||
@ -417,20 +407,17 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
||||
if (! $ao) {
|
||||
$so = System::createUnknownSystem();
|
||||
$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->errors->forget('from');
|
||||
$msg->errors->forget('fftn_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));
|
||||
}
|
||||
|
||||
// If the $msg->tftn doesnt exist, we'll need to create it
|
||||
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 ($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));
|
||||
@ -441,13 +428,10 @@ abstract class Packet extends FTNBase implements \Iterator, \Countable
|
||||
if (! $ao) {
|
||||
$so = System::createUnknownSystem();
|
||||
$ao = Address::createFTN($msg->set->get('set_fftn'),$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->errors->forget('to');
|
||||
$msg->errors->forget('tftn_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));
|
||||
}
|
||||
|
||||
// If there is no fftn, then its from a system that we dont know about
|
||||
|
@ -65,7 +65,7 @@ final class FSC39 extends Packet
|
||||
*/
|
||||
protected function header(Collection $msgs): string
|
||||
{
|
||||
$oldest = $this->messages->sortBy('date')->last();
|
||||
$oldest = $this->messages->sortBy('datetime')->last();
|
||||
|
||||
return pack(collect(self::HEADER)->pluck(1)->join(''),
|
||||
$this->fftn_p->node_id, // Orig Node
|
||||
|
@ -55,10 +55,6 @@ final class FSC48 extends Packet
|
||||
case 'capability':
|
||||
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:
|
||||
return parent::__get($key);
|
||||
}
|
||||
|
@ -6,10 +6,9 @@ use Illuminate\Support\Facades\Notification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Classes\FTN\Process;
|
||||
use App\Classes\FTN\Process\Netmail\Robot\Unknown;
|
||||
use App\Models\{Echomail,Netmail};
|
||||
use App\Notifications\Netmails\FixCantHandle;
|
||||
use App\Notifications\Netmails\Areafix\{CommandsProcessed,InvalidPassword,NotConfiguredHere};
|
||||
use App\Notifications\Netmails\Areafix as AreafixNotification;
|
||||
use App\Notifications\Netmails\Areafix\NotConfiguredHere as AreafixNotConfiguredHereNotification;
|
||||
|
||||
/**
|
||||
* Process messages to Ping
|
||||
@ -20,102 +19,18 @@ final class Areafix extends Process
|
||||
{
|
||||
private const LOGKEY = 'RP-';
|
||||
|
||||
public const areafix_commands = 'App\\Classes\\FTN\\Process\\Netmail\\Robot\\Areafix\\';
|
||||
|
||||
public static function handle(Echomail|Netmail $mo): bool
|
||||
{
|
||||
if (((strtolower($mo->to) !== 'areafix') && (strtolower($mo->to) !== 'filefix')) || (! ($mo instanceof Netmail)))
|
||||
if (strtolower($mo->to) !== 'areafix')
|
||||
return FALSE;
|
||||
|
||||
Log::info(sprintf('%s:- Processing *FIX [%s] message from (%s) [%s]',self::LOGKEY,$mo->to,$mo->from,$mo->fftn->ftn));
|
||||
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 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 self::areafix($mo);
|
||||
|
||||
if ((strtolower($mo->to) === 'filefix'))
|
||||
return self::filefix($mo);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
public static function areafix(Netmail $mo): bool
|
||||
{
|
||||
$result = collect();
|
||||
$result->push('--> BEGIN <--');
|
||||
|
||||
foreach ($mo->body_lines as $command) {
|
||||
// Skip empty lines
|
||||
if (! $command)
|
||||
continue;
|
||||
|
||||
$command = explode(' ',strtoupper(trim($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::debug(sprintf('%s:= We got a tearline/tagline, end of processing',self::LOGKEY));
|
||||
|
||||
$result->push('--> END OF PROCESSING <--');
|
||||
|
||||
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::areafix_commands.'AreaList';
|
||||
break;
|
||||
|
||||
default:
|
||||
// Parse the message body and pluck out the commands on each line
|
||||
$class = self::areafix_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;
|
||||
}
|
||||
|
||||
public static function filefix(Netmail $mo): bool
|
||||
{
|
||||
Notification::route('netmail',$mo->fftn)->notify(new FixCantHandle($mo));
|
||||
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;
|
||||
}
|
||||
|
@ -1,143 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||
|
||||
use Carbon\Carbon;
|
||||
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 command starts with '-', its to unsubscribe
|
||||
if (str_starts_with($this->arguments[0],'-')) {
|
||||
$sub = FALSE;
|
||||
$area = substr($this->arguments[0],1);
|
||||
|
||||
} elseif (str_starts_with($this->arguments[0],'+')) {
|
||||
$sub = TRUE;
|
||||
$area = substr($this->arguments[0],1);
|
||||
|
||||
} else {
|
||||
$sub = TRUE;
|
||||
$area = $this->arguments[0];
|
||||
}
|
||||
|
||||
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->format('Y-m-d H:i')));
|
||||
|
||||
return sprintf('%-25s <-- ALREADY subscribed since %s',$command,$nea->pivot->subscribed->format('Y-m-d H:i'));
|
||||
|
||||
// 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)
|
||||
->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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
<?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;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
<?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\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::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);
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||
|
||||
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 (! is_numeric($this->arguments[1]))
|
||||
return sprintf('%-25s <-- INVALID, DAYS [%s] NOT NUMERIC',$command,$this->arguments[1]);
|
||||
|
||||
// Area exists
|
||||
if ($ea=$this->mo->fftn->domain->echoareas->where('name',$this->arguments[0])->pop()) {
|
||||
// If already subscribed
|
||||
if ($this->mo->fftn->echoareas->pluck('name')->contains($this->arguments[0])) {
|
||||
AreafixRescan::dispatch($this->mo->fftn,$ea,$this->arguments[1],TRUE)
|
||||
->onQueue('mail');
|
||||
|
||||
Log::debug(sprintf('%s:- FTN [%s] RESCAN [%s] DAYS [%d]',self::LOGKEY,$this->mo->fftn->ftn,$this->arguments[0],$this->arguments[1]));
|
||||
|
||||
return sprintf('%-25s <-- RESCAN [%d] DAYS queued',$command,$this->arguments[1]);
|
||||
|
||||
// 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:- FTN [%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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\FTN\Process\Netmail\Robot\Areafix;
|
||||
|
||||
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 (! is_numeric($this->arguments[1]))
|
||||
return sprintf('%-25s <-- INVALID, DAYS [%s] NOT NUMERIC',$command,$this->arguments[1]);
|
||||
|
||||
// Area exists
|
||||
if ($ea=$this->mo->fftn->domain->echoareas->where('name',$this->arguments[0])->pop()) {
|
||||
// If already subscribed
|
||||
if ($this->mo->fftn->echoareas->pluck('name')->contains($this->arguments[0])) {
|
||||
AreafixRescan::dispatch($this->mo->fftn,$ea,$this->arguments[1])
|
||||
->onQueue('mail');
|
||||
|
||||
Log::debug(sprintf('%s:- FTN [%s] SCAN [%s] DAYS [%d]',self::LOGKEY,$this->mo->fftn->ftn,$this->arguments[0],$this->arguments[1]));
|
||||
|
||||
return sprintf('%-25s <-- SCAN [%d] DAYS queued',$command,$this->arguments[1]);
|
||||
|
||||
// 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:- FTN [%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);
|
||||
}
|
||||
}
|
||||
}
|
@ -83,11 +83,6 @@ class File extends FileBase implements \Iterator
|
||||
|
||||
/* METHODS */
|
||||
|
||||
public function isArchive(): bool
|
||||
{
|
||||
return $this->isArchive;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* + Netmail/Echomail/TIC
|
||||
* + name is the hex ID of the youngest item in the mail bundle
|
||||
* + name is dynamically calculated, based on timew() of the youngest item in the mail bundle
|
||||
* + size is dynamically calculated based on the size of the bundle
|
||||
* + mtime is dynamically calculated, based on the age of the youngest item
|
||||
* + sendas (nameas) is name + [.pkt|.tic]
|
||||
|
@ -86,9 +86,11 @@ final class File extends Send
|
||||
$this->size = $this->f->size;
|
||||
|
||||
// If sending file is a File::class, then our file is s3
|
||||
$this->fd = ($this->nameas && $this->f instanceof FileModel)
|
||||
? Storage::readStream($this->f->rel_name)
|
||||
: fopen($this->full_name,'rb');
|
||||
if ($this->nameas && $this->f instanceof FileModel) {
|
||||
$this->fd = Storage::readStream($this->f->rel_name);
|
||||
|
||||
} else {
|
||||
$this->fd = fopen($this->full_name,'rb');
|
||||
|
||||
if (! $this->fd) {
|
||||
Log::error(sprintf('%s:! Unable to open file [%s] for reading',self::LOGKEY,$this->full_name));
|
||||
@ -97,6 +99,7 @@ final class File extends Send
|
||||
}
|
||||
|
||||
Log::info(sprintf('%s:= File [%s] opened with size [%d]',self::LOGKEY,$this->full_name,$this->size));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -108,6 +111,6 @@ final class File extends Send
|
||||
|
||||
public function seek(int $pos): bool
|
||||
{
|
||||
return (fseek($this->fd,$pos,SEEK_SET) === 0);
|
||||
return (fseek($this->f,$pos,SEEK_SET) === 0);
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ namespace App\Classes\File;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@ -37,13 +36,13 @@ final class Mail extends Send
|
||||
return $this->f->messages->pluck('id');
|
||||
|
||||
case 'name':
|
||||
return sprintf('%08x',$this->youngest_id());
|
||||
return sprintf('%08x',timew($this->youngest()));
|
||||
|
||||
case 'nameas':
|
||||
return sprintf('%s.pkt',$this->name);
|
||||
|
||||
case 'mtime':
|
||||
return $this->youngest_date()->timestamp;
|
||||
return $this->youngest()->timestamp;
|
||||
|
||||
case 'type':
|
||||
return ($this->ftype&0xff00)>>8;
|
||||
@ -115,18 +114,8 @@ final class Mail extends Send
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
private function youngest(): array
|
||||
private function youngest(): Carbon
|
||||
{
|
||||
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());
|
||||
return $this->f->messages->pluck('date')->sort()->last();
|
||||
}
|
||||
}
|
@ -128,9 +128,9 @@ class Receive extends Base
|
||||
// If packet is greater than a size, lets queue it
|
||||
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));
|
||||
PacketProcess::dispatch($this->receiving->rel_name,$this->ao->system,FALSE,$rcvd_time);
|
||||
PacketProcess::dispatch($this->receiving->rel_name,$this->ao->zone->domain,FALSE,$rcvd_time);
|
||||
} else
|
||||
PacketProcess::dispatchSync($this->receiving->rel_name,$this->ao->system,TRUE,$rcvd_time);
|
||||
PacketProcess::dispatchSync($this->receiving->rel_name,$this->ao->zone->domain,TRUE,$rcvd_time);
|
||||
|
||||
} 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()));
|
||||
|
@ -125,9 +125,6 @@ class Send extends Base
|
||||
if ($successful) {
|
||||
$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()));
|
||||
|
||||
} else {
|
||||
Log::alert(sprintf('%s:- Closing [%s], file NOT SENT successfully',self::LOGKEY,$this->sending->nameas));
|
||||
}
|
||||
|
||||
$this->sending->close($successful,$node);
|
||||
@ -204,7 +201,7 @@ class Send extends Base
|
||||
|
||||
// Files
|
||||
if (($x=$ao->filesWaiting())->count()) {
|
||||
Log::info(sprintf('%s:- [%d] Files(s) added for sending to [%s]',self::LOGKEY,$x->count(),$ao->ftn));
|
||||
Log::debug(sprintf('%s:- [%d] Files(s) added for sending to [%s]',self::LOGKEY,$x->count(),$ao->ftn));
|
||||
|
||||
// Add Files
|
||||
foreach ($x as $fo) {
|
||||
@ -227,12 +224,12 @@ class Send extends Base
|
||||
*/
|
||||
public function open(string $compress=''): bool
|
||||
{
|
||||
Log::debug(sprintf('%s:+ File Open to send',self::LOGKEY));
|
||||
Log::debug(sprintf('%s:+ Opening file to send',self::LOGKEY));
|
||||
|
||||
if ((($this->index=$this->list->search(function($item) { return $item->complete === FALSE; })) !== FALSE)
|
||||
&& $this->sending->open())
|
||||
{
|
||||
Log::info(sprintf('%s:- Content Send item [#%d] (%s) with size [%d]',self::LOGKEY,$this->index,$this->sending->nameas,$this->sending->size));
|
||||
Log::info(sprintf('%s:- Sending item [%d] (%s)',self::LOGKEY,$this->index,$this->sending->nameas));
|
||||
|
||||
$this->pos = 0;
|
||||
$this->start = time();
|
||||
@ -245,10 +242,7 @@ class Send extends Base
|
||||
return TRUE;
|
||||
|
||||
} else {
|
||||
Log::error(sprintf('%s:- No files to open',self::LOGKEY));
|
||||
$this->index = NULL;
|
||||
|
||||
return FALSE;
|
||||
throw new Exception('No files to open');
|
||||
}
|
||||
}
|
||||
|
||||
@ -309,7 +303,7 @@ class Send extends Base
|
||||
|
||||
$this->pos += strlen($data);
|
||||
|
||||
Log::debug(sprintf('%s:- Content Read [%d] bytes, pos now [%d]',self::LOGKEY,strlen($data),$this->pos));
|
||||
Log::debug(sprintf('%s:- Read [%d] bytes, file pos now [%d]',self::LOGKEY,strlen($data),$this->pos));
|
||||
|
||||
return $data;
|
||||
}
|
||||
@ -329,7 +323,7 @@ class Send extends Base
|
||||
if ($this->sending->seek($pos)) {
|
||||
$this->pos = $pos;
|
||||
|
||||
Log::debug(sprintf('%s:= Content Seek to [%d]',self::LOGKEY,$this->pos));
|
||||
Log::debug(sprintf('%s:= Seeked to [%d]',self::LOGKEY,$this->pos));
|
||||
|
||||
return TRUE;
|
||||
|
||||
|
@ -47,7 +47,7 @@ final class Dynamic extends Send
|
||||
return $this->sent->timestamp;
|
||||
|
||||
case 'size':
|
||||
return $this->{$key};
|
||||
return strlen($this->buffer);
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
@ -104,7 +104,6 @@ final class Dynamic extends Send
|
||||
public function open(string $compress=''): bool
|
||||
{
|
||||
$this->buffer = (string)$this->item;
|
||||
$this->size = strlen($this->buffer);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ class Node
|
||||
private Collection $ftns_authed; // The FTNs we have validated
|
||||
private Collection $ftns_other; // Other FTN addresses presented
|
||||
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
|
||||
|
||||
@ -85,10 +84,6 @@ class Node
|
||||
|
||||
// The nodes 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 ($this->ftns_authed->count())
|
||||
return $this->ftns_authed->first()->pass_session;
|
||||
@ -199,8 +194,6 @@ class Node
|
||||
throw new Exception('Already authed');
|
||||
|
||||
foreach ($this->ftns as $o) {
|
||||
Log::debug(sprintf('%s:- Attempting to authenticate [%s] with [%s]',self::LOGKEY,$o->ftn,$o->pass_session));
|
||||
|
||||
if (! $sespass=$o->pass_session)
|
||||
continue;
|
||||
|
||||
@ -276,8 +269,7 @@ class Node
|
||||
*/
|
||||
public function originate(Address $o): void
|
||||
{
|
||||
$this->originate = $o;
|
||||
$this->ftns_authed = $o->system->match($o->zone,Address::NODE_ALL);
|
||||
$this->ftns_authed->push($o);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,11 +283,19 @@ class Node
|
||||
if ($this->authed)
|
||||
return TRUE;
|
||||
|
||||
Log::debug(sprintf('%s:- Making sure we called [%s] from [%s]',self::LOGKEY,$this->originate->ftn,$this->ftns->pluck('ftn')->join(',')));
|
||||
if ($this->ftns_authed->count() !== 1 || ! $this->ftns->count())
|
||||
return FALSE;
|
||||
|
||||
$this->authed = $this->ftns->pluck('ftn')->contains($this->originate->ftn);
|
||||
$ftn = $this->ftns_authed->first()->ftn;
|
||||
|
||||
return $this->authed;
|
||||
return $this->ftns->search(function($item) use ($ftn) {
|
||||
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
|
||||
|
@ -332,13 +332,13 @@ class Page
|
||||
$subtext = substr($this->text,$current_pos,$space_pos);
|
||||
}
|
||||
|
||||
// If the rest of the string will fit on the current line
|
||||
} elseif ($text_length-$current_pos < static::MSG_WIDTH-$this->x-$buffer) {
|
||||
// If the reset of the string will fit on the current line
|
||||
} elseif ($text_length-$current_pos < static::MSG_WIDTH-$buffer) {
|
||||
$subtext = substr($this->text,$current_pos);
|
||||
|
||||
// Get the next lines worth of chars, breaking on a space
|
||||
} else {
|
||||
$subtext = $this->text_substr(substr($this->text,$current_pos),static::MSG_WIDTH-$this->x-$buffer);
|
||||
$subtext = $this->text_substr(substr($this->text,$current_pos),static::MSG_WIDTH-$buffer);
|
||||
|
||||
// Include the text up to the last space
|
||||
if (substr($this->text,$current_pos+strlen($subtext),1) !== ' ')
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace App\Classes;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Classes\File\{Receive,Send};
|
||||
@ -108,7 +107,7 @@ abstract class Protocol
|
||||
public const TCP_SPEED = 115200;
|
||||
|
||||
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 */
|
||||
/** The list of files we are sending */
|
||||
protected Send $send;
|
||||
@ -135,9 +134,12 @@ abstract class Protocol
|
||||
|
||||
abstract protected function protocol_session(bool $force_queue=FALSE): int;
|
||||
|
||||
public function __construct()
|
||||
public function __construct(Setup $o=NULL)
|
||||
{
|
||||
$this->setup = Config::get('setup',Setup::findOrFail(config('app.id')));
|
||||
if ($o && ! $o->system->akas->count())
|
||||
throw new \Exception('We dont have any ACTIVE FTN addresses assigned');
|
||||
|
||||
$this->setup = $o;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -257,7 +259,7 @@ abstract class Protocol
|
||||
throw new SocketException(SocketException::CANT_ACCEPT,'Could not fork process');
|
||||
|
||||
if ($pid)
|
||||
Log::info(sprintf('%s:+ New connection from [%s], thread [%d] created',self::LOGKEY,$client->address_remote,$pid));
|
||||
Log::info(sprintf('%s:+ New connection, thread [%d] created',self::LOGKEY,$pid));
|
||||
|
||||
// Parent return ready for next connection
|
||||
return $pid;
|
||||
@ -316,7 +318,7 @@ abstract class Protocol
|
||||
Log::debug(sprintf('%s:- Presenting limited AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
||||
|
||||
} else {
|
||||
$addresses = our_address();
|
||||
$addresses = $this->setup->system->akas;
|
||||
|
||||
Log::debug(sprintf('%s:- Presenting ALL our AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
||||
}
|
||||
@ -429,8 +431,8 @@ abstract class Protocol
|
||||
|
||||
if ($so && $so->exists) {
|
||||
foreach ($this->node->aka_other as $aka)
|
||||
// @todo For disabled zones, we shouldnt refuse to record the address
|
||||
if ((! Address::findFTN($aka)) && ($oo=Address::createFTN($aka,$so))) {
|
||||
if (! Address::findFTN($aka)) {
|
||||
$oo = Address::createFTN($aka,$so);
|
||||
$oo->validated = TRUE;
|
||||
$oo->save();
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use App\Classes\Protocol as BaseProtocol;
|
||||
use App\Classes\Sock\Exception\SocketException;
|
||||
use App\Classes\Sock\SocketClient;
|
||||
use App\Exceptions\{FileGrewException,InvalidFTNException};
|
||||
use App\Models\{Address,Mailer,Setup};
|
||||
use App\Models\{Address,Mailer};
|
||||
|
||||
final class Binkp extends BaseProtocol
|
||||
{
|
||||
@ -177,8 +177,8 @@ final class Binkp extends BaseProtocol
|
||||
|
||||
$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()) {}
|
||||
while ($this->tx_left || $this->mqueue->count())
|
||||
$this->binkp_send();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
@ -195,7 +195,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('TIME %s',Carbon::now()->toRfc2822String()));
|
||||
$this->msgs(self::BPM_NUL,
|
||||
sprintf('VER %s/%s %s/%s',Setup::PRODUCT_NAME_SHORT,Setup::version(),self::PROT,self::VERSION));
|
||||
sprintf('VER %s-%s %s/%s',config('app.name'),$this->setup->version,self::PROT,self::VERSION));
|
||||
|
||||
if ($this->originate) {
|
||||
$opt = $this->capGet(self::F_NOREL,self::O_WANT) ? ' NR' : '';
|
||||
@ -393,13 +393,11 @@ final class Binkp extends BaseProtocol
|
||||
|
||||
if ($this->capGet(self::F_CRYPT,self::O_YES)) {
|
||||
Log::debug(sprintf('%s:%% Decrypting data from remote.',self::LOGKEY));
|
||||
$this->rx_buf .= ($x=$this->crypt_in->decrypt($rx_buf));
|
||||
$this->rx_buf .= $this->crypt_in->decrypt($rx_buf);
|
||||
|
||||
} else {
|
||||
$this->rx_buf .= ($x=$rx_buf);
|
||||
$this->rx_buf .= $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)));
|
||||
@ -436,7 +434,7 @@ final class Binkp extends BaseProtocol
|
||||
}
|
||||
|
||||
if (static::DEBUG)
|
||||
Log::debug(sprintf('%s:- rx_buf size [%d]',self::LOGKEY,strlen($this->rx_buf)));
|
||||
Log::debug(sprintf('%s: - binkp_recv BUFFER [%d]',self::LOGKEY,strlen($this->rx_buf)));
|
||||
|
||||
$msg = ord(substr($this->rx_buf,0,1));
|
||||
|
||||
@ -489,11 +487,6 @@ final class Binkp extends BaseProtocol
|
||||
$rc = $this->M_get($data);
|
||||
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:
|
||||
Log::debug(sprintf('%s:- GOT:Remote received, or already has a file [%s]',self::LOGKEY,$data));
|
||||
$rc = $this->M_gotskip($data);
|
||||
@ -663,7 +656,7 @@ final class Binkp extends BaseProtocol
|
||||
$offs = (int)$this->strsep($str,' ');
|
||||
$flags = $this->strsep($str,' ');
|
||||
|
||||
if ($name && is_numeric($size) && $time) {
|
||||
if ($name && $size && $time) {
|
||||
return [
|
||||
'file'=>['name'=>$name,'size'=>$size,'mtime'=>$time],
|
||||
'offs'=>$offs,
|
||||
@ -718,7 +711,7 @@ final class Binkp extends BaseProtocol
|
||||
|
||||
// If we only present limited AKAs dont validate password against akas outside of the domains we present
|
||||
} elseif (is_null(our_address($o))) {
|
||||
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(',')));
|
||||
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(',')));
|
||||
|
||||
$this->node->ftn_other = $rem_aka;
|
||||
continue;
|
||||
@ -733,7 +726,6 @@ final class Binkp extends BaseProtocol
|
||||
Log::info(sprintf('%s:- Got AKA [%s]',self::LOGKEY,$rem_aka));
|
||||
|
||||
// We'll update this address status
|
||||
// @todo this shouldnt be here, since we havent authenticated the node
|
||||
$o->validated = TRUE;
|
||||
$o->role &= ~(Address::NODE_HOLD|Address::NODE_DOWN);
|
||||
$o->save();
|
||||
@ -894,13 +886,16 @@ final class Binkp extends BaseProtocol
|
||||
//if ($this->recv->fd)
|
||||
// $this->recv->close();
|
||||
|
||||
// If we cannot understand the file, we'll send back a SKIP
|
||||
if (! ($file=$this->file_parse($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_SKIP,$buf);
|
||||
|
||||
return TRUE;
|
||||
if ($this->sessionGet(self::SE_SENDFILE))
|
||||
$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.
|
||||
@ -918,18 +913,6 @@ final class Binkp extends BaseProtocol
|
||||
|
||||
$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 {
|
||||
switch ($this->recv->open($file['offs']<0,$file['flags'])) {
|
||||
case self::FOP_ERROR:
|
||||
@ -1023,46 +1006,7 @@ final class Binkp extends BaseProtocol
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* M_GOT/M_SKIP commands
|
||||
*
|
||||
* @param string $buf
|
||||
* @return bool
|
||||
@ -1286,13 +1230,9 @@ final class Binkp extends BaseProtocol
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->optionGet(self::O_PWD)) {
|
||||
if ($this->optionGet(self::O_PWD))
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1388,9 +1328,6 @@ final class Binkp extends BaseProtocol
|
||||
if ($this->node->aka_authed) {
|
||||
$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 {
|
||||
$this->msgs(self::OK,'non-secure');
|
||||
}
|
||||
@ -1573,7 +1510,12 @@ final class Binkp extends BaseProtocol
|
||||
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) {
|
||||
Log::info(sprintf('%s:- Checking for any new mail and files to [%s]',self::LOGKEY,$ao->ftn));
|
||||
Log::debug(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->files($ao);
|
||||
@ -1590,11 +1532,11 @@ final class Binkp extends BaseProtocol
|
||||
*/
|
||||
}
|
||||
|
||||
Log::info(sprintf('%s:- We have [%d] items to send to [%s]',self::LOGKEY,$this->send->togo_count,$ao->ftn));
|
||||
Log::info(sprintf('%s:- We have [%d] items to send to [%s]',self::LOGKEY,$this->send->togo_count,$ao->system->name));
|
||||
|
||||
} else {
|
||||
// @todo We should only send netmail if unauthenticated - netmail that is direct to this node (no routing)
|
||||
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(',')));
|
||||
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(',')));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ final class DNS extends BaseProtocol
|
||||
$this->client = $client;
|
||||
$this->protocol_session();
|
||||
|
||||
Log::debug(sprintf('%s:= onConnect - Connection closed [%s]',self::LOGKEY,$client->address_remote));
|
||||
Log::info(sprintf('%s:= onConnect - Connection closed [%s]',self::LOGKEY,$client->address_remote));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ final class DNS extends BaseProtocol
|
||||
case self::DNS_TYPE_AAAA:
|
||||
case self::DNS_TYPE_SRV:
|
||||
case self::DNS_TYPE_TXT:
|
||||
Log::debug(sprintf('%s:= Looking for record [%s] for [%s]',self::LOGKEY,$this->query->type,$this->query->domain));
|
||||
Log::info(sprintf('%s:= Looking for record [%s] for [%s]',self::LOGKEY,$this->query->type,$this->query->domain));
|
||||
|
||||
$labels = clone($this->query->labels);
|
||||
$mailer = '';
|
||||
@ -232,15 +232,14 @@ final class DNS extends BaseProtocol
|
||||
// Check we have the right record
|
||||
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));
|
||||
|
||||
return $this->nameerr();
|
||||
}
|
||||
|
||||
switch ($this->query->type) {
|
||||
case self::DNS_TYPE_SRV:
|
||||
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));
|
||||
|
||||
if (($ao->system->address) && ($xx=$ao->system->mailers->where('id',$mailer->id)->pop())) {
|
||||
return $this->reply(
|
||||
self::DNS_NOERROR,
|
||||
[serialize([
|
||||
@ -251,8 +250,6 @@ final class DNS extends BaseProtocol
|
||||
]) => self::DNS_TYPE_SRV]);
|
||||
|
||||
} 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();
|
||||
}
|
||||
|
||||
@ -264,7 +261,7 @@ final class DNS extends BaseProtocol
|
||||
[serialize($ao->system->name) => self::DNS_TYPE_TXT]);
|
||||
|
||||
default:
|
||||
Log::info(sprintf('%s:= Returning [%s] for DNS query [%s]',self::LOGKEY,$ao->system->address ?: 'NO ADDRESS',$ao->ftn));
|
||||
Log::info(sprintf('%s:= Returning [%s] for DNS query [%s]',self::LOGKEY,$ao->system->address,$ao->ftn));
|
||||
|
||||
return (! $ao->system->address)
|
||||
? $this->nodata()
|
||||
|
@ -207,8 +207,8 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
// Mailer Details
|
||||
$makedata .= sprintf('{%s}{%s}{%s}{%s}',
|
||||
Setup::product_id(),
|
||||
Setup::PRODUCT_NAME_SHORT,
|
||||
Setup::version(),
|
||||
config('app.name'),
|
||||
$this->setup->version,
|
||||
'#000000' // Serial Numbers
|
||||
);
|
||||
|
||||
@ -1061,9 +1061,6 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
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
|
||||
|
||||
Log::info(sprintf('%s:- We have [%lu%s] mail, [%lu%s] files',self::LOGKEY,$this->send->mail_size,'b',$this->send->files_size,'b'));
|
||||
@ -1237,6 +1234,11 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
// Add our mail to the queue if we have authenticated
|
||||
if ($this->node->aka_authed)
|
||||
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
|
||||
while ($this->send->mail($ao)) {
|
||||
$z = new Zmodem;
|
||||
|
@ -48,7 +48,7 @@ final class SocketClient {
|
||||
/** @var string Data in the RX buffer */
|
||||
private string $rx_buf = '';
|
||||
|
||||
public function __construct (\Socket $connection,bool $originate=FALSE) {
|
||||
public function __construct (\Socket $connection) {
|
||||
$this->connection = $connection;
|
||||
|
||||
if ($this->type === SOCK_STREAM) {
|
||||
@ -56,58 +56,12 @@ final class SocketClient {
|
||||
socket_getpeername($connection,$this->address_remote,$this->port_remote);
|
||||
|
||||
// If HAPROXY is used, work get the clients address
|
||||
if ((! $originate) && config('fido.haproxy')) {
|
||||
if (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));
|
||||
|
||||
if (($x=$this->read(5,6)) === 'PROXY ')
|
||||
$vers = 1;
|
||||
|
||||
elseif (($x === "\x0d\x0a\x0d\x0a\x00\x0d") && ($this->read('5,6') === "\x0aQUIT\x0a"))
|
||||
$vers = 2;
|
||||
|
||||
else
|
||||
if ($this->read(5,12) !== "\x0d\x0a\x0d\x0a\x00\x0d\x0aQUIT\x0a")
|
||||
throw new HAproxyException('Failed to initialise HAPROXY connection');
|
||||
|
||||
switch ($vers) {
|
||||
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);
|
||||
|
||||
@ -129,6 +83,7 @@ final class SocketClient {
|
||||
|
||||
// Protocol/Address Family
|
||||
$pa = $this->read_ch(5);
|
||||
$p = NULL;
|
||||
|
||||
switch ($x=($pa>>4)&0x7) {
|
||||
case 1: // AF_INET
|
||||
@ -139,7 +94,8 @@ final class SocketClient {
|
||||
$p = 6;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new HAproxyException(sprintf('HAPROXY protocol [%d] is not handled',$x));
|
||||
}
|
||||
|
||||
switch ($x=($pa&0x7)) {
|
||||
@ -166,28 +122,21 @@ final class SocketClient {
|
||||
}
|
||||
|
||||
$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;
|
||||
|
||||
default:
|
||||
throw new HAproxyException('Failed to initialise HAPROXY connection');
|
||||
}
|
||||
$dst_port = unpack('n',$this->read(5,2));
|
||||
|
||||
$this->address_remote = $src;
|
||||
$this->port_remote = Arr::get($src_port,1);
|
||||
|
||||
Log::debug(sprintf('%s:- HAPROXY src [%s:%d] dst [%s:%d]',
|
||||
Log::info(sprintf('%s:! HAPROXY src [%s:%d] dst [%s:%d]',
|
||||
self::LOGKEY,
|
||||
$this->address_remote,
|
||||
$this->port_remote,
|
||||
$dst,
|
||||
$dst_port,
|
||||
Arr::get($dst_port,1),
|
||||
));
|
||||
}
|
||||
|
||||
Log::debug(sprintf('%s:+ Connection host [%s] on port [%d] (%s)',self::LOGKEY,$this->address_remote,$this->port_remote,$this->type));
|
||||
Log::info(sprintf('%s:+ Connection host [%s] on port [%d] (%s)',self::LOGKEY,$this->address_remote,$this->port_remote,$this->type));
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,14 +184,13 @@ final class SocketClient {
|
||||
* @param string $address
|
||||
* @param int $port
|
||||
* @return static
|
||||
* @throws SocketException|HAproxyException
|
||||
* @throws SocketException
|
||||
*/
|
||||
public static function create(string $address,int $port): self
|
||||
{
|
||||
Log::info(sprintf('%s:+ Creating connection to [%s:%d]',self::LOGKEY,$address,$port));
|
||||
|
||||
$type = collect(config('fido.ip'))
|
||||
->filter(fn($item)=>$item['enabled']);
|
||||
$sort = collect(['AAAA','A']);
|
||||
|
||||
if (filter_var($address,FILTER_VALIDATE_IP))
|
||||
$resolved = collect([[
|
||||
@ -251,15 +199,14 @@ final class SocketClient {
|
||||
]]);
|
||||
else
|
||||
// We only look at AAAA/A records
|
||||
$resolved = collect(dns_get_record($address,$type->map(fn($item)=>$item['type'])->sum()))
|
||||
->filter(fn($item)=>$type->has(Arr::get($item,'type')))
|
||||
->sort(fn($a,$b)=>$type->get(Arr::get($a,'type'))['order'] < $type->get(Arr::get($b,'type'))['order']);
|
||||
$resolved = collect(dns_get_record($address,DNS_AAAA|DNS_A))
|
||||
->filter(function($item) use ($sort) { return $sort->search(Arr::get($item,'type')) !== FALSE; })
|
||||
->sort(function($item) use ($sort) { return $sort->search(Arr::get($item,'type')); });
|
||||
|
||||
if (! $resolved->count())
|
||||
throw new SocketException(SocketException::CANT_CONNECT,sprintf('%s doesnt resolved to an IPv4/IPv6 address',$address));
|
||||
|
||||
$result = FALSE;
|
||||
$socket = NULL;
|
||||
|
||||
foreach ($resolved as $address) {
|
||||
try {
|
||||
@ -289,7 +236,7 @@ final class SocketClient {
|
||||
if ($result === FALSE)
|
||||
throw new SocketException(SocketException::CANT_CONNECT,socket_strerror(socket_last_error($socket)));
|
||||
|
||||
return new self($socket,TRUE);
|
||||
return new self($socket);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -405,7 +352,7 @@ final class SocketClient {
|
||||
Log::error(sprintf('%s:! Closing socket [%s]',self::LOGKEY,$e->getMessage()));
|
||||
}
|
||||
|
||||
Log::debug(sprintf('%s:= Connection closed with [%s]',self::LOGKEY,$this->address_remote));
|
||||
Log::info(sprintf('%s:= Connection closed with [%s]',self::LOGKEY,$this->address_remote));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,42 +0,0 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
@ -2,10 +2,10 @@
|
||||
|
||||
namespace App\Console\Commands\Areafix;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
use App\Jobs\AreafixRescan;
|
||||
use App\Models\{Address,Echoarea};
|
||||
use App\Models\{Address,Echoarea,Echomail};
|
||||
|
||||
class Rescan extends Command
|
||||
{
|
||||
@ -14,13 +14,7 @@ class Rescan extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
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}';
|
||||
protected $signature = 'areafix:rescan {ftn} {area} {days?}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@ -49,12 +43,50 @@ class Rescan extends Command
|
||||
if (! $this->argument('area'))
|
||||
throw new \Exception('Areaname is required');
|
||||
|
||||
$eo = Echoarea::where('name',$this->argument('area'))->sole();
|
||||
$eao = Echoarea::where('name',$this->argument('area'))->singleOrFail();
|
||||
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));
|
||||
|
||||
if ($this->option('queue'))
|
||||
AreafixRescan::dispatch($ao,$eo,$this->argument('days'))->onQueue($this->option('queuename'));
|
||||
else
|
||||
AreafixRescan::dispatchSync($ao,$eo,$this->argument('days'));
|
||||
// Check that the user is subscribed
|
||||
if (! $ao->echoareas->contains($eao->id))
|
||||
throw new \Exception(sprintf('FTN [%s] is not subscribed to [%s]',$ao->ftn,$eao->name));
|
||||
|
||||
// Check that an FTN can read the area
|
||||
if (! $eao->can_read($ao->security))
|
||||
throw new \Exception(sprintf('FTN [%s] doesnt have permission to receive [%s]',$ao->ftn,$eao->name));
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ class CommBinkpReceive extends Command
|
||||
$o = Setup::findOrFail(config('app.id'));
|
||||
|
||||
$server = new SocketServer($o->binkp_port,$o->binkp_bind);
|
||||
$server->handler = [new Binkp,'onConnect'];
|
||||
$server->handler = [new Binkp($o),'onConnect'];
|
||||
|
||||
try {
|
||||
$server->listen();
|
||||
|
@ -16,9 +16,7 @@ class CommBinkpSend extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'comm:binkp:send'
|
||||
.'{--N|now : Dont queue}'
|
||||
.'{ftn : FTN to Send to}';
|
||||
protected $signature = 'comm:binkp:send {ftn : FTN to Send to}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@ -34,7 +32,7 @@ class CommBinkpSend extends Command
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle()
|
||||
public function handle(): void
|
||||
{
|
||||
$ao = Address::findFTN($this->argument('ftn'));
|
||||
if (! $ao)
|
||||
@ -44,11 +42,6 @@ class CommBinkpSend extends Command
|
||||
|
||||
$mo = Mailer::where('name',self::ID)->singleOrFail();
|
||||
|
||||
if ($this->option('now'))
|
||||
Job::dispatchSync($ao,$mo);
|
||||
else
|
||||
Job::dispatch($ao,$mo);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ class CommEMSIReceive extends Command
|
||||
$o = Setup::findOrFail(config('app.id'));
|
||||
|
||||
$server = new SocketServer($o->emsi_port,$o->emsi_bind);
|
||||
$server->handler = [new EMSI,'onConnect'];
|
||||
$server->handler = [new EMSI($o),'onConnect'];
|
||||
|
||||
try {
|
||||
$server->listen();
|
||||
|
@ -16,9 +16,7 @@ class CommEMSISend extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'comm:emsi:send'
|
||||
.'{--N|now : Dont queue}'
|
||||
.'{ftn : FTN to Send to}';
|
||||
protected $signature = 'comm:emsi:send {ftn : FTN to Send to}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@ -34,7 +32,7 @@ class CommEMSISend extends Command
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle()
|
||||
public function handle(): void
|
||||
{
|
||||
$ao = Address::findFTN($this->argument('ftn'));
|
||||
if (! $ao)
|
||||
@ -44,11 +42,6 @@ class CommEMSISend extends Command
|
||||
|
||||
$mo = Mailer::where('name',self::ID)->singleOrFail();
|
||||
|
||||
if ($this->option('now'))
|
||||
Job::dispatchSync($ao,$mo);
|
||||
else
|
||||
Job::dispatch($ao,$mo);
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
@ -49,11 +49,6 @@ class PacketDump extends Command
|
||||
throw new \Exception('Unknown type: '.$this->argument('type'));
|
||||
}
|
||||
|
||||
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 Type:'.get_class($pkt));
|
||||
|
@ -49,8 +49,8 @@ class MailList extends Command
|
||||
return [
|
||||
'id'=>$item->id,
|
||||
'msgid'=>$item->msgid,
|
||||
'from'=>sprintf('%s (%s)',$item->from,$item->fftn->ftn3d),
|
||||
'to'=>sprintf('%s (%s)',$item->to,$item->tftn->ftn3d),
|
||||
'from'=>$item->from,
|
||||
'to'=>$item->to,
|
||||
'subject'=>$item->subject,
|
||||
];
|
||||
}));
|
||||
|
@ -1,50 +0,0 @@
|
||||
<?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'))->singleOrFail();
|
||||
$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) {
|
||||
$pkt = Packet::process($packet,$x=$f->itemName(),$f->itemSize(),$a?->system);
|
||||
$pkt = Packet::process($packet,$x=$f->itemName(),$f->itemSize(),$a?->zone->domain);
|
||||
|
||||
$this->alert(sprintf('File Name: %s',$x));
|
||||
|
||||
@ -68,7 +68,7 @@ class PacketInfo extends Command
|
||||
echo "\n";
|
||||
|
||||
try {
|
||||
$this->warn(sprintf('- Date : %s (%s)',$msg->date,$msg->date->tz->toOffsetName()));
|
||||
$this->warn(sprintf('- Date : %s (%s)',$msg->datetime,$msg->datetime->tz->toOffsetName()));
|
||||
$this->warn(sprintf(' - Errors : %s',$msg->errors->count() ? 'YES' : 'No'));
|
||||
$this->warn(sprintf(' - Flags : %s',$msg->flags()->keys()->join(', ')));
|
||||
$this->warn(sprintf(' - Cost : %d',$msg->cost));
|
||||
@ -97,7 +97,7 @@ class PacketInfo extends Command
|
||||
}
|
||||
|
||||
foreach ($pkt->errors as $msg) {
|
||||
$this->error(sprintf('- Date: %s',$msg->datetime));
|
||||
$this->error(sprintf('- Date: %s',$msg->date));
|
||||
$this->error(sprintf(' - FLAGS: %s',$msg->flags()->filter()->keys()->join(', ')));
|
||||
$this->error(sprintf(' - From: %s (%s)',$msg->from,$msg->fftn));
|
||||
$this->error(sprintf(' - To: %s (%s)',$msg->to,$msg->tftn));
|
||||
|
@ -78,7 +78,7 @@ class PacketProcess extends Command
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
Job::dispatchSync($rel_name,$ao->system,$this->option('dontqueue'));
|
||||
Job::dispatchSync($rel_name,$ao->zone->domain,$this->option('dontqueue'));
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
@ -3,12 +3,12 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Classes\Protocol\{Binkp,DNS,EMSI};
|
||||
use App\Classes\Sock\Exception\SocketException;
|
||||
use App\Classes\Sock\SocketServer;
|
||||
use App\Models\Setup;
|
||||
|
||||
class ServerStart extends Command
|
||||
{
|
||||
@ -37,11 +37,7 @@ class ServerStart extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
Log::info(sprintf('%s:+ Server Starting (%d)',self::LOGKEY,getmypid()));
|
||||
|
||||
if (! our_address()->count())
|
||||
throw new \Exception('We dont have any ACTIVE FTN addresses assigned');
|
||||
|
||||
$o = Config::get('setup');
|
||||
$o = Setup::findOrFail(config('app.id'));
|
||||
|
||||
$start = collect();
|
||||
|
||||
@ -50,7 +46,7 @@ class ServerStart extends Command
|
||||
'address'=>$o->binkp_bind,
|
||||
'port'=>$o->binkp_port,
|
||||
'proto'=>SOCK_STREAM,
|
||||
'class'=>new Binkp,
|
||||
'class'=>new Binkp($o),
|
||||
]);
|
||||
|
||||
if ($o->emsi_active)
|
||||
@ -58,7 +54,7 @@ class ServerStart extends Command
|
||||
'address'=>$o->emsi_bind,
|
||||
'port'=>$o->emsi_port,
|
||||
'proto'=>SOCK_STREAM,
|
||||
'class'=>new EMSI,
|
||||
'class'=>new EMSI($o),
|
||||
]);
|
||||
|
||||
if ($o->dns_active)
|
||||
@ -66,7 +62,7 @@ class ServerStart extends Command
|
||||
'address'=>$o->dns_bind,
|
||||
'port'=>$o->dns_port,
|
||||
'proto'=>SOCK_DGRAM,
|
||||
'class'=>new DNS,
|
||||
'class'=>new DNS(),
|
||||
]);
|
||||
|
||||
$children = collect();
|
||||
|
46
app/Console/Kernel.php
Normal file
46
app/Console/Kernel.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
@ -42,13 +42,13 @@ abstract class Base
|
||||
|
||||
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));
|
||||
->get(sprintf('https://%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));
|
||||
->get(sprintf('https://%s/_matrix/client/v3/rooms/%s/state/m.room.topic',config('matrix.server'),$this->room_id));
|
||||
|
||||
return $subject->json('topic','Message from Matrix');
|
||||
|
||||
|
40
app/Exceptions/Handler.php
Normal file
40
app/Exceptions/Handler.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?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) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
@ -1,213 +0,0 @@
|
||||
<?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/2.1.2/css/dataTables.bootstrap4.css',
|
||||
//'//cdn.datatables.net/2.1.2/css/dataTables.dataTables.min.css',
|
||||
],
|
||||
'js' => [
|
||||
'//cdn.datatables.net/2.1.2/js/dataTables.min.js',
|
||||
'//cdn.datatables.net/2.1.2/js/dataTables.bootstrap4.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',
|
||||
],
|
||||
],
|
||||
'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.5.0/css/rowGroup.bootstrap4.min.css',
|
||||
//'//cdn.datatables.net/rowgroup/1.5.0/css/rowGroup.dataTables.min.css',
|
||||
],
|
||||
'js' => [
|
||||
'//cdn.datatables.net/rowgroup/1.5.0/js/dataTables.rowGroup.min.js',
|
||||
//'//cdn.datatables.net/rowgroup/1.5.0/js/rowGroup.dataTables.min.js',
|
||||
'//cdn.datatables.net/rowgroup/1.5.0/js/rowGroup.bootstrap4.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' => [
|
||||
'//cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css',
|
||||
],
|
||||
'js' => [
|
||||
'//cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js',
|
||||
],
|
||||
],
|
||||
'autofocus' => [
|
||||
'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,9 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Foundation\Auth\ConfirmsPasswords;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
class ConfirmPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
@ -25,7 +26,7 @@ class ConfirmPasswordController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
|
@ -2,12 +2,11 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|
@ -8,6 +8,7 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
@ -29,7 +30,7 @@ class LoginController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
@ -38,8 +39,8 @@ class LoginController extends Controller
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest')->except('logout');
|
||||
$this->middleware('auth')->only('logout');
|
||||
$this->middleware('guest')
|
||||
->except('logout');
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
@ -55,7 +56,6 @@ class LoginController extends Controller
|
||||
return $this->sendFailedLoginResponse($request);
|
||||
}
|
||||
|
||||
// Record our last logged in time
|
||||
protected function authenticated(Request $request, $user)
|
||||
{
|
||||
$user->last_on = Carbon::now();
|
||||
|
@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use App\Models\User;
|
||||
|
||||
class RegisterController extends Controller
|
||||
@ -29,7 +30,7 @@ class RegisterController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
@ -26,5 +26,15 @@ class ResetPasswordController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Foundation\Auth\VerifiesEmails;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Foundation\Auth\VerifiesEmails;
|
||||
|
||||
class VerificationController extends Controller
|
||||
{
|
||||
@ -26,7 +26,7 @@ class VerificationController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
|
@ -2,10 +2,12 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
|
||||
abstract class Controller extends \Illuminate\Routing\Controller
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests,ValidatesRequests;
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
}
|
@ -18,7 +18,7 @@ class EchoareaController extends Controller
|
||||
|
||||
$request->validate([
|
||||
'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',
|
||||
'active' => 'required|boolean',
|
||||
'show' => 'required|boolean',
|
||||
|
@ -10,7 +10,7 @@ use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
use App\Classes\File;
|
||||
use App\Classes\FTN\{Message,Packet};
|
||||
use App\Classes\FTN\Packet;
|
||||
use App\Http\Requests\SetupRequest;
|
||||
use App\Models\File as FileModel;
|
||||
use App\Models\{Address,Echomail,Netmail,Setup,System};
|
||||
@ -169,7 +169,7 @@ class HomeController extends Controller
|
||||
->orWhere('replyid','like','%'.$request->query('term').'%')
|
||||
->get() as $o)
|
||||
{
|
||||
$result->push(['id'=>$o->id,'name'=>sprintf('%s (%s)',Message::tr($o->from),$o->fftn->ftn3d),'value'=>url('echomail/view',[$o->id]),'category'=>'Echomail']);
|
||||
$result->push(['id'=>$o->id,'name'=>sprintf('%s (%s)',$o->from,$o->fftn->ftn3d),'value'=>url('echomail/view',[$o->id]),'category'=>'Echomail']);
|
||||
}
|
||||
|
||||
// Look for Netmail
|
||||
|
@ -36,7 +36,7 @@ class SystemController extends Controller
|
||||
|
||||
// Sometimes items
|
||||
foreach (['sysop','hold','notes','zt_id','heartbeat'] as $key)
|
||||
if ($request->has($key))
|
||||
if ($request->validated($key))
|
||||
$o->{$key} = $request->validated($key);
|
||||
|
||||
switch ($request->validated('pollmode')) {
|
||||
@ -66,17 +66,7 @@ class SystemController extends Controller
|
||||
->with('saved',TRUE);
|
||||
}
|
||||
|
||||
$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',
|
||||
]);
|
||||
$o->load(['addresses.zone.domain','addresses.nodes_hub','addresses.system','sessions.domain','sessions.systems']);
|
||||
|
||||
return view('system.addedit')
|
||||
->with('action',$o->exists ? 'update_nn' : 'create')
|
||||
@ -498,17 +488,11 @@ class SystemController extends Controller
|
||||
public function api_autohold_toggle(Request $request,string $state): array
|
||||
{
|
||||
$o = System::findOrFail($request->id);
|
||||
|
||||
if ($request->user()->can('update_nn',$o)) {
|
||||
$o->autohold = !($state === 'off');
|
||||
$o->autohold = $state === 'off' ? FALSE : TRUE;
|
||||
$o->save();
|
||||
|
||||
Log::debug(sprintf('%s:- Autohold set to [%s]',self::LOGKEY,$o->autohold ? 'ON' : 'OFF'));
|
||||
|
||||
} else {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return ['autohold'=>$o->autohold];
|
||||
}
|
||||
|
||||
@ -621,12 +605,31 @@ class SystemController extends Controller
|
||||
*/
|
||||
public function register(SystemRegisterRequest $request)
|
||||
{
|
||||
// Step 1, show the user a form to select an existing defined system
|
||||
if ($request->isMethod('GET'))
|
||||
return view('user.system.register');
|
||||
|
||||
if ($request->action === 'register' && $request->name && is_numeric($request->name))
|
||||
return view('user.system.widget.register_confirm')
|
||||
->with('o',System::findOrFail($request->name));
|
||||
|
||||
$o = System::findOrNew(is_numeric($request->system_id) ? $request->system_id : NULL);
|
||||
|
||||
// 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;
|
||||
|
83
app/Http/Kernel.php
Normal file
83
app/Http/Kernel.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?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,7 +5,6 @@ namespace App\Http\Middleware;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
use App\Models\Setup;
|
||||
|
||||
@ -46,10 +45,8 @@ class AddUserToView
|
||||
*/
|
||||
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('setup',$x);
|
||||
$this->factory->share('setup',Setup::find(config('app.id')));
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
21
app/Http/Middleware/Authenticate.php
Normal file
21
app/Http/Middleware/Authenticate.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
||||
}
|
17
app/Http/Middleware/CheckForMaintenanceMode.php
Normal file
17
app/Http/Middleware/CheckForMaintenanceMode.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?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 = [
|
||||
//
|
||||
];
|
||||
}
|
17
app/Http/Middleware/EncryptCookies.php
Normal file
17
app/Http/Middleware/EncryptCookies.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?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 = [
|
||||
//
|
||||
];
|
||||
}
|
28
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
28
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
use App\Providers\RouteServiceProvider;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $guard
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request,\Closure $next,?string $guard=NULL)
|
||||
{
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
18
app/Http/Middleware/TrimStrings.php
Normal file
18
app/Http/Middleware/TrimStrings.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
28
app/Http/Middleware/TrustProxies.php
Normal file
28
app/Http/Middleware/TrustProxies.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $proxies;
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $headers =
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
24
app/Http/Middleware/VerifyCsrfToken.php
Normal file
24
app/Http/Middleware/VerifyCsrfToken.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
/**
|
||||
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $addHttpCookie = true;
|
||||
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
@ -36,7 +36,7 @@ class AddressMerge extends FormRequest
|
||||
$src = Address::withTrashed()->findOrFail($request->src);
|
||||
|
||||
if ((! $dst->active) && ($dst->system_id !== $src->system_id) && ($src->system->name !== System::default))
|
||||
$fail('Destination must be active, or be from the same system');
|
||||
$fail('Destination must be active, or be from the system system');
|
||||
},
|
||||
function ($attribute,$value,$fail) use ($request) {
|
||||
$dst = Address::withTrashed()->findOrFail($value);
|
||||
|
@ -23,21 +23,14 @@ class SystemRegisterRequest extends FormRequest
|
||||
|
||||
// @todo Also disallow claiming this hosts system
|
||||
|
||||
return Gate::allows(
|
||||
$this->route('o')->users->count()
|
||||
? 'update_nn'
|
||||
: 'register',
|
||||
$this->route('o')
|
||||
);
|
||||
return Gate::allows($this->route('o')->users->count() ? 'update_nn' : 'register',$this->route('o'));
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'heartbeat' => 'Sorry, only an admin can set this below 12',
|
||||
'hold' => 'Must be Yes or No',
|
||||
'pollmode' => 'Must be Hold, Normal or Crash',
|
||||
'pkt_msgs' => 'Sorry, only an admin can increase this above 100',
|
||||
];
|
||||
}
|
||||
|
||||
@ -76,26 +69,8 @@ class SystemRegisterRequest extends FormRequest
|
||||
'active' => 'required|boolean',
|
||||
'hold' => 'sometimes|boolean',
|
||||
'pollmode' => 'required|integer|min:0|max:2',
|
||||
'heartbeat' => [
|
||||
'nullable',
|
||||
'integer',
|
||||
function ($attribute,$value,$fail) {
|
||||
if (($value < 12) && (! Gate::allows('admin')))
|
||||
$fail(true);
|
||||
},
|
||||
'min:0',
|
||||
'max:48'
|
||||
],
|
||||
'pkt_msgs' => [
|
||||
'nullable',
|
||||
'integer',
|
||||
function ($attribute,$value,$fail) {
|
||||
if (($value > 100) && (! Gate::allows('admin')))
|
||||
$fail(true);
|
||||
},
|
||||
'min:5',
|
||||
'max:65535',
|
||||
],
|
||||
'heartbeat' => 'nullable|integer|min:0|max:48',
|
||||
'pkt_msgs' => 'nullable|integer|min:5',
|
||||
] : []));
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
use App\Classes\FTN\Message;
|
||||
use App\Models\{Address,Domain,System};
|
||||
use App\Notifications\Echomails\AbsentNodes;
|
||||
use App\Notifications\Emails\NodeMarkedDown as NodeMarkedDownEmail;
|
||||
use App\Notifications\Netmails\NodeMarkedDown as NodeMarkedDownNetmail;
|
||||
use App\Notifications\Emails\NodeMarkedHold as NodeMarkedHoldEmail;
|
||||
use App\Notifications\Netmails\NodeMarkedHold as NodeMarkedHoldNetmail;
|
||||
use App\Notifications\Emails\NodeDelisted as NodeDelistedEmail;
|
||||
use App\Notifications\Netmails\NodeDelisted as NodeDelistedNetmail;
|
||||
|
||||
class AddressClearQueue implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private const LOGKEY = 'JAC';
|
||||
|
||||
private Address $ao; // System address
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(Address $ao)
|
||||
{
|
||||
$this->ao = $ao->withoutRelations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
// Remove echomail not collected from echomail_seenby
|
||||
DB::table('echomail_seenby')
|
||||
->where('address_id',$this->ao->id)
|
||||
->whereNotNull('export_at')
|
||||
->whereNull('sent_at')
|
||||
->delete();
|
||||
|
||||
// Remove FLAG_INTRANSIT from netmail that hasnt been delivered
|
||||
DB::table('netmails')
|
||||
->where('tftn_id',$this->ao->id)
|
||||
->whereRaw(sprintf('(flags & %d) > 0',Message::FLAG_INTRANSIT))
|
||||
->update(['flags'=>DB::raw(sprintf('(flags & ~%d)',Message::FLAG_INTRANSIT))]);
|
||||
|
||||
// Remove files not collected
|
||||
DB::table('file_seenby')
|
||||
->where('address_id',$this->ao->id)
|
||||
->whereNotNull('export_at')
|
||||
->whereNull('sent_at')
|
||||
->delete();
|
||||
}
|
||||
}
|
@ -37,8 +37,8 @@ class AddressIdle implements ShouldQueue
|
||||
*/
|
||||
public function __construct(Domain $do,Address $ao=NULL)
|
||||
{
|
||||
$this->do = $do->withoutRelations();
|
||||
$this->ao = $ao?->withoutRelations();
|
||||
$this->do = $do;
|
||||
$this->ao = $ao;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,24 +49,35 @@ class AddressIdle implements ShouldQueue
|
||||
$result = collect();
|
||||
|
||||
// Delist DOWN nodes
|
||||
if (config('fido.idle.delist')) {
|
||||
$age = Carbon::now()->subDays(config('fido.idle.delist'));
|
||||
|
||||
foreach ($this->old($this->do,config('fido.idle.delist'),Address::NODE_DOWN,$this->ao) as $ao) {
|
||||
Log::debug(sprintf('%s:- Evaluating DOWN node [%s], not seen for at least [%d] days, last update [%d] days',self::LOGKEY,$ao->ftn,$ao->system->last_seen?->diffInDays(),$ao->updated_at->diffInDays()));
|
||||
|
||||
// Only delist system that has been marked down
|
||||
// Only delist them if its been 14 days since they were marked DOWN
|
||||
if ((! $ao->is_down) || ($ao->updated_at->greaterThan(Carbon::now()->subWeeks(2))))
|
||||
// Only mark delist them if its been 7 days since they were marked DOWN
|
||||
if ((! $ao->is_down) || ($ao->updated_at->isPast(Carbon::now()->subWeek())))
|
||||
continue;
|
||||
|
||||
// Validate that the last seen was infact that long ago
|
||||
if ($ao->system->last_seen && $ao->system->last_seen->greaterThan($age))
|
||||
continue;
|
||||
|
||||
Log::info(sprintf('%s:- Delisting [%s], not seen for at least [%d] days',self::LOGKEY,$ao->ftn,$ao->system->last_seen?->diffInDays()));
|
||||
Log::info(sprintf('%s:- Delisting [%s], not seen for [%d] days',self::LOGKEY,$ao->ftn,config('fido.idle.delist')));
|
||||
$contact = FALSE;
|
||||
|
||||
// Remove echomail not collected from echomail_seenby
|
||||
DB::table('echomail_seenby')
|
||||
->where('address_id',$ao->id)
|
||||
->whereNotNull('export_at')
|
||||
->whereNull('sent_at')
|
||||
->delete();
|
||||
|
||||
// Remove FLAG_INTRANSIT from netmail that hasnt been delivered
|
||||
DB::table('netmails')
|
||||
->where('tftn_id',$ao->id)
|
||||
->whereRaw(sprintf('(flags & %d) > 0',Message::FLAG_INTRANSIT))
|
||||
->update(['flags'=>DB::raw(sprintf('(flags & ~%d)',Message::FLAG_INTRANSIT))]);
|
||||
|
||||
// Remove files not collected
|
||||
DB::table('file_seenby')
|
||||
->where('address_id',$ao->id)
|
||||
->whereNotNull('export_at')
|
||||
->whereNull('sent_at')
|
||||
->delete();
|
||||
|
||||
// Remove subscribed echoareas
|
||||
$ao->echoareas()->detach();
|
||||
|
||||
@ -77,9 +88,6 @@ class AddressIdle implements ShouldQueue
|
||||
$ao->validated = FALSE;
|
||||
$ao->save();
|
||||
|
||||
// Clear the queue
|
||||
AddressClearQueue::dispatchSync($ao);
|
||||
|
||||
// Email Alert
|
||||
if ($ao->system->users->count()) {
|
||||
Notification::send($ao->system->users,new NodeDelistedEmail($ao->withoutRelations()));
|
||||
@ -87,33 +95,18 @@ class AddressIdle implements ShouldQueue
|
||||
}
|
||||
|
||||
// Netmail Alert (to othernet network address)
|
||||
if ($ao->system->aka_unknown()->count()) {
|
||||
Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeDelistedNetmail($ao->withoutRelations()));
|
||||
if ($ao->system->uncommon()->count()) {
|
||||
Notification::route('netmail',$ao->system->uncommon()->first()->withoutRelations())->notify(new NodeDelistedNetmail($ao->withoutRelations()));
|
||||
$contact = TRUE;
|
||||
}
|
||||
|
||||
$ao->contacted = $contact;
|
||||
$ao->contacted = (! $contact);
|
||||
$result->push($ao);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark nodes DOWN
|
||||
if (config('fido.idle.down')) {
|
||||
$age = Carbon::now()->subDays(config('fido.idle.down'));
|
||||
|
||||
foreach ($this->old($this->do,config('fido.idle.down'),Address::NODE_HOLD,$this->ao) as $ao) {
|
||||
Log::debug(sprintf('%s:- Evaluating HOLD node [%s], not seen for at least [%d] days, last update [%d] days',self::LOGKEY,$ao->ftn,$ao->system->last_seen?->diffInDays(),$ao->updated_at->diffInDays()));
|
||||
|
||||
// Only mark down system that has been marked down
|
||||
// Only mark down them if its been 14 days since they were marked HOLD
|
||||
if ((! $ao->is_hold) || ($ao->updated_at->greaterThan(Carbon::now()->subWeeks(2))))
|
||||
continue;
|
||||
|
||||
// Validate that the last seen was infact that long ago
|
||||
if ($ao->system->last_seen && $ao->system->last_seen->greaterThan($age))
|
||||
continue;
|
||||
|
||||
Log::info(sprintf('%s:- Marking [%s] as DOWN, not seen for at least [%d] days',self::LOGKEY,$ao->ftn,$ao->system->last_seen?->diffInDays()));
|
||||
Log::info(sprintf('%s:- Marking [%s] as DOWN, not seen for [%d] days',self::LOGKEY,$ao->ftn,config('fido.idle.down')));
|
||||
$contact = FALSE;
|
||||
|
||||
// Email Alert
|
||||
@ -123,8 +116,8 @@ class AddressIdle implements ShouldQueue
|
||||
}
|
||||
|
||||
// Netmail Alert (to othernet network address)
|
||||
if ($ao->system->aka_unknown()->count()) {
|
||||
Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations()));
|
||||
if ($ao->system->uncommon()->count()) {
|
||||
Notification::route('netmail',$ao->system->uncommon()->first()->withoutRelations())->notify(new NodeMarkedDownNetmail($ao->withoutRelations()));
|
||||
$contact = TRUE;
|
||||
}
|
||||
|
||||
@ -133,19 +126,12 @@ class AddressIdle implements ShouldQueue
|
||||
$ao->role |= Address::NODE_DOWN;
|
||||
$ao->save();
|
||||
|
||||
$ao->contacted = $contact;
|
||||
$ao->contacted = (! $contact);
|
||||
$result->push($ao);
|
||||
}
|
||||
}
|
||||
|
||||
// @todo Make sure we only process addresses that we are responsible for, eg: 1/999 shouldnt have been processed even though 3/999 as eligible (and they are were connected to the same system)
|
||||
// Mark nodes as HOLD
|
||||
if (config('fido.idle.hold')) {
|
||||
$age = Carbon::now()->subDays(config('fido.idle.hold'));
|
||||
|
||||
foreach ($this->old($this->do,config('fido.idle.hold'),Address::NODE_ALL,$this->ao) as $ao) {
|
||||
Log::debug(sprintf('%s:- Evaluating IDLE node [%s], not seen for at least [%d] days',self::LOGKEY,$ao->ftn,$ao->system->last_seen?->diffInDays()));
|
||||
|
||||
foreach ($this->old($this->do,config('fido.idle.hold'),0,$this->ao) as $ao) {
|
||||
// Ignore any systems that are a Discoverd System
|
||||
if ($ao->system->name === System::default) {
|
||||
Log::alert(sprintf('%s:! Ignoring HOLD for discovered System [%s]',self::LOGKEY,$ao->ftn));
|
||||
@ -156,13 +142,9 @@ class AddressIdle implements ShouldQueue
|
||||
if ($ao->role & (Address::NODE_DOWN|Address::NODE_HOLD))
|
||||
continue;
|
||||
|
||||
// Validate that the last seen was infact that long ago
|
||||
if ($ao->system->last_seen && $ao->system->last_seen->greaterThan($age))
|
||||
continue;
|
||||
|
||||
$contact = FALSE;
|
||||
|
||||
Log::info(sprintf('%s:- Marking [%s] as HOLD, not seen for at least [%d] days',self::LOGKEY,$ao->ftn,$ao->system->last_seen?->diffInDays()));
|
||||
Log::info(sprintf('%s:- Marking [%s] as HOLD, not seen for [%d] days',self::LOGKEY,$ao->ftn,config('fido.idle.hold')));
|
||||
|
||||
// Email Alert
|
||||
if ($ao->system->users->count()) {
|
||||
@ -171,8 +153,8 @@ class AddressIdle implements ShouldQueue
|
||||
}
|
||||
|
||||
// Netmail Alert (to othernet network address)
|
||||
if ($ao->system->aka_unknown()->count()) {
|
||||
Notification::route('netmail',$ao->system->aka_unknown()->first()->withoutRelations())->notify(new NodeMarkedHoldNetmail($ao->withoutRelations()));
|
||||
if ($ao->system->uncommon()->count()) {
|
||||
Notification::route('netmail',$ao->system->uncommon()->first()->withoutRelations())->notify(new NodeMarkedHoldNetmail($ao->withoutRelations()));
|
||||
$contact = TRUE;
|
||||
}
|
||||
|
||||
@ -180,36 +162,55 @@ class AddressIdle implements ShouldQueue
|
||||
$ao->role |= Address::NODE_HOLD;
|
||||
$ao->save();
|
||||
|
||||
$ao->contacted = $contact;
|
||||
$ao->contacted = (! $contact);
|
||||
$result->push($ao);
|
||||
}
|
||||
}
|
||||
|
||||
if ($result->count())
|
||||
Notification::route('echomail',$this->do->nodestatus_echoarea)->notify(new AbsentNodes($result));
|
||||
Notification::route('echomail',$this->do->nodestatusarea)->notify(new AbsentNodes($result));
|
||||
}
|
||||
|
||||
private function old(Domain $do,int $days,int $flags=0,Address $ao=NULL): Collection
|
||||
{
|
||||
// Ignore dates that are zero
|
||||
if (! $days)
|
||||
return collect();
|
||||
|
||||
$age = Carbon::now()->subDays($days)->endOfDay();
|
||||
$ours = our_address($do)->pluck('ftn');
|
||||
|
||||
return Address::FTN()
|
||||
->ActiveFTN()
|
||||
->addSelect(['addresses.updated_at'])
|
||||
->where(fn($query)=>$query->where('point_id',0)->orWhereNull('point_id'))
|
||||
->whereIn('addresses.id',our_nodes($do)->pluck('id'))
|
||||
return Address::select([
|
||||
'a.id',
|
||||
'a.system_id',
|
||||
'a.zone_id',
|
||||
'addresses.region_id',
|
||||
'a.host_id',
|
||||
'a.node_id',
|
||||
'a.point_id',
|
||||
'addresses.active',
|
||||
'addresses.hub_id',
|
||||
'addresses.role',
|
||||
'addresses.updated_at',
|
||||
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()
|
||||
->union(Address::UncollectedNetmailTotal())
|
||||
->union(Address::UncollectedFilesTotal()),'a')
|
||||
->where('systems.active',TRUE)
|
||||
->where('addresses.active',TRUE)
|
||||
->where('zones.active',TRUE)
|
||||
->where('domains.active',TRUE)
|
||||
->whereNotIn('a.id',our_address()->pluck('id'))
|
||||
->when($ao,fn($query)=>$query->where('addresses.id',$ao->id))
|
||||
->where(fn($q)=>$q->where('last_session','<',$age)->orWhereNull('last_session'))
|
||||
->whereRaw(sprintf('((role IS NULL) OR (role=0) OR ((role & %d) > 0))',$flags))
|
||||
->whereRaw(sprintf('((role IS NULL) OR ((role & %d) = 0))',Address::NODE_KEEP))
|
||||
->join('systems',['systems.id'=>'addresses.system_id'])
|
||||
//->with(['system','zone.domain'])
|
||||
->get()
|
||||
->filter(fn($item)=>$ours->contains($item->parent()?->ftn));
|
||||
->where('last_session','<',$age)
|
||||
->where('domains.id',$do->id)
|
||||
->whereRaw(sprintf('((role IS NULL) OR ((role & %d) > 0))',$flags))
|
||||
->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','addresses.active','addresses.updated_at')
|
||||
->with(['system','zone.domain'])
|
||||
->dontCache()
|
||||
->get();
|
||||
}
|
||||
}
|
@ -14,6 +14,13 @@ class AddressIdleDomain implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
|
@ -27,7 +27,7 @@ class AddressPoll implements ShouldQueue, ShouldBeUnique
|
||||
|
||||
private const LOGKEY = 'JAP';
|
||||
|
||||
public int $tries = 10;
|
||||
public int $tries = 5;
|
||||
public int $maxExceptions = 1;
|
||||
public bool $failOnTimeout = TRUE;
|
||||
|
||||
@ -38,8 +38,8 @@ class AddressPoll implements ShouldQueue, ShouldBeUnique
|
||||
|
||||
public function __construct(Address $ao,Mailer $mo=NULL)
|
||||
{
|
||||
$this->ao = $ao->withoutRelations();
|
||||
$this->mo = $mo?->withoutRelations();
|
||||
$this->ao = $ao;
|
||||
$this->mo = $mo;
|
||||
|
||||
$this->onQueue(self::QUEUE);
|
||||
}
|
||||
@ -93,13 +93,13 @@ class AddressPoll implements ShouldQueue, ShouldBeUnique
|
||||
|
||||
switch ($o->name) {
|
||||
case 'BINKP':
|
||||
$s = new Binkp;
|
||||
$s = new Binkp(Setup::findOrFail(config('app.id')));
|
||||
$mo = Mailer::where('name','BINKP')->singleOrFail();
|
||||
|
||||
break;
|
||||
|
||||
case 'EMSI':
|
||||
$s = new EMSI;
|
||||
$s = new EMSI(Setup::findOrFail(config('app.id')));
|
||||
$mo = Mailer::where('name','EMSI')->singleOrFail();
|
||||
|
||||
break;
|
||||
|
@ -1,169 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\Skip;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
use App\Models\{Address,Echoarea,Echomail};
|
||||
use App\Notifications\Netmails\Areafix\Scan;
|
||||
|
||||
class AreafixRescan implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private const LOGKEY = 'JAR';
|
||||
|
||||
private Address $ao; // System address
|
||||
private Echoarea $eao; // Domain we are processing
|
||||
private int $days;
|
||||
private bool $rescan;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(Address $ao,Echoarea $eao,int $days=30,bool $rescan=FALSE)
|
||||
{
|
||||
$this->ao = $ao->withoutRelations();
|
||||
$this->eao = $eao->withoutRelations();
|
||||
$this->days = $days;
|
||||
$this->rescan = $rescan;
|
||||
}
|
||||
|
||||
public function __get(string $key): mixed
|
||||
{
|
||||
switch ($key) {
|
||||
case 'jobname':
|
||||
return sprintf('%s %s (%d)',$this->ao->ftn,$this->eao->name,$this->days);
|
||||
|
||||
default:
|
||||
$this->fail('Unkown key:'.$key);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
public function middleware(): array
|
||||
{
|
||||
return [
|
||||
Skip::when(function(): bool {
|
||||
if ($this->eao->domain_id !== $this->ao->zone->domain_id) {
|
||||
Log::error(sprintf('%s:! Echo area [%s] is not in domain [%s] for FTN [%s]',self::LOGKEY,$this->eao->name,$this->ao->zone->domain->name,$this->ao->ftn));
|
||||
|
||||
return TRUE;
|
||||
|
||||
// Check that the user is subscribed
|
||||
} elseif (! $this->ao->echoareas->contains($this->eao->id)) {
|
||||
Log::error(sprintf('%s:! FTN [%s] is not subscribed to [%s]',self::LOGKEY,$this->ao->ftn,$this->eao->name));
|
||||
|
||||
return TRUE;
|
||||
|
||||
// Check that an FTN can read the area
|
||||
} elseif (! $this->eao->can_read($this->ao->security)) {
|
||||
Log::error(sprintf('%s:! FTN [%s] doesnt have permission to receive [%s]',self::LOGKEY,$this->ao->ftn,$this->eao->name));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$c = 0;
|
||||
$s = 0;
|
||||
|
||||
$earliest = NULL;
|
||||
$latest = NULL;
|
||||
|
||||
foreach (Echomail::select(['id','datetime'])
|
||||
->where('echoarea_id',$this->eao->id)
|
||||
->where('datetime','>=',
|
||||
Carbon::now()
|
||||
->subDays($this->days)
|
||||
->startOfDay()
|
||||
)
|
||||
->orderBy('datetime')
|
||||
->cursor() as $eo) {
|
||||
|
||||
// Echomail hasnt been exported before
|
||||
if (! $eo->seenby->count()) {
|
||||
$eo->seenby()->attach($this->ao->id,['export_at'=>Carbon::now()]);
|
||||
$c++;
|
||||
|
||||
if (($eo->datetime < $earliest) || (! $earliest))
|
||||
$earliest = $eo->datetime;
|
||||
|
||||
if (($latest < $eo->datetime) || (! $latest))
|
||||
$latest = $eo->datetime;
|
||||
|
||||
Log::debug(sprintf('Exported [%d] MSG (%s) dated (%s) to [%s]',$eo->id,$eo->msgid ?: '*NO MSGID*',$eo->datetime->format('Y-m-d H:i:s'),$this->ao->ftn3d));
|
||||
|
||||
} else {
|
||||
$export = $eo->seenby->where('id',$this->ao->id)->pop();
|
||||
|
||||
if ($export) {
|
||||
// Echomail is pending export
|
||||
if ($export->pivot->export_at && is_null($export->pivot->sent_at) && is_null($export->pivot->sent_pkt)) {
|
||||
$s++;
|
||||
Log::debug(sprintf('Not exporting [%d] MSG (%s) dated (%s) already queued for [%s]',$eo->id,$eo->msgid ?: '*NO MSGID*',$eo->datetime->format('Y-m-d H:i:s'),$this->ao->ftn3d));
|
||||
|
||||
// Echomail has been exported
|
||||
} elseif ($this->rescan) {
|
||||
$eo->seenby()->updateExistingPivot($this->ao,['export_at'=>Carbon::now(),'sent_at'=>NULL]);
|
||||
$c++;
|
||||
|
||||
if (($eo->datetime < $earliest) || (! $earliest))
|
||||
$earliest = $eo->datetime;
|
||||
|
||||
if (($latest < $eo->datetime) || (! $latest))
|
||||
$latest = $eo->datetime;
|
||||
|
||||
Log::debug(sprintf('Re-exported [%d] MSG (%s) dated (%s) to [%s]',$eo->id,$eo->msgid ?: '*NO MSGID*',$eo->datetime,$this->ao->ftn3d));
|
||||
|
||||
} else {
|
||||
$s++;
|
||||
Log::debug(sprintf('Not resending previously sent message [%d], MSGID (%s) - sent in Pkt [%s] on [%s]',
|
||||
$eo->id,
|
||||
$eo->msgid ?: '* NO MSGID*',
|
||||
$export->pivot->sent_pkt ?: '-',
|
||||
$export->pivot->sent_at ?: '-',
|
||||
));
|
||||
}
|
||||
|
||||
// Echomail has not been exported
|
||||
} else {
|
||||
$eo->seenby()->attach($this->ao,['export_at'=>Carbon::now(),'sent_at'=>NULL,'sent_pkt'=>NULL]);
|
||||
$c++;
|
||||
|
||||
if (($eo->datetime < $earliest) || (! $earliest))
|
||||
$earliest = $eo->datetime;
|
||||
|
||||
if (($latest < $eo->datetime) || (! $latest))
|
||||
$latest = $eo->datetime;
|
||||
|
||||
Log::debug(sprintf('Exported [%d] to [%s]',$eo->id,$this->ao->ftn3d));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Notification::route('netmail',$this->ao)
|
||||
->notify(new Scan(collect([
|
||||
'area'=>$this->eao->name,
|
||||
'queued'=>$c,
|
||||
'skipped'=>$s,
|
||||
'earliest'=>$earliest,
|
||||
'latest'=>$latest,
|
||||
])));
|
||||
|
||||
Log::info(sprintf('%s:= Queued [%d], Skipped [%d] echomails for [%s] in [%s]',self::LOGKEY,$c,$s,$this->ao->ftn,$this->eao->name));
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ class EchoareaImport implements ShouldQueue
|
||||
public function __construct(string $file,Domain $do,string $prefix='',bool $delete_recs=FALSE,bool $delete_file=FALSE)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->do = $do->withoutRelations();
|
||||
$this->do = $do;
|
||||
$this->prefix = $prefix ?: '';
|
||||
$this->delete_file = $delete_file;
|
||||
$this->delete_recs = $delete_recs;
|
||||
|
@ -36,7 +36,7 @@ class FileareaImport implements ShouldQueue
|
||||
public function __construct(string $file,Domain $do,string $prefix='',bool $delete_recs=FALSE,bool $delete_file=FALSE)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->do = $do->withoutRelations();
|
||||
$this->do = $do;
|
||||
$this->prefix = $prefix ?: '';
|
||||
$this->delete_file = $delete_file;
|
||||
$this->delete_recs = $delete_recs;
|
||||
|
@ -48,6 +48,21 @@ class MailSend #implements ShouldQueue
|
||||
->join('systems',['systems.id'=>'a.system_id'])
|
||||
->join('zones',['zones.id'=>'a.zone_id'])
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->where(function($query) {
|
||||
return $query->whereNull('autohold')
|
||||
->orWhere('autohold',FALSE);
|
||||
})
|
||||
->when(! is_null($this->crash),function($query) {
|
||||
return $query->when(
|
||||
$this->crash,
|
||||
function($query) {
|
||||
return $query->where('pollmode',$this->crash);
|
||||
},
|
||||
function($query) {
|
||||
return $query->whereNotNull('pollmode');
|
||||
}
|
||||
);
|
||||
})
|
||||
->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'])
|
||||
->dontCache()
|
||||
@ -65,12 +80,6 @@ class MailSend #implements ShouldQueue
|
||||
} else {
|
||||
return $item;
|
||||
}
|
||||
})
|
||||
->filter(function($item) {
|
||||
if ($item->system->autohold)
|
||||
return NULL;
|
||||
|
||||
return is_null($this->crash) || ($item->system->pollmode) || ($item->system->pollmode === $this->crash) ? $item : NULL;
|
||||
});
|
||||
|
||||
foreach ($u->groupBy('ftn') as $oo) {
|
||||
|
@ -34,7 +34,7 @@ class MessageProcess implements ShouldQueue
|
||||
public function __construct(Echomail|Netmail $mo,bool $skipbot=FALSE)
|
||||
{
|
||||
// @todo We need to serialize this model here, because laravel has an error unserializing it (Model Not Found)
|
||||
$this->mo = utf8_encode(serialize($mo->withoutRelations()));
|
||||
$this->mo = utf8_encode(serialize($mo));
|
||||
$this->skipbot = $skipbot;
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ class MessageProcess implements ShouldQueue
|
||||
// @todo generate exception when netmail to system that doesnt exist (node/point) and its this host's responsibility
|
||||
Log::info(sprintf('%s:- Processing Netmail [%s] to (%s) [%s] from (%s) [%s].',
|
||||
self::LOGKEY,
|
||||
$this->mo->msgid ?: '*NO MSGID*',
|
||||
$this->mo->msgid,
|
||||
$this->mo->to,$this->mo->tftn->ftn,
|
||||
$this->mo->from,$this->mo->fftn->ftn,
|
||||
));
|
||||
@ -81,7 +81,7 @@ class MessageProcess implements ShouldQueue
|
||||
Log::debug(sprintf('%s:- Checking for duplicate from host [%s].',self::LOGKEY,$this->mo->fftn->ftn));
|
||||
|
||||
$o = Netmail::where('msgid',$this->mo->msgid)
|
||||
->where('fftn_id',$this->mo->fftn_id)
|
||||
->where('fftn_id',$this->mo->fftn->id)
|
||||
->where('datetime','>',Carbon::now()->subYears(3))
|
||||
->single();
|
||||
|
||||
@ -137,19 +137,12 @@ class MessageProcess implements ShouldQueue
|
||||
->single();
|
||||
|
||||
if ($uo && ($ao=$uo->system->match($this->mo->tftn->zone)?->pop())) {
|
||||
Log::info(sprintf('%s:- Forwarding Netmail [%s] to (%s) [%s] from (%s) [%s].',
|
||||
self::LOGKEY,
|
||||
$this->mo->msgid ?: '*NO MSGID*',
|
||||
$this->mo->to,$ao->ftn,
|
||||
$this->mo->from,$this->mo->fftn->ftn,
|
||||
));
|
||||
|
||||
$note = "+--[ FORWARDED MESSAGE ]----------------------------------+\r";
|
||||
$note .= "+ This message has been forwarded to you, it was originally sent to you\r";
|
||||
$note .= sprintf("+ at [%s]\r",$this->mo->tftn->ftn);
|
||||
$note .= "+---------------------------------------------------------+\r\r";
|
||||
|
||||
$this->mo->msg_src = $note.$this->mo->content;
|
||||
$this->mo->msg = $note.$this->mo->content;
|
||||
$this->mo->tftn_id = $ao->id;
|
||||
$this->mo->flags |= Message::FLAG_INTRANSIT;
|
||||
$this->mo->save();
|
||||
@ -206,7 +199,7 @@ class MessageProcess implements ShouldQueue
|
||||
// The packet sender
|
||||
$sender = $this->mo->set->get('set_sender');
|
||||
|
||||
// @todo Check that this does evaluate to true if a message has been rescanned
|
||||
// @todo Check that this does evaulate to true if a message has been rescanned
|
||||
$rescanned = $this->mo->kludges->get('RESCANNED',FALSE);
|
||||
|
||||
// Echoarea doesnt exist, cant import the message
|
||||
@ -233,24 +226,17 @@ class MessageProcess implements ShouldQueue
|
||||
// Check for duplicate messages
|
||||
// FTS-0009.001
|
||||
if ($this->mo->msgid) {
|
||||
$o = ($x=Echomail::where('msgid',$this->mo->msgid)
|
||||
->where('fftn_id',$this->mo->fftn_id)
|
||||
->where('datetime','>=',$this->mo->datetime->clone()->subYears(3))
|
||||
->where('datetime','<=',$this->mo->datetime)
|
||||
->dontCache())
|
||||
$o = Echomail::where('msgid',$this->mo->msgid)
|
||||
->where('fftn_id',$this->mo->fftn->id)
|
||||
->where('datetime','>=',$this->mo->date->subYears(3))
|
||||
->where('datetime','<=',$this->mo->date)
|
||||
->single();
|
||||
|
||||
Log::debug(sprintf('%s:- Checking for duplicate from host id [%d], with msgid [%s] between [%s] and [%s].',
|
||||
self::LOGKEY,
|
||||
$this->mo->fftn_id,
|
||||
$this->mo->msgid,
|
||||
$this->mo->datetime->clone()->subYears(3),
|
||||
$this->mo->datetime,
|
||||
));
|
||||
Log::debug(sprintf('%s:- Checking for duplicate from host id [%d].',self::LOGKEY,$this->mo->fftn->id));
|
||||
|
||||
if ($x->count()) {
|
||||
if ($o) {
|
||||
// @todo Actually update seenby
|
||||
Log::alert(sprintf('%s:! Duplicate echomail (%s) in [%s] from (%s) [%s] to (%s) - ignoring.',
|
||||
Log::alert(sprintf('%s:! Duplicate echomail [%s] in [%s] from (%s) [%s] to (%s) - updating seenby.',
|
||||
self::LOGKEY,
|
||||
$this->mo->msgid,
|
||||
$this->mo->echoarea->name,
|
||||
@ -258,9 +244,37 @@ class MessageProcess implements ShouldQueue
|
||||
$this->mo->to,
|
||||
));
|
||||
|
||||
//$o->save();
|
||||
|
||||
// @todo This duplicate message may have gone via a different path, be nice to record it.
|
||||
|
||||
/*
|
||||
// If we didnt get the path on the original message, we'll override it
|
||||
if (! $o->path->count()) {
|
||||
$dummy = collect();
|
||||
$path = $this->parseAddresses('path',$this->mo->path,$sender->zone,$dummy);
|
||||
|
||||
$ppoid = NULL;
|
||||
foreach ($path as $aoid) {
|
||||
$po = DB::select('INSERT INTO echomail_path (echomail_id,address_id,parent_id) VALUES (?,?,?) RETURNING id',[
|
||||
$o->id,
|
||||
$aoid,
|
||||
$ppoid,
|
||||
]);
|
||||
|
||||
$ppoid = $po[0]->id;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// @todo if we have an export for any of the seenby addresses, remove it
|
||||
|
||||
//$seenby = $this->parseAddresses('seenby',$this->mo->seenby,$sender->zone,$o->rogue_seenby);
|
||||
//$this->mo->seenby()->syncWithoutDetaching($seenby);
|
||||
|
||||
// In case our rogue_seenby changed
|
||||
//$this->mo->save();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -268,9 +282,8 @@ class MessageProcess implements ShouldQueue
|
||||
// Find another message with the same msg_crc
|
||||
if ($this->mo->msg_crc) {
|
||||
$o = Echomail::where('msg_crc',$xx=md5($this->mo->msg_crc))
|
||||
->where('fftn_id',$this->mo->fftn_id)
|
||||
->where('fftn_id',$this->mo->fftn->id)
|
||||
->where('datetime','>',Carbon::now()->subWeek())
|
||||
->dontCache()
|
||||
->get();
|
||||
|
||||
if ($o->count())
|
||||
|
@ -1,65 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
use App\Models\{Address, Domain, System};
|
||||
use App\Notifications\Netmails\NodesNew as NotificationNodesNew;
|
||||
|
||||
class NodesNew implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private const LOGKEY = 'JNN';
|
||||
|
||||
private ?Carbon $since; // New nodes since this date
|
||||
private Address $ao; // Domain we are processing
|
||||
private Domain $do; // Domain we are processing
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(Domain $do,Carbon $since=NULL,Address $ao=NULL)
|
||||
{
|
||||
$this->do = $do->withoutRelations();
|
||||
$this->ao = $ao?->withoutRelations();
|
||||
$this->since = $since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$since = ($this->since ?: Carbon::parse('last saturday'))->startOfDay();
|
||||
|
||||
$result = Address::FTN()
|
||||
->ActiveFTN()
|
||||
->join('systems',['systems.id'=>'addresses.system_id'])
|
||||
->join('system_zone',['system_zone.system_id'=>'systems.id','system_zone.zone_id'=>'zones.id'])
|
||||
->whereIn('zones.id',$this->do->zones->pluck('id'))
|
||||
->where('systems.active',TRUE)
|
||||
->where('systems.created_at','>=',$since)
|
||||
->get();
|
||||
|
||||
if ($result->count()) {
|
||||
Log::notice(sprintf('%s:- Sending new nodes since [%s] (%d)',self::LOGKEY,$since,$result->count()));
|
||||
|
||||
Notification::route('netmail',$this->ao->withoutRelations())
|
||||
->notify(new NotificationNodesNew(
|
||||
$since,
|
||||
$result,
|
||||
));
|
||||
|
||||
} else
|
||||
Log::notice(sprintf('%s:- No nodes since [%s]',self::LOGKEY,$since));
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ use League\Flysystem\UnableToMoveFile;
|
||||
use App\Classes\File;
|
||||
use App\Classes\FTN\Packet;
|
||||
use App\Exceptions\InvalidPacketException;
|
||||
use App\Models\{Echomail,Netmail,System};
|
||||
use App\Models\{Domain,Echomail,Netmail};
|
||||
use App\Notifications\Netmails\PacketPasswordInvalid;
|
||||
|
||||
class PacketProcess implements ShouldQueue
|
||||
@ -27,15 +27,15 @@ class PacketProcess implements ShouldQueue
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private string $filename;
|
||||
private System $so;
|
||||
private Domain $do;
|
||||
private Carbon $rcvd_time;
|
||||
private bool $interactive;
|
||||
private bool $nobot;
|
||||
|
||||
public function __construct(string $filename,System $so,bool $interactive=TRUE,Carbon $rcvd_time=NULL,bool $nobot=FALSE)
|
||||
public function __construct(string $filename,Domain $do,bool $interactive=TRUE,Carbon $rcvd_time=NULL,bool $nobot=FALSE)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
$this->so = $so->withoutRelations();
|
||||
$this->do = $do;
|
||||
$this->interactive = $interactive;
|
||||
$this->rcvd_time = $rcvd_time ?: Carbon::now();
|
||||
$this->nobot = $nobot;
|
||||
@ -67,11 +67,9 @@ class PacketProcess implements ShouldQueue
|
||||
$f = new File($fs->path($this->filename));
|
||||
|
||||
$processed = FALSE;
|
||||
$bad_archive = FALSE;
|
||||
|
||||
foreach ($f as $packet) {
|
||||
try {
|
||||
$pkt = Packet::process($packet,Arr::get(stream_get_meta_data($packet),'uri'),$f->itemSize(),$this->so);
|
||||
$pkt = Packet::process($packet,Arr::get(stream_get_meta_data($packet),'uri'),$f->itemSize(),$this->do);
|
||||
|
||||
// Check that the packet is from a system that is defined in the DB
|
||||
if (! $pkt->fftn) {
|
||||
@ -81,21 +79,13 @@ class PacketProcess implements ShouldQueue
|
||||
}
|
||||
|
||||
if (! our_nodes($pkt->fftn->zone->domain)->contains($pkt->fftn)) {
|
||||
Log::error(sprintf('%s:! Packet [%s] is from a system that is not configured with us? [%s]',self::LOGKEY,$this->filename,$pkt->fftn_t));
|
||||
Log::error(sprintf('%s:! Packet [%s] is from a system that is not configured with us? [%s] for [%s]',self::LOGKEY,$this->filename,$pkt->fftn_t,$this->do->name));
|
||||
|
||||
// @todo Notification::route('netmail',$pkt->fftn)->notify(new UnexpectedPacketFromYou($this->filename));
|
||||
// @todo Parse the packet for netmails and process them. We'll only accept netmails to us, and ignore all others
|
||||
break;
|
||||
}
|
||||
|
||||
// If we dont have the tftn in the DB, then packet cannot be to us
|
||||
if (! $pkt->tftn) {
|
||||
Log::error(sprintf('%s:! Packet [%s] is from a system [%s] we dont know about?',self::LOGKEY,$this->filename,$pkt->tftn_t));
|
||||
|
||||
// @todo Notification::route('netmail',$pkt->fftn)->notify(new UnexpectedPacketToUs($this->filename));
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the packet is to our address, if not we'll reject it.
|
||||
if (! our_address($pkt->tftn->zone->domain)->contains($pkt->tftn)) {
|
||||
Log::error(sprintf('%s:! Packet [%s] is not to our address? [%s]',self::LOGKEY,$this->filename,$pkt->tftn->ftn));
|
||||
@ -155,15 +145,9 @@ class PacketProcess implements ShouldQueue
|
||||
|
||||
if ($count === $pkt->count())
|
||||
$processed = TRUE;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('%s:! Got an exception [%s] processing packet',self::LOGKEY,$e->getMessage()));
|
||||
|
||||
$bad_archive = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if ((! $processed) || $bad_archive) {
|
||||
if (! $processed) {
|
||||
Log::alert(sprintf('%s:- Not deleting packet [%s], it doesnt seem to be processed?',self::LOGKEY,$this->filename));
|
||||
|
||||
} else {
|
||||
|
@ -60,15 +60,15 @@ class SystemHeartbeat #implements ShouldQueue
|
||||
// If we havent polled in heatbeat hours, poll system
|
||||
foreach ($l as $oo) {
|
||||
if (Job::where('queue','poll')->get()->pluck('command.address.id')->search($oo->id) === FALSE) {
|
||||
if ((! $oo->system->last_seen)
|
||||
|| ($oo->system->hearbeat && ($oo->system->last_seen->addHours($oo->system->heartbeat) < Carbon::now()))
|
||||
|| ((! $oo->system->hearbeat) && ($oo->role_id < Address::NODE_NN) && ($oo->system->last_seen->addHours(6) < Carbon::now())))
|
||||
if ((! $oo->system->last_session)
|
||||
|| ($oo->system->hearbeat && ($oo->system->last_session->addHours($oo->system->heartbeat) < Carbon::now()))
|
||||
|| ((! $oo->system->hearbeat) && ($oo->role_id < Address::NODE_NN) && ($oo->system->last_session->addHours(6) < Carbon::now())))
|
||||
{
|
||||
Log::info(sprintf('%s:- Polling [%s] (%s) - we havent seen them since [%s], heartbeat [%d]',
|
||||
self::LOGKEY,
|
||||
$oo->ftn,
|
||||
$oo->system->name,
|
||||
$oo->system->last_seen ?: 'Never',
|
||||
$oo->system->last_session ?: 'Never',
|
||||
$oo->system->heartbeat,
|
||||
));
|
||||
|
||||
@ -78,7 +78,7 @@ class SystemHeartbeat #implements ShouldQueue
|
||||
Log::debug(sprintf('%s:= Not scheduling poll to [%s], we saw them [%s], heartbeat [%d]',
|
||||
self::LOGKEY,
|
||||
$oo->ftn,
|
||||
$oo->system->last_seen,
|
||||
$oo->system->last_session,
|
||||
$oo->system->heartbeat
|
||||
));
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class EchomailListener implements ShouldQueue
|
||||
$ea = $event->eo->echoarea;
|
||||
|
||||
// Catch our messages that we've posted, so they dont go back
|
||||
if (preg_match('/^@.+:/',$event->eo->from))
|
||||
if (str_ends_with($event->eo->from,':'.config('matrix.server')))
|
||||
return;
|
||||
|
||||
if ($ea && collect(config('matrix.rooms'))->contains($ea->name)) {
|
||||
|
@ -42,7 +42,7 @@ class TestEmail extends Mailable
|
||||
->markdown('mail.system.test_email')
|
||||
->subject('Just a test...')
|
||||
->with([
|
||||
'url'=>sprintf('https://%s',gethostname()),
|
||||
'url'=>'https://localhost',
|
||||
]);
|
||||
}
|
||||
}
|
@ -24,28 +24,19 @@ use App\Traits\{QueryCacheableConfig,ScopeActive};
|
||||
*
|
||||
* If an address is validated, we know that the system is using the address (we've confirmed that during a session).
|
||||
* validated is update/removed is during a mailer session, confirming if the node has the address
|
||||
* @todo Remove validated, if that system hasnt used the address for a defined period (eg: 30)
|
||||
*
|
||||
* We'll only trigger a poll to a system that we have mail for if active && validated, unless "forced".
|
||||
*
|
||||
* Any mail for that address will be delivered, if active && validated.
|
||||
*
|
||||
* Address status:
|
||||
* + Active (active=true/validated=true) - mail can flow and in one of our networks (we have session details)
|
||||
* + Pending (active=true/validated=false) - remote hasnt configured address during a session and in one of our networks
|
||||
* + Known (active=true/validated=true) - the node presented this address, but we didnt auth it, and its a network we are not in
|
||||
* + Unconfirm (active=true/validated=true) - the node presented this address, but we dont manage the address (it uses a different hub)
|
||||
* + Nodelist (active=true/validated=true) - the node presented this address, but we dont manage the address and its in a recent nodelist
|
||||
* + Delisted (active=false/validated=true) - this node shouldnt use this address any more
|
||||
* + Freed (active=false/validated=false) - this node shouldnt is known to not be using this address any more
|
||||
*
|
||||
* Other Status
|
||||
* + citizen - The address belongs to one of our jurisdiction (hub_id = our address, NC,RC,ZC)
|
||||
* + foreign - The address doesnt belong to our jurisdiction
|
||||
*
|
||||
* @see \App\Http\Requests\AddressAdd::class for rules about AKA and role
|
||||
*/
|
||||
|
||||
// Need to add
|
||||
// Calculated Region for an FTN, ie: find the parent and use their region id
|
||||
// - if creating an RC, then the region is part of the creation, ZC region is 0
|
||||
// Then use this in createFTN
|
||||
|
||||
class Address extends Model
|
||||
{
|
||||
use QueryCacheableConfig,ScopeActive,SoftDeletes;
|
||||
@ -61,10 +52,9 @@ class Address extends Model
|
||||
public const NODE_HC = 1<<3; // Hub
|
||||
public const NODE_NN = 1<<4; // Node
|
||||
public const NODE_PVT = 1<<5; // Pvt (we dont have address information) @todo
|
||||
public const NODE_HOLD = 1<<6; // Hold (user has requested hold, we havent heard from the node for 7 days
|
||||
public const NODE_DOWN = 1<<7; // Down we havent heard from the node for 30 days
|
||||
public const NODE_HOLD = 1<<6; // Hold (user has requested hold, we havent heard from the node for 7 days @todo
|
||||
public const NODE_DOWN = 1<<7; // Down we havent heard from the node for 30 days @todo
|
||||
public const NODE_POINT = 1<<8; // Point
|
||||
public const NODE_KEEP = 1<<9; // Dont mark an address hold/down or de-list automatically
|
||||
public const NODE_UNKNOWN = 1<<15; // Unknown
|
||||
public const NODE_ALL = 0x811f; // Mask to catch all nodes, excluding their status
|
||||
|
||||
@ -185,11 +175,10 @@ class Address extends Model
|
||||
*
|
||||
* @param string $address
|
||||
* @param bool $trashed
|
||||
* @param bool $recent
|
||||
* @return Address|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function findFTN(string $address,bool $trashed=FALSE,bool $recent=FALSE): ?self
|
||||
public static function findFTN(string $address,bool $trashed=FALSE): ?self
|
||||
{
|
||||
$ftn = self::parseFTN($address);
|
||||
$o = NULL;
|
||||
@ -198,11 +187,8 @@ class Address extends Model
|
||||
->select('addresses.*')
|
||||
->join('zones',['zones.id'=>'addresses.zone_id'])
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->when($trashed,function($query) use ($recent) {
|
||||
return $query->withTrashed()
|
||||
->orderBy('updated_at','DESC')
|
||||
->when($recent,fn($query)=>$query->where(fn($query)=>$query
|
||||
->where('deleted_at','>=',Carbon::now()->subMonth())->orWhereNull('deleted_at')));
|
||||
->when($trashed,function($query) {
|
||||
$query->withTrashed();
|
||||
},function($query) {
|
||||
$query->active();
|
||||
})
|
||||
@ -223,7 +209,7 @@ class Address extends Model
|
||||
$o = $query
|
||||
->where('region_id',$ftn['n'])
|
||||
->where('host_id',$ftn['n'])
|
||||
->with(['system:id,active,name,address,pkt_msgs,last_session,hold'])
|
||||
->with(['system:id,active'])
|
||||
->first();
|
||||
|
||||
// Look for a normal address
|
||||
@ -238,7 +224,7 @@ class Address extends Model
|
||||
})
|
||||
->orWhere('host_id',$ftn['n']);
|
||||
})
|
||||
->with(['system:id,active,name,address,pkt_msgs,last_session,hold'])
|
||||
->with(['system:id,active'])
|
||||
->first();
|
||||
|
||||
// Check and see if we are a flattened domain, our address might be available with a different zone.
|
||||
@ -255,7 +241,7 @@ class Address extends Model
|
||||
$o = self::findZone($do,$ftn['n'],$ftn['f'],$ftn['p'],$trashed);
|
||||
}
|
||||
|
||||
return ($o && ($trashed || $o->system->active)) ? $o : NULL;
|
||||
return ($o && $o->system->active) ? $o : NULL;
|
||||
}
|
||||
|
||||
public static function newFTN(string $address): self
|
||||
@ -263,26 +249,16 @@ class Address extends Model
|
||||
$ftn = self::parseFTN($address);
|
||||
$do = $ftn['d'] ? Domain::where('name',$ftn['d'])->single() : NULL;
|
||||
|
||||
$o = new self;
|
||||
$o->region_id = $ftn['r'];
|
||||
$o->host_id = $ftn['n'];
|
||||
$o->node_id = $ftn['f'];
|
||||
$o->point_id = $ftn['p'];
|
||||
|
||||
$zo = Zone::where('zone_id',$ftn['z'])
|
||||
->when($do,fn($query)=>$query->where('domain_id',$do->id))
|
||||
->single();
|
||||
|
||||
$o = new self;
|
||||
$o->zone_id = $zo?->id;
|
||||
|
||||
if (($ftn['z'] === 0) || (! $zo)) {
|
||||
Log::alert(sprintf('%s:! newFTN was parsed an FTN [%s] with a zero zone, adding empty zone in domain',self::LOGKEY,$address));
|
||||
|
||||
$zo = new Zone;
|
||||
$zo->domain_id = $do?->id;
|
||||
}
|
||||
|
||||
$o->zone()->associate($zo);
|
||||
$o->region_id = $ftn['r'];
|
||||
$o->host_id = $ftn['n'];
|
||||
$o->node_id = $ftn['f'];
|
||||
$o->point_id = $ftn['p'];
|
||||
|
||||
return $o;
|
||||
}
|
||||
@ -353,42 +329,37 @@ class Address extends Model
|
||||
throw new InvalidFTNException(sprintf('Invalid FTN: [%s] - point address invalid [%d]',$ftn,$matches[4]));
|
||||
|
||||
// Work out region
|
||||
$region_id = NULL;
|
||||
$region_id = 0;
|
||||
$zone_id = NULL;
|
||||
|
||||
// We can only work out region/zone if we have a domain - this is for 2D parsing
|
||||
// We can only work out region if we have a domain
|
||||
if ($matches[5] ?? NULL) {
|
||||
$o = new self;
|
||||
$o->host_id = $matches[2];
|
||||
$o->node_id = $matches[3];
|
||||
$o->point_id = empty($matches[4]) ? 0 : (int)$matches[4];
|
||||
|
||||
if ($matches[1] !== "0") {
|
||||
$zo = Zone::select('zones.*')
|
||||
->where('zone_id',$matches[1])
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->where('domains.name',$matches[5])
|
||||
->single();
|
||||
|
||||
// Try and find out the zone from the host_id
|
||||
} else {
|
||||
$zo = Zone::select('zones.*')
|
||||
->where(fn($query)=>$query->where('host_id',$matches[2])->orWhere('region_id',$matches[2]))
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->join('addresses',['addresses.zone_id'=>'zones.id'])
|
||||
->where('domains.name',$matches[5])
|
||||
->first();
|
||||
}
|
||||
|
||||
$zo = Zone::select('zones.*')->where('zone_id',$matches[1])->join('domains',['domains.id'=>'zones.domain_id'])->where('domains.name',$matches[5])->single();
|
||||
$o->zone_id = $zo?->id;
|
||||
$parent = $o->parent();
|
||||
$zone_id = $parent?->zone->zone_id;
|
||||
|
||||
// For flattened domains
|
||||
if ($zo?->domain->flatten && is_null($zone_id))
|
||||
foreach ($zo->domain->zones as $zoo) {
|
||||
$o->zone_id = $zoo->id;
|
||||
$parent = $o->parent();
|
||||
|
||||
if ($parent)
|
||||
break;
|
||||
}
|
||||
|
||||
$region_id = $parent?->region_id;
|
||||
$zone_id = $parent?->zone->zone_id;
|
||||
}
|
||||
|
||||
return [
|
||||
'z' => (int)($zone_id ?: $matches[1]),
|
||||
'z' => (int)$zone_id ?: $matches[1],
|
||||
'r' => (int)$region_id,
|
||||
'n' => (int)$matches[2],
|
||||
'f' => (int)$matches[3],
|
||||
@ -404,14 +375,16 @@ class Address extends Model
|
||||
*
|
||||
* @param $query
|
||||
* @return mixed
|
||||
* @note zones and domains needs to be joined in the base call, or use FTN()
|
||||
*/
|
||||
public function scopeActiveFTN($query)
|
||||
{
|
||||
return $query
|
||||
return $query->select($this->getTable().'.*')
|
||||
->join('zones',['zones.id'=>'addresses.zone_id'])
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->where('zones.active',TRUE)
|
||||
->where('domains.active',TRUE)
|
||||
->active();
|
||||
->active()
|
||||
->FTNorder();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -423,21 +396,13 @@ class Address extends Model
|
||||
public function scopeFTN($query)
|
||||
{
|
||||
return $query
|
||||
->select(['addresses.id','region_id','host_id','node_id','point_id','addresses.zone_id','addresses.active','role','security','addresses.system_id','addresses.active','validated','deleted_at'])
|
||||
->join('zones',['zones.id'=>'addresses.zone_id'])
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->orderBy('domains.name')
|
||||
->orderBy('region_id')
|
||||
->orderBy('host_id')
|
||||
->orderBy('node_id')
|
||||
->orderBy('point_id')
|
||||
->select(['id','addresses.zone_id','host_id','node_id','point_id','system_id'])
|
||||
->with([
|
||||
'zone:zones.id,domain_id,zone_id,active',
|
||||
'zone.domain:domains.id,name,active,public',
|
||||
'zone:zones.id,domain_id,zone_id',
|
||||
'zone.domain:domains.id,name',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @deprecated use FTN() */
|
||||
public function scopeFTNOrder($query)
|
||||
{
|
||||
return $query
|
||||
@ -483,7 +448,7 @@ class Address extends Model
|
||||
'node_id',
|
||||
'point_id',
|
||||
'system_id',
|
||||
DB::raw('count(addresses.id) as uncollected_echomail'),
|
||||
DB::raw('count(*) as uncollected_echomail'),
|
||||
DB::raw('0 as uncollected_netmail'),
|
||||
DB::raw('0 as uncollected_files'),
|
||||
])
|
||||
@ -520,7 +485,7 @@ class Address extends Model
|
||||
'system_id',
|
||||
DB::raw('0 as uncollected_echomail'),
|
||||
DB::raw('0 as uncollected_netmail'),
|
||||
DB::raw('count(addresses.id) as uncollected_files')
|
||||
DB::raw('count(*) as uncollected_files')
|
||||
])
|
||||
->UncollectedFiles();
|
||||
}
|
||||
@ -558,7 +523,7 @@ class Address extends Model
|
||||
'point_id',
|
||||
'system_id',
|
||||
DB::raw('0 as uncollected_echomail'),
|
||||
DB::raw('count(addresses.id) as uncollected_netmail'),
|
||||
DB::raw('count(*) as uncollected_netmail'),
|
||||
DB::raw('0 as uncollected_files')
|
||||
])
|
||||
->UncollectedNetmail();
|
||||
@ -584,7 +549,6 @@ class Address extends Model
|
||||
public function echoareas()
|
||||
{
|
||||
return $this->belongsToMany(Echoarea::class)
|
||||
->using(AddressEchoarea::class)
|
||||
->orderBy('name')
|
||||
->withPivot(['subscribed']);
|
||||
}
|
||||
@ -784,9 +748,6 @@ class Address extends Model
|
||||
if (! $this->relationLoaded('zone'))
|
||||
$this->load(['zone:id,domain_id,zone_id']);
|
||||
|
||||
if (! $this->zone)
|
||||
throw new InvalidFTNException(sprintf('Invalid Zone for FTN address [%d/%d.%d@%s]',$this->host_id ?: $this->region_id,$this->node_id,$this->point_id,$this->domain?->name));
|
||||
|
||||
return sprintf('%d:%s',$this->zone->zone_id,$this->getFTN2DAttribute());
|
||||
}
|
||||
|
||||
@ -1174,8 +1135,8 @@ class Address extends Model
|
||||
*/
|
||||
public function getNetmail(): ?Packet
|
||||
{
|
||||
if ($count=($num=$this->netmailAlertWaiting())->count()) {
|
||||
Log::info(sprintf('%s:= Packaging [%d] netmail alerts to [%s]',self::LOGKEY,$count,$this->ftn));
|
||||
if (($num=$this->netmailAlertWaiting())->count()) {
|
||||
Log::debug(sprintf('%s:= Packaging [%d] netmail alerts to [%s]',self::LOGKEY,$num->count(),$this->ftn));
|
||||
|
||||
$msgs = $num->get();
|
||||
|
||||
@ -1199,11 +1160,11 @@ class Address extends Model
|
||||
);
|
||||
}
|
||||
|
||||
if ($count=($num=$this->netmailWaiting())->count()) {
|
||||
Log::info(sprintf('%s:= Got [%d] netmails for [%s] for sending',self::LOGKEY,$count,$this->ftn));
|
||||
if (($num=$this->netmailWaiting())->count()) {
|
||||
Log::debug(sprintf('%s:= Got [%d] netmails for [%s] for sending',self::LOGKEY,$num->count(),$this->ftn));
|
||||
|
||||
// Limit to max messages
|
||||
if ($count > $this->system->pkt_msgs)
|
||||
if ($num->count() > $this->system->pkt_msgs)
|
||||
Log::alert(sprintf('%s:= Only sending [%d] netmails for [%s]',self::LOGKEY,$this->system->pkt_msgs,$this->ftn));
|
||||
|
||||
return $this->system->packet($this)->mail($num->take($this->system->pkt_msgs)->get());
|
||||
@ -1228,17 +1189,10 @@ class Address extends Model
|
||||
*/
|
||||
public function netmailWaiting(): Builder
|
||||
{
|
||||
// Addresses that our downstream of this address, except anybody that has session details with us
|
||||
$ours = our_nodes($this->zone->domain)->pluck('id');
|
||||
|
||||
$addresses = $this->downstream()
|
||||
->filter(fn($item)=>! $ours->contains($item->id))
|
||||
->merge($this->system->match($this->zone,Address::NODE_ALL));
|
||||
|
||||
$netmails = $this
|
||||
->UncollectedNetmail()
|
||||
->select('netmails.id')
|
||||
->whereIn('addresses.id',$addresses->pluck('id'))
|
||||
->whereIn('addresses.id',$this->downlinks()->add($this)->pluck('id'))
|
||||
->groupBy(['netmails.id'])
|
||||
->get();
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class AddressEchoarea extends Pivot
|
||||
{
|
||||
protected $casts = [
|
||||
'subscribed' => 'datetime:Y-m-d H:i',
|
||||
];
|
||||
}
|
@ -11,16 +11,9 @@ use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
use App\Models\Casts\CompressedStringOrNull;
|
||||
use App\Casts\CompressedStringOrNull;
|
||||
use App\Traits\{QueryCacheableConfig,ScopeActive};
|
||||
|
||||
/**
|
||||
* This represents an FTN Domain.
|
||||
*
|
||||
* We are either a member of the domain (because we have an AKA in it) or NOT.
|
||||
*
|
||||
* The assumption is, if we are a member of the domain, we receive/send mail to an uplink and possibly downlinks
|
||||
*/
|
||||
class Domain extends Model
|
||||
{
|
||||
use HasFactory,QueryCacheableConfig,ScopeActive;
|
||||
@ -35,30 +28,26 @@ class Domain extends Model
|
||||
/* SCOPES */
|
||||
|
||||
/**
|
||||
* A domain is public (visible), if the user is an admin or, the domain is marked public)
|
||||
* Only query active records
|
||||
*/
|
||||
public function scopePublic($query)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
return $query
|
||||
->active()
|
||||
->when((! $user) || (! $user->isAdmin()),
|
||||
fn($query)=>$query->where('public',TRUE));
|
||||
->when(((! $user) || (! $user->isAdmin())),function($query) { return $query->where('public',TRUE)->active(); });
|
||||
}
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
public function echoareas()
|
||||
{
|
||||
return $this->hasMany(Echoarea::class)
|
||||
->orderBy('name');
|
||||
return $this->hasMany(Echoarea::class);
|
||||
}
|
||||
|
||||
public function fileareas()
|
||||
{
|
||||
return $this->hasMany(Filearea::class)
|
||||
->orderBy('name');
|
||||
return $this->hasMany(Filearea::class);
|
||||
}
|
||||
|
||||
public function nodelist_filearea()
|
||||
@ -66,7 +55,7 @@ class Domain extends Model
|
||||
return $this->belongsTo(Filearea::class);
|
||||
}
|
||||
|
||||
public function nodestatus_echoarea()
|
||||
public function nodestatusarea()
|
||||
{
|
||||
return $this->belongsTo(Echoarea::class,'nodestatus_id');
|
||||
}
|
||||
@ -74,47 +63,38 @@ class Domain extends Model
|
||||
public function zones()
|
||||
{
|
||||
return $this->hasMany(Zone::class)
|
||||
->select(['id','zone_id','domain_id','active'])
|
||||
->orderBy('zone_id');
|
||||
->select(['id','zone_id','domain_id']);
|
||||
}
|
||||
|
||||
/* ATTRIBUTES */
|
||||
|
||||
/**
|
||||
* We can accept applications if we have an address in the domain
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getCanAcceptAppAttribute(): bool
|
||||
{
|
||||
return $this->isManaged()
|
||||
return ($x=our_address($this))
|
||||
&& $x->count()
|
||||
&& $this->active
|
||||
&& $this->accept_app
|
||||
&& Auth::id();
|
||||
&& Auth::id()
|
||||
&& $this->userHasSystemsNotInNet(Auth::user())->count();
|
||||
}
|
||||
|
||||
public function getHomePageAttribute(?string $value): string
|
||||
{
|
||||
//0xFD2FB528
|
||||
return $this->castAttribute('homepage',$value) ?: 'No available information at the moment.';
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
/**
|
||||
* Show count of messages by day/week/month/all stats for each echoarea in this domain
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function echoarea_stats(): Collection
|
||||
{
|
||||
return Cache::remember(md5(sprintf('%d-%s',$this->id,__METHOD__)),self::CACHE_TIME,function() {
|
||||
$dt = Carbon::now()->startOfday();
|
||||
$case = CaseBuilder::whenRaw("datetime >= '?'",$dt->subDay()->format('Y-m-d'))->thenRaw("'day'")
|
||||
->whenRaw("datetime >= '?'",$dt->subDays(7)->format('Y-m-d'))->thenRaw("'week'")
|
||||
->whenRaw("datetime >= '?'",$dt->subMonth()->format('Y-m-d'))->thenRaw("'month'")
|
||||
->elseRaw("'all'");
|
||||
|
||||
return Echoarea::select(['echoareas.id','name','description','active',DB::raw('count(echomails.id) AS count'),DB::raw('min(datetime) as first_message'),DB::raw('max(datetime) as last_message')])
|
||||
return Echoarea::select(['echoareas.id','name','description','active',DB::raw('count(echomails.id) AS count'),DB::raw('max(datetime) as last_message')])
|
||||
->selectRaw($case->toRaw().' AS stats')
|
||||
->join('echomails',['echomails.echoarea_id'=>'echoareas.id'],NULL,NULL,'left outer')
|
||||
->where('domain_id',$this->id)
|
||||
@ -124,26 +104,19 @@ class Domain extends Model
|
||||
->orderBy('echoareas.name')
|
||||
->orderBy('last_message','DESC')
|
||||
->get();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show daily total of messages by echoarea in this domain
|
||||
*
|
||||
* @param Collection|NULL $systems
|
||||
* @return Collection
|
||||
*/
|
||||
public function echoarea_total_daily(Collection $systems=NULL): Collection
|
||||
{
|
||||
return Cache::remember(md5(sprintf('%d-%s',$this->id,$systems?->pluck('id')->join(','))),self::CACHE_TIME,function() use ($systems) {
|
||||
return DB::query()
|
||||
->select(['echoareas.name','echoareas.show',DB::raw('COUNT(echoareas.name) AS count'),DB::raw('datetime::date AS date')])
|
||||
->select(['echoareas.name','echoareas.show',DB::raw('COUNT(*) AS count'),DB::raw('datetime::date AS date')])
|
||||
->from($this->getTable())
|
||||
->join('echoareas',['echoareas.domain_id'=>'domains.id'])
|
||||
->join('echomails',['echomails.echoarea_id'=>'echoareas.id'])
|
||||
->where('domains.id',$this->id)
|
||||
->where('echomails.datetime','>=',Carbon::now()->subMonths(self::STATS_MONTHS)->startOfMonth())
|
||||
->when($systems?->count(),fn($query)=>$query->whereIn('echomails.fftn_id',$systems->pluck('addresses')->flatten()->pluck('id')))
|
||||
->when($systems?->count(),function($query) use ($systems) { return $query->whereIn('echomails.fftn_id',$systems->pluck('addresses')->flatten()->pluck('id')); })
|
||||
->groupBy(['echoareas.name','echoareas.show','date'])
|
||||
->orderBy('echoareas.name')
|
||||
->orderBy('date')
|
||||
@ -151,30 +124,6 @@ class Domain extends Model
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show count of files by week/month/all status for each filearea in this domain
|
||||
*/
|
||||
public function filearea_stats()
|
||||
{
|
||||
return Cache::remember(md5(sprintf('%d-%s',$this->id,__METHOD__)),self::CACHE_TIME,function() {
|
||||
$dt = Carbon::now()->startOfday();
|
||||
$case = CaseBuilder::whenRaw("datetime >= '?'",$dt->subDays(7)->format('Y-m-d'))->thenRaw("'week'")
|
||||
->whenRaw("datetime >= '?'",$dt->subMonth()->format('Y-m-d'))->thenRaw("'month'")
|
||||
->elseRaw("'all'");
|
||||
|
||||
return Filearea::select(['fileareas.id','fileareas.name','description','active',DB::raw('count(files.id) AS count'),DB::raw('min(datetime) as first_file'),DB::raw('max(datetime) as last_file')])
|
||||
->selectRaw($case->toRaw().' AS stats')
|
||||
->join('files',['files.filearea_id'=>'fileareas.id'],NULL,NULL,'left outer')
|
||||
->where('domain_id',$this->id)
|
||||
->groupBy('fileareas.id')
|
||||
->groupBy('fileareas.name')
|
||||
->groupBy('stats')
|
||||
->orderBy('fileareas.name')
|
||||
->orderBy('last_file','DESC')
|
||||
->get();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this zone is managed by this host
|
||||
*
|
||||
@ -183,6 +132,28 @@ class Domain extends Model
|
||||
*/
|
||||
public function isManaged(): bool
|
||||
{
|
||||
return our_address($this)->count() > 0;
|
||||
return ($x=our_address())
|
||||
&& $x->pluck('zone.domain')
|
||||
->pluck('id')
|
||||
->contains($this->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Work out which of the users systems are not in this domain
|
||||
*
|
||||
* @param User $o
|
||||
* @return Collection
|
||||
*/
|
||||
public function userHasSystemsNotInNet(User $o): Collection
|
||||
{
|
||||
$o->load('systems.akas.zone');
|
||||
|
||||
$result = collect();
|
||||
foreach ($o->systems->filter(function($item) { return $item->active; }) as $so) {
|
||||
if (! $so->akas->pluck('zone')->unique('domain_id')->pluck('domain_id')->contains($this->id))
|
||||
$result->push($so);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
use App\Models\Casts\CollectionOrNull;
|
||||
use App\Casts\CollectionOrNull;
|
||||
|
||||
class Dynamic extends Model
|
||||
{
|
||||
|
@ -46,8 +46,7 @@ class Echoarea extends Model
|
||||
private const CACHE_TIME = 3600;
|
||||
|
||||
protected $casts = [
|
||||
'first_message' => 'datetime:Y-m-d H:i:s',
|
||||
'last_message' => 'datetime:Y-m-d H:i:s',
|
||||
'last_message' => 'datetime:Y-m-d H:i:s'
|
||||
];
|
||||
|
||||
/* RELATIONS */
|
||||
|
@ -9,10 +9,10 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Casts\{CollectionOrNull,CompressedStringOrNull,UTF8StringOrNull};
|
||||
use App\Classes\FTN\Message;
|
||||
use App\Events\Echomail as EchomailEvent;
|
||||
use App\Interfaces\Packet;
|
||||
use App\Models\Casts\{CompressedStringOrNull,CollectionOrNull,UTF8StringOrNull};
|
||||
use App\Traits\{MessageAttributes,MsgID,ParseAddresses,QueryCacheableConfig};
|
||||
|
||||
final class Echomail extends Model implements Packet
|
||||
@ -150,7 +150,7 @@ final class Echomail extends Model implements Packet
|
||||
// Make sure our origin contains our FTN
|
||||
$m = [];
|
||||
if ((preg_match('#^(.*)\s+\(([0-9]+:[0-9]+/[0-9]+.*)\)+\s*$#',$model->set_origin,$m))
|
||||
&& (Address::findFTN(sprintf('%s@%s',$m[2],$model->fftn->domain->name),TRUE,TRUE)?->id === $model->fftn_id))
|
||||
&& (Address::findFTN(sprintf('%s@%s',$m[2],$model->fftn->domain->name))?->id === $model->fftn_id))
|
||||
{
|
||||
$x = Origin::where('value',utf8_encode($m[1]))->single();
|
||||
|
||||
@ -184,13 +184,13 @@ final class Echomail extends Model implements Packet
|
||||
Log::debug(sprintf('%s:^ Message [%d] from point address is [%d]',self::LOGKEY,$model->id,$model->fftn->point_id));
|
||||
|
||||
// Make sure our sender is first in the path
|
||||
if (($model->fftn->point_id === 0) && (! $model->isFlagSet(Message::FLAG_LOCAL)) && (! $path->contains($model->fftn_id))) {
|
||||
if ((! $model->isFlagSet(Message::FLAG_LOCAL)) && (! $path->contains($model->fftn_id))) {
|
||||
Log::alert(sprintf('%s:? Echomail adding sender to start of PATH [%s].',self::LOGKEY,$model->fftn_id));
|
||||
$path->prepend($model->fftn_id);
|
||||
}
|
||||
|
||||
// Make sure our pktsrc is last in the path
|
||||
if ($model->set->has('set_sender') && (! $path->contains($model->set->get('set_sender')->id)) && ($model->set->get('set_sender')->point_id === 0)) {
|
||||
if ($model->set->has('set_sender') && (! $path->contains($model->set->get('set_sender')->id))) {
|
||||
Log::alert(sprintf('%s:? Echomail adding pktsrc to end of PATH [%s].',self::LOGKEY,$model->set->get('set_sender')->ftn));
|
||||
$path->push($model->set->get('set_sender')->id);
|
||||
}
|
||||
@ -215,13 +215,13 @@ final class Echomail extends Model implements Packet
|
||||
$seenby = self::parseAddresses('seenby',$model->set->get('set_seenby'),$model->fftn->zone,$rogue);
|
||||
|
||||
// Make sure our sender is in the seenby
|
||||
if (($model->fftn->point_id === 0) && (! $model->isFlagSet(Message::FLAG_LOCAL)) && (! $seenby->contains($model->fftn_id))) {
|
||||
if ((! $model->isFlagSet(Message::FLAG_LOCAL)) && (! $seenby->contains($model->fftn_id))) {
|
||||
Log::alert(sprintf('%s:? Echomail adding sender to SEENBY [%s].',self::LOGKEY,$model->fftn_id));
|
||||
$seenby->push($model->fftn_id);
|
||||
}
|
||||
|
||||
// Make sure our pktsrc is in the seenby
|
||||
if ($model->set->has('set_sender') && (! $seenby->contains($model->set->get('set_sender')->id)) && ($model->set->get('set_sender')->point_id === 0)) {
|
||||
if ($model->set->has('set_sender') && (! $seenby->contains($model->set->get('set_sender')->id))) {
|
||||
Log::alert(sprintf('%s:? Echomail adding pktsrc to SEENBY [%s].',self::LOGKEY,$model->set->get('set_sender')->ftn));
|
||||
$seenby->push($model->set->get('set_sender')->id);
|
||||
}
|
||||
@ -250,7 +250,6 @@ final class Echomail extends Model implements Packet
|
||||
}
|
||||
|
||||
// See if we need to export this message.
|
||||
// @todo We need to limit exporting if address/system is not active
|
||||
if ($model->echoarea->sec_read) {
|
||||
$exportto = $model
|
||||
->echoarea
|
||||
@ -291,7 +290,6 @@ final class Echomail extends Model implements Packet
|
||||
return $this->belongsToMany(Address::class,'echomail_seenby')
|
||||
->select(['id','zone_id','host_id','node_id'])
|
||||
->withPivot(['export_at','sent_at','sent_pkt'])
|
||||
->dontCache()
|
||||
->FTN2DOrder();
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
use App\Models\Casts\{CompressedStringOrNull,CollectionOrNull};
|
||||
use App\Casts\{CollectionOrNull,CompressedStringOrNull};
|
||||
|
||||
class File extends Model
|
||||
{
|
||||
|
@ -11,11 +11,6 @@ class Filearea extends Model
|
||||
{
|
||||
use SoftDeletes,ScopeActive,AreaSecurity;
|
||||
|
||||
protected $casts = [
|
||||
'first_file' => 'datetime:Y-m-d H:i:s',
|
||||
'last_file' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
];
|
||||
@ -31,9 +26,4 @@ class Filearea extends Model
|
||||
{
|
||||
return $this->belongsTo(Domain::class);
|
||||
}
|
||||
|
||||
public function files()
|
||||
{
|
||||
return $this->hasMany(File::class);
|
||||
}
|
||||
}
|
@ -10,9 +10,9 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use App\Casts\{CollectionOrNull,CompressedStringOrNull,UTF8StringOrNull};
|
||||
use App\Interfaces\Packet;
|
||||
use App\Models\Casts\{CompressedStringOrNull,CollectionOrNull,UTF8StringOrNull};
|
||||
use App\Models\Pivots\ViaPivot;
|
||||
use App\Pivots\ViaPivot;
|
||||
use App\Traits\{MessageAttributes,MsgID};
|
||||
|
||||
final class Netmail extends Model implements Packet
|
||||
@ -21,7 +21,7 @@ final class Netmail extends Model implements Packet
|
||||
|
||||
private const LOGKEY = 'MN-';
|
||||
public const UPDATED_AT = NULL;
|
||||
private const PATH_REGEX = '/^([0-9]+:[0-9]+\/[0-9]+(\.?[0-9@a-zA-Z]*)?)\s+@([0-9.a-zA-Z]+)\s+(.*)$/';
|
||||
private const PATH_REGEX = '/^([0-9]+:[0-9]+\/[0-9]+(\..*)?)\s+@([0-9.a-zA-Z]+)\s+(.*)$/';
|
||||
|
||||
/**
|
||||
* Kludges that we absorb in this model
|
||||
@ -46,12 +46,6 @@ final class Netmail extends Model implements Packet
|
||||
public function __get($key)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'get_fftn':
|
||||
return $this->set->get('set_fftn') ?: $this->fftn->ftn;
|
||||
|
||||
case 'get_tftn':
|
||||
return $this->set->get('set_tftn') ?: $this->tftn->ftn;
|
||||
|
||||
case 'set_fftn':
|
||||
case 'set_tftn':
|
||||
case 'set_path':
|
||||
@ -148,7 +142,7 @@ final class Netmail extends Model implements Packet
|
||||
// Make sure our origin contains our FTN
|
||||
$m = [];
|
||||
if ((preg_match('#^(.*)\s+\(([0-9]+:[0-9]+/[0-9]+.*)\)+\s*$#',$model->set_origin,$m))
|
||||
&& (Address::findFTN(sprintf('%s@%s',$m[2],$model->fftn->domain->name),TRUE,TRUE)?->id === $model->fftn_id))
|
||||
&& (Address::findFTN(sprintf('%s@%s',$m[2],$model->fftn->domain->name))?->id === $model->fftn_id))
|
||||
{
|
||||
$x = Origin::where('value',utf8_encode($m[1]))->single();
|
||||
|
||||
@ -247,11 +241,6 @@ final class Netmail extends Model implements Packet
|
||||
->using(ViaPivot::class);
|
||||
}
|
||||
|
||||
public function sent()
|
||||
{
|
||||
return $this->belongsTo(Address::class);
|
||||
}
|
||||
|
||||
public function tftn()
|
||||
{
|
||||
return $this
|
||||
@ -277,16 +266,6 @@ final class Netmail extends Model implements Packet
|
||||
: $this->getRelationValue('path');
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the body of a message into a collection of lines.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBodyLinesAttribute(): array
|
||||
{
|
||||
return explode("\r",$this->msg_src);
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
/**
|
||||
|
@ -2,9 +2,10 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use App\Models\Casts\UTF8StringOrNull;
|
||||
use App\Casts\UTF8StringOrNull;
|
||||
|
||||
class Origin extends Model
|
||||
{
|
||||
|
@ -71,6 +71,9 @@ class Setup extends Model
|
||||
case 'msgs_pkt':
|
||||
return Arr::get($this->options,$key,self::MAX_MSGS_PKT);
|
||||
|
||||
case 'version':
|
||||
return File::exists('VERSION') ? chop(File::get('VERSION')) : 'dev';
|
||||
|
||||
default:
|
||||
return parent::__get($key);
|
||||
}
|
||||
@ -110,16 +113,6 @@ class Setup extends Model
|
||||
return hexstr($c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Application version
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function version(): string
|
||||
{
|
||||
return File::exists('VERSION') ? chop(File::get('VERSION')) : 'dev';
|
||||
}
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
@ -48,39 +47,35 @@ class System extends Model
|
||||
$uo = Auth::user();
|
||||
|
||||
return $query
|
||||
->when(
|
||||
$uo && ! $uo->isAdmin(),
|
||||
fn($query)=>
|
||||
$query
|
||||
->whereIn('id',$uo->systems->pluck('id'))
|
||||
->orWhere($this->getTable().'.active',TRUE),
|
||||
fn($query)=>$query->where($this->getTable().'.active',TRUE))
|
||||
->when($uo && ! $uo->isAdmin(),function($query) use ($uo) {
|
||||
return $query->whereIn('id',$uo->systems->pluck('id'))
|
||||
->orWhere($this->getTable().'.active',TRUE);
|
||||
},function($query) { $query->where($this->getTable().'.active',TRUE); })
|
||||
->orderBy('name');
|
||||
}
|
||||
|
||||
/* RELATIONS */
|
||||
|
||||
/**
|
||||
* All addresses assigned to a system, including addresses pending deletion
|
||||
* @return mixed
|
||||
*/
|
||||
public function addresses()
|
||||
{
|
||||
return $this->hasMany(Address::class)
|
||||
->FTN()
|
||||
->withTrashed();
|
||||
->withTrashed()
|
||||
->FTNorder();
|
||||
}
|
||||
|
||||
/**
|
||||
* System addresses that are active
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function akas()
|
||||
{
|
||||
return $this->hasMany(Address::class)
|
||||
->FTN()
|
||||
->ActiveFTN();
|
||||
->select('addresses.*')
|
||||
->join('zones',['zones.id'=>'addresses.zone_id'])
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->where('addresses.active',TRUE)
|
||||
->where('zones.active',TRUE)
|
||||
->where('domains.active',TRUE)
|
||||
->orderBy('domains.name')
|
||||
->with(['zone.domain'])
|
||||
->FTNorder()
|
||||
->orderBy('role','ASC');
|
||||
}
|
||||
|
||||
public function mailers()
|
||||
@ -115,10 +110,9 @@ class System extends Model
|
||||
public function sessions()
|
||||
{
|
||||
return $this->belongsToMany(Zone::class)
|
||||
->select(['zones.id','zones.zone_id','domain_id','zones.active'])
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->select(['id','zones.zone_id','domain_id','active'])
|
||||
->withPivot(['sespass','pktpass','ticpass','fixpass','zt_ipv4','zt_ipv6','default'])
|
||||
->orderBy('domains.name');
|
||||
->dontCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,14 +135,7 @@ class System extends Model
|
||||
*/
|
||||
public function zcs()
|
||||
{
|
||||
return $this->hasMany(Zone::class)
|
||||
->select(['zones.id','zone_id','domain_id','system_id','zones.active'])
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->orderBy('domains.name')
|
||||
->orderBy('zone_id')
|
||||
->with([
|
||||
'domain:id,name,active',
|
||||
]);
|
||||
return $this->hasMany(Zone::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,73 +181,8 @@ class System extends Model
|
||||
return $val ?: Setup::findOrFail(config('app.id'))->msgs_pkt;
|
||||
}
|
||||
|
||||
public function getLastSeenAttribute(): ?Carbon
|
||||
{
|
||||
return $this->logs_recent->first()?->created_at;
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
public function addresses_common(): Collection
|
||||
{
|
||||
$our = our_address()->pluck('zone.domain_id')->unique();
|
||||
|
||||
// Return our akas, filter with our_addresses()
|
||||
return $this->addresses->filter(fn($item)=>$our->contains($item->zone->domain_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ACTIVE addresses that are common with our addresses
|
||||
*
|
||||
* @return Collection
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function aka_common(): Collection
|
||||
{
|
||||
$our = our_address()->pluck('zone.domain_id')->unique();
|
||||
|
||||
// Return our akas, filter with our_addresses()
|
||||
return $this->akas->filter(fn($item)=>$our->contains($item->zone->domain_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ACTIVE addresses that we auth with
|
||||
*
|
||||
* @return Collection
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function aka_known(): Collection
|
||||
{
|
||||
return $this->aka_common()
|
||||
->filter(fn($item)=>$this->sessions->contains($item->zone_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ACTIVE addresses in the same networks as us, but dont auth here
|
||||
*
|
||||
* @return Collection
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function aka_unknown(): Collection
|
||||
{
|
||||
return $this->aka_common()
|
||||
->filter(fn($item)=>! $this->sessions->contains($item->zone_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the AKAs that are in networks not common with us
|
||||
*
|
||||
* @return Collection
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function aka_uncommon(): Collection
|
||||
{
|
||||
$our = our_address()->pluck('zone.domain_id')->unique();
|
||||
|
||||
// Return our akas, filter with our_addresses()
|
||||
return $this->akas->filter(fn($item)=>! $our->contains($item->zone->domain_id));
|
||||
}
|
||||
|
||||
public function echoareas()
|
||||
{
|
||||
return Echoarea::select('echoareas.*')
|
||||
@ -302,13 +224,6 @@ class System extends Model
|
||||
}
|
||||
}
|
||||
|
||||
public function inDomain(Domain $o): bool
|
||||
{
|
||||
return $this->addresses
|
||||
->filter(fn($item)=>$item->zone->domain_id === $o->id)
|
||||
->count() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the system's address in the same zone
|
||||
* This function can filter based on the address type needed.
|
||||
@ -329,18 +244,32 @@ class System extends Model
|
||||
: $akas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the addresses and return which ones are in my zones
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Collection $addresses
|
||||
* @param int $type
|
||||
* @return Collection
|
||||
*/
|
||||
public function inMyZones(Collection $addresses,int $type=(Address::NODE_HC|Address::NODE_NN|Address::NODE_POINT)): Collection
|
||||
{
|
||||
$myzones = $this->addresses->pluck('zone_id')->unique();
|
||||
|
||||
return $addresses->filter(function($item) use ($myzones,$type) {
|
||||
return ($item->role & $type) && ($myzones->search($item->zone_id) !== FALSE);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the packet that this system uses
|
||||
*
|
||||
* @param Address $ao
|
||||
* @param string|null $password
|
||||
* @return Packet
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function packet(Address $ao,string $password=NULL): Packet
|
||||
{
|
||||
if ($ao->system_id !== $this->id)
|
||||
throw new \Exception('Packet for [%s] is not for system [%d]',$ao->ftn,$this->id);
|
||||
// @todo Check that the address is one of the system's addresses
|
||||
|
||||
return
|
||||
(new (collect(Packet::PACKET_TYPES)
|
||||
@ -353,7 +282,21 @@ class System extends Model
|
||||
{
|
||||
return Job::where('queue',AddressPoll::QUEUE)
|
||||
->get()
|
||||
->where(fn($item)=>$this->akas->pluck('id')->contains($item->command->address->id))
|
||||
->where(function($item) {
|
||||
return $this->akas->pluck('id')->search($item->command->address->id) !== FALSE; })
|
||||
->last();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return other addresses that are no collected here, but are on the same network as us.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function uncommon(): Collection
|
||||
{
|
||||
$our = our_address();
|
||||
|
||||
return $this->akas->filter(fn($item)=>($item->parent() && (! $our->contains($item->parent()))));
|
||||
}
|
||||
}
|
@ -2,9 +2,10 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use App\Models\Casts\UTF8StringOrNull;
|
||||
use App\Casts\UTF8StringOrNull;
|
||||
|
||||
class Tagline extends Model
|
||||
{
|
||||
|
@ -2,9 +2,10 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use App\Models\Casts\UTF8StringOrNull;
|
||||
use App\Casts\UTF8StringOrNull;
|
||||
|
||||
class Tearline extends Model
|
||||
{
|
||||
|
@ -75,14 +75,19 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
|
||||
/* GENERAL METHODS */
|
||||
|
||||
public function addresses(): Collection
|
||||
public function addresses(Domain $o=NULL): Collection
|
||||
{
|
||||
return Address::select('addresses.*')
|
||||
->join('systems',['systems.id'=>'addresses.system_id'])
|
||||
->join('system_user',['system_user.system_id'=>'systems.id'])
|
||||
->when(! is_null($o),function($query) use ($o) {
|
||||
return $query
|
||||
->join('zones',['zones.id'=>'addresses.zone_id'])
|
||||
->where('zones.domain_id',$o->id);
|
||||
})
|
||||
->where('system_user.user_id',$this->id)
|
||||
->ActiveFTN()
|
||||
->FTN()
|
||||
->activeFTN()
|
||||
->with(['zone.domain'])
|
||||
->get();
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
use App\Classes\FTN\Message;
|
||||
use App\Models\{Echoarea,Echomail,Netmail,Setup};
|
||||
use App\Models\{Echoarea,Echomail,Setup};
|
||||
|
||||
abstract class Echomails extends Notification //implements ShouldQueue
|
||||
{
|
||||
@ -53,8 +53,8 @@ abstract class Echomails extends Notification //implements ShouldQueue
|
||||
|
||||
$o->echoarea_id = $eo->id;
|
||||
|
||||
$o->datetime = Carbon::now()->utc();
|
||||
$o->tzoffset = Carbon::now()->utcOffset();
|
||||
$o->datetime = Carbon::now();
|
||||
$o->tzoffset = $o->datetime->utcOffset();
|
||||
|
||||
$o->flags = (Message::FLAG_LOCAL);
|
||||
|
||||
@ -62,13 +62,4 @@ abstract class Echomails extends Notification //implements ShouldQueue
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
protected function sourceSummary(Echomail|Netmail $o,string $item=NULL): string
|
||||
{
|
||||
return sprintf("Your %s was received here on [%s] and it looks like you sent it on [%s].",
|
||||
$item ?: sprintf('%s with ID [%s] to [%s]',$o instanceof Netmail ? 'Netmail' : 'Echomail',$o->msgid,$o->to),
|
||||
Carbon::now()->utc()->toDateTimeString(),
|
||||
$o->date->utc()->toDateTimeString(),
|
||||
);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user