Exemple #1
0
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
Exemple #2
0
 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()
Exemple #3
0
    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")
Exemple #4
0
 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()
Exemple #5
0
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
Exemple #6
0
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
Exemple #7
0
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
Exemple #8
0
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)
Exemple #9
0
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
Exemple #10
0
 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()
Exemple #11
0
 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()
Exemple #12
0
    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()
Exemple #13
0
    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()
Exemple #14
0
    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
Exemple #15
0
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
Exemple #16
0
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
Exemple #18
0
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)
Exemple #19
0
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
Exemple #20
0
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
Exemple #21
0
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)
Exemple #22
0
 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
Exemple #24
0
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
Exemple #25
0
 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()
Exemple #26
0
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
Exemple #27
0
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