From edee0643eca44965275a3c0487d2fe8652060373 Mon Sep 17 00:00:00 2001 From: Deon George Date: Thu, 9 May 2024 21:31:50 +1000 Subject: [PATCH] Reorder functions, no functional changes --- app/Http/Controllers/UserController.php | 2 +- app/Models/Address.php | 551 ++++++++++----------- app/Notifications/Netmails/AddressLink.php | 2 +- 3 files changed, 276 insertions(+), 279 deletions(-) diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index b9bd4a8..c8813d8 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -50,7 +50,7 @@ class UserController extends Controller ]); $ao = Address::findOrFail($request->address_id); - if ($ao->check_activation(Auth::user(),$request->code)) { + if ($ao->activation_check(Auth::user(),$request->code)) { $ao->validated = TRUE; $ao->save(); diff --git a/app/Models/Address.php b/app/Models/Address.php index 4c8f24e..b47747a 100644 --- a/app/Models/Address.php +++ b/app/Models/Address.php @@ -77,6 +77,244 @@ class Address extends Model }); } + /** + * Create an FTN address associated with a system + * + * @param string $address + * @param System $so + * @return Address|null + * @throws \Exception + */ + public static function createFTN(string $address,System $so): ?self + { + $ftn = self::parseFTN($address); + + if (! $ftn['d']) { + // See if we have a default domain for this domain + if ($ftn['z']) { + $zo = Zone::where('zone_id',$ftn['z'])->where('default',TRUE)->single(); + + if ($zo) + $ftn['d'] = $zo->domain->name; + + else { + Log::alert(sprintf('%s:! Refusing to create address [%s] no domain available',self::LOGKEY,$address)); + + return NULL; + } + } + } + + Log::debug(sprintf('%s:- Creating AKA [%s] for [%s]',self::LOGKEY,$address,$so->name)); + + // Check Domain exists + Domain::unguard(); + $do = Domain::singleOrNew(['name'=>$ftn['d']]); + Domain::reguard(); + + if (! $do->exists) { + $do->public = TRUE; + $do->active = TRUE; + $do->notes = 'Auto created'; + + $do->save(); + } + + // If the zone is zero, see if this is a flatten domain, and if so, find an NC + if (($ftn['z'] === 0) && $do->flatten) { + $nc = self::findZone($do,$ftn['n'],0,0); + + if ($nc) { + Log::info(sprintf('%s:- Setting zone to [%d]',self::LOGKEY,$nc->zone->zone_id)); + $ftn['z'] = $nc->zone->zone_id; + } + } + + // Create zone + Zone::unguard(); + $zo = Zone::singleOrNew(['domain_id'=>$do->id,'zone_id'=>$ftn['z']]); + Zone::reguard(); + + if (! $zo->exists) { + $zo->active = TRUE; + $zo->notes = 'Auto created'; + $zo->system_id = System::createUnknownSystem()->id; + + $do->zones()->save($zo); + } + + if (! $zo->active || ! $do->active) { + Log::alert(sprintf('%s:! Refusing to create address [%s] in disabled zone or domain',self::LOGKEY,$address)); + + return NULL; + } + + // Create Address, assigned to $so + $o = new self; + $o->active = TRUE; + $o->zone_id = $zo->id; + $o->region_id = 0; // @todo Automatically determine region + $o->host_id = $ftn['n']; + $o->node_id = $ftn['f']; + $o->point_id = $ftn['p']; + + try { + $so->addresses()->save($o); + + } catch (QueryException $e) { + Log::error(sprintf('%s:! ERROR creating address [%s] (%s)',self::LOGKEY,$o->ftn,get_class($e))); + return NULL; + } + + return $o; + } + + /** + * Find a record in the DB for a node string, eg: 10:1/1.0 + * + * @param string $address + * @param bool $trashed + * @return Address|null + * @throws \Exception + */ + public static function findFTN(string $address,bool $trashed=FALSE): ?self + { + $ftn = self::parseFTN($address); + $o = NULL; + + $query = (new self) + ->select('addresses.*') + ->join('zones',['zones.id'=>'addresses.zone_id']) + ->join('domains',['domains.id'=>'zones.domain_id']) + ->when($trashed,function($query) { + $query->withTrashed(); + },function($query) { + $query->active(); + }) + ->where('zones.zone_id',$ftn['z']) + ->where('node_id',$ftn['f']) + ->where('point_id',$ftn['p']) + ->when($ftn['d'],function($query,$domain) { + $query->where('domains.name',$domain); + },function($query) { + $query->where('zones.default',TRUE); + }) + ->orderBy('created_at','DESC'); + + $q = $query->clone(); + + // Are we looking for a region address + if (($ftn['f'] === 0) && ($ftn['p'] === 0)) + $o = $query + ->where('region_id',$ftn['n']) + ->where('host_id',$ftn['n']) + ->first(); + + // Look for a normal address + if (! $o) + $o = $q + ->where(function($q) use ($ftn) { + return $q + ->where(function($q) use ($ftn) { + return $q + ->where('region_id',$ftn['n']) + ->where('host_id',$ftn['n']); + }) + ->orWhere('host_id',$ftn['n']); + }) + ->first(); + + // Check and see if we are a flattened domain, our address might be available with a different zone. + // This occurs when we are parsing 2D addresses from SEEN-BY, but we have the zone + if (! $o && ($ftn['p'] === 0)) { + if ($ftn['d']) + $do = Domain::where(['name'=>$ftn['d']])->single(); + else { + $zo = Zone::where('zone_id',$ftn['z'])->where('default',TRUE)->single(); + $do = $zo?->domain; + } + + if ($do && $do->flatten && (($ftn['z'] === 0) || $do->zones->pluck('zone_id')->contains($ftn['z']))) + $o = self::findZone($do,$ftn['n'],$ftn['f'],$ftn['p'],$trashed); + } + + return ($o && $o->system->active) ? $o : NULL; + } + + /** + * This is to find an address for a domain (like fidonet), which is technically 2D even though it uses multiple zones. + * + * This was implemented to identify seenby and path kludges + * + * @param Domain $do + * @param int $host + * @param int $node + * @param int $point + * @param bool $trashed + * @return self|null + * @throws \Exception + */ + public static function findZone(Domain $do,int $host,int $node,int $point,bool $trashed=FALSE): ?self + { + if (! $do->flatten) + throw new \Exception(sprintf('Domain is not set with flatten: %d',$do->id)); + + $zones = $do->zones->pluck('zone_id'); + + $o = (new self) + ->select('addresses.*') + ->join('zones',['zones.id'=>'addresses.zone_id']) + ->when($trashed,function($query) { + $query->withTrashed(); + },function($query) { + $query->active(); + }) + ->whereIN('zones.zone_id',$zones) + ->where(function($q) use ($host) { + return $q + ->where(function($q) use ($host) { + return $q->where('region_id',$host) + ->where('host_id',$host); + }) + ->orWhere('host_id',$host); + }) + ->where('node_id',$node) + ->where('point_id',$point) + ->where('zones.domain_id',$do->id) + ->single(); + + return $o; + } + + /** + * Parse a string and split it out as an FTN array + * + * @param string $ftn + * @return array + * @throws \Exception + */ + public static function parseFTN(string $ftn): array + { + if (! preg_match(sprintf('#^%s$#',self::ftn_regex),strtolower($ftn),$matches)) + throw new InvalidFTNException(sprintf('Invalid FTN: [%s] - regex failed',serialize($ftn))); + + // Check our numbers are correct. + foreach ([1,2,3] as $i) + if ((! is_numeric($matches[$i])) || ($matches[$i] > self::ADDRESS_FIELD_MAX)) + throw new InvalidFTNException(sprintf('Invalid FTN: [%s] - zone, host, or node address invalid [%d]',$ftn,$matches[$i])); + + if ((! empty($matches[4])) AND ((! is_numeric($matches[$i])) || ($matches[4] > self::ADDRESS_FIELD_MAX))) + throw new InvalidFTNException(sprintf('Invalid FTN: [%s] - point address invalid [%d]',$ftn,$matches[4])); + + return [ + 'z'=>(int)$matches[1], + 'n'=>(int)$matches[2], + 'f'=>(int)$matches[3], + 'p'=>empty($matches[4]) ? 0 : (int)$matches[4], + 'd'=>$matches[5] ?? NULL + ]; + } + /* SCOPES */ /** @@ -516,6 +754,41 @@ class Address extends Model /* METHODS */ + /** + * Check the user's activation code for this address is correct + * + * @param User $uo + * @param string $code + * @return bool + */ + public function activation_check(User $uo,string $code): bool + { + try { + Log::info(sprintf('%s:Checking Activation code [%s] is valid for user [%d]',self::LOGKEY,$code,$uo->id)); + + return ($code === $this->activation_set($uo)); + + } catch (\Exception $e) { + Log::error(sprintf('%s:! Activation code [%s] invalid for user [%d]',self::LOGKEY,$code,$uo->id)); + + return FALSE; + } + } + + /** + * Create an activation code for this address + * + * @param User $uo + * @return string + */ + public function activation_set(User $uo): string + { + return sprintf('%x:%s', + $this->id, + substr(md5(sprintf('%d:%x',$uo->id,timew($this->updated_at))),0,10) + ); + } + /** * Find the immediate children dependent on this record * @@ -600,253 +873,6 @@ class Address extends Model return $children->filter(function($item) use ($exclude) { return ! $exclude->pluck('id')->contains($item->id);}); } - /** - * Create an FTN address associated with a system - * - * @param string $address - * @param System $so - * @return Address|null - * @throws \Exception - * @todo Move to Static - */ - public static function createFTN(string $address,System $so): ?self - { - $ftn = self::parseFTN($address); - - if (! $ftn['d']) { - // See if we have a default domain for this domain - if ($ftn['z']) { - $zo = Zone::where('zone_id',$ftn['z'])->where('default',TRUE)->single(); - - if ($zo) - $ftn['d'] = $zo->domain->name; - - else { - Log::alert(sprintf('%s:! Refusing to create address [%s] no domain available',self::LOGKEY,$address)); - - return NULL; - } - } - } - - Log::debug(sprintf('%s:- Creating AKA [%s] for [%s]',self::LOGKEY,$address,$so->name)); - - // Check Domain exists - Domain::unguard(); - $do = Domain::singleOrNew(['name'=>$ftn['d']]); - Domain::reguard(); - - if (! $do->exists) { - $do->public = TRUE; - $do->active = TRUE; - $do->notes = 'Auto created'; - - $do->save(); - } - - // If the zone is zero, see if this is a flatten domain, and if so, find an NC - if (($ftn['z'] === 0) && $do->flatten) { - $nc = self::findZone($do,$ftn['n'],0,0); - - if ($nc) { - Log::info(sprintf('%s:- Setting zone to [%d]',self::LOGKEY,$nc->zone->zone_id)); - $ftn['z'] = $nc->zone->zone_id; - } - } - - // Create zone - Zone::unguard(); - $zo = Zone::singleOrNew(['domain_id'=>$do->id,'zone_id'=>$ftn['z']]); - Zone::reguard(); - - if (! $zo->exists) { - $zo->active = TRUE; - $zo->notes = 'Auto created'; - $zo->system_id = System::createUnknownSystem()->id; - - $do->zones()->save($zo); - } - - if (! $zo->active || ! $do->active) { - Log::alert(sprintf('%s:! Refusing to create address [%s] in disabled zone or domain',self::LOGKEY,$address)); - - return NULL; - } - - // Create Address, assigned to $so - $o = new self; - $o->active = TRUE; - $o->zone_id = $zo->id; - $o->region_id = 0; // @todo Automatically determine region - $o->host_id = $ftn['n']; - $o->node_id = $ftn['f']; - $o->point_id = $ftn['p']; - - try { - $so->addresses()->save($o); - - } catch (QueryException $e) { - Log::error(sprintf('%s:! ERROR creating address [%s] (%s)',self::LOGKEY,$o->ftn,get_class($e))); - return NULL; - } - - return $o; - } - - /** - * Find a record in the DB for a node string, eg: 10:1/1.0 - * - * @param string $address - * @param bool $trashed - * @return Address|null - * @throws \Exception - * @todo Move to Static - */ - public static function findFTN(string $address,bool $trashed=FALSE): ?self - { - $ftn = self::parseFTN($address); - $o = NULL; - - $query = (new self) - ->select('addresses.*') - ->join('zones',['zones.id'=>'addresses.zone_id']) - ->join('domains',['domains.id'=>'zones.domain_id']) - ->when($trashed,function($query) { - $query->withTrashed(); - },function($query) { - $query->active(); - }) - ->where('zones.zone_id',$ftn['z']) - ->where('node_id',$ftn['f']) - ->where('point_id',$ftn['p']) - ->when($ftn['d'],function($query,$domain) { - $query->where('domains.name',$domain); - },function($query) { - $query->where('zones.default',TRUE); - }) - ->orderBy('created_at','DESC'); - - $q = $query->clone(); - - // Are we looking for a region address - if (($ftn['f'] === 0) && ($ftn['p'] === 0)) - $o = $query - ->where('region_id',$ftn['n']) - ->where('host_id',$ftn['n']) - ->first(); - - // Look for a normal address - if (! $o) - $o = $q - ->where(function($q) use ($ftn) { - return $q - ->where(function($q) use ($ftn) { - return $q - ->where('region_id',$ftn['n']) - ->where('host_id',$ftn['n']); - }) - ->orWhere('host_id',$ftn['n']); - }) - ->first(); - - // Check and see if we are a flattened domain, our address might be available with a different zone. - // This occurs when we are parsing 2D addresses from SEEN-BY, but we have the zone - if (! $o && ($ftn['p'] === 0)) { - if ($ftn['d']) - $do = Domain::where(['name'=>$ftn['d']])->single(); - else { - $zo = Zone::where('zone_id',$ftn['z'])->where('default',TRUE)->single(); - $do = $zo?->domain; - } - - if ($do && $do->flatten && (($ftn['z'] === 0) || $do->zones->pluck('zone_id')->contains($ftn['z']))) - $o = self::findZone($do,$ftn['n'],$ftn['f'],$ftn['p'],$trashed); - } - - return ($o && $o->system->active) ? $o : NULL; - } - - /** - * This is to find an address for a domain (like fidonet), which is technically 2D even though it uses multiple zones. - * - * This was implemented to identify seenby and path kludges - * - * @param Domain $do - * @param int $host - * @param int $node - * @param int $point - * @param bool $trashed - * @return self|null - * @throws \Exception - * @todo Move to Static - */ - public static function findZone(Domain $do,int $host,int $node,int $point,bool $trashed=FALSE): ?self - { - if (! $do->flatten) - throw new \Exception(sprintf('Domain is not set with flatten: %d',$do->id)); - - $zones = $do->zones->pluck('zone_id'); - - $o = (new self) - ->select('addresses.*') - ->join('zones',['zones.id'=>'addresses.zone_id']) - ->when($trashed,function($query) { - $query->withTrashed(); - },function($query) { - $query->active(); - }) - ->whereIN('zones.zone_id',$zones) - ->where(function($q) use ($host) { - return $q - ->where(function($q) use ($host) { - return $q->where('region_id',$host) - ->where('host_id',$host); - }) - ->orWhere('host_id',$host); - }) - ->where('node_id',$node) - ->where('point_id',$point) - ->where('zones.domain_id',$do->id) - ->single(); - - return $o; - } - - /** - * Create an activation code for this address - * - * @param User $uo - * @return string - */ - public function set_activation(User $uo): string - { - return sprintf('%x:%s', - $this->id, - substr(md5(sprintf('%d:%x',$uo->id,timew($this->updated_at))),0,10) - ); - } - - /** - * Check the user's activation code for this address is correct - * - * @param User $uo - * @param string $code - * @return bool - */ - public function check_activation(User $uo,string $code): bool - { - try { - Log::info(sprintf('%s:Checking Activation code [%s] is valid for user [%d]',self::LOGKEY,$code,$uo->id)); - - return ($code === $this->set_activation($uo)); - - } catch (\Exception $e) { - Log::error(sprintf('%s:! Activation code [%s] invalid for user [%d]',self::LOGKEY,$code,$uo->id)); - - return FALSE; - } - } - /** * Files waiting to be sent to this system * @@ -937,6 +963,7 @@ class Address extends Model * @param bool $update * @param Collection|null $echomail * @return Packet|null + * @throws \Exception * @todo If we export to uplink hubs without our address in the seenby, they should send the message back to * us with their seenby's. */ @@ -1176,36 +1203,6 @@ class Address extends Model } } - /** - * Parse a string and split it out as an FTN array - * - * @param string $ftn - * @return array - * @throws \Exception - * @todo Move to Static - */ - public static function parseFTN(string $ftn): array - { - if (! preg_match(sprintf('#^%s$#',self::ftn_regex),strtolower($ftn),$matches)) - throw new InvalidFTNException(sprintf('Invalid FTN: [%s] - regex failed',serialize($ftn))); - - // Check our numbers are correct. - foreach ([1,2,3] as $i) - if ((! is_numeric($matches[$i])) || ($matches[$i] > self::ADDRESS_FIELD_MAX)) - throw new InvalidFTNException(sprintf('Invalid FTN: [%s] - zone, host, or node address invalid [%d]',$ftn,$matches[$i])); - - if ((! empty($matches[4])) AND ((! is_numeric($matches[$i])) || ($matches[4] > self::ADDRESS_FIELD_MAX))) - throw new InvalidFTNException(sprintf('Invalid FTN: [%s] - point address invalid [%d]',$ftn,$matches[4])); - - return [ - 'z'=>(int)$matches[1], - 'n'=>(int)$matches[2], - 'f'=>(int)$matches[3], - 'p'=>empty($matches[4]) ? 0 : (int)$matches[4], - 'd'=>$matches[5] ?? NULL - ]; - } - /** * Retrieve the address session details/passwords * diff --git a/app/Notifications/Netmails/AddressLink.php b/app/Notifications/Netmails/AddressLink.php index 6ac7aab..5aa15d3 100644 --- a/app/Notifications/Netmails/AddressLink.php +++ b/app/Notifications/Netmails/AddressLink.php @@ -55,7 +55,7 @@ class AddressLink extends Netmails $ao->system->sysop, $ao->ftn3d, url('/link'), - $ao->set_activation($this->uo) + $ao->activation_set($this->uo) )); $o->msg = $msg->render();