# only include a signed message in the reply.
get_signed_part = True
- replyinfo_obj.msg_to_quote = flatten_decrypted_payloads(piece.gpg_data.plainobj, get_signed_part)
+ flatten_decrypted_payloads(piece.gpg_data.plainobj, replyinfo_obj, get_signed_part)
# to catch public keys in encrypted blocks
prepare_for_reply(piece.gpg_data.plainobj, replyinfo_obj)
replyinfo_obj.fallback_target_key = piece.gpg_data.sigs[0]
+def flatten_decrypted_payloads (eddymsg_obj, replyinfo_obj, get_signed_part):
+ """For creating a string representation of a signed, encrypted part.
-def flatten_decrypted_payloads (eddymsg_obj, get_signed_part):
- """Returns a string representation of a signed, encrypted part.
-
- Returns the string representation of the first signed/encrypted or signed
- then encrypted block of text. (Signature inside of Encryption)
+ When given a decrypted payload, it will add either the plaintext or signed
+ plaintext to the reply message, depeding on 'get_signed_part'. This is
+ useful for ensuring that the reply message only comes from a signed and
+ ecrypted GPG message. It also sets the target_key for encrypting the reply
+ if it's told to get signed text only.
Args:
eddymsg_obj: the message in EddyMsg format created by decrypting GPG
text
+ replyinfo_obj: a ReplyInfo object for holding the message to quote and
+ the target_key to encrypt to.
get_signed_part: True if we should only include text that contains a
further signature. If False, then include plain text.
Returns:
- A string representation of encrypted and signed text.
+ Nothing
Pre:
The EddyMsg instance passed in should be a piece.gpg_data.plainobj
which represents decrypted text. It may or may not be signed on that
level.
- """
- flat_string = ""
+ Post:
+ the ReplyInfo instance may have a new 'target_key' set and its
+ 'msg_to_quote' will be updated with (possibly signed) plaintext, if any
+ could be found.
+ """
if eddymsg_obj == None:
- return ""
+ return
# recurse on multi-part mime
if eddymsg_obj.multipart == True:
for sub in eddymsg_obj.subparts:
- flat_string += flatten_decrypted_payloads (sub, get_signed_part)
-
- return flat_string
+ flatten_decrypted_payloads(sub, replyinfo_obj, get_signed_part)
for piece in eddymsg_obj.payload_pieces:
if (get_signed_part):
or (piece.piece_type == "detachedsig") \
or (piece.piece_type == "signature")) \
and (piece.gpg_data != None):
- # FIXME: the key used to sign this message needs to be the one that is used for the encrypted reply.
- flat_string += flatten_decrypted_payloads (piece.gpg_data.plainobj, False)
+ flatten_decrypted_payloads(piece.gpg_data.plainobj, replyinfo_obj, False)
+ replyinfo_obj.target_key = piece.gpg_data.sigs[0]
break
else:
if piece.piece_type == "text":
- flat_string += piece.string
-
- return flat_string
+ replyinfo_obj.msg_to_quote += piece.string
def get_key_from_fp (replyinfo_obj, gpgme_ctx):
return (plaintext, fingerprints)
+def email_to_from_subject (email_text):
+ """Returns the values of the email's To:, From: and Subject: fields
+ Returns this information from an email.
+ Args:
+ email_text: the string form of the email
-def email_to_from_subject (email_text):
+ Returns:
+ the email To:, From:, and Subject: fields as strings
+ """
email_struct = email.parser.Parser().parsestr(email_text)
def import_lang(email_to):
+ """Imports appropriate language file for basic i18n support
+
+ The language imported depends on the To: address of the email received by
+ edward. an -en ending implies the English language, whereas a -ja ending
+ implies Japanese. The list of supported languages is listed in the 'langs'
+ list at the beginning of the program.
+
+ Args:
+ email_to: the string containing the email address that the mail was
+ sent to.
+
+ Returns:
+ the reference to the imported language module. The only variable in
+ this file is the 'replies' dictionary.
+ """
if email_to != None:
for lang in langs:
def generate_encrypted_mime (plaintext, 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.
+ Otherwise it is unencrypted.
+
+ Args:
+ plaintext: the plaintext body of the message to create.
+ email_from: the email address to reply to
+ email_subject: the subject to use in reply
+ encrypt_to_key: the key object to use for encrypting the email. (or
+ None)
+ gpgme_ctx: the gpgme context
+
+ Returns
+ A string version of the mime message, possibly encrypted and signed.
+ """
# quoted printable encoding lets most ascii characters look normal
# before the mime message is decoded.
encrypted_text = encrypt_sign_message(plaintext_mime.as_string(),
encrypt_to_key,
gpgme_ctx)
+ gpg_payload = encrypted_text
+
+ else:
+ signed_text = sign_message(plaintext_mime.as_string(), gpgme_ctx)
+ gpg_payload = signed_text
- control_mime = MIMEApplication("Version: 1",
- _subtype='pgp-encrypted',
- _encoder=email.encoders.encode_7or8bit)
- control_mime['Content-Description'] = 'PGP/MIME version identification'
- control_mime.set_charset('us-ascii')
+ control_mime = MIMEApplication("Version: 1",
+ _subtype='pgp-encrypted',
+ _encoder=email.encoders.encode_7or8bit)
+ control_mime['Content-Description'] = 'PGP/MIME version identification'
+ control_mime.set_charset('us-ascii')
- encoded_mime = MIMEApplication(encrypted_text,
- _subtype='octet-stream; name="encrypted.asc"',
- _encoder=email.encoders.encode_7or8bit)
- encoded_mime['Content-Description'] = 'OpenPGP encrypted message'
- encoded_mime['Content-Disposition'] = 'inline; filename="encrypted.asc"'
- encoded_mime.set_charset('us-ascii')
+ encoded_mime = MIMEApplication(gpg_payload,
+ _subtype='octet-stream; name="encrypted.asc"',
+ _encoder=email.encoders.encode_7or8bit)
+ encoded_mime['Content-Description'] = 'OpenPGP encrypted message'
+ encoded_mime['Content-Disposition'] = 'inline; filename="encrypted.asc"'
+ encoded_mime.set_charset('us-ascii')
- message_mime = MIMEMultipart(_subtype="encrypted", protocol="application/pgp-encrypted")
- message_mime.attach(control_mime)
- message_mime.attach(encoded_mime)
- message_mime['Content-Disposition'] = 'inline'
+ message_mime = MIMEMultipart(_subtype="encrypted", protocol="application/pgp-encrypted")
+ message_mime.attach(control_mime)
+ message_mime.attach(encoded_mime)
+ message_mime['Content-Disposition'] = 'inline'
- else:
- message_mime = plaintext_mime
message_mime['To'] = email_from
message_mime['Subject'] = email_subject
def email_quote_text (text):
+ """Quotes input text by inserting "> "s
+
+ This is useful for quoting a text for the reply message. It inserts "> "
+ strings at the beginning of lines.
+
+ Args:
+ text: plain text to quote
+
+ Returns:
+ Quoted text
+ """
quoted_message = re.sub(r'^', r'> ', text, flags=re.MULTILINE)
def encrypt_sign_message (plaintext, encrypt_to_key, gpgme_ctx):
+ """Encrypts and signs plaintext
+
+ This encrypts and signs a message.
+
+ Args:
+ plaintext: text to sign and ecrypt
+ encrypt_to_key: the key object to encrypt to
+ gpgme_ctx: the gpgme context
+
+ Returns:
+ An encrypted and signed string of text
+ """
+ # the plaintext should be mime encoded in an ascii-compatible form
plaintext_bytes = io.BytesIO(plaintext.encode('ascii'))
encrypted_bytes = io.BytesIO()
return encrypted_txt
+def sign_message (plaintext, gpgme_ctx):
+ """Signs plaintext
+
+ This signs a message.
+
+ Args:
+ plaintext: text to sign
+ gpgme_ctx: the gpgme context
+
+ Returns:
+ An armored signature as a string of text
+ """
+
+ # the plaintext should be mime encoded in an ascii-compatible form
+ plaintext_bytes = io.BytesIO(plaintext.encode('ascii'))
+ signed_bytes = io.BytesIO()
+
+ gpgme_ctx.sign(plaintext_bytes, signed_bytes, gpgme.SIG_MODE_NORMAL)
+
+ signed_txt = signed_bytes.getvalue().decode('ascii')
+ return signed_txt
+
+
def error (error_msg):
+ """Write an error message to stdout
+
+ The error message includes the program name.
+
+ Args:
+ error_msg: the message to print
+
+ Returns:
+ Nothing
+
+ Post:
+ An error message is printed to stdout
+ """
sys.stderr.write(progname + ": " + str(error_msg) + "\n")
def debug (debug_msg):
+ """Writes a debug message to stdout if debug == True
+
+ If the debug option is set in edward_config.py, then the passed message
+ gets printed to stdout.
+
+ Args:
+ debug_msg: the message to print to stdout
+
+ Returns:
+ Nothing
+
+ Post:
+ A debug message is printed to stdout
+ """
if edward_config.debug == True:
error(debug_msg)
def handle_args ():
+ """Sets the progname variable and complains about any arguments
+
+ If there are any arguments, then edward complains and quits, because input
+ is read from stdin.
+
+ Args:
+ None
+
+ Returns:
+ None
+
+ Post:
+ Exits with error 1 if there are arguments, otherwise returns to the
+ calling function, such as main().
+ """
global progname
progname = sys.argv[0]
if __name__ == "__main__":
+ """Executes main if this file is not loaded interactively"""
main()