/** * Execute the command * * @return void */ public function handle() { Log::info('(JOB ' . getmypid() . ') ' . get_class($this) . ': ' . 'Queued worker is starting the processing of email file: ' . $this->filename); $filesystem = new Filesystem(); $rawEmail = $filesystem->get($this->filename); $parsedMail = new MimeParser(); $parsedMail->setText($rawEmail); // Sanity checks if (empty($parsedMail->getHeader('from')) || empty($parsedMail->getMessageBody())) { Log::warning('(JOB ' . getmypid() . ') ' . get_class($this) . ': ' . 'Missing e-mail headers from and/or empty body: ' . $this->filename); $this->alertAdmin(); return; } // Ignore email from our own notification address to prevent mail loops if (preg_match('/' . Config::get('main.notifications.from_address') . '/', $parsedMail->getHeader('from'))) { Log::warning('(JOB ' . getmypid() . ') ' . get_class($this) . ': ' . 'Loop prevention: Ignoring email from self ' . Config::get('main.notifications.from_address')); $this->alertAdmin(); return; } // Start with detecting valid ARF e-mail $attachments = $parsedMail->getAttachments(); $arfMail = []; foreach ($attachments as $attachment) { if ($attachment->contentType == 'message/feedback-report') { $arfMail['report'] = $attachment->getContent(); } if ($attachment->contentType == 'message/rfc822') { $arfMail['evidence'] = utf8_encode($attachment->getContent()); } if ($attachment->contentType == 'text/plain') { $arfMail['message'] = $attachment->getContent(); } } /* * Sometimes the mime header does not set the main message correctly. This is ment as a fallback and will * use the original content body (which is basicly the same mime element). But only fallback if we actually * have a RFC822 message with a feedback report. */ if (empty($arfMail['message']) && isset($arfMail['report']) && isset($arfMail['evidence'])) { $arfMail['message'] = $parsedMail->getMessageBody(); } // If we do not have a complete e-mail, then we empty the perhaps partially filled arfMail // which is useless, hence reset to false if (!isset($arfMail['report']) || !isset($arfMail['evidence']) || !isset($arfMail['message'])) { $arfMail = false; } // Asking ParserFactory for an object based on mappings, or die trying $parser = ParserFactory::create($parsedMail, $arfMail); if ($parser !== false) { $parserResult = $parser->parse(); } else { Log::error('(JOB ' . getmypid() . ') ' . get_class($this) . ': ' . ': No parser available to handle message from : ' . $parsedMail->getHeader('from') . ' with subject: ' . $parsedMail->getHeader('subject')); $this->alertAdmin(); return; } if ($parserResult !== false && $parserResult['errorStatus'] === true) { Log::error('(JOB ' . getmypid() . ') ' . get_class($parser) . ': ' . ': Parser has ended with fatal errors ! : ' . $parserResult['errorMessage']); $this->alertAdmin(); return; } else { Log::info('(JOB ' . getmypid() . ') ' . get_class($parser) . ': ' . ': Parser completed with ' . $parserResult['warningCount'] . ' warnings and collected ' . count($parserResult['data']) . ' events to save'); } if ($parserResult['warningCount'] !== 0 && Config::get('main.emailparser.notify_on_warnings') === true) { Log::error('(JOB ' . getmypid() . ') ' . get_class($this) . ': ' . 'Configuration has warnings set as critical and ' . $parserResult['warningCount'] . ' warnings were detected. Sending alert to administrator'); $this->alertAdmin(); return; } if (count($parserResult['data']) !== 0) { // Call validator $validator = new EventsValidate(); $validatorResult = $validator->check($parserResult['data']); if ($validatorResult['errorStatus'] === true) { Log::error('(JOB ' . getmypid() . ') ' . get_class($validator) . ': ' . 'Validator has ended with errors ! : ' . $validatorResult['errorMessage']); $this->alertAdmin(); return; } else { Log::info('(JOB ' . getmypid() . ') ' . get_class($validator) . ': ' . 'Validator has ended without errors'); } /** * save evidence into table **/ $evidence = new Evidence(); $evidence->filename = $this->filename; $evidence->sender = $parsedMail->getHeader('from'); $evidence->subject = $parsedMail->getHeader('subject'); $evidence->save(); /** * call saver **/ $saver = new EventsSave(); $saverResult = $saver->save($parserResult['data'], $evidence->id); /** * We've hit a snag, so we are gracefully killing ourselves * after we contact the admin about it. EventsSave should never * end with problems unless the mysql died while doing transactions **/ if ($saverResult['errorStatus'] === true) { Log::error('(JOB ' . getmypid() . ') ' . get_class($saver) . ': ' . 'Saver has ended with errors ! : ' . $saverResult['errorMessage']); $this->alertAdmin(); return; } else { Log::info('(JOB ' . getmypid() . ') ' . get_class($saver) . ': ' . 'Saver has ended without errors'); } } else { Log::warning('(JOB ' . getmypid() . ') ' . get_class($this) . ': ' . 'Parser did not return any events therefore skipping validation and saving a empty event set'); } Log::info('(JOB ' . getmypid() . ') ' . get_class($this) . ': ' . 'Queued worker has ended the processing of email file: ' . $this->filename); }
/** * Execute the command * * @return boolean */ public function handle() { Log::info(get_class($this) . ': ' . 'Queued worker is starting the collector: ' . $this->collector); $collector = collectorFactory::create($this->collector); if (!$collector) { Log::error("The requested collector {$this->collector} could not be started check logs for PID:" . getmypid()); $this->exception(); } $collectorResult = $collector->parse(); if ($collectorResult['errorStatus'] == true) { Log::error("The requested collector {$this->collector} returned an error. check logs for PID:" . getmypid()); $this->exception(); } if (count($collectorResult['data']) !== 0) { // Call validator $validator = new EventsValidate(); $validatorResult = $validator->check($collectorResult['data']); if ($validatorResult['errorStatus'] === true) { Log::error(get_class($validator) . ': ' . 'Validator has ended with errors ! : ' . $validatorResult['errorMessage']); $this->exception(); return; } else { Log::info(get_class($validator) . ': ' . 'Validator has ended without errors'); } /** * save evidence onto disk */ $filesystem = new Filesystem(); $datefolder = Carbon::now()->format('Ymd'); $path = storage_path() . '/mailarchive/' . $datefolder . '/'; $file = Uuid::generate(4) . '.eml'; $filename = $path . $file; if (!$filesystem->isDirectory($path)) { // If a datefolder does not exist, then create it or die trying if (!$filesystem->makeDirectory($path)) { Log::error(get_class($this) . ': ' . 'Unable to create directory: ' . $path); $this->exception(); } chown($path, 'abuseio'); chgrp($path, 'abuseio'); } if ($filesystem->isFile($filename)) { Log::error(get_class($this) . ': ' . 'File aready exists: ' . $filename); $this->exception(); } if ($filesystem->put($filename, json_encode(['collectorName' => $this->collector, 'collectorData' => $collectorResult])) === false) { Log::error(get_class($this) . ': ' . 'Unable to write file: ' . $filename); $this->exception(); } chown($path . $filename, 'abuseio'); chgrp($path . $filename, 'abuseio'); /** * save evidence into table **/ $evidence = new Evidence(); $evidence->filename = $filename; $evidence->sender = 'abuse@localhost'; $evidence->subject = "CLI Collector {$this->collector}"; $evidence->save(); /** * call saver **/ $saver = new EventsSave(); $saverResult = $saver->save($collectorResult['data'], $evidence->id); /** * We've hit a snag, so we are gracefully killing ourselves * after we contact the admin about it. EventsSave should never * end with problems unless the mysql died while doing transactions **/ if ($saverResult['errorStatus'] === true) { Log::error(get_class($saver) . ': ' . 'Saver has ended with errors ! : ' . $saverResult['errorMessage']); $this->exception(); return; } else { Log::info(get_class($saver) . ': ' . 'Saver has ended without errors'); } } else { Log::warning(get_class($this) . ': ' . 'Collector did not return any events therefore skipping validation and saving a empty event set'); } Log::info(get_class($this) . ': ' . 'Queued worker has ended the processing of collector: ' . $this->collector); }