From db0e9553d1ac412a9761fae2e358b7e8ef491ef7 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Thu, 3 Feb 2022 15:05:17 +0200 Subject: [PATCH 01/22] add mail attachments to deck & bug fixes --- .gitignore | 1 + attachments/attachment.txt | 0 config.example.php | 11 ++++ config.php | 10 --- index.php | 60 ++++++----------- lib/DeckClass.php | 131 +++++++++++++++---------------------- 6 files changed, 83 insertions(+), 130 deletions(-) create mode 100644 .gitignore delete mode 100644 attachments/attachment.txt create mode 100644 config.example.php delete mode 100644 config.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4e9b47a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +config.php \ No newline at end of file diff --git a/attachments/attachment.txt b/attachments/attachment.txt deleted file mode 100644 index e69de29..0000000 diff --git a/config.example.php b/config.example.php new file mode 100644 index 0000000..66c2bd6 --- /dev/null +++ b/config.example.php @@ -0,0 +1,11 @@ + diff --git a/config.php b/config.php deleted file mode 100644 index 9c230ae..0000000 --- a/config.php +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/index.php b/index.php index 9d10a3b..4753a6a 100644 --- a/index.php +++ b/index.php @@ -3,24 +3,18 @@ error_reporting(E_ERROR | E_PARSE); require_once("config.php"); require_once('lib/DeckClass.php'); -$inbox = imap_open("{" . MAIL_SERVER . "/imap" . MAIL_SERVER_FLAGS . "}INBOX", MAIL_USER, MAIL_PASSWORD) +$inbox = imap_open("{" . MAIL_SERVER . ":" . MAIL_SERVER_PORT . MAIL_SERVER_FLAGS . "}INBOX", MAIL_USER, MAIL_PASSWORD) or die("can't connect:" . imap_last_error()); $emails = imap_search($inbox, 'UNSEEN'); if ($emails) - for ($j = 0; $j <= count($emails) && $j <= 4; $j++) { + for ($j = 0; $j < count($emails) && $j < 5; $j++) { $structure = imap_fetchstructure($inbox, $emails[$j]); $attachments = array(); + $attNames = array(); if (isset($structure->parts) && count($structure->parts)) { for ($i = 0; $i < count($structure->parts); $i++) { - $attachments[$i] = array( - 'is_attachment' => false, - 'filename' => '', - 'name' => '', - 'attachment' => '' - ); - if ($structure->parts[$i]->ifdparameters) { foreach ($structure->parts[$i]->dparameters as $object) { if (strtolower($object->attribute) == 'filename') { @@ -50,51 +44,37 @@ if ($emails) } } } - for ($i = 1; $i < count($attachments); $i++) { + for ($i = 1; $i <= count($attachments); $i++) { + if(! file_exists(getcwd() . '/attachments')) { + mkdir(getcwd() . '/attachments'); + } if ($attachments[$i]['is_attachment'] == 1) { $filename = $attachments[$i]['name']; if (empty($filename)) $filename = $attachments[$i]['filename']; - $fp = fopen("./attachments/" . $filename, "w+"); + $fp = fopen(getcwd() . '/attachments/' . $filename, "w+"); fwrite($fp, $attachments[$i]['attachment']); fclose($fp); - } - } - - $hasAttachment = false; - for ($i = 0; $i < count($attachments); $i++) { - if ($attachments[$i]['is_attachment'] != '') { - $hasAttachment = true; + array_push($attNames, $attachments[$i]['filename']); } } $overview = imap_headerinfo($inbox, $emails[$j]); - $toAddress = strrev($overview->toaddress); - if(preg_match('/@([^+]+)/', $toAddress, $m)) { - global $boardName; - $boardName = strrev($m[1]); - } - if ($hasAttachment) { - $message = imap_fetchbody($inbox, $emails[$j], 1.1); + + $data = new stdClass(); + $data->title = DECODE_SPECIAL_CHARACTERS ? mb_decode_mimeheader($overview->subject) : $overview->subject; + $data->type = "plain"; + if(count($attachments)) { + $data->attachments = $attNames; + $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode(imap_fetchbody($inbox, $emails[$j], 1.1)) : imap_fetchbody($inbox, $emails[$j], 1.1); } else { - $message = imap_fetchbody($inbox, $emails[$j], 1); + $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode(imap_fetchbody($inbox, $emails[$j], 1)) : imap_fetchbody($inbox, $emails[$j], 1); } - $mailData = new stdClass(); - $mailData->mailSubject = DECODE_SPECIAL_CHARACTERS ? mb_decode_mimeheader($overview->subject) : $overview->subject; - $mailData->mailMessage = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode($message) : $message; - $mailData->from = $overview->from[0]->mailbox . '@' . $overview->from[0]->host; + $data->description = $description; $newcard = new DeckClass(); - $newcard->getParameters(); $newcard->addCard($data); - - if ($hasAttachment) { - for ($i = 1; $i <= count($attachments); $i++) { - $mailData->fileAttached[$i] = $attachments[$i]['name']; - } - $newcard->addAttachment($data); - } } - -imap_close($inbox); + + imap_close($inbox); ?> diff --git a/lib/DeckClass.php b/lib/DeckClass.php index 45d6cc5..b01710d 100644 --- a/lib/DeckClass.php +++ b/lib/DeckClass.php @@ -1,116 +1,87 @@ NC_USER . ":" . NC_PASSWORD, + if($data && !$attachment) { + $endpoint .= '?' . http_build_query($data); + } + curl_setopt_array($curl, array( CURLOPT_URL => $endpoint, CURLOPT_RETURNTRANSFER => true, - CURLOPT_SSLVERSION => "all", - ]; - - // set HTTP request specific headers and options/data - if ($request == '') {// an empty request value is used for attachments - // add data without JSON encoding or JSON Content-Type header - $options[CURLOPT_POST] = true; - $options[CURLOPT_POSTFIELDS] = $data; - } elseif ($request == "POST") { - array_push($headers, "Content-Type: application/json"); - $options[CURLOPT_POST] = true; - $options[CURLOPT_POSTFIELDS] = json_encode($data); - } elseif ($request == "GET") { - array_push($headers, "Content-Type: application/json"); - } - - // add headers to options - $options[CURLOPT_HTTPHEADER] = $headers; - curl_setopt_array($curl, $options); + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => 0, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => $request, + CURLOPT_POSTFIELDS => (array) $data, + CURLOPT_HTTPHEADER => array( + 'Authorization: Basic ' . base64_encode(NC_USER . ':' . NC_PASSWORD), + 'OCS-APIRequest: true', + ), + )); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); - - if ($err) { - echo "cURL Error #:" . $err; - } + if($err) echo "cURL Error #:" . $err; return json_decode($response); } - public function getParameters() {// get the board and the stack - global $mailData; - global $boardId; - - if(preg_match('/b-"([^"]+)"/', $mailData->mailSubject, $m) || preg_match("/b-'([^']+)'/", $mailData->mailSubject, $m)) { + public function getParameters($params) {// get the board and the stack + if(preg_match('/b-"([^"]+)"/', $params, $m) || preg_match("/b-'([^']+)'/", $params, $m)) { $boardFromMail = $m[1]; - $mailData->mailSubject = str_replace($m[0], '', $mailData->mailSubject); + $params = str_replace($m[0], '', $params); } - if(preg_match('/s-"([^"]+)"/', $mailData->mailSubject, $m) || preg_match("/s-'([^']+)'/", $mailData->mailSubject, $m)) { + if(preg_match('/s-"([^"]+)"/', $params, $m) || preg_match("/s-'([^']+)'/", $params, $m)) { $stackFromMail = $m[1]; - $mailData->mailSubject = str_replace($m[0], '', $mailData->mailSubject); + $params = str_replace($m[0], '', $params); } - global $boardName; - $boards = $this->apiCall("GET", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards", ''); + $boards = $this->apiCall("GET", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards"); foreach($boards as $board) { - if($board->title == $boardFromMail || $board->title == $boardName) { + if(strtolower($board->title) == strtolower($boardFromMail)) { $boardId = $board->id; - } else { - echo "Board not found\n"; } } - $stacks = $this->apiCall("GET", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/$boardId/stacks", ''); - foreach($stacks as $stack) { - if($stack->title == $stackFromMail) { - global $stackId; - $stackId = $stack->id; - } else if (!is_numeric($stackId)) { - global $stackId; - $stackId = $stacks[0]->id; - } + if($boardId) { + $stacks = $this->apiCall("GET", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/$boardId/stacks"); + foreach($stacks as $stack) + (strtolower($stack->title) == strtolower($stackFromMail)) ? $stackId = $stack->id : $stackId = $stacks[0]->id; } + + $boardStack = new stdClass(); + $boardStack->board = $boardId; + $boardStack->stack = $stackId; + $boardStack->newTitle = $params; + + return $boardStack; } public function addCard($data) { - global $mailData; - global $stackId; + $params = $this->getParameters($data->title); + $data->title = $params->newTitle; + $card = $this->apiCall("POST", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/{$params->board}/stacks/{$params->stack}/cards", $data); + $card->board = $params->board; + $card->stack = $params->stack; + if($data->attachments) $this->addAttachments($card, $data->attachments); - $data = new stdClass(); - $data->stackId = $stackId; - $data->title = $mailData->mailSubject; - $data->description = -"$mailData->mailMessage -*** -### $mailData->from -"; - $data->type = "plain"; - $data->order = "-" . time(); // put the card to the top - - //create card - $response = $this->apiCall("POST", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/1/stacks/1/cards", $data); - global $cardId; - $cardId = $response->id; + return $card; } - public function addAttachment($data) { - global $mailData; - global $cardId; - $fullPath = getcwd() . "/attachments/"; //get full path to attachments dirctory - - for ($i = 1; $i < count($mailData->fileAttached); $i++) { + private function addAttachments($card, $attachments) { + $fullPath = getcwd() . "/attachments/"; //get full path to attachments directory + for ($i = 0; $i < count($attachments); $i++) { + $file = $fullPath . $attachments[$i]; $data = array( - 'file' => new CURLFile($fullPath . $mailData->fileAttached[$i]) - ); - $this->apiCall("", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/1/stacks/1/cards/$cardId/attachments?type=deck_file", $data); + 'file' => new CURLFile($file) + ); + $this->apiCall("POST", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/{$card->board}/stacks/{$card->stack}/cards/{$card->id}/attachments?type=file", $data, true); + unlink($file); } } } From 77f99de5bb337666e644fde3fe6b8c63ec1aea22 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Fri, 4 Feb 2022 10:46:44 +0200 Subject: [PATCH 02/22] Assign user to newly created card if has NC account. --- config.example.php | 1 + index.php | 25 +++++++++++++------------ lib/DeckClass.php | 21 +++++++++++++++++++-- lib/MailClass.php | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 lib/MailClass.php diff --git a/config.example.php b/config.example.php index 66c2bd6..665ffca 100644 --- a/config.example.php +++ b/config.example.php @@ -8,4 +8,5 @@ define("MAIL_SERVER_PORT", "143"); define("MAIL_USER", "incoming"); define("MAIL_PASSWORD", "****"); define("DECODE_SPECIAL_CHARACTERS", true); //requires mbstring, if false special characters (like öäüß) won't be displayed correctly +define("ASSIGN_SENDER", true); // if true, sender will be assigned to card if has NC account ?> diff --git a/index.php b/index.php index 4753a6a..7f36eb5 100644 --- a/index.php +++ b/index.php @@ -2,15 +2,14 @@ error_reporting(E_ERROR | E_PARSE); require_once("config.php"); require_once('lib/DeckClass.php'); +require_once('lib/MailClass.php'); -$inbox = imap_open("{" . MAIL_SERVER . ":" . MAIL_SERVER_PORT . MAIL_SERVER_FLAGS . "}INBOX", MAIL_USER, MAIL_PASSWORD) - or die("can't connect:" . imap_last_error()); - -$emails = imap_search($inbox, 'UNSEEN'); +$inbox = new MailClass(); +$emails = $inbox->getNewMessages(); if ($emails) for ($j = 0; $j < count($emails) && $j < 5; $j++) { - $structure = imap_fetchstructure($inbox, $emails[$j]); + $structure = $inbox->fetchMessageStructure($emails[$j]); $attachments = array(); $attNames = array(); if (isset($structure->parts) && count($structure->parts)) { @@ -34,7 +33,7 @@ if ($emails) } if ($attachments[$i]['is_attachment']) { - $attachments[$i]['attachment'] = imap_fetchbody($inbox, $emails[$j], $i+1); + $attachments[$i]['attachment'] = $inbox->fetchMessageBody($emails[$j], $i+1); if ($structure->parts[$i]->encoding == 3) { // 3 = BASE64 $attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']); } @@ -59,22 +58,24 @@ if ($emails) } } - $overview = imap_headerinfo($inbox, $emails[$j]); + $overview = $inbox->headerInfo($emails[$j]); $data = new stdClass(); $data->title = DECODE_SPECIAL_CHARACTERS ? mb_decode_mimeheader($overview->subject) : $overview->subject; $data->type = "plain"; + $data->order = 999; if(count($attachments)) { $data->attachments = $attNames; - $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode(imap_fetchbody($inbox, $emails[$j], 1.1)) : imap_fetchbody($inbox, $emails[$j], 1.1); + $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode($inbox->fetchMessageBody($emails[$j], 1.1)) : $inbox->fetchMessageBody($emails[$j], 1.1); } else { - $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode(imap_fetchbody($inbox, $emails[$j], 1)) : imap_fetchbody($inbox, $emails[$j], 1); + $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode($inbox->fetchMessageBody($emails[$j], 1)) : $inbox->fetchMessageBody($emails[$j], 1); } $data->description = $description; + $mailSender = new stdClass(); + $mailSender->userId = $overview->from[0]->mailbox; + $mailSender = 'alex.puiu'; $newcard = new DeckClass(); - $newcard->addCard($data); + $newcard->addCard($data, $mailSender); } - - imap_close($inbox); ?> diff --git a/lib/DeckClass.php b/lib/DeckClass.php index b01710d..b0486ee 100644 --- a/lib/DeckClass.php +++ b/lib/DeckClass.php @@ -15,13 +15,14 @@ class DeckClass { CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => $request, - CURLOPT_POSTFIELDS => (array) $data, CURLOPT_HTTPHEADER => array( 'Authorization: Basic ' . base64_encode(NC_USER . ':' . NC_PASSWORD), 'OCS-APIRequest: true', ), )); + if($request === 'POST') curl_setopt($curl, CURLOPT_POSTFIELDS, (array) $data); + $response = curl_exec($curl); $err = curl_error($curl); @@ -62,12 +63,15 @@ class DeckClass { return $boardStack; } - public function addCard($data) { + public function addCard($data, $user) { $params = $this->getParameters($data->title); $data->title = $params->newTitle; $card = $this->apiCall("POST", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/{$params->board}/stacks/{$params->stack}/cards", $data); $card->board = $params->board; $card->stack = $params->stack; + if(ASSIGN_SENDER) { + $this->assignUser($card, $user); + } if($data->attachments) $this->addAttachments($card, $data->attachments); return $card; @@ -84,5 +88,18 @@ class DeckClass { unlink($file); } } + + public function assignUser($card, $mailUser) + { + $board = $this->apiCall("GET", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/{$card->board}"); + $boardUsers = array_map(function ($user) { return $user->uid; }, $board->users); + + foreach($boardUsers as $user) { + if($user === $mailUser->userId) { + $this->apiCall("PUT", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/{$card->board}/stacks/{$card->stack}/cards/{$card->id}/assignUser", $mailUser); + break; + } + } + } } ?> diff --git a/lib/MailClass.php b/lib/MailClass.php new file mode 100644 index 0000000..438fc7f --- /dev/null +++ b/lib/MailClass.php @@ -0,0 +1,37 @@ +inbox = imap_open("{" . MAIL_SERVER . ":" . MAIL_SERVER_PORT . MAIL_SERVER_FLAGS . "}INBOX", MAIL_USER, MAIL_PASSWORD) + or die("can't connect:" . imap_last_error()); + } + + public function __destruct() + { + imap_close($this->inbox); + } + + public function getNewMessages() { + return imap_search($this->inbox, 'UNSEEN'); + } + + public function fetchMessageStructure($email) { + return imap_fetchstructure($this->inbox, $email); + } + + public function fetchMessageBody($email, $section) { + return imap_fetchbody($this->inbox, $email, $section); + } + + public function headerInfo($email) { + return imap_headerinfo($this->inbox, $email); + } + + public function reply($sender, $response) { + $to = $sender->mailbox . '@' . $sender->host; + mail($to, 'your card has been created', 'card/board link.....'); + } +} \ No newline at end of file From e616ebfffb407a4f4d2d0e7ba3c3ea19630f3950 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Fri, 4 Feb 2022 14:57:16 +0200 Subject: [PATCH 03/22] reply to sender with newly created card, fixes #13 --- index.php | 7 +++++-- lib/DeckClass.php | 15 ++++++++++++--- lib/MailClass.php | 19 +++++++++++++++++-- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/index.php b/index.php index 7f36eb5..10bf1b0 100644 --- a/index.php +++ b/index.php @@ -73,9 +73,12 @@ if ($emails) $data->description = $description; $mailSender = new stdClass(); $mailSender->userId = $overview->from[0]->mailbox; - $mailSender = 'alex.puiu'; $newcard = new DeckClass(); - $newcard->addCard($data, $mailSender); + $response = $newcard->addCard($data, $mailSender); + $mailSender->userId .= "@{$overview->from[0]->host}"; + if($response && ASSIGN_SENDER) { + $inbox->reply($mailSender->userId, $response); + } } ?> diff --git a/lib/DeckClass.php b/lib/DeckClass.php index b0486ee..f6b210d 100644 --- a/lib/DeckClass.php +++ b/lib/DeckClass.php @@ -1,6 +1,8 @@ responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if($err) echo "cURL Error #:" . $err; @@ -46,6 +49,7 @@ class DeckClass { foreach($boards as $board) { if(strtolower($board->title) == strtolower($boardFromMail)) { $boardId = $board->id; + $boardName = $board->title; } } @@ -59,6 +63,7 @@ class DeckClass { $boardStack->board = $boardId; $boardStack->stack = $stackId; $boardStack->newTitle = $params; + $boardStack->boardTitle = $boardName; return $boardStack; } @@ -69,10 +74,14 @@ class DeckClass { $card = $this->apiCall("POST", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/{$params->board}/stacks/{$params->stack}/cards", $data); $card->board = $params->board; $card->stack = $params->stack; - if(ASSIGN_SENDER) { - $this->assignUser($card, $user); + + if($this->responseCode == 200) { + if(ASSIGN_SENDER) $this->assignUser($card, $user); + if($data->attachments) $this->addAttachments($card, $data->attachments); + $card->boardTitle = $params->boardTitle; + } else { + return false; } - if($data->attachments) $this->addAttachments($card, $data->attachments); return $card; } diff --git a/lib/MailClass.php b/lib/MailClass.php index 438fc7f..9044b03 100644 --- a/lib/MailClass.php +++ b/lib/MailClass.php @@ -31,7 +31,22 @@ class MailClass { } public function reply($sender, $response) { - $to = $sender->mailbox . '@' . $sender->host; - mail($to, 'your card has been created', 'card/board link.....'); + $serverName = parse_url(NC_SERVER); + + $headers = array( + 'From' => 'no-reply@' . $serverName['host'], + 'MIME-Version' => '1.0', + 'Content-Type' => 'text/html' + ); + + $message = ""; + $message .= "You created a new issue on board {$response->boardTitle}."; + $message .= ""; + $message .= "

You created a new issue on board {$response->boardTitle}.

"; + $message .= "

Check out this board}/card/{$response->id}" . "\">link to see your newly created card.

"; + $message .= ""; + $message .= ""; + + mail($sender, 'An issue has been reported!', $message, $headers); } } \ No newline at end of file From 34a8cd843871977e328873a89164415663ecf7de Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Fri, 4 Feb 2022 16:41:37 +0200 Subject: [PATCH 04/22] set board by email address delimiter (+). --- README.md | 36 +++++++++++++++++++----------------- index.php | 4 +++- lib/DeckClass.php | 15 ++++++++------- lib/MailClass.php | 2 +- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index f9c509c..2b579da 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,27 @@ # mail2deck Provides an "email in" solution for the Nextcloud Deck app -## A. For users +# 🚀 A. For users Follow the above steps to add a new card from email. * Deck Bot is the user who will create the cards and it will be set up by your nextcloud admin. * In this tutorial email address for Deck Bot will be: bot@ncserver.com -### 1) Assign Deck Bot to the board. -### 2) Mail subject & content +## 1) Assign Deck Bot to the board. +## 2) Mail subject & content Let's assume you want to add a card with title "Update website logo" on board "Website" and stack "To do". You can do this in two ways. -#### 2.1: Set stack and board in the email subject +### 2.1: Set stack and board in the email subject Here's how the email subject should look like: -Update website logo b-'Website' s-'To do' +Update website logo b-'website' s-'to do' *You can use single or double quotes.* +*Case-insensitive for board and stack respectively* -#### 2.2: Set the board in the email address -At the end of the email address prefix (before @) add "+Website" +### 2.2: Set the board in the email address +At the end of the email address prefix (before @) add "+website" -Example: bot+Website@ncserver.com +Example: bot+website@ncserver.com In this case, if you don't specify the stack in the email subject, the card will be added in the first stack (if it exists). @@ -28,22 +29,22 @@ Note: * Email content will be card description * You can add attachments in the email and those will be integrated in the created card -## B. For NextCloud admins to setup -### Requirements +# ⚙️ B. For NextCloud admins to setup +## Requirements This app requires php-curl, php-mbstring ,php-imap and some sort of imap server (e.g. Postfix with Courier). -### NC new user +## NC new user Create a new user from User Management on your NC server, which will have to function as a bot. We chose to call him *deckbot*, but you can call it however you want.
__Note__: that you have to assign *deckbot* on each board you want to add new cards from email. -### Configure Email -#### Option 1 - Set up Postfix for incoming email +## Configure Email +### Option 1 - Set up Postfix for incoming email You can setup Posfix mail server folowing the instructions on [Posfix setup](https://docs.gitlab.com/ee/administration/reply_by_email_postfix_setup.html), and after that add "+" delimiter (which separe the user from the board in the email address) using the command:
``` sudo postconf -e "recipient_delimiter = +" ``` -#### Option 2 - Use an existing email server +### Option 2 - Use an existing email server This could be any hosted email service. The only requirement is that you can connect to it via the IMAP protocol. *Please note this option may not be as flexible as a self-hosted server. For example your email service may not support the "+"delimiter for directing messages to a specific board.* -### Download and install +## Download and install If using a self-hosted Postfix server, clone this repository into the home directory of the *incoming* user. If not self-hosting, you may need to create a new user on your system and adjust the commands in future steps to match that username.
``` cd /home/incoming/ @@ -54,11 +55,12 @@ Edit the config file as you need: sudo nano /home/incoming/mail2deck/config.php ``` *You can refer to https://www.php.net/manual/en/function.imap-open.php for setting the value of MAIL_SERVER_FLAGS* -### Add a cronjob which will run mail2deck. +## Add a cronjob which will run mail2deck. ``` sudo crontab -u incoming -e ``` Add the following line in the opened file: */5 * * * * /usr/bin/php /home/incoming/mail2deck/index.php >/dev/null 2>&1 -### Finish + +## Finish Now __mail2deck__ will add new cards every five minutes if new emails are received. diff --git a/index.php b/index.php index 10bf1b0..5c75eab 100644 --- a/index.php +++ b/index.php @@ -59,6 +59,8 @@ if ($emails) } $overview = $inbox->headerInfo($emails[$j]); + $board = null; + if(strstr($overview->to[0]->mailbox, '+')) $board = substr($overview->to[0]->mailbox, strpos($overview->to[0]->mailbox, '+') + 1); $data = new stdClass(); $data->title = DECODE_SPECIAL_CHARACTERS ? mb_decode_mimeheader($overview->subject) : $overview->subject; @@ -75,7 +77,7 @@ if ($emails) $mailSender->userId = $overview->from[0]->mailbox; $newcard = new DeckClass(); - $response = $newcard->addCard($data, $mailSender); + $response = $newcard->addCard($data, $mailSender, $board); $mailSender->userId .= "@{$overview->from[0]->host}"; if($response && ASSIGN_SENDER) { $inbox->reply($mailSender->userId, $response); diff --git a/lib/DeckClass.php b/lib/DeckClass.php index f6b210d..6d38f6a 100644 --- a/lib/DeckClass.php +++ b/lib/DeckClass.php @@ -35,11 +35,12 @@ class DeckClass { return json_decode($response); } - public function getParameters($params) {// get the board and the stack - if(preg_match('/b-"([^"]+)"/', $params, $m) || preg_match("/b-'([^']+)'/", $params, $m)) { - $boardFromMail = $m[1]; - $params = str_replace($m[0], '', $params); - } + public function getParameters($params, $boardFromMail = null) {// get the board and the stack + if(!$boardFromMail) // if board is not set within the email address, look for board into email subject + if(preg_match('/b-"([^"]+)"/', $params, $m) || preg_match("/b-'([^']+)'/", $params, $m)) { + $boardFromMail = $m[1]; + $params = str_replace($m[0], '', $params); + } if(preg_match('/s-"([^"]+)"/', $params, $m) || preg_match("/s-'([^']+)'/", $params, $m)) { $stackFromMail = $m[1]; $params = str_replace($m[0], '', $params); @@ -68,8 +69,8 @@ class DeckClass { return $boardStack; } - public function addCard($data, $user) { - $params = $this->getParameters($data->title); + public function addCard($data, $user, $board = null) { + $params = $this->getParameters($data->title, $board); $data->title = $params->newTitle; $card = $this->apiCall("POST", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/{$params->board}/stacks/{$params->stack}/cards", $data); $card->board = $params->board; diff --git a/lib/MailClass.php b/lib/MailClass.php index 9044b03..562fc3a 100644 --- a/lib/MailClass.php +++ b/lib/MailClass.php @@ -49,4 +49,4 @@ class MailClass { mail($sender, 'An issue has been reported!', $message, $headers); } -} \ No newline at end of file +} From 698c513b8bfc9d6953dad9b08f09dd5165585ca7 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Fri, 4 Feb 2022 16:44:11 +0200 Subject: [PATCH 05/22] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b579da..dcce1bb 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ Here's how the email subject should look like: Update website logo b-'website' s-'to do' *You can use single or double quotes.* -*Case-insensitive for board and stack respectively* + +*Case-insensitive for board and stack respectively.* ### 2.2: Set the board in the email address At the end of the email address prefix (before @) add "+website" From 8d42b8c6d3dd053428345d5281cd9dba6bd39d55 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Tue, 8 Feb 2022 13:34:16 +0200 Subject: [PATCH 06/22] inform sender if card was not created. --- README.md | 2 ++ index.php | 5 ++--- lib/DeckClass.php | 40 ++++++++++++++++++++++++++++------------ lib/MailClass.php | 19 ++++++++++++------- 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index dcce1bb..2e340d4 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ Follow the above steps to add a new card from email. * In this tutorial email address for Deck Bot will be: bot@ncserver.com ## 1) Assign Deck Bot to the board. +Deck Bot must be assigned and must have edit permission inside the board. + ## 2) Mail subject & content Let's assume you want to add a card with title "Update website logo" on board "Website" and stack "To do". You can do this in two ways. diff --git a/index.php b/index.php index 5c75eab..c2defc8 100644 --- a/index.php +++ b/index.php @@ -79,8 +79,7 @@ if ($emails) $newcard = new DeckClass(); $response = $newcard->addCard($data, $mailSender, $board); $mailSender->userId .= "@{$overview->from[0]->host}"; - if($response && ASSIGN_SENDER) { - $inbox->reply($mailSender->userId, $response); - } + + ($response) ? $inbox->reply($mailSender->userId, $response) : $inbox->reply($mailSender->userId); } ?> diff --git a/lib/DeckClass.php b/lib/DeckClass.php index 6d38f6a..6b148cd 100644 --- a/lib/DeckClass.php +++ b/lib/DeckClass.php @@ -49,6 +49,9 @@ class DeckClass { $boards = $this->apiCall("GET", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards"); foreach($boards as $board) { if(strtolower($board->title) == strtolower($boardFromMail)) { + if(!$this->checkBotPermissions($board)) { + return false; + } $boardId = $board->id; $boardName = $board->title; } @@ -58,6 +61,8 @@ class DeckClass { $stacks = $this->apiCall("GET", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/$boardId/stacks"); foreach($stacks as $stack) (strtolower($stack->title) == strtolower($stackFromMail)) ? $stackId = $stack->id : $stackId = $stacks[0]->id; + } else { + return false; } $boardStack = new stdClass(); @@ -71,20 +76,23 @@ class DeckClass { public function addCard($data, $user, $board = null) { $params = $this->getParameters($data->title, $board); - $data->title = $params->newTitle; - $card = $this->apiCall("POST", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/{$params->board}/stacks/{$params->stack}/cards", $data); - $card->board = $params->board; - $card->stack = $params->stack; - if($this->responseCode == 200) { - if(ASSIGN_SENDER) $this->assignUser($card, $user); - if($data->attachments) $this->addAttachments($card, $data->attachments); - $card->boardTitle = $params->boardTitle; - } else { - return false; + if($params) { + $data->title = $params->newTitle; + $card = $this->apiCall("POST", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/{$params->board}/stacks/{$params->stack}/cards", $data); + $card->board = $params->board; + $card->stack = $params->stack; + + if($this->responseCode == 200) { + if(ASSIGN_SENDER) $this->assignUser($card, $user); + if($data->attachments) $this->addAttachments($card, $data->attachments); + $card->boardTitle = $params->boardTitle; + } else { + return false; + } + return $card; } - - return $card; + return false; } private function addAttachments($card, $attachments) { @@ -111,5 +119,13 @@ class DeckClass { } } } + + private function checkBotPermissions($board) { + foreach($board->acl as $acl) + if($acl->participant->uid == NC_USER && $acl->permissionEdit) + return true; + + return false; + } } ?> diff --git a/lib/MailClass.php b/lib/MailClass.php index 562fc3a..98e11e6 100644 --- a/lib/MailClass.php +++ b/lib/MailClass.php @@ -30,7 +30,7 @@ class MailClass { return imap_headerinfo($this->inbox, $email); } - public function reply($sender, $response) { + public function reply($sender, $response = null) { $serverName = parse_url(NC_SERVER); $headers = array( @@ -39,14 +39,19 @@ class MailClass { 'Content-Type' => 'text/html' ); + if($response) { + $body = "

You created a new issue on board {$response->boardTitle}.

Check out this board}/card/{$response->id}" . "\">link to see your newly created card.

"; + $subject = 'An issue has been reported!'; + } else { + $body = "

There was a problem creating your new card.

Make sure you set up the board correctly.

"; + $subject = "Your issue has not been reported!"; + } + $message = ""; - $message .= "You created a new issue on board {$response->boardTitle}."; - $message .= ""; - $message .= "

You created a new issue on board {$response->boardTitle}.

"; - $message .= "

Check out this board}/card/{$response->id}" . "\">link to see your newly created card.

"; - $message .= ""; + $message .= "Mail2Deck response"; + $message .= "$body"; $message .= ""; - mail($sender, 'An issue has been reported!', $message, $headers); + mail($sender, $subject, $message, $headers); } } From 27e908f2c313ad1fdbb53436644cfcaaac2ba084 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Tue, 8 Feb 2022 14:19:12 +0200 Subject: [PATCH 07/22] remove attachments from directory if card was not created. --- index.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/index.php b/index.php index c2defc8..77ac70a 100644 --- a/index.php +++ b/index.php @@ -80,6 +80,11 @@ if ($emails) $response = $newcard->addCard($data, $mailSender, $board); $mailSender->userId .= "@{$overview->from[0]->host}"; - ($response) ? $inbox->reply($mailSender->userId, $response) : $inbox->reply($mailSender->userId); + if($response) { + $inbox->reply($mailSender->userId, $response); + } else { + $inbox->reply($mailSender->userId); + foreach($attNames as $attachment) unlink(getcwd() . "/attachments/" . $attachment); + } } ?> From 08e8b51cc7a018cbf396ebe5a075d7b7cd1eafd0 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Tue, 8 Feb 2022 15:06:13 +0200 Subject: [PATCH 08/22] reply with correct mail server address. --- lib/MailClass.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/MailClass.php b/lib/MailClass.php index 98e11e6..7174336 100644 --- a/lib/MailClass.php +++ b/lib/MailClass.php @@ -31,10 +31,16 @@ class MailClass { } public function reply($sender, $response = null) { - $serverName = parse_url(NC_SERVER); + $server = NC_SERVER; + + if(str_contains($server, "https://")) { + $server = str_replace('https://', '', $server); + } else if(str_contains($server, "http://")) { + $server = str_replace('http://', '', $server); + } $headers = array( - 'From' => 'no-reply@' . $serverName['host'], + 'From' => 'no-reply@' . $server, 'MIME-Version' => '1.0', 'Content-Type' => 'text/html' ); From 05aaaefae7197cc79055a31843961dad6ce71555 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Tue, 8 Feb 2022 16:59:33 +0200 Subject: [PATCH 09/22] find board from mail address if has multiple words. --- index.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.php b/index.php index 77ac70a..81403c0 100644 --- a/index.php +++ b/index.php @@ -60,7 +60,10 @@ if ($emails) $overview = $inbox->headerInfo($emails[$j]); $board = null; - if(strstr($overview->to[0]->mailbox, '+')) $board = substr($overview->to[0]->mailbox, strpos($overview->to[0]->mailbox, '+') + 1); + if(strstr($overview->to[0]->mailbox, '+')) { + $board = substr($overview->to[0]->mailbox, strpos($overview->to[0]->mailbox, '+') + 1); + if(str_contains($board, '+')) $board = str_replace('+', ' ', $board); + } $data = new stdClass(); $data->title = DECODE_SPECIAL_CHARACTERS ? mb_decode_mimeheader($overview->subject) : $overview->subject; From bed78e53332b4ca7e88f7593f40ebf0fde7a0799 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Thu, 10 Feb 2022 14:29:00 +0200 Subject: [PATCH 10/22] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e340d4..449f92c 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,8 @@ This could be any hosted email service. The only requirement is that you can con ## Download and install If using a self-hosted Postfix server, clone this repository into the home directory of the *incoming* user. If not self-hosting, you may need to create a new user on your system and adjust the commands in future steps to match that username.
``` -cd /home/incoming/ -git clone https://github.com/putt1ck/mail2deck.git mail2deck +su - incoming +git clone https://github.com/newroco/mail2deck.git mail2deck ``` Edit the config file as you need: ``` From 5b025184a0a2846bbd6c7eb0a375c34a31f4f5a0 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Thu, 10 Feb 2022 14:50:46 +0200 Subject: [PATCH 11/22] update README.md --- README.md | 6 ++++-- config.example.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 449f92c..94e4365 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,11 @@ If using a self-hosted Postfix server, clone this repository into the home direc su - incoming git clone https://github.com/newroco/mail2deck.git mail2deck ``` -Edit the config file as you need: +Create config.php file and edit it for your needs: ``` -sudo nano /home/incoming/mail2deck/config.php +cd /home/incoming/mail2deck +cp config.example.php config.php +sudo vim config.php ``` *You can refer to https://www.php.net/manual/en/function.imap-open.php for setting the value of MAIL_SERVER_FLAGS* ## Add a cronjob which will run mail2deck. diff --git a/config.example.php b/config.example.php index 665ffca..d957e3e 100644 --- a/config.example.php +++ b/config.example.php @@ -1,5 +1,5 @@ Date: Thu, 10 Feb 2022 16:24:29 +0200 Subject: [PATCH 12/22] Modify function to be accessible in lower versions of php. --- index.php | 2 +- lib/DeckClass.php | 9 +++++++-- lib/MailClass.php | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/index.php b/index.php index 81403c0..595f035 100644 --- a/index.php +++ b/index.php @@ -62,7 +62,7 @@ if ($emails) $board = null; if(strstr($overview->to[0]->mailbox, '+')) { $board = substr($overview->to[0]->mailbox, strpos($overview->to[0]->mailbox, '+') + 1); - if(str_contains($board, '+')) $board = str_replace('+', ' ', $board); + if(strstr($board, '+')) $board = str_replace('+', ' ', $board); } $data = new stdClass(); diff --git a/lib/DeckClass.php b/lib/DeckClass.php index 6b148cd..72f7ac7 100644 --- a/lib/DeckClass.php +++ b/lib/DeckClass.php @@ -54,13 +54,18 @@ class DeckClass { } $boardId = $board->id; $boardName = $board->title; + break; } } if($boardId) { $stacks = $this->apiCall("GET", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards/$boardId/stacks"); - foreach($stacks as $stack) - (strtolower($stack->title) == strtolower($stackFromMail)) ? $stackId = $stack->id : $stackId = $stacks[0]->id; + foreach($stacks as $key => $stack) + if(strtolower($stack->title) == strtolower($stackFromMail)) { + $stackId = $stack->id; + break; + } + if($key == array_key_last($stacks) && !isset($stackId)) $stackId = $stacks[0]->id; } else { return false; } diff --git a/lib/MailClass.php b/lib/MailClass.php index 7174336..3a7ba3d 100644 --- a/lib/MailClass.php +++ b/lib/MailClass.php @@ -33,9 +33,9 @@ class MailClass { public function reply($sender, $response = null) { $server = NC_SERVER; - if(str_contains($server, "https://")) { + if(strstr($server, "https://")) { $server = str_replace('https://', '', $server); - } else if(str_contains($server, "http://")) { + } else if(strstr($server, "http://")) { $server = str_replace('http://', '', $server); } From 99cb30dd9ee8587bc54081ba5bf8fc9fb9a2d2d5 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Thu, 10 Feb 2022 17:17:53 +0200 Subject: [PATCH 13/22] Add card to the top --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index 595f035..58fa628 100644 --- a/index.php +++ b/index.php @@ -68,7 +68,7 @@ if ($emails) $data = new stdClass(); $data->title = DECODE_SPECIAL_CHARACTERS ? mb_decode_mimeheader($overview->subject) : $overview->subject; $data->type = "plain"; - $data->order = 999; + $data->order = 1; if(count($attachments)) { $data->attachments = $attNames; $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode($inbox->fetchMessageBody($emails[$j], 1.1)) : $inbox->fetchMessageBody($emails[$j], 1.1); From 6f798eb6bdf6a500e89e5b66dcd1ca481b54b8aa Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Thu, 10 Feb 2022 17:29:56 +0200 Subject: [PATCH 14/22] Add card to the top - update --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index 58fa628..3a89cd6 100644 --- a/index.php +++ b/index.php @@ -68,7 +68,7 @@ if ($emails) $data = new stdClass(); $data->title = DECODE_SPECIAL_CHARACTERS ? mb_decode_mimeheader($overview->subject) : $overview->subject; $data->type = "plain"; - $data->order = 1; + $data->order = -time(); if(count($attachments)) { $data->attachments = $attNames; $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode($inbox->fetchMessageBody($emails[$j], 1.1)) : $inbox->fetchMessageBody($emails[$j], 1.1); From dfe2a048d392848b97bebe1b18e04186c897deaf Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Fri, 11 Feb 2022 10:27:23 +0200 Subject: [PATCH 15/22] Decode description if needed. --- index.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.php b/index.php index 3a89cd6..a7efbcd 100644 --- a/index.php +++ b/index.php @@ -75,6 +75,8 @@ if ($emails) } else { $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode($inbox->fetchMessageBody($emails[$j], 1)) : $inbox->fetchMessageBody($emails[$j], 1); } + if(base64_encode(base64_decode($description)) == $description) // if description is base64 encoded, decode it + $description = base64_decode($description); $data->description = $description; $mailSender = new stdClass(); $mailSender->userId = $overview->from[0]->mailbox; From fa3d1eace05f83bd59b9c73a837776eb887dfea2 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Fri, 11 Feb 2022 11:49:49 +0200 Subject: [PATCH 16/22] Better way to handle encoded description --- functions.php | 19 +++++++++++++++++++ index.php | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 functions.php diff --git a/functions.php b/functions.php new file mode 100644 index 0000000..e09abcf --- /dev/null +++ b/functions.php @@ -0,0 +1,19 @@ += count($descarray) - 1) { + $data = base64_decode($data); + } + + return $data; +} + +?> \ No newline at end of file diff --git a/index.php b/index.php index a7efbcd..78aff60 100644 --- a/index.php +++ b/index.php @@ -1,6 +1,7 @@ fetchMessageBody($emails[$j], 1)) : $inbox->fetchMessageBody($emails[$j], 1); } - if(base64_encode(base64_decode($description)) == $description) // if description is base64 encoded, decode it - $description = base64_decode($description); + $description = decodeIfNeeded($description); $data->description = $description; $mailSender = new stdClass(); $mailSender->userId = $overview->from[0]->mailbox; From c728e97165aeb11a054444bc2fa66c80970e2971 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Fri, 11 Feb 2022 13:37:20 +0200 Subject: [PATCH 17/22] Improvements for encoded description. --- functions.php | 19 ------------------- index.php | 9 +++++++-- 2 files changed, 7 insertions(+), 21 deletions(-) delete mode 100644 functions.php diff --git a/functions.php b/functions.php deleted file mode 100644 index e09abcf..0000000 --- a/functions.php +++ /dev/null @@ -1,19 +0,0 @@ -= count($descarray) - 1) { - $data = base64_decode($data); - } - - return $data; -} - -?> \ No newline at end of file diff --git a/index.php b/index.php index 78aff60..e082245 100644 --- a/index.php +++ b/index.php @@ -1,7 +1,6 @@ getNewMessages(); if ($emails) for ($j = 0; $j < count($emails) && $j < 5; $j++) { $structure = $inbox->fetchMessageStructure($emails[$j]); + $base64encode = false; + if($structure->encoding == 3) { + $base64encode = true; // BASE64 + } $attachments = array(); $attNames = array(); if (isset($structure->parts) && count($structure->parts)) { @@ -76,7 +79,9 @@ if ($emails) } else { $description = DECODE_SPECIAL_CHARACTERS ? quoted_printable_decode($inbox->fetchMessageBody($emails[$j], 1)) : $inbox->fetchMessageBody($emails[$j], 1); } - $description = decodeIfNeeded($description); + if($base64encode) { + $description = base64_decode($description); + } $data->description = $description; $mailSender = new stdClass(); $mailSender->userId = $overview->from[0]->mailbox; From 49bd797057812b93013b1a5d4e0601f168cfb536 Mon Sep 17 00:00:00 2001 From: Alexe Puiu Date: Thu, 17 Feb 2022 08:08:48 +0200 Subject: [PATCH 18/22] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94e4365..10c76af 100644 --- a/README.md +++ b/README.md @@ -17,15 +17,17 @@ You can do this in two ways. Here's how the email subject should look like: Update website logo b-'website' s-'to do' -*You can use single or double quotes.* +* *You can use single or double quotes.* -*Case-insensitive for board and stack respectively.* +* *Case-insensitive for board and stack respectively.* ### 2.2: Set the board in the email address At the end of the email address prefix (before @) add "+website" Example: bot+website@ncserver.com +* *If board has multiple words e.g. "some project", you'll have to send the email to bot+some+project@ncserver.com* + In this case, if you don't specify the stack in the email subject, the card will be added in the first stack (if it exists). Note: From 3ac5b2785e24eb0c41eb5bb5b4da3097e1c8a049 Mon Sep 17 00:00:00 2001 From: Alex Puiu Date: Thu, 17 Feb 2022 13:27:00 +0200 Subject: [PATCH 19/22] Use 'reply_to' attribute instead of 'from' to reply & assign sender. --- index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index e082245..31ee957 100644 --- a/index.php +++ b/index.php @@ -84,11 +84,11 @@ if ($emails) } $data->description = $description; $mailSender = new stdClass(); - $mailSender->userId = $overview->from[0]->mailbox; + $mailSender->userId = $overview->reply_to[0]->mailbox; $newcard = new DeckClass(); $response = $newcard->addCard($data, $mailSender, $board); - $mailSender->userId .= "@{$overview->from[0]->host}"; + $mailSender->userId .= "@{$overview->reply_to[0]->host}"; if($response) { $inbox->reply($mailSender->userId, $response); From 73703981ebb67861d90e05a1f89cde888a2d0234 Mon Sep 17 00:00:00 2001 From: kidhab Date: Fri, 4 Mar 2022 11:27:10 +0100 Subject: [PATCH 20/22] Add setting to disable mail notifications --- config.example.php | 1 + index.php | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/config.example.php b/config.example.php index d957e3e..52c6365 100644 --- a/config.example.php +++ b/config.example.php @@ -9,4 +9,5 @@ define("MAIL_USER", "incoming"); define("MAIL_PASSWORD", "****"); define("DECODE_SPECIAL_CHARACTERS", true); //requires mbstring, if false special characters (like öäüß) won't be displayed correctly define("ASSIGN_SENDER", true); // if true, sender will be assigned to card if has NC account +define("MAIL_NOTIFICATION", true); // if true, send notifications when a new card was created or an error occured ?> diff --git a/index.php b/index.php index 31ee957..f73f5e6 100644 --- a/index.php +++ b/index.php @@ -90,10 +90,14 @@ if ($emails) $response = $newcard->addCard($data, $mailSender, $board); $mailSender->userId .= "@{$overview->reply_to[0]->host}"; - if($response) { - $inbox->reply($mailSender->userId, $response); - } else { - $inbox->reply($mailSender->userId); + if(MAIL_NOTIFICATION) { + if($response) { + $inbox->reply($mailSender->userId, $response); + } else { + $inbox->reply($mailSender->userId); + } + } + if(!$response) { foreach($attNames as $attachment) unlink(getcwd() . "/attachments/" . $attachment); } } From 4c3f68a52cb17bc785aa9a882f2cf92c6ca846cf Mon Sep 17 00:00:00 2001 From: Alex Puiu <=> Date: Wed, 13 Apr 2022 13:31:54 +0300 Subject: [PATCH 21/22] Convert HTML description to MD --- .gitignore | 3 +- composer.json | 5 ++ composer.lock | 108 ++++++++++++++++++++++++++++++++++++++++++++ index.php | 18 ++++++-- lib/ConvertToMD.php | 22 +++++++++ lib/DeckClass.php | 1 + lib/MailClass.php | 16 ++++++- 7 files changed, 166 insertions(+), 7 deletions(-) create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 lib/ConvertToMD.php diff --git a/.gitignore b/.gitignore index 4e9b47a..a078a68 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -config.php \ No newline at end of file +config.php +vendor/ \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..5efac28 --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "league/html-to-markdown": "^5.1" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..c693662 --- /dev/null +++ b/composer.lock @@ -0,0 +1,108 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "55d22588d74c4cd2af8b00fa004ab06f", + "packages": [ + { + "name": "league/html-to-markdown", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/html-to-markdown.git", + "reference": "e0fc8cf07bdabbcd3765341ecb50c34c271d64e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/e0fc8cf07bdabbcd3765341ecb50c34c271d64e1", + "reference": "e0fc8cf07bdabbcd3765341ecb50c34c271d64e1", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xml": "*", + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "mikehaertl/php-shellcommand": "^1.1.0", + "phpstan/phpstan": "^0.12.99", + "phpunit/phpunit": "^8.5 || ^9.2", + "scrutinizer/ocular": "^1.6", + "unleashedtech/php-coding-standard": "^2.7", + "vimeo/psalm": "^4.22" + }, + "bin": [ + "bin/html-to-markdown" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\HTMLToMarkdown\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + }, + { + "name": "Nick Cernis", + "email": "nick@cern.is", + "homepage": "http://modernnerd.net", + "role": "Original Author" + } + ], + "description": "An HTML-to-markdown conversion helper for PHP", + "homepage": "https://github.com/thephpleague/html-to-markdown", + "keywords": [ + "html", + "markdown" + ], + "support": { + "issues": "https://github.com/thephpleague/html-to-markdown/issues", + "source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.0" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/html-to-markdown", + "type": "tidelift" + } + ], + "time": "2022-03-02T17:24:08+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.1.0" +} diff --git a/index.php b/index.php index f73f5e6..5bb037b 100644 --- a/index.php +++ b/index.php @@ -1,8 +1,10 @@ getNewMessages(); @@ -64,11 +66,16 @@ if ($emails) $overview = $inbox->headerInfo($emails[$j]); $board = null; - if(strstr($overview->to[0]->mailbox, '+')) { - $board = substr($overview->to[0]->mailbox, strpos($overview->to[0]->mailbox, '+') + 1); - if(strstr($board, '+')) $board = str_replace('+', ' ', $board); - } - + if(isset($overview->{'X-Original-To'}) && strstr($overview->{'X-Original-To'}, '+')) { + $board = strstr(substr($overview->{'X-Original-To'}, strpos($overview->{'X-Original-To'}, '+') + 1), '@', true); + } else { + if(strstr($overview->to[0]->mailbox, '+')) { + $board = substr($overview->to[0]->mailbox, strpos($overview->to[0]->mailbox, '+') + 1); + } + }; + + if(strstr($board, '+')) $board = str_replace('+', ' ', $board); + $data = new stdClass(); $data->title = DECODE_SPECIAL_CHARACTERS ? mb_decode_mimeheader($overview->subject) : $overview->subject; $data->type = "plain"; @@ -82,6 +89,7 @@ if ($emails) if($base64encode) { $description = base64_decode($description); } + $description = (new ConvertToMD($description))->execute(); $data->description = $description; $mailSender = new stdClass(); $mailSender->userId = $overview->reply_to[0]->mailbox; diff --git a/lib/ConvertToMD.php b/lib/ConvertToMD.php new file mode 100644 index 0000000..0549e19 --- /dev/null +++ b/lib/ConvertToMD.php @@ -0,0 +1,22 @@ +converter = new HtmlConverter([ + 'strip_tags' => true, + 'remove_nodes' => 'title' + ]); + $this->html = $html; + } + + public function execute() + { + return $this->converter->convert($this->html); + } +} + +?> \ No newline at end of file diff --git a/lib/DeckClass.php b/lib/DeckClass.php index 72f7ac7..802a821 100644 --- a/lib/DeckClass.php +++ b/lib/DeckClass.php @@ -47,6 +47,7 @@ class DeckClass { } $boards = $this->apiCall("GET", NC_SERVER . "/index.php/apps/deck/api/v1.0/boards"); + $boardId = $boardName = null; foreach($boards as $board) { if(strtolower($board->title) == strtolower($boardFromMail)) { if(!$this->checkBotPermissions($board)) { diff --git a/lib/MailClass.php b/lib/MailClass.php index 3a7ba3d..f498162 100644 --- a/lib/MailClass.php +++ b/lib/MailClass.php @@ -27,7 +27,21 @@ class MailClass { } public function headerInfo($email) { - return imap_headerinfo($this->inbox, $email); + $headerInfo = imap_headerinfo($this->inbox, $email); + $additionalHeaderInfo = imap_fetchheader($this->inbox, $email); + $infos = explode("\n", $additionalHeaderInfo); + + foreach($infos as $info) { + $data = explode(":", $info); + if( count($data) == 2 && !isset($head[$data[0]])) { + if(trim($data[0]) === 'X-Original-To') { + $headerInfo->{'X-Original-To'} = trim($data[1]); + break; + } + } + } + + return $headerInfo; } public function reply($sender, $response = null) { From 7eafb95fcd383a8d415c1b7edddb4f93ad27fb41 Mon Sep 17 00:00:00 2001 From: Alex Puiu <=> Date: Wed, 13 Apr 2022 14:15:58 +0300 Subject: [PATCH 22/22] convert to MD only if needed --- index.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.php b/index.php index 5bb037b..1d8fba6 100644 --- a/index.php +++ b/index.php @@ -89,7 +89,9 @@ if ($emails) if($base64encode) { $description = base64_decode($description); } - $description = (new ConvertToMD($description))->execute(); + if($description != strip_tags($description)) { + $description = (new ConvertToMD($description))->execute(); + } $data->description = $description; $mailSender = new stdClass(); $mailSender->userId = $overview->reply_to[0]->mailbox;