Repository: frappe/frappe

Issue:
sendmail stopped working after recent changes
## Description of the issue

The functionality to send mail stopped working after upgrading frappe to the latest version. 

## Context information (for bug reports)

**Output of `bench version`**
```
$ bench version
community 0.0.1
frappe 13.x.x-develop
mon_school 0.0.1
```

Not sure why bench is returning frappe version as 13.x.

```
$ python -c "import frappe; print(frappe.__version__)"
14.0.0-dev
```

And frappe is at this revision:
```
$ git show HEAD
commit 8e9432e40d7831208481e801366198e1271636c5 (HEAD -> develop, upstream/develop, upstream/HEAD)
Merge: e8d79030f7 ff21cd4fce
Author: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Date:   Fri May 21 11:18:24 2021 +0000

    Merge pull request #13282 from revant/fix-node-utils

    fix: set correct default redis async broker in node_utils
```

## Steps to reproduce the issue

1. open bench console. I've done this on my prod instance with sendmail email account already configred.

        $ bench --site mon.school console

2. Try to send email using frappe.sendmail and it fails.

          frappe.sendmail(['anand@fossunited.org'], sender='noreply@mon.school', subject='Welcome to Mon School', message='Dear Anand, welcome to Mon School!', delayed=False)

### Observed result

The mail is not going because frappe is swapping the `to` and `from`.

### Expected result

Mail should have been sent. 

### Stacktrace / full error message

```
In [1]: frappe.sendmail(['anand@fossunited.org'], sender='noreply@mon.school', subject='Welcome to Mon School', message='Dear Anand, welcome to Mon School!', delayed=False)


SMTPRecipientsRefused                     Traceback (most recent call last)
~/frappe-bench/apps/frappe/frappe/commands/utils.py in <module>
----> 1 frappe.sendmail(['anand@fossunited.org'], sender='noreply@mon.school', subject='Welcome to Mon School', message='Dear Anand, welcome to Mon School!', delayed=False)

~/frappe-bench/apps/frappe/frappe/__init__.py in sendmail(recipients, sender, subject, message, as_markdown, delayed, reference_doctype, reference_name, unsubscribe_method, unsubscribe_params, unsubscribe_message, add_unsubscribe_link, attachments, content, doctype, name, reply_to, queue_separately, cc, bcc, message_id, in_reply_to, send_after, expose_recipients, send_priority, communication, retry, now, read_receipt, is_notification, inline_images, template, args, header, print_letterhead, with_container)
    530
    531         from frappe.email import queue
--> 532         queue.send(recipients=recipients, sender=sender,
    533                 subject=subject, message=message, text_content=text_content,
    534                 reference_doctype = doctype or reference_doctype, reference_name = name or reference_name, add_unsubscribe_link=add_unsubscribe_link,

~/frappe-bench/apps/frappe/frappe/email/queue.py in send(recipients, sender, subject, message, text_content, reference_doctype, reference_name, unsubscribe_method, unsubscribe_params, unsubscribe_message, attachments, reply_to, cc, bcc, message_id, in_reply_to, send_after, expose_recipients, send_priority, communication, now, read_receipt, queue_separately, is_notification, add_unsubscribe_link, inline_images, header, print_letterhead, with_container)
    138
    139         # add to queue
--> 140         add(recipients, sender, subject,
    141                 formatted=email_content,
    142                 text_content=email_text_context,

~/frappe-bench/apps/frappe/frappe/email/queue.py in add(recipients, sender, subject, **kwargs)
    186                 email_queue = get_email_queue(recipients, sender, subject, **kwargs)
    187                 if kwargs.get('now'):
--> 188                         email_queue.send()
    189
    190 def get_email_queue(recipients, sender, subject, **kwargs):

~/frappe-bench/apps/frappe/frappe/email/doctype/email_queue/email_queue.py in send(self, is_background_task)
    103                                 message = ctx.build_message(recipient.recipient)
    104                                 if not frappe.flags.in_test:
--> 105                                         ctx.smtp_session.sendmail(recipient.recipient, self.sender, message)
    106                                 ctx.add_to_sent_list(recipient)
    107

/usr/lib/python3.8/smtplib.py in sendmail(self, from_addr, to_addrs, msg, mail_options, rcpt_options)
    883             # the server refused all our recipients
    884             self._rset()
--> 885             raise SMTPRecipientsRefused(senderrs)
    886         (code, resp) = self.data(msg)
    887         if code != 250:

SMTPRecipientsRefused: {'Mon School <noreply@mon.school>': (550, b'5.1.1 <noreply@mon.school>: Recipient address rejected: User unknown in local recipient table')}

```
And in the mail server log,  I notice:

```
May 22 05:14:05 alpha postfix/smtpd[382175]: NOQUEUE: reject: RCPT from localhost[127.0.0.1]: 550 5.1.1 <noreply@mon.school>: Recipient address rejected: User unknown in local recipient table; from=<anand@fossunited.org> to=<noreply@mon.school> proto=ESMTP helo=<alpha.fossunited.org>
```

Notice that the `from` and `to` are reversed in the mail server logs.

## Additional information

It looks like this functionality got broken in the commit 59bfc12da6fe077f2c3cfd9dfc527de3aa95b43a.

The culprint seems to be:

https://github.com/frappe/frappe/blob/develop/frappe/email/doctype/email_queue/email_queue.py#L105

```
Line 105:           ctx.smtp_session.sendmail(recipient.recipient, self.sender, message)
```

The order of recipient and sender are in the reverse oder.  For reference, see help on `smtplib.SMTP.sendmail`.

```
$ pydoc smtplib.SMTP.sendmail
Help on function sendmail in smtplib.SMTP:

smtplib.SMTP.sendmail = sendmail(self, from_addr, to_addrs, msg, mail_options=(), rcpt_options=())
    This command performs an entire mail transaction.

    The arguments are:
        - from_addr    : The address sending this mail.
        - to_addrs     : A list of addresses to send this mail to.  A bare
                         string will be treated as a list with 1 address.
        - msg          : The message to send.
        - mail_options : List of ESMTP options (such as 8bitmime) for the
                         mail command.
        - rcpt_options : List of ESMTP options (such as DSN commands) for
                         all the rcpt commands.
```


Discussion:

Resolving patch:
diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py
--- a/frappe/email/doctype/email_queue/email_queue.py
+++ b/frappe/email/doctype/email_queue/email_queue.py
@@ -102,7 +102,7 @@ def send(self, is_background_task=False):
 
 				message = ctx.build_message(recipient.recipient)
 				if not frappe.flags.in_test:
-					ctx.smtp_session.sendmail(recipient.recipient, self.sender, message)
+					ctx.smtp_session.sendmail(from_addr=self.sender, to_addrs=recipient.recipient, msg=message)
 				ctx.add_to_sent_list(recipient)
 
 			if frappe.flags.in_test:


Test patch: