Compare commits

...

4 Commits

Author SHA1 Message Date
fdede4fca9 Enable per system configuration of messages per packet
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 43s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 1m49s
Create Docker Image / Final Docker Image Manifest (push) Successful in 10s
2024-06-02 09:37:08 +10:00
b5f22bea86 Downgrade DNS query errors, since they are handled
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 38s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 1m48s
Create Docker Image / Final Docker Image Manifest (push) Successful in 10s
2024-06-01 12:55:44 +10:00
aa50580c13 Move HAproxy exceptions into their own class, and downgrade HAproxy errors since they are handled 2024-06-01 12:55:27 +10:00
81f59dcbb8 Remove EncodeUTF8 infavour of using attribute casting only. The implementation of EncodeUTF8 was not correct, essentially removing any previous casting causing issues when saving a record.
All checks were successful
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 42s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 1m50s
Create Docker Image / Final Docker Image Manifest (push) Successful in 11s
2024-06-01 10:46:02 +10:00
29 changed files with 169 additions and 214 deletions

View File

@ -5,20 +5,19 @@ namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
class CompressedString implements CastsAttributes class CompressedStringOrNull implements CastsAttributes
{ {
/** /**
* Cast the given value. * Cast the given value.
* *
* For postgresl bytea columns the value is a resource stream
*
* @param Model $model * @param Model $model
* @param string $key * @param string $key
* @param mixed $value * @param mixed $value
* @param array $attributes * @param array $attributes
* @return string * @return string|null
* @note postgres bytea columns the value is a resource stream
*/ */
public function get($model,string $key,mixed $value,array $attributes): string public function get(Model $model,string $key,mixed $value,array $attributes): ?string
{ {
// For stream resources, we to fseek in case we've already read it. // For stream resources, we to fseek in case we've already read it.
if (is_resource($value)) if (is_resource($value))
@ -28,13 +27,7 @@ class CompressedString implements CastsAttributes
? stream_get_contents($value) ? stream_get_contents($value)
: $value; : $value;
// If we get an error decompressing, it might not be zstd (or its already been done) return $value ? zstd_uncompress(base64_decode($value)) : NULL;
try {
return $value ? zstd_uncompress(base64_decode($value)) : '';
} catch (\ErrorException $e) {
return $value;
}
} }
/** /**
@ -44,10 +37,10 @@ class CompressedString implements CastsAttributes
* @param string $key * @param string $key
* @param mixed $value * @param mixed $value
* @param array $attributes * @param array $attributes
* @return string * @return string|null
*/ */
public function set($model,string $key,$value,array $attributes): string public function set(Model $model,string $key,$value,array $attributes): ?string
{ {
return $value ? base64_encode(zstd_compress($value)) : ''; return $value ? base64_encode(zstd_compress($value)) : NULL;
} }
} }

View File

@ -0,0 +1,29 @@
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
class UTF8StringOrNull implements CastsAttributes
{
/**
* Cast the given value.
*
* @param array<string, mixed> $attributes
*/
public function get(Model $model,string $key,mixed $value,array $attributes): ?string
{
return $value ? utf8_decode($value) : NULL;
}
/**
* Prepare the given value for storage.
*
* @param array<string, mixed> $attributes
*/
public function set(Model $model,string $key,mixed $value,array $attributes): ?string
{
return $value ? utf8_encode($value) : NULL;
}
}

View File

@ -7,8 +7,8 @@ use Illuminate\Support\Facades\Log;
use App\Classes\File\{Receive,Send}; use App\Classes\File\{Receive,Send};
use App\Classes\Protocol\EMSI; use App\Classes\Protocol\EMSI;
use App\Classes\Sock\Exception\SocketException;
use App\Classes\Sock\SocketClient; use App\Classes\Sock\SocketClient;
use App\Classes\Sock\SocketException;
use App\Models\{Address,Mailer,Setup,System,SystemLog}; use App\Models\{Address,Mailer,Setup,System,SystemLog};
// @todo after receiving a mail packet/file, dont acknowledge it until we can validate that we can read it properly. // @todo after receiving a mail packet/file, dont acknowledge it until we can validate that we can read it properly.

View File

@ -11,8 +11,8 @@ use League\Flysystem\UnreadableFileEncountered;
use App\Classes\Crypt; use App\Classes\Crypt;
use App\Classes\Node; use App\Classes\Node;
use App\Classes\Protocol as BaseProtocol; use App\Classes\Protocol as BaseProtocol;
use App\Classes\Sock\Exception\SocketException;
use App\Classes\Sock\SocketClient; use App\Classes\Sock\SocketClient;
use App\Classes\Sock\SocketException;
use App\Exceptions\{FileGrewException,InvalidFTNException}; use App\Exceptions\{FileGrewException,InvalidFTNException};
use App\Models\{Address,Mailer}; use App\Models\{Address,Mailer};

View File

@ -110,7 +110,7 @@ final class DNS extends BaseProtocol
$this->query = new BaseProtocol\DNS\Query($this->client->read(0,512)); $this->query = new BaseProtocol\DNS\Query($this->client->read(0,512));
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error(sprintf('%s:! Ignoring bad DNS query (%s)',self::LOGKEY,$e->getMessage())); Log::notice(sprintf('%s:! Ignoring bad DNS query (%s)',self::LOGKEY,$e->getMessage()));
return FALSE; return FALSE;
} }
@ -119,7 +119,7 @@ final class DNS extends BaseProtocol
// If the wrong class // If the wrong class
if ($this->query->class !== self::DNS_QUERY_IN) { if ($this->query->class !== self::DNS_QUERY_IN) {
Log::error(sprintf('%s:! We only service Internet queries [%d]',self::LOGKEY,$this->query->class)); Log::notice(sprintf('%s:! We only service Internet queries [%d]',self::LOGKEY,$this->query->class));
return $this->reply(self::DNS_NOTIMPLEMENTED,[],$this->soa()); return $this->reply(self::DNS_NOTIMPLEMENTED,[],$this->soa());
} }
@ -272,7 +272,7 @@ final class DNS extends BaseProtocol
// Other attributes return NOTIMPL // Other attributes return NOTIMPL
default: default:
Log::error(sprintf('%s:! We dont support DNS query types [%d]',self::LOGKEY,$this->query->type)); Log::notice(sprintf('%s:! We dont support DNS query types [%d]',self::LOGKEY,$this->query->type));
return $this->reply(self::DNS_NOTIMPLEMENTED,[],$this->soa()); return $this->reply(self::DNS_NOTIMPLEMENTED,[],$this->soa());
} }
@ -309,14 +309,14 @@ final class DNS extends BaseProtocol
private function nameerr(): int private function nameerr(): int
{ {
Log::error(sprintf('%s:! DNS query for a resource we dont manage [%s]',self::LOGKEY,$this->query->domain)); Log::notice(sprintf('%s:! DNS query for a resource we dont manage [%s]',self::LOGKEY,$this->query->domain));
return $this->reply(self::DNS_NAMEERR,[],$this->soa()); return $this->reply(self::DNS_NAMEERR,[],$this->soa());
} }
private function nodata(): int private function nodata(): int
{ {
Log::error(sprintf('%s:! DNS query for a resource we dont manage [%s] in our zone(s)',self::LOGKEY,$this->query->domain)); Log::notice(sprintf('%s:! DNS query for a resource we dont manage [%s] in our zone(s)',self::LOGKEY,$this->query->domain));
return $this->reply(self::DNS_NOERROR,[],$this->soa()); return $this->reply(self::DNS_NOERROR,[],$this->soa());
} }

View File

@ -6,12 +6,12 @@ use Carbon\Carbon;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Classes\Protocol as BaseProtocol; use App\Classes\Protocol as BaseProtocol;
use App\Classes\Sock\Exception\SocketException;
use App\Classes\Sock\SocketClient; use App\Classes\Sock\SocketClient;
use App\Classes\Sock\SocketException;
use App\Exceptions\InvalidFTNException; use App\Exceptions\InvalidFTNException;
use App\Models\{Address,Mailer,Setup};
use App\Interfaces\CRC as CRCInterface; use App\Interfaces\CRC as CRCInterface;
use App\Interfaces\Zmodem as ZmodemInterface; use App\Interfaces\Zmodem as ZmodemInterface;
use App\Models\{Address,Mailer,Setup};
use App\Traits\CRC as CRCTrait; use App\Traits\CRC as CRCTrait;
// http://ftsc.org/docs/fsc-0056.001 // http://ftsc.org/docs/fsc-0056.001

View File

@ -5,9 +5,9 @@ namespace App\Classes\Protocol;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Classes\{Node,Protocol}; use App\Classes\{Node,Protocol};
use App\Classes\Protocol\Zmodem as ZmodemClass;
use App\Classes\File\{Receive,Send}; use App\Classes\File\{Receive,Send};
use App\Classes\Sock\{SocketClient,SocketException}; use App\Classes\Sock\Exception\SocketException;
use App\Classes\Sock\SocketClient;
use App\Interfaces\CRC as CRCInterface; use App\Interfaces\CRC as CRCInterface;
use App\Interfaces\Zmodem as ZmodemInterface; use App\Interfaces\Zmodem as ZmodemInterface;
use App\Models\{Address,Mailer}; use App\Models\{Address,Mailer};

View File

@ -0,0 +1,6 @@
<?php
namespace App\Classes\Sock\Exception;
final class HAproxyException extends \Exception {
}

View File

@ -1,6 +1,6 @@
<?php <?php
namespace App\Classes\Sock; namespace App\Classes\Sock\Exception;
// @todo Can we change this to use socket_strerr() && socket_last_error() // @todo Can we change this to use socket_strerr() && socket_last_error()
final class SocketException extends \Exception { final class SocketException extends \Exception {

View File

@ -6,6 +6,8 @@ use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use App\Classes\Sock\Exception\{HAproxyException,SocketException};
/** /**
* Class SocketClient * Class SocketClient
* *
@ -57,34 +59,26 @@ final class SocketClient {
if (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)); Log::debug(sprintf('%s:+ HAPROXY connection host [%s] on port [%d] (%s)',self::LOGKEY,$this->address_remote,$this->port_remote,$this->type));
if ($this->read(5,12) !== "\x0d\x0a\x0d\x0a\x00\x0d\x0aQUIT\x0a") { if ($this->read(5,12) !== "\x0d\x0a\x0d\x0a\x00\x0d\x0aQUIT\x0a")
Log::error(sprintf('%s:! Failed to initialise HAPROXY connection',self::LOGKEY)); throw new HAproxyException('Failed to initialise HAPROXY connection');
throw new SocketException(SocketException::CANT_CONNECT,'Failed to initialise HAPROXY connection');
}
// Version/Command // Version/Command
$vc = $this->read_ch(5); $vc = $this->read_ch(5);
if (($x=($vc>>4)&0x7) !== 2) { if (($x=($vc>>4)&0x7) !== 2)
Log::error(sprintf('%s:! HAPROXY version [%d] is not handled',self::LOGKEY,$x)); throw new HAproxyException(sprintf('Unknown HAPROXY version [%d]',$x));
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY version');
}
switch ($x=($vc&0x7)) { switch ($x=($vc&0x7)) {
// HAPROXY internal // HAPROXY internal
case 0: case 0:
Log::debug(sprintf('%s:! HAPROXY internal health-check',self::LOGKEY)); throw new HAproxyException('HAPROXY internal health-check');
throw new SocketException(SocketException::CANT_CONNECT,'Healthcheck');
// PROXY connection // PROXY connection
case 1: case 1:
break; break;
default: default:
Log::error(sprintf('%s:! HAPROXY command [%d] is not handled',self::LOGKEY,$x)); throw new HAproxyException(sprintf('HAPROXY command [%d] is not handled',$x));
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY command');
} }
// Protocol/Address Family // Protocol/Address Family
@ -101,8 +95,7 @@ final class SocketClient {
break; break;
default: default:
Log::error(sprintf('%s:! HAPROXY protocol [%d] is not handled',self::LOGKEY,$x)); throw new HAproxyException(sprintf('HAPROXY protocol [%d] is not handled',$x));
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY protocol');
} }
switch ($x=($pa&0x7)) { switch ($x=($pa&0x7)) {
@ -110,8 +103,7 @@ final class SocketClient {
break; break;
default: default:
Log::error(sprintf('%s:! HAPROXY address family [%d] is not handled',self::LOGKEY,$x)); throw new HAproxyException(sprintf('HAPROXY address family [%d] is not handled',$x));
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY address family');
} }
$len = Arr::get(unpack('n',$this->read(5,2)),1); $len = Arr::get(unpack('n',$this->read(5,2)),1);
@ -126,8 +118,7 @@ final class SocketClient {
$dst = inet_ntop($this->read(5,16)); $dst = inet_ntop($this->read(5,16));
} else { } else {
Log::error(sprintf('%s:! HAPROXY address len [%d:%d] is not handled',self::LOGKEY,$p,$len)); throw new HAproxyException(sprintf('HAPROXY address len [%d:%d] is not handled',$p,$len));
throw new SocketException(SocketException::CANT_CONNECT,'Unknown HAPROXY address length');
} }
$src_port = unpack('n',$this->read(5,2)); $src_port = unpack('n',$this->read(5,2));

View File

@ -4,6 +4,8 @@ namespace App\Classes\Sock;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Classes\Sock\Exception\{HAproxyException,SocketException};
final class SocketServer { final class SocketServer {
private const LOGKEY = 'SS-'; private const LOGKEY = 'SS-';
@ -128,8 +130,13 @@ final class SocketServer {
try { try {
$r = new SocketClient($accept); $r = new SocketClient($accept);
} catch (HAproxyException $e) {
Log::notice(sprintf('%s:! HAPROXY Exception [%s]',self::LOGKEY,$e->getMessage()));
socket_close($accept);
continue;
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error(sprintf('%s:! Creating Socket client failed? [%s]',self::LOGKEY,$e->getMessage())); Log::notice(sprintf('%s:! Creating Socket client failed? [%s]',self::LOGKEY,$e->getMessage()));
socket_close($accept); socket_close($accept);
continue; continue;
} }

View File

@ -6,7 +6,7 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Classes\Protocol\Binkp; use App\Classes\Protocol\Binkp;
use App\Classes\Sock\SocketException; use App\Classes\Sock\Exception\SocketException;
use App\Classes\Sock\SocketServer; use App\Classes\Sock\SocketServer;
use App\Models\Setup; use App\Models\Setup;

View File

@ -6,7 +6,7 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Classes\Protocol\EMSI; use App\Classes\Protocol\EMSI;
use App\Classes\Sock\SocketException; use App\Classes\Sock\Exception\SocketException;
use App\Classes\Sock\SocketServer; use App\Classes\Sock\SocketServer;
use App\Models\Setup; use App\Models\Setup;

View File

@ -6,7 +6,8 @@ use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Classes\Protocol\{Binkp,DNS,EMSI}; use App\Classes\Protocol\{Binkp,DNS,EMSI};
use App\Classes\Sock\{SocketException,SocketServer}; use App\Classes\Sock\Exception\SocketException;
use App\Classes\Sock\SocketServer;
use App\Models\Setup; use App\Models\Setup;
class ServerStart extends Command class ServerStart extends Command

View File

@ -31,7 +31,7 @@ class SystemController extends Controller
public function add_edit(SystemRegisterRequest $request, System $o) public function add_edit(SystemRegisterRequest $request, System $o)
{ {
if ($request->validated()) { if ($request->validated()) {
foreach (['name','location','phone','address','port','active','method','pkt_type'] as $key) foreach (['name','location','phone','address','port','active','method','pkt_msgs','pkt_type'] as $key)
$o->{$key} = $request->validated($key); $o->{$key} = $request->validated($key);
// Sometimes items // Sometimes items

View File

@ -73,6 +73,7 @@ class SystemRegisterRequest extends FormRequest
'hold' => 'sometimes|boolean', 'hold' => 'sometimes|boolean',
'pollmode' => 'required|integer|min:0|max:2', 'pollmode' => 'required|integer|min:0|max:2',
'heartbeat' => 'nullable|integer|min:0|max:48', 'heartbeat' => 'nullable|integer|min:0|max:48',
'pkt_msgs' => 'nullable|integer|min:5',
] : [])); ] : []));
} }
} }

View File

@ -15,8 +15,8 @@ use Illuminate\Support\Facades\Notification;
use App\Classes\Protocol; use App\Classes\Protocol;
use App\Classes\Protocol\{Binkp,EMSI}; use App\Classes\Protocol\{Binkp,EMSI};
use App\Classes\Sock\Exception\SocketException;
use App\Classes\Sock\SocketClient; use App\Classes\Sock\SocketClient;
use App\Classes\Sock\SocketException;
use App\Models\{Address,Mailer,Setup}; use App\Models\{Address,Mailer,Setup};
use App\Notifications\Netmails\PollingFailed; use App\Notifications\Netmails\PollingFailed;
use App\Traits\ObjectIssetFix; use App\Traits\ObjectIssetFix;

View File

@ -14,21 +14,17 @@ use Illuminate\Support\Facades\Notification;
use App\Classes\FTN\Message; use App\Classes\FTN\Message;
use App\Models\{Echomail,Netmail,User}; use App\Models\{Echomail,Netmail,User};
use App\Notifications\Netmails\{EchoareaNotExist,EchoareaNotSubscribed,EchoareaNoWrite,NetmailForward,NetmailHubNoUser}; use App\Notifications\Netmails\{EchoareaNotExist,EchoareaNotSubscribed,EchoareaNoWrite,NetmailForward,NetmailHubNoUser};
use App\Traits\{EncodeUTF8,ParseAddresses}; use App\Traits\ParseAddresses;
class MessageProcess implements ShouldQueue class MessageProcess implements ShouldQueue
{ {
private const LOGKEY = 'JMP'; private const LOGKEY = 'JMP';
use Dispatchable,InteractsWithQueue,Queueable,SerializesModels,ParseAddresses,EncodeUTF8; use Dispatchable,InteractsWithQueue,Queueable,SerializesModels,ParseAddresses;
private Echomail|Netmail|string $mo; private Echomail|Netmail|string $mo;
private bool $skipbot; private bool $skipbot;
private const cast_utf8 = [
'mo',
];
/** /**
* Process a message from a packet * Process a message from a packet
* *
@ -54,16 +50,6 @@ class MessageProcess implements ShouldQueue
} }
} }
public function __serialize()
{
return $this->encode();
}
public function __unserialize(array $values)
{
$this->decode($values);
}
/** /**
* At this point, we know that the packet is from a system we know about, and the packet is to us: * At this point, we know that the packet is from a system we know about, and the packet is to us:
* + From a system that is configured with us, and the password has been validated * + From a system that is configured with us, and the password has been validated
@ -144,8 +130,8 @@ class MessageProcess implements ShouldQueue
// Check if the netmail is to a user, with netmail forwarding enabled // Check if the netmail is to a user, with netmail forwarding enabled
$uo = User::active() $uo = User::active()
->where(function($query) { ->where(function($query) {
return $query->whereRaw(sprintf("LOWER(name)='%s'",strtolower($this->mo->to))) return $query->whereRaw(sprintf("LOWER(name)='%s'",strtolower(utf8_encode($this->mo->to))))
->orWhereRaw(sprintf("LOWER(alias)='%s'",strtolower($this->mo->to))); ->orWhereRaw(sprintf("LOWER(alias)='%s'",strtolower(utf8_encode($this->mo->to))));
}) })
->whereNotNull('system_id') ->whereNotNull('system_id')
->single(); ->single();

View File

@ -129,6 +129,8 @@ class PacketProcess implements ShouldQueue
if ($queue || (! $this->interactive)) if ($queue || (! $this->interactive))
Log::info(sprintf('%s:! Message [%s] will be sent to the queue to process',self::LOGKEY,$msg->msgid)); Log::info(sprintf('%s:! Message [%s] will be sent to the queue to process',self::LOGKEY,$msg->msgid));
//dump(['msg'=>$msg]);
Log::debug('***',['a']);
try { try {
// Dispatch job. // Dispatch job.
if ($queue || (! $this->interactive)) if ($queue || (! $this->interactive))
@ -136,10 +138,12 @@ class PacketProcess implements ShouldQueue
else else
MessageProcess::dispatchSync($msg->withoutRelations(),$this->nobot); MessageProcess::dispatchSync($msg->withoutRelations(),$this->nobot);
Log::debug('***',['b']);
$count++; $count++;
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error(sprintf('%s:! Got error dispatching message [%s] (%d:%s-%s).',self::LOGKEY,$msg->msgid,$e->getLine(),$e->getFile(),$e->getMessage())); Log::debug('***',['c']);
Log::error(sprintf('%s:! Got error [%s] dispatching message [%s] (%d:%s-%s).',self::LOGKEY,get_class($e),$msg->msgid,$e->getLine(),$e->getFile(),$e->getMessage()));
} }
} }

View File

@ -1045,15 +1045,13 @@ class Address extends Model
public function getEchomail(): ?Packet public function getEchomail(): ?Packet
{ {
if (($num=$this->echomailWaiting())->count()) { if (($num=$this->echomailWaiting())->count()) {
$s = Setup::findOrFail(config('app.id'));
Log::info(sprintf('%s:= Got [%d] echomails for [%s] for sending',self::LOGKEY,$num->count(),$this->ftn)); Log::info(sprintf('%s:= Got [%d] echomails for [%s] for sending',self::LOGKEY,$num->count(),$this->ftn));
// Limit to max messages // Limit to max messages
if ($num->count() > $s->msgs_pkt) if ($num->count() > $this->system->pkt_msgs)
Log::notice(sprintf('%s:= Only sending [%d] echomails for [%s]',self::LOGKEY,$s->msgs_pkt,$this->ftn)); Log::notice(sprintf('%s:= Only sending [%d] echomails for [%s]',self::LOGKEY,$this->system->pkt_msgs,$this->ftn));
return $this->system->packet($this)->mail($num->take($s->msgs_pkt)); return $this->system->packet($this)->mail($num->take($this->system->pkt_msgs));
} }
return NULL; return NULL;
@ -1103,15 +1101,13 @@ class Address extends Model
} }
if (($num=$this->netmailWaiting())->count()) { if (($num=$this->netmailWaiting())->count()) {
$s = Setup::findOrFail(config('app.id'));
Log::debug(sprintf('%s:= Got [%d] netmails for [%s] for sending',self::LOGKEY,$num->count(),$this->ftn)); Log::debug(sprintf('%s:= Got [%d] netmails for [%s] for sending',self::LOGKEY,$num->count(),$this->ftn));
// Limit to max messages // Limit to max messages
if ($num->count() > $s->msgs_pkt) if ($num->count() > $this->system->pkt_msgs)
Log::alert(sprintf('%s:= Only sending [%d] netmails for [%s]',self::LOGKEY,$num->count(),$this->ftn)); 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($s->msgs_pkt)); return $this->system->packet($this)->mail($num->take($this->system->pkt_msgs));
} }
return NULL; return NULL;
@ -1145,7 +1141,7 @@ class Address extends Model
$c = 0; $c = 0;
foreach ($msgs as $oo) { foreach ($msgs as $oo) {
// Only bundle up to max messages // Only bundle up to max messages
if (++$c > $s->msgs_pkt) if (++$c > $s->pkt_msgs)
break; break;
$o->addMail($oo->packet($this,$passwd)); $o->addMail($oo->packet($this,$passwd));

View File

@ -11,7 +11,7 @@ use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use App\Casts\CompressedString; use App\Casts\CompressedStringOrNull;
use App\Traits\{QueryCacheableConfig,ScopeActive}; use App\Traits\{QueryCacheableConfig,ScopeActive};
class Domain extends Model class Domain extends Model
@ -22,7 +22,7 @@ class Domain extends Model
private const STATS_MONTHS = 6; private const STATS_MONTHS = 6;
protected $casts = [ protected $casts = [
'homepage' => CompressedString::class, 'homepage' => CompressedStringOrNull::class,
]; ];
/* SCOPES */ /* SCOPES */

View File

@ -9,7 +9,7 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Casts\{CollectionOrNull,CompressedString}; use App\Casts\{CollectionOrNull,CompressedStringOrNull,UTF8StringOrNull};
use App\Classes\FTN\Message; use App\Classes\FTN\Message;
use App\Interfaces\Packet; use App\Interfaces\Packet;
use App\Traits\{MessageAttributes,MsgID,ParseAddresses,QueryCacheableConfig}; use App\Traits\{MessageAttributes,MsgID,ParseAddresses,QueryCacheableConfig};
@ -32,10 +32,16 @@ final class Echomail extends Model implements Packet
public Address $tftn; public Address $tftn;
protected $casts = [ protected $casts = [
'to' => UTF8StringOrNull::class,
'from' => UTF8StringOrNull::class,
'subject' => UTF8StringOrNull::class,
'origin' => UTF8StringOrNull::class,
'tearline' => UTF8StringOrNull::class,
'tagline' => UTF8StringOrNull::class,
'datetime' => 'datetime:Y-m-d H:i:s', 'datetime' => 'datetime:Y-m-d H:i:s',
'kludges' => CollectionOrNull::class, 'kludges' => CollectionOrNull::class,
'msg' => CompressedString::class, 'msg' => CompressedStringOrNull::class,
'msg_src' => CompressedString::class, 'msg_src' => CompressedStringOrNull::class,
'rogue_seenby' => CollectionOrNull::class, 'rogue_seenby' => CollectionOrNull::class,
'rogue_path' => CollectionOrNull::class, // @deprecated? 'rogue_path' => CollectionOrNull::class, // @deprecated?
]; ];

View File

@ -11,12 +11,11 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use App\Casts\{CollectionOrNull,CompressedString}; use App\Casts\{CollectionOrNull,CompressedStringOrNull};
use App\Traits\EncodeUTF8;
class File extends Model class File extends Model
{ {
use SoftDeletes,EncodeUTF8; use SoftDeletes;
private const LOGKEY = 'MF-'; private const LOGKEY = 'MF-';
private bool $no_export = FALSE; private bool $no_export = FALSE;
@ -28,18 +27,13 @@ class File extends Model
protected $casts = [ protected $casts = [
'kludges' => CollectionOrNull::class, 'kludges' => CollectionOrNull::class,
'datetime' => 'datetime:Y-m-d H:i:s', 'datetime' => 'datetime:Y-m-d H:i:s',
'desc' => CompressedString::class, 'desc' => CompressedStringOrNull::class,
'ldesc' => CompressedString::class, 'ldesc' => CompressedStringOrNull::class,
'rogue_seenby' => CollectionOrNull::class, 'rogue_seenby' => CollectionOrNull::class,
'rogue_path' => CollectionOrNull::class, 'rogue_path' => CollectionOrNull::class,
'size' => 'int', 'size' => 'int',
]; ];
private const cast_utf8 = [
'desc',
'ldesc',
];
public static function boot() public static function boot()
{ {
parent::boot(); parent::boot();

View File

@ -10,7 +10,7 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use App\Casts\{CollectionOrNull,CompressedString}; use App\Casts\{CollectionOrNull,CompressedStringOrNull,UTF8StringOrNull};
use App\Interfaces\Packet; use App\Interfaces\Packet;
use App\Pivots\ViaPivot; use App\Pivots\ViaPivot;
use App\Traits\{MessageAttributes,MsgID}; use App\Traits\{MessageAttributes,MsgID};
@ -32,10 +32,16 @@ final class Netmail extends Model implements Packet
]; ];
protected $casts = [ protected $casts = [
'to' => UTF8StringOrNull::class,
'from' => UTF8StringOrNull::class,
'subject' => UTF8StringOrNull::class,
'origin' => UTF8StringOrNull::class,
'tearline' => UTF8StringOrNull::class,
'tagline' => UTF8StringOrNull::class,
'datetime' => 'datetime:Y-m-d H:i:s', 'datetime' => 'datetime:Y-m-d H:i:s',
'kludges' => CollectionOrNull::class, 'kludges' => CollectionOrNull::class,
'msg' => CompressedString::class, 'msg' => CompressedStringOrNull::class,
'msg_src' => CompressedString::class, 'msg_src' => CompressedStringOrNull::class,
'sent_at' => 'datetime:Y-m-d H:i:s', 'sent_at' => 'datetime:Y-m-d H:i:s',
]; ];

View File

@ -170,6 +170,11 @@ class System extends Model
} }
} }
public function getPktMsgsAttribute(?int $val): int
{
return $val ?: Setup::findOrFail(config('app.id'))->msgs_pkt;
}
/* METHODS */ /* METHODS */
public function echoareas() public function echoareas()

View File

@ -1,99 +0,0 @@
<?php
/**
* Encode our data so that it can be serialised
*/
namespace App\Traits;
use Illuminate\Support\Arr;
trait EncodeUTF8
{
private array $_encoded = []; // Remember what we've decoded - when calling getAttribute()
private function decode(array $values): void
{
$properties = (new \ReflectionClass($this))->getProperties();
$class = get_class($this);
foreach ($properties as $property) {
if ($property->isStatic())
continue;
$name = $property->getName();
$decode = in_array($name,self::cast_utf8);
if ($property->isPrivate())
$name = "\0{$class}\0{$name}";
elseif ($property->isProtected())
$name = "\0*\0{$name}";
if (! array_key_exists($name,$values))
continue;
$property->setAccessible(true);
try {
$property->setValue(
$this,$decode ? utf8_decode($values[$name]) : $values[$name]
);
} catch (\Exception $e) {
dd(['e'=>$e->getMessage(),'name'=>$name,'values'=>$values[$name],'decode'=>$decode]);
}
}
}
private function encode(): array
{
$values = [];
$properties = (new \ReflectionClass($this))->getProperties();
$class = get_class($this);
foreach ($properties as $property) {
// Dont serialize the validation error
if (($property->name === 'errors') || $property->isStatic())
continue;
$property->setAccessible(true);
if (! $property->isInitialized($this))
continue;
$name = $property->getName();
$encode = in_array($name,self::cast_utf8);
if ($property->isPrivate())
$name = "\0{$class}\0{$name}";
elseif ($property->isProtected())
$name = "\0*\0{$name}";
$property->setAccessible(true);
$value = $property->getValue($this);
$values[$name] = $encode ? utf8_encode($value) : $value;
}
return $values;
}
public function getAttribute($key)
{
if (in_array($key,self::cast_utf8) && Arr::get($this->attributes,$key) && (! Arr::get($this->_encoded,$key))) {
// We need to get it from the parent first, taking into account any casting
$this->attributes[$key] = utf8_decode(parent::getAttribute($key));
$this->_encoded[$key] = TRUE;
return $this->attributes[$key];
}
return Arr::get($this->_encoded,$key) ? $this->attributes[$key] : parent::getAttribute($key);
}
public function setAttribute($key,$value)
{
return parent::setAttribute($key,in_array($key,self::cast_utf8) ? utf8_encode($value) : $value);
}
}

View File

@ -15,24 +15,11 @@ use App\Models\{Address,Echomail};
trait MessageAttributes trait MessageAttributes
{ {
use EncodeUTF8;
// Items we need to set when creating() // Items we need to set when creating()
public Collection $set; public Collection $set;
// Validation Errors // Validation Errors
public ?MessageBag $errors = NULL; public ?MessageBag $errors = NULL;
private const cast_utf8 = [
'to',
'from',
'subject',
'msg',
'msg_src',
'origin',
'tearline',
'tagline',
];
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('systems', function (Blueprint $table) {
$table->integer('pkt_msgs')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('systems', function (Blueprint $table) {
$table->dropColumn(['pkt_msgs']);
});
}
};

View File

@ -1,6 +1,6 @@
@php @php
use App\Classes\FTN\Packet; use App\Classes\FTN\Packet;
use App\Models\{Mailer,User}; use App\Models\{Mailer,Setup,User};
@endphp @endphp
<!-- $o=System::class --> <!-- $o=System::class -->
@ -189,6 +189,20 @@ use App\Models\{Mailer,User};
</span> </span>
</div> </div>
</div> </div>
<!-- Packet Msgs -->
<div class="col-2">
<label for="pkt_msgs" class="form-label w-100">Packet Msgs</label>
<div class="input-group has-validation">
<span class="input-group-text"><i class="bi bi-hash"></i></span>
<input type="text" class="form-control text-end @error('pkt_msgs') is-invalid @enderror" id="pkt_msgs" placeholder="{{ Setup::MAX_MSGS_PKT }}" name="pkt_msgs" value="{{ old('pkt_msgs',$o->pkt_msgs) }}" @cannot($action,$o)readonly @endcannot>
<span class="invalid-feedback" role="alert">
@error('pkt_msgs')
{{ $message }}
@enderror
</span>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>