# IssueTrackerProduct
#
# Peter Bengtsson <mail@peterbe.com>
# License: ZPL
#

"""
Email is for accepting inbound emails into the issue tracker
"""

# python
import sys, os, re

# Zope
from OFS import SimpleItem
from AccessControl import ClassSecurityInfo

try:
    # >= Zope 2.12
    from App.class_init import InitializeClass
except ImportError:
    # < Zope 2.12
    from Globals import InitializeClass

# Product
import IssueTracker
from Permissions import *
from Constants import *
from Utils import ss



class POP3Account(IssueTracker.IssueTrackerFolderBase):
    """ POP3Account class """
    
    meta_type = POP3ACCOUNT_METATYPE
    icon = '%s/issuetracker_pop3account.gif'%ICON_LOCATION

    _properties = ({'id':'hostname',     'type': 'string',  'mode':'w'},
                   {'id':'portnr',       'type': 'int',     'mode':'w'},
                   {'id':'username',     'type': 'string',  'mode':'w'},
                   {'id':'delete_after', 'type': 'boolean', 'mode':'w'},
                   {'id':'ssl', 'type': 'boolean', 'mode':'w'},
                   )

    # for legacy reasons
    delete_after = True
    ssl = False
    
    security = ClassSecurityInfo()

    # default
    portnr = 110
    
    def __init__(self, id, hostname, username, password, portnr=110,
                 delete_after=False, ssl=False):
        """ init """
        self.id = str(id)
        self.hostname = str(hostname)
        self.portnr = int(portnr)
        self.username = str(username)
        self._password = str(password)
        self.delete_after = bool(delete_after)
        self.ssl = bool(ssl)
        
    def getTitle(self):
        """ return hostname for title """
        return self.hostname
        
    def getId(self):
        """ return id """
        return self.id
    
    def getHostname(self):
        """ return hostname """
        return self.hostname

    def getPort(self):
        """ return portnr """
        return getattr(self, 'portnr', 110)
    
    def getUsername(self):
        """ return username """
        return self.username
    
    def doDeleteAfter(self):
        """ return delete_after """
        return self.delete_after

    def doSSL(self):
        """ return ssl """
        return self.ssl
    
    def getAcceptingEmails(self, ids=0):
        """ return accepting email objects here """
        if ids:
            return self.objectIds(ACCEPTINGEMAIL_METATYPE)
        else:
            return self.objectValues(ACCEPTINGEMAIL_METATYPE)
        
    def doSendConfirmSuggestion(self):
        """ return true if most accepting emails herein use doSendConfirm """
        total = 0
        for ae in self.getAcceptingEmails():
            if ae.doSendConfirm():
                total += 1
            else:
                total -= 1
                
        # since default is True, be more lenient to total=0
        return total >= 0
        
    def getAcceptingEmailbyTo(self, to, default=None):
        """ which accepting email here has 'to' 
        for email_address """
        to = to.lower().strip()
        for object in self.getAcceptingEmails():
            if object.email_address.lower().strip() == to:
                return object
        else:
            return default
        
    security.declareProtected(VMS, 'editAccount')
    def editAccount(self, hostname=None, portnr=None,
                    username=None, password=None, delete_after=False):
        """ old name """
        import warnings 
        warnings.warn("editAccount() is an old name. Use manage_editAccount() instead",
                      DeprecationWarning, 2)
        return self.manage_editAccount(hostname, portnr, username, password, delete_after)

    
    security.declareProtected(VMS, 'manage_editAccount')
    def manage_editAccount(self, hostname=None, portnr=None,
                    username=None, password=None, delete_after=False, ssl=False):
        """ change the attributes """
        if hostname is not None:
            self.hostname = hostname
        if portnr is not None:
            self.portnr = portnr
        if username is not None:
            self.username = username
        if password is not None:
            self._password = password
            
        self.delete_after = bool(delete_after)
        self.ssl = bool(ssl)
            
        
    security.declareProtected(VMS, 'createAcceptingEmail')
    def createAcceptingEmail(self, id, email_address, defaultsections,
                             default_type, default_urgency,
                             send_confirm, reveal_issue_url):
        """ create object and return it """
        
        acceptingemail = AcceptingEmail(id, email_address, defaultsections,
                                        default_type, default_urgency,
                                        send_confirm,
                                        reveal_issue_url=reveal_issue_url)
        self._setObject(id, acceptingemail)
        return getattr(self, id)
    
        
InitializeClass(POP3Account)



class AcceptingEmail(SimpleItem.SimpleItem,
                     IssueTracker.IssueTrackerFolderBase):
    """ AcceptingEmail class """

    meta_type = ACCEPTINGEMAIL_METATYPE
    icon = '%s/issuetracker_acceptingemail.gif'%ICON_LOCATION
    
    meta_types = []

    _properties=({'id':'email_address',   'type': 'string', 'mode':'w'},
                 {'id':'defaultsections', 'type': 'lines',  'mode':'w'},
                 {'id':'default_type',    'type': 'string', 'mode':'w'},
                 {'id':'default_urgency', 'type': 'string', 'mode':'w'},
                 {'id':'send_confirm',    'type': 'boolean','mode':'w'},
                 {'id':'whitelist_emails','type': 'lines',  'mode':'w'},
                 {'id':'blacklist_emails','type': 'lines',  'mode':'w'},
                 {'id':'reveal_issue_url','type': 'lines',  'mode':'w'},
                 )
    
    security=ClassSecurityInfo()

    
    def __init__(self, id, email_address, defaultsections,
                 default_type, default_urgency, send_confirm=True,
                 reveal_issue_url=True):
        """ init """
        self.id = str(id)
        self.email_address = str(email_address)
        if type(defaultsections) == type('s'):
            defaultsections = [defaultsections]
        self.defaultsections = defaultsections
        self.default_type = str(default_type)
        self.default_urgency = str(default_urgency)
        self.send_confirm = bool(send_confirm)
            
        self.whitelist_emails = []
        self.blacklist_emails = []
        self.reveal_issue_url = bool(reveal_issue_url)

    def getId(self):
        """ return id """
        return self.id
    
    def getTitle(self):
        """ return email_address """
        return self.getEmailAddress()
    
    def getEmailAddress(self):
        """ return email_address """
        return self.email_address
    
    def doSendConfirm(self):
        """ return send_confirm """
        return getattr(self, 'send_confirm', False)
    
    def getWhitelistEmails(self):
        """ return whitelist_emails """
        return getattr(self, 'whitelist_emails', [])
    
    def getBlacklistEmails(self):
        """ return blacklist_emails """
        return getattr(self, 'blacklist_emails', [])

    def revealIssueURL(self):
        """ return if the URL of the issue should be revealed in the
        email coming back. 
        Since this attribute was introduced late, we have to assume 
        that the attribute isn't always available. 
        """
        default = True
        return getattr(self, 'reveal_issue_url', default)
    
    def acceptOriginatorEmail(self, email, default_accept=True):
        """ return true if this email is either whitelisted or 
        not blacklisted """
        whitelist = self.getWhitelistEmails()
        blacklist = self.getBlacklistEmails()
        
        # note the order 
        for reject, emaillist in ([False, whitelist], [True, blacklist]):
            for okpattern in emaillist:
                _okpattern = re.compile(okpattern.replace('*','\S+'), re.I)
                if _okpattern.findall(email):
                    # match!
                    if reject:
                        return False
                    else:
                        return True
                   
        # default is to accept all
        return default_accept
                    
    
    def editDetails(self, email_address=None, defaultsections=None,
                    default_type=None, default_urgency=None,
                    send_confirm=None,
                    reveal_issue_url=None,
                    whitelist_emails=None,
                    blacklist_emails=None):
        """ edit details if not None """
        if email_address is not None:
            self.email_address = email_address
        if defaultsections is not None:
            if type(defaultsections) == type('s'):
                defaultsections = [defaultsections]
            self.defaultsections = defaultsections
        if default_type is not None:
            self.default_type = default_type
        if default_urgency is not None:
            self.default_urgency = default_urgency
        if send_confirm is not None:
            self.send_confirm = bool(send_confirm)
        if reveal_issue_url is not None:
            self.reveal_issue_url = bool(reveal_issue_url)
        if whitelist_emails is not None:
            self.whitelist_emails = self._tidyEmailList(whitelist_emails)
        if blacklist_emails is not None:
            self.blacklist_emails = self._tidyEmailList(blacklist_emails)
            
    def _tidyEmailList(self, emaillist):
        """ only accept either valid email addresses or
        those with * in them. """
        emaillist = [x.strip() for x in emaillist if x.strip()]
        for e in emaillist:
            if e.find('**') > -1:
                m = "Email wildcard repeated excessively %s"
                raise ValueError, m % e
            elif e.find(' ') > -1:
                m = "Email wildcard contains whitespace %r"
                raise ValueError, m % e
        return emaillist

        
    
    def assertAllProperties(self):
        """ make sure accepting email has all properties """
        props = {'email_address':'', 'defaultsections':[], 
                 'default_type':self.default_type,
                 'default_urgency':self.default_urgency,
                 'send_confirm':1,
                 'whitelist_emails':[],
                 'blacklist_emails':[],
                 }
                 
        count = 0
        for key, default in props.items():
            if not self.__dict__.has_key(key):
                self.__dict__[key] = default
                count += 1
        return count    
    
InitializeClass(AcceptingEmail)
