import edward_config
-langs = ["de", "el", "en", "es", "fr", "it", "ja", "pt-br", "ro", "ru", "tr"]
+langs = ["de", "el", "en", "es", "fr", "it", "ja", "pt-br", "ro", "ru", "tr", "zh"]
"""This list contains the abbreviated names of reply languages available to
edward."""
print_reply_only = handle_args()
+ email_bytes = sys.stdin.buffer.read()
+
+ test_auto_reply(email_bytes)
+
gpgme_ctx = get_gpg_context(edward_config.gnupghome,
edward_config.sign_with_key)
- email_bytes = sys.stdin.buffer.read()
+ # do this twice so sigs can be verified with newly imported keys
+ parse_pgp_mime(email_bytes, gpgme_ctx)
email_struct = parse_pgp_mime(email_bytes, gpgme_ctx)
- email_to, email_from, email_subject = email_to_from_subject(email_bytes)
+ email_to, email_reply_to, email_subject = email_to_reply_to_subject(email_bytes)
lang, reply_from = import_lang_pick_address(email_to, edward_config.hostname)
replyinfo_obj = ReplyInfo()
get_key_from_fp(replyinfo_obj, gpgme_ctx)
reply_plaintext = write_reply(replyinfo_obj)
- reply_mime = generate_encrypted_mime(reply_plaintext, email_from, \
+ reply_mime = generate_encrypted_mime(reply_plaintext, email_reply_to, reply_from, \
email_subject, replyinfo_obj.encrypt_to_key,
gpgme_ctx)
if print_reply_only == True:
print(reply_mime)
else:
- send_reply(reply_mime, email_from, reply_from)
+ send_reply(reply_mime, email_reply_to, reply_from)
def get_gpg_context (gnupghome, sign_with_key_fp):
return [payload_piece]
flags = re.DOTALL | re.MULTILINE
- matches = re.search("(?P<beginning>.*?)(?P<match>" + pattern +
- ")(?P<rest>.*)", payload_piece.string, flags=flags)
+ matches = re.search(pattern, payload_piece.string, flags=flags)
if matches == None:
pieces = [payload_piece]
else:
beginning = PayloadPiece()
- beginning.string = matches.group('beginning')
+ beginning.string = payload_piece.string[:matches.start()]
beginning.piece_type = payload_piece.piece_type
match = PayloadPiece()
- match.string = matches.group('match')
+ match.string = payload_piece.string[matches.start():matches.end()]
match.piece_type = match_name
rest = PayloadPiece()
- rest.string = matches.group('rest')
+ rest.string = payload_piece.string[matches.end():]
rest.piece_type = payload_piece.piece_type
more_pieces = scan_and_split(rest, match_name, pattern)
if replyinfo_obj.decrypt_success == True:
debug('decrypt success')
reply_plain += replyinfo_obj.replies['success_decrypt']
-
- if (replyinfo_obj.sig_success == True) and (replyinfo_obj.have_reply_key == True):
- debug('message quoted')
- reply_plain += replyinfo_obj.replies['space']
- reply_plain += replyinfo_obj.replies['quote_follows']
- reply_plain += "\n\n"
- quoted_text = email_quote_text(replyinfo_obj.msg_to_quote)
- reply_plain += quoted_text
-
reply_plain += "\n\n"
elif replyinfo_obj.decrypt_failure == True:
reply_plain += "\n\n"
+ if (replyinfo_obj.decrypt_success == True) \
+ and (replyinfo_obj.sig_success == True) \
+ and (replyinfo_obj.have_reply_key == True):
+ debug('message quoted')
+ reply_plain += replyinfo_obj.replies['quote_follows']
+ reply_plain += "\n\n"
+ quoted_text = email_quote_text(replyinfo_obj.msg_to_quote)
+ reply_plain += quoted_text
+ reply_plain += "\n\n"
+
+
if (reply_plain == ""):
debug('plaintext message')
reply_plain += replyinfo_obj.replies['failed_decrypt']
return False
-def email_to_from_subject (email_bytes):
- """Returns the values of the email's To:, From: and Subject: fields
+def test_auto_reply (email_bytes):
+ """Test whether email is auto-generated
+
+ If the email is autogenerated, edward quits without sending a response.
+ This is not a perfect test. Some auto-responses will go undetected.
+
+ Args:
+ email_bytes: the byte string from of the email
+
+ Returns:
+ Nothing, or exits the program
+ """
+
+ email_struct = email.parser.BytesHeaderParser().parsebytes(email_bytes)
+
+ auto_submitted = email_struct['Auto-Submitted']
+
+ if auto_submitted == None or auto_submitted == "no" \
+ or auto_submitted == "No":
+
+ return
+
+ debug("autoreply")
+ exit(0)
+
+
+def email_to_reply_to_subject (email_bytes):
+ """Returns the email's To:, Reply-To: (or From:), and Subject: fields
Returns this information from an email.
email_bytes: the byte string form of the email
Returns:
- the email To:, From:, and Subject: fields as strings
+ the email To:, Reply-To: (or From:), and Subject: fields as strings
"""
- email_struct = email.parser.BytesParser().parsebytes(email_bytes)
+ email_struct = email.parser.BytesHeaderParser().parsebytes(email_bytes)
email_to = email_struct['To']
email_from = email_struct['From']
+ email_reply_to = email_struct['Reply-To']
+
email_subject = email_struct['Subject']
- return email_to, email_from, email_subject
+ if email_reply_to == None:
+ email_reply_to = email_from
+
+ return email_to, email_reply_to, email_subject
def import_lang_pick_address(email_to, hostname):
return lang_module, reply_from
-def generate_encrypted_mime (plaintext, email_to, email_subject, encrypt_to_key,
- gpgme_ctx):
+def generate_encrypted_mime (plaintext, email_to, email_from, email_subject,
+ encrypt_to_key, gpgme_ctx):
"""This function creates the mime email reply. It can encrypt the email.
If the encrypt_key is included, then the email is encrypted and signed.
else:
message_mime = plaintext_mime
+ message_mime['Auto-Submitted'] = 'auto-replied'
+
message_mime['To'] = email_to
+ message_mime['From'] = email_from
message_mime['Subject'] = email_subject
reply = message_mime.as_string()