file = $file; $this->domain = $domain; $this->delete_file = $delete_file; $this->delete_recs = $delete_recs; } /** * Execute the job. * * @return void * @throws \Exception */ public function handle() { $us = Setup::findOrFail(config('app.id')); // Get the file from the host $file = $this->getFileFromHost(self::importkey,$this->file); Log::debug(sprintf('%s:Loading file [%s] (%s).',static::LOGKEY,$file,getcwd())); $lines = $this->getFileLines($file); Log::debug(sprintf('%s:Processing [%d] lines.',static::LOGKEY,$lines)); $fh = NULL; $z = $this->openFile($file,$fh); // Line 1 tells us the nodelist and the CRC $line = stream_get_line($fh,0,"\r\n"); $matches = []; if ((! preg_match('/^;A\ /',$line)) || (! preg_match('/^;A\ (.*)\ Nodelist for ([MTWFS][a-z]+,\ [JFMASOND][a-z]+\ [0-9]{1,2},\ [0-9]{4})\ --\ Day\ number\ ([0-9]+)\ :\ ([0-9a-f]+)$/',$line,$matches))) { Log::error(sprintf('%s:Nodelist file [%d] is not valid?',self::LOGKEY,$this->file->id),['m'=>$matches,'l'=>$line]); throw new \Exception('Invalid nodelist for file: '.$this->file->id); } $file_crc = $matches[4]; $do = Domain::where('name',strtolower($matches[1] ?: $this->domain))->single(); if (! $do) { Log::error(sprintf('%s:! Domain not found [%s].',static::LOGKEY,strtolower($matches[1] ?: $this->domain))); throw new \Exception('Nodelist Domain not found: '.$this->file->id); } $date = Carbon::createFromFormat('D, M d, Y H:i',$matches[2].'0:00'); if ($date->dayOfYear != $matches[3]) { Log::error(sprintf('%s:! Nodelist date doesnt match [%d] (%d:%s).',static::LOGKEY,$matches[3],$date->dayOfYear,$date->format('Y-m-d'))); throw new \Exception('Nodelist date doesnt match for file: '.$this->file->id); } Log::debug(sprintf('%s:Importing nodelist for [%s] dated [%s].',static::LOGKEY,$do->name,$date->format('Y-m-d'))); DB::beginTransaction(); $no = Nodelist::firstOrCreate(['date'=>$date,'domain_id'=>$do->id]); if ($this->delete_recs) $no->addresses()->detach(); $p = $c = 0; $region = NULL; $host = NULL; $hub_id = NULL; $zo = NULL; $tocrc = ''; while (! feof($fh)) { $line = stream_get_line($fh,0,"\r\n"); $tocrc .= $line."\r\n"; // Lines beginning with a semicolon(;) are comments if ((! $line) OR preg_match('/^;/',$line) OR ($line == chr(0x1a))) continue; // Remove any embedded CR and BOM $line = str_replace("\r",'',$line); $line = preg_replace('/^\x{feff}/u','',$line); $c++; $fields = str_getcsv(trim($line)); // First field is either zone,region,host,hub,down,pvt (or blank) if ($fields[0] AND ! in_array($fields[0],Nodelist::definitions)) { Log::error(sprintf('%s:Invalid field zero [%s] - IGNORING record (%s)',self::LOGKEY,$fields[0],$line)); continue; } $node = 0; switch ($fields[0]) { case 'Zone': $zone = $fields[1]; Zone::unguard(); $zo = Zone::firstOrNew([ 'zone_id'=>$zone, 'domain_id'=>$do->id, 'active'=>TRUE, ]); Zone::reguard(); $region = 0; $host = 0; $hub_id = NULL; $role = Address::NODE_ZC; break; case 'Region': $region = $fields[1]; $role = Address::NODE_RC; $host = 0; $hub_id = NULL; break; case 'Host': $host = $fields[1]; $hub_id = NULL; $role = Address::NODE_NC; break; case 'Hub': $node = $fields[1]; $role = Address::NODE_HC; break; case 'Pvt': $node = $fields[1]; $role = Address::NODE_PVT; break; case 'Hold': $node = $fields[1]; $role = Address::NODE_HOLD; break; case 'Down': $node = $fields[1]; $role = Address::NODE_DOWN; break; case '': $node = $fields[1]; $role = Address::NODE_ACTIVE; break; default: Log::error(sprintf('%s:Unhandled first field [%s]',self::LOGKEY,$fields[0])); continue 2; } if (! $zone) { Log::error(sprintf('%s:Zone NOT set, ignoring record...',self::LOGKEY)); continue; } Address::unguard(); $ao = Address::firstOrNew([ 'zone_id' => $zo->id, 'region_id' => $region, 'host_id' => $host, 'node_id' => $node, 'point_id' => 0, ]); Address::reguard(); $ao->active = TRUE; $ao->role = $role; $ao->hub_id = $hub_id; if ($ao->exists) Log::debug(sprintf('%s:Processing existing address [%s]',self::LOGKEY,$ao->ftn)); $sysop = trim(str_replace('_',' ',$fields[4])); $system = trim(str_replace('_',' ',$fields[2])); // Flags $method = NULL; $address = ''; $port = ''; for ($i=7;$isplit('/:/'); switch ($x->first()) { // address case 'INA': $address = $x->get(1); break; case 'IBN': $method = Setup::O_BINKP; switch ($x->count()) { case 1: $port = 24554; break; case 2: $port = $x->get(1); break; case 3: $address = $x->get(1); $port = $x->get(2); } break; case 'ITN': $method = Setup::O_EMSI; switch ($x->count()) { case 1: $port = 60179; break; case 2: $port = $x->get(1); break; case 3: $address = $x->get(1); $port = $x->get(2); } break; // Ignore case 'ZEC': case 'REC': case 'MO': case 'CM': break; default: Log::debug(sprintf('%s: - Not configured to handle flag [%s]',self::LOGKEY,$x->first())); continue 2; } } // Get the System if ($ao->system_id && (($ao->system->sysop === $sysop) || ($ao->system->name === $system))) { $so = $ao->system; // Dont change the system details if a user exists here, or its us if ((! $so->users->count()) && ($so->id != $us->system_id)) { // If the sysop name is different if ($so->sysop !== $sysop) { Log::debug(sprintf('%s:Sysop Name changed for BBS [%s:%s] from [%s] to [%s]', self::LOGKEY,$so->id,$so->name,$so->sysop,$sysop)); $so->sysop = $sysop; // We have the same name has changed. } elseif ($so->name !== $system) { Log::debug(sprintf('%s:System Name changed for BBS [%s:%s] to [%s]', self::LOGKEY,$so->id,$so->name,$system)); $so->name = $system; } } // We'll search and see if we already have that system } else { // If we dont have $address/port $so = NULL; if ($address && $port) $so = System::where('mailer_address',$address) ->where('mailer_port',$port) ->single(); if (! $so) $so = System::where('name',$system) ->where('sysop',$sysop) ->firstOrNew(); if ($so->exists) Log::debug(sprintf('%s:Linking address [%d:%d/%d] to [%s:%s]',self::LOGKEY,$zo->zone_id,$ao->host_id,$ao->node_id,$so->id,$so->name)); else Log::debug(sprintf('%s:New System [%s] with address [%d:%d/%d]',self::LOGKEY,$system,$zo->zone_id,$ao->host_id,$ao->node_id)); $so->name = $system; $so->sysop = $sysop; $so->active = TRUE; if (! $so->exists) $so->notes = sprintf('Created by Nodelist Import: %d',$no->id); } $so->phone = $fields[5] != '-Unpublished-' ? $fields[5] : NULL; $so->location = trim(str_replace('_',' ',$fields[3])); /* if (! in_array($fields[5],['-Unpublished-'])) $so->phone = $fields[5]; $so->baud = $fields[6]; */ if ($method && ($ao->role != Address::NODE_PVT)) { $so->mailer_type = $method; $so->mailer_address = $address; $so->mailer_port = $port; } // Save the system record try { $so->save(); } catch (\Exception $e) { Log::error(sprintf('%s:Error with line [%s] (%s)',self::LOGKEY,$line,$e->getMessage()),['fields'=>$fields]); DB::rollBack(); throw new \Exception($e->getMessage()); } // If our zone didnt exist, we'll create it with this system if (! $zo->exists) { $zo->system_id = $so->id; $zo->save(); } $ao->zone_id = $zo->id; if ($ao->getDirty()) $p++; try { $so->addresses()->save($ao); if ($ao->role == Address::NODE_HC) $hub_id = $ao->id; $no->addresses()->attach($ao,['role'=>$ao->role]); } catch (\Exception $e) { Log::error(sprintf('%s:Error with line [%s] (%s)',self::LOGKEY,$line,$e->getMessage()),['fields'=>$fields]); DB::rollBack(); throw new \Exception($e->getMessage()); } if (! ($c % 100)) { Log::notice(sprintf('%s:Processed [%s] records',self::LOGKEY,$c),['memory'=>memory_get_usage(TRUE)]); } } // Remove addresses not recorded; $remove = $zo->addresses->except($us->system->addresses->pluck('id')->toArray())->diff($no->addresses); Log::notice(sprintf('%s:Deleting addresses [%s]',self::LOGKEY,$remove->pluck('ftn2d')->join(','))); Address::whereIN('id',$remove->pluck('id')->toArray())->update(['active'=>FALSE]); Address::whereIN('id',$remove->pluck('id')->toArray())->delete(); if ($x=crc16(substr($tocrc,0,-3)) == $file_crc) { Log::info(sprintf('%s:Committing nodelist',self::LOGKEY)); DB::commit(); } else { Log::error(sprintf('%s:Rolling back nodelist, CRC doesnt match [%s](%s)',self::LOGKEY,$x,$file_crc)); DB::rollBack(); } fclose($fh); if ($this->delete_file and $c) unlink($file); Log::info(sprintf('%s:Updated %d records from %d systems',self::LOGKEY,$p,$c)); } }