def decode_subject(subject): if subject[0:2] == '=?' and subject[-2:] == '?=': subject = u''.join( unicode(s, c or 'us-ascii') for s, c in decode_header(subject)) else: subject = unicode(collapse_rfc2231_value(subject)) return subject
def get_filename(self, failobj=None): missing = object() filename = self.get_param('filename', missing, 'content-disposition') if filename is missing: filename = self.get_param('name', missing, 'content-type') return failobj if filename is missing else utils.collapse_rfc2231_value( filename).strip()
def test_parse_encoded_params(self): raw = dedent("""\ MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="this_is_a_boundary" --this_is_a_boundary Content-Type: text/plain; charset="UTF-8" This is the body --this_is_a_boundary Content-Type: text/plain; name*=us-ascii''TPS%20Report Content-Disposition: attachment; filename*=iso-8859-1''Une%20pi%E8ce%20jointe%2Etxt This is an attachment --this_is_a_boundary-- """) msg = AnymailInboundMessage.parse_raw_mime(raw) att = msg.attachments[0] self.assertTrue(att.is_attachment()) self.assertEqual(att.get_content_disposition(), "attachment") self.assertEqual( collapse_rfc2231_value(att.get_param("Name", header="Content-Type")), "TPS Report") self.assertEqual(att.get_filename(), "Une pièce jointe.txt")
def get_filename(self, failobj = None): missing = object() filename = self.get_param('filename', missing, 'content-disposition') if filename is missing: filename = self.get_param('name', missing, 'content-type') if filename is missing: return failobj return utils.collapse_rfc2231_value(filename).strip()
def XMPP(msg, host=None): sender=collapse_rfc2231_value(msg['from']) resp = view.respond({}, "1stcontact.txt", From=sendermail, To=sender, Subject="start chatting") relay.deliver(resp) return XMPP
def JITSI(msg, host=None): sender=collapse_rfc2231_value(msg['from']) resp = view.respond({}, "jitsi.txt", From=sendermail, To=sender, Subject="chatting continued") relay.deliver(resp) return SECRET
def SECRET(msg, host=None): sender=collapse_rfc2231_value(msg['from']) resp = view.respond({'buddyurl': 'https://%s/buddy' % webhost}, "fetchsecret.txt", From=sendermail, To=sender, Subject="getting serious") relay.deliver(resp) return XMPP
def UPLOADPK(msg, address=None, host=None): res={} sender=collapse_rfc2231_value(msg['from']) m=sendere.match(sender) if m: res['sender_name'], res['sender_mail']=m.groups() else: res['sender_mail']=sender for mpart in msg.walk(): ispgp=False part=to_message(mpart) if part.get_content_type()=='text/plain': # cut of preamble inblock=False lines=part.get_payload(decode=True).split('\n') i=0 while i<len(lines): if not inblock: if lines[i].strip()=='-----BEGIN PGP PUBLIC KEY BLOCK-----': inblock=True i+=2 else: if lines[i].strip()=='-----END PGP PUBLIC KEY BLOCK-----': break i+=1 if i<len(lines): ispgp=True elif part.get_content_type()=='application/pgp-keys': ispgp=True if ispgp: res=getpgpmeta(part.get_payload(decode=True)) ret=gpg('--import', _err_to_out=True, _in=part.get_payload(decode=True)) #logging.info(ret) modifiers={'fresh': False, 'abbreved': False, 'singleid': False, 'tidy': False, } if res['datetime']>datetime.datetime.utcnow()-datetime.timedelta(days=10): modifiers['fresh']=True if len(res['ids'])<2: modifiers['singleid']=True if len(res['ids'][0]['email'].split('@')[0])<9: modifiers['abbreved']=True if len([1 for x in res['sigs'] if x['st'] not in ['Positive certification of a User ID and Public Key packet', 'Subkey Binding Signature']])==0: modifiers['tidy']=True res['award']=award("You uploaded your public key.\n%s" % '\n'.join(["%s [%s]" % (k,'X' if v else ' ') for k,v in modifiers.items()])) #logging.info(res) welcome = view.respond(res, "pkuploaded.msg", From=sendermail, To=sender, Subject="Welcome to the Privacy Challenge") view.attach(welcome, {}, "pubkey.asc", filename="my key", content_type="application/pgp-keys") relay.deliver(welcome)
def START(msg, host=None): sender=collapse_rfc2231_value(msg['from']) #subj=collapse_rfc2231_value(msg['subject']) resp = view.respond({}, "welcome.txt", From=sendermail, To=sender, #Subject="Re: %s" % subj) Subject="thanks! let's chat") relay.deliver(resp) return JITSI
def get_boundary(self, failobj=None): """Return the boundary associated with the payload if present. The boundary is extracted from the Content-Type header's `boundary' parameter, and it is unquoted. """ missing = object() boundary = self.get_param('boundary', missing) if boundary is missing: return failobj return utils.collapse_rfc2231_value(boundary).rstrip()
def get_boundary(self, failobj = None): """Return the boundary associated with the payload if present. The boundary is extracted from the Content-Type header's `boundary' parameter, and it is unquoted. """ missing = object() boundary = self.get_param('boundary', missing) if boundary is missing: return failobj return utils.collapse_rfc2231_value(boundary).rstrip()
def get_filename(self, failobj=None): """Return the filename associated with the payload if present. The filename is extracted from the Content-Disposition header's `filename' parameter, and it is unquoted. If that header is missing the `filename' parameter, this method falls back to looking for the `name' parameter. """ missing = object() filename = self.get_param('filename', missing, 'content-disposition') if filename is missing: filename = self.get_param('name', missing, 'content-type') if filename is missing: return failobj return utils.collapse_rfc2231_value(filename).strip()
def attachments(self): attachments = dict() if not self.message: return attachments for part in self.message.walk(): if 'Content-Disposition' in part and part[ 'Content-Disposition'].startswith('attachment'): filename = part.get_param('filename', header='Content-Disposition') if filename: filename = collapse_rfc2231_value(filename) attachments[filename] = dict( filename=filename, mimetype=part['Content-Type'], payload=part.get_content(), ) return attachments
def parse_attachment(message_part): # Check again if this is a valid attachment content_disposition = message_part.get("Content-Disposition", None) if content_disposition is not None and not message_part.is_multipart(): dispositions = [ disposition.strip() for disposition in content_disposition.split(";") if disposition.strip() ] if dispositions[0].lower() in ["attachment", "inline"]: file_data = message_part.get_payload(decode=True) attachment = { 'content-type': message_part.get_content_type(), 'size': len(file_data), 'content': io.BytesIO(file_data), 'content-id': message_part.get("Content-ID", None) } filename = message_part.get_filename() if filename: filename = collapse_rfc2231_value(filename) attachment['filename'] = filename attachment['imaplib_filename'] = filename print("Dispositions:") pprint(dispositions) filename_parts = [] print("Attachment-pre-join:") pprint(attachment) for param in dispositions[1:]: if param: name, value = decode_param(param) # who wants linefeeds in filenames? value = "".join(value.splitlines()) pprint(decode_param(param)) # Check for split filename # and other names # filename*0*=utf-8''%F0 # filename*=utf-8''%F0 s_name = name.split("*") if s_name[0] == 'filename': # If this is a split file name - use the number after the * as an index to insert this part if len(s_name) > 1 and len(s_name[1]) > 0: filename_parts.insert(int(s_name[1]),value[1:-1] if value.startswith('"') else value) else: filename_parts.insert(0,value[1:-1] if value.startswith('"') else value) if 'create-date' in name: attachment['create-date'] = value print("Attachments-post-for:") pprint(attachment) if len(filename_parts) > 0: attachment['filename'] = collapse_rfc2231_value("".join(filename_parts)) print("Post-collapse:") pprint(attachment) if 'filename' in attachment: match = re.search(r'([^\']+)\'\'(.*)', attachment['filename']) if match: encoding, code = match.groups() attachment['filename'] = urllib.parse.unquote(code, encoding=encoding) print("Attachment-post-decode:") pprint(attachment) attachment['filename'] = attachment['filename'].replace('\r', '_') attachment['filename'] = attachment['filename'].replace('\n', '_') attachment['filename'] = attachment['filename'].replace('\\', '_') print("Attachment-post-linefeed-removal:") pprint(attachment) return attachment return None
def ticket_from_message(message, quiet): """ Create a ticket or a followup (if ticket id in subject) """ msg = message message = email.message_from_string(msg) subject = message.get('subject', 'Created from e-mail') subject = decode_mail_headers(decodeUnknown(message.get_charset(), subject)) sender = message.get('from', ('Unknown Sender')) sender = decode_mail_headers(decodeUnknown(message.get_charset(), sender)) sender_email = parseaddr(sender)[1] body_plain, body_html = '', '' matchobj = re.match(r".*\[" + "-(?P<id>\d+)\]", subject) if matchobj: # This is a reply or forward. ticket = matchobj.group('id') else: ticket = None counter = 0 files = [] for part in message.walk(): if part.get_content_maintype() == 'multipart': continue name = part.get_param("name") if name: name = collapse_rfc2231_value(name) if part.get_content_maintype() == 'text' and name == None: if part.get_content_subtype() == 'plain': body_plain = EmailReplyParser.parse_reply( decodeUnknown(part.get_content_charset(), part.get_payload(decode=True))) else: body_html = part.get_payload(decode=True) else: if not name: ext = mimetypes.guess_extension(part.get_content_type()) name = "part-%i%s" % (counter, ext) files.append( { 'filename': name, 'content': part.get_payload(decode=True), 'type': part.get_content_type() }, ) counter += 1 if body_plain: body = body_plain else: body = 'No plain-text email body available. Please see attachment email_html_body.html.' if body_html: files.append({ 'filename': 'email_html_body.html', 'content': body_html, 'type': 'text/html', }) now = timezone.now() if ticket: try: t = Ticket.objects.get(id=ticket) new = False except Ticket.DoesNotExist: ticket = None if ticket == None: # set owner depending on sender_email # list of all email addresses from the user model users = User.objects.all() email_addresses = [] for user in users: email_addresses.append(user.email) ############################################################ # if ticket id in subject => new followup instead of new ticket tickets = Ticket.objects.all() ticket_ids = [] for ticket in tickets: ticket_ids.append(ticket.id) # extract id from subject subject_id = re.search(r'\[#(\d*)\]\s.*', subject) try: subject_id = subject_id.group(1) except: subject_id = "0000" # no valid id # if there was an ID in the subject, create followup if int(subject_id) in ticket_ids: if sender_email in email_addresses: f = FollowUp( title=subject, created=now, text=body, ticket=Ticket.objects.get(id=subject_id), user=User.objects.get(email=sender_email), ) else: f = FollowUp( title=subject, created=now, text=body, ticket=Ticket.objects.get(id=subject_id), ) f.save() # if no ID in the subject, create ticket else: # if known sender, set also the field owner if sender_email in email_addresses: t = Ticket( title=subject, status="TODO", created=now, description=body, owner=User.objects.get(email=sender_email), ) # if unknown sender, skip the field owner else: t = Ticket( title=subject, status="TODO", created=now, description=body, ) t.save() from django.core.mail import send_mail notification_subject = "[#" + str(t.id) + "] New ticket created" notification_body = "Hi,\n\na new ticket was created: http://localhost:8000/ticket/" \ + str(t.id) + "/" send_mail(notification_subject, notification_body, os.environ["DJANGO_TICKET_EMAIL_NOTIFICATIONS_FROM"], [os.environ["DJANGO_TICKET_EMAIL_NOTIFICATIONS_TO"]], fail_silently=False) ############################################################ new = True update = '' elif t.status == Ticket.CLOSED_STATUS: t.status = Ticket.REOPENED_STATUS t.save() # files of followups should be assigned to the corresponding ticket for file in files: if file['content']: filename = file['filename'].encode('ascii', 'replace').replace(' ', '_') filename = re.sub('[^a-zA-Z0-9._-]+', '', filename) # if followup if int(subject_id) in ticket_ids: a = Attachment( ticket=Ticket.objects.get(id=subject_id), filename=filename, #mime_type=file['type'], #size=len(file['content']), ) # if new ticket else: a = Attachment( ticket=t, filename=filename, #mime_type=file['type'], #size=len(file['content']), ) a.file.save(filename, ContentFile(file['content']), save=False) a.save() if not quiet: print " - %s" % filename if int(subject_id) in ticket_ids: return f else: return t
def ticket_from_message(message, queue, logger): # 'message' must be an RFC822 formatted message. msg = message message = email.message_from_string(msg) subject = message.get('subject', _('Created from e-mail')) subject = decode_mail_headers(decodeUnknown(message.get_charset(), subject)) for affix in STRIPPED_SUBJECT_STRINGS: subject = subject.replace(affix, "") subject = subject.strip() sender = message.get('from', _('Unknown Sender')) sender = decode_mail_headers(decodeUnknown(message.get_charset(), sender)) sender_email = parseaddr(sender)[1] body_plain, body_html = '', '' for ignore in IgnoreEmail.objects.filter( Q(queues=queue) | Q(queues__isnull=True)): if ignore.test(sender_email): if ignore.keep_in_mailbox: # By returning 'False' the message will be kept in the mailbox, # and the 'True' will cause the message to be deleted. return False return True matchobj = re.match(r".*\[" + queue.slug + "-(?P<id>\d+)\]", subject) if matchobj: # This is a reply or forward. ticket = matchobj.group('id') logger.info("Matched tracking ID %s-%s" % (queue.slug, ticket)) else: logger.info("No tracking ID matched.") ticket = None counter = 0 files = [] for part in message.walk(): if part.get_content_maintype() == 'multipart': continue name = part.get_param("name") if name: name = collapse_rfc2231_value(name) if part.get_content_maintype() == 'text' and name is None: if part.get_content_subtype() == 'plain': body_plain = EmailReplyParser.parse_reply( decodeUnknown(part.get_content_charset(), part.get_payload(decode=True))) logger.debug("Discovered plain text MIME part") else: body_html = part.get_payload(decode=True) logger.debug("Discovered HTML MIME part") else: if not name: ext = mimetypes.guess_extension(part.get_content_type()) name = "part-%i%s" % (counter, ext) files.append( { 'filename': name, 'content': part.get_payload(decode=True), 'type': part.get_content_type() }, ) logger.debug("Found MIME attachment %s" % name) counter += 1 if body_plain: body = body_plain else: body = _( 'No plain-text email body available. Please see attachment "email_html_body.html".' ) if body_html: files.append({ 'filename': _("email_html_body.html"), 'content': body_html, 'type': 'text/html', }) now = timezone.now() if ticket: try: t = Ticket.objects.get(id=ticket) new = False logger.info("Found existing ticket with Tracking ID %s-%s" % (t.queue.slug, t.id)) except Ticket.DoesNotExist: logger.info( "Tracking ID %s-%s not associated with existing ticket. Creating new ticket." % (queue.slug, ticket)) ticket = None priority = 3 smtp_priority = message.get('priority', '') smtp_importance = message.get('importance', '') high_priority_types = ('high', 'important', '1', 'urgent') if smtp_priority in high_priority_types or smtp_importance in high_priority_types: priority = 2 if ticket is None: t = Ticket( title=subject, queue=queue, submitter_email=sender_email, created=now, description=body, priority=priority, ) t.save() new = True logger.debug("Created new ticket %s-%s" % (t.queue.slug, t.id)) elif t.status == Ticket.CLOSED_STATUS: t.status = Ticket.REOPENED_STATUS t.save() f = FollowUp( ticket=t, title=_('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}), date=timezone.now(), public=True, comment=body, ) if t.status == Ticket.REOPENED_STATUS: f.new_status = Ticket.REOPENED_STATUS f.title = _( 'Ticket Re-Opened by E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}) f.save() logger.debug("Created new FollowUp for Ticket") if six.PY2: logger.info(("[%s-%s] %s" % ( t.queue.slug, t.id, t.title, )).encode('ascii', 'replace')) elif six.PY3: logger.info("[%s-%s] %s" % ( t.queue.slug, t.id, t.title, )) for file in files: if file['content']: if six.PY2: filename = file['filename'].encode('ascii', 'replace').replace( ' ', '_') elif six.PY3: filename = file['filename'].replace(' ', '_') filename = re.sub('[^a-zA-Z0-9._-]+', '', filename) logger.info("Found attachment '%s'" % filename) a = Attachment( followup=f, filename=filename, mime_type=file['type'], size=len(file['content']), ) a.file.save(filename, ContentFile(file['content']), save=False) a.save() logger.info("Attachment '%s' successfully added to ticket." % filename) context = safe_template_context(t) if new: if sender_email: send_templated_mail( 'newticket_submitter', context, recipients=sender_email, sender=queue.from_address, fail_silently=True, ) if queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.new_ticket_cc, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc and queue.updated_ticket_cc != queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) else: context.update(comment=f.comment) # if t.status == Ticket.REOPENED_STATUS: # update = _(' (Reopened)') # else: # update = _(' (Updated)') if t.assigned_to: send_templated_mail( 'updated_owner', context, recipients=t.assigned_to.email, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc: send_templated_mail( 'updated_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) return t
def DECODER(msg, address=None, host=None): sender=collapse_rfc2231_value(msg['from']) m=sendere.match(sender) res={} if m: res['sender_name'], res['sender_mail']=m.groups() else: res['sender_mail']=sender for mpart in msg.walk(): part=to_message(mpart) # cut of preamble inblock=False lines=part.get_payload(decode=True).split('\n') i=0 #logging.info(lines) while i<len(lines): if not inblock: if lines[i].strip()=='-----BEGIN PGP MESSAGE-----': inblock=True i+=2 else: if lines[i].strip()=='-----END PGP MESSAGE-----': break i+=1 #logging.info(i) if i<len(lines): res.update(getpgpmeta(part.get_payload(decode=True))) ret=gpg('-d', _ok_code=[0,2], _in=part.get_payload(decode=True)) #logging.info('ret '+str(ret)) #logging.info('stderr '+ret.stderr) res['msg']='\n'.join(["> %s" % x for x in ret.stdout.split('\n')]) # extra points, # - no named recipient # - signed modifiers={'sekrit': False, 'signed': False} #logging.info(res['keys']) if len([x for x in res['keys'] if x['key_id']!="0000000000000000"])==0: modifiers['sekrit']=True signed={} for line in ret.stderr.split('\n'): if line.startswith('gpg: Signature made '): # gpg: Signature made Fri 11 May 2012 04:43:04 PM CEST using RSA key ID XXXXXX m=signed1re.match(line) if m: #logging.info(m.groups()) signed['date']=dparse(str(m.group(1))) signed['algo']=m.group(2) signed['key_id']=m.group(3) elif line.startswith('gpg: Good signature from '): # gpg: Good signature from "name <mail>" m=signed2re.match(line) if m: #logging.info(m.groups()) signed['name']=m.group(1) signed['mail']=m.group(2) modifiers['signed']=True if signed: res['signed']=signed res['award']=award("You sent an encrypted mail.\n%s" % '\n'.join(["%s [%s]" % (k,'X' if v else ' ') for k,v in modifiers.items()])) #logging.info(res) welcome = view.respond(res, "pgpmail.msg", From=sendermail, To=sender, Subject="Encrypted mail received") relay.deliver(welcome)
def ticket_from_message(message, quiet): """ Create a ticket or a followup (if ticket id in subject) """ msg = message message = email.message_from_string(msg) subject = message.get("subject", "Created from e-mail") subject = decode_mail_headers(decodeUnknown(message.get_charset(), subject)) sender = message.get("from", ("Unknown Sender")) sender = decode_mail_headers(decodeUnknown(message.get_charset(), sender)) sender_email = parseaddr(sender)[1] body_plain, body_html = "", "" matchobj = re.match(r".*\[" + "-(?P<id>\d+)\]", subject) if matchobj: # This is a reply or forward. ticket = matchobj.group("id") else: ticket = None counter = 0 files = [] for part in message.walk(): if part.get_content_maintype() == "multipart": continue name = part.get_param("name") if name: name = collapse_rfc2231_value(name) if part.get_content_maintype() == "text" and name == None: if part.get_content_subtype() == "plain": body_plain = EmailReplyParser.parse_reply( decodeUnknown(part.get_content_charset(), part.get_payload(decode=True)) ) else: body_html = part.get_payload(decode=True) else: if not name: ext = mimetypes.guess_extension(part.get_content_type()) name = "part-%i%s" % (counter, ext) files.append({"filename": name, "content": part.get_payload(decode=True), "type": part.get_content_type()}) counter += 1 if body_plain: body = body_plain else: body = "No plain-text email body available. Please see attachment email_html_body.html." if body_html: files.append({"filename": "email_html_body.html", "content": body_html, "type": "text/html"}) now = timezone.now() if ticket: try: t = Ticket.objects.get(id=ticket) new = False except Ticket.DoesNotExist: ticket = None if ticket == None: # set owner depending on sender_email # list of all email addresses from the user model users = User.objects.all() email_addresses = [] for user in users: email_addresses.append(user.email) ############################################################ # if ticket id in subject => new followup instead of new ticket tickets = Ticket.objects.all() ticket_ids = [] for ticket in tickets: ticket_ids.append(ticket.id) # extract id from subject subject_id = re.search(r"\[#(\d*)\]\s.*", subject) try: subject_id = subject_id.group(1) except: subject_id = "0000" # no valid id # if there was an ID in the subject, create followup if int(subject_id) in ticket_ids: if sender_email in email_addresses: f = FollowUp( title=subject, created=now, text=body, ticket=Ticket.objects.get(id=subject_id), user=User.objects.get(email=sender_email), ) else: f = FollowUp(title=subject, created=now, text=body, ticket=Ticket.objects.get(id=subject_id)) f.save() # if no ID in the subject, create ticket else: # if known sender, set also the field owner if sender_email in email_addresses: t = Ticket( title=subject, status="TODO", created=now, description=body, owner=User.objects.get(email=sender_email), ) # if unknown sender, skip the field owner else: t = Ticket(title=subject, status="TODO", created=now, description=body) t.save() from django.core.mail import send_mail notification_subject = "[#" + str(t.id) + "] New ticket created" notification_body = "Hi,\n\na new ticket was created: http://localhost:8000/ticket/" + str(t.id) + "/" send_mail( notification_subject, notification_body, os.environ["DJANGO_TICKET_EMAIL_NOTIFICATIONS_FROM"], [os.environ["DJANGO_TICKET_EMAIL_NOTIFICATIONS_TO"]], fail_silently=False, ) ############################################################ new = True update = "" elif t.status == Ticket.CLOSED_STATUS: t.status = Ticket.REOPENED_STATUS t.save() # files of followups should be assigned to the corresponding ticket for file in files: if file["content"]: filename = file["filename"].encode("ascii", "replace").replace(" ", "_") filename = re.sub("[^a-zA-Z0-9._-]+", "", filename) # if followup if int(subject_id) in ticket_ids: a = Attachment( ticket=Ticket.objects.get(id=subject_id), filename=filename, # mime_type=file['type'], # size=len(file['content']), ) # if new ticket else: a = Attachment( ticket=t, filename=filename, # mime_type=file['type'], # size=len(file['content']), ) a.file.save(filename, ContentFile(file["content"]), save=False) a.save() if not quiet: print " - %s" % filename if int(subject_id) in ticket_ids: return f else: return t
def decode_subject(subject): if subject[0:2] == "=?" and subject[-2:] == "?=": subject = u"".join(unicode(s, c or "us-ascii") for s, c in decode_header(subject)) else: subject = unicode(collapse_rfc2231_value(subject)) return subject
def otrfp(msg, address=None, host=None): sender=collapse_rfc2231_value(msg['from']) m=sendere.match(sender) res={} if m: res['sender_name'], res['sender_mail']=m.groups() else: res['sender_mail']=sender for mpart in msg.walk(): part=to_message(mpart) # cut of preamble inblock=False lines=part.get_payload(decode=True).split('\n') i=0 #logging.info(lines) while i<len(lines): if not inblock: if lines[i].strip()=='-----BEGIN PGP SIGNED MESSAGE-----' or lines[i].strip()=='-----BEGIN PGP MESSAGE-----': inblock=True i+=2 else: if lines[i].strip()=='-----END PGP SIGNATURE-----' or lines[i].strip()=='-----END PGP MESSAGE-----': break i+=1 #logging.info(i) if i<len(lines): res.update(getpgpmeta(part.get_payload(decode=True))) ret=gpg('-d', _ok_code=[0,2], _in=part.get_payload(decode=True)) #logging.info('ret '+str(ret)) #logging.info('stderr '+ret.stderr) res['msg']='\n'.join(["> %s" % x for x in ret.stdout.split('\n')]) # extra points, # - no named recipient # - signed #logging.info(res['keys']) modifiers={'sekrit': False, 'signed': False} if len([x for x in res['keys'] if x['key_id']!="0000000000000000"])==0: modifiers['sekrit']=True else: logging.warn([x for x in res['keys'] if x['key_id']!="0000000000000000"]) signed={} for line in ret.stderr.split('\n'): if line.startswith('gpg: Signature made '): # gpg: Signature made Fri 11 May 2012 04:43:04 PM CEST using RSA key ID XXXXXX m=signed1re.match(line) if m: #logging.info(m.groups()) signed['date']=dparse(str(m.group(1))) signed['algo']=m.group(2) signed['key_id']=m.group(3) elif line.startswith('gpg: Good signature from '): # gpg: Good signature from "name <mail>" m=signed2re.match(line) if m: #logging.info(m.groups()) signed['name']=m.group(1) signed['mail']=m.group(2) modifiers['signed']=True if not signed: plssign = view.respond(res, "plssign.msg", From=sendermail, To=sender, Subject="OTR fingerprint help") relay.deliver(plssign) continue res['signed']=signed res['award']=award("you bootstrapped OTR trust using PGP.\n%s" % '\n'.join(["%s [%s]" % (k,'X' if v else ' ') for k,v in modifiers.items()])) #logging.info(res) jid=None fp=None secret=None for line in to_message(from_string(ret.stdout)).get_payload(decode=True).split('\n'): if not line.strip(): continue if line=='-- ': break if jid and fp: secret=line break #logging.info("line "+line) m=otrfpre.match(line) if m: #logging.info(m.groups()) jid, fp = m.group(1), m.group(2) if jid and fp: with FileLock('%s/otr/otr/%s.fpr' % (basepath, botjid)): fr=open('%s/otr/otr/%s.fpr' % (basepath, botjid), 'r') fw=open('%s/otr/otr/%s.fpr.new' % (basepath, botjid), 'w') for line in fr: #logging.info(line) #logging.info("%s\t%s\tjabber\t%s" % (jid, # botjid, # fp.lower().replace(' ',''))) if line.startswith("%s\t%s\tjabber\t%s" % (jid, botjid, fp.lower().replace(' ',''))): fw.write("%s\t%s\tjabber\t%s\ttrust\n" % (jid, botjid, fp.lower().replace(' ',''))) else: fw.write(line) fw.close() fr.close() os.unlink('%s/otr/otr/%s.fpr' % (basepath, botjid)) shutil.move('%s/otr/otr/%s.fpr.new' % (basepath, botjid), '%s/otr/otr/%s.fpr' % (basepath, botjid)) if secret: fs=open('%s/otr/otr/%s.s' % (basepath, jid), 'w') fs.write("%s %s" % (signed['key_id'], secret)) fs.close() welcome = view.respond(res, "otrtrust.msg", From=sendermail, To=sender, Subject="OTR fingerprint received") relay.deliver(welcome)
def get_boundary(self, failobj = None): missing = object() boundary = self.get_param('boundary', missing) if boundary is missing: return failobj return utils.collapse_rfc2231_value(boundary).rstrip()
def ticket_from_message(message, queue, logger): # 'message' must be an RFC822 formatted message. msg = message message = email.message_from_string(msg) subject = message.get('subject', _('Created from e-mail')) subject = decode_mail_headers(decodeUnknown(message.get_charset(), subject)) for affix in STRIPPED_SUBJECT_STRINGS: subject = subject.replace(affix, "") subject = subject.strip() sender = message.get('from', _('Unknown Sender')) sender = decode_mail_headers(decodeUnknown(message.get_charset(), sender)) sender_email = parseaddr(sender)[1] body_plain, body_html = '', '' for ignore in IgnoreEmail.objects.filter(Q(queues=queue) | Q(queues__isnull=True)): if ignore.test(sender_email): if ignore.keep_in_mailbox: # By returning 'False' the message will be kept in the mailbox, # and the 'True' will cause the message to be deleted. return False return True matchobj = re.match(r".*\[" + queue.slug + "-(?P<id>\d+)\]", subject) if matchobj: # This is a reply or forward. ticket = matchobj.group('id') logger.info("Matched tracking ID %s-%s" % (queue.slug, ticket)) else: logger.info("No tracking ID matched.") ticket = None counter = 0 files = [] for part in message.walk(): if part.get_content_maintype() == 'multipart': continue name = part.get_param("name") if name: name = collapse_rfc2231_value(name) if part.get_content_maintype() == 'text' and name is None: if part.get_content_subtype() == 'plain': body_plain = EmailReplyParser.parse_reply( decodeUnknown(part.get_content_charset(), part.get_payload(decode=True))) logger.debug("Discovered plain text MIME part") else: body_html = part.get_payload(decode=True) logger.debug("Discovered HTML MIME part") else: if not name: ext = mimetypes.guess_extension(part.get_content_type()) name = "part-%i%s" % (counter, ext) files.append({ 'filename': name, 'content': part.get_payload(decode=True), 'type': part.get_content_type()}, ) logger.debug("Found MIME attachment %s" % name) counter += 1 if body_plain: body = body_plain else: body = _('No plain-text email body available. Please see attachment "email_html_body.html".') if body_html: files.append({ 'filename': _("email_html_body.html"), 'content': body_html, 'type': 'text/html', }) now = timezone.now() if ticket: try: t = Ticket.objects.get(id=ticket) new = False logger.info("Found existing ticket with Tracking ID %s-%s" % (t.queue.slug, t.id)) except Ticket.DoesNotExist: logger.info("Tracking ID %s-%s not associated with existing ticket. Creating new ticket." % (queue.slug, ticket)) ticket = None priority = 3 smtp_priority = message.get('priority', '') smtp_importance = message.get('importance', '') high_priority_types = ('high', 'important', '1', 'urgent') if smtp_priority in high_priority_types or smtp_importance in high_priority_types: priority = 2 if ticket is None: t = Ticket( title=subject, queue=queue, submitter_email=sender_email, created=now, description=body, priority=priority, ) t.save() new = True logger.debug("Created new ticket %s-%s" % (t.queue.slug, t.id)) elif t.status == Ticket.CLOSED_STATUS: t.status = Ticket.REOPENED_STATUS t.save() f = FollowUp( ticket=t, title=_('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}), date=timezone.now(), public=True, comment=body, ) if t.status == Ticket.REOPENED_STATUS: f.new_status = Ticket.REOPENED_STATUS f.title = _('Ticket Re-Opened by E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}) f.save() logger.debug("Created new FollowUp for Ticket") if six.PY2: logger.info(("[%s-%s] %s" % (t.queue.slug, t.id, t.title,)).encode('ascii', 'replace')) elif six.PY3: logger.info("[%s-%s] %s" % (t.queue.slug, t.id, t.title,)) for file in files: if file['content']: if six.PY2: filename = file['filename'].encode('ascii', 'replace').replace(' ', '_') elif six.PY3: filename = file['filename'].replace(' ', '_') filename = re.sub('[^a-zA-Z0-9._-]+', '', filename) logger.info("Found attachment '%s'" % filename) a = Attachment( followup=f, filename=filename, mime_type=file['type'], size=len(file['content']), ) a.file.save(filename, ContentFile(file['content']), save=False) a.save() logger.info("Attachment '%s' successfully added to ticket." % filename) context = safe_template_context(t) if new: if sender_email: send_templated_mail( 'newticket_submitter', context, recipients=sender_email, sender=queue.from_address, fail_silently=True, ) if queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.new_ticket_cc, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc and queue.updated_ticket_cc != queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) else: context.update(comment=f.comment) # if t.status == Ticket.REOPENED_STATUS: # update = _(' (Reopened)') # else: # update = _(' (Updated)') if t.assigned_to: send_templated_mail( 'updated_owner', context, recipients=t.assigned_to.email, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc: send_templated_mail( 'updated_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) return t
def decode_subject(subject): if subject[0:2] == '=?' and subject[-2:] == '?=': subject = u''.join(unicode(s, c or 'us-ascii') for s, c in decode_header(subject)) else: subject = unicode(collapse_rfc2231_value(subject)) return subject
def get_boundary(self, failobj=None): missing = object() boundary = self.get_param('boundary', missing) if boundary is missing: return failobj return utils.collapse_rfc2231_value(boundary).rstrip()
def ticket_from_message(message, queue, quiet): # 'message' must be an RFC822 formatted message. msg = message message = email.message_from_string(msg.decode('utf-8')) subject = message.get('subject', _('Created from e-mail')) subject = decode_mail_headers(decodeUnknown(message.get_charset(), subject)) subject = subject.replace("Re: ", "").replace("Fw: ", "").replace("RE: ", "").replace("FW: ", "").replace("Automatic reply: ", "").strip() sender = message.get('from', _('Unknown Sender')) sender = decode_mail_headers(decodeUnknown(message.get_charset(), sender)) sender_email = parseaddr(sender)[1] body_plain, body_html = '', '' for ignore in IgnoreEmail.objects.filter(Q(queues=queue) | Q(queues__isnull=True)): if ignore.test(sender_email): if ignore.keep_in_mailbox: # By returning 'False' the message will be kept in the mailbox, # and the 'True' will cause the message to be deleted. return False return True matchobj = re.match(r'.*\['+re.escape(queue.slug)+r'-(?P<id>\d+)\]', subject) if matchobj: # This is a reply or forward. ticket = matchobj.group('id') else: ticket = None counter = 0 files = [] for part in message.walk(): if part.get_content_maintype() == 'multipart': continue name = part.get_param("name") if name: name = collapse_rfc2231_value(name) if part.get_content_maintype() == 'text' and name is None: if part.get_content_subtype() == 'plain': body_plain = EmailReplyParser.parse_reply(decodeUnknown(part.get_content_charset(), part.get_payload(decode=True))) else: body_html = decodeUnknown(part.get_content_charset(), part.get_payload(decode=True)) # make plain text more legible when viewing the ticket body_html, n = re.subn(r'[\r\n]+', r'', body_html) body_html, n = re.subn(r'\>\s+\<', r'><', body_html) body_html = body_html.replace("</h1>", "</h1>\n") body_html = body_html.replace("</h2>", "</h2>\n") body_html = body_html.replace("</h3>", "</h3>\n") body_html = body_html.replace("<p>", "\n<p>") body_html = body_html.replace("</p>", "</p>\n") body_html = body_html.replace("</div>", "</div>\n") body_html = body_html.replace("</tr>", "</tr>\n") body_html = body_html.replace("</td>", "</td> ") body_html = body_html.replace("<table>", "\n<table>") body_html = body_html.replace("</table>", "</table>\n") body_html = body_html.replace("<br />", "<br />\n") try: # strip html tags body_plain = striptags(body_html) except DjangoUnicodeDecodeError: charset = chardet.detect(body_html)['encoding'] body_plain = striptags(str(body_html, charset)) body_plain = unescape(body_plain) else: if not name: ext = mimetypes.guess_extension(part.get_content_type()) name = "part-%i%s" % (counter, ext) files.append({ 'filename': name, 'content': part.get_payload(decode=True), 'type': part.get_content_type()}, ) counter += 1 if body_plain: body = body_plain if body_html: body += '\n\n' body += _('***Note that HTML tags are stripped out. Please see attachment email_html_body.html for the full html content.') else: body = _('No plain-text email body available. Please see attachment email_html_body.html.') if body_html: files.append({ 'filename': _("email_html_body.html"), 'content': body_html, 'type': 'text/html', }) now = timezone.now() if ticket: try: t = Ticket.objects.get(id=ticket) new = False except Ticket.DoesNotExist: ticket = None priority = 3 smtp_priority = message.get('priority', '') smtp_importance = message.get('importance', '') high_priority_types = ('high', 'important', '1', 'urgent') if smtp_priority in high_priority_types or smtp_importance in high_priority_types: priority = 2 if ticket is None: t = Ticket( title=subject, queue=queue, submitter_email=sender_email, created=now, description=body, priority=priority, ) t.save() new = True #update = '' elif t.status == Ticket.CLOSED_STATUS: t.status = Ticket.REOPENED_STATUS t.save() f = FollowUp( ticket = t, title = _('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}), date = timezone.now(), public = True, comment = body, ) if t.status == Ticket.REOPENED_STATUS: f.new_status = Ticket.REOPENED_STATUS f.title = _('Ticket Re-Opened by E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}) f.save() if not quiet: print((" [%s-%s] %s" % (t.queue.slug, t.id, t.title,)).encode('ascii', 'replace')) for file in files: if file['content']: filename = file['filename'].replace(' ', '_') filename = re.sub(r'[^a-zA-Z0-9._-]+', '', filename) a = Attachment( followup=f, filename=filename, mime_type=file['type'], size=len(file['content']), ) a.file.save(filename, ContentFile(file['content']), save=False) a.save() if not quiet: print(" - %s" % filename) context = safe_template_context(t) if new: if sender_email and not is_no_reply_address(sender_email): send_templated_mail( 'newticket_submitter', context, recipients=sender_email, sender=queue.from_address, fail_silently=True, ) if queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.new_ticket_cc, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc and queue.updated_ticket_cc != queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) else: context.update(comment=f.comment) #if t.status == Ticket.REOPENED_STATUS: # update = _(' (Reopened)') #else: # update = _(' (Updated)') if t.assigned_to: send_templated_mail( 'updated_owner', context, recipients=t.assigned_to.email, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc: send_templated_mail( 'updated_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) return t
def ticket_from_message(message, queue, quiet): # 'message' must be an RFC822 formatted message. msg = message message = email.message_from_string(msg.decode('utf-8')) subject = message.get('subject', _('Created from e-mail')) subject = decode_mail_headers(decodeUnknown(message.get_charset(), subject)) subject = subject.replace("Re: ", "").replace("Fw: ", "").replace("RE: ", "").replace("FW: ", "").replace("Automatic reply: ", "").strip() sender = message.get('from', _('Unknown Sender')) sender = decode_mail_headers(decodeUnknown(message.get_charset(), sender)) sender_email = parseaddr(sender)[1] body_plain, body_html = '', '' for ignore in IgnoreEmail.objects.filter(Q(queues=queue) | Q(queues__isnull=True)): if ignore.test(sender_email): if ignore.keep_in_mailbox: # By returning 'False' the message will be kept in the mailbox, # and the 'True' will cause the message to be deleted. return False return True matchobj = re.match(r'.*\['+re.escape(queue.slug)+r'-(?P<id>\d+)\]', subject) if matchobj: # This is a reply or forward. ticket = matchobj.group('id') else: ticket = None counter = 0 files = [] for part in message.walk(): if part.get_content_maintype() == 'multipart': continue name = part.get_param("name") if name: name = collapse_rfc2231_value(name) if part.get_content_maintype() == 'text' and name is None: if part.get_content_subtype() == 'plain': body_plain = EmailReplyParser.parse_reply(decodeUnknown(part.get_content_charset(), part.get_payload(decode=True))) else: body_html = decodeUnknown(part.get_content_charset(), part.get_payload(decode=True)) # make plain text more legible when viewing the ticket body_html, n = re.subn(r'[\r\n]+', r'', body_html) body_html, n = re.subn(r'\>\s+\<', r'><', body_html) body_html = body_html.replace("</h1>", "</h1>\n") body_html = body_html.replace("</h2>", "</h2>\n") body_html = body_html.replace("</h3>", "</h3>\n") body_html = body_html.replace("<p>", "\n<p>") body_html = body_html.replace("</p>", "</p>\n") body_html = body_html.replace("</div>", "</div>\n") body_html = body_html.replace("</tr>", "</tr>\n") body_html = body_html.replace("</td>", "</td> ") body_html = body_html.replace("<table>", "\n<table>") body_html = body_html.replace("</table>", "</table>\n") body_html = body_html.replace("<br />", "<br />\n") try: # strip html tags body_plain = striptags(body_html) except DjangoUnicodeDecodeError: charset = chardet.detect(body_html)['encoding'] body_plain = striptags(str(body_html, charset)) body_plain = unescape(body_plain) else: if not name: ext = mimetypes.guess_extension(part.get_content_type()) name = "part-%i%s" % (counter, ext) files.append({ 'filename': name, 'content': part.get_payload(decode=True), 'type': part.get_content_type()}, ) counter += 1 if body_plain: body = body_plain if body_html: body += '\n\n' body += _('***Note that HTML tags are stripped out. Please see attachment email_html_body.html for the full html content.') else: body = _('No plain-text email body available. Please see attachment email_html_body.html.') if body_html: files.append({ 'filename': _("email_html_body.html"), 'content': body_html, 'type': 'text/html', }) now = timezone.now() if ticket: try: t = Ticket.objects.get(id=ticket) new = False except Ticket.DoesNotExist: ticket = None priority = 3 smtp_priority = message.get('priority', '') smtp_importance = message.get('importance', '') high_priority_types = ('high', 'important', '1', 'urgent') if smtp_priority in high_priority_types or smtp_importance in high_priority_types: priority = 2 if ticket is None: t = Ticket( title=subject, queue=queue, submitter_email=sender_email, created=now, description=body, priority=priority, ) t.save() new = True #update = '' elif t.status == Ticket.CLOSED_STATUS: t.status = Ticket.REOPENED_STATUS t.save() f = FollowUp( ticket = t, title = _('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}), date = timezone.now(), public = True, comment = body, ) if t.status == Ticket.REOPENED_STATUS: f.new_status = Ticket.REOPENED_STATUS f.title = _('Ticket Re-Opened by E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}) f.save() if not quiet: print((" [%s-%s] %s" % (t.queue.slug, t.id, t.title,)).encode('ascii', 'replace')) for file in files: if file['content']: filename = file['filename'].replace(' ', '_') filename = re.sub(r'[^a-zA-Z0-9._-]+', '', filename) a = Attachment( followup=f, filename=filename, mime_type=file['type'], size=len(file['content']), ) a.file.save(filename, ContentFile(file['content']), save=False) a.save() if not quiet: print(" - %s" % filename) context = safe_template_context(t) if new: if sender_email: send_templated_mail( 'newticket_submitter', context, recipients=sender_email, sender=queue.from_address, fail_silently=True, ) if queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.new_ticket_cc, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc and queue.updated_ticket_cc != queue.new_ticket_cc: send_templated_mail( 'newticket_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) else: context.update(comment=f.comment) #if t.status == Ticket.REOPENED_STATUS: # update = _(' (Reopened)') #else: # update = _(' (Updated)') if t.assigned_to: send_templated_mail( 'updated_owner', context, recipients=t.assigned_to.email, sender=queue.from_address, fail_silently=True, ) if queue.updated_ticket_cc: send_templated_mail( 'updated_cc', context, recipients=queue.updated_ticket_cc, sender=queue.from_address, fail_silently=True, ) return t