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)
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)
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
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