# -*- coding: iso-8859-1 -*
##
## <peter@fry-it.com>
##



import sys, os
import stat
if __name__ == '__main__':
    execfile(os.path.join(sys.path[0], 'framework.py'))

from base import TestBase

#------------------------------------------------------------------------------
#
# Some constants
#

#------------------------------------------------------------------------------


class POP3TestCase(TestBase):
    """ 
    Test to create a POP3 account object and several accepting email accounts
    inside it.
    """ 
    
    def test_creatingAccount(self):
        """ test to create a POP3 account """
        tracker = self.folder.tracker
        
        self.assertEqual(tracker.getPOP3Accounts(), [])
        
        tracker.createPOP3Account('mail.example.com', 'peter', 'secret')
        self.assertEqual(len(tracker.getPOP3Accounts()), 1)
        
        account = tracker.getPOP3Accounts()[0]
        self.assertEqual(account.getTitle(), 'mail.example.com')
        self.assertEqual(account.getHostname(), 'mail.example.com')
        self.assertEqual(account.getPort(), 110)
        self.assertEqual(account.getUsername(), 'peter')
        self.assertFalse(account.doDeleteAfter())
        self.assertFalse(account.doSSL())
        
        self.assertEqual(len(account.getAcceptingEmails()), 0)
        
        # try editing it
        account.manage_editAccount(hostname='m.example.com', username='peterbe', 
                            portnr=210, delete_after=1)
                            
        self.assertEqual(account.getHostname(), 'm.example.com')
        self.assertEqual(account.getPort(), 210)
        self.assertEqual(account.getUsername(), 'peterbe')
        self.assertTrue(account.doDeleteAfter())
    

    def test_creatingAcceptingEmail(self):
        """ test to create a POP3 account accepting email object """
        tracker = self.folder.tracker
        account = tracker.createPOP3Account('mail.example.com', 'peter', 'secret')
        
        # there are unfortunately two different ways to create accepting email
        # objects. Either directly on the account or via the issuetracker 
        # itself. With the latter option, you have to pass the ID of the 
        # account.
        # The latter one is more user friendly and easier to "access" because
        # that's what the DTML files do.
        ae = tracker.createAcceptingEmail(account.getId(), 'mail@example.com')
        self.assertEqual(ae.aq_parent.absolute_url(), account.absolute_url())
        
        self.assertEqual(ae.getEmailAddress(), 'mail@example.com')
        self.assertFalse(ae.doSendConfirm())
        self.assertFalse(ae.revealIssueURL())
        
        ## try to mess with it
        # invalid email address
        self.assertRaises(ValueError, tracker.createAcceptingEmail, account.getId(), 'mail @ example.com')
        # non-existant pop3 account
        self.assertRaises(AttributeError, tracker.createAcceptingEmail, 'zxy', 'mail@example.com')
        
        ## test to edit the accepting email object
        ae.editDetails(email_address='www@example.com', send_confirm=True, reveal_issue_url=True)
        self.assertEqual(ae.getEmailAddress(), 'www@example.com')
        self.assertTrue(ae.doSendConfirm())
        self.assertTrue(ae.revealIssueURL())
        
    def test_acceptingEmailMatch(self):
        """ try creating a accepting email address and add some whitelist and blacklist 
        email addresses. """
        
        tracker = self.folder.tracker
        account = tracker.createPOP3Account('mail.example.com', 'peter', 'secret')
        
        ae = tracker.createAcceptingEmail(account.getId(), 'mail@example.com')
        
        # just blacklist
        ae.editDetails(blacklist_emails=['test@peterbe.com'])
        
        self.assertFalse(ae.acceptOriginatorEmail('TEST@peterbe.COM'))
        self.assertTrue(ae.acceptOriginatorEmail('TEST.ElSE@peterbe.COM'))
        
        ae.editDetails(blacklist_emails=['*@peterbe.com'], whitelist_emails=['exception@peterbe.com'])
        
        self.assertFalse(ae.acceptOriginatorEmail('TEST@peterbe.COM'))
        self.assertTrue(ae.acceptOriginatorEmail('eXception@peterbe.COM'))        
        
                 
        
        
from poplib import POP3, error_proto

class FakePOP3(POP3):
    
    username = 'test'
    password = 'test'
    files = []

    def __init__(self, hostname, port=110):
        self.hostname = hostname
        self.port = port
            
    def getwelcome(self):
        return "Welcome to fake account"

    def user(self, user):
        if user != self.username:
            raise error_proto("Wrong username.")

    def pass_(self, pswd):
        if pswd != self.password:
            raise error_proto("Wrong password.")

    def list(self, which=None):
        # eg. ('+OK 4 messages:', ['1 71017', '2 2201', '3 7723', '4 44152'], 34)
        files = self.files
        responses = []
        for i, f in enumerate(files):
            responses.append('%s %s' % (i+1, os.stat(f)[stat.ST_SIZE]))
        return ('+OK %s messages:' % len(files), responses, None)

    def retr(self, which):
        # ['response', ['line', ...], octets]
        filename = self.files[which-1]
        return ('response', open(filename, 'r').xreadlines(), None)

    def quit(self):
        pass
        



class EmailInTestCase(TestBase):
    """
    Here we'll try to actually send some emails in to the issuetracker by
    setting up a fake POP3 server. 
    """
    
    def test_emailIn1(self):
        """ test a very basic email in """
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        FakePOP3.files = [abs_path('email-in-1.email')]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3

        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 1 issue') > -1)

        # this should have created an issue 
        self.assertEqual(len(tracker.getIssueObjects()), 1)
        
    def test_emailIn1_with_autoreply(self):
        """ test a very basic email in """
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        FakePOP3.files = [abs_path('email-in-1_with-autoreply.email'),
                          abs_path('email-in-2_with-autoreply.email')]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3

        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 0 issues') > -1)

        # this should have created an issue 
        self.assertEqual(len(tracker.getIssueObjects()), 0)
        
        
    def test_emailIn2(self):
        """ test rejecting an email based on from address """
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        ae.editDetails(blacklist_emails=['*@peterbe.com'], whitelist_emails=['special@peterbe.com'])
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        # this sends from mail@peterbe.com and another one from special@peterbe.com
        
        FakePOP3.files = [abs_path('email-in-1.email'), 
                          abs_path('email-in-2.email')]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3

        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 1 issue') > -1)

        # this should have created an issue 
        self.assertEqual(len(tracker.getIssueObjects()), 1)
        
    def test_emailIn3(self):
        """ test setting section, urgency and type by the subject line """
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        # this sends from mail@peterbe.com and another one from special@peterbe.com
        
        FakePOP3.files = [abs_path('email-in-3.email'),]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3
        
        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 1 issue') > -1)
        
        issue = tracker.getIssueObjects()[0]
        self.assertEqual(issue.getSections(), ['General'])
        self.assertEqual(issue.getUrgency(), 'critical')
        self.assertEqual(issue.getType(), 'bug report')
        self.assertEqual(issue.getTitle(), 'NonExistant: There is a problem')
        
        
    def test_emailIn4(self):
        """ Test emails CCed in """
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        
        # 'email-in-4.email' sends to peter@example.com but is CCed to
        # mail@example.com
        FakePOP3.files = [abs_path('email-in-4.email'),]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3
        
        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 1 issue') > -1)

        # this should have created an issue 
        self.assertEqual(len(tracker.getIssueObjects()), 1)
        
        
    def test_emailIn5(self):
        """ Test emails in multipart """
        tracker = self.folder.tracker
        self._send_in_emails('email-in-5.email')
        
        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 1 issue') > -1)
        
        issue = tracker.getIssueObjects()[0]
        self.assertTrue(isinstance(issue.getTitle(), unicode))
        
        # XXX I'm not sure what this test is meant to test.
        # Perhaps the functionality of emailing in HTML emails should
        # change to show HTML safely.

        
    def test_emailIn6(self):
        """ Test emails with type and urgency in the subject line """
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        
        # 'email-in-4.email' sends to peter@example.com but is CCed to
        # mail@example.com
        FakePOP3.files = [abs_path('email-in-6.email'),]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3
        
        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 1 issue') > -1)

        # this should have created an issue 
        self.assertEqual(len(tracker.getIssueObjects()), 1)
        
        issue = tracker.getIssueObjects()[0]
        self.assertEqual(issue.getUrgency(), 'critical')
        self.assertEqual(issue.getType(), 'bug report')
        self.assertEqual(issue.getSections(), ['Homepage'])
        self.assertEqual(issue.getTitle(), u'Subject line')
        self.assertTrue(isinstance(issue.getTitle(), unicode))
        
        
    def test_emailIn7(self):
        """ Test emails with 'Re:' in the subject line """
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        
        # 'email-in-4.email' sends to peter@example.com but is CCed to
        # mail@example.com
        FakePOP3.files = [abs_path('email-in-7.email'),]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3
        
        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 1 issue') > -1)

        issue = tracker.getIssueObjects()[0]
        self.assertEqual(issue.getTitle(), u'Re: Hello')
        
        
    def test_emailIn8(self):
        """ Test what the email in receipt says when emailing in """
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        ae.send_confirm = True
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        
        # 'email-in-4.email' sends to peter@example.com but is CCed to
        # mail@example.com
        FakePOP3.files = [abs_path('email-in-8.email'),]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3
        
        result = tracker.check4MailIssues(verbose=True)
        
        receipt = self.snatched_emails[0]
        
        self.assertEqual(receipt['to'], 'Mr Exception <special@peterbe.com>')
        self.assertTrue(receipt['subject'].count(tracker.getTitle()))
        # expect the issue id to be mentioned in the email msg
        issue = tracker.getIssueObjects()[0]
        self.assertTrue(receipt['msg'].count(issue.getId()))
        
        # do it again and this time configure the receipt to 
        # reveal the issue URL
        ae.reveal_issue_url = True
        
        FakePOP3.files = [abs_path('email-in-8b.email'),]
        result = tracker.check4MailIssues(verbose=False)
        
        assert len(tracker.getIssueObjects()) == 2
        issue2 = tracker.getIssueObjects()[1]
        receipt = self.snatched_emails[1]
        self.assertTrue(receipt['msg'].count(issue2.absolute_url()))
        
        
    def test_emailIn9(self):
        """Test email with content encoding 'unicode-1-1-utf-7'"""
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        ae.send_confirm = True
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        
        # 'email-in-4.email' sends to peter@example.com but is CCed to
        # mail@example.com
        FakePOP3.files = [abs_path('email-in-9.email'),]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3
        
        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue('Saved email' in result)
        
        

        
    def test_emailIn_none(self):
        """ Test emails in multipart """
        tracker = self.folder.tracker
        self._send_in_emails([])

        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 0 issue') > -1)
        
        issues = len(tracker.getIssueObjects())
        self.assertEqual(issues, 0)
        
        
    def test_emailInMultipart(self):
        """ when emailing in an email with multipart text and html, 
        favor the text part.
        """
        tracker = self.folder.tracker
        self._send_in_emails('multipart-email-with-ms.email',
                             accepting_email='ims@issuetrackerproduct.com')
        
        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 1 issue') > -1)
        
        issue = tracker.getIssueObjects()[0]
        description = issue.description
        self.assertTrue('THIS IS THE PLAIN PART' in description)
        display_format = issue.display_format
        self.assertEqual(display_format, 'plaintext')

        
    def _send_in_emails(self, email_filenames, accepting_email='mail@example.com'):
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = accepting_email
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        
        # 'email-in-5.email' is multipart/alternative and the HTML part of it
        # is not quoted.
        
        if not isinstance(email_filenames, (tuple, list)):
            email_filenames = [email_filenames]
        FakePOP3.files = [abs_path(x) for x in email_filenames]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3        

        
    def test_jp_regression_tests(self):
        """ these tests come from a bug report by Jesse.
        """
        tracker = self.folder.tracker
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'helpdesktest@example3.org'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        
        # 'email-in-5.email' is multipart/alternative and the HTML part of it
        # is not quoted.
        FakePOP3.files = [abs_path('jp-0.email'), abs_path('jp-1.email')]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3
        
        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 2 issues') > -1)

        
    def test__appendEmailIssueData(self):
        """ test the private method _appendEmailIssueData() """
        tracker = self.folder.tracker
        
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'helpdesktest@example3.org'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        
        email = {'subject':'Re: Fwd: Foo, High, Bug Report: Subject line',
                 'to':'helpdesktest@example3.org',
                 'from':'test@peterbe.com'
                }
                
        email = tracker._appendEmailIssueData([email], account)[0]
        
        # expect certain things from this email.
        
        # Since none of the sections in this tracker was in the  subject line 
        # expect the sections to be the default
        self.assertEqual(email['sections'], tracker.getDefaultSections())
        
        # The subject line contained some junk ('Fwd: Re:') followed by some
        # sensible parsable stuff where one word is neither section, type 
        # or urgency. Therefor the issue subject must be 'Foo: Subject line'
        self.assertEqual(email['title'], u'Foo: Subject line')
        self.assertTrue(isinstance(email['title'], unicode))
        
        # The urgency is 'High' in the parsable subject line
        self.assertEqual(email['urgency'], u'high')
        
        # And the type is Bug report
        self.assertEqual(email['type'], u'bug report')
        
    

    def test_email_in_without_General_section(self):
        """A stupidity test that checks that it's possible to email in an issue
        into an issuetracker instance that doesn't have of the default sections.
        """
        tracker = self.folder.tracker
        
        tracker.sections_options = [u'Other', u'One']
        
        u, p = 'test', 'test' # doesn't really matter
        account = tracker.createPOP3Account('mail.example.com', u, p)
        email = 'mail@example.com'
        ae = tracker.createAcceptingEmail(account.getId(), email)
        
        abs_path = lambda x: os.path.join(os.path.dirname(__file__), x)
        
        # 'email-in-5.email' is multipart/alternative and the HTML part of it
        # is not quoted.
        FakePOP3.files = [abs_path('no-general.email')]

        # Monkey patch!
        from Products.IssueTrackerProduct import IssueTracker
        IssueTracker.POP3 = FakePOP3
        
        result = tracker.check4MailIssues(verbose=True)
        self.assertTrue(result.find('Created 1 issue') > -1)



def test_suite():
    from unittest import TestSuite, makeSuite
    suite = TestSuite()
    suite.addTest(makeSuite(POP3TestCase))
    suite.addTest(makeSuite(EmailInTestCase))
    return suite
    
if __name__ == '__main__':
    framework()
        

