def maildir_source(self): from mailbox import Maildir import codecs def get_encoding(enc, default='latin1'): """Return *enc* if *enc* is a valid encoding, *default* otherwise. """ if not enc: return default try: codecs.lookup(enc) return enc except LookupError: return default m = Maildir(self._infile, create=False) if self._folder: m = m.get_folder(self._folder) for _, msg in m.iteritems(): for part in self.get_plaintext_parts(msg): enc = get_encoding(part.get_content_charset()) content_bytes = part.get_payload(decode=True) content = content_bytes.decode(encoding=enc, errors='ignore') yield from self.filter_text(content)
def create(self, path, mails, compression='xz', server=None): path = Path.cwd() / path with TemporaryDirectory(prefix="mailarchive-") as tmpdir: with tmp_chdir(tmpdir), tmp_umask(0o077): basedir = Path(path.name.split('.')[0]) maildir = Maildir(basedir, create=True) self.mailindex = MailIndex(server=server) last_folder = None for folder, msgbytes in mails: if folder != last_folder: mailfolder = maildir.add_folder(folder) last_folder = folder sha256 = hashlib.sha256(msgbytes).hexdigest() key = mailfolder.add(msgbytes) msg = mailfolder.get_message(key) idx_item = { "Date": msg.get("Date"), "From": msg.get("From"), "MessageId": msg.get("Message-Id"), "Subject": msg.get("Subject"), "To": msg.get("To"), "checksum": { "sha256": sha256 }, "folder": folder, "key": key, } self.mailindex.append(idx_item) with TemporaryFile(dir=tmpdir) as tmpf: self.mailindex.write(tmpf) tmpf.seek(0) self.add_metadata(".mailindex.yaml", tmpf) super().create(path, compression, [basedir]) return self
def test_maildir(self): tmpdir = mkdtemp('archweb') mdir = tmpdir + '/maildir' maildir = Maildir(mdir) msg = Message() msg['subject'] = 'John Doe' msg['to'] = 'John Doe <*****@*****.**>' maildir.add(msg) # Invalid call_command('donor_import', mdir) self.assertEqual(len(Donor.objects.all()), 0) # Valid msg = Message() msg['subject'] = 'Receipt [$25.00] By: David Doe [[email protected]]' msg['to'] = 'John Doe <*****@*****.**>' maildir.add(msg) call_command('donor_import', mdir) self.assertEqual(len(Donor.objects.all()), 1) # Re-running should result in no new donor call_command('donor_import', mdir) self.assertEqual(len(Donor.objects.all()), 1) rmtree(tmpdir)
def test_mailbox(self): with SMTP(*self.address) as client: client.sendmail( '*****@*****.**', ['*****@*****.**'], """\ From: Anne Person <*****@*****.**> To: Bart Person <*****@*****.**> Subject: A test Message-ID: <ant> Hi Bart, this is Anne. """) client.sendmail( '*****@*****.**', ['*****@*****.**'], """\ From: Cate Person <*****@*****.**> To: Dave Person <*****@*****.**> Subject: A test Message-ID: <bee> Hi Dave, this is Cate. """) client.sendmail( '*****@*****.**', ['*****@*****.**'], """\ From: Elle Person <*****@*****.**> To: Fred Person <*****@*****.**> Subject: A test Message-ID: <cat> Hi Fred, this is Elle. """) # Check the messages in the mailbox. mailbox = Maildir(self.maildir_path) messages = sorted(mailbox, key=itemgetter('message-id')) self.assertEqual( list(message['message-id'] for message in messages), ['<ant>', '<bee>', '<cat>'])
def cmd_dl(self, argv): ''' Collect messages from a POP3 server and deliver to a Maildir. Usage: {cmd} [{{ssl,tcp}}:]{{netrc_account|[user@]host[!sni_name][:port]}} maildir ''' pop_target = argv.pop(0) maildir_path = argv.pop(0) assert len(argv) == 0 if not isdirpath(maildir_path): raise ValueError("maildir %s: not a directory" % (maildir_path,)) M = Maildir(maildir_path) with POP3(pop_target) as pop3: msg_uid_map = dict(pop3.client_uidl()) print( f'{len(msg_uid_map)} message', ('' if len(msg_uid_map) == 1 else 's'), ('.' if len(msg_uid_map) == 0 else ':'), sep='' ) with ResultSet() as deleRs: with ResultSet() as retrRs: for msg_n in msg_uid_map.keys(): retrRs.add(pop3.dl_bg(msg_n, M, deleRs)) pop3.flush() retrRs.wait() # now the deleRs are all queued pop3.flush() if deleRs: print("wait for DELEs...") deleRs.wait()
def enqueue_message(msg, queue_path, sender, recipients, return_msg=False, in_progress=False, **queue_args): msg_bytes = serialize_message_with_queue_data(msg, sender=sender, recipients=recipients, **queue_args) create_maildir_directories(queue_path) mailbox = Maildir(queue_path) sub_dir = 'cur' if in_progress else 'new' return inject_message_into_maildir(msg_bytes, mailbox, sub_dir=sub_dir, return_msg=return_msg)
def add_file(go, path): if not go: print(f"Would add {path}") return # Open the mailbox md = Maildir(DEST.joinpath(DEST_FOLDER), create=False) # Determine the UID for the next message uid_re = re.compile(",U=([0-9]+),") uid = 1 + max( chain([0], map(lambda key: int(uid_re.search(key).groups()[0]), md.keys()))) print(f"Next UID: {uid}") # Determine the time when the message was originally delivered msg_bytes = path.read_bytes() msg = MaildirMessage(msg_bytes) msg_time = time.mktime(parsedate(msg["Date"])) # Create a Maildir filename in the format used by OfflineIMAP key = (f"{int(msg_time)}_0.{os.getpid()}.{socket.gethostname()},U={uid}," f"FMD5={md5(DEST_FOLDER.encode()).hexdigest()}:2,S") # Complete path dest = DEST.joinpath(DEST_FOLDER, "cur", key) assert not dest.exists() and dest.parent.exists() # Write the file print(f"Write {key}") dest.write_bytes(msg_bytes) # Update the utime os.utime(dest, (msg_time, msg_time))
def main() -> None: args = parse_args() global debug_enabled debug_enabled = args.debug mailbox = Maildir( dirname=args.maildir_path, factory=message_factory, create=False, ) message_filter = MessageFilter.create(args) debug("filter: {}".format(message_filter)) matches = 0 for message in mailbox: debug( "message: {}".format(( message.get("From"), message.get("Reply-To"), message.get("To"), message.get("Subject"), ), ), ) if match(message, message_filter): matches += 1 debug("matched: {}".format(matches)) if args.count is None or args.count == matches: exit(0) exit(1)
async def run_tests(): with tempfile.TemporaryDirectory() as maildir: # create temporary mailbox for d in ['cur', 'new', 'tmp']: maildirdir = os.path.join(maildir, d) os.mkdir(maildirdir) mailbox = Maildir(maildir) # start smtp server handler = MessageHandler(maildir) controller = Controller(handler) controller.start() # run tests tests = filter( lambda f: f.startswith('test_'), globals().keys(), ) for test in tests: print(test + '...', end=' ') try: await globals()[test](mailbox) except AssertionError: fail() continue succeed()
def import_email(email, import_path, format, **kwargs): from caliopen.core.user import User from caliopen.core.contact import Contact, ContactLookup from caliopen.core.mail import MailMessage from caliopen.smtp.agent import DeliveryAgent AVATAR_DIR = '../../../caliopen.ng/src/assets/images/avatars' if format == 'maildir': emails = Maildir(import_path, factory=message_from_file) mode = 'maildir' else: if os.path.isdir(import_path): mode = 'mbox_directory' emails = {} files = [ f for f in listdir(import_path) if os.path.isfile(os.path.join(import_path, f)) ] for f in files: with open('%s/%s' % (import_path, f)) as fh: emails[f] = message_from_file(fh) else: mode = 'mbox' emails = mbox(import_path) print email user = User.get(email) agent = DeliveryAgent() mailfrom = '' rcpts = [email] log.info("Processing mode %s" % mode) msgs = [] for key, mail in emails.iteritems(): # Create contact for user log.info('Processing mail %s' % key) msgs.append(MailMessage(mail)) msgs = sorted(msgs, key=lambda msg: msg.date) for msg in msgs: for type, addresses in msg.recipients.iteritems(): if not addresses: continue for alias, _address in addresses: lookup = ContactLookup.get(user, alias) if not lookup: log.info('Creating contact %s' % alias) infos = {'mail': alias} name, domain = alias.split('@') if os.path.isfile('%s/%s.png' % (AVATAR_DIR, name)): infos.update({'avatar': '%s.png' % name}) Contact.create(user, infos) res = agent.process(mailfrom, rcpts, msg.mail.as_string()) log.info('Process result %r' % res)
def open(self, basedir=None): """Maildirの利用を開始する""" if not basedir: basedir = os.path.expanduser(user_mailbox) mdir = Maildir(basedir, None) self.mdir = mdir self.cur = '' self.fld = mdir self.basedir = basedir
def send_email(self, message): """Sends an email directly (no queuing). No retries are done, the caller should handle MailDeliveryException in order to ensure that the mail is never lost. :param message: the email.message.Message to send. The envelope sender will be extracted from the ``Return-Path`` or ``From`` headers. The envelope recipients will be extracted from the combined list of ``To``, ``CC`` and ``BCC`` headers. :return: the Message-ID of the message that was just sent, if successfully sent, otherwise raises MailDeliveryException and logs root cause. """ smtp_from = message['Return-Path'] or message['From'] assert smtp_from, "The Return-Path or From header is required for any outbound email" # The email's "Envelope From" (Return-Path), and all recipient addresses must only contain ASCII characters. from_rfc2822 = extract_rfc2822_addresses(smtp_from) assert len( from_rfc2822 ) == 1, "Malformed 'Return-Path' or 'From' address - it may only contain plain ASCII characters" smtp_from = from_rfc2822[0] email_to = message['To'] email_cc = message['Cc'] email_bcc = message['Bcc'] smtp_to_list = filter( None, flatten( map(extract_rfc2822_addresses, [email_to, email_cc, email_bcc]))) assert smtp_to_list, "At least one valid recipient address should be specified for outgoing emails (To/Cc/Bcc)" try: message_id = message['Message-Id'] # Add email in Maildir if smtp_host contains maildir. if self.smtp_host.startswith('maildir:/'): from mailbox import Maildir maildir_path = self.smtp_host[8:] mdir = Maildir(maildir_path, factory=None, create=True) mdir.add(message.as_string(True)) return message_id try: self.conn.sendmail(smtp_from, smtp_to_list, message.as_string()) finally: try: # Close Connection of SMTP Server self.conn.quit() except Exception: # ignored, just a consequence of the previous exception pass except Exception, e: msg = "Mail delivery failed via SMTP server '%s'.\n%s: %s" % ( unicode(self.smtp_host), e.__class__.__name__, unicode(e)) raise ZatoException(self.cid, msg)
def __init__(self, oldBoxDir, newBoxDir, daystoremove): from sys import stdout from mailbox import Maildir, MaildirMessage, NoSuchMailboxError from os.path import expanduser from time import time, strftime, gmtime self.logfile = strftime( expanduser("~/") + "mail-remove-log-%Y%m%d%H%M", gmtime()) # DeltaT is epoch now - 60(s)*m*d*nm where n is number of months - here set for 4 months self.deltaT = int(round(time() - (60 * 60 * 24 * int(daystoremove)))) self.newBox = Maildir(newBoxDir, create=True, factory=MaildirMessage) try: self.oldBox = Maildir(oldBoxDir, create=False, factory=MaildirMessage) except NoSuchMailboxError: print("[E] Invalid mail dir. Aborting") return (1)
def __init__(self, mailDirPath: Path, mailBoxesPath: Path): super().__init__() self.clearMailDirPath = mailDirPath self.clearMailDirPath.mkdir(parents=True, exist_ok=True) self.clearMailDir = Maildir(str(self.clearMailDirPath)) self.mailBoxesPath = mailBoxesPath self.mailBoxes = {} # Signals self.sNewMessage = AsyncSignal(str)
def _email_send(smtp_from, smtp_to_list, message, openobject_id=None, ssl=False, debug=False): """Low-level method to send directly a Message through the configured smtp server. :param smtp_from: RFC-822 envelope FROM (not displayed to recipient) :param smtp_to_list: RFC-822 envelope RCPT_TOs (not displayed to recipient) :param message: an email.message.Message to send :param debug: True if messages should be output to stderr before being sent, and smtplib.SMTP put into debug mode. :return: True if the mail was delivered successfully to the smtp, else False (+ exception logged) """ if openobject_id: message['Message-Id'] = "<%s-openobject-%s@%s>" % (time.time(), openobject_id, socket.gethostname()) try: smtp_server = config['smtp_server'] if smtp_server.startswith('maildir:/'): from mailbox import Maildir maildir_path = smtp_server[8:] mdir = Maildir(maildir_path,factory=None, create = True) mdir.add(msg.as_string(True)) return True oldstderr = smtplib.stderr if not ssl: ssl = config.get('smtp_ssl', False) s = smtplib.SMTP() try: # in case of debug, the messages are printed to stderr. if debug: smtplib.stderr = WriteToLogger() s.set_debuglevel(int(bool(debug))) # 0 or 1 s.connect(smtp_server, config['smtp_port']) if ssl: s.ehlo() s.starttls() s.ehlo() if config['smtp_user'] or config['smtp_password']: s.login(config['smtp_user'], config['smtp_password']) s.sendmail(smtp_from, smtp_to_list, message.as_string()) finally: try: s.quit() if debug: smtplib.stderr = oldstderr except: # ignored, just a consequence of the previous exception pass except Exception, e: _logger.error('could not deliver email', exc_info=True) return False
def _maildir_delivery(self, username, domain, data): try: path = os.path.join(os.path.expanduser(MAILDIR_DELIVERY_USER), domain, username) mdir = Maildir(path) mdirid = mdir.add(data) print "Maildir Delivered %s@%s %s/%s" % (username, domain, path, mdirid) except: # XXX log errors with maildir but continue print "Maildir delivery error %s@%s " % (username, domain)
def main(args=sys.argv): parser = option_parser() opts, args = parser.parse_args(args) if len(args) > 1: if len(args) < 4: print ('You must specify the from address, to address and body text' ' on the command line') return 1 msg = compose_mail(args[1], args[2], args[3], subject=opts.subject, attachment=opts.attachment) from_, to = args[1:3] eto = [extract_email_address(x.strip()) for x in to.split(',')] efrom = extract_email_address(from_) else: msg = sys.stdin.read() from email import message_from_string from email.utils import getaddresses eml = message_from_string(msg) tos = eml.get_all('to', []) ccs = eml.get_all('cc', []) + eml.get_all('bcc', []) all_tos = [] for x in tos + ccs: all_tos.extend(y.strip() for y in x.split(',')) eto = list(map(extract_email_address, all_tos)) if not eto: raise ValueError('Email from STDIN does not specify any recipients') efrom = getaddresses(eml.get_all('from', [])) if not efrom: raise ValueError('Email from STDIN does not specify a sender') efrom = efrom[0][1] outbox = None if opts.outbox is not None: outbox = os.path.abspath(os.path.expanduser(opts.outbox)) from mailbox import Maildir outbox = Maildir(opts.outbox, factory=None) if opts.fork: if os.fork() != 0: return 0 try: sendmail(msg, efrom, eto, localhost=opts.localhost, verbose=opts.verbose, timeout=opts.timeout, relay=opts.relay, username=opts.username, password=opts.password, port=opts.port, encryption=opts.encryption_method) except: if outbox is not None: outbox.add(msg) outbox.close() print 'Delivery failed. Message saved to', opts.outbox raise return 0
def test_mailbox_reset(self): with SMTP(*self.address) as client: client.sendmail( '*****@*****.**', ['*****@*****.**'], """\ From: Anne Person <*****@*****.**> To: Bart Person <*****@*****.**> Subject: A test Message-ID: <ant> Hi Bart, this is Anne. """) self.handler.reset() mailbox = Maildir(self.maildir_path) self.assertEqual(list(mailbox), [])
def _load_mails_as_is(mail_paths, store): deferreds = [] for path in mail_paths: if isfile(path): mbox_mails = mbox(path, factory=None) yield add_mail_folder(store, mbox_mails, 'INBOX', deferreds) else: maildir = Maildir(path, factory=None) yield add_mail_folder(store, maildir, 'INBOX', deferreds) for mail_folder_name in maildir.list_folders(): mail_folder = maildir.get_folder(mail_folder_name) yield add_mail_folder(store, mail_folder, mail_folder_name, deferreds) yield defer.gatherResults(deferreds, consumeErrors=True)
def duplicates_run(opts, maildir_paths): mails_by_hash = {} mail_count = 0 check_maildirs_valid(maildir_paths) for maildir_path in maildir_paths: maildir = Maildir(maildir_path, factory=None) mail_count += collate_folder_by_hash(mails_by_hash, maildir, opts.message_id) duplicates, sizes_too_dissimilar, diff_too_big, removed, sets = \ find_duplicates(mails_by_hash, opts) report_results(duplicates, sizes_too_dissimilar, diff_too_big, removed, sets, mail_count)
def archive_message(mlist, message): """See `IArchiver`. This archiver saves messages into a maildir. """ archive_dir = os.path.join(config.ARCHIVE_DIR, 'prototype') try: os.makedirs(archive_dir, 0o775) except OSError as error: # If this already exists, then we're fine if error.errno != errno.EEXIST: raise # Maildir will throw an error if the directories are partially created # (for instance the toplevel exists but cur, new, or tmp do not) # therefore we don't create the toplevel as we did above. list_dir = os.path.join(archive_dir, mlist.fqdn_listname) mailbox = Maildir(list_dir, create=True, factory=None) lock_file = os.path.join( config.LOCK_DIR, '{0}-maildir.lock'.format(mlist.fqdn_listname)) # Lock the maildir as Maildir.add() is not threadsafe. Don't use the # context manager because it's not an error if we can't acquire the # archiver lock. We'll just log the problem and continue. # # XXX 2012-03-14 BAW: When we extend the chain/pipeline architecture # to other runners, e.g. the archive runner, it would be better to let # any TimeOutError propagate up. That would cause the message to be # re-queued and tried again later, rather than being discarded as # happens now below. lock = Lock(lock_file) try: lock.lock(timeout=timedelta(seconds=1)) # Add the message to the maildir. The return value could be used # to construct the file path if necessary. E.g. # # os.path.join(archive_dir, mlist.fqdn_listname, 'new', # message_key) mailbox.add(message) except TimeOutError: # Log the error and go on. log.error('Unable to acquire prototype archiver lock for {0}, ' 'discarding: {1}'.format( mlist.fqdn_listname, message.get('message-id', 'n/a'))) finally: lock.unlock(unconditionally=True) # Can we get return the URL of the archived message? return None
def load_mails(args, mail_paths): leap_session, soledad = args account = leap_session.account deferreds = [] for path in mail_paths: maildir = Maildir(path, factory=None) add_mail_folder(account, maildir, 'INBOX', deferreds) add_mail_folder(account, maildir, 'DRAFTS', deferreds) for mail_folder_name in maildir.list_folders(): mail_folder = maildir.get_folder(mail_folder_name) add_mail_folder(account, mail_folder, mail_folder_name, deferreds) yield defer.DeferredList(deferreds) defer.returnValue(args)
def test_mailbox_reset(self, temp_maildir, mailbox_controller, client): client.sendmail( "*****@*****.**", ["*****@*****.**"], dedent("""\ From: Anne Person <*****@*****.**> To: Bart Person <*****@*****.**> Subject: A test Message-ID: <ant> Hi Bart, this is Anne. """), ) mailbox_controller.handler.reset() mailbox = Maildir(temp_maildir) assert list(mailbox) == []
def load_mails(args, mail_paths): leap_session, soledad = args store = leap_session.mail_store deferreds = [] for path in mail_paths: maildir = Maildir(path, factory=None) yield add_mail_folder(store, maildir, 'INBOX', deferreds) for mail_folder_name in maildir.list_folders(): mail_folder = maildir.get_folder(mail_folder_name) yield add_mail_folder(store, mail_folder, mail_folder_name, deferreds) yield defer.gatherResults(deferreds, consumeErrors=True) defer.returnValue(args)
def delete(self, address, maildir=False): if not maildir: a = address.lower() if self.db.has_key(a): del self.db[a] else: print "Warning: %s is not in database" % a else: maildir = Maildir(address) for msg in maildir: mfrom, retpa, cc, to = self.msg_addrs(msg) if not retpa: continue for address in retpa: address = address.lower() if self.db.has_key(address): print "deleting: <%s>" % address del (self.db[address])
def test_mailbox(self, temp_maildir, mailbox_controller, client): client.sendmail( "*****@*****.**", ["*****@*****.**"], dedent("""\ From: Anne Person <*****@*****.**> To: Bart Person <*****@*****.**> Subject: A test Message-ID: <ant> Hi Bart, this is Anne. """), ) client.sendmail( "*****@*****.**", ["*****@*****.**"], dedent("""\ From: Cate Person <*****@*****.**> To: Dave Person <*****@*****.**> Subject: A test Message-ID: <bee> Hi Dave, this is Cate. """), ) client.sendmail( "*****@*****.**", ["*****@*****.**"], dedent("""\ From: Elle Person <*****@*****.**> To: Fred Person <*****@*****.**> Subject: A test Message-ID: <cat> Hi Fred, this is Elle. """), ) # Check the messages in the mailbox. mailbox = Maildir(temp_maildir) messages = sorted(mailbox, key=itemgetter("message-id")) assert list(message["message-id"] for message in messages) == [ "<ant>", "<bee>", "<cat>", ]
def main(smtp_host, smtp_port, web_host, web_port, develop, debug, maildir): if debug: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) logger.info("SMTP server is running on %s:%s", smtp_host, smtp_port) logger.info("Web server is running on %s:%s", web_host, web_port) if develop: logger.info("Running in developer mode") dir_context = TemporaryDirectory if maildir is None else lambda: nullcontext( maildir) with dir_context() as maildir_path: maildir_path = pathlib.Path(maildir_path) maildir_path.mkdir(parents=True, exist_ok=True) logger.info("Mail directory: %s", maildir_path) config = Configuration( smtp_host=smtp_host, smtp_port=smtp_port, web_host=web_host, web_port=web_port, develop=develop, debug=debug, ) maildir = Maildir(maildir_path / "maildir") mailbox = MailboxHandler(maildir_path / "maildir") controller = Controller(mailbox, hostname=config.smtp_host, port=config.smtp_port) web_server = WebServer(config, maildir) mailbox.register_message_observer(web_server) controller.start() web_server.start() controller.stop()
def populateMailbox(self, username): # Here will the mail directory of the user be stored pathShort = "/var/vmail/" + self.domain + "/" + username pathLong = pathShort + "/Maildir" # Create those directories os.mkdir(pathShort) mailbox = Maildir(pathLong) # And now populate that directory numOfMails = random.randint(1, 6666) percentageRead = random.randint(0, 100) for i in xrange(1, numOfMails): message = MaildirMessage(message=str(i)) message.set_subdir("cur") readSeed = random.randint(0, 100) if percentageRead <= readSeed: message.set_flags("S") mailbox.add(message) self.chmodMailbox(pathShort)
def update(self, maildir, allcc=False, fromaddr=None): """updates Database allcc: add all from: and cc: addresses from all mails fromaddr: if set, add cc: only from this sender""" maildir = Maildir(maildir) if fromaddr: fromaddr = fromaddr.lower() for msg in maildir: mfrom, retpa, cc, to = self.msg_addrs(msg) # mfrom header is set and it is identical to the give address if allcc or (mfrom and fromaddr and mfrom[0] == fromaddr): self._updatel(mfrom) self._updatel(retpa) self._updatel(to) self._updatel(cc) # add from: and return-path: in any case elif mfrom or retpa: self._updatel(mfrom) self._updatel(retpa) else: pass
async def handle_DATA(self, server, session, envelope): d = Maildir(MAILDIR_PATH) message = email.message_from_bytes(envelope.original_content) for r in envelope.rcpt_tos: message['X-Rcpt-To'] = r d.add(message) try: conn = smtplib.SMTP(RELAY_HOST, RELAY_PORT) conn.ehlo() conn.starttls() conn.login(RELAY_USER, RELAY_PASS) refused = conn.sendmail(envelope.mail_from, envelope.rcpt_tos, envelope.original_content) if refused: print(refused, flush=True) print("To: %r" % (envelope.rcpt_tos, ), flush=True) return '250 OK' except smtplib.SMTPException as exn: traceback.print_exc() return '550 SMTPException: %s' % exn