mail icon indicating copy to clipboard operation
mail copied to clipboard

feat(ocs): send a message via api

Open miaulalala opened this issue 1 year ago • 7 comments

Mühselig ernährt sich das Eichhörnchen https://github.com/nextcloud/mail/issues/9450

Sent via api:

image

miaulalala avatar May 27 '24 14:05 miaulalala

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

ChristophWurst avatar May 28 '24 13:05 ChristophWurst

This was for sending an email by another Nextcloud app?

kesselb avatar Jun 25 '24 14:06 kesselb

This was for sending an email by another Nextcloud app?

The workflow engine

miaulalala avatar Jun 25 '24 14:06 miaulalala

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-&gt;find('admin',
        12)
        #1 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(208): OCA\Mail\Controller\MessageApiController-&gt;send(12,
        '[email protected]', 'Hello Hello', '&lt;html&gt;&lt;body&gt;&lt;h1...', true, Array, Array, Array, Array)
        #2 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(114): OC\AppFramework\Http\Dispatcher-&gt;executeController(Object(OCA\Mail\Controller\MessageApiController),
        'send')
        #3 /var/www/html/lib/private/AppFramework/App.php(161): OC\AppFramework\Http\Dispatcher-&gt;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-&gt;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 &quot;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)&quot;; 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-&gt;findOneQuery(Object(OC\DB\QueryBuilder\QueryBuilder))
        #1 /var/www/html/apps-extra/mail/lib/Db/AliasMapper.php(67): OCP\AppFramework\Db\QBMapper-&gt;findEntity(Object(OC\DB\QueryBuilder\QueryBuilder))
        #2 /var/www/html/apps-extra/mail/lib/Service/AliasesService.php(57): OCA\Mail\Db\AliasMapper-&gt;findByAlias('[email protected]',
        'admin')
        #3 /var/www/html/apps-extra/mail/lib/Controller/MessageApiController.php(77): OCA\Mail\Service\AliasesService-&gt;findByAliasAndUserId('[email protected]',
        'admin')
        #4 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(208): OCA\Mail\Controller\MessageApiController-&gt;send(10,
        '[email protected]', 'Hello Hello', '&lt;html&gt;&lt;body&gt;&lt;h1...', true, Array, Array, Array, Array)
        #5 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(114): OC\AppFramework\Http\Dispatcher-&gt;executeController(Object(OCA\Mail\Controller\MessageApiController),
        'send')
        #6 /var/www/html/lib/private/AppFramework/App.php(161): OC\AppFramework\Http\Dispatcher-&gt;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-&gt;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.

kesselb avatar Jun 28 '24 14:06 kesselb

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 ;)

kesselb avatar Jun 28 '24 14:06 kesselb

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!

kesselb avatar Jun 28 '24 15:06 kesselb

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.

kesselb avatar Jun 28 '24 15:06 kesselb

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.

miaulalala avatar Jul 02 '24 08:07 miaulalala

Mind to change "AGPL-3.0-only" to "AGPL-3.0-or-later"?

kesselb avatar Jul 03 '24 13:07 kesselb

Is this ready for merge?

ChristophWurst avatar Jul 09 '24 07:07 ChristophWurst

Is this ready for merge?

Yes - if you can look through it too?

miaulalala avatar Jul 09 '24 09:07 miaulalala

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);
}

kesselb avatar Jul 11 '24 15:07 kesselb