1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
"""Copyright 2011 Askbot.org and Askbot project contributors.
Custom management command that takes questions posted
via email at the IMAP server
Expects subject line of emails to have format:
[Tag1, Tag2] Question title
Tags can be entered as multiword, but the space character
within the tag may be replaced with a dash, per live
setting EMAIL_REPLACE_SPACE_IN_TAGS
also, to make use of this command, the feature must
be enabled via ALLOW_ASKING_BY_EMAIL
and IMAP settings in the settings.py must be configured
correctly
todo: use templates for the email formatting
"""
import imaplib
import email
import quopri
import base64
from django.conf import settings as django_settings
from django.core.management.base import NoArgsCommand, CommandError
from django.core import exceptions
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from django.core.urlresolvers import reverse
from askbot.conf import settings as askbot_settings
from askbot.utils import mail
from askbot import models
from askbot.forms import AskByEmailForm
USAGE = _(
"""<p>To ask by email, please:</p>
<ul>
<li>Format the subject line as: [Tag1; Tag2] Question title</li>
<li>Type details of your question into the email body</li>
</ul>
<p>Note that tags may consist of more than one word, and tags
may be separated by a semicolon or a comma</p>
"""
)
def bounce_email(email, subject, reason = None, body_text = None):
"""sends a bounce email at address ``email``, with the subject
line ``subject``, accepts several reasons for the bounce:
* ``'problem_posting'``, ``unknown_user`` and ``permission_denied``
* ``body_text`` in an optional parameter that allows to append
extra text to the message
"""
if reason == 'problem_posting':
error_message = _(
'<p>Sorry, there was an error posting your question '
'please contact the %(site)s administrator</p>'
) % {'site': askbot_settings.APP_SHORT_NAME}
error_message = string_concat(error_message, USAGE)
elif reason == 'unknown_user':
error_message = _(
'<p>Sorry, in order to post questions on %(site)s '
'by email, please <a href="%(url)s">register first</a></p>'
) % {
'site': askbot_settings.APP_SHORT_NAME,
'url': askbot_settings.APP_URL + reverse('user_signin')
}
elif reason == 'permission_denied':
error_message = _(
'<p>Sorry, your question could not be posted '
'due to insufficient privileges of your user account</p>'
)
else:
raise ValueError('unknown reason to bounce an email: "%s"' % reason)
if body_text != None:
error_message = string_concat(error_message, body_text)
#print 'sending email'
#print email
#print subject
#print error_message
mail.send_mail(
recipient_list = (email,),
subject_line = 'Re: ' + subject,
body_text = error_message
)
class CannotParseEmail(Exception):
"""This exception will bounce the email"""
def __init__(self, email, subject):
super(CannotParseEmail, self).__init__()
bounce_email(email, subject, reason = 'problem_posting')
def parse_message(msg):
"""returns a tuple
(<from email address>, <subject>, <body>)
the function will attempt to decode the email
supported encodings are "quoted-printable" and "base64"
not supported - emails using language - specific encodings
"""
sender = msg.get('From')
subject = msg.get('Subject')
if msg.is_multipart():
msg = msg.get_payload()
if isinstance(msg, list):
raise CannotParseEmail(sender, subject)
ctype = msg.get_content_type()#text/plain only
raw_body = msg.get_payload()#text/plain only
encoding = msg.get('Content-Transfer-Encoding')
if encoding == 'base64':
body = base64.b64decode(raw_body)
elif encoding == 'quoted-printable':
body = quopri.decodestring(raw_body)
else:
body = raw_body
return (sender, subject, body)
class Command(NoArgsCommand):
def handle_noargs(self, **options):
"""reads all emails from the INBOX of
imap server and posts questions based on
those emails. Badly formatted questions are
bounced, as well as emails from unknown users
all messages are deleted thereafter
"""
if askbot_settings.ALLOW_ASKING_BY_EMAIL:
raise CommandError('Asking by email is not enabled')
#open imap server and select the inbox
if django_settings.IMAP_USE_TLS:
imap_getter = imaplib.IMAP4_SSL
else:
imap_getter = imaplib.IMAP4
imap = imap_getter(
django_settings.IMAP_HOST,
django_settings.IMAP_PORT
)
imap.login(
django_settings.IMAP_HOST_USER,
django_settings.IMAP_HOST_PASSWORD
)
imap.select('INBOX')
#get message ids
status, ids = imap.search(None, 'ALL')
#for each id - read a message, parse it and post a question
for id in ids[0].split(' '):
t, data = imap.fetch(id, '(RFC822)')
message_body = data[0][1]
msg = email.message_from_string(data[0][1])
imap.store(id, '+FLAGS', '\\Deleted')
try:
(sender, subject, body) = parse_message(msg)
except CannotParseEmail, e:
continue
data = {
'sender': sender,
'subject': subject,
'body_text': body
}
form = AskByEmailForm(data)
print data
if form.is_valid():
email_address = form.cleaned_data['email']
try:
print 'looking for ' + email_address
user = models.User.objects.get(email = email_address)
except models.User.DoesNotExist:
bounce_email(email_address, subject, reason = 'unknown_user')
except models.User.MultipleObjectsReturned:
bounce_email(email_address, subject, reason = 'problem_posting')
tagnames = form.cleaned_data['tagnames']
title = form.cleaned_data['title']
body_text = form.cleaned_data['body_text']
try:
print 'posting question'
print title
print tagnames
print body_text
user.post_question(
title = title,
tags = tagnames,
body_text = body_text
)
except exceptions.PermissionDenied, e:
bounce_email(
email_address,
subject,
reason = 'permission_denied',
body_text = unicode(e)
)
else:
email_address = mail.extract_first_email_address(sender)
if email_address:
bounce_email(
email_address,
subject,
reason = 'problem_posting'
)
imap.expunge()
imap.close()
imap.logout()
|