Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- """
- Mass-emailer, reading an email text file and a list of email addresses.
- (c) 2009-2015 University of Manchester
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (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, see <http://www.gnu.org/licenses/>.
- Author: Stian Soiland-Reyes <stian@soiland-reyes.com>
- http://orcid.org/0000-0001-9842-9718
- https://github.com/stain/mailer
- On first run, ~/.mailer is created, and must be configured with the
- smtp details.
- Tested with smtp.gmail.com.
- """
- from ConfigParser import ConfigParser
- import email
- import email.utils
- import getpass
- import os
- import re
- import smtplib
- import sys
- import time
- import threading
- DUMMY_USER="nobody@gmail.com"
- def _configpath():
- configpath = os.path.expanduser("~/.mailer")
- if not os.path.exists(configpath):
- open(configpath, "w").close()
- os.chmod(configpath, 0600)
- return configpath
- _config = None
- def config():
- global _config
- if _config is not None:
- return _config
- _config = ConfigParser()
- _config.read(_configpath())
- if not _config.has_section("smtp"):
- template_config()
- save_config()
- return _config
- def save_config():
- fp = open(_configpath(), "w")
- try:
- config().write(fp)
- finally:
- fp.close()
- def template_config():
- if not config().has_section("smtp"):
- config().add_section("smtp")
- config().set("smtp", "host", "smtp.gmail.com")
- config().set("smtp", "port", 587)
- config().set("smtp", "tls", True)
- config().set("smtp", "username", DUMMY_USER)
- config().set("smtp", "password", "WrongPassword")
- if not config().has_section("email"):
- config().add_section("email")
- config().set("email", "sender", DUMMY_USER)
- config().set("email", "from", "Dummy User")
- _smtp = None
- def smtp(_smtp):
- if _smtp is not None:
- return _smtp
- if config().get("smtp", "username") == DUMMY_USER:
- print >>sys.stderr, "You need to configure 'smtp' settings in", _configpath()
- sys.exit(2)
- if config().get("email", "sender") == DUMMY_USER:
- print >>sys.stderr, "You need to configure 'email' settings in", _configpath()
- sys.exit(3)
- _smtp = smtplib.SMTP()
- _smtp.connect(config().get("smtp", "host"), config().getint("smtp", "port"))
- if (config().getboolean("smtp", "tls")):
- _smtp.starttls()
- if (config().has_option("smtp", "username")):
- _smtp.login(config().get("smtp", "username"), config().get("smtp", "password"))
- return _smtp
- def send_email(_smtp, email_filename, to, counter, cc=None):
- text = open(email_filename).read()
- if "John Doe" in text:
- print >>sys.stderr, "You need to edit", email_filename
- sys.exit(4)
- ## template to insert a tracking counter that is
- ## unique per email, e.g. into the Subject field
- ## or signature. Note that the counter starttls
- ## from 00 for each execution.
- text = text.replace("--counter--", "%02x" % counter)
- if cc:
- ## template to insert the name of the first CC-ed person
- ## into message, e.g. the recommending "friend"
- first_cc = cc[0].split("<")[0].strip()
- text = text.replace("--friend--", first_cc)
- else:
- ## some kind of fallback
- text = text.replace("--friend--", "someone")
- msg = email.message_from_string(text)
- # Set standard headers if not already read from
- # file
- msg["MIME-Version"]= "1.0"
- if not msg["Content-Type"]:
- msg["Content-Type"] = "text/plain; charset=UTF-8"
- sender_email = config().get("email", "sender")
- sender_from = config().get("email", "from")
- msg["Sender"] = sender_email
- if not msg["From"]:
- msg["From"] = "%s <%s>" % (sender_from, sender_email)
- msg["Date"] = email.utils.formatdate(localtime=True)
- msg["Message-Id"] = email.utils.make_msgid("mailer.py")
- msg["To"] = to
- if (cc):
- msg["Cc"] = ", ".join(cc)
- #to_email = re.split("[<>]", to)[1]
- #print to_email
- ## sendmail seems to parse "Blah Blah <>" format?
- recipients = [to]
- if cc:
- recipients += cc
- _smtp.sendmail(sender_email, recipients, msg.as_string())
- global running
- running = 0
- def checklist(chunk):
- global running
- running += 1
- threadID = running
- try:
- _smtp = smtp(None)
- except:
- time.sleep(30)
- try:
- _smtp = smtp(None)
- except:
- return
- for recipient in chunk:
- recipient = recipient.strip()
- if "johndoe@example.com" in recipient:
- print >>sys.stderr, "You need to edit", email_filename
- sys.exit(5)
- cc = []
- if ("\t") in recipient:
- # A line with CC
- fields = recipient.split("\t")
- recipient = fields[0]
- cc = fields[1:]
- if recipient.count("<") > 1:
- print "FAILED: More than one email address on line - use \\t (tab character)"
- return
- counter = threadID
- counter += 1
- sent = False
- try:
- while not sent:
- try:
- send_email(_smtp, sys.argv[1], recipient, counter, cc)
- print recipient,
- print "%02x" % counter
- for also in cc:
- print " Cc:", also
- sent = True
- except smtplib.SMTPServerDisconnected, ex:
- print recipient,
- print repr(ex)
- print "Reconnecting in 5 seconds"
- time.sleep(5)
- _smtp.quit()
- _smtp = None
- _smtp = smtp(_smtp)
- except Exception, ex:
- #print recipient,
- print repr(ex)
- pass
- running -= 1
- if __name__ == "__main__":
- config()
- if len(sys.argv) < 3:
- print "Usage:", sys.argv[0], "email.txt", "addresses.txt"
- print ""
- print "Where email.txt is the RC822 formatted email (with headers)"
- print "and addresses.txt is a list of addresses to email, in the form"
- print ""
- print "First Last <email@example.com>"
- print ""
- sys.exit(1)
- threads=200
- def chunkify(lst,n):
- return [ lst[i::n] for i in xrange(n) ]
- lines = open(sys.argv[2],"r").readlines()
- lines = map(lambda s: s.strip(), lines) # remove all newlines
- chunks = chunkify(lines, threads) # make seperate chunk for each thread
- for thread in xrange(0,threads):
- if thread >= 384:
- sleep(0.2)
- try:
- threading.Thread(target = checklist, args = (chunks[thread],)).start()
- except:
- pass
- print "Scanning... Press enter 3 times to stop."
- for i in range(0,3):
- raw_input()
- os.popen("kill -9 " + str(os.getpid()))
Add Comment
Please, Sign In to add comment