diff --git a/app/Helpers/Mail/Mailbox/BaseMailbox.php b/app/Helpers/Mail/Mailbox/BaseMailbox.php new file mode 100644 index 000000000000..6ab63b11acac --- /dev/null +++ b/app/Helpers/Mail/Mailbox/BaseMailbox.php @@ -0,0 +1,19 @@ +sub(new \DateInterval('P30D')); $search->addCondition(new Since($thirtyDaysAgo)); - // not flagged with IN-PARSED - $search->addCondition(new Unflagged()); - - return $mailbox->getMessages($search); } diff --git a/app/Helpers/Mail/Mailbox/Pop3/Pop3Mailbox.php b/app/Helpers/Mail/Mailbox/Pop3/Pop3Mailbox.php new file mode 100644 index 000000000000..658b14f08eb4 --- /dev/null +++ b/app/Helpers/Mail/Mailbox/Pop3/Pop3Mailbox.php @@ -0,0 +1,57 @@ +server = new Server($server, $port != '' ? $port : null); + + $this->connection = $this->server->authenticate($user, $password); + } + + + public function getUnprocessedEmails() + { + $mailbox = $this->connection->getMailbox('INBOX'); + + $search = new SearchExpression(); + + // not older than 30days + $today = new \DateTimeImmutable(); + $thirtyDaysAgo = $today->sub(new \DateInterval('P30D')); + $search->addCondition(new Since($thirtyDaysAgo)); + + return $mailbox->getMessages($search); + } + + public function moveProcessed(MessageInterface $mail) + { + return $mail->move($this->connection->getMailbox('PROCESSED')); + } + + public function moveFailed(MessageInterface $mail) + { + return $mail->move($this->connection->getMailbox('FAILED')); + } +} diff --git a/app/Helpers/Mail/Webhook/BaseWebhookHandler.php b/app/Helpers/Mail/Webhook/BaseWebhookHandler.php new file mode 100644 index 000000000000..cb839f4b5ef4 --- /dev/null +++ b/app/Helpers/Mail/Webhook/BaseWebhookHandler.php @@ -0,0 +1,20 @@ +header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('services.postmark.token')) { + ProcessMailgunInboundWebhook::dispatch($request->all())->delay(10); + + return response()->json(['message' => 'Success'], 200); + } + + return response()->json(['message' => 'Unauthorized'], 403); + } +} diff --git a/app/Jobs/Mail/ExpenseMailboxJob.php b/app/Jobs/Mail/ExpenseMailboxJob.php index 956d29c95619..972d835b45ef 100644 --- a/app/Jobs/Mail/ExpenseMailboxJob.php +++ b/app/Jobs/Mail/ExpenseMailboxJob.php @@ -17,7 +17,7 @@ use App\Events\Expense\ExpenseWasCreated; use App\Events\Invoice\InvoiceWasEmailedAndFailed; use App\Events\Payment\PaymentWasEmailedAndFailed; use App\Factory\ExpenseFactory; -use App\Helpers\Mail\ImapMailbox; +use App\Helpers\Mail\Mailbox\Imap\ImapMailbox; use App\Jobs\Util\SystemLogger; use App\Libraries\Google\Google; use App\Libraries\MultiDB; diff --git a/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php b/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php new file mode 100644 index 000000000000..069b11003b4c --- /dev/null +++ b/app/Jobs/Mailgun/ProcessMailgunInboundWebhook.php @@ -0,0 +1,180 @@ + '', + 'subject' => 'Message not found.', + 'entity' => '', + 'entity_id' => '', + 'events' => [], + ]; + + /** + * Create a new job instance. + * + */ + public function __construct(private array $request) + { + } + + private function getSystemLog(string $message_id): ?SystemLog + { + return SystemLog::query() + ->where('company_id', $this->invitation->company_id) + ->where('type_id', SystemLog::TYPE_WEBHOOK_RESPONSE) + ->whereJsonContains('log', ['MessageID' => $message_id]) + ->orderBy('id', 'desc') + ->first(); + + } + + private function updateSystemLog(SystemLog $system_log, array $data): void + { + $system_log->log = $data; + $system_log->save(); + } + + /** + * Execute the job. + * + * + * @return void + */ + public function handle() + { + MultiDB::findAndSetDbByCompanyKey($this->request['Tag']); + + // match companies + if (array_key_exists('ToFull', $this->request)) + throw new \Exception('invalid body'); + + $toEmails = []; + foreach ($this->request['ToFull'] as $toEmailEntry) + $toEmails[] = $toEmailEntry['Email']; + + // create expense for each company + $expense = new Expense(); + + $expense->company_id; + } + // { + // "FromName": "Postmarkapp Support", + // "MessageStream": "inbound", + // "From": "support@postmarkapp.com", + // "FromFull": { + // "Email": "support@postmarkapp.com", + // "Name": "Postmarkapp Support", + // "MailboxHash": "" + // }, + // "To": "\"Firstname Lastname\" ", + // "ToFull": [ + // { + // "Email": "yourhash+SampleHash@inbound.postmarkapp.com", + // "Name": "Firstname Lastname", + // "MailboxHash": "SampleHash" + // } + // ], + // "Cc": "\"First Cc\" , secondCc@postmarkapp.com>", + // "CcFull": [ + // { + // "Email": "firstcc@postmarkapp.com", + // "Name": "First Cc", + // "MailboxHash": "" + // }, + // { + // "Email": "secondCc@postmarkapp.com", + // "Name": "", + // "MailboxHash": "" + // } + // ], + // "Bcc": "\"First Bcc\" , secondbcc@postmarkapp.com>", + // "BccFull": [ + // { + // "Email": "firstbcc@postmarkapp.com", + // "Name": "First Bcc", + // "MailboxHash": "" + // }, + // { + // "Email": "secondbcc@postmarkapp.com", + // "Name": "", + // "MailboxHash": "" + // } + // ], + // "OriginalRecipient": "yourhash+SampleHash@inbound.postmarkapp.com", + // "Subject": "Test subject", + // "MessageID": "73e6d360-66eb-11e1-8e72-a8904824019b", + // "ReplyTo": "replyto@postmarkapp.com", + // "MailboxHash": "SampleHash", + // "Date": "Fri, 1 Aug 2014 16:45:32 -04:00", + // "TextBody": "This is a test text body.", + // "HtmlBody": "

This is a test html body.<\/p><\/body><\/html>", + // "StrippedTextReply": "This is the reply text", + // "Tag": "TestTag", + // "Headers": [ + // { + // "Name": "X-Header-Test", + // "Value": "" + // }, + // { + // "Name": "X-Spam-Status", + // "Value": "No" + // }, + // { + // "Name": "X-Spam-Score", + // "Value": "-0.1" + // }, + // { + // "Name": "X-Spam-Tests", + // "Value": "DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,SPF_PASS" + // } + // ], + // "Attachments": [ + // { + // "Name": "test.txt", + // "Content": "VGhpcyBpcyBhdHRhY2htZW50IGNvbnRlbnRzLCBiYXNlLTY0IGVuY29kZWQu", + // "ContentType": "text/plain", + // "ContentLength": 45 + // } + // ] + // } +} diff --git a/routes/api.php b/routes/api.php index 078274348f40..69b8530bf312 100644 --- a/routes/api.php +++ b/routes/api.php @@ -388,6 +388,7 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] // Route::post('hooks', [SubscriptionController::class, 'subscribe'])->name('hooks.subscribe'); // Route::delete('hooks/{subscription_id}', [SubscriptionController::class, 'unsubscribe'])->name('hooks.unsubscribe'); + Route::post('stripe/update_payment_methods', [StripeController::class, 'update'])->middleware('password_protected')->name('stripe.update'); Route::post('stripe/import_customers', [StripeController::class, 'import'])->middleware('password_protected')->name('stripe.import'); @@ -415,6 +416,7 @@ Route::match(['get', 'post'], 'payment_notification_webhook/{company_key}/{compa Route::post('api/v1/postmark_webhook', [PostMarkController::class, 'webhook'])->middleware('throttle:1000,1'); Route::post('api/v1/postmark_inbound_webhook', [PostMarkController::class, 'inboundWebhook'])->middleware('throttle:1000,1'); +Route::post('api/v1/mailgun_inbound_webhook', [MailgunController::class, 'inboundWebhook'])->middleware('throttle:1000,1'); Route::get('token_hash_router', [OneTimeTokenController::class, 'router'])->middleware('throttle:500,1'); Route::get('webcron', [WebCronController::class, 'index'])->middleware('throttle:100,1'); Route::post('api/v1/get_migration_account', [HostedMigrationController::class, 'getAccount'])->middleware('guest')->middleware('throttle:100,1');