feat(ocs): send a message via api
Mühselig ernährt sich das Eichhörnchen https://github.com/nextcloud/mail/issues/9450
Sent via api:
PRs don't go onto the board if they have a ticket. Else you are tracking two items for one task ;)
Edit: seems github doesn't show my action in the history. I removed the PR from https://github.com/orgs/nextcloud/projects/61
This was for sending an email by another Nextcloud app?
This was for sending an email by another Nextcloud app?
The workflow engine
Response (with debug = false, loglevel = 2) when an account does not exist.
<?xml version="1.0"?>
<ocs>
<meta>
<status>failure</status>
<statuscode>404</statuscode>
<message></message>
</meta>
<data>OCA\Mail\Exception\ClientException: Account 12 does not exist or you don\'t have permission to access it in
/var/www/html/apps-extra/mail/lib/Service/AccountService.php:101
Stack trace:
#0 /var/www/html/apps-extra/mail/lib/Controller/MessageApiController.php(69): OCA\Mail\Service\AccountService->find('admin',
12)
#1 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(208): OCA\Mail\Controller\MessageApiController->send(12,
'[email protected]', 'Hello Hello', '<html><body><h1...', true, Array, Array, Array, Array)
#2 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(114): OC\AppFramework\Http\Dispatcher->executeController(Object(OCA\Mail\Controller\MessageApiController),
'send')
#3 /var/www/html/lib/private/AppFramework/App.php(161): OC\AppFramework\Http\Dispatcher->dispatch(Object(OCA\Mail\Controller\MessageApiController),
'send')
#4 /var/www/html/lib/private/Route/Router.php(309): OC\AppFramework\App::main('OCA\\Mail\\Contro...', 'send',
Object(OC\AppFramework\DependencyInjection\DIContainer), Array)
#5 /var/www/html/ocs/v1.php(43): OC\Route\Router->match('/ocsapp/apps/ma...')
#6 /var/www/html/ocs/v2.php(7): require_once('/var/www/html/o...')
#7 {main}
</data>
</ocs>
Response (debug = false, loglevel = 2) when alias does not exist
<?xml version="1.0"?>
<ocs>
<meta>
<status>failure</status>
<statuscode>404</statuscode>
<message></message>
</meta>
<data>OCP\AppFramework\Db\DoesNotExistException: Did expect one result but found none when executing: query "SELECT
`aliases`.*, `accounts`.`provisioning_id` FROM `*PREFIX*mail_aliases` `aliases` INNER JOIN
`*PREFIX*mail_accounts` `accounts` ON `aliases`.`account_id` = `accounts`.`id` WHERE (`accounts`.`user_id` =
:dcValue1) AND (`aliases`.`alias` = :dcValue2)"; in
/var/www/html/lib/public/AppFramework/Db/QBMapper.php:260
Stack trace:
#0 /var/www/html/lib/public/AppFramework/Db/QBMapper.php(338): OCP\AppFramework\Db\QBMapper->findOneQuery(Object(OC\DB\QueryBuilder\QueryBuilder))
#1 /var/www/html/apps-extra/mail/lib/Db/AliasMapper.php(67): OCP\AppFramework\Db\QBMapper->findEntity(Object(OC\DB\QueryBuilder\QueryBuilder))
#2 /var/www/html/apps-extra/mail/lib/Service/AliasesService.php(57): OCA\Mail\Db\AliasMapper->findByAlias('[email protected]',
'admin')
#3 /var/www/html/apps-extra/mail/lib/Controller/MessageApiController.php(77): OCA\Mail\Service\AliasesService->findByAliasAndUserId('[email protected]',
'admin')
#4 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(208): OCA\Mail\Controller\MessageApiController->send(10,
'[email protected]', 'Hello Hello', '<html><body><h1...', true, Array, Array, Array, Array)
#5 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(114): OC\AppFramework\Http\Dispatcher->executeController(Object(OCA\Mail\Controller\MessageApiController),
'send')
#6 /var/www/html/lib/private/AppFramework/App.php(161): OC\AppFramework\Http\Dispatcher->dispatch(Object(OCA\Mail\Controller\MessageApiController),
'send')
#7 /var/www/html/lib/private/Route/Router.php(309): OC\AppFramework\App::main('OCA\\Mail\\Contro...', 'send',
Object(OC\AppFramework\DependencyInjection\DIContainer), Array)
#8 /var/www/html/ocs/v1.php(43): OC\Route\Router->match('/ocsapp/apps/ma...')
#9 /var/www/html/ocs/v2.php(7): require_once('/var/www/html/o...')
#10 {main}
</data>
</ocs>
Unless it's supposed like that in ocs, we should avoid leaking the actual exceptions via ocs.
Request with malformed recipients
POST https://server.internal/ocs/v2.php/apps/mail/message/send
Authorization: Basic admin admin
Content-Type: application/json
#Cookie: XDEBUG_SESSION=start
{
"accountId": 10,
"fromEmail": "[email protected]",
"subject": "Hello Hello",
"body": "<html><body><h1>Hello Hello</h1><p style=\"font-color: red\">Hello Hello</p></body></html>",
"isHtml": 1,
"to": [
"[email protected]",
"[email protected]"
],
"cc": [],
"bcc": []
}
Response
<?xml version="1.0"?>
<ocs>
<meta>
<status>failure</status>
<statuscode>500</statuscode>
<message>Internal Server Error
</message>
</meta>
<data/>
</ocs>
The response should be more helpful ;)
Request as x-www-form-urlencoded:
POST https://server.internal/ocs/v2.php/apps/mail/message/send
Authorization: Basic admin admin
Content-Type: application/x-www-form-urlencoded
Cookie: XDEBUG_SESSION=start
accountId = 10 &
fromEmail = [email protected] &
subject = Hello Hello www-form-urlencoded &
body = <html><body><h1>Hello Hello</h1><p style="font-color: red">Hello Hello</p></body></html> &
isHtml = 1 &
to[0][email] = [email protected] &
to[0][label] = Alice &
to[1][email] = [email protected] &
to[1][label] = Bob
Request as application/json
POST https://server.internal/ocs/v2.php/apps/mail/message/send
Authorization: Basic admin admin
Content-Type: application/json
Cookie: XDEBUG_SESSION=start
{
"accountId": 10,
"fromEmail": "[email protected]",
"subject": "Hello Hello application/json",
"body": "<html><body><h1>Hello Hello</h1><p style=\"font-color: red\">Hello Hello</p></body></html>",
"isHtml": 1,
"to": [
{
"email": "[email protected]",
"label": "Alice"
},
{
"email": "[email protected]",
"label": "Bob"
}
],
"cc": [],
"bcc": []
}
Request as multipart/form-data
POST https://server.internal/ocs/v2.php/apps/mail/message/send
Authorization: Basic admin admin
Content-Type: multipart/form-data; boundary=WebAppBoundary
Cookie: XDEBUG_SESSION=start
--WebAppBoundary
Content-Disposition: form-data; name="accountId"
10
--WebAppBoundary
Content-Disposition: form-data; name="fromEmail"
[email protected]
--WebAppBoundary
Content-Disposition: form-data; name="subject"
Hello Hello multipart/form-data
--WebAppBoundary
Content-Disposition: form-data; name="body"
<html><body><h1>Hello Hello</h1><p style="font-color: red">Hello Hello</p></body></html>
--WebAppBoundary
Content-Disposition: form-data; name="isHtml"
1
--WebAppBoundary
Content-Disposition: form-data; name="to[0][email]"
[email protected]
--WebAppBoundary
Content-Disposition: form-data; name="to[0][label]"
Alice
--WebAppBoundary
Content-Disposition: form-data; name="to[1][email]"
[email protected]
--WebAppBoundary
Content-Disposition: form-data; name="to[1][label]"
Bob
--WebAppBoundary
Content-Disposition: form-data; name="attachments[]"; filename="sample.txt"
Content-Type: text/plain
I'm an attachment, please attach me!
I guess it's a side effect of using ocs that three different ways of formatting the body are supported, but it's a bit weird that uploading files only works with one.
Moreover, using multipart/form-data does not feel very "rest-like". I would prefer to only accept a request in JSON and include possible attachments into the JSON.
I guess it's a side effect of using ocs that three different ways of formatting the body are supported, but it's a bit weird that uploading files only works with one.
Moreover, using multipart/form-data does not feel very "rest-like". I would prefer to only accept a request in JSON and include possible attachments into the JSON.
Christoph and I had this discussion and have decided that that's the best way to do it. Yes, it's not very REST- like, I agree, but it is what it is.
Mind to change "AGPL-3.0-only" to "AGPL-3.0-or-later"?
Is this ready for merge?
Is this ready for merge?
Yes - if you can look through it too?
I've accidentally run into the following problem while testing:
The recipient "daniel [email protected]" returns an 500er response. That's correct because the email is wrong, but the error comes from the mail transmission / horde already. We should have some basic validation in the controller for the email address.
For example:
$mightBeValidEmail = filter_var($recipient['email'], FILTER_VALIDATE_EMAIL);
if ($mightBeValidEmail === false) {
return new DataResponse('Email address looks weird.', Http::STATUS_BAD_REQUEST);
}