示例#1
0
 /**
  * @param Ticket   $ticket
  * @param int|null $newstatus
  *
  * @return bool
  */
 public static function status($ticket, $newstatus = null)
 {
     /*
      * status list is defined in config/types.php
      */
     if (array_key_exists(strtoupper($newstatus), config('types.status.abusedesk'))) {
         $ticket->status_id = $newstatus;
         $ticket->save();
         return true;
     } else {
         return false;
     }
 }
示例#2
0
 /**
  * Update IP contact for a ticket
  *
  * @param Ticket $ticket
  */
 public static function ipContact($ticket)
 {
     $ipContact = FindContact::byIP($ticket['ip']);
     // Update IP Contact fields in ticket
     $ticket->ip_contact_account_id = $ipContact->account_id;
     $ticket->ip_contact_reference = $ipContact->reference;
     $ticket->ip_contact_name = $ipContact->name;
     $ticket->ip_contact_email = $ipContact->email;
     $ticket->ip_contact_api_host = $ipContact->api_host;
     $ticket->ip_contact_api_key = $ipContact->api_key;
     $ticket->ip_contact_auto_notify = $ipContact->auto_notify;
     $ticket->save();
 }
示例#3
0
 /**
  * Method to add a note to a ticket
  *
  * @param integer $ticketID
  * @param string $token
  * @return \Illuminate\Http\Response
  */
 public function addNote($ticketID, $token)
 {
     $brand = false;
     $submittor = false;
     $ticket = Ticket::find($ticketID);
     $AshAuthorisedBy = Request::get('AshAuthorisedBy');
     if ($AshAuthorisedBy == 'TokenIP') {
         $brand = $ticket->accountIp->brand;
         $submittor = trans('ash.basic.ip') . ' ' . trans('ash.communication.contact');
     }
     if ($AshAuthorisedBy == 'TokenDomain') {
         $brand = $ticket->accountDomain->brand;
         $submittor = trans('ash.basic.domain') . ' ' . trans('ash.communication.contact');
     }
     if (empty($brand) || empty($submittor)) {
         abort(500);
     }
     $text = Input::get('text');
     if (empty($text)) {
         $message = 'You cannot add an empty message!';
     } else {
         $message = 'Note has been added.';
         $note = new Note();
         $note->ticket_id = $ticket->id;
         $note->submitter = $submittor;
         $note->text = $text;
         $note->save();
     }
     return view('ash')->with('brand', $brand)->with('ticket', $ticket)->with('token', $token)->with('message', $message);
 }
示例#4
0
 /**
  * Method to add a note to a ticket.
  *
  * @param int    $ticketID
  * @param string $token
  *
  * @return \Illuminate\Http\Response
  */
 public function addNote($ticketID, $token)
 {
     $submittor = false;
     $ticket = Ticket::find($ticketID);
     $AshAuthorisedBy = Request::get('AshAuthorisedBy');
     if ($AshAuthorisedBy == 'TokenIP') {
         $account = Account::find($ticket->ip_contact_account_ip);
         $submittor = trans('ash.basic.ip') . ' ' . trans('ash.communication.contact');
     }
     if ($AshAuthorisedBy == 'TokenDomain') {
         $account = Account::find($ticket->domain_contact_account_id);
         $submittor = trans('ash.basic.domain') . ' ' . trans('ash.communication.contact');
     }
     $brand = empty($account) ? Brand::getSystemBrand() : $account->brand;
     if (empty($brand) || empty($submittor)) {
         abort(500);
     }
     $changeStatus = Input::get('changeStatus');
     if ($changeStatus == 'IGNORED' || $changeStatus == 'RESOLVED') {
         $ticket->contact_status_id = $changeStatus;
         $ticket->save();
     }
     $text = Input::get('text');
     if (empty($text) || strlen($text) < 1) {
         $message = 'noteEmpty';
     } else {
         $message = 'noteAdded';
         $note = new Note();
         $note->ticket_id = $ticket->id;
         $note->submitter = $submittor;
         $note->text = $text;
         $note->save();
     }
     return view('ash')->with('brand', $brand)->with('ticket', $ticket)->with('allowedChanges', $this->allowedStatusChanges($ticket))->with('token', $token)->with('message', $message);
 }
示例#5
0
 /**
  * @param $request
  * @param Closure $next
  * @return \BladeView|bool|\Illuminate\Contracts\Routing\ResponseFactory
  *         \Illuminate\Contracts\View\Factory
  *         \Illuminate\View\View
  *         \Symfony\Component\HttpFoundation\Response
  * @throws \Exception
  */
 public function handle($request, Closure $next)
 {
     /*
      * TODO: find how laravel passes $ticketID and $token from the controller to middleware
      * this will remove the need of parsing the URI ourselves
      */
     $uri = explode('/', Request::path());
     if ($uri[0] === 'ash' && $uri['1'] === 'collect') {
         $ticketID = $uri[2];
         $token = $uri[3];
         $ticket = Ticket::find($ticketID);
         if (!empty($ticket)) {
             // Prevent unauthorized access by UNDEF contact tokens(default)
             $validTokenIP = md5(Uuid::generate(4));
             $validTokenDomain = md5(Uuid::generate(4));
             if ($ticket->ip_contact_reference != 'UNDEF') {
                 $validTokenIP = md5($ticket->id . $ticket->ip . $ticket->ip_contact_reference);
             }
             if ($token == $validTokenIP) {
                 $request->merge(['AshAuthorisedBy' => 'TokenIP']);
                 return $next($request);
             }
             if ($ticket->domain_contact_reference != 'UNDEF') {
                 $request->merge(['AshAuthorisedBy' => 'TokenDomain']);
                 $validTokenDomain = md5($ticket->id . $ticket->domain . $ticket->domain_contact_reference);
             }
             if ($token == $validTokenDomain) {
                 return $next($request);
             }
         }
     }
     return $request->ajax ? response('Unauthorized.', 401) : view('errors.403');
 }
 /**
  * Export listing to CSV format.
  *
  * @return Response
  */
 public function export()
 {
     $tickets = Ticket::all();
     $columns = ['id' => 'Ticket ID'];
     $output = '"' . implode('", "', $columns) . '"' . PHP_EOL;
     foreach ($tickets as $ticket) {
         $row = [$ticket->id];
         $output .= '"' . implode('", "', $row) . '"' . PHP_EOL;
     }
     return response(substr($output, 0, -1), 200)->header('Content-Type', 'text/csv')->header('Content-Disposition', 'attachment; filename="Tickets.csv"');
 }
示例#7
0
 /**
  * Display a listing of the resource.
  * @return Response
  */
 public function index($ticketID, $token)
 {
     $ticket = Ticket::find($ticketID);
     $validTokenIP = md5($ticket->id . $ticket->ip . $ticket->ip_contact_reference);
     $validTokenDomain = md5($ticket->id . $ticket->ip . $ticket->domain_contact_reference);
     if ($token == $validTokenIP || $token == $validTokenDomain) {
         return view('ash')->with('ticket', $ticket);
     } else {
         return view('errors.403');
     }
 }
示例#8
0
 /**
  * Display a listing of the resource.
  *
  * @return Response
  */
 public function index($ticketID, $token)
 {
     $ticket = Ticket::find($ticketID);
     // 6bb1aef09ea536260e3afe3fb9b432e4
     // c1eee3ce87f1fd774eb8819c820fa5be
     $validTokenIP = md5($ticket->id . $ticket->ip . $ticket->ip_contact_reference);
     $validTokenDomain = md5($ticket->id . $ticket->ip . $ticket->domain_contact_reference);
     if ($token == $validTokenIP || $token == $validTokenDomain) {
         return view('ash')->with('ticket', $ticket);
     } else {
         return view('errors.403');
     }
 }
示例#9
0
 /**
  * Export tickets to CSV format.
  *
  * @param string $format
  * @return \Illuminate\Http\Response
  */
 public function export($format)
 {
     // TODO #AIO-?? ExportProvider - (mark) Move this into an ExportProvider or something s?
     $tickets = Ticket::all();
     if ($format === 'csv') {
         $columns = ['id' => 'Ticket ID', 'ip' => 'IP address', 'class_id' => 'Classification', 'type_id' => 'Type', 'first_seen' => 'First seen', 'last_seen' => 'Last seen', 'event_count' => 'Events', 'status_id' => 'Ticket Status'];
         $output = '"' . implode('", "', $columns) . '"' . PHP_EOL;
         foreach ($tickets as $ticket) {
             $row = [$ticket->id, $ticket->ip, $ticket->class_id, $ticket->type_id, $ticket->firstEvent[0]->seen, $ticket->lastEvent[0]->seen, $ticket->events->count(), trans('types.status.' . $ticket->status_id . '.name')];
             $output .= '"' . implode('", "', $row) . '"' . PHP_EOL;
         }
         return response(substr($output, 0, -1), 200)->header('Content-Type', 'text/csv')->header('Content-Disposition', 'attachment; filename="Tickets.csv"');
     }
     return Redirect::route('admin.contacts.index')->with('message', "The requested format {$format} is not available for exports");
 }
示例#10
0
 /**
  * Display a listing of the resource.
  *
  * @return \Illuminate\Http\RedirectResponse
  */
 public function index()
 {
     $classCounts = [];
     foreach ((array) Lang::get('classifications') as $classID => $classInfo) {
         $classTotal = new \stdClass();
         $tickets = Ticket::where('class_id', $classID);
         $ticketCount = $tickets->count();
         $tickets = $tickets->get();
         if ($ticketCount !== 0) {
             $classTotal->name = $classInfo['name'];
             $classTotal->tickets = $tickets->count();
             $classCounts[] = $classTotal;
         }
     }
     return view('analytics')->with('classCounts', $classCounts)->with('auth_user', $this->auth_user);
 }
示例#11
0
 /**
  * Create a list of tickets that need outgoing notifications.
  *
  * @param int|bool    $ticket    Ticket ID
  * @param string|bool $reference ReferenceName of contact in ticket
  * @param bool|bool   $force     Force sending even when there are no new events
  * @param string      $only      Only send to ('ip', 'domain' or null (both))
  *
  * @return array $notificationList   List of notifications to send
  */
 public function buildList($ticket = false, $reference = false, $force = false, $only = null)
 {
     /*
      * Select a list of tickets that are not closed(2) by default or add ticket / reference
      * conditions if options were passed along.
      */
     $selection = [];
     $search = Ticket::where('id', '>', '0');
     if (!$force) {
         $search = Ticket::where('status_id', '!=', 'CLOSED');
     }
     if ($ticket !== false) {
         $search->where('id', '=', $ticket);
     }
     if ($reference !== false) {
         $search->where('ip_contact_reference', '=', $reference)->orwhere('domain_contact_reference', '=', $reference);
     }
     $tickets = $search->get();
     $validator = Validator::make(['notification info_interval' => strtotime(config('main.notifications.info_interval') . ' ago'), 'notification abuse_interval' => strtotime(config('main.notifications.abuse_interval') . ' ago'), 'notification min_lastseen' => strtotime(config('main.notifications.min_lastseen') . ' ago')], ['notification info_interval' => 'required|timestamp', 'notification abuse_interval' => 'required|timestamp', 'notification min_lastseen' => 'required|timestamp']);
     if ($validator->fails()) {
         $messages = $validator->messages();
         $message = '';
         foreach ($messages->all() as $messagePart) {
             $message .= $messagePart . PHP_EOL;
         }
         return $message;
     }
     // If an invalid value for $only is given, set default (null = both)
     if (!in_array($only, ['ip', 'domain'])) {
         $only = null;
     }
     $sendInfoAfter = strtotime(config('main.notifications.info_interval') . ' ago');
     $sendAbuseAfter = strtotime(config('main.notifications.abuse_interval') . ' ago');
     $sendNotOlderThen = strtotime(config('main.notifications.min_lastseen') . ' ago');
     foreach ($tickets as $ticket) {
         /*
          * Only send a notification if there is something new to report
          * or if this is the first notification.
          */
         if ($ticket->last_notify_count == $ticket->events->count() && $ticket->last_notify_count != 0 && $force !== true) {
             continue;
         } else {
             /*
              * Filter outgoing notifications and aggregate them by reference so we can send out a single
              * notifications for multiple tickets if needed.
              */
             if ($force !== true) {
                 // Skip if status Ignored
                 if ($ticket->status_id == 'IGNORED') {
                     continue;
                 }
                 // Skip if type Info and contact status Ignored
                 if ($ticket->type_id == 'INFO' && $ticket->contact_status_id == 'IGNORED') {
                     continue;
                 }
                 // Skip if type Info (1) and last notification was send after info interval
                 if ($ticket->last_notify_count != 0 && $ticket->type_id == 'INFO' && $ticket->last_notify_timestamp >= $sendInfoAfter) {
                     continue;
                 }
                 // Skip if type Info (1) and last notification was send after abuse interval
                 if ($ticket->last_notify_count != 0 && $ticket->type_id != 'INFO' && $ticket->last_notify_timestamp >= $sendAbuseAfter) {
                     continue;
                 }
                 // Skip if the event received is older the minimal last seen
                 if ($ticket->lastEvent[0]->timestamp <= $sendNotOlderThen) {
                     continue;
                 }
                 // Conditions just for IP contacts
                 if (!empty($ticket->ip_contact_reference) && $ticket->ip_contact_reference != 'UNDEF' && $ticket->ip_contact_auto_notify == true && $only != 'domain') {
                     $selection[$ticket->ip_contact_reference]['ip'][] = $ticket;
                 }
                 // Conditions just for Domain contacts
                 if (!empty($ticket->domain_contact_reference) && $ticket->domain_contact_reference != 'UNDEF' && $ticket->domain_contact_auto_notify == true && $ticket->domain_contact_reference != $ticket->ip_contact_reference && $only != 'ip') {
                     $selection[$ticket->domain_contact_reference]['domain'][] = $ticket;
                 }
             } else {
                 /*
                  * Notifications are forced, therefor we skip all the checks except empty/undef
                  */
                 // Conditions just for IP contacts
                 if (!empty($ticket->ip_contact_reference) && $ticket->ip_contact_reference != 'UNDEF' && $only != 'domain') {
                     $selection[$ticket->ip_contact_reference]['ip'][] = $ticket;
                 }
                 // Conditions just for Domain contacts
                 if (!empty($ticket->domain_contact_reference) && $ticket->domain_contact_reference != 'UNDEF' && $ticket->domain_contact_reference != $ticket->ip_contact_reference && $only != 'ip') {
                     $selection[$ticket->domain_contact_reference]['domain'][] = $ticket;
                 }
             }
         }
     }
     return $selection;
 }
示例#12
0
 /**
  * Walk thru all tickets to see which need closing.
  *
  * @return bool
  */
 private function ticketsClosing()
 {
     Log::info(get_class($this) . ': Housekeeper is starting to close old tickets');
     $closeOlderThen = strtotime(config('main.housekeeping.tickets_close_after') . ' ago');
     $validator = Validator::make(['mailarchive_remove_after' => $closeOlderThen], ['mailarchive_remove_after' => 'required|timestamp']);
     if ($validator->fails()) {
         $messages = $validator->messages();
         $message = '';
         foreach ($messages->all() as $messagePart) {
             $message .= $messagePart . PHP_EOL;
         }
         echo $message;
         return false;
     } else {
         $tickets = Ticket::where('status_id', '!=', 'CLOSED')->get();
         foreach ($tickets as $ticket) {
             if ($ticket->lastEvent[0]->timestamp <= $closeOlderThen && strtotime($ticket->created_at) <= $closeOlderThen) {
                 $ticket->update(['status_id' => 'CLOSED']);
             }
         }
     }
     return true;
 }
示例#13
0
 /**
  * {@inheritdoc}.
  */
 protected function findAll()
 {
     return Ticket::all();
 }
示例#14
0
 /**
  * Execute the command.
  *
  * @param array $events
  * @param integer $evidenceID
  * @return array
  */
 public function save($events, $evidenceID)
 {
     $ticketCount = 0;
     $eventCount = 0;
     $eventsIgnored = 0;
     foreach ($events as $event) {
         /* Here we will seek through all the events and look if there is an existing ticket. We will split them up
          * into two seperate arrays: $eventsNew and $events$known. We can save all the known events in the DB with
          * a single event saving loads of queries
          *
          * IP Owner is leading, as in most cases even if the domain is moved
          * The server might still have a problem. Next to the fact that domains
          * Arent transferred to a new owner 'internally' anyways.
          *
          * So we do a lookup based on the IP same as with the 3.x engine. After
          * the lookup we check wither the domain contact was changed, if so we UPDATE
          * the ticket and put a note somewhere about it. This way the IP owner does
          * not get a new ticket on this matter and the new domain owner is getting updates.
          *
          * As the ASH link is based on the contact code, the old domain owner will not
          * have any access to the ticket anymore.
          */
         // If an event is too old we are ignoring it
         if (config('main.reports.min_lastseen') !== false && strtotime(config('main.reports.min_lastseen')) !== false && strtotime(config('main.reports.min_lastseen') . ' ago') > $event['timestamp']) {
             Log::debug(get_class($this) . ': ' . "is ignoring event because its older then " . config('main.reports.min_lastseen'));
             continue;
         }
         // Start with building a classification lookup table  and switch out name for ID
         foreach ((array) Lang::get('classifications') as $classID => $class) {
             if ($class['name'] == $event['class']) {
                 $event['class'] = $classID;
             }
         }
         // Also build a types lookup table and switch out name for ID
         foreach ((array) Lang::get('types.type') as $typeID => $type) {
             if ($type['name'] == $event['type']) {
                 $event['type'] = $typeID;
             }
         }
         // Lookup the ip contact and if needed the domain contact too
         $findContact = new FindContact();
         $ipContact = $findContact->byIP($event['ip']);
         if ($event['domain'] != '') {
             $domainContact = $findContact->byDomain($event['domain']);
         } else {
             $domainContact = $findContact->undefined();
         }
         /*
          * Ignore the event if both ip and domain contacts are undefined and the resolving of an contact
          * was required. This is handy to ignore any reports that are not considered local, but use
          * with caution as it might just ignore anything if your IP/domains are not correctly configured
          */
         if ($ipContact->reference == 'UNDEF' && $domainContact->reference == 'UNDEF' && config('main.reports.resolvable_only') === true) {
             if (!empty($domainContact) && $domainContact->reference == 'UNDEF' || empty($domainContact)) {
                 Log::debug(get_class($this) . ': ' . "is ignoring event because there is no IP or Domain contact");
                 continue;
             }
         }
         /*
          * Search to see if there is an existing ticket for this event classification
          */
         $ticket = Ticket::where('ip', '=', $event['ip'])->where('class_id', '=', $event['class'], 'AND')->where('type_id', '=', $event['type'], 'AND')->where('ip_contact_reference', '=', $ipContact->reference, 'AND')->where('status_id', '!=', 2, 'AND')->get();
         if ($ticket->count() === 0) {
             /*
              * If there are no search results then there is no existing ticket and we should create one
              */
             $ticketCount++;
             $newTicket = new Ticket();
             $newTicket->ip = $event['ip'];
             $newTicket->domain = empty($event['domain']) ? '' : $event['domain'];
             $newTicket->class_id = $event['class'];
             $newTicket->type_id = $event['type'];
             $newTicket->ip_contact_account_id = $ipContact->account_id;
             $newTicket->ip_contact_reference = $ipContact->reference;
             $newTicket->ip_contact_name = $ipContact->name;
             $newTicket->ip_contact_email = $ipContact->email;
             $newTicket->ip_contact_api_host = $ipContact->api_host;
             $newTicket->ip_contact_api_key = $ipContact->api_key;
             $newTicket->ip_contact_auto_notify = $ipContact->auto_notify;
             $newTicket->ip_contact_notified_count = 0;
             $newTicket->domain_contact_account_id = $domainContact->account_id;
             $newTicket->domain_contact_reference = $domainContact->reference;
             $newTicket->domain_contact_name = $domainContact->name;
             $newTicket->domain_contact_email = $domainContact->email;
             $newTicket->domain_contact_api_host = $domainContact->api_host;
             $newTicket->domain_contact_api_key = $domainContact->api_key;
             $newTicket->domain_contact_auto_notify = $domainContact->auto_notify;
             $newTicket->domain_contact_notified_count = 0;
             $newTicket->status_id = 1;
             $newTicket->last_notify_count = 0;
             $newTicket->last_notify_timestamp = 0;
             $newTicket->save();
             $newEvent = new Event();
             $newEvent->evidence_id = $evidenceID;
             $newEvent->information = $event['information'];
             $newEvent->source = $event['source'];
             $newEvent->ticket_id = $newTicket->id;
             $newEvent->timestamp = $event['timestamp'];
             $newEvent->save();
         } elseif ($ticket->count() === 1) {
             /*
              * There is an existing ticket, so we just need to add the event to this ticket. If the event is an
              * exact match we consider it a duplicate and will ignore it.
              */
             $ticket = $ticket[0];
             if (Event::where('information', '=', $event['information'])->where('source', '=', $event['source'])->where('ticket_id', '=', $ticket->id)->where('timestamp', '=', $event['timestamp'])->exists()) {
                 $eventsIgnored++;
             } else {
                 // New unique event, so we will save this
                 $eventCount++;
                 $newEvent = new Event();
                 $newEvent->evidence_id = $evidenceID;
                 $newEvent->information = $event['information'];
                 $newEvent->source = $event['source'];
                 $newEvent->ticket_id = $ticket->id;
                 $newEvent->timestamp = $event['timestamp'];
                 $newEvent->save();
                 /*
                  * If the reference has changed for the domain owner, then we update the ticket with the new
                  * domain owner. We not check if anything else then the reference has changed. If you change the
                  * contact data you have the option to propogate it onto open tickets.
                  */
                 if (!empty($event['domain']) && $domainContact !== false && $domainContact->reference !== $ticket->domain_contact_reference) {
                     $ticket->domain_contact_reference = $domainContact->reference;
                     $ticket->domain_contact_name = $domainContact->name;
                     $ticket->domain_contact_email = $domainContact->email;
                     $ticket->domain_contact_api_host = $domainContact->api_host;
                     $ticket->domain_contact_api_key = $domainContact->api_key;
                     $ticket->domain_contact_auto_notify = $domainContact->auto_notify;
                     $ticket->account_id = $domainContact->account->id;
                     $ticket->save();
                 }
                 // TODO: If this is an abuse/escalation ticket and currently 'resolved' then put status back to Open
                 // TODO: Implement escalation triggers
             }
         } else {
             /*
              * We should not never have more then two open tickets for the same case. If this happens there is a
              * fault in the aggregator which must be resolved first. Until then we will permfail here.
              */
             $this->failed('Unable to link to ticket, multiple open tickets found for same event type');
         }
     }
     Log::debug(get_class($this) . ': ' . "has completed creating {$ticketCount} new tickets, " . "linking {$eventCount} new events and ignored {$eventsIgnored} duplicates");
     $this->success('');
 }
示例#15
0
 /**
  * Execute the command.
  *
  * @return array
  */
 public function handle()
 {
     // Start with building a classification lookup table
     $classNames = [];
     foreach (Lang::get('classifications') as $classID => $class) {
         $classNames[$class['name']] = $classID;
     }
     // Also build a types lookup table
     $typeNames = [];
     foreach (Lang::get('types.type') as $typeID => $type) {
         $typeNames[$type['name']] = $typeID;
     }
     // Also build a status lookup table
     $statusNames = [];
     foreach (Lang::get('types.status') as $statusID => $status) {
         $statusNames[$status['name']] = $statusID;
     }
     foreach ($this->events as $event) {
         /* Here we will thru all the events and look if these is an existing ticket. We will split them up into
          * two seperate arrays: $eventsNew and $events$known. We can save all the known events in the DB with
          * a single event saving loads of queries
          *
          * IP Owner is leading, as in most cases even if the domain is moved
          * The server might still have a problem. Next to the fact that domains
          * Arent transferred to a new owner 'internally' anyways.
          *
          * So we do a lookup based on the IP same as with the 3.x engine. After
          * the lookup we check wither the domain contact was changed, if so we UPDATE
          * the ticket and put a note somewhere about it. This way the IP owner does
          * not get a new ticket on this matter and the new domain owner is getting updates.
          *
          * As the ASH link is based on the contact code, the old domain owner will not
          * have any access to the ticket anymore.
          */
         // Lookup the ip contact and if needed the domain contact too
         $ipContact = FindContact::byIP($event['ip']);
         if ($event['domain'] != '') {
             $domainContact = FindContact::byDomain($event['domain']);
         }
         // Search to see if there is an existing ticket for this event classification
         // Todo: somehow add the domain too!!!!
         $search = Ticket::where('ip', '=', $event['ip'])->where('class_id', '=', $classNames[$event['class']], 'AND')->where('type_id', '=', $typeNames[$event['type']], 'AND')->where('ip_contact_reference', '=', $ipContact->reference, 'AND')->where('status_id', '!=', 2, 'AND')->get();
         if ($search->count() === 0) {
             // Build an array with all new tickes and save it with its related event and evidence link.
             $newTicket = new Ticket();
             $newTicket->ip = $event['ip'];
             $newTicket->domain = $event['domain'];
             $newTicket->class_id = $classNames[$event['class']];
             $newTicket->type_id = $typeNames[$event['type']];
             $newTicket->ip_contact_reference = $ipContact->reference;
             $newTicket->ip_contact_name = $ipContact->name;
             $newTicket->ip_contact_email = $ipContact->email;
             $newTicket->ip_contact_rpchost = $ipContact->rpc_host;
             $newTicket->ip_contact_rpckey = $ipContact->rpc_key;
             $newTicket->ip_contact_auto_notify = $ipContact->auto_notify;
             if ($event['domain'] != '') {
                 $newTicket->domain_contact_reference = $domainContact->reference;
                 $newTicket->domain_contact_name = $domainContact->name;
                 $newTicket->domain_contact_email = $domainContact->email;
                 $newTicket->domain_contact_rpchost = $domainContact->rpc_host;
                 $newTicket->domain_contact_rpckey = $domainContact->rpc_key;
                 $newTicket->domain_contact_auto_notify = $domainContact->auto_notify;
             }
             $newTicket->status_id = 1;
             $newTicket->notified_count = 0;
             $newTicket->last_notify_count = 0;
             $newTicket->last_notify_timestamp = 0;
             $newTicket->save();
             $newEvent = new Event();
             $newEvent->evidence_id = $this->evidenceID;
             $newEvent->information = $event['information'];
             $newEvent->source = $event['source'];
             $newEvent->ticket_id = $newTicket->id;
             $newEvent->timestamp = $event['timestamp'];
             $newEvent->save();
             // Call notifier action handler, type new
         } elseif ($search->count() === 1) {
             $ticketID = $search[0]->id;
             if (Event::where('information', '=', $event['information'])->where('source', '=', $event['source'])->where('ticket_id', '=', $ticketID)->where('timestamp', '=', $event['timestamp'])->exists()) {
                 // Exact duplicate match so we will ignore this event
             } else {
                 // New unique event, so we will save this
                 $newEvent = new Event();
                 $newEvent->evidence_id = $this->evidenceID;
                 $newEvent->information = $event['information'];
                 $newEvent->source = $event['source'];
                 $newEvent->ticket_id = $ticketID;
                 $newEvent->timestamp = $event['timestamp'];
                 $newEvent->save();
                 // Call notifier action handler, type update
             }
             // This is an existing ticket
         } else {
             $this->failed('Unable to link to ticket, multiple open tickets found for same event type');
         }
     }
     $this->success('');
 }
示例#16
0
 /**
  * Execute the command.
  *
  * @param array $incidents
  * @param int   $evidenceID
  *
  * @return array
  */
 public function save($incidents, $evidenceID)
 {
     $ticketCount = 0;
     $incidentCount = 0;
     $incidentsIgnored = 0;
     foreach ($incidents as $incident) {
         /* Here we will seek through all the incidents and look if there is an existing ticket. We will split
          * them up into two seperate arrays: $incidentsNew and $incidents$known. We can save all the known
          * incidents in the DB with a single incident saving loads of queries
          *
          * IP Owner is leading, as in most cases even if the domain is moved
          * The server might still have a problem. Next to the fact that domains
          * Arent transferred to a new owner 'internally' anyways.
          *
          * So we do a lookup based on the IP same as with the 3.x engine. After
          * the lookup we check wither the domain contact was changed, if so we UPDATE
          * the ticket and put a note somewhere about it. This way the IP owner does
          * not get a new ticket on this matter and the new domain owner is getting updates.
          *
          * As the ASH link is based on the contact code, the old domain owner will not
          * have any access to the ticket anymore.
          */
         // If an incident is too old we are ignoring it
         if (config('main.reports.min_lastseen') !== false && strtotime(config('main.reports.min_lastseen')) !== false && strtotime(config('main.reports.min_lastseen') . ' ago') > $incident->timestamp) {
             Log::debug(get_class($this) . ': ' . 'is ignoring incident because its older then ' . config('main.reports.min_lastseen'));
             continue;
         }
         // Lookup the ip contact and if needed the domain contact too
         $findContact = new FindContact();
         $ipContact = $findContact->byIP($incident->ip);
         if ($incident->domain != '') {
             $domainContact = $findContact->byDomain($incident->domain);
         } else {
             $domainContact = $findContact->undefined();
         }
         /*
          * Ignore the incident if both ip and domain contacts are undefined and the resolving of an contact
          * was required. This is handy to ignore any reports that are not considered local, but use
          * with caution as it might just ignore anything if your IP/domains are not correctly configured
          */
         if ($ipContact->reference == 'UNDEF' && $domainContact->reference == 'UNDEF' && config('main.reports.resolvable_only') === true) {
             if (!empty($domainContact) && $domainContact->reference == 'UNDEF' || empty($domainContact)) {
                 Log::debug(get_class($this) . ': ' . 'is ignoring incident because there is no IP or Domain contact');
                 continue;
             }
         }
         /*
          * Search to see if there is an existing ticket for this incident classification
          */
         $ticket = Ticket::where('ip', '=', $incident->ip)->where('class_id', '=', $incident->class, 'AND')->where('ip_contact_reference', '=', $ipContact->reference, 'AND')->where('status_id', '!=', 'CLOSED', 'AND')->get();
         if ($ticket->count() === 0) {
             /*
              * If there are no search results then there is no existing ticket and we should create one
              */
             $ticketCount++;
             $newTicket = new Ticket();
             $newTicket->ip = $incident->ip;
             $newTicket->domain = empty($incident->domain) ? '' : $incident->domain;
             $newTicket->class_id = $incident->class;
             $newTicket->type_id = $incident->type;
             $newTicket->ip_contact_account_id = $ipContact->account_id;
             $newTicket->ip_contact_reference = $ipContact->reference;
             $newTicket->ip_contact_name = $ipContact->name;
             $newTicket->ip_contact_email = $ipContact->email;
             $newTicket->ip_contact_api_host = $ipContact->api_host;
             $newTicket->ip_contact_auto_notify = $ipContact->auto_notify;
             $newTicket->ip_contact_notified_count = 0;
             $newTicket->domain_contact_account_id = $domainContact->account_id;
             $newTicket->domain_contact_reference = $domainContact->reference;
             $newTicket->domain_contact_name = $domainContact->name;
             $newTicket->domain_contact_email = $domainContact->email;
             $newTicket->domain_contact_api_host = $domainContact->api_host;
             $newTicket->domain_contact_auto_notify = $domainContact->auto_notify;
             $newTicket->domain_contact_notified_count = 0;
             $newTicket->status_id = 'OPEN';
             $newTicket->last_notify_count = 0;
             $newTicket->last_notify_timestamp = 0;
             // Validate the model before saving
             $validator = Validator::make(json_decode(json_encode($newTicket), true), Ticket::createRules());
             if ($validator->fails()) {
                 return $this->error('DevError: Internal validation failed when saving the Ticket object ' . implode(' ', $validator->messages()->all()));
             }
             $newTicket->save();
             $newEvent = new Event();
             $newEvent->evidence_id = $evidenceID;
             $newEvent->information = $incident->information;
             $newEvent->source = $incident->source;
             $newEvent->ticket_id = $newTicket->id;
             $newEvent->timestamp = $incident->timestamp;
             // Validate the model before saving
             $validator = Validator::make(json_decode(json_encode($newEvent), true), Event::createRules());
             if ($validator->fails()) {
                 return $this->error('DevError: Internal validation failed when saving the Event object ' . implode(' ', $validator->messages()->all()));
             }
             $newEvent->save();
         } elseif ($ticket->count() === 1) {
             /*
              * There is an existing ticket, so we just need to add the incident to this ticket. If the
              * incident is an exact match we consider it a duplicate and will ignore it.
              */
             $ticket = $ticket[0];
             if (Event::where('information', '=', $incident->information)->where('source', '=', $incident->source)->where('ticket_id', '=', $ticket->id)->where('timestamp', '=', $incident->timestamp)->exists()) {
                 $incidentsIgnored++;
             } else {
                 // New unique incident, so we will save this
                 $incidentCount++;
                 $newEvent = new Event();
                 $newEvent->evidence_id = $evidenceID;
                 $newEvent->information = $incident->information;
                 $newEvent->source = $incident->source;
                 $newEvent->ticket_id = $ticket->id;
                 $newEvent->timestamp = $incident->timestamp;
                 // Validate the model before saving
                 $validator = Validator::make(json_decode(json_encode($newEvent), true), Event::createRules());
                 if ($validator->fails()) {
                     return $this->error('DevError: Internal validation failed when saving the Event object ' . implode(' ', $validator->messages()->all()));
                 }
                 $newEvent->save();
                 /*
                  * If the reference has changed for the domain owner, then we update the ticket with the new
                  * domain owner. We not check if anything else then the reference has changed. If you change the
                  * contact data you have the option to propogate it onto open tickets.
                  */
                 if (!empty($incident->domain) && $domainContact !== false && $domainContact->reference !== $ticket->domain_contact_reference) {
                     $ticket->domain_contact_reference = $domainContact->reference;
                     $ticket->domain_contact_name = $domainContact->name;
                     $ticket->domain_contact_email = $domainContact->email;
                     $ticket->domain_contact_api_host = $domainContact->api_host;
                     $ticket->domain_contact_auto_notify = $domainContact->auto_notify;
                     $ticket->account_id = $domainContact->account->id;
                 }
                 /*
                  * Upgrade the type if the received event has a higher priority type included
                  */
                 $priority = ['INFO', 'ABUSE', 'ESCALATION'];
                 if (array_search($ticket->type_id, $priority) < array_search($incident->type, $priority)) {
                     $ticket->type_id = $incident->type;
                 }
                 /*
                  * If the ticket was set to resolved, move it back to open
                  */
                 if ($ticket->status_id == 'RESOLVED') {
                     $ticket->status_id = 'OPEN';
                 }
                 /*
                  * Walk thru the escalation upgrade path, and upgrade if required
                  */
                 //echo config("escalations.{$ticket->class_id}.abuse.enabled");
                 if (is_array(config("escalations.{$ticket->class_id}"))) {
                     // There is a specific escalation path for this class
                     $escalationPath = $ticket->class_id;
                 } else {
                     // Use the default escalation path
                     $escalationPath = 'DEFAULT';
                 }
                 // Check if all the values are set, or log a warning that were skipping escalation paths
                 if (!is_bool(empty(config("escalations.{$escalationPath}.abuse.enabled"))) || !is_numeric(config("escalations.{$escalationPath}.abuse.threshold")) || !is_bool(empty(config("escalations.{$escalationPath}.escalation.enabled"))) || !is_numeric(config("escalations.{$escalationPath}.escalation.threshold"))) {
                     Log::warning(get_class($this) . ': ' . 'Escalation path settings are missing or incomplete. Skipping this phase');
                 } else {
                     // Now actually check if anything needs to be changed and if so, change it.
                     if (!empty(config("escalations.{$escalationPath}.abuse.enabled")) && !empty(config("escalations.{$escalationPath}.abuse.threshold")) && $ticket->events->count() > config("escalations.{$escalationPath}.abuse.threshold") && $ticket->type_id == 'INFO') {
                         // Upgrade to abuse
                         Log::debug(get_class($this) . ': ' . "An escalation path threshold has been reached for ticket {$ticket->id}, " . 'threshold: ' . config("escalations.{$escalationPath}.abuse.threshold") . ', ' . 'setting: info -> abuse');
                         $ticket->type_id = 'ABUSE';
                     }
                     if (!empty(config("escalations.{$escalationPath}.escalation.enabled")) && !empty(config("escalations.{$escalationPath}.escalation.threshold")) && $ticket->events->count() > config("escalations.{$escalationPath}.escalation.threshold") && $ticket->type_id == 'ABUSE') {
                         // Upgrade to escalation
                         Log::debug(get_class($this) . ': ' . "An escalation path threshold has been reached for ticket {$ticket->id}, " . 'threshold: ' . config("escalations.{$escalationPath}.escalation.threshold") . ', ' . 'setting: abuse -> escalation');
                         $ticket->type_id = 'ESCALATION';
                     }
                 }
                 // Validate the model before saving
                 $validator = Validator::make(json_decode(json_encode($ticket), true), Ticket::createRules());
                 if ($validator->fails()) {
                     return $this->error('DevError: Internal validation failed when saving the Ticket object ' . implode(' ', $validator->messages()->all()));
                 }
                 $ticket->save();
             }
         } else {
             /*
              * We should not never have more then two open tickets for the same case. If this happens there is a
              * fault in the aggregator which must be resolved first. Until then we will permfail here.
              */
             $this->error('Unable to link to ticket, multiple open tickets found for same incident type');
         }
     }
     Log::debug(get_class($this) . ': ' . "has completed creating {$ticketCount} new tickets, " . "linking {$incidentCount} new incidents and ignored {$incidentsIgnored} duplicates");
     return $this->success('');
 }
示例#17
0
 /**
  * Export tickets to CSV format.
  *
  * @param string $format
  *
  * @return \Illuminate\Http\Response
  */
 public function export($format)
 {
     // TODO #AIO-?? ExportProvider - (mark) Move this into an ExportProvider or something?
     // only export all tickets when we are in the systemaccount
     $auth_account = $this->auth_user->account;
     if ($auth_account->isSystemAccount()) {
         $tickets = Ticket::all();
     } else {
         $tickets = Ticket::select('tickets.*')->where('ip_contact_account_id', $auth_account->id)->orWhere('domain_contact_account_id', $auth_account);
     }
     if ($format === 'csv') {
         $columns = ['id' => 'Ticket ID', 'ip' => 'IP address', 'class_id' => 'Classification', 'type_id' => 'Type', 'first_seen' => 'First seen', 'last_seen' => 'Last seen', 'event_count' => 'Events', 'status_id' => 'Ticket Status'];
         $output = '"' . implode('", "', $columns) . '"' . PHP_EOL;
         foreach ($tickets as $ticket) {
             $row = [$ticket->id, $ticket->ip, trans("classifications.{$ticket->class_id}.name"), trans("types.type.{$ticket->type_id}.name"), $ticket->firstEvent[0]->seen, $ticket->lastEvent[0]->seen, $ticket->events->count(), trans("types.status.abusedesk.{$ticket->status_id}.name")];
             $output .= '"' . implode('", "', $row) . '"' . PHP_EOL;
         }
         return response(substr($output, 0, -1), 200)->header('Content-Type', 'text/csv')->header('Content-Disposition', 'attachment; filename="Tickets.csv"');
     }
     return Redirect::route('admin.contacts.index')->with('message', "The requested format {$format} is not available for exports");
 }
示例#18
0
 /**
  * {@inheritdoc}.
  */
 protected function getObjectByArguments()
 {
     return Ticket::find($this->argument('id'));
 }
示例#19
0
 /**
  * Walk thru all tickets to see which need closing
  *
  * @return boolean
  */
 private function ticketsClosing()
 {
     $closeOlderThen = strtotime(config('main.housekeeping.tickets_close_after') . " ago");
     $validator = Validator::make(['mailarchive_remove_after' => $closeOlderThen], ['mailarchive_remove_after' => 'required|timestamp']);
     if ($validator->fails()) {
         $messages = $validator->messages();
         $message = '';
         foreach ($messages->all() as $messagePart) {
             $message .= $messagePart . PHP_EOL;
         }
         echo $message;
         return false;
     } else {
         $tickets = Ticket::where('status_id', '!=', '2')->get();
         foreach ($tickets as $ticket) {
             if ($ticket->lastEvent[0]->timestamp <= $closeOlderThen) {
                 $ticket->update(['status_id' => 2]);
             }
         }
     }
     return true;
 }
示例#20
0
 /**
  * @param $ticket
  * @param $account
  *
  * @return Ticket
  */
 private function createTicket($ticket, $account)
 {
     // Start with building a classification lookup table  and switch out name for ID
     // But first fix the names:
     $replaces = ['Possible DDOS sending NTP Server' => 'Possible DDoS sending Server', 'Possible DDOS sending DNS Server' => 'Possible DDoS sending Server'];
     $old = array_keys($replaces);
     $new = array_values($replaces);
     $ticket->Class = str_replace($old, $new, $ticket->Class);
     foreach ((array) Lang::get('classifications') as $classID => $class) {
         if ($class['name'] == $ticket->Class) {
             $ticket->Class = $classID;
         }
     }
     // Also build a types lookup table and switch out name for ID
     foreach ((array) Lang::get('types.type') as $typeID => $type) {
         // Consistancy fixes:
         $ticket->Type = strtoupper($ticket->Type);
         if ($type['name'] == $ticket->Type) {
             $ticket->Type = $typeID;
         }
     }
     // Create the ticket
     $newTicket = new Ticket();
     $newTicket->id = $ticket->ID;
     $newTicket->ip = $ticket->IP;
     $newTicket->domain = empty($ticket->Domain) ? '' : $ticket->Domain;
     $newTicket->class_id = $ticket->Class;
     $newTicket->type_id = $ticket->Type;
     $newTicket->ip_contact_account_id = $account->id;
     $newTicket->ip_contact_reference = $ticket->CustomerCode;
     $newTicket->ip_contact_name = $ticket->CustomerName;
     $newTicket->ip_contact_email = $ticket->CustomerContact;
     $newTicket->ip_contact_api_host = '';
     $newTicket->ip_contact_auto_notify = $ticket->AutoNotify;
     $newTicket->ip_contact_notified_count = $ticket->NotifiedCount;
     $domainContact = FindContact::undefined();
     $newTicket->domain_contact_account_id = $domainContact->account_id;
     $newTicket->domain_contact_reference = $domainContact->reference;
     $newTicket->domain_contact_name = $domainContact->name;
     $newTicket->domain_contact_email = $domainContact->email;
     $newTicket->domain_contact_api_host = $domainContact->api_host;
     $newTicket->domain_contact_auto_notify = $domainContact->auto_notify;
     $newTicket->domain_contact_notified_count = 0;
     $newTicket->last_notify_count = $ticket->LastNotifyReportCount;
     $newTicket->last_notify_timestamp = $ticket->LastNotifyTimestamp;
     $newTicket->created_at = Carbon::createFromTimestamp($ticket->FirstSeen);
     $newTicket->updated_at = Carbon::parse($ticket->LastModified);
     if ($ticket->Status == 'CLOSED') {
         $newTicket->status_id = 'CLOSED';
     } elseif ($ticket->Status == 'OPEN') {
         $newTicket->status_id = 'OPEN';
     } else {
         $this->error('Unknown ticket status');
         $this->exception();
     }
     if ($ticket->CustomerResolved == 1) {
         $newTicket->contact_status_id = 'RESOLVED';
     } elseif ($ticket->CustomerIgnored == 1) {
         $newTicket->contact_status_id = 'IGNORED';
     } else {
         $newTicket->contact_status_id = 'OPEN';
     }
     // Validate the model before saving
     $validator = Validator::make(json_decode(json_encode($newTicket), true), Ticket::createRules());
     if ($validator->fails()) {
         $this->error('DevError: Internal validation failed when saving the Ticket object ' . implode(' ', $validator->messages()->all()));
         var_dump($ticket);
         $this->exception();
     }
     $newTicket->save();
     return $newTicket;
 }
示例#21
0
 /**
  * Execute the command.
  * @return array
  */
 public function handle()
 {
     $ticketCount = 0;
     $eventCount = 0;
     $eventsIgnored = 0;
     foreach ($this->events as $event) {
         /* Here we will thru all the events and look if these is an existing ticket. We will split them up into
          * two seperate arrays: $eventsNew and $events$known. We can save all the known events in the DB with
          * a single event saving loads of queries
          *
          * IP Owner is leading, as in most cases even if the domain is moved
          * The server might still have a problem. Next to the fact that domains
          * Arent transferred to a new owner 'internally' anyways.
          *
          * So we do a lookup based on the IP same as with the 3.x engine. After
          * the lookup we check wither the domain contact was changed, if so we UPDATE
          * the ticket and put a note somewhere about it. This way the IP owner does
          * not get a new ticket on this matter and the new domain owner is getting updates.
          *
          * As the ASH link is based on the contact code, the old domain owner will not
          * have any access to the ticket anymore.
          */
         // Start with building a classification lookup table  and switch out name for ID
         foreach ((array) Lang::get('classifications') as $classID => $class) {
             if ($class['name'] == $event['class']) {
                 $event['class'] = $classID;
             }
         }
         // Also build a types lookup table and switch out name for ID
         foreach ((array) Lang::get('types.type') as $typeID => $type) {
             if ($type['name'] == $event['type']) {
                 $event['type'] = $typeID;
             }
         }
         // Lookup the ip contact and if needed the domain contact too
         $ipContact = FindContact::byIP($event['ip']);
         if ($event['domain'] != '') {
             $domainContact = FindContact::byDomain($event['domain']);
         } else {
             $domainContact = false;
         }
         /*
          * Search to see if there is an existing ticket for this event classification
          */
         $search = Ticket::where('ip', '=', $event['ip'])->where('class_id', '=', $event['class'], 'AND')->where('type_id', '=', $event['type'], 'AND')->where('ip_contact_reference', '=', $ipContact->reference, 'AND')->where('status_id', '!=', 2, 'AND')->get();
         if ($search->count() === 0) {
             /*
              * If there are no search results then there is no existing ticket and we should create one
              */
             $ticketCount++;
             $newTicket = new Ticket();
             $newTicket->ip = $event['ip'];
             $newTicket->domain = $event['domain'];
             $newTicket->class_id = $event['class'];
             $newTicket->type_id = $event['type'];
             $newTicket->ip_contact_reference = $ipContact->reference;
             $newTicket->ip_contact_name = $ipContact->name;
             $newTicket->ip_contact_email = $ipContact->email;
             $newTicket->ip_contact_rpchost = $ipContact->rpc_host;
             $newTicket->ip_contact_rpckey = $ipContact->rpc_key;
             $newTicket->ip_contact_auto_notify = $ipContact->auto_notify;
             if (!empty($event['domain']) && $domainContact !== false) {
                 $newTicket->domain_contact_reference = $domainContact->reference;
                 $newTicket->domain_contact_name = $domainContact->name;
                 $newTicket->domain_contact_email = $domainContact->email;
                 $newTicket->domain_contact_rpchost = $domainContact->rpc_host;
                 $newTicket->domain_contact_rpckey = $domainContact->rpc_key;
                 $newTicket->domain_contact_auto_notify = $domainContact->auto_notify;
             }
             $newTicket->status_id = 1;
             $newTicket->notified_count = 0;
             $newTicket->last_notify_count = 0;
             $newTicket->last_notify_timestamp = 0;
             $newTicket->save();
             $newEvent = new Event();
             $newEvent->evidence_id = $this->evidenceID;
             $newEvent->information = $event['information'];
             $newEvent->source = $event['source'];
             $newEvent->ticket_id = $newTicket->id;
             $newEvent->timestamp = $event['timestamp'];
             $newEvent->save();
             // TODO - Call notifier action handler, type new
         } elseif ($search->count() === 1) {
             /*
              * There is an existing ticket, so we just need to add the event to this ticket. If the event is an
              * exact match we consider it a duplicate and will ignore it.
              */
             $ticketID = $search[0]->id;
             if (Event::where('information', '=', $event['information'])->where('source', '=', $event['source'])->where('ticket_id', '=', $ticketID)->where('timestamp', '=', $event['timestamp'])->exists()) {
                 $eventsIgnored++;
             } else {
                 // New unique event, so we will save this
                 $eventCount++;
                 $newEvent = new Event();
                 $newEvent->evidence_id = $this->evidenceID;
                 $newEvent->information = $event['information'];
                 $newEvent->source = $event['source'];
                 $newEvent->ticket_id = $ticketID;
                 $newEvent->timestamp = $event['timestamp'];
                 $newEvent->save();
                 // TODO - Update domain owner if changed based on the contactID (reference)
                 // TODO - Call notifier action handler, type update
             }
         } else {
             /*
              * We should not never have more then two open tickets for the same case. If this happens there is a
              * fault in the aggregator which must be resolved first. Until then we will permfail here.
              */
             $this->failed('Unable to link to ticket, multiple open tickets found for same event type');
         }
     }
     Log::debug('(JOB ' . getmypid() . ') ' . get_class($this) . ': ' . "has completed creating {$ticketCount} new tickets, " . "linking {$eventCount} new events and ignored {$eventsIgnored} duplicates");
     $this->success('');
 }
示例#22
0
 /**
  * {@inherit docs}.
  */
 protected function getCollectionWithArguments()
 {
     return Ticket::where('id', $this->argument('ticket'));
 }
示例#23
0
 /**
  * Retrieve a list of addresses based on open tickets and kick off scanAddress
  *
  * @return boolean
  */
 private function scanTickets()
 {
     $tickets = Ticket::where('status_id', '!=', '2')->get();
     foreach ($tickets as $ticket) {
         $this->scanAddress($ticket->ip);
     }
     return true;
 }