diff --git a/doc/REST-CalDAV-CardDAV/Mail.md b/doc/REST-CalDAV-CardDAV/Mail.md
index f4fb4cf886..582350db03 100644
--- a/doc/REST-CalDAV-CardDAV/Mail.md
+++ b/doc/REST-CalDAV-CardDAV/Mail.md
@@ -9,9 +9,9 @@ Implemented requests (relative to https://example.org/egroupware/groupdav.php)
Example: Querying available identities / signatures
```bash
-curl -i https://example.org/egroupware/mail --user -H 'Accept: application/json'
+curl -i https://example.org/egroupware/groupdav.php/mail --user -H 'Accept: application/json'
HTTP/1.1 200 OK
-Content-Type: application/json; charset=utf-8
+Content-Type: application/json
{
"responses": {
@@ -23,7 +23,7 @@ Content-Type: application/json; charset=utf-8
```
-- ```POST /mail[/]``` send mail for default or given account
+- ```POST /mail[/]``` send mail for default or given identity
Example: Sending mail
@@ -31,7 +31,7 @@ The content of the POST request is a JSON encoded object with following attribut
- ```to```: array of strings with (RFC882) email addresses like ```["info@egroupware.org", "Ralf Becker \
+```
+curl -i https://example.org/egroupware/groupdav.php/mail/ --user \
-X POST -H 'Content-Type: application/json' \
- --content `{"to":["info@egroupware.org"],"subject":"Testmail","body":"This is a test :)\n\nRegards"}`
-HTTP/1.1 204 No Content
+ --data-binary '{"to":["info@egroupware.org"],"subject":"Testmail","body":"This is a test :)\n\nRegards"}'
+HTTP/1.1 200 Ok
+Content-Type: application/json
+
+{
+ "status": 200,
+ "message": "Mail successful sent"
+}
```
If you are not authenticated you will get:
```
@@ -61,9 +68,8 @@ If there is an error sending the mail you will get:
```
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
-Content-Length: ...
-{"error": 123,"message":"SMTP Server not reachable"}
+{"error": 500,"message":"SMTP Server not reachable"}
```
@@ -79,21 +85,7 @@ Content-Type: application/json
{
"status": 200,
- "message": "Request to open compose window sent",
- "extra": {
- "preset": {
- "to": [
- "Birgit Becker This is a test :)\n\nRegards",
- "mimeType": "html",
- "identity": "52"
- }
- }
+ "message": "Request to open compose window sent"
}
```
- user is not online, therefore compose window can NOT be opened
@@ -116,7 +108,7 @@ The content of the POST request is the attachment, a Location header in the resp
to use in further requests, instead of the attachment.
```
-curl -i https://example.org/egroupware/mail/attachment/ --user \
+curl -i https://example.org/egroupware/groupdav.php/mail/attachment/ --user \
--data-binary @ -H 'Content-Type: '
HTTP/1.1 204 No Content
Location: https://example.org/egroupware/mail/attachment/
diff --git a/mail/inc/class.mail_compose.inc.php b/mail/inc/class.mail_compose.inc.php
index 456fda00f0..4b4faec804 100644
--- a/mail/inc/class.mail_compose.inc.php
+++ b/mail/inc/class.mail_compose.inc.php
@@ -1006,7 +1006,7 @@ class mail_compose
}
if(!empty($remember)) $content = array_merge($content,$remember);
}
- foreach(array('to','cc','bcc','subject','body','mimeType') as $name)
+ foreach(array('to','cc','bcc','subject','body','mimeType','replyto','priority') as $name)
{
//always handle mimeType
if ($name=='mimeType' && !empty($_REQUEST['preset'][$name]))
@@ -2504,7 +2504,7 @@ class mail_compose
$disableRuler = false;
$signature = $_identity['ident_signature'];
$sigAlreadyThere = $this->mailPreferences['insertSignatureAtTopOfMessage']!='no_belowaftersend'?1:0;
- if ($sigAlreadyThere)
+ if ($sigAlreadyThere && empty($_formData['add_signature']))
{
// note: if you use stationery ' s the insert signatures at the top does not apply here anymore, as the signature
// is already part of the body, so the signature part of the template will not be applied.
diff --git a/mail/src/ApiHandler.php b/mail/src/ApiHandler.php
index 2c8d648b25..24bace43b7 100644
--- a/mail/src/ApiHandler.php
+++ b/mail/src/ApiHandler.php
@@ -58,31 +58,24 @@ class ApiHandler extends Api\CalDAV\Handler
try {
if (str_starts_with($path, '/mail/attachments/'))
{
- $attachment_path = tempnam($GLOBALS['egw_info']['server']['temp_dir'], 'attach--'.
- (str_replace('/', '-', substr($options['path'], 18)) ?: 'no-name').'--');
- if (file_put_contents($attachment_path, $options['content']))
- {
- header('Location: '.($location = '/mail/attachments/'.substr(basename($attachment_path), 8)));
- echo json_encode([
- 'status' => 200,
- 'message' => 'Attachment stored',
- 'location' => $location,
- ], self::JSON_RESPONSE_OPTIONS);
- return '200 Ok';
- }
- throw new \Exception('Error storing attachment');
+ return self::storeAttachment($path, $options['content']);
}
- elseif (preg_match('#^/mail/((\d+):(\d+)/)?(compose)?#', $path, $matches))
+ elseif (preg_match('#^/mail(/(\d+))?(/compose)?#', $path, $matches))
{
- $acc_id = $matches[2] ?? null;
- $ident_id = $matches[3] ?? null;
- $do_compose = (bool)($matches[4] ?? false);
+ $ident_id = $matches[2] ?? self::defaultIdentity($user);
+ $do_compose = (bool)($matches[3] ?? false);
if (!($data = json_decode($options['content'], true)))
{
throw new \Exception('Error decoding JSON: '.json_last_error_msg(), 422);
}
// ToDo: check required attributes
+ $preset = array_filter(array_intersect_key($data, array_flip(['to', 'cc', 'bcc', 'replyto', 'subject', 'priority']))+[
+ 'body' => $data['bodyHtml'] ?? null ?: $data['body'] ?? '',
+ 'mimeType' => !empty($data['bodyHtml']) ? 'html' : 'plain',
+ 'identity' => $ident_id,
+ ]+self::prepareAttachments($data['attachments'] ?? [], $data['attachmentType'] ?? 'attach', $do_compose));
+
// for compose we need to construct a URL and push it to the client (or give an error if the client is not online)
if ($do_compose)
{
@@ -91,39 +84,38 @@ class ApiHandler extends Api\CalDAV\Handler
$account_lid = Api\Accounts::id2name($user);
throw new \Exception("User '$account_lid' (#$user) is NOT online", 404);
}
- $extra = [
- //'menuaction' => 'mail.mail_compose.compose',
- 'preset' => array_filter(array_intersect_key($data, array_flip(['to', 'cc', 'bcc', 'subject']))+[
- 'body' => $data['bodyHtml'] ?? null ?: $data['body'] ?? '',
- 'mimeType' => !empty($data['bodyHtml']) ? 'html' : 'plain',
- 'identity' => $ident_id,
- ]+self::prepareAttachments($data['attachments'] ?? [], $data['attachmentType'] ?? 'attach')),
- ];
$push = new Api\Json\Push($user);
- //$push->call('egw.open_link', $link, '_blank', '640x1024');
- $push->call('egw.open', '', 'mail', 'add', $extra, '_blank', 'mail');
+ $push->call('egw.open', '', 'mail', 'add', ['preset' => $preset], '_blank', 'mail');
header('Content-Type: application/json');
echo json_encode([
'status' => 200,
'message' => 'Request to open compose window sent',
- 'extra' => $extra,
+ //'data' => $preset,
], self::JSON_RESPONSE_OPTIONS);
return true;
}
- $compose = new mail_compose($acc_id);
- $compose->compose([
- 'mailaccount' => $acc_id.':'.$ident_id,
- 'mail_plaintext' => $data['body'] ?? null,
- 'mail_htmltext' => $data['bodyHtml'] ?? null,
- 'mimeType' => !empty($data['bodyHtml']) ? 'html' : 'plain',
- 'file' => array_map(__CLASS__.'::uploadsForAttachments', $data['attachments'] ?? []),
- ]+array_diff_key($data+array_flip(['attachments', 'body', 'bodyHtml'])));
+ $compose = new \mail_compose($acc_id=Api\Mail\Account::read_identity($ident_id)['acc_id']);
+ $preset = array_filter([
+ 'mailaccount' => $acc_id,
+ 'mailidentity' => $ident_id,
+ 'identity' => null,
+ 'add_signature' => true, // add signature in send, independent what preference says
+ ]+$preset);
+ if ($compose->send($preset))
+ {
+ header('Content-Type: application/json');
+ echo json_encode([
+ 'status' => 200,
+ 'message' => 'Mail successful sent',
+ //'data' => $preset,
+ ], self::JSON_RESPONSE_OPTIONS);
+ return true;
+ }
+ throw new \Exception($compose->error_info);
}
- header('Content-Type: application/json');
- echo $options['content'];
- return true;
+ throw new \Exception('Not Found', 404);
}
catch (\Throwable $e) {
_egw_log_exception($e);
@@ -142,15 +134,64 @@ class ApiHandler extends Api\CalDAV\Handler
}
}
+ /**
+ * Store uploaded attachment and return token
+ *
+ * @param string $path
+ * @param string|stream $content
+ * @return string HTTP status
+ * @throws \Exception on error
+ */
+ protected static function storeAttachment(string $path, $content)
+ {
+ $attachment_path = tempnam($GLOBALS['egw_info']['server']['temp_dir'], 'attach--'.
+ (str_replace('/', '-', substr($path, 18)) ?: 'no-name').'--');
+ if (file_put_contents($attachment_path, $content))
+ {
+ header('Location: '.($location = '/mail/attachments/'.substr(basename($attachment_path), 8)));
+ echo json_encode([
+ 'status' => 200,
+ 'message' => 'Attachment stored',
+ 'location' => $location,
+ ], self::JSON_RESPONSE_OPTIONS);
+ return '200 Ok';
+ }
+ throw new \Exception('Error storing attachment');
+ }
+
+ /**
+ * Get default identity of user
+ *
+ * @param int $user
+ * @return int ident_id
+ * @throws Api\Exception\WrongParameter
+ * @throws \Exception (404) if user has no IMAP account
+ */
+ protected static function defaultIdentity(int $user)
+ {
+ foreach(Api\Mail\Account::search($user,false) as $acc_id => $account)
+ {
+ // do NOT add SMTP only accounts as identities
+ if (!$account->is_imap(false)) continue;
+
+ foreach($account->identities($acc_id) as $ident_id => $identity)
+ {
+ return $ident_id;
+ }
+ }
+ throw new \Exception("No IMAP account found for user #$user", 404);
+ }
+
/**
* Convert an attachment name into an upload array for mail_compose::compose
*
* @param string[]? $attachments either "/mail/attachments/" / file in temp_dir or VFS path
* @param string? $attachmentType "attach" (default), "link", "share_ro", "share_rw"
+ * @param bool $compose true: for compose window, false: to send
* @return array with values for keys "file", "name" and "filemode"
* @throws Exception if file not found or unreadable
*/
- protected static function prepareAttachments(array $attachments, string $attachmentType=null)
+ protected static function prepareAttachments(array $attachments, string $attachmentType=null, bool $compose=true)
{
$ret = [];
foreach($attachments as $attachment)
@@ -161,14 +202,20 @@ class ApiHandler extends Api\CalDAV\Handler
{
throw new \Exception("Attachment $attachment NOT found", 400);
}
- $ret['file'][] = $path;
- $ret['name'][] = $matches[2];
- /*return [
- 'name' => $matches[2],
- 'type' => Api\Vfs::mime_content_type($path),
- 'file' => $path,
- 'size' => filesize($path),
- ];*/
+ if ($compose)
+ {
+ $ret['file'][] = $path;
+ $ret['name'][] = $matches[2];
+ }
+ else
+ {
+ $ret['attachments'][] = [
+ 'name' => $matches[2],
+ 'type' => Api\Vfs::mime_content_type($path),
+ 'file' => $path,
+ 'size' => filesize($path),
+ ];
+ }
}
else
{
@@ -176,14 +223,20 @@ class ApiHandler extends Api\CalDAV\Handler
{
throw new \Exception("Attachment $attachment NOT found", 400);
}
- $ret['file'][] = Api\Vfs::PREFIX.$attachment;
- $ret['name'][] = Api\Vfs::basename($attachment);
- /*return [
- 'name' => Api\Vfs::basename($attachment),
- 'type' => Api\Vfs::mime_content_type($attachment),
- 'file' => Api\Vfs::PREFIX.$attachment,
- 'size' => filesize(Api\Vfs::PREFIX.$attachment),
- ];*/
+ if ($compose)
+ {
+ $ret['file'][] = Api\Vfs::PREFIX.$attachment;
+ $ret['name'][] = Api\Vfs::basename($attachment);
+ }
+ else
+ {
+ $ret['attachments'][] = [
+ 'name' => Api\Vfs::basename($attachment),
+ 'type' => Api\Vfs::mime_content_type($attachment),
+ 'file' => Api\Vfs::PREFIX.$attachment,
+ 'size' => filesize(Api\Vfs::PREFIX.$attachment),
+ ];
+ }
}
}
if ($ret)