diff --git a/app/Classes/Protocol.php b/app/Classes/Protocol.php index 4e70779..0255fa5 100644 --- a/app/Classes/Protocol.php +++ b/app/Classes/Protocol.php @@ -87,12 +87,12 @@ abstract class Protocol protected const O_YES = 1<<5; // Session Status - protected const S_OK = 0; + public const S_OK = 0; protected const S_NODIAL = 1; protected const S_REDIAL = 2; protected const S_BUSY = 3; protected const S_FAILURE = 4; - protected const S_MASK = 7; + public const S_MASK = 7; protected const S_HOLDR = 8; protected const S_HOLDX = 16; protected const S_HOLDA = 32; @@ -324,10 +324,10 @@ abstract class Protocol * @param int $type * @param SocketClient $client * @param Address|null $o - * @return void + * @return int * @throws \Exception */ - public function session(int $type,SocketClient $client,Address $o=NULL): void + public function session(int $type,SocketClient $client,Address $o=NULL): int { if ($o->exists) Log::withContext(['ftn'=>$o->ftn]); @@ -370,7 +370,7 @@ abstract class Protocol if ($rc < 0) { Log::error(sprintf('%s:! Unable to start EMSI [%d]',self::LOGKEY,$rc)); - return; + return self::S_FAILURE; } case self::SESSION_EMSI: @@ -389,14 +389,13 @@ abstract class Protocol Log::debug(sprintf('%s:- Starting ZMODEM',self::LOGKEY)); $this->client->speed = self::TCP_SPEED; $this->originate = FALSE; - $this->protocol_session(); - return; + return $this->protocol_session(); default: Log::error(sprintf('%s:! Unsupported session type [%d]',self::LOGKEY,$type)); - return; + return self::S_FAILURE; } // @todo Unlock outbounds @@ -453,6 +452,8 @@ abstract class Protocol // @todo Optional after session includes mail event // if ($this->node->start_time && $this->setup->cfg('CFG_AFTERMAIL')) {} + + return $rc; } /* SE_* flags determine our session processing status, at any point in time */ diff --git a/app/Classes/Protocol/DNS.php b/app/Classes/Protocol/DNS.php index 38b86f1..c08484c 100644 --- a/app/Classes/Protocol/DNS.php +++ b/app/Classes/Protocol/DNS.php @@ -194,16 +194,16 @@ final class DNS extends BaseProtocol $ao = Address::findFTN(sprintf('%d:%d/%d.%d@%s',$z,$n,$f,$p,$d)); // Check we have the right record - if ((! $ao) || (! $ao->system->mailer_address) || (($rootdn !== self::TLD) && ((! $ao->zone->domain->dnsdomain) || ($ao->zone->domain->dnsdomain !== $d.'.'.$rootdn)))) { + if ((! $ao) || (! $ao->system->address) || (($rootdn !== self::TLD) && ((! $ao->zone->domain->dnsdomain) || ($ao->zone->domain->dnsdomain !== $d.'.'.$rootdn)))) { Log::alert(sprintf('%s:= No DNS record for [%d:%d/%d.%d@%s]',self::LOGKEY,$z,$n,$f,$p,$d)); return $this->nameerr(); } - Log::debug(sprintf('%s:= Returning [%s] for DNS query [%s]',self::LOGKEY,$ao->system->mailer_address,$ao->ftn)); + Log::debug(sprintf('%s:= Returning [%s] for DNS query [%s]',self::LOGKEY,$ao->system->address,$ao->ftn)); return $this->reply( self::DNS_NOERROR, - [serialize($this->domain_split($ao->system->mailer_address)) => self::DNS_TYPE_CNAME]); + [serialize($this->domain_split($ao->system->address)) => self::DNS_TYPE_CNAME]); // Other attributes return NOTIMPL default: diff --git a/app/Console/Commands/CommBinkpSend.php b/app/Console/Commands/CommBinkpSend.php index 2acd16e..de1b038 100644 --- a/app/Console/Commands/CommBinkpSend.php +++ b/app/Console/Commands/CommBinkpSend.php @@ -6,7 +6,7 @@ use Illuminate\Console\Command; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Support\Facades\Log; -use App\Models\Address; +use App\Models\{Address,Mailer}; use App\Jobs\AddressPoll as Job; class CommBinkpSend extends Command @@ -25,6 +25,8 @@ class CommBinkpSend extends Command */ protected $description = 'BINKP send'; + private const ID = 'BINKP'; + /** * Execute the console command. * @@ -34,10 +36,12 @@ class CommBinkpSend extends Command { Log::info('CBS:- Call BINKP send'); + $mo = Mailer::where('name',self::ID)->singleOrFail(); + $ao = Address::findFTN($this->argument('ftn')); if (! $ao) throw new ModelNotFoundException('Unknown node: '.$this->argument('ftn')); - Job::dispatchSync($ao); + Job::dispatchSync($ao,$mo); } } diff --git a/app/Console/Commands/CommEMSISend.php b/app/Console/Commands/CommEMSISend.php index 25c1c33..38f5522 100644 --- a/app/Console/Commands/CommEMSISend.php +++ b/app/Console/Commands/CommEMSISend.php @@ -6,7 +6,7 @@ use Illuminate\Console\Command; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Support\Facades\Log; -use App\Models\Address; +use App\Models\{Address,Mailer}; use App\Jobs\AddressPoll as Job; class CommEMSISend extends Command @@ -25,6 +25,8 @@ class CommEMSISend extends Command */ protected $description = 'EMSI send'; + private const ID = 'EMSI'; + /** * Execute the console command. * @@ -32,12 +34,14 @@ class CommEMSISend extends Command */ public function handle(): void { - Log::info('Call EMSI send'); + Log::info('CES:- Call EMSI send'); + + $mo = Mailer::where('name',self::ID)->singleOrFail(); $ao = Address::findFTN($this->argument('ftn')); if (! $ao) throw new ModelNotFoundException('Unknown node: '.$this->argument('ftn')); - Job::dispatchSync($ao); + Job::dispatchSync($ao,$mo); } } diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php index d46afb2..f01c981 100644 --- a/app/Http/Controllers/SystemController.php +++ b/app/Http/Controllers/SystemController.php @@ -276,11 +276,17 @@ class SystemController extends Controller $this->authorize('update',$o); if ($request->post()) { - foreach (['name','location','sysop','hold','phone','address','port','active','method','notes','mailer_type','mailer_address','mailer_port','zt_id','pkt_type'] as $key) + foreach (['name','location','sysop','hold','phone','address','port','active','method','notes','zt_id','pkt_type'] as $key) $o->{$key} = $request->post($key); $o->save(); + $mailers = collect($request->post('mailer_details')) + ->filter(function($item) { return $item['port']; }) + ->transform(function($item) { $item['active'] = Arr::get($item,'active',FALSE); return $item; }); + + $o->mailers()->sync($mailers); + return redirect()->action([self::class,'home']); } @@ -687,7 +693,7 @@ class SystemController extends Controller if (! $o->exist) { $o->sysop = Auth::user()->name; - foreach (['name','zt_id','location','mailer_type','mailer_address','mailer_port','phone','method','address','port'] as $item) + foreach (['name','zt_id','location','phone','method','address','port'] as $item) if ($request->{$item}) $o->{$item} = $request->{$item}; diff --git a/app/Http/Requests/SystemRegister.php b/app/Http/Requests/SystemRegister.php index c86c7ca..622f7bf 100644 --- a/app/Http/Requests/SystemRegister.php +++ b/app/Http/Requests/SystemRegister.php @@ -56,9 +56,8 @@ class SystemRegister extends FormRequest 'address' => 'nullable|regex:/^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$/i', 'port' => 'nullable|digits_between:2,5', 'method' => 'nullable|numeric', - 'mailer_type' => 'nullable|numeric', - 'mailer_address' => 'nullable|regex:/^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$/i', - 'mailer_port' => 'nullable|digits_between:2,5', + 'mailer_details.*' => 'nullable|array', + 'mailer_details.*.port' => 'nullable|digits_between:2,5', 'zt_id' => 'nullable|size:10|regex:/^([A-Fa-f0-9]){10}$/|unique:systems,zt_id,'.($this->so->exists ? $this->so->id : 0), 'pkt_type' => ['required',Rule::in(array_keys(Packet::PACKET_TYPES))], ] : [], diff --git a/app/Jobs/AddressPoll.php b/app/Jobs/AddressPoll.php index 4072ab5..5c519e5 100644 --- a/app/Jobs/AddressPoll.php +++ b/app/Jobs/AddressPoll.php @@ -9,10 +9,11 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; +use App\Classes\Protocol; use App\Classes\Protocol\{Binkp,EMSI}; use App\Classes\Sock\SocketClient; use App\Classes\Sock\SocketException; -use App\Models\{Address,Setup}; +use App\Models\{Address,Mailer,Setup}; class AddressPoll implements ShouldQueue { @@ -21,11 +22,12 @@ class AddressPoll implements ShouldQueue use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; private Address $ao; + private ?Mailer $mo; - public function __construct(Address $ao) + public function __construct(Address $ao,Mailer $mo=NULL) { - // Some checks $this->ao = $ao; + $this->mo = $mo; } /** @@ -33,34 +35,49 @@ class AddressPoll implements ShouldQueue */ public function handle() { - if ((! $this->ao->system->mailer_address) || (! $this->ao->system->mailer_port)) + if (! $this->ao->system->mailer_preferred->count() || ($this->mo && (! $this->ao->system->mailer_preferred->find($this->mo)))) throw new \Exception(sprintf('Unable to poll [%s] missing mailer details',$this->ao->ftn)); - try { - $client = SocketClient::create($this->ao->system->mailer_address,$this->ao->system->mailer_port); - } catch (SocketException $e) { - Log::error(sprintf('%s:! Unable to connect to [%s]: %s',self::LOGKEY,$this->ao->ftn,$e->getMessage())); - abort(500); - } + foreach ($this->ao->system->mailer_preferred as $o) { + // If we chose a protocol, skip to find the mailer details for it + if ($this->mo && ($o->id !== $this->mo->id)) + continue; - switch ($this->ao->system->mailer_type) { - case Setup::O_BINKP: - $o = new Binkp(Setup::findOrFail(config('app.id'))); - $o->session(Binkp::SESSION_BINKP,$client,$this->ao); + Log::info(sprintf('%s:- Starting a [%s] session to [%s:%d]',self::LOGKEY,$o->name,$this->ao->system->address,$o->pivot->port)); + try { + $client = SocketClient::create($this->ao->system->address,$o->pivot->port); + + } catch (SocketException $e) { + Log::error(sprintf('%s:! Unable to connect to [%s]: %s',self::LOGKEY,$this->ao->ftn,$e->getMessage())); + abort(500); + } + + switch ($o->name) { + case 'BINKP': + $s = new Binkp(Setup::findOrFail(config('app.id'))); + $session = Binkp::SESSION_BINKP; + + break; + + case 'EMSI': + $s = new EMSI(Setup::findOrFail(config('app.id'))); + $session = EMSI::SESSION_AUTO; + + break; + + default: + throw new \Exception(sprintf('Node [%s] has a mailer type that is unhandled',$this->ao->ftn)); + } + + if (($s->session($session,$client,$this->ao) & Protocol::S_MASK) === Protocol::S_OK) { + Log::info(sprintf('%s:= Connection ended successfully with [%s]',self::LOGKEY,$client->address_remote)); break; - case Setup::O_EMSI: - $o = new EMSI(Setup::findOrFail(config('app.id'))); - $o->session(EMSI::SESSION_AUTO,$client,$this->ao); - - break; - - default: - throw new \Exception(sprintf('Node [%s] has a mailer type that is unhandled',$this->ao->ftn)); + } else { + Log::alert(sprintf('%s:! Connection failed to [%s]',self::LOGKEY,$client->address_remote)); + } } - - Log::info(sprintf('%s:Connection ended: %s',self::LOGKEY,$client->address_remote)); } } \ No newline at end of file diff --git a/app/Models/Mailer.php b/app/Models/Mailer.php new file mode 100644 index 0000000..aa9242a --- /dev/null +++ b/app/Models/Mailer.php @@ -0,0 +1,31 @@ +1, + 'EMSI'=>2, + ]; + + /* RELATIONS */ + + public function system() + { + return $this->belongsTo(System::class); + } + + /* SCOPES */ + + public function scopePreferred($query) + { + return $query + ->orderBy('priority','ASC') + ->where('mailer_system.active',TRUE); + } +} \ No newline at end of file diff --git a/app/Models/System.php b/app/Models/System.php index 4f96ad3..af3c9d4 100644 --- a/app/Models/System.php +++ b/app/Models/System.php @@ -48,6 +48,18 @@ class System extends Model ->active(); } + public function mailers() + { + return $this->belongsToMany(Mailer::class) + ->withPivot(['last_poll','port','attempts','active']); + } + + public function mailer_preferred() + { + return $this->mailers() + ->preferred(); + } + public function logs() { return $this->hasMany(SystemLog::class); @@ -119,11 +131,11 @@ class System extends Model public function getAccessMailerAttribute(): string { - switch ($this->mailer_type) { - case Setup::O_BINKP: return sprintf('binkp://%s:%s',$this->mailer_address,$this->mailer_port); - case Setup::O_EMSI: return sprintf('emsi://%s:%s',$this->mailer_address,$this->mailer_port); + switch (($x=$this->mailer_preferred()->first())?->name) { + case 'BINKP': return sprintf('binkp://%s:%s',$this->address,$x->pivot->port); + case 'EMSI': return sprintf('emsi://%s:%s',$this->address,$x->pivot->port); default: - return $this->mailer_type ? sprintf('%s:%s',$this->address,$this->port) : 'No mailer available.'; + return $x?->name ? sprintf('%s:%s',$this->address,$x->pivot->port) : 'No mailer available.'; } } diff --git a/database/migrations/2023_07_06_204740_system_mailer.php b/database/migrations/2023_07_06_204740_system_mailer.php new file mode 100644 index 0000000..4b35963 --- /dev/null +++ b/database/migrations/2023_07_06_204740_system_mailer.php @@ -0,0 +1,89 @@ +id(); + $table->string('name')->unqiue(); + $table->integer('priority')->unsigned()->unique(); + }); + + Schema::create('mailer_system',function (Blueprint $table) { + $table->integer('port')->unsigned(); + $table->dateTime('last_poll')->nullable(); + $table->boolean('active'); + $table->integer('attempts')->unsigned()->nullable(); + + $table->bigInteger('system_id'); + $table->foreign('system_id')->references('id')->on('systems'); + $table->bigInteger('mailer_id'); + $table->foreign('mailer_id')->references('id')->on('mailers'); + + $table->unique(['system_id','mailer_id']); + $table->unique(['system_id','port']); + }); + + Schema::table('systems',function (Blueprint $table) { + $table->dropUnique(['mailer_type','mailer_address','mailer_port']); + }); + + $binkp = new \App\Models\Mailer; + $binkp->name = 'BINKP'; + $binkp->priority = 1; + $binkp->save(); + + $emsi = new \App\Models\Mailer; + $emsi->name = 'EMSI'; + $emsi->priority = 2; + $emsi->save(); + + foreach (\App\Models\System::whereNotNull('mailer_address')->cursor() as $o) { + if ($o->mailer_type & \App\Models\Setup::O_BINKP) + $type = $binkp; + elseif ($o->mailer_type & \App\Models\Setup::O_EMSI) + $type = $emsi; + else + $type = NULL; + + if (! $type) + abort(500,sprintf('No mailer type [%d] :',$o->mailer_type)); + + $o->mailers()->sync([$type->id=>[ + 'port'=>$o->mailer_port, + 'active'=>TRUE, + ]]); + + $o->address = $o->mailer_address; + $o->mailer_port = null; + $o->mailer_type = null; + $o->mailer_address = null; + $o->save(); + } + + Schema::table('systems',function (Blueprint $table) { + $table->dropColumn(['mailer_type','mailer_address','mailer_port']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + abort(500,'Cant go back'); + Schema::dropIfExists('mailer_system'); + Schema::dropIfExists('mailers'); + Schema::table('systems',function (Blueprint $table) { + $table->unique(['mailer_type','mailer_address','mailer_port']); + }); + } +}; diff --git a/resources/views/mail/system/test_email.blade.php b/resources/views/mail/system/test_email.blade.php index 1d4d364..7a8df0e 100644 --- a/resources/views/mail/system/test_email.blade.php +++ b/resources/views/mail/system/test_email.blade.php @@ -16,7 +16,7 @@ System Test Messsage | System Name | Address | FTNs | | ------------- |:-------------:| --------:| @foreach ($user->systems as $so) -| {{ $so->name }} | {{ $so->mailer_address }} | {{ $so->addresses->pluck('ftn3d')->join(', ') }} | +| {{ $so->name }} | {{ $so->address }} | {{ $so->addresses->pluck('ftn3d')->join(', ') }} | @endforeach @endcomponent @endif diff --git a/resources/views/system/widget/form-system.blade.php b/resources/views/system/widget/form-system.blade.php index 35a4e61..8f24c3c 100644 --- a/resources/views/system/widget/form-system.blade.php +++ b/resources/views/system/widget/form-system.blade.php @@ -73,7 +73,7 @@
- + @error('sysop') @@ -102,60 +102,68 @@
+
+
+ +
+ + + + @error('phone') + {{ $message }} + @enderror + +
+
+ + +
+ +
+ + + + @error('address') + {{ $message }} + @enderror + +
+
+
+
-

Mailer Details

+

Mailer Details

-
- -
- - -
-
- -
- -
- - - - - @error('mailer_address') - {{ $message }} - @enderror - @error('mailer_port') - {{ $message }} - @enderror - -
-
-
- -
-
- -
- - - - @error('phone') - {{ $message }} - @enderror - -
+
+ @foreach (\App\Models\Mailer::all() as $mo) + @php($x=$o->mailers->find($mo)) +
+
+ +
+ + +
+ id.'.active',$x?->pivot->active))checked @endif> +
+ + @error('mailer_details.'.$mo->id.'.port') + {{ $message }} + @enderror + +
+
+
+ @endforeach
- +
- -
- -
- - - - - @error('address') - {{ $message }} - @enderror - @error('port') - {{ $message }} - @enderror - +
+ +
+ +
+