From 0e1ed2edbccbbbbdf34ebf21b841df49e1e8b7fe Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 22 Jan 2014 11:54:53 -0300 Subject: Start sending from the first subject. --- src/send-mail-batch.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/send-mail-batch.py b/src/send-mail-batch.py index fceea74..17c9543 100755 --- a/src/send-mail-batch.py +++ b/src/send-mail-batch.py @@ -191,10 +191,10 @@ gmail = GMail(FROM, SECRET) print "Sending {0} mails batch...".format(MAX_MAILS) -count = 1 -while count <= MAX_MAILS: +count = 0 +while count < MAX_MAILS: idx = (count % len(lorem_subjects)) - subject = "[TEST] {0:03} - {1}".format(count, lorem_subjects[idx]) + subject = "[TEST] {0:03} - {1}".format(count+1, lorem_subjects[idx]) print "Sending '{0}' ... ".format(subject), try: problems = send_test_mail(subject) -- cgit v1.2.3 From 619056df37c58ec7c1198d3164aac50926274a23 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 22 Jan 2014 11:55:14 -0300 Subject: Factor out gmail class. --- src/gmail.py | 125 +++++++++++++++++++++++++++++++++++++++++++++++++ src/send-mail-batch.py | 121 +---------------------------------------------- 2 files changed, 126 insertions(+), 120 deletions(-) create mode 100644 src/gmail.py diff --git a/src/gmail.py b/src/gmail.py new file mode 100644 index 0000000..34579b6 --- /dev/null +++ b/src/gmail.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import mimetypes +import os +import smtplib + +from email import encoders +from email.mime.base import MIMEBase +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + + +class GMail(object): + """Email sender using the gmail service.""" + SMTPSERVER = 'smtp.gmail.com:587' + + def __init__(self, account, password): + """ + Initializes the class. + You should provide credentials in order to login into gmail. + + :param account: the username@gmail.com account to use for sending. + :type account: str + :param password: the password for the account. + :type password: str + """ + self._from = account + login = account.split('@')[0] + self._server = smtplib.SMTP(self.SMTPSERVER) + self._server.starttls() + self._server.login(login, password) + + def __del__(self): + """ + Quits the server. + """ + self._server.quit() + + def _get_attachment(self, file_path): + """ + Creates the MIMEBase attachment for the file 'file_path'. + + Might raise: + IOError + + :param file_path: the file from to generate the attachment from. + :type file_path: str + + :return: the MIMEBase attachment. + :rtype: MIMEBase + """ + ctype, encoding = mimetypes.guess_type(file_path) + if ctype is None or encoding is not None: + # No guess could be made, or the file is encoded (compressed), + # so use a generic bag-of-bits type. + ctype = 'application/octet-stream' + maintype, subtype = ctype.split('/', 1) + + # Read the file, this may rise IOError + with open(file_path, 'rb') as fp: + the_file = MIMEBase(maintype, subtype) + the_file.set_payload(fp.read()) + + # Encode the payload using Base64 + encoders.encode_base64(the_file) + + # Set the filename parameter + the_file.add_header('Content-Disposition', 'attachment', + filename=os.path.basename(file_path)) + + return the_file + + def send_email(self, to_addr_list, cc_addr_list, subject, + message, attachments=None): + """ + Sends an email. + + :param to_addr_list: a list of addresses to send the email. + :type to_addr_list: list of str + :param cc_addr_list: a list of addresses to send (as CC) the email. + :type cc_addr_list: list of str + :param subject: the subject of the email + :type subject: str + :param message: the message to send + :type message: str + :param attachments: a list of paths for the files to attach. + :type attachments: list of str + + :return: an empty dict if everything went ok or + the problems result of sending the email(s). + :rtype: dict + """ + # Create the container (outer) email message. + msg = MIMEMultipart() + msg['Subject'] = subject + msg['From'] = self._from + msg['To'] = ', '.join(to_addr_list) + + if cc_addr_list: + msg['Cc'] = ', '.join(cc_addr_list) + + # Add the message's text + msg.attach(MIMEText(message)) + + problems = '' + if attachments is not None: + # Attach every file in the attachment parameter + for file_path in attachments: + try: + the_file = self._get_attachment(file_path) + msg.attach(the_file) + except IOError: + problems += "Could not attach {0!r}\n".format(file_path) + + message = msg.as_string() + try: + result = self._server.sendmail(self._from, to_addr_list, message) + except Exception as e: + result['Exception'] = repr(e) + + if problems: + result['Attachments'] = problems + + return result diff --git a/src/send-mail-batch.py b/src/send-mail-batch.py index 17c9543..4d889f7 100755 --- a/src/send-mail-batch.py +++ b/src/send-mail-batch.py @@ -1,133 +1,14 @@ #!/usr/bin/env python # encoding: utf-8 # import getpass -import mimetypes import os import sys import random -import smtplib import time from ConfigParser import SafeConfigParser -from email import encoders -from email.mime.base import MIMEBase -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText - - -class GMail(object): - """Email sender using the gmail service.""" - SMTPSERVER = 'smtp.gmail.com:587' - - def __init__(self, account, password): - """ - Initializes the class. - You should provide credentials in order to login into gmail. - - :param account: the username@gmail.com account to use for sending. - :type account: str - :param password: the password for the account. - :type password: str - """ - self._from = account - login = account.split('@')[0] - self._server = smtplib.SMTP(self.SMTPSERVER) - self._server.starttls() - self._server.login(login, password) - - def __del__(self): - """ - Quits the server. - """ - self._server.quit() - - def _get_attachment(self, file_path): - """ - Creates the MIMEBase attachment for the file 'file_path'. - - Might raise: - IOError - - :param file_path: the file from to generate the attachment from. - :type file_path: str - - :return: the MIMEBase attachment. - :rtype: MIMEBase - """ - ctype, encoding = mimetypes.guess_type(file_path) - if ctype is None or encoding is not None: - # No guess could be made, or the file is encoded (compressed), - # so use a generic bag-of-bits type. - ctype = 'application/octet-stream' - maintype, subtype = ctype.split('/', 1) - - # Read the file, this may rise IOError - with open(file_path, 'rb') as fp: - the_file = MIMEBase(maintype, subtype) - the_file.set_payload(fp.read()) - - # Encode the payload using Base64 - encoders.encode_base64(the_file) - - # Set the filename parameter - the_file.add_header('Content-Disposition', 'attachment', - filename=os.path.basename(file_path)) - - return the_file - - def send_email(self, to_addr_list, cc_addr_list, subject, - message, attachments=None): - """ - Sends an email. - - :param to_addr_list: a list of addresses to send the email. - :type to_addr_list: list of str - :param cc_addr_list: a list of addresses to send (as CC) the email. - :type cc_addr_list: list of str - :param subject: the subject of the email - :type subject: str - :param message: the message to send - :type message: str - :param attachments: a list of paths for the files to attach. - :type attachments: list of str - - :return: an empty dict if everything went ok or - the problems result of sending the email(s). - :rtype: dict - """ - # Create the container (outer) email message. - msg = MIMEMultipart() - msg['Subject'] = subject - msg['From'] = self._from - msg['To'] = ', '.join(to_addr_list) - - if cc_addr_list: - msg['Cc'] = ', '.join(cc_addr_list) - - # Add the message's text - msg.attach(MIMEText(message)) - - problems = '' - if attachments is not None: - # Attach every file in the attachment parameter - for file_path in attachments: - try: - the_file = self._get_attachment(file_path) - msg.attach(the_file) - except IOError: - problems += "Could not attach {0!r}\n".format(file_path) - - message = msg.as_string() - try: - result = self._server.sendmail(self._from, to_addr_list, message) - except Exception as e: - result['Exception'] = repr(e) - - if problems: - result['Attachments'] = problems - - return result +from gmail import GMail def send_test_mail(subject): -- cgit v1.2.3 From 38b56cd07fdd5c841f014656b8e67379692f78f0 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 22 Jan 2014 14:13:58 -0300 Subject: Add script to send stored mails. - Add a problematic email to emails/. - Fix error: local variable result referenced before assignment --- src/emails/non-ascii-plain-subject.message | 14 ++++++++ src/gmail.py | 22 +++++++++++- src/send-mail-strings.py | 58 ++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/emails/non-ascii-plain-subject.message create mode 100755 src/send-mail-strings.py diff --git a/src/emails/non-ascii-plain-subject.message b/src/emails/non-ascii-plain-subject.message new file mode 100644 index 0000000..25c44f4 --- /dev/null +++ b/src/emails/non-ascii-plain-subject.message @@ -0,0 +1,14 @@ +Content-Type: multipart/mixed; boundary="===============1817442041==" +MIME-Version: 1.0 +Subject: Héllò thërê - stored message - ÿȩāḩ +From: {FROM} +To: {TO} + +--===============1817442041== +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit + +Howdy from python! +The subject: Héllò thërê - stored message - ÿȩāḩ +--===============1817442041==-- diff --git a/src/gmail.py b/src/gmail.py index 34579b6..4e7af04 100644 --- a/src/gmail.py +++ b/src/gmail.py @@ -117,9 +117,29 @@ class GMail(object): try: result = self._server.sendmail(self._from, to_addr_list, message) except Exception as e: - result['Exception'] = repr(e) + result = {'Exception': repr(e)} if problems: result['Attachments'] = problems return result + + def send_email_string(self, mail, to): + """ + Sends an email from an already created email as a string. + + :param mail: the mail to send + :type mail: str + :param to: a address to send the email. + :type to: str + + :return: an empty dict if everything went ok or + the problems result of sending the email(s). + :rtype: dict + """ + try: + result = self._server.sendmail(self._from, [to], mail) + except Exception as e: + result = {'Exception': repr(e)} + + return result diff --git a/src/send-mail-strings.py b/src/send-mail-strings.py new file mode 100755 index 0000000..b682c90 --- /dev/null +++ b/src/send-mail-strings.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# encoding: utf-8 +# import getpass +import os +import sys + +from ConfigParser import SafeConfigParser + +from gmail import GMail + + +# Read credentials from options file +parser = SafeConfigParser() +parser.read('options.cfg') +try: + FROM = parser.get('Credentials', 'account') + SECRET = parser.get('Credentials', 'password') + TO = [parser.get('Configs', 'to')] + MAX_MAILS = parser.getint('Configs', 'mails_amount') +except Exception as e: + print "Problem reading options.cfg" + print "Exception: {0!r}".format(e) + sys.exit() + + +# create the GMail global object +gmail = GMail(FROM, SECRET) + +directory = './emails/' +file_list = [] + +for filename in os.listdir(directory): + path = os.path.join(directory, filename) + if os.path.isfile(path): + file_list.append(path) + +print "Sending {0} mail(s)...".format(len(file_list)) + +for mail_file in file_list: + print "Sending '{0}' ... ".format(mail_file), + with open(mail_file) as f: + email = f.read() + + try: + # replace placeholders with actual data + email = email.format(FROM=FROM, TO=','.join(TO)) + except KeyError: + print "Warning: missing placeholder in {0}".format(mail_file) + + try: + problems = gmail.send_email_string(email, TO) + except Exception as e: + problems = repr(e) + + if problems: + print "Problems: {0!r}".format(problems) + else: + print 'ok.' -- cgit v1.2.3 From cc516f070c938d02a7616910ef4233c390494661 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 22 Jan 2014 14:18:47 -0300 Subject: Add swaks helper script. --- src/swaks-batch.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 src/swaks-batch.sh diff --git a/src/swaks-batch.sh b/src/swaks-batch.sh new file mode 100755 index 0000000..1e81210 --- /dev/null +++ b/src/swaks-batch.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# This scripts depends on swaks: http://www.jetmore.org/john/code/swaks/ + +if [ -n "$1" ]; then + SWAKS=$1 +else + SWAKS=swaks +fi + +# if [[ "$SWAKS" == "" ]]; then +if ! `hash $SWAKS 2>/dev/null`; then + echo "Error: I need swaks to work." + echo "You can get swaks from: http://www.jetmore.org/john/code/swaks/" + echo "If you already have it, you can send its location as a first parameter of the script." + echo "E.g.: $0 /path/to/swaks" + exit 1 +fi + +# Customize this in order to test the desired accounts +FROM=ivan@dev.bitmask.net +TO=test_123@dev.bitmask.net + +# NOTE: Add the swak commands below here: + +# Unicode subject and body, without charset specification +$SWAKS --to $TO --from $FROM --header "Subject: Test -- áèīôü" --body "Howdy from swaks. ýëąĥ" --helo 'asdf.com' -- cgit v1.2.3