コード例 #1
0
def handle_post_squadbox(message, group, host, verified):

    email_message = message_from_string(str(message))
    msg_id = message['Message-ID']

    sender_name, sender_addr = parseaddr(message['From'])
    sender_addr = sender_addr.lower()
    if sender_name == '':
        sender_name = None

    # if this looks like a double-post, ignore it
    if check_duplicate(message, group, sender_addr):
        logging.debug("ignoring duplicate")
        return

    subj = message['Subject'].strip()
    message_is_reply = (subj[0:4].lower() == "re: ")

    if message_is_reply:
        original_subj = subj[4:]
        post_subject = "Re: " + re.sub("\[.*?\]", "", subj[4:])
    else:
        original_subj = subj
        post_subject = subj

    res = get_attachments(email_message)
    check = check_attachments(res, group.allow_attachments)

    if not check['status']:
        send_error_email(group.name, check['error'], sender_addr, ADMIN_EMAILS)
        return

    attachments = res['attachments']

    msg_text = get_body(email_message)
    msg_text = check_html_and_plain(msg_text, message_is_reply)

    # initially, assume that it's pending and will go through moderation.
    status = 'P'
    reason = None

    # deactivated squad - message is auto-approved.
    if not group.active:
        status = 'A'
        reason = 'deactivated'
        logging.debug("Squad deactivated; automatically approving message")
    else:

        # first try whitelist/blacklist
        status, reason = check_whitelist_blacklist(group, sender_addr)

        # if that didn't give us an answer
        if not reason:

            # see if moderation has been shut off for this user/thread combo. that happens if either:
            # 1) group has setting on to auto-approve posts from a user to thread after their 1st post to thread is approved
            # 2) that setting isn't on, but recipient manually shut off moderation for this user to this thread
            if check_if_sender_approved_for_thread(group.name, sender_addr,
                                                   original_subj):
                status = 'A'

                # case 1
                if group.auto_approve_after_first:
                    reason = 'auto approve on'
                    logging.debug(
                        'Sender approved for this thread previously; automatically approving post'
                    )
                # case 2
                else:
                    reason = "mod off for sender-thread"

            else:
                logging.debug('Post needs to be moderated')

    # should refactor logic to check for this first, but for now
    # doing it here will be ok. (should it supercede all
    # of the other rules?)
    if len(attachments) > 0 and not group.allow_attachments:
        status = 'R'
        reason = 'no attachments'
        logging.debug("squad does not allow attachments")

    moderators = MemberGroup.objects.filter(group=group, moderator=True)
    if not moderators.exists():
        status = 'A'
        reason = 'no mods'
        logging.debug("Squad has no moderators")

    elif moderators.filter(member__email=sender_addr).exists():
        status = 'A'
        reason = 'is mod'
        logging.debug('Message is from a moderator')

    elif MemberGroup.objects.filter(group=group,
                                    member__email=sender_addr,
                                    admin=True).exists():
        status = 'A'
        reason = 'is owner'
        logging.debug('Message is from owner')

    # if pending or rejected, we need to put it in the DB
    if status in ['P', 'R']:
        if message_is_reply:
            insert_func = insert_squadbox_reply
        else:
            insert_func = insert_post

        args = [
            group.name, post_subject, msg_text['html'], None, sender_addr,
            msg_id, verified
        ]
        keyword_args = {
            'attachments': attachments,
            'sender_name': sender_name,
            'post_status': status
        }

        res = insert_func(*args, **keyword_args)

        if not res['status']:
            send_error_email(group.name, res['code'], None, ADMIN_EMAILS)
            return

        post_id = res['post_id']

        res = upload_message(message, post_id, msg_id)
        if not res['status']:
            logging.debug(
                "Error uploading original post to s3; continuing anyway")

    # one of following is true:
    # 1) sender is whitelisted
    # 2) sender is blacklisted, but the user still wants rejected messages.
    # 3) moderation is turned off for now (inactive group)
    # 4) this group doesn't have any moderators yet
    # 4) group has "auto approve after first post" on, and this sender posted before and got approved
    # (and the recipient did not subsequently opt back in to moderation for that user)
    # 5) group has "auto approve after first post" off, but owner manually shut off moderation for this sender

    if status == 'A' or (status == 'R' and group.send_rejected_tagged):

        # we can just send it on to the intended recipient, i.e. the admin of the group.
        admin_mg = MemberGroup.objects.get(group=group, admin=True)
        admin = admin_mg.member

        if status == 'A':
            try:
                mail_service = build_services(admin)['mail']
                logging.debug("MAIL SERVICE:", mail_service)
                updated_count = untrash_message(mail_service, sender_addr,
                                                subj)
                if updated_count > 0:
                    logging.debug("untrashed count: %s" % updated_count)
                    return
            except Exception, e:
                logging.error("error untrashing msg: %s" % e)
                pass

        new_subj = subj
        if status == 'R':
            new_subj = '[Rejected] ' + new_subj

        mail = MurmurMailResponse(From=message['From'],
                                  To=admin.email,
                                  Subject=new_subj)

        fix_headers(message, mail)

        ccs = email_message.get_all('cc', None)
        if ccs:
            mail['Cc'] = ','.join(ccs)

        add_attachments(mail, attachments)

        html_blurb = unicode(
            ps_squadbox(sender_addr, reason, group.name,
                        group.auto_approve_after_first, original_subj, None,
                        True))
        html_blurb = ''
        mail.Html = get_new_body(msg_text, html_blurb, 'html')

        #plain_blurb = ps_squadbox(sender_addr, reason, group.name, group.auto_approve_after_first, original_subj, None, False)
        plain_blurb = ''
        mail.Body = get_new_body(msg_text, plain_blurb, 'plain')

        res = get_or_generate_filter_hash(admin, group.name)
        if res['status']:
            mail['List-Id'] = '%s@%s' % (res['hash'], BASE_URL)
            logging.error("updated list id to %s" % mail['List-Id'])

        relay.deliver(mail)
コード例 #2
0
ファイル: main.py プロジェクト: haystack/murmur
def handle_post_squadbox(message, group, host, verified):

    email_message = message_from_string(str(message))
    msg_id = message['Message-ID']

    sender_name, sender_addr = parseaddr(message['From'])
    sender_addr = sender_addr.lower()
    if sender_name == '':
        sender_name = None

    # if this looks like a double-post, ignore it
    if check_duplicate(message, group, sender_addr):
        logging.debug("ignoring duplicate")
        return
    
    subj = message['Subject'].strip()
    message_is_reply = (subj[0:4].lower() == "re: ")

    if message_is_reply:
        original_subj = subj[4:]
        post_subject = "Re: " + re.sub("\[.*?\]", "", subj[4:])
    else:
        original_subj = subj
        post_subject = subj

    res = get_attachments(email_message)
    check = check_attachments(res, group.allow_attachments)

    if not check['status']:
        send_error_email(group.name, check['error'], sender_addr, ADMIN_EMAILS)
        return

    attachments = res['attachments']

    msg_text = get_body(email_message)
    msg_text = check_html_and_plain(msg_text, message_is_reply)

    # initially, assume that it's pending and will go through moderation. 
    status = 'P'
    reason = None

    # deactivated squad - message is auto-approved. 
    if not group.active:
        status = 'A'
        reason = 'deactivated'
        logging.debug("Squad deactivated; automatically approving message")
    else:

        # first try whitelist/blacklist
        status, reason = check_whitelist_blacklist(group, sender_addr)

        # if that didn't give us an answer
        if not reason:

            # see if moderation has been shut off for this user/thread combo. that happens if either:
            # 1) group has setting on to auto-approve posts from a user to thread after their 1st post to thread is approved
            # 2) that setting isn't on, but recipient manually shut off moderation for this user to this thread
            if check_if_sender_approved_for_thread(group.name, sender_addr, original_subj):
                status = 'A'

                # case 1 
                if group.auto_approve_after_first:
                    reason = 'auto approve on'
                    logging.debug('Sender approved for this thread previously; automatically approving post')
                # case 2
                else:
                    reason = "mod off for sender-thread"
                    
            else:
                logging.debug('Post needs to be moderated')


    # should refactor logic to check for this first, but for now
    # doing it here will be ok. (should it supercede all
    # of the other rules?)
    if len(attachments) > 0 and not group.allow_attachments:
        status = 'R'
        reason = 'no attachments'
        logging.debug("squad does not allow attachments")


    moderators = MemberGroup.objects.filter(group=group, moderator=True)
    if not moderators.exists():
        status = 'A'
        reason = 'no mods'
        logging.debug("Squad has no moderators")

    elif moderators.filter(member__email=sender_addr).exists():
        status = 'A'
        reason = 'is mod'
        logging.debug('Message is from a moderator')

    elif MemberGroup.objects.filter(group=group, member__email=sender_addr, admin=True).exists():
        status = 'A'
        reason = 'is owner'
        logging.debug('Message is from owner')

    # if pending or rejected, we need to put it in the DB 
    if status in ['P', 'R']:
        if message_is_reply:
            insert_func = insert_squadbox_reply
        else:
            insert_func = insert_post
            
        args = [group.name, post_subject, msg_text['html'], None, sender_addr, msg_id, verified]
        keyword_args = {'attachments' : attachments, 'sender_name' : sender_name, 'post_status' : status}
    
        res = insert_func(*args, **keyword_args)

        if not res['status']:
            send_error_email(group.name, res['code'], None, ADMIN_EMAILS)
            return

        post_id = res['post_id']

        res = upload_message(message, post_id, msg_id)
        if not res['status']:
            logging.debug("Error uploading original post to s3; continuing anyway")

    # one of following is true: 
    # 1) sender is whitelisted
    # 2) sender is blacklisted, but the user still wants rejected messages. 
    # 3) moderation is turned off for now (inactive group)
    # 4) this group doesn't have any moderators yet
    # 4) group has "auto approve after first post" on, and this sender posted before and got approved 
    # (and the recipient did not subsequently opt back in to moderation for that user)
    # 5) group has "auto approve after first post" off, but owner manually shut off moderation for this sender

    if status == 'A' or (status == 'R' and group.send_rejected_tagged):

        # we can just send it on to the intended recipient, i.e. the admin of the group. 
        admin_mg = MemberGroup.objects.get(group=group, admin=True)
        admin = admin_mg.member

        if status == 'A':
            try:
                mail_service = build_services(admin)['mail']
                logging.debug("MAIL SERVICE:", mail_service)
                updated_count = untrash_message(mail_service, sender_addr, subj)
                if updated_count > 0:
                    logging.debug("untrashed count: %s" % updated_count)
                    return 
            except Exception, e:
                logging.error("error untrashing msg: %s" % e)
                pass

        new_subj = subj
        if status == 'R':
            new_subj = '[Rejected] ' + new_subj

        mail = MurmurMailResponse(From = message['From'], To = admin.email, Subject = new_subj)

        fix_headers(message, mail)

        ccs = email_message.get_all('cc', None)
        if ccs:
            mail['Cc'] = ','.join(ccs)

        add_attachments(mail, attachments)

        html_blurb = unicode(ps_squadbox(sender_addr, reason, group.name, group.auto_approve_after_first, original_subj, None, True))
        html_blurb = ''
        mail.Html = get_new_body(msg_text, html_blurb, 'html')

        #plain_blurb = ps_squadbox(sender_addr, reason, group.name, group.auto_approve_after_first, original_subj, None, False)
        plain_blurb = ''
        mail.Body = get_new_body(msg_text, plain_blurb, 'plain')

        res = get_or_generate_filter_hash(admin, group.name)
        if res['status']:
            mail['List-Id'] = '%s@%s' % (res['hash'], BASE_URL)
            logging.error("updated list id to %s" % mail['List-Id'])

        relay.deliver(mail)
コード例 #3
0
def handle_post_murmur(message, group, host, verified):

    # just setting up a bunch of variables with values we'll use
    email_message = message_from_string(str(message))

    to_header = email_message.get_all('to', [])
    to_emails = [i[1] for i in getaddresses(to_header)]
    sender_name, sender_addr = parseaddr(message['From'])
    sender_addr = sender_addr.lower()
    if not sender_name:
        sender_name = None

    # any of the lists in the "to" field might be what forwarded this to us
    possible_list_addresses = to_emails
    # if List-Id set that might also be it
    _, list_addr = parseaddr(message['List-Id'])
    if list_addr:
        possible_list_addresses.append(list_addr)

    # according to rules and group membership, can this post go through?
    can_post = check_if_can_post_murmur(group, sender_addr,
                                        possible_list_addresses)
    if not can_post['can_post']:
        error_msg = 'You are not authorized to post to the Murmur group %s@%s. This is either ' % (group.name, host) + \
        'because you are posting directly to the group, but you are not a member, or because you are posting to ' + \
        'a mailing list that forwards to the Murmur group, but does not have permission to do so.'

        send_error_email(group.name, error_msg, sender_addr, ADMIN_EMAILS)
        return

    # if this looks like a double-post, ignore it
    if check_duplicate(message, group, sender_addr):
        logging.debug("ignoring duplicate")
        return

    msg_id = message['Message-ID']
    msg_text = get_body(email_message)
    message_is_reply = (message['Subject'][0:4].lower() == "re: ")
    msg_text = check_html_and_plain(msg_text, message_is_reply)

    if msg_text['plain'].startswith(
            'unsubscribe\n') or msg_text['plain'] == 'unsubscribe':
        unsubscribe(message, group_name=group.name, host=HOST)
        return
    elif msg_text['plain'].startswith(
            'subscribe\n') or msg_text['plain'] == 'subscribe':
        subscribe(message, group_name=group.name, host=HOST)
        return

    # get user and/or forwarding list objects (however we are receiving this message)
    if can_post['reason'] == 'is_member':
        user = can_post['which_user']
        original_list = None
        original_list_email = None
    elif can_post['reason'] == 'via_list':
        user = None
        original_list = can_post['which_list']
        original_list_email = original_list.email

    # if we make it to here, then post is valid under one of following conditions:
    # 1) it's a normal post by a group member to the group.
    # 2) it's a post via a list that is allowed to fwd to this group, by someone who may
    # or may not be a Murmur user, but is not a group member.

    # _create_post (called by both insert_reply and insert_post) will check which of user
    # and forwarding list are None and post appropriately.
    try:
        res = get_attachments(email_message)
        check = check_attachments(res, group.allow_attachments)

        if not check['status']:
            send_error_email(group.name, check['error'], sender_addr,
                             ADMIN_EMAILS)
            return

        attachments = res['attachments']

        if message_is_reply:
            post_subject = "Re: " + re.sub("\[.*?\]", "",
                                           message['Subject'][4:]).strip()
            insert_func = insert_reply
        else:
            post_subject = message['Subject'].strip()
            insert_func = insert_post

        args = [
            group.name, post_subject, msg_text['html'], user, sender_addr,
            msg_id, verified
        ]
        keyword_args = {
            'attachments': attachments,
            'forwarding_list': original_list,
            'sender_name': sender_name
        }

        res = insert_func(*args, **keyword_args)

        if not res['status']:
            send_error_email(group.name, res['code'], sender_addr,
                             ADMIN_EMAILS)
            return

        post_id = res['post_id']

        s3_res = upload_message(message, post_id, msg_id)
        if not s3_res['status']:
            logging.debug(
                "Error uploading original post to s3; continuing anyway")

        subject = get_subject(message, res, group.name)
        mail = setup_post(message['From'], subject, group.name)

        fix_headers(message, mail)

        msg_id = res['msg_id']
        mail['message-id'] = msg_id

        to_send = res['recipients']
        logging.debug('TO LIST: ' + str(to_send))

        ccs = email_message.get_all('cc', None)
        if ccs:
            mail['Cc'] = ','.join(ccs)

        g = group
        t = Thread.objects.get(id=res['thread_id'])

        direct_recips = get_direct_recips(email_message)

        try:
            if len(to_send) > 0:

                recips = UserProfile.objects.filter(email__in=to_send)
                membergroups = MemberGroup.objects.filter(group=g,
                                                          member__in=recips)
                followings = Following.objects.filter(thread=t,
                                                      user__in=recips)
                mutings = Mute.objects.filter(thread=t, user__in=recips)

                tag_followings = FollowTag.objects.filter(
                    group=g, tag__in=res['tag_objs'], user__in=recips)
                tag_mutings = MuteTag.objects.filter(group=g,
                                                     tag__in=res['tag_objs'],
                                                     user__in=recips)

                for recip in recips:

                    # Don't send email to the sender if it came from email
                    # Don't send email to people that already directly got the email via CC/BCC
                    if recip.email == sender_addr or recip.email in direct_recips:
                        continue

                    # clear out message other than the headers
                    mail.clear()

                    membergroup = membergroups.filter(member=recip)[0]
                    following = followings.filter(user=recip).exists()
                    muting = mutings.filter(user=recip).exists()
                    tag_following = tag_followings.filter(user=recip)
                    tag_muting = tag_mutings.filter(user=recip)

                    has_attachments = len(attachments) > 0

                    html_ps_blurb = html_ps(
                        g,
                        t,
                        res['post_id'],
                        membergroup,
                        following,
                        muting,
                        tag_following,
                        tag_muting,
                        res['tag_objs'],
                        has_attachments,
                        original_list_email=original_list_email)
                    mail.Html = get_new_body(msg_text, html_ps_blurb, 'html')

                    plain_ps_blurb = plain_ps(
                        g,
                        t,
                        res['post_id'],
                        membergroup,
                        following,
                        muting,
                        tag_following,
                        tag_muting,
                        res['tag_objs'],
                        has_attachments,
                        original_list_email=original_list_email)
                    mail.Body = get_new_body(msg_text, plain_ps_blurb, 'plain')

                    if membergroup.receive_attachments:
                        add_attachments(mail, attachments)

                    relay.deliver(mail, To=recip.email)

            fwd_to_lists = ForwardingList.objects.filter(group=g,
                                                         can_receive=True)

            for l in fwd_to_lists:

                mail.clear()

                # non murmur list, send as usual
                if HOST not in l.email:

                    footer_html = html_forwarded_blurb(
                        g.name,
                        l.email,
                        original_list_email=original_list_email)
                    footer_plain = plain_forwarded_blurb(
                        g.name,
                        l.email,
                        original_list_email=original_list_email)

                    mail.Html = get_new_body(msg_text, footer_html, 'html')
                    mail.Body = get_new_body(msg_text, footer_plain, 'plain')
                    add_attachments(mail, attachments)

                    relay.deliver(mail, To=l.email)
                # it's another murmur list. can't send mail to ourself ("loops back to
                #myself" error) so have to directly pass back to handle_post
                else:
                    group_name = l.email.split('@')[0]
                    handle_post(message, address=group_name)

        except Exception, e:
            logging.debug(e)
            send_error_email(group.name, e, None, ADMIN_EMAILS)

            # try to deliver mail even without footers
            mail.Html = msg_text['html']
            mail.Body = msg_text['plain']
            relay.deliver(mail, To=to_send)

    except Exception, e:
        logging.debug(e)
        send_error_email(group.name, e, None, ADMIN_EMAILS)
        return
コード例 #4
0
ファイル: main.py プロジェクト: haystack/murmur
def handle_post_murmur(message, group, host, verified):

    # just setting up a bunch of variables with values we'll use
    email_message = message_from_string(str(message))

    to_header = email_message.get_all('to', [])
    to_emails = [i[1] for i in getaddresses(to_header)]
    sender_name, sender_addr = parseaddr(message['From'])
    sender_addr = sender_addr.lower()
    if not sender_name:
        sender_name = None
    else:
        sn = sender_name.split(" ")
        print "sender name", sender_name
        if len(sn) > 0:
            u = UserProfile.objects.filter(email=sender_addr)
            print "sender addr", sender_addr
            # update first/last name 
            if u.exists():
                first_name = ''
                last_name = ''

                first_name = sn[0].strip()[:30]
                print "first name", first_name
                entry = u[0]
                entry.first_name = first_name
                
                
                if len(sn) > 1:
                    last_name = " ".join(sn[1:]).strip()[:30]
                    entry.last_name = last_name

                entry.save()
            
    # any of the lists in the "to" field might be what forwarded this to us
    possible_list_addresses = to_emails
    # if List-Id set that might also be it 
    _, list_addr = parseaddr(message['List-Id'])
    if list_addr:
        possible_list_addresses.append(list_addr)

    # according to rules and group membership, can this post go through?
    can_post = check_if_can_post_murmur(group, sender_addr, possible_list_addresses)
    if not can_post['can_post']:
        error_msg = 'You are not authorized to post to the Murmur group %s@%s. This is either ' % (group.name, host) + \
        'because you are posting directly to the group, but you are not a member, or because you are posting to ' + \
        'a mailing list that forwards to the Murmur group, but does not have permission to do so.'

        send_error_email(group.name, error_msg, sender_addr, ADMIN_EMAILS)
        return

    # if this looks like a double-post, ignore it
    if check_duplicate(message, group, sender_addr):
        logging.debug("ignoring duplicate")
        return

    msg_id = message['Message-ID']
    msg_text = get_body(email_message)
    message_is_reply = (message['Subject'][0:4].lower() == "re: ")
    msg_text = check_html_and_plain(msg_text, message_is_reply)

    if msg_text['plain'].startswith('unsubscribe\n') or msg_text['plain'] == 'unsubscribe':
        unsubscribe(message, group_name = group.name, host = HOST)
        return
    elif msg_text['plain'].startswith('subscribe\n') or msg_text['plain'] == 'subscribe':
        subscribe(message, group_name = group.name, host = HOST)
        return

    # get user and/or forwarding list objects (however we are receiving this message) 
    if can_post['reason'] == 'is_member':
        user = can_post['which_user']
        original_list = None
        original_list_email = None
    elif can_post['reason'] == 'via_list':
        user = None
        original_list = can_post['which_list']
        original_list_email = original_list.email

    # if we make it to here, then post is valid under one of following conditions:
    # 1) it's a normal post by a group member to the group.
    # 2) it's a post via a list that is allowed to fwd to this group, by someone who may
    # or may not be a Murmur user, but is not a group member. 

    # _create_post (called by both insert_reply and insert_post) will check which of user 
    # and forwarding list are None and post appropriately. 
    try:
        res = get_attachments(email_message)
        check = check_attachments(res, group.allow_attachments)

        if not check['status']:
            send_error_email(group.name, check['error'], sender_addr, ADMIN_EMAILS)
            return

        attachments = res['attachments']

        if message_is_reply:
            post_subject = "Re: " + re.sub("\[.*?\]", "", message['Subject'][4:]).strip()
            insert_func = insert_reply
        else:
            post_subject = message['Subject'].strip()
            insert_func = insert_post
            
        args = [group.name, post_subject, msg_text['html'], user, sender_addr, msg_id, verified]
        keyword_args = {'attachments' : attachments, 'forwarding_list' : original_list, 'sender_name' : sender_name}

        res = insert_func(*args, **keyword_args)

        if not res['status']:
            send_error_email(group.name, res['code'], sender_addr, ADMIN_EMAILS)
            return

        post_id = res['post_id']

        s3_res = upload_message(message, post_id, msg_id)
        if not s3_res['status']:
            logging.debug("Error uploading original post to s3; continuing anyway")

        subject = get_subject(message, res, group.name)
        mail = setup_post(message['From'], subject, group.name)

        fix_headers(message, mail)
    
        msg_id = res['msg_id']
        mail['message-id'] = msg_id

        to_send =  res['recipients']
        print ('TO LIST: ' + str(to_send))
        
        ccs = email_message.get_all('cc', None)
        if ccs:
            mail['Cc'] = ','.join(ccs)
        
        g = group
        t = Thread.objects.get(id=res['thread_id'])
        
        direct_recips = get_direct_recips(email_message)
        
        try:
            if len(to_send) > 0:
                
                recips = UserProfile.objects.filter(email__in=to_send)
                membergroups = MemberGroup.objects.filter(group=g, member__in=recips)
                followings = Following.objects.filter(thread=t, user__in=recips)
                mutings = Mute.objects.filter(thread=t, user__in=recips)
                
                tag_followings = FollowTag.objects.filter(group=g, tag__in=res['tag_objs'], user__in=recips)
                tag_mutings = MuteTag.objects.filter(group=g, tag__in=res['tag_objs'], user__in=recips)
                
                for recip in recips:
                    
                    # Don't send email to the sender if it came from email
                    # Don't send email to people that already directly got the email via CC/BCC
                    if recip.email == sender_addr or recip.email in direct_recips:
                        continue

                    print "email to", recip.email
                    # Don't send email to the sender if she is at the sender's do-not-send list
                    # up = UserProfile.objects.filter(email=user.member.email)
                    # if DoNotSendList.objects.filter(group=g, user=up[0], donotsend_user=recip).exists():
                    #     continue
                    
                    # clear out message other than the headers
                    mail.clear()

                    membergroup = membergroups.filter(member=recip)[0]
                    following = followings.filter(user=recip).exists()
                    muting = mutings.filter(user=recip).exists()
                    tag_following = tag_followings.filter(user=recip)
                    tag_muting = tag_mutings.filter(user=recip)
                
                    has_attachments = len(attachments) > 0

                    html_ps_blurb = html_ps(g, t, res['post_id'], membergroup, following, muting, tag_following, tag_muting, res['tag_objs'], has_attachments, original_list_email=original_list_email)
                    mail.Html = get_new_body(msg_text, html_ps_blurb, 'html')
                    
                    plain_ps_blurb = plain_ps(g, t, res['post_id'], membergroup, following, muting, tag_following, tag_muting, res['tag_objs'], has_attachments, original_list_email=original_list_email)
                    mail.Body = get_new_body(msg_text, plain_ps_blurb, 'plain')

                    if membergroup.receive_attachments:
                        add_attachments(mail, attachments)

                    relay.deliver(mail, To = recip.email)

            fwd_to_lists = ForwardingList.objects.filter(group=g, can_receive=True) 

            for l in fwd_to_lists:
                print "forwarding emails", l.email 
                mail.clear()

                # non murmur list, send as usual 
                if HOST not in l.email:

                    footer_html = html_forwarded_blurb(g.name, l.email, original_list_email=original_list_email)
                    footer_plain = plain_forwarded_blurb(g.name, l.email, original_list_email=original_list_email)

                    mail.Html = get_new_body(msg_text, footer_html, 'html')
                    mail.Body = get_new_body(msg_text, footer_plain, 'plain')
                    add_attachments(mail, attachments)

                    relay.deliver(mail, To = l.email)
                # it's another murmur list. can't send mail to ourself ("loops back to 
                #myself" error) so have to directly pass back to handle_post 
                else:
                    group_name = l.email.split('@')[0]
                    handle_post(message, address=group_name)


        except Exception, e:
            logging.debug(e)
            send_error_email(group.name, e, None, ADMIN_EMAILS)
            
            # try to deliver mail even without footers
            mail.Html = msg_text['html']
            mail.Body = msg_text['plain']
            relay.deliver(mail, To = to_send)
                
    except Exception, e:
        logging.debug(e)
        send_error_email(group.name, e, None, ADMIN_EMAILS)
        return