def test_esc_set_false(self):
     r = Reply('250', 'Ok')
     self.assertEqual('2.0.0 Ok', r.message)
     r.enhanced_status_code = None
     self.assertEqual('2.0.0 Ok', r.message)
     r.enhanced_status_code = False
     self.assertEqual('Ok', r.message)
 def test_message_set_clear_esc(self):
     r = Reply('250', '2.3.4 Ok')
     self.assertEqual('2.3.4 Ok', r.message)
     self.assertEqual('2.3.4', r.enhanced_status_code)
     r.message = None
     self.assertEqual(None, r.message)
     self.assertEqual('2.0.0', r.enhanced_status_code)
 def test_copy(self):
     r1 = Reply('250', '2.1.0 Ok')
     r2 = Reply(command='RCPT')
     r2.copy(r1)
     self.assertEqual('250', r2.code)
     self.assertEqual('2.1.0', r2.enhanced_status_code)
     self.assertEqual('2.1.0 Ok', r2.message)
     self.assertEqual('RCPT', r2.command)
 def test_enqueue_wait_partial_relay_expired(self):
     env = Envelope('*****@*****.**', ['*****@*****.**',
                                           '*****@*****.**',
                                           '*****@*****.**'])
     bounce_mock = self.mox.CreateMockAnything()
     bounce_mock(IsA(Envelope), IsA(Reply)).AndReturn(None)
     bounce_mock(IsA(Envelope), IsA(Reply)).AndReturn(None)
     self.store.write(env, IsA(float)).AndReturn('1234')
     self.relay._attempt(env, 0).AndReturn([TransientRelayError('transient', Reply('450', 'transient 1')),
                                            TransientRelayError('transient', Reply('450', 'transient 1')),
                                            TransientRelayError('transient', Reply('450', 'transient 2'))])
     self.store.increment_attempts('1234')
     self.store.remove('1234')
     self.mox.ReplayAll()
     queue = Queue(self.store, self.relay, bounce_factory=bounce_mock, relay_pool=5)
     queue.enqueue(env)
     queue.relay_pool.join()
Example #5
0
    def _run(self):
        result, envelope = self.poll()
        if not result:
            return
        reraise = True
        try:
            self._connect()
            self._handshake()
            while result:
                if self._check_server_timeout():
                    self.queue.appendleft((result, envelope))
                    break
                self._deliver(result, envelope)
                if self.idle_timeout is None:
                    break
                result, envelope = self.poll()
        except SmtpRelayError as e:
            result.set_exception(e)
        except SmtpError as e:
            if not result.ready():
                if self.client.io.last_error:
                    reply = Reply(self.client.io.last_error_code, self.client.io.last_error_message,
                                  command=self.current_command)
                else:
                    reply = Reply('421', '4.3.0 {0!s}'.format(e),
                                  command=self.current_command)

                relay_error = SmtpRelayError.factory(reply)
                result.set_exception(relay_error)
        except Timeout:
            if not result.ready():
                reply = Reply(command=self.current_command).copy(timed_out)
                relay_error = SmtpRelayError.factory(reply)
                result.set_exception(relay_error)
        except Exception as e:
            if not result.ready():
                result.set_exception(e)
            reraise = False
            raise
        finally:
            try:
                self._disconnect()
            except Exception:
                if reraise:
                    raise
 def test_attempt_delivery_permanentrelayerror_nullsender(self):
     task = self.mox.CreateMockAnything()
     self.relay.attempt(self.bounce, 0).AndRaise(PermanentRelayError('permanent', Reply('550', 'permanent error')))
     self.celery.task(IgnoreArg()).AndReturn(task)
     self.mox.ReplayAll()
     def return_bounce(envelope, reply):
         self.fail('Tried to generate a bounce to a NULL sender.')
     queue = CeleryQueue(self.celery, self.relay, bounce_factory=return_bounce)
     queue.attempt_delivery(self.bounce, 0)
Example #7
0
 def _handle_encoding(self, envelope):
     if '8BITMIME' not in self.client.extensions:
         try:
             envelope.encode_7bit(self.binary_encoder)
         except UnicodeError:
             reply = Reply('554', '5.6.3 Conversion not allowed',
                           command=b'[data conversion]',
                           address=self.address)
             raise SmtpRelayError.factory(reply)
Example #8
0
 def _try_pipe(self, envelope):
     try:
         status, stdout, stderr = self._exec_process(envelope)
     except Timeout:
         msg = 'Delivery timed out'
         reply = Reply('450', msg)
         raise TransientRelayError(msg, reply)
     if status != 0:
         self.raise_error(status, stdout, stderr)
Example #9
0
 def test_send_empty_data(self):
     self.sock.sendall(b'.\r\n')
     self.sock.recv(IsA(int)).AndReturn(b'250 2.0.0 Ok\r\n'
                                        b'550 5.0.0 Not Ok\r\n')
     self.mox.ReplayAll()
     client = LmtpClient(self.sock)
     client.rcpttos = [('test1', Reply('250')), ('test2', Reply('250')),
                       ('test3', Reply('550'))]
     replies = client.send_empty_data()
     self.assertEqual(2, len(replies))
     self.assertEqual('test1', replies[0][0])
     self.assertEqual('250', replies[0][1].code)
     self.assertEqual('2.0.0 Ok', replies[0][1].message)
     self.assertEqual(b'[SEND_DATA]', replies[0][1].command)
     self.assertEqual('test2', replies[1][0])
     self.assertEqual('550', replies[1][1].code)
     self.assertEqual('5.0.0 Not Ok', replies[1][1].message)
     self.assertEqual(b'[SEND_DATA]', replies[1][1].command)
 def test_process_response_200(self):
     http_res = self.mox.CreateMockAnything()
     http_res.status = '200'
     http_res.reason = 'OK'
     http_res.getheader('X-Smtp-Reply', '').AndReturn('250; message="2.0.0 Ok"')
     http_res.getheaders()
     self.result.set(Reply('250', '2.0.0 Ok'))
     self.mox.ReplayAll()
     self.client._process_response(http_res, self.result)
Example #11
0
 def test_run_multiple(self):
     result1 = AsyncResult()
     result2 = AsyncResult()
     env1 = Envelope('*****@*****.**', ['*****@*****.**'])
     env1.parse(b'From: [email protected]\r\n\r\ntest test\r\n')
     env2 = Envelope('*****@*****.**', ['*****@*****.**'])
     env2.parse(b'From: [email protected]\r\n\r\ntest test\r\n')
     queue = BlockingDeque()
     queue.append((result1, env1))
     queue.append((result2, env2))
     self.sock.recv(IsA(int)).AndReturn(b'220 Welcome\r\n')
     self.sock.sendall(b'EHLO there\r\n')
     self.sock.recv(IsA(int)).AndReturn(b'250-Hello\r\n250 PIPELINING\r\n')
     self.sock.sendall(
         b'MAIL FROM:<*****@*****.**>\r\nRCPT TO:<*****@*****.**>\r\nDATA\r\n'
     )
     self.sock.recv(
         IsA(int)).AndReturn(b'250 Ok\r\n250 Ok\r\n354 Go ahead\r\n')
     self.sock.sendall(
         b'From: [email protected]\r\n\r\ntest test\r\n.\r\n')
     self.sock.recv(IsA(int)).AndReturn(b'250 Ok\r\n')
     self.sock.sendall(
         b'MAIL FROM:<*****@*****.**>\r\nRCPT TO:<*****@*****.**>\r\nDATA\r\n'
     )
     self.sock.recv(
         IsA(int)).AndReturn(b'250 Ok\r\n250 Ok\r\n354 Go ahead\r\n')
     self.sock.sendall(
         b'From: [email protected]\r\n\r\ntest test\r\n.\r\n')
     self.sock.recv(IsA(int)).AndReturn(b'250 Ok\r\n')
     self.sock.sendall(b'QUIT\r\n')
     self.sock.recv(IsA(int)).AndReturn(b'221 Goodbye\r\n')
     self.sock.close()
     self.mox.ReplayAll()
     client = SmtpRelayClient(('addr', 0),
                              queue,
                              socket_creator=self._socket_creator,
                              ehlo_as='there',
                              idle_timeout=0.0)
     client._run()
     self.assertEqual({'*****@*****.**': Reply('250', 'Ok')},
                      result1.get_nowait())
     self.assertEqual({'*****@*****.**': Reply('250', 'Ok')},
                      result2.get_nowait())
Example #12
0
 def _handle_encoding(self, result, envelope):
     if '8BITMIME' not in self.client.extensions:
         try:
             envelope.encode_7bit(self.binary_encoder)
         except UnicodeDecodeError:
             reply = Reply('554', '5.6.3 Conversion not allowed')
             e = SmtpRelayError.factory(reply)
             result.set_exception(e)
             return False
     return True
 def test_enqueue_wait_partial_relay(self):
     env = Envelope('*****@*****.**', ['*****@*****.**',
                                           '*****@*****.**',
                                           '*****@*****.**'])
     self.store.write(env, IsA(float)).AndReturn('1234')
     self.relay._attempt(env, 0).AndReturn([None,
                                            TransientRelayError('transient', Reply('450', 'transient')),
                                            PermanentRelayError('permanent', Reply('550', 'permanent'))])
     self.store.increment_attempts('1234')
     self.store.set_timestamp('1234', IsA(float))
     self.store.set_recipients_delivered('1234', [0, 2])
     self.mox.ReplayAll()
     def backoff(envelope, attempts):
         return 0
     def no_bounce(envelope, reply):
         return None
     queue = Queue(self.store, self.relay, backoff=backoff, bounce_factory=no_bounce, relay_pool=5)
     queue.enqueue(env)
     queue.relay_pool.join()
 def test_enqueue_wait_permanentfail(self):
     self.store.write(self.env, IsA(float)).AndReturn('1234')
     self.relay._attempt(self.env, 0).AndRaise(PermanentRelayError('permanent', Reply('550', 'permanent')))
     self.store.remove('1234')
     self.mox.ReplayAll()
     def no_bounce(envelope, reply):
         return None
     queue = Queue(self.store, self.relay, bounce_factory=no_bounce, relay_pool=5)
     queue.enqueue(self.env)
     queue.relay_pool.join()
Example #15
0
 def test_enqueue_relayerror(self):
     err = PermanentRelayError('msg failure', Reply('550', 'Not Ok'))
     self.relay._attempt(self.env, 0).AndRaise(err)
     self.mox.ReplayAll()
     q = ProxyQueue(self.relay)
     ret = q.enqueue(self.env)
     self.assertEqual(1, len(ret))
     self.assertEqual(2, len(ret[0]))
     self.assertEqual(self.env, ret[0][0])
     self.assertEqual(err, ret[0][1])
Example #16
0
 def _get_error_reply(self, exc):
     try:
         if self.client.last_error.code == '421':
             return self.client.last_error
     except Exception:
         pass
     return Reply('421',
                  '4.3.0 ' + str(exc),
                  command=self.current_command,
                  address=self.address)
Example #17
0
    def attempt(self, envelope, attempts):
        """Overrides the |Relay| :meth:`~slimta.relay.Relay.attempt` method to
        silently discard attempted messages. The |Queue| will see the attempt
        as a successful delivery.

        :param envelope: |Envelope| to attempt delivery for.
        :param attempts: Number of times the envelope has attempted delivery.

        """
        msg = '2.0.0 Message Delivered; {0!s}'.format(uuid.uuid4())
        return Reply('250', msg)
 def test_have_data(self):
     env = Envelope()
     handoff = self.mox.CreateMockAnything()
     handoff(env).AndReturn([(env, 'testid')])
     self.mox.ReplayAll()
     h = SmtpSession(('127.0.0.1', 0), None, handoff)
     h.envelope = env
     reply = Reply('250')
     h.HAVE_DATA(reply, b'', None)
     self.assertEqual('250', reply.code)
     self.assertEqual('2.6.0 Message accepted for delivery', reply.message)
Example #19
0
 def object_hook(obj):
     if '__datetime__' in obj:
         obj = datetime.datetime.strptime(
             obj['as_str'],
             "%Y%m%dT%H:%M:%S.%f").replace(tzinfo=pytz.utc)
     elif '__slimta.Reply__' in obj:
         obj = Reply(code=obj['as_dict'].get('code'),
                     message=obj['as_dict'].get('message'),
                     command=obj['as_dict'].get('command'),
                     address=obj['as_dict'].get('address'))
     return obj
 def test_enqueue_wait_transientfail(self):
     self.store.write(self.env, IsA(float)).AndReturn('1234')
     self.relay._attempt(self.env, 0).AndRaise(TransientRelayError('transient', Reply('450', 'transient')))
     self.store.increment_attempts('1234')
     self.store.set_timestamp('1234', IsA(float))
     self.mox.ReplayAll()
     def backoff(envelope, attempts):
         return 0
     queue = Queue(self.store, self.relay, backoff=backoff, relay_pool=5)
     queue.enqueue(self.env)
     queue.relay_pool.join()
Example #21
0
 def test_have_data_queueerror(self):
     env = Envelope()
     handoff = self.mox.CreateMockAnything()
     handoff(env).AndReturn([(env, QueueError())])
     self.mox.ReplayAll()
     h = SmtpSession(('127.0.0.1', 0), None, handoff)
     h.envelope = env
     reply = Reply('250')
     h.HAVE_DATA(reply, b'', None)
     self.assertEqual('451', reply.code)
     self.assertEqual('4.3.0 Error queuing message', reply.message)
Example #22
0
 def _connect(self):
     try:
         with Timeout(self.connect_timeout):
             self.socket = self.socket_creator(self.address)
     except socket_error:
         reply = Reply('451', '4.3.0 Connection failed',
                       command=self.current_command)
         raise SmtpRelayError.factory(reply)
     else:
         log.connect(self.socket, self.address)
     self.client = self._client_class(self.socket, self.tls_wrapper)
 def attempt_delivery(self, envelope, attempts):
     try:
         self.relay.attempt(envelope, attempts)
     except TransientRelayError as exc:
         self._handle_transient_failure(envelope, attempts, exc.reply)
     except PermanentRelayError as exc:
         self.enqueue_bounce(envelope, exc.reply)
     except Exception as exc:
         log_exception(__name__)
         reply = Reply('450', '4.0.0 Unhandled delivery error: ' + str(exc))
         self._handle_transient_failure(envelope, attempts, reply)
         raise
 def test_extended_handshake(self):
     creds = self.mox.CreateMockAnything()
     creds.authcid = 'testuser'
     creds.authzid = 'testzid'
     mock = self.mox.CreateMockAnything()
     mock.__call__(IsA(SmtpSession)).AndReturn(mock)
     mock.handle_banner(IsA(Reply), ('127.0.0.1', 0))
     mock.handle_ehlo(IsA(Reply), 'there')
     mock.handle_tls()
     mock.handle_auth(IsA(Reply), creds)
     self.mox.ReplayAll()
     h = SmtpSession(('127.0.0.1', 0), mock, None)
     h.BANNER_(Reply('220'))
     h.EHLO(Reply('250'), 'there')
     h.TLSHANDSHAKE()
     h.AUTH(Reply('235'), creds)
     self.assertEqual('there', h.ehlo_as)
     self.assertTrue(h.extended_smtp)
     self.assertEqual('TLS', h.security)
     self.assertEqual(('testuser', 'testzid'), h.auth)
     self.assertEqual('ESMTPSA', h.protocol)
Example #25
0
 def _try_pipe_one_rcpt(self, envelope):
     header_data, message_data = envelope.flatten()
     stdin = b''.join((header_data, message_data))
     rcpt = envelope.recipients[0]
     try:
         with Timeout(self.timeout):
             args = self._process_args(envelope, rcpt)
             return self._exec_process(args, stdin)
     except Timeout:
         msg = 'Delivery timed out'
         reply = Reply('450', '4.4.2 ' + msg)
         raise TransientRelayError(msg, reply)
Example #26
0
 def _parse_smtp_reply_header(self, http_res):
     raw_reply = http_res.getheader('X-Smtp-Reply', '')
     match = re.match(self.reply_code_pattern, raw_reply)
     if not match:
         return None
     code = match.group(1)
     message = ''
     command = None
     for match in re.finditer(self.reply_param_pattern, raw_reply):
         if match.group(1).lower() == 'message':
             message = match.group(2)
         elif match.group(1).lower() == 'command':
             command = match.group(2)
     return Reply(code, message, command)
Example #27
0
 def _attempt(self, id, envelope, attempts):
     try:
         self.relay._attempt(envelope, attempts)
     except TransientRelayError as e:
         self._pool_spawn('store', self._retry_later, id, envelope, e.reply)
     except PermanentRelayError as e:
         self._perm_fail(id, envelope, e.reply)
     except Exception as e:
         log_exception(__name__)
         reply = Reply('450', '4.0.0 Unhandled delivery error: ' + str(e))
         self._pool_spawn('store', self._retry_later, id, envelope, reply)
         raise
     else:
         self._pool_spawn('store', self.store.remove, id)
Example #28
0
 def attempt(self, envelope, attempts):
     domain = self._get_rcpt_domain(envelope)
     if domain in self._force_mx:
         dest, port = self._force_mx[domain]
     else:
         record = self._mx_records.setdefault(domain, MxRecord(domain))
         try:
             dest = self.choose_mx(record.get(), attempts)
         except ValueError as exc:
             msg = str(exc)
             reply = Reply('550', '5.1.2 ' + msg)
             raise PermanentRelayError(msg, reply)
         except DNSException:
             log_exception(__name__)
             msg = 'DNS lookup failed'
             reply = Reply('451', '4.4.3 ' + msg)
             raise TransientRelayError(msg, reply)
         port = 25
     try:
         relayer = self._relayers[(dest, port)]
     except KeyError:
         relayer = self.new_static_relay(dest, port)
         self._relayers[(dest, port)] = relayer
     return relayer.attempt(envelope, attempts)
 def test_attempt_delivery_permanentrelayerror(self):
     task = self.mox.CreateMockAnything()
     subtask = self.mox.CreateMockAnything()
     result = self.mox.CreateMockAnything()
     result.id = '12345'
     self.relay.attempt(self.env, 0).AndRaise(PermanentRelayError('permanent', Reply('550', 'permanent error')))
     self.celery.task(IgnoreArg()).AndReturn(task)
     task.s(self.bounce, 0).AndReturn(subtask)
     subtask.apply_async().AndReturn(result)
     self.mox.ReplayAll()
     def return_bounce(envelope, reply):
         self.assertEqual(self.env, envelope)
         return self.bounce
     queue = CeleryQueue(self.celery, self.relay, bounce_factory=return_bounce)
     queue.attempt_delivery(self.env, 0)
Example #30
0
 def _try_pipe_all_rcpts(self, envelope):
     header_data, message_data = envelope.flatten()
     stdin = b''.join((header_data, message_data))
     results = {}
     try:
         with Timeout(self.timeout):
             for rcpt in envelope.recipients:
                 args = self._process_args(envelope, rcpt)
                 results[rcpt] = self._exec_process(args, stdin)
     except Timeout:
         for rcpt in envelope.recipients:
             if rcpt not in results:
                 msg = 'Delivery timed out'
                 reply = Reply('450', '4.4.2 ' + msg)
                 results[rcpt] = TransientRelayError(msg, reply)
     return results
Example #31
0
 def test_have_data(self):
     class PtrLookup(object):
         def finish(self, *args):
             return 'localhost'
     env = Envelope()
     handoff = self.mox.CreateMockAnything()
     handoff(env).AndReturn([(env, 'testid')])
     self.mox.ReplayAll()
     h = SmtpSession(('127.0.0.1', 0), None, handoff)
     h.envelope = env
     h._ptr_lookup = PtrLookup()
     reply = Reply('250')
     h.HAVE_DATA(reply, b'', None)
     self.assertEqual('250', reply.code)
     self.assertEqual('2.6.0 Message accepted for delivery', reply.message)
     self.assertEqual('localhost', env.client['host'])
Example #32
0
 def _attempt(self, id, envelope, attempts):
     try:
         results = self.relay._attempt(envelope, attempts)
     except TransientRelayError as e:
         self._pool_spawn('store', self._retry_later, id, envelope, e.reply)
     except PermanentRelayError as e:
         self._perm_fail(id, envelope, e.reply)
     except Exception as e:
         log_exception(__name__)
         reply = Reply('450', '4.0.0 Unhandled delivery error: ' + str(e))
         self._pool_spawn('store', self._retry_later, id, envelope, reply)
         raise
     else:
         if isinstance(results, collections.Sequence):
             self._handle_partial_relay(id, envelope, attempts, results)
         else:
             self._remove(id)
 def test_attempt_delivery_transientrelayerror(self):
     task = self.mox.CreateMockAnything()
     subtask = self.mox.CreateMockAnything()
     result = self.mox.CreateMockAnything()
     result.id = '12345'
     self.relay.attempt(self.env, 0).AndRaise(TransientRelayError('transient', Reply('450', 'transient error')))
     self.celery.task(IgnoreArg()).AndReturn(task)
     task.s(self.env, 1).AndReturn(subtask)
     subtask.set(countdown=60)
     subtask.apply_async().AndReturn(result)
     self.mox.ReplayAll()
     def backoff(envelope, attempts):
         self.assertEqual(self.env, envelope)
         self.assertEqual(1, attempts)
         return 60
     queue = CeleryQueue(self.celery, self.relay, backoff=backoff)
     queue.attempt_delivery(self.env, 0)
 def test_esc_set(self):
     r = Reply('250')
     r.enhanced_status_code = None
     self.assertEqual('2.0.0', r.enhanced_status_code)
     r.enhanced_status_code = '2.3.4'
     self.assertEqual('2.3.4', r.enhanced_status_code)
 def test_code_set(self):
     r = Reply()
     r.code = None
     assert_equal(None, r.code)
     r.code = '100'
     assert_equal('100', r.code)
 def test_message_set(self):
     r = Reply()
     r.message = None
     assert_equal(None, r.message)
     r.message = 'Ok'
     assert_equal('Ok', r.message)
 def test_send(self):
     r = Reply('250', 'Ok')
     io = IO(None)
     r.send(io)
     self.assertEqual(b'250 2.0.0 Ok\r\n', io.send_buffer.getvalue())
 def test_code_set(self):
     r = Reply()
     r.code = None
     self.assertEqual(None, r.code)
     r.code = '100'
     self.assertEqual('100', r.code)
 def test_send_newline_first(self):
     r = Reply('250', 'Ok')
     r.newline_first = True
     io = IO(None)
     r.send(io)
     self.assertEqual(b'\r\n250 2.0.0 Ok\r\n', io.send_buffer.getvalue())
 def test_code_set_bad_value(self):
     r = Reply()
     with self.assertRaises(ValueError):
         r.code = 'asdf'
 def test_esc_without_code(self):
     r = Reply()
     r.enhanced_status_code = '2.3.4'
     self.assertEqual(None, r.enhanced_status_code)
     r.code = '250'
     self.assertEqual('2.3.4', r.enhanced_status_code)
 def test_esc_set_bad_value(self):
     r = Reply()
     with self.assertRaises(ValueError):
         r.enhanced_status_code = 'abc'
 def test_message_set_with_esc(self):
     r = Reply('250')
     r.message = '2.3.4 Ok'
     self.assertEqual('2.3.4 Ok', r.message)
     self.assertEqual('2.3.4', r.enhanced_status_code)
 def test_message_set(self):
     r = Reply()
     r.message = None
     self.assertEqual(None, r.message)
     r.message = 'Ok'
     self.assertEqual('Ok', r.message)