Email with multiple attachments (from POST) - django

Although I am using Mailgun as my SMTP gateway, this question specifically relates to Django and forms.
A user sends an email to something#mydomain.com. The subject starts with the text "another".
The email is intercepted by MailGun.
MailGun creates a POST to my code.
My code is supposed to take that email and forward it to another#mydomain.com (the first word of the subject) with the following requirements:
Remove "another" from the subject line.
Set the FROM to the person that sent the email
Retain all of the text (html or plain) from the body of the email.
Retain all of the attachments from the original email.
Sounds simple enough but I'm having a problem with having it send multiple attachments. And, I haven't even tried to figure out #1 and #3. (Yes, I'm kind of new to this.)
Here is what I have so far:
#public
#csrf_exempt
def send_email(request):
if request.method == 'POST':
mail_data = request.FILES
sender = request.POST.get('sender')
subject = request.POST.get('subject', '')
bodyhtml = request.POST.get('body-html', 'i am not found')
mail = EmailMultiAlternatives(subject, bodyhtml, sender, ["another#mydomain.com"])
for field, value in mail_data.items():
mail.attach(value.name, value.read(), value.content_type)
mail.content_subtype = "html"
mail.send()
else:
# Just for my own testing.
return HttpResponse('Not Posted')
return HttpResponse('OK')
Can you help me modify this so that I can attach multiple files? I can probably figure out the other items (but if you have any suggestions that would be appreciated). I also know that anyone can send email and that is a security risk. I'm planning on checking the FROM address before actually sending any email. That should take care of that problem, but, again if you have any suggestions that would be appreciated.
Thanks

Related

Django: dynamic changing email recepients

I making a feedback form on website. I made model called 'globalapp' with all settings for future admin, it have email,address and phone fields without permissions for add or delete this objects.
In my views i have a simple code:
def index(request):
seos = SEO.objects.get(id__exact=1)
socs = Social_networks.objects.get(id__exact=1)
globs = globalapp.objects.get(id__exact=1)
index = Index.objects.get(id__exact=1)
form = ContactForm(request.POST)
if form.is_valid():
subject = form.cleaned_data['subject']
sender = form.cleaned_data['sender']
message = form.cleaned_data['message']
fille = form.cleaned_data['fille']
recepients = ['test#test.ru']
from_email, to = sender, recepients
html_content = loader.render_to_string('globalapp/chunks/email_tpl.html',
{'subject': subject, 'sender':sender, 'message':message, 'fille':fille})
msg = EmailMultiAlternatives(subject, html_content, from_email, to)
msg.send()
return render(request, 'globalapp/index.html', {'seos': seos,
'socs': socs,
'globs': globs,
'index': index,
'form': form })
Right now mail sending on test#test.ru. I want to take email field from globalapp object, and put it in 'recepients', to give admin ability to change email address when he needs it.
The best thing that i get yet, i get email value by queryset with:
email = globalapp.objects.filter(id=1).values('email')
by in mail ive got only To: {'email': 'test#gmail.com'}
So question is how to get string from queryset object for dynamic email recepients changing? Or maybe i have option how to do it other way?
Also i have another little problem, that i cant deal with yet: after i push submit button, my page reloading, and i dont need it, can i disable it somehow?
Well there are two problems here:
you use .filter(..), and a filter means you do not get a single dictionary, but a QuerySet of dictionaries. This can be empty, contain one, or multiple elements. Since you filter on id=..., it will contan at most one element, but still it will require some extra logic to unwrap it out of the QuerySet, so we better use .get(..) here; and
we obtain a dictionary, we can retrieve the element associated with the key, by performing an element lookup, so the_dict['email'].
We can tus obtain the email address with:
email = globalapp.objects.values('email').get(id=1)['email']
Or perhaps more elegant:
email = globalapp.objects.values_list('email', flat=True).get(id=1)
Also i have another little problem, that i cant deal with yet: after i push submit button, my page reloading, and i dont need it, can i disable it somehow?
Not with a form, since that is exactly the task the browser is supposed to carry out: send a HTTP request, and load the response. But you can use an AJAX call to perform a HTTP request while the webpage still remains the same.

request.POST.get() always returning None

I am completely new to django , you can I just started it today
when I am making a POST request with parameters using postman , I am always getting None for email,password, name and other variables
#csrf_exempt
def signup(request):
if request.method != 'POST':
raise Http404
email = request.POST.get('email')
password = request.POST.get('password')
name = request.POST.get('name')
os = request.POST.get('os')
device_id = request.POST.get('device_id')
version = request.POST.get('version')
device = request.POST.get('device')
print "email value is = %s", email
user=AppUser.objects.get_or_create(email=password,password=password)
user.save()
return HttpResponse(json.dumps({"result": True}), content_type='application/json')
Please help , Why it is always showing None even though I am passing values from POST request for email and for other parameters
Below is the body request from post man using POST
http://127.0.0.1:8000/v1.0/signup/?email=nagu#nagu.com&password=nagendra&name=nagendra&os=android&device_id=12345678&version=23.0.1&device=samsung
below postman screen shot
The parameters you added to the url are GET parameters not POST parameters. POST parameters are in the request body and not visible through urls. Even you specify your request method is POST with your original url, you are not going to send any data.
If you in your commandline do something like:
curl --data "email=nagu#nagu.com&password=nagendra&name=nagendra&os=android&device_id=12345678&version=23.0.1&device=samsung" http://127.0.0.1:8000/v1.0/signup/
It should send POST data to your view.
Take a look at this SO question and answer on how POST requests are delivered.
I have tried postman in chrome.
Below is the screenshot.
Does this work for you?
please make sure your parameter of body in postman is correct, it
should be "x-www-form-urlencoded".
THKS!
check your header
you probably may have
Content-type set to application/json

Control a function (E.g send mail) from a users profile page

I have a profile page like so: http://i.stack.imgur.com/Rx4kg.png . In management I would like a option "Notify by mail" that would control my send_email functions in every application I want. As example I'm using django-messages and it sends private messages aswell as emails when you send a message. I would like for the user to be able to specify if he wants emails aswell when he gets a message.
messages/utils.py
def new_message_email(sender, instance, signal,
subject_prefix=_(u'New Message: %(subject)s'),
template_name="messages/new_message.html",
default_protocol=None,
*args, **kwargs):
"""
This function sends an email and is called via Django's signal framework.
Optional arguments:
``template_name``: the template to use
``subject_prefix``: prefix for the email subject.
``default_protocol``: default protocol in site URL passed to template
"""
if default_protocol is None:
default_protocol = getattr(settings, 'DEFAULT_HTTP_PROTOCOL', 'http')
if 'created' in kwargs and kwargs['created']:
try:
current_domain = Site.objects.get_current().domain
subject = subject_prefix % {'subject': instance.subject}
message = render_to_string(template_name, {
'site_url': '%s://%s' % (default_protocol, current_domain),
'message': instance,
})
if instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
except Exception, e:
#print e
pass #fail silently
Apparently instance.recipient.email is the email for the recipient user. So my questions are: How do I go about creating an option in my profile management that can be used in my new_message_email to check if the user wants emails or not? My own thoughts are that I need to save a value in the database for the user and then check for that value in new_message_email function. How I do that isn't clear though. Do I create a new function in my userprofile/views.py and class in userprofile/forms.py? And have my userprofile/overview.html template change them? Some specifics and thoughts if this is the right approach would help alot!
You probably want to start off by creating a user profile so that you have a good way to store weather or not the user wants these emails sent to them. This is done using the AUTH_PROFILE_MODULE setting in your settings.py.
Once you have the data stored, you should be able to access it from instance.recipient (assuming that instance.recipient is a User object). So you could change your code to:
if instance.recipient.get_profile().wants_emails and instance.recipient.email != "":
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL,
[instance.recipient.email,])
Done and done.

Proper technique for a contact form?

I have a contact form at shantiyoga.ca/contact
With the view
def contact(request):
template = 'contact.html'
form = ContactForm(request.POST or None)
if form.is_valid(): # All validation rules pass
subject = form.cleaned_data['subject']
message = "%s\n\n%s" % (form.cleaned_data['message'], form.cleaned_data['sender'])
cc_myself = form.cleaned_data['cc_myself']
recipients = ['contact#shantiyoga.ca']
sender = form.cleaned_data['sender']
if cc_myself:
recipients.append(sender)
headers = {'Reply-To': form.cleaned_data['sender']}
from django.core.mail import send_mail
send_mail(subject,message,sender,recipients,headers)
return redirect('/thanks/')
return render_to_response(template, {'form': form, 'current':'contact'}, context_instance=RequestContext(request))
Which works fairly well. I'm not terribly sophisticated with Django and my Python skills are not quite up to snuff, so please bear with me if I step through this in a basic fashion.
I would like to clarify that there is no way for the form recipient (contact#shantiyoga.ca) to receive the contact form from the value of the email field (user entered). It will always be sent by the authenticated email in my settings.py, which at this point is my personal email?
A user fills out the contact form and hits submit, an email is sent to contact#shantiyoga.ca from my personal email, and if the user decides to cc themself, a copy of the email is sent to them, also from my personal email.
This is not ideal, should I create an email like contactform#shantiyoga.ca for my settings.py to send the email from?
Also, the headers = {'Reply-To': form.cleaned_data['sender']} does not appear to be doing anything and I can't seem to find documentation describing its proper usage, has anyone had success using this technique?
Thank you for your time,
Noah
I would like to clarify that there is no way for the form recipient (contact#shantiyoga.ca) to receive the contact form from the value of the email field (user entered). It will always be sent by the authenticated email in my settings.py, which at this point is my personal email?
You're setting the sender of the email to sender but using EMAIL_HOST, correct? If this is the case, be careful that your SMTP account will let you send email from users other than your domain. Normally, providing you have an authenticated account on that server, you'll be able to set the From: field to whatever you like.
So in short, this email will hit your inbox appearing to be from the sender variable. So when you hit reply, you'll be able to email them back, which is I think what you want. However, it will be sent using the SMTP server whose authentication details you provide. There is a disconnect between being able to send email (SMTP) and being able to receive it, which I think has got you confused.
I've never tried it, but to add extra headers I believe you need to use the EmailMessage object. According to the docs, you would:
e = EmailMessage(headers={'Reply-To':...
e.send()
Be careful doing this; specifically, be careful to strip out newlines in the reply to field. I do not know if the default clean methods would do this.
Finally, there isn't much wrong with your django/python at all. The only thing I'd say, by way of awareness, is not to use this:
return redirect('/thanks/')
but instead:
return HttpResponseRedirect(reverse('myapp.views.method',args=tuple,kwargs=dict))
This gives you the ability to not hardcode urls into your application. The reverse method will look them up from urls.py for you, so you can move your application around/change urls and it will still work.
Here is signature of send_mail from django documentation:
send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None)
As you can see it does not have headers argument.
You will need to use EmailMessage object directly.
It would also be nice to remove email-formatting from view. I would write everything something more like this:
from django.core.mail.message import EmailMessage
from django.conf import settings
class ContactsEmailMessage(EmailMessage):
def __init__(self, sender, subject, message, cc_myself):
super(ContactEmailMessage, self).__init__(
subject=subject,
body=self._get_message(self, sender, message),
from=settings.DEFAULT_FROM_EMAIL,
to=settings.CONTACT_RECIPIENTS,
headers=self._get_headers(sender),
cc=(sender, ) if cc_myself else None
)
def _format_message(self, sender, message):
return "%s\n\n%s" % (sender, message)
def _get_headers(self, sender):
return {
'reply-to': sender
}
Now you can write clean and easy to read view:
from myproject.mail.message import ContactsEmailMessage, BadHeaderError
from django.core.exceptions import SuspiciousOperation
def contact(request):
...
form = ContactForm(request.POST or None)
if form.is_valid(): # All validation rules pass
try:
message = ContactsEmailMessage(**form.cleaned_data)
message.send()
except BadHeaderError:
# django will raise this error if user will try to pass suspicious new-line
# characters to "sender" or other fields. This is safe-guard from
# header injections
raise SuspiciousOperation()
return redirect('/thanks/')
...

Emails, a different 'reply to' address than sender address

I have a contact form on a website (a general form: name, email, subject, message) in which mails are sent using google apps smtp to the admins.
Currently if an administrator wants to reply to the mail directly selecting the reply option, the person's reply's To field will be filled by the sender's address automatically.
What I wan't to ask is, Is there any standardized way to pass on some additional info with the mail which would define any reply to the mail should go to this address instead of the sender's?
It does seems that there is a little chance for this option as it may lead to some problems due to spammers (They may define a custom reply field in their mail and a general user might not look where they are replying).
So as an alternative what I thought is to find a way to create a filter with sender's account which figures out the reply email address from the format and forwards the mail (Doesn't seems like a good solution and I have no idea how to achieve this).
I have tagged django, though this is not directly related with this, as I will finally implement this through django.
There are in fact standardized headers to specify response headers: http://cr.yp.to/immhf/response.html.
As far as implementing this in Django is concerned, the documentation contains an example:
from django.core.mail import EmailMessage
email = EmailMessage(
'Hello', # email subject
'Body goes here', # email body
'from#example.com', # sender address
['to1#example.com', 'to2#example.com'],
['bcc#example.com'],
headers={'Reply-To': 'another#example.com'},
)
This solved my problem.
Reply-To is a standard SMTP header.
I can't find a good reference for it at the moment, but it is mentioned in the Wikipedia article on Email.
Edit: Found it: RFC 5322, section 3.6.2
The RFC says you can specify multiple emails and that is what I was looking for. Came up with this:
from django.core.mail import EmailMessage
headers = {'Reply-To': 'email#one.com;email#two.com'}
msg = EmailMessage(subject, html_content, EMAIL_HOST_USER, email_list, headers=headers)
msg.content_subtype = "html"
msg.send()
Works like a charm. Note: EMAIL_HOST_USER is imported from your settings file as per Django doc email setup. More on this here, search for 'reply-to': https://docs.djangoproject.com/en/dev/topics/email/
Here is also how reply-to can be used
from django.core.mail import EmailMessage
email = EmailMessage(
'Hello',
'Body goes here',
'from#example.com',
['to1#example.com', 'to2#example.com'],
['bcc#example.com'],
reply_to=['another#example.com'],
headers={'Message-ID': 'foo'},
)
Read more at docs docs.djangoproject