def __init__(self): from pyme import core core.check_version(None) self.c = core.Context() self.c.set_armor(1) Gtk.Window.__init__(self, title="Bitcoin Offline Storage Tool") self.btc_filename = None self.gpg_filename = None self.button_generate = Gtk.Button(label="Generate") self.button_generate.connect("clicked", self.on_button_generate_clicked) box1 = Gtk.Box(spacing=20) label1 = Gtk.Label(label="Filename to write new keys to: ", halign=Gtk.Align.END) label2 = Gtk.Label(label="Number of keys/addresses to generate: ", halign=Gtk.Align.END) label3 = Gtk.Label( label= "GPG Encrypt using public keys for the following email addresses/GPG IDs: ", halign=Gtk.Align.END) label4 = Gtk.Label( label="Filename to write GPG keyring to (private keys included): ", halign=Gtk.Align.END) label5 = Gtk.Label( label= "Check this box to create a new GPG key-pair (unsure? check the box as 'yes'):", halign=Gtk.Align.END) self.file_chooser_btc = FileChooserEntry( self, title="Choose a file to save encrypted bitcoin addresses") self.file_chooser_gpg = FileChooserEntry( self, title="Choose a file to save GPG key files") self.num_of_keys_spinner = Gtk.SpinButton() self.num_of_keys_spinner.set_adjustment( Gtk.Adjustment(1.0, 0, 1000.0, 1, 0)) self.gpg_emails = Gtk.Entry() self.generate_gpg_keys = Gtk.CheckButton("Generate a new GPG key-pair") self.generate_gpg_keys.set_active(True) table = Gtk.Table(8, 2, True) table.attach(label1, 0, 1, 0, 1) table.attach(self.file_chooser_btc, 1, 2, 0, 1) table.attach(self.num_of_keys_spinner, 1, 2, 1, 2) table.attach(self.button_generate, 1, 2, 7, 8) table.attach(label2, 0, 1, 1, 2) table.attach(label3, 0, 1, 2, 3) table.attach(self.gpg_emails, 1, 2, 2, 3) table.attach(label4, 0, 1, 3, 4) table.attach(self.file_chooser_gpg, 1, 2, 3, 4) table.attach(label5, 0, 1, 4, 5) table.attach(self.generate_gpg_keys, 1, 2, 4, 5) box1.add(table) self.add(box1)
def generate_gpg_key_pair(self): """ Generate a key-pair for the encrypting of the BTC keys """ from pyme import core, callbacks # Initialize our context. core.check_version(None) c = self.c c.set_progress_cb(self.progress, None) pass1 = self.get_user_pw("Enter password for the private key", "Key generation") pass2 = self.get_user_pw("Repeat password for private key", "Key generation") if pass1 != pass2: raise Warning("Passwords do not match!") dlg = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "Click OK to start generating encryption key-pair.\n"+\ "This might take a very long time...") dlg.run() dlg.destroy() parms = """<GnupgKeyParms format="internal"> Key-Type: DSA Key-Length: 1024 Key-Usage: sign Subkey-Type: ELG-E Subkey-Length: 1024 Subkey-Usage: encrypt Name-Real: Bitcoin Key Pair Name-Comment: Key for the locking and unlocking of bitcoins Name-Email: bitcoin@localhost Passphrase: %s </GnupgKeyParms> """ % pass1 c.op_genkey(parms, None, None) fpr = c.op_genkey_result().fpr c.op_keylist_start(fpr, 0) key = c.op_keylist_next() enc_key = None for subkey in key.subkeys: keyid = subkey.keyid if keyid == None: break can_encrypt = subkey.can_encrypt if can_encrypt: enc_key = c.get_key(keyid, 0) if enc_key is None: print "No encryption key found!" return (enc_key, key)
def generate_gpg_key_pair(self): """ Generate a key-pair for the encrypting of the BTC keys """ from pyme import core, callbacks # Initialize our context. core.check_version(None) c = self.c c.set_progress_cb(self.progress, None) pass1 = self.get_user_pw("Enter password for the private key", "Key generation") pass2 = self.get_user_pw("Repeat password for private key", "Key generation") if pass1 != pass2: raise Warning("Passwords do not match!") dlg = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "Click OK to start generating encryption key-pair.\n"+\ "This might take a very long time...") dlg.run() dlg.destroy() parms = """<GnupgKeyParms format="internal"> Key-Type: DSA Key-Length: 1024 Key-Usage: sign Subkey-Type: ELG-E Subkey-Length: 1024 Subkey-Usage: encrypt Name-Real: Bitcoin Key Pair Name-Comment: Key for the locking and unlocking of bitcoins Name-Email: bitcoin@localhost Passphrase: %s </GnupgKeyParms> """%pass1 c.op_genkey(parms, None, None) fpr = c.op_genkey_result().fpr c.op_keylist_start(fpr, 0) key = c.op_keylist_next() enc_key = None for subkey in key.subkeys: keyid = subkey.keyid if keyid == None: break can_encrypt = subkey.can_encrypt if can_encrypt: enc_key = c.get_key(keyid, 0) if enc_key is None: print "No encryption key found!" return (enc_key, key)
def context(self): core.check_version() # pyme fails otherwise *facepalm* if hasattr(self._local, 'context') is False: context = core.Context() context.set_armor(True) if self._path or self._home: context.set_engine_info(constants.PROTOCOL_OpenPGP, self._path, self._home) self._local.context = context return self._local.context
def main(): # gpgme_check_version() necessary for initialisation according to # gogme 1.1.6 and this is not done automatically in pyme-0.7.0 print "gpgme version:", core.check_version(None) for eng in core.get_engine_info(): printgetkeyresults(eng)
def __init__(self): from pyme import core core.check_version(None) self.c = core.Context() self.c.set_armor(1) Gtk.Window.__init__(self, title="Bitcoin Offline Storage Tool") self.btc_filename = None self.gpg_filename = None self.button_generate = Gtk.Button(label="Generate") self.button_generate.connect("clicked", self.on_button_generate_clicked) box1 = Gtk.Box(spacing=20) label1 = Gtk.Label(label="Filename to write new keys to: ", halign=Gtk.Align.END) label2 = Gtk.Label(label="Number of keys/addresses to generate: ", halign=Gtk.Align.END) label3 = Gtk.Label(label="GPG Encrypt using public keys for the following email addresses/GPG IDs: ", halign=Gtk.Align.END) label4 = Gtk.Label(label="Filename to write GPG keyring to (private keys included): ", halign=Gtk.Align.END) label5 = Gtk.Label(label="Check this box to create a new GPG key-pair (unsure? check the box as 'yes'):", halign=Gtk.Align.END) self.file_chooser_btc = FileChooserEntry(self, title="Choose a file to save encrypted bitcoin addresses") self.file_chooser_gpg = FileChooserEntry(self, title="Choose a file to save GPG key files") self.num_of_keys_spinner = Gtk.SpinButton() self.num_of_keys_spinner.set_adjustment(Gtk.Adjustment(1.0, 0, 1000.0, 1, 0)) self.gpg_emails = Gtk.Entry() self.generate_gpg_keys = Gtk.CheckButton("Generate a new GPG key-pair") self.generate_gpg_keys.set_active(True) table = Gtk.Table(8,2,True) table.attach(label1, 0, 1, 0, 1) table.attach(self.file_chooser_btc, 1, 2, 0, 1) table.attach(self.num_of_keys_spinner, 1, 2, 1, 2) table.attach(self.button_generate, 1, 2, 7, 8) table.attach(label2, 0, 1, 1, 2) table.attach(label3, 0, 1, 2, 3) table.attach(self.gpg_emails, 1, 2, 2, 3) table.attach(label4, 0, 1, 3, 4) table.attach(self.file_chooser_gpg, 1, 2, 3, 4) table.attach(label5, 0, 1, 4, 5) table.attach(self.generate_gpg_keys, 1, 2, 4, 5) box1.add(table) self.add(box1)
def print_engine_infos(): print "gpgme version:", core.check_version(None) print "engines:" for engine in core.get_engine_info(): print engine.file_name, engine.version for proto in [protocol.OpenPGP, protocol.CMS]: print core.get_protocol_name(proto), core.engine_check_version(proto)
def genkey(): """Creates a new standard GPG key""" # Initialize our context. core.check_version(None) c = core.Context() c.set_armor(1) #c.set_progress_cb(callbacks.progress_stdout, None) # Check if standard key is already present keyname = config["gpg_key_uid"] c.op_keylist_start(keyname, 0) key = c.op_keylist_next() if key is None: # Key not present --> Create new one print "Generating new GPG key", keyname, \ ". This may take some time..." c.op_genkey(config["gpg_key_parameters"], None, None) print c.op_genkey_result().fpr
def printgetkeyresults(keyfpr): """Run gpgme_get_key().""" # gpgme_check_version() necessary for initialisation according to # gogme 1.1.6 and this is not done automatically in pyme-0.7.0 print "gpgme version:", core.check_version(None) c = core.Context() c.set_protocol(protocol.CMS) key = c.get_key(keyfpr, False) print "got key: ", key.subkeys[0].fpr for uid in key.uids: print uid.uid
def send(self, to, subject='[no subject]', body='[no body]', sender=None, attachments=None, cc=None, bcc=None, reply_to=None, encoding='utf-8', raw=False, headers={}, from_address=None, cipher_type=None, sign=None, sign_passphrase=None, encrypt=None, x509_sign_keyfile=None, x509_sign_chainfile=None, x509_sign_certfile=None, x509_crypt_certfiles=None, x509_nocerts=None ): """ Sends an email using data specified in constructor Args: to: list or tuple of receiver addresses; will also accept single object subject: subject of the email body: email body text; depends on type of passed object: - if 2-list or 2-tuple is passed: first element will be source of plain text while second of html text; - otherwise: object will be the only source of plain text and html source will be set to None If text or html source is: - None: content part will be ignored, - string: content part will be set to it, - file-like object: content part will be fetched from it using it's read() method attachments: list or tuple of Mailer.Attachment objects; will also accept single object cc: list or tuple of carbon copy receiver addresses; will also accept single object bcc: list or tuple of blind carbon copy receiver addresses; will also accept single object reply_to: address to which reply should be composed encoding: encoding of all strings passed to this method (including message bodies) headers: dictionary of headers to refine the headers just before sending mail, e.g. `{'X-Mailer' : 'web2py mailer'}` from_address: address to appear in the 'From:' header, this is not the envelope sender. If not specified the sender will be used cipher_type : gpg - need a python-pyme package and gpgme lib x509 - smime gpg_home : you can set a GNUPGHOME environment variable to specify home of gnupg sign : sign the message (True or False) sign_passphrase : passphrase for key signing encrypt : encrypt the message (True or False). It defaults to True. ... x509 only ... x509_sign_keyfile : the signers private key filename or string containing the key. (PEM format) x509_sign_certfile: the signers certificate filename or string containing the cert. (PEM format) x509_sign_chainfile: sets the optional all-in-one file where you can assemble the certificates of Certification Authorities (CA) which form the certificate chain of email certificate. It can be a string containing the certs to. (PEM format) x509_nocerts : if True then no attached certificate in mail x509_crypt_certfiles: the certificates file or strings to encrypt the messages with can be a file name / string or a list of file names / strings (PEM format) Examples: Send plain text message to single address:: mail.send('*****@*****.**', 'Message subject', 'Plain text body of the message') Send html message to single address:: mail.send('*****@*****.**', 'Message subject', '<html>Plain text body of the message</html>') Send text and html message to three addresses (two in cc):: mail.send('*****@*****.**', 'Message subject', ('Plain text body', '<html>html body</html>'), cc=['*****@*****.**', '*****@*****.**']) Send html only message with image attachment available from the message by 'photo' content id:: mail.send('*****@*****.**', 'Message subject', (None, '<html><img src="cid:photo" /></html>'), Mailer.Attachment('/path/to/photo.jpg' content_id='photo')) Send email with two attachments and no body text:: mail.send('[email protected], 'Message subject', None, [Mailer.Attachment('/path/to/fist.file'), Mailer.Attachment('/path/to/second.file')]) Returns: True on success, False on failure. Before return, method updates two object's fields: - self.result: return value of smtplib.SMTP.sendmail() or GAE's mail.send_mail() method """ # We don't want to use base64 encoding for unicode mail add_charset('utf-8', charset_QP, charset_QP, 'utf-8') def encode_header(key): if [c for c in key if 32 > ord(c) or ord(c) > 127]: return Header(key.encode('utf-8'), 'utf-8') else: return key # encoded or raw text def encoded_or_raw(text): if raw: text = encode_header(text) return text sender = sender or self.settings.sender if not isinstance(self.settings.server, str): raise Exception('Server address not specified') if not isinstance(sender, str): raise Exception('Sender address not specified') if not raw and attachments: # Use multipart/mixed if there is attachments payload_in = MIMEMultipart('mixed') elif raw: # no encoding configuration for raw messages if not isinstance(body, basestring): body = body.read() if isinstance(body, unicodeT): text = body.encode('utf-8') elif not encoding == 'utf-8': text = body.decode(encoding).encode('utf-8') else: text = body # No charset passed to avoid transport encoding # NOTE: some unicode encoded strings will produce # unreadable mail contents. payload_in = MIMEText(text) if to: if not isinstance(to, (list, tuple)): to = [to] else: raise Exception('Target receiver address not specified') if cc: if not isinstance(cc, (list, tuple)): cc = [cc] if bcc: if not isinstance(bcc, (list, tuple)): bcc = [bcc] if body is None: text = html = None elif isinstance(body, (list, tuple)): text, html = body elif body.strip().startswith('<html') and \ body.strip().endswith('</html>'): text = self.settings.server == 'gae' and body or None html = body else: text = body html = None if (text is not None or html is not None) and (not raw): if text is not None: if not isinstance(text, basestring): text = text.read() if isinstance(text, unicodeT): text = text.encode('utf-8') elif not encoding == 'utf-8': text = text.decode(encoding).encode('utf-8') if html is not None: if not isinstance(html, basestring): html = html.read() if isinstance(html, unicodeT): html = html.encode('utf-8') elif not encoding == 'utf-8': html = html.decode(encoding).encode('utf-8') # Construct mime part only if needed if text is not None and html: # We have text and html we need multipart/alternative attachment = MIMEMultipart('alternative') attachment.attach(MIMEText(text, _charset='utf-8')) attachment.attach(MIMEText(html, 'html', _charset='utf-8')) elif text is not None: attachment = MIMEText(text, _charset='utf-8') elif html: attachment = MIMEText(html, 'html', _charset='utf-8') if attachments: # If there is attachments put text and html into # multipart/mixed payload_in.attach(attachment) else: # No attachments no multipart/mixed payload_in = attachment if (attachments is None) or raw: pass elif isinstance(attachments, (list, tuple)): for attachment in attachments: payload_in.attach(attachment) else: payload_in.attach(attachments) attachments = [attachments] ####################################################### # CIPHER # ####################################################### cipher_type = cipher_type or self.settings.cipher_type sign = sign if sign is not None else self.settings.sign sign_passphrase = sign_passphrase or self.settings.sign_passphrase encrypt = encrypt if encrypt is not None else self.settings.encrypt ####################################################### # GPGME # ####################################################### if cipher_type == 'gpg': if self.settings.gpg_home: # Set GNUPGHOME environment variable to set home of gnupg import os os.environ['GNUPGHOME'] = self.settings.gpg_home if not sign and not encrypt: raise RuntimeError("No sign and no encrypt is set but cipher type to gpg") if not pyme: raise RuntimeError('pyme not installed') ############################################ # sign # ############################################ if sign: import string core.check_version(None) pin = payload_in.as_string().replace('\n', '\r\n') plain = core.Data(pin) sig = core.Data() c = core.Context() c.set_armor(1) c.signers_clear() # search for signing key for From: for sigkey in c.op_keylist_all(sender, 1): if sigkey.can_sign: c.signers_add(sigkey) if not c.signers_enum(0): raise RuntimeError('No key for signing [%s]' % sender) c.set_passphrase_cb(lambda x, y, z: sign_passphrase) try: # make a signature c.op_sign(plain, sig, pyme_mode.DETACH) sig.seek(0, 0) # make it part of the email payload = MIMEMultipart('signed', boundary=None, _subparts=None, **dict(micalg="pgp-sha1", protocol="application/pgp-signature")) # insert the origin payload payload.attach(payload_in) # insert the detached signature p = MIMEBase("application", 'pgp-signature') p.set_payload(sig.read()) payload.attach(p) # it's just a trick to handle the no encryption case payload_in = payload except errors.GPGMEError as ex: raise RuntimeError("GPG error: %s" % ex.getstring()) ############################################ # encrypt # ############################################ if encrypt: core.check_version(None) plain = core.Data(payload_in.as_string()) cipher = core.Data() c = core.Context() c.set_armor(1) # collect the public keys for encryption recipients = [] rec = to[:] if cc: rec.extend(cc) if bcc: rec.extend(bcc) for addr in rec: c.op_keylist_start(addr, 0) r = c.op_keylist_next() if r is None: raise RuntimeError('No key for [%s]' % addr) recipients.append(r) try: # make the encryption c.op_encrypt(recipients, 1, plain, cipher) cipher.seek(0, 0) # make it a part of the email payload = MIMEMultipart('encrypted', boundary=None, _subparts=None, **dict(protocol="application/pgp-encrypted")) p = MIMEBase("application", 'pgp-encrypted') p.set_payload("Version: 1\r\n") payload.attach(p) p = MIMEBase("application", 'octet-stream') p.set_payload(cipher.read()) payload.attach(p) except errors.GPGMEError as ex: raise RuntimeError("GPG error: %s" % ex.getstring()) ####################################################### # X.509 # ####################################################### elif cipher_type == 'x509': if not sign and not encrypt: raise RuntimeError("No sign and no encrypt is set but cipher type to x509") import os x509_sign_keyfile = x509_sign_keyfile or self.settings.x509_sign_keyfile x509_sign_chainfile = x509_sign_chainfile or self.settings.x509_sign_chainfile x509_sign_certfile = x509_sign_certfile or self.settings.x509_sign_certfile or \ x509_sign_keyfile or self.settings.x509_sign_certfile # crypt certfiles could be a string or a list x509_crypt_certfiles = x509_crypt_certfiles or self.settings.x509_crypt_certfiles x509_nocerts = x509_nocerts or self.settings.x509_nocerts # need m2crypto if not M2Crypto: raise RuntimeError("Can't load M2Crypto module") BIO, SMIME, X509 = M2Crypto.BIO, M2Crypto.SMIME, M2Crypto.X509 msg_bio = BIO.MemoryBuffer(payload_in.as_string()) s = SMIME.SMIME() # SIGN if sign: # key for signing try: keyfile_bio = BIO.openfile(x509_sign_keyfile)\ if os.path.isfile(x509_sign_keyfile)\ else BIO.MemoryBuffer(x509_sign_keyfile) sign_certfile_bio = BIO.openfile(x509_sign_certfile)\ if os.path.isfile(x509_sign_certfile)\ else BIO.MemoryBuffer(x509_sign_certfile) s.load_key_bio(keyfile_bio, sign_certfile_bio, callback=lambda x: sign_passphrase) if x509_sign_chainfile: sk = X509.X509_Stack() chain = X509.load_cert(x509_sign_chainfile)\ if os.path.isfile(x509_sign_chainfile)\ else X509.load_cert_string(x509_sign_chainfile) sk.push(chain) s.set_x509_stack(sk) except Exception as e: raise RuntimeError("Something went wrong on certificate / private key loading: <%s>" % str(e)) try: if x509_nocerts: flags = SMIME.PKCS7_NOCERTS else: flags = 0 if not encrypt: flags += SMIME.PKCS7_DETACHED p7 = s.sign(msg_bio, flags=flags) msg_bio = BIO.MemoryBuffer(payload_in.as_string( )) # Recreate coz sign() has consumed it. except Exception as e: raise RuntimeError("Something went wrong on signing: <%s> %s" % ( str(e), str(flags))) # ENCRYPT if encrypt: try: sk = X509.X509_Stack() if not isinstance(x509_crypt_certfiles, (list, tuple)): x509_crypt_certfiles = [x509_crypt_certfiles] # make an encryption cert's stack for crypt_certfile in x509_crypt_certfiles: certfile = X509.load_cert(crypt_certfile)\ if os.path.isfile(crypt_certfile)\ else X509.load_cert_string(crypt_certfile) sk.push(certfile) s.set_x509_stack(sk) s.set_cipher(SMIME.Cipher('des_ede3_cbc')) tmp_bio = BIO.MemoryBuffer() if sign: s.write(tmp_bio, p7) else: tmp_bio.write(payload_in.as_string()) p7 = s.encrypt(tmp_bio) except Exception as e: raise RuntimeError("Something went wrong on encrypting: <%s>" % str(e)) # Final stage in sign and encryption out = BIO.MemoryBuffer() if encrypt: s.write(out, p7) else: if sign: s.write(out, p7, msg_bio, SMIME.PKCS7_DETACHED) else: out.write('\r\n') out.write(payload_in.as_string()) out.close() st = str(out.read()) payload = message_from_string(st) else: # no cryptography process as usual payload = payload_in if from_address: payload['From'] = encoded_or_raw(to_unicode(from_address, encoding)) else: payload['From'] = encoded_or_raw(to_unicode(sender, encoding)) origTo = to[:] if to: payload['To'] = encoded_or_raw(to_unicode(', '.join(to), encoding)) if reply_to: payload['Reply-To'] = encoded_or_raw(to_unicode(reply_to, encoding)) if cc: payload['Cc'] = encoded_or_raw(to_unicode(', '.join(cc), encoding)) to.extend(cc) if bcc: to.extend(bcc) payload['Subject'] = encoded_or_raw(to_unicode(subject, encoding)) payload['Date'] = email.utils.formatdate() for k, v in iteritems(headers): payload[k] = encoded_or_raw(to_unicode(v, encoding)) result = {} try: if self.settings.server == 'logging': entry = 'email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \ ('-' * 40, sender, ', '.join(to), subject, text or html, '-' * 40) self.settings.logger.warning(entry) elif self.settings.server.startswith('logging:'): entry = 'email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \ ('-' * 40, sender, ', '.join(to), subject, text or html, '-' * 40) open(self.settings.server[8:], 'a').write(entry) elif self.settings.server == 'gae': if not GAE: raise RuntimeError('Not running on GAE') xcc = dict() if cc: xcc['cc'] = cc if bcc: xcc['bcc'] = bcc if reply_to: xcc['reply_to'] = reply_to attachments = attachments and [google_mail.Attachment( a.my_filename, a.my_payload, content_id='<attachment-%s>' % k ) for k, a in enumerate(attachments) if not raw] if attachments: result = google_mail.send_mail( sender=sender, to=origTo, subject=to_unicode(subject, encoding), body=to_unicode(text or '', encoding), html=html, attachments=attachments, **xcc) elif html and (not raw): result = google_mail.send_mail( sender=sender, to=origTo, subject=to_unicode(subject, encoding), body=to_unicode(text or '', encoding), html=html, **xcc) else: result = google_mail.send_mail( sender=sender, to=origTo, subject=to_unicode(subject, encoding), body=to_unicode(text or '', encoding), **xcc) elif self.settings.server == 'aws': import boto3 from botocore.exceptions import ClientError client = boto3.client('ses') try: raw = {'Data': payload.as_string()} response = client.send_raw_email(RawMessage=raw, Source=sender, Destinations=to) return True except ClientError as e: raise RuntimeError() else: smtp_args = self.settings.server.split(':') kwargs = dict(timeout=self.settings.timeout) func = smtplib.SMTP_SSL if self.settings.ssl else smtplib.SMTP server = func(*smtp_args, **kwargs) try: if self.settings.tls and not self.settings.ssl: server.ehlo(self.settings.hostname) server.starttls() server.ehlo(self.settings.hostname) if self.settings.login: server.login(*self.settings.login.split(':', 1)) result = server.sendmail(sender, to, payload.as_string()) finally: server.quit() except Exception as e: self.settings.logger.warning('Mailer.send failure:%s' % e) self.result = result raise self.result = result self.error = None return True
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Sample of unattended signing/verifying of a message. # It uses keys for [email protected] generated by genkey.pl script import sys from pyme import core, callbacks from pyme.constants.sig import mode core.check_version(None) plain = core.Data("Test message") sig = core.Data() c = core.Context() user = "******" c.signers_clear() # Add [email protected]'s keys in the list of signers for sigkey in c.op_keylist_all(user, 1): if sigkey.can_sign: c.signers_add(sigkey) if not c.signers_enum(0): print "No secret %s's keys suitable for signing!" % user sys.exit(0)
# You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import gtk, gobject, gtk.glade import time, sys, os from pyme import callbacks, core, errors from pyme.core import Data, Context, pubkey_algo_name from pyme import constants from pyme.constants import validity from pyme.constants.keylist import mode # Thanks to Bernhard Reiter for pointing out the following: # gpgme_check_version() necessary for initialisation according to # gpgme 1.1.6 and this is not done automatically in pyme-0.7.0 print "gpgme version:", core.check_version(None) # Convert trust constant into a string trusts = { validity.UNKNOWN: "", validity.UNDEFINED: "Undefined", validity.NEVER: "Never", validity.MARGINAL: "Marginal", validity.FULL: "Full", validity.ULTIMATE: "Ultimate" } # Convert seconds into a date def sec2str(secs): if secs > 0: return time.strftime("%Y-%m-%d", time.gmtime(secs))
import sys from pyme import core, constants if not core.check_version('1.3.0'): logger.error('this program is designed to run against GPGME version 1.3.0') exit() # Set up our input and output buffers. plain = core.Data('This is my message.') plain = core.Data(file='/Users/jdw5/Lockbox/foobar/firsttest.txt') outf = open('/Users/jdw5/out.t','wb') cipher = core.Data(file=outf) # Initialize our context. c = core.Context() c.set_armor(1) # Set up the recipients. #sys.stdout.write("Enter name of your recipient: ") #name = sys.stdin.readline().strip() name = '*****@*****.**' c.op_keylist_start(name, 0) r = c.op_keylist_next() # Do the encryption. c.op_encrypt_sign([r], 1, plain, cipher)
def send(self, to, subject='None', message='None', attachments=None, cc=None, bcc=None, reply_to=None, encoding='utf-8', raw=False, headers={}): """ Sends an email using data specified in constructor Arguments: to: list or tuple of receiver addresses; will also accept single object subject: subject of the email message: email body text; depends on type of passed object: if 2-list or 2-tuple is passed: first element will be source of plain text while second of html text; otherwise: object will be the only source of plain text and html source will be set to None; If text or html source is: None: content part will be ignored, string: content part will be set to it, file-like object: content part will be fetched from it using it's read() method attachments: list or tuple of Mail.Attachment objects; will also accept single object cc: list or tuple of carbon copy receiver addresses; will also accept single object bcc: list or tuple of blind carbon copy receiver addresses; will also accept single object reply_to: address to which reply should be composed encoding: encoding of all strings passed to this method (including message bodies) headers: dictionary of headers to refine the headers just before sending mail, e.g. {'Return-Path' : '*****@*****.**'} Examples: #Send plain text message to single address: mail.send('*****@*****.**', 'Message subject', 'Plain text body of the message') #Send html message to single address: mail.send('*****@*****.**', 'Message subject', '<html>Plain text body of the message</html>') #Send text and html message to three addresses (two in cc): mail.send('*****@*****.**', 'Message subject', ('Plain text body', '<html>html body</html>'), cc=['*****@*****.**', '*****@*****.**']) #Send html only message with image attachment available from the message by 'photo' content id: mail.send('*****@*****.**', 'Message subject', (None, '<html><img src="cid:photo" /></html>'), Mail.Attachment('/path/to/photo.jpg' content_id='photo')) #Send email with two attachments and no body text mail.send('[email protected], 'Message subject', None, [Mail.Attachment('/path/to/fist.file'), Mail.Attachment('/path/to/second.file')]) Returns True on success, False on failure. Before return, method updates two object's fields: self.result: return value of smtplib.SMTP.sendmail() or GAE's mail.send_mail() method self.error: Exception message or None if above was successful """ def encode_header(key): if [c for c in key if 32 > ord(c) or ord(c) > 127]: return Header.Header(key.encode('utf-8'), 'utf-8') else: return key # encoded or raw text def encoded_or_raw(text): if raw: text = encode_header(text) return text if not isinstance(self.server, str): raise Exception('Server address not specified') if not isinstance(self.sender, str): raise Exception('Sender address not specified') if not raw: payload_in = MIMEMultipart.MIMEMultipart('mixed') else: # no encoding configuration for raw messages if isinstance(message, basestring): text = message.decode(encoding).encode('utf-8') else: text = message.read().decode(encoding).encode('utf-8') # No charset passed to avoid transport encoding # NOTE: some unicode encoded strings will produce # unreadable mail contents. payload_in = MIMEText.MIMEText(text) if to: if not isinstance(to, (list, tuple)): to = [to] else: raise Exception('Target receiver address not specified') if cc: if not isinstance(cc, (list, tuple)): cc = [cc] if bcc: if not isinstance(bcc, (list, tuple)): bcc = [bcc] if message is None: text = html = None elif isinstance(message, (list, tuple)): text, html = message elif message.strip().startswith('<html') and message.strip().endswith( '</html>'): text = self.server == 'gae' and message or None html = message else: text = message html = None if (not text is None or not html is None) and (not raw): attachment = MIMEMultipart.MIMEMultipart('alternative') if not text is None: if isinstance(text, basestring): text = text.decode(encoding).encode('utf-8') else: text = text.read().decode(encoding).encode('utf-8') attachment.attach(MIMEText.MIMEText(text, _charset='utf-8')) if not html is None: if isinstance(html, basestring): html = html.decode(encoding).encode('utf-8') else: html = html.read().decode(encoding).encode('utf-8') attachment.attach( MIMEText.MIMEText(html, 'html', _charset='utf-8')) payload_in.attach(attachment) if (attachments is None) or raw: pass elif isinstance(attachments, (list, tuple)): for attachment in attachments: payload_in.attach(attachment) else: payload_in.attach(attachments) ####################################################### # CIPHER # ####################################################### cipher_type = self.cipher_type sign = self.sign sign_passphrase = self.sign_passphrase encrypt = self.encrypt ####################################################### # GPGME # ####################################################### if cipher_type == 'gpg': if self.gpg_home: # Set GNUPGHOME environment variable to set home of gnupg import os os.environ['GNUPGHOME'] = self.gpg_home if not sign and not encrypt: self.error = "No sign and no encrypt is set but cipher type to gpg" return False # need a python-pyme package and gpgme lib from pyme import core, errors from pyme.constants.sig import mode ############################################ # sign # ############################################ if sign: import string core.check_version(None) pin = string.replace(payload_in.as_string(), '\n', '\r\n') plain = core.Data(pin) sig = core.Data() c = core.Context() c.set_armor(1) c.signers_clear() # search for signing key for From: for sigkey in c.op_keylist_all(self.sender, 1): if sigkey.can_sign: c.signers_add(sigkey) if not c.signers_enum(0): self.error = 'No key for signing [%s]' % self.sender return False c.set_passphrase_cb(lambda x, y, z: sign_passphrase) try: # make a signature c.op_sign(plain, sig, mode.DETACH) sig.seek(0, 0) # make it part of the email payload = MIMEMultipart.MIMEMultipart( 'signed', boundary=None, _subparts=None, **dict(micalg="pgp-sha1", protocol="application/pgp-signature")) # insert the origin payload payload.attach(payload_in) # insert the detached signature p = MIMEBase.MIMEBase("application", 'pgp-signature') p.set_payload(sig.read()) payload.attach(p) # it's just a trick to handle the no encryption case payload_in = payload except errors.GPGMEError: self.error = "GPG error: %s" return False ############################################ # encrypt # ############################################ if encrypt: core.check_version(None) plain = core.Data(payload_in.as_string()) cipher = core.Data() c = core.Context() c.set_armor(1) # collect the public keys for encryption recipients = [] rec = to[:] if cc: rec.extend(cc) if bcc: rec.extend(bcc) for addr in rec: c.op_keylist_start(addr, 0) r = c.op_keylist_next() if r is None: self.error = 'No key for [%s]' % addr return False recipients.append(r) try: # make the encryption c.op_encrypt(recipients, 1, plain, cipher) cipher.seek(0, 0) # make it a part of the email payload = MIMEMultipart.MIMEMultipart( 'encrypted', boundary=None, _subparts=None, **dict(protocol="application/pgp-encrypted")) p = MIMEBase.MIMEBase("application", 'pgp-encrypted') p.set_payload("Version: 1\r\n") payload.attach(p) p = MIMEBase.MIMEBase("application", 'octet-stream') p.set_payload(cipher.read()) payload.attach(p) except errors.GPGMEError: self.error = "GPG error: %s" return False ####################################################### # X.509 # ####################################################### elif cipher_type == 'x509': if not sign and not encrypt: self.error = "No sign and no encrypt is set but cipher type to x509" return False x509_sign_keyfile = self.x509_sign_keyfile if self.x509_sign_certfile: x509_sign_certfile = self.x509_sign_certfile else: # if there is no sign certfile we'll assume the # cert is in keyfile x509_sign_certfile = self.x509_sign_keyfile # crypt certfiles could be a string or a list x509_crypt_certfiles = self.x509_crypt_certfiles x509_nocerts = self.x509_nocerts # need m2crypto try: from M2Crypto import BIO, SMIME, X509 except Exception: self.error = "Can't load M2Crypto module" return False msg_bio = BIO.MemoryBuffer(payload_in.as_string()) s = SMIME.SMIME() # SIGN if sign: #key for signing try: s.load_key(x509_sign_keyfile, x509_sign_certfile, callback=lambda x: sign_passphrase) except Exception: self.error = "Something went wrong on certificate / private key loading: <%s>" % str( e) return False try: if x509_nocerts: flags = SMIME.PKCS7_NOCERTS else: flags = 0 if not encrypt: flags += SMIME.PKCS7_DETACHED p7 = s.sign(msg_bio, flags=flags) msg_bio = BIO.MemoryBuffer(payload_in.as_string( )) # Recreate coz sign() has consumed it. except Exception: self.error = "Something went wrong on signing: <%s> %s" return False # ENCRYPT if encrypt: try: sk = X509.X509_Stack() if not isinstance(x509_crypt_certfiles, (list, tuple)): x509_crypt_certfiles = [x509_crypt_certfiles] # make an encryption cert's stack for x in x509_crypt_certfiles: sk.push(X509.load_cert(x)) s.set_x509_stack(sk) s.set_cipher(SMIME.Cipher('des_ede3_cbc')) tmp_bio = BIO.MemoryBuffer() if sign: s.write(tmp_bio, p7) else: tmp_bio.write(payload_in.as_string()) p7 = s.encrypt(tmp_bio) except Exception: self.error = "Something went wrong on encrypting: <%s>" return False # Final stage in sign and encryption out = BIO.MemoryBuffer() if encrypt: s.write(out, p7) else: if sign: s.write(out, p7, msg_bio, SMIME.PKCS7_DETACHED) else: out.write('\r\n') out.write(payload_in.as_string()) out.close() st = str(out.read()) payload = message_from_string(st) else: # no cryptography process as usual payload = payload_in payload['From'] = encoded_or_raw(self.sender.decode(encoding)) origTo = to[:] if to: payload['To'] = encoded_or_raw(', '.join(to).decode(encoding)) if reply_to: payload['Reply-To'] = encoded_or_raw(reply_to.decode(encoding)) if cc: payload['Cc'] = encoded_or_raw(', '.join(cc).decode(encoding)) to.extend(cc) if bcc: to.extend(bcc) payload['Subject'] = encoded_or_raw(subject.decode(encoding)) payload['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) for k, v in headers.iteritems(): payload[k] = encoded_or_raw(v.decode(encoding)) result = {} try: if self.server == 'logging': logger.warn('email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \ ('-'*40,self.sender, ', '.join(to),subject, text or html,'-'*40)) elif self.server == 'gae': xcc = dict() if cc: xcc['cc'] = cc if bcc: xcc['bcc'] = bcc if reply_to: xcc['reply_to'] = reply_to from google.appengine.api import mail attachments = attachments and [(a.my_filename, a.my_payload) for a in attachments if not raw] if attachments: result = mail.send_mail(sender=self.sender, to=origTo, subject=subject, body=text, html=html, attachments=attachments, **xcc) elif html and (not raw): result = mail.send_mail(sender=self.sender, to=origTo, subject=subject, body=text, html=html, **xcc) else: result = mail.send_mail(sender=self.sender, to=origTo, subject=subject, body=text, **xcc) else: smtp_args = self.server.split(':') if self.ssl: server = smtplib.SMTP_SSL(*smtp_args) else: server = smtplib.SMTP(*smtp_args) if self.tls and not self.ssl: server.ehlo() server.starttls() server.ehlo() if self.login: server.login(*self.login.split(':', 1)) result = server.sendmail(self.sender, to, payload.as_string()) server.quit() except Exception: self.result = result self.error = None return False self.result = result self.error = None return True
def send(self, to, subject='[no subject]', message='[no message]', attachments=None, cc=None, bcc=None, reply_to=None, sender=None, encoding='utf-8', raw=False, headers={}, from_address=None, cipher_type=None, sign=None, sign_passphrase=None, encrypt=None, x509_sign_keyfile=None, x509_sign_chainfile=None, x509_sign_certfile=None, x509_crypt_certfiles=None, x509_nocerts=None ): """ Sends an email using data specified in constructor Args: to: list or tuple of receiver addresses; will also accept single object subject: subject of the email message: email body text; depends on type of passed object: - if 2-list or 2-tuple is passed: first element will be source of plain text while second of html text; - otherwise: object will be the only source of plain text and html source will be set to None If text or html source is: - None: content part will be ignored, - string: content part will be set to it, - file-like object: content part will be fetched from it using it's read() method attachments: list or tuple of Mail.Attachment objects; will also accept single object cc: list or tuple of carbon copy receiver addresses; will also accept single object bcc: list or tuple of blind carbon copy receiver addresses; will also accept single object reply_to: address to which reply should be composed encoding: encoding of all strings passed to this method (including message bodies) headers: dictionary of headers to refine the headers just before sending mail, e.g. `{'X-Mailer' : 'web2py mailer'}` from_address: address to appear in the 'From:' header, this is not the envelope sender. If not specified the sender will be used cipher_type : gpg - need a python-pyme package and gpgme lib x509 - smime gpg_home : you can set a GNUPGHOME environment variable to specify home of gnupg sign : sign the message (True or False) sign_passphrase : passphrase for key signing encrypt : encrypt the message (True or False). It defaults to True. ... x509 only ... x509_sign_keyfile : the signers private key filename or string containing the key. (PEM format) x509_sign_certfile: the signers certificate filename or string containing the cert. (PEM format) x509_sign_chainfile: sets the optional all-in-one file where you can assemble the certificates of Certification Authorities (CA) which form the certificate chain of email certificate. It can be a string containing the certs to. (PEM format) x509_nocerts : if True then no attached certificate in mail x509_crypt_certfiles: the certificates file or strings to encrypt the messages with can be a file name / string or a list of file names / strings (PEM format) Examples: Send plain text message to single address:: mail.send('*****@*****.**', 'Message subject', 'Plain text body of the message') Send html message to single address:: mail.send('*****@*****.**', 'Message subject', '<html>Plain text body of the message</html>') Send text and html message to three addresses (two in cc):: mail.send('*****@*****.**', 'Message subject', ('Plain text body', '<html>html body</html>'), cc=['*****@*****.**', '*****@*****.**']) Send html only message with image attachment available from the message by 'photo' content id:: mail.send('*****@*****.**', 'Message subject', (None, '<html><img src="cid:photo" /></html>'), Mail.Attachment('/path/to/photo.jpg' content_id='photo')) Send email with two attachments and no body text:: mail.send('[email protected], 'Message subject', None, [Mail.Attachment('/path/to/fist.file'), Mail.Attachment('/path/to/second.file')]) Returns: True on success, False on failure. Before return, method updates two object's fields: - self.result: return value of smtplib.SMTP.sendmail() or GAE's mail.send_mail() method - self.error: Exception message or None if above was successful """ # We don't want to use base64 encoding for unicode mail Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') def encode_header(key): if [c for c in key if 32 > ord(c) or ord(c) > 127]: return Header.Header(key.encode('utf-8'), 'utf-8') else: return key # encoded or raw text def encoded_or_raw(text): if raw: text = encode_header(text) return text sender = sender or self.settings.sender if not isinstance(self.settings.server, str): raise Exception('Server address not specified') if not isinstance(sender, str): raise Exception('Sender address not specified') if not raw and attachments: # Use multipart/mixed if there is attachments payload_in = MIMEMultipart.MIMEMultipart('mixed') elif raw: # no encoding configuration for raw messages if not isinstance(message, basestring): message = message.read() if isinstance(message, unicode): text = message.encode('utf-8') elif not encoding == 'utf-8': text = message.decode(encoding).encode('utf-8') else: text = message # No charset passed to avoid transport encoding # NOTE: some unicode encoded strings will produce # unreadable mail contents. payload_in = MIMEText.MIMEText(text) if to: if not isinstance(to, (list, tuple)): to = [to] else: raise Exception('Target receiver address not specified') if cc: if not isinstance(cc, (list, tuple)): cc = [cc] if bcc: if not isinstance(bcc, (list, tuple)): bcc = [bcc] if message is None: text = html = None elif isinstance(message, (list, tuple)): text, html = message elif message.strip().startswith('<html') and \ message.strip().endswith('</html>'): text = self.settings.server == 'gae' and message or None html = message else: text = message html = None if (text is not None or html is not None) and (not raw): if text is not None: if not isinstance(text, basestring): text = text.read() if isinstance(text, unicode): text = text.encode('utf-8') elif not encoding == 'utf-8': text = text.decode(encoding).encode('utf-8') if html is not None: if not isinstance(html, basestring): html = html.read() if isinstance(html, unicode): html = html.encode('utf-8') elif not encoding == 'utf-8': html = html.decode(encoding).encode('utf-8') # Construct mime part only if needed if text is not None and html: # We have text and html we need multipart/alternative attachment = MIMEMultipart.MIMEMultipart('alternative') attachment.attach(MIMEText.MIMEText(text, _charset='utf-8')) attachment.attach( MIMEText.MIMEText(html, 'html', _charset='utf-8')) elif text is not None: attachment = MIMEText.MIMEText(text, _charset='utf-8') elif html: attachment = \ MIMEText.MIMEText(html, 'html', _charset='utf-8') if attachments: # If there is attachments put text and html into # multipart/mixed payload_in.attach(attachment) else: # No attachments no multipart/mixed payload_in = attachment if (attachments is None) or raw: pass elif isinstance(attachments, (list, tuple)): for attachment in attachments: payload_in.attach(attachment) else: payload_in.attach(attachments) attachments = [attachments] ####################################################### # CIPHER # ####################################################### cipher_type = cipher_type or self.settings.cipher_type sign = sign if sign is not None else self.settings.sign sign_passphrase = sign_passphrase or self.settings.sign_passphrase encrypt = encrypt if encrypt is not None else self.settings.encrypt ####################################################### # GPGME # ####################################################### if cipher_type == 'gpg': if self.settings.gpg_home: # Set GNUPGHOME environment variable to set home of gnupg os.environ['GNUPGHOME'] = self.settings.gpg_home if not sign and not encrypt: self.error = "No sign and no encrypt is set but cipher type to gpg" return False # need a python-pyme package and gpgme lib from pyme import core, errors from pyme.constants.sig import mode ############################################ # sign # ############################################ if sign: core.check_version(None) pin = string.replace(payload_in.as_string(), '\n', '\r\n') plain = core.Data(pin) sig = core.Data() c = core.Context() c.set_armor(1) c.signers_clear() # search for signing key for From: for sigkey in c.op_keylist_all(sender, 1): if sigkey.can_sign: c.signers_add(sigkey) if not c.signers_enum(0): self.error = 'No key for signing [%s]' % sender return False c.set_passphrase_cb(lambda x, y, z: sign_passphrase) try: # make a signature c.op_sign(plain, sig, mode.DETACH) sig.seek(0, 0) # make it part of the email payload = \ MIMEMultipart.MIMEMultipart('signed', boundary=None, _subparts=None, **dict(micalg="pgp-sha1", protocol="application/pgp-signature")) # insert the origin payload payload.attach(payload_in) # insert the detached signature p = MIMEBase.MIMEBase("application", 'pgp-signature') p.set_payload(sig.read()) payload.attach(p) # it's just a trick to handle the no encryption case payload_in = payload except errors.GPGMEError, ex: self.error = "GPG error: %s" % ex.getstring() return False ############################################ # encrypt # ############################################ if encrypt: core.check_version(None) plain = core.Data(payload_in.as_string()) cipher = core.Data() c = core.Context() c.set_armor(1) # collect the public keys for encryption recipients = [] rec = to[:] if cc: rec.extend(cc) if bcc: rec.extend(bcc) for addr in rec: c.op_keylist_start(addr, 0) r = c.op_keylist_next() if r is None: self.error = 'No key for [%s]' % addr return False recipients.append(r) try: # make the encryption c.op_encrypt(recipients, 1, plain, cipher) cipher.seek(0, 0) # make it a part of the email payload = MIMEMultipart.MIMEMultipart('encrypted', boundary=None, _subparts=None, **dict(protocol="application/pgp-encrypted")) p = MIMEBase.MIMEBase("application", 'pgp-encrypted') p.set_payload("Version: 1\r\n") payload.attach(p) p = MIMEBase.MIMEBase("application", 'octet-stream') p.set_payload(cipher.read()) payload.attach(p) except errors.GPGMEError, ex: self.error = "GPG error: %s" % ex.getstring() return False
# You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import gtk, gobject, gtk.glade import time, sys, os from pyme import callbacks, core, errors from pyme.core import Data, Context, pubkey_algo_name from pyme import constants from pyme.constants import validity from pyme.constants.keylist import mode # Thanks to Bernhard Reiter for pointing out the following: # gpgme_check_version() necessary for initialisation according to # gpgme 1.1.6 and this is not done automatically in pyme-0.7.0 print "gpgme version:", core.check_version(None) # Convert trust constant into a string trusts = {validity.UNKNOWN: "", validity.UNDEFINED: "Undefined", validity.NEVER: "Never", validity.MARGINAL: "Marginal", validity.FULL: "Full", validity.ULTIMATE: "Ultimate"} # Convert seconds into a date def sec2str(secs): if secs > 0: return time.strftime("%Y-%m-%d", time.gmtime(secs)) elif secs == 0: return "Unlimited" else: return ""
# (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import os from pyme import core from pyme.core import Data, Context core.check_version(None) class KeyEditor: def __init__(self): self.steps = ["fpr", "expire", "1", "primary", "quit"] self.step = 0 def edit_fnc(self, status, args, out): print "[-- Response --]" out.seek(0,0) print out.read(), print "[-- Code: %d, %s --]" % (status, args) if args == "keyedit.prompt": result = self.steps[self.step] self.step += 1
def send( self, to, subject='[no subject]', message='[no message]', attachments=None, cc=None, bcc=None, reply_to=None, sender=None, encoding='utf-8', raw=False, headers={} ): """ Sends an email using data specified in constructor Arguments: to: list or tuple of receiver addresses; will also accept single object subject: subject of the email message: email body text; depends on type of passed object: if 2-list or 2-tuple is passed: first element will be source of plain text while second of html text; otherwise: object will be the only source of plain text and html source will be set to None; If text or html source is: None: content part will be ignored, string: content part will be set to it, file-like object: content part will be fetched from it using it's read() method attachments: list or tuple of Mail.Attachment objects; will also accept single object cc: list or tuple of carbon copy receiver addresses; will also accept single object bcc: list or tuple of blind carbon copy receiver addresses; will also accept single object reply_to: address to which reply should be composed encoding: encoding of all strings passed to this method (including message bodies) headers: dictionary of headers to refine the headers just before sending mail, e.g. {'Return-Path' : '*****@*****.**'} Examples: #Send plain text message to single address: mail.send('*****@*****.**', 'Message subject', 'Plain text body of the message') #Send html message to single address: mail.send('*****@*****.**', 'Message subject', '<html>Plain text body of the message</html>') #Send text and html message to three addresses (two in cc): mail.send('*****@*****.**', 'Message subject', ('Plain text body', '<html>html body</html>'), cc=['*****@*****.**', '*****@*****.**']) #Send html only message with image attachment available from the message by 'photo' content id: mail.send('*****@*****.**', 'Message subject', (None, '<html><img src="cid:photo" /></html>'), Mail.Attachment('/path/to/photo.jpg' content_id='photo')) #Send email with two attachments and no body text mail.send('[email protected], 'Message subject', None, [Mail.Attachment('/path/to/fist.file'), Mail.Attachment('/path/to/second.file')]) Returns True on success, False on failure. Before return, method updates two object's fields: self.result: return value of smtplib.SMTP.sendmail() or GAE's mail.send_mail() method self.error: Exception message or None if above was successful """ # We don't want to use base64 encoding for unicode mail Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') def encode_header(key): if [c for c in key if 32 > ord(c) or ord(c) > 127]: return Header.Header(key.encode('utf-8'), 'utf-8') else: return key # encoded or raw text def encoded_or_raw(text): if raw: text = encode_header(text) return text sender = sender or self.settings.sender if not isinstance(self.settings.server, str): raise Exception('Server address not specified') if not isinstance(sender, str): raise Exception('Sender address not specified') if not raw and attachments: # Use multipart/mixed if there is attachments payload_in = MIMEMultipart.MIMEMultipart('mixed') elif raw: # no encoding configuration for raw messages if not isinstance(message, basestring): message = message.read() if isinstance(message, unicode): text = message.encode('utf-8') elif not encoding == 'utf-8': text = message.decode(encoding).encode('utf-8') else: text = message # No charset passed to avoid transport encoding # NOTE: some unicode encoded strings will produce # unreadable mail contents. payload_in = MIMEText.MIMEText(text) if to: if not isinstance(to, (list, tuple)): to = [to] else: raise Exception('Target receiver address not specified') if cc: if not isinstance(cc, (list, tuple)): cc = [cc] if bcc: if not isinstance(bcc, (list, tuple)): bcc = [bcc] if message is None: text = html = None elif isinstance(message, (list, tuple)): text, html = message elif message.strip().startswith('<html') and \ message.strip().endswith('</html>'): text = self.settings.server == 'gae' and message or None html = message else: text = message html = None if (text is not None or html is not None) and (not raw): if text is not None: if not isinstance(text, basestring): text = text.read() if isinstance(text, unicode): text = text.encode('utf-8') elif not encoding == 'utf-8': text = text.decode(encoding).encode('utf-8') if html is not None: if not isinstance(html, basestring): html = html.read() if isinstance(html, unicode): html = html.encode('utf-8') elif not encoding == 'utf-8': html = html.decode(encoding).encode('utf-8') # Construct mime part only if needed if text is not None and html: # We have text and html we need multipart/alternative attachment = MIMEMultipart.MIMEMultipart('alternative') attachment.attach(MIMEText.MIMEText(text, _charset='utf-8')) attachment.attach( MIMEText.MIMEText(html, 'html', _charset='utf-8')) elif text is not None: attachment = MIMEText.MIMEText(text, _charset='utf-8') elif html: attachment = \ MIMEText.MIMEText(html, 'html', _charset='utf-8') if attachments: # If there is attachments put text and html into # multipart/mixed payload_in.attach(attachment) else: # No attachments no multipart/mixed payload_in = attachment if (attachments is None) or raw: pass elif isinstance(attachments, (list, tuple)): for attachment in attachments: payload_in.attach(attachment) else: payload_in.attach(attachments) ####################################################### # CIPHER # ####################################################### cipher_type = self.settings.cipher_type sign = self.settings.sign sign_passphrase = self.settings.sign_passphrase encrypt = self.settings.encrypt ####################################################### # GPGME # ####################################################### if cipher_type == 'gpg': if self.settings.gpg_home: # Set GNUPGHOME environment variable to set home of gnupg import os os.environ['GNUPGHOME'] = self.settings.gpg_home if not sign and not encrypt: self.error = "No sign and no encrypt is set but cipher type to gpg" return False # need a python-pyme package and gpgme lib from pyme import core, errors from pyme.constants.sig import mode ############################################ # sign # ############################################ if sign: import string core.check_version(None) pin = string.replace(payload_in.as_string(), '\n', '\r\n') plain = core.Data(pin) sig = core.Data() c = core.Context() c.set_armor(1) c.signers_clear() # search for signing key for From: for sigkey in c.op_keylist_all(sender, 1): if sigkey.can_sign: c.signers_add(sigkey) if not c.signers_enum(0): self.error = 'No key for signing [%s]' % sender return False c.set_passphrase_cb(lambda x, y, z: sign_passphrase) try: # make a signature c.op_sign(plain, sig, mode.DETACH) sig.seek(0, 0) # make it part of the email payload = MIMEMultipart.MIMEMultipart('signed', boundary=None, _subparts=None, **dict( micalg="pgp-sha1", # NOQA protocol="application/pgp-signature")) # NOQA # insert the origin payload payload.attach(payload_in) # insert the detached signature p = MIMEBase("application", 'pgp-signature') p.set_payload(sig.read()) payload.attach(p) # it's just a trick to handle the no encryption case payload_in = payload except errors.GPGMEError as ex: self.error = "GPG error: %s" % ex.getstring() return False ############################################ # encrypt # ############################################ if encrypt: core.check_version(None) plain = core.Data(payload_in.as_string()) cipher = core.Data() c = core.Context() c.set_armor(1) # collect the public keys for encryption recipients = [] rec = to[:] if cc: rec.extend(cc) if bcc: rec.extend(bcc) for addr in rec: c.op_keylist_start(addr, 0) r = c.op_keylist_next() if r is None: self.error = 'No key for [%s]' % addr return False recipients.append(r) try: # make the encryption c.op_encrypt(recipients, 1, plain, cipher) cipher.seek(0, 0) # make it a part of the email payload = MIMEMultipart.MIMEMultipart('encrypted', boundary=None, _subparts=None, **dict(protocol="application/pgp-encrypted")) p = MIMEBase("application", 'pgp-encrypted') p.set_payload("Version: 1\r\n") payload.attach(p) p = MIMEBase("application", 'octet-stream') p.set_payload(cipher.read()) payload.attach(p) except errors.GPGMEError as ex: self.error = "GPG error: %s" % ex.getstring() return False ####################################################### # X.509 # ####################################################### elif cipher_type == 'x509': if not sign and not encrypt: self.error = "No sign and no encrypt is set but cipher type to x509" return False x509_sign_keyfile = self.settings.x509_sign_keyfile if self.settings.x509_sign_certfile: x509_sign_certfile = self.settings.x509_sign_certfile else: # if there is no sign certfile we'll assume the # cert is in keyfile x509_sign_certfile = self.settings.x509_sign_keyfile # crypt certfiles could be a string or a list x509_crypt_certfiles = self.settings.x509_crypt_certfiles x509_nocerts = self.settings.x509_nocerts # need m2crypto try: from M2Crypto import BIO, SMIME, X509 except Exception as e: self.error = "Can't load M2Crypto module" return False msg_bio = BIO.MemoryBuffer(payload_in.as_string()) s = SMIME.SMIME() # SIGN if sign: #key for signing try: s.load_key(x509_sign_keyfile, x509_sign_certfile, callback=lambda x: sign_passphrase) except Exception as e: self.error = "Something went wrong on certificate / private key loading: <%s>" % str(e) return False try: if x509_nocerts: flags = SMIME.PKCS7_NOCERTS else: flags = 0 if not encrypt: flags += SMIME.PKCS7_DETACHED p7 = s.sign(msg_bio, flags=flags) msg_bio = BIO.MemoryBuffer(payload_in.as_string( )) # Recreate coz sign() has consumed it. except Exception as e: self.error = "Something went wrong on signing: <%s> %s" % ( str(e), str(flags)) return False # ENCRYPT if encrypt: try: sk = X509.X509_Stack() if not isinstance(x509_crypt_certfiles, (list, tuple)): x509_crypt_certfiles = [x509_crypt_certfiles] # make an encryption cert's stack for x in x509_crypt_certfiles: sk.push(X509.load_cert(x)) s.set_x509_stack(sk) s.set_cipher(SMIME.Cipher('des_ede3_cbc')) tmp_bio = BIO.MemoryBuffer() if sign: s.write(tmp_bio, p7) else: tmp_bio.write(payload_in.as_string()) p7 = s.encrypt(tmp_bio) except Exception as e: self.error = "Something went wrong on encrypting: <%s>" % str(e) return False # Final stage in sign and encryption out = BIO.MemoryBuffer() if encrypt: s.write(out, p7) else: if sign: s.write(out, p7, msg_bio, SMIME.PKCS7_DETACHED) else: out.write('\r\n') out.write(payload_in.as_string()) out.close() st = str(out.read()) payload = message_from_string(st) else: # no cryptography process as usual payload = payload_in payload['From'] = encoded_or_raw(sender.decode(encoding)) origTo = to[:] if to: payload['To'] = encoded_or_raw(', '.join(to).decode(encoding)) if reply_to: payload['Reply-To'] = encoded_or_raw(reply_to.decode(encoding)) if cc: payload['Cc'] = encoded_or_raw(', '.join(cc).decode(encoding)) to.extend(cc) if bcc: to.extend(bcc) payload['Subject'] = encoded_or_raw(subject.decode(encoding)) payload['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) for k, v in iteritems(headers): payload[k] = encoded_or_raw(v.decode(encoding)) result = {} try: if self.settings.server == 'logging': self.app.log.warn('email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % ('-' * 40, sender, # NOQA ', '.join(to), subject, text or html, '-' * 40)) elif self.settings.server == 'gae': xcc = dict() if cc: xcc['cc'] = cc if bcc: xcc['bcc'] = bcc if reply_to: xcc['reply_to'] = reply_to from google.appengine.api import mail attachments = attachments and [(a.my_filename, a.my_payload) for a in attachments if not raw] if attachments: result = mail.send_mail( sender=sender, to=origTo, subject=subject, body=text, html=html, attachments=attachments, **xcc) elif html and (not raw): result = mail.send_mail( sender=sender, to=origTo, subject=subject, body=text, html=html, **xcc) else: result = mail.send_mail( sender=sender, to=origTo, subject=subject, body=text, **xcc) else: smtp_args = self.settings.server.split(':') if self.settings.ssl: server = smtplib.SMTP_SSL(*smtp_args) else: server = smtplib.SMTP(*smtp_args) if self.settings.tls and not self.settings.ssl: server.ehlo(self.settings.hostname) server.starttls() server.ehlo(self.settings.hostname) if self.settings.login: server.login(*self.settings.login.split(':', 1)) result = server.sendmail( sender, to, payload.as_string()) server.quit() except Exception as e: self.app.log.warn('Mail.send failure:%s' % e) self.result = result self.error = e return False self.result = result self.error = None return True
def genkey(): """Creates a new standard GPG key""" # Initialize our context. core.check_version(None) context = core.Context() context.set_armor(1) # Check if standard key is already present keyname = str(config["gpg_key_uid"]) context.op_keylist_start(keyname, 0) key = context.op_keylist_next() # If no key is chosen generate one no_key = key is None or not key or not keyname if no_key: # If no GPG key is set in config, choose one uid = choose_uid(context) if no_key and uid is not None: # A key has been chosen config["gpg_key_uid"] = repr(uid) stored = config["gpg_key_passphrase_isstored"] passwd = get_gpg_passwd_from_user(stored=stored, uid=uid) if passwd is None: sys.exit() else: config["gpg_key_passphrase"] = repr(passwd) elif no_key and uid is None: # No key has been chosen --> Create new one # Show progress dialog dlg_msg = _("Generating new GPG key {}.\nThis may take some time.\n \n" "Progress bar may stall. Please wait.").format(keyname) dlg = wx.ProgressDialog(_("GPG key generation"), dlg_msg, maximum=200, parent=None, style=wx.PD_ELAPSED_TIME) class CBFs(object): """Callback functions for pyme""" progress = 0 def cbf(self, what=None, type=None, current=None, total=None, hook=None): """Callback function that updates progress dialog""" dlg.Update(self.progress % 199) self.progress += 1 cbfs = CBFs() gpg_key_parameters = get_key_params_from_user() config["gpg_key_uid"] = repr(str( \ [val for key, val in gpg_key_parameters if key == 'Name-Real'][0])) config["gpg_key_passphrase"] = repr(str(([val \ for key, val in gpg_key_parameters if key == 'Passphrase'][0]))) gpg_key_parameters_string = get_key_params_string(gpg_key_parameters) context.set_progress_cb(cbfs.cbf, None) context.op_genkey(gpg_key_parameters_string, None, None) dlg.Destroy()