# -*- coding: utf-8 -*-
# MailArchive plugin

from trac.core import *
from trac.env import IEnvironmentSetupParticipant
from trac.Search import ISearchSource, query_to_sql, shorten_result
from trac.web import IRequestHandler
from trac.util import NaivePopen
from StringIO import StringIO
import urllib
import time
import calendar
import re

import mimetypes
import email
#from email.Parser  import Parser 
from email.Header import decode_header
#from email.Utils import collapse_rfc2231_value
import os
import email.Errors
import email.Utils
import mailbox

from trac import util
from trac.wiki import wiki_to_html,wiki_to_oneliner, IWikiSyntaxProvider
from trac.util import  Markup
from trac.web.chrome import add_link, add_stylesheet, INavigationContributor, ITemplateProvider
from trac.attachment import attachment_to_hdf, Attachment,AttachmentModule
import tempfile

from trac.Timeline import ITimelineEventProvider

from trac.mimeview import *

class Timeline(Component):
    implements(ITimelineEventProvider)

    # ITimelineEventProvider methods

    def get_timeline_filters(self, req):
        #if req.perm.has_permission('MILESTONE_VIEW'):
        yield ('mailarchive', self.env.config.get('mailarchive', 'title','MailArchive'))

    def get_timeline_events(self, req, start, stop, filters):
        if 'mailarchive' in filters:
            format = req.args.get('format')
            db = self.env.get_db_cnx()
            cursor = db.cursor()
            cursor.execute("SELECT id,category as mlname,utcdate as localdate,fromname,fromaddr , subject  FROM mailarc "
                           "WHERE utcdate>=%s AND utcdate<=%s ",
                           (start, stop,))
            for id,category,localdate, fromname, fromaddr,subject in cursor:
                if fromname=='':
                    title = Markup('メール <em>%s</em> が %s によって送信されました', '',re.match('(.+?)@',fromaddr).group(1))
                else:
                    title = Markup('メール <em>%s</em> が %s によって送信されました', '',fromname)

                if format == 'rss':
                    href = self.env.abs_href.mailarchive(id)
                    message = wiki_to_html(subject, self.env, db,
                                           absurls=True)
                else:
                    href = self.env.href.mailarchive(id)
                    message = subject
                    #message = wiki_to_oneliner(subject, self.env, db,
                    #                           shorten=True)
                yield 'mailarchive', href, title, localdate, None, message 

class SearchProvider(Component):
    implements(ISearchSource)

    # ISearchProvider methods

    def get_search_filters(self, req):
        #if req.perm.has_permission('MAIL_VIEW'):
        yield ('mailarchive', self.env.config.get('mailarchive', 'title','MailArchive'))


    def get_search_results(self, req, query, filters):
        if 'mailarchive' in filters:
            db = self.env.get_db_cnx()
            sql_query, args = query_to_sql(db, query, 'm1.messageid||m1.subject||m1.fromname||m1.fromaddr||m1.text')
            cursor = db.cursor()
            cursor.execute("SELECT m1.id,m1.subject,m1.fromname,m1.fromaddr,m1.text,m1.utcdate as localdate "
                           "FROM mailarc m1 "
                           "WHERE "
                           "" + sql_query, args)
            for id,subject,fromname,fromaddr, text,localdate in cursor:
                if fromname=='':
                    yield (self.env.href.mailarchive(id),
                           subject,
                           localdate, re.match('(.+?)@',fromaddr).group(1),
                           shorten_result(text, query.split()))
                else:
                    yield (self.env.href.mailarchive(id),
                           subject,
                           localdate, fromname,
                           shorten_result(text, query.split()))

class NavigationContributor(Component):
    implements(INavigationContributor)

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return 'mailarchive'


    def get_navigation_items(self, req):
        #if not req.perm.has_permission('TICKET_CREATE'):
        #    return
        yield ('mainnav', 'mailarchive',
               util.Markup('<a href="%s">%s</a>',
                           self.env.href.mailarchive(),
                           self.env.config.get('mailarchive', 'title','MailArchive')))


class MailArchiveModule(Component):
    implements(ITemplateProvider, 
        IRequestHandler,IEnvironmentSetupParticipant)

    # ITemplateProvider methods
    def get_templates_dirs(self):
        """
        Return the absolute path of the directory containing the provided
        ClearSilver templates.
        """
        from pkg_resources import resource_filename
        return [resource_filename(__name__, 'templates')]

    def get_htdocs_dirs(self):
        """
        Return a list of directories with static resources (such as style
        sheets, images, etc.)

        Each item in the list must be a `(prefix, abspath)` tuple. The
        `prefix` part defines the path in the URL that requests to these
        resources are prefixed with.
        
        The `abspath` is the absolute path to the directory containing the
        resources on the local file system.
        """
        from pkg_resources import resource_filename
        return [('ma', resource_filename(__name__, 'htdocs'))]

    # IRequestHandler methods

    def match_request(self, req):
        match = re.match(r'^/mailarchive(?:/(.*))?', req.path_info)
        if match:
            if match.group(1):
                req.args['messageid'] = match.group(1)
            return 1

    def process_request(self, req):
        db = self.env.get_db_cnx()

        #req.perm.assert_permission('TICKET_CREATE')
        messageid = req.args.get('messageid', '')

        #id = req.args.get('id','')
        action = req.args.get('action', 'list')

        if action == 'import':
            # brefore import lock db , in order to avoid import twice
            self.import_unixmails(req.remote_addr,db)
            # after import unlock db
            return self._render_list(req, db, False,False)
        
        elif messageid != '':
            return self._render_view(req, db, messageid)
        else:
            # did the user ask for any special report?
            return self._render_list(req, db, False,False)

    # IEnvironmentSetupParticipant methods

    ienvironment_log = ""
    def environment_created(self):
        pass

    def environment_needs_upgrade(self, db):
        cursor = db.cursor()
        try:
            cursor.execute("SELECT id FROM mailarc WHERE id='1'")
        except :
            db.rollback()
            self.log.debug('MailArchive environment_needs_upgrade')
            return True
        
        return False
        

    def upgrade_environment(self, db):
        self.log.debug('MailArchive upgrade_environment')

        sql = [
"""
CREATE TABLE mailarc (id integer,category text, messageid text PRIMARY KEY,
 utcdate integer, zoneoffset integer, 
 subject text, fromname text, fromaddr text, header text, text text, 
 threadroot text, threadparent text);
""",
"""
CREATE TABLE mailarc_category ( category text PRIMARY KEY,mlid text ,yearmonth text ,count integer);
""",
        ]

        
        #db = self.env.get_db_cnx()
        cursor = db.cursor()
        for s in sql:
            cursor.execute(s)
            self.log.debug('%s' % s)

        #pass

    def _get_category_href(self,category):
        return self.env.href.mailarchive()+'?category=%s' % category

    def _render_view(self, req, db, id):
        title, description, sql = ('ML名','MLの説明','select * from mailarc')
        req.hdf['mailarc.action'] = 'view'

        target_threadroot = ''
        cursor = db.cursor()
        cursor.execute("SELECT id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,text,threadroot FROM mailarc WHERE id=%s",(id,))

        for id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,text,threadroot in cursor:
            prefix ='mailarc'
            
            #zone and date
            zone = ''
            if zoneoffset == '':
                zoneoffset = 0
            if zoneoffset >0:
                zone = ' +' + time.strftime('%H%M',time.gmtime(zoneoffset))
            elif zoneoffset < 0:
                zone = ' -' + time.strftime('%H%M',time.gmtime(-1*zoneoffset))

            localdate = time.strftime("%Y/%m/%d %H:%M:%S",time.gmtime(utcdate+zoneoffset))

            # from
            fromtext = ''
            fromaddr = fromaddr.replace('@',
                    self.env.config.get('mailarchive', 'replaceat','_at_'))
            if fromname=='':
                fromtext = fromaddr
            else:
                fromtext = '%s (%s)' % (fromname,fromaddr)
        
            #message
            req.hdf[prefix + '.subject'] = subject
            req.hdf[prefix + '.subject.ticket_href'] = self.env.href.mailarchive(id)
            req.hdf[prefix + '.from']=fromtext
            req.hdf[prefix + '.senddate'] = localdate + zone
            req.hdf[prefix + '.messageid'] = messageid
            target_threadroot = threadroot

            text = text.replace('@',self.env.config.get('mailarchive', 'replaceat','_at_') )

            contentlines = text.splitlines()
            htmllines = ['',]
            for line in contentlines:
                if self.env.config.get('mailarchive', 'wikiview','enabled') == 'enabled':
                    htmllines.append(wiki_to_oneliner(line, self.env, db))
                else:
                    htmllines.append(Markup(Markup().escape(line).replace(' ','&nbsp;')))
                
            content = Markup('<br/>').join(htmllines)

            req.hdf[prefix + '.page_html'] = content
        
            break

        # Todo:Raise error when messsageid is wrong.

        # List attached files
        for idx, attachment in util.enum(Attachment.select(self.env, 'mailarchive',
                                                           id)):
            hdf = attachment_to_hdf(self.env, db, req, attachment)
            req.hdf['mailarc.attachments.%s' % idx] = hdf
        #if req.perm.has_permission('TICKET_APPEND'):
        req.hdf['mailarc.attach_href'] = self.env.href.attachment('mailarchive',
                                                                     id)

        if 'mailarc_mails' in req.session:
            self.log.debug(req.session['mailarc_mails'])
            mails = req.session['mailarc_mails'].split()
            if str(id) in mails:
                idx = mails.index(str(id))
                if idx > 0:
                    add_link(req, 'first', self.env.href.mailarchive(mails[0]),
                             'first')
                    add_link(req, 'prev', self.env.href.mailarchive(mails[idx - 1]),
                             'pre')
                if idx < len(mails) - 1:
                    add_link(req, 'next', self.env.href.mailarchive(mails[idx + 1]),
                             'next')
                    add_link(req, 'last', self.env.href.mailarchive(mails[-1]),
                             'last')
                add_link(req, 'up', req.session['mailarc_category_href'])


        if target_threadroot == '':
            target_threadroot = messageid
        
        ref_count=0
        cursor.execute("SELECT id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,text,threadroot FROM mailarc WHERE messageid=%s or threadroot=%s ORDER BY utcdate",(target_threadroot,target_threadroot))
        for ref_id,ref_messageid,utcdate,zoneoffset,subject,fromname,fromaddr,text,threadroot in cursor:
            ref_count = ref_count +1
            prefix = 'mailarc.reflist.items.' + str(ref_id)
            req.hdf[prefix + '.subject'] = subject[:20]
            req.hdf[prefix + '.fromname'] = fromname
            req.hdf[prefix + '.date'] = time.strftime("%Y/%m/%d %H:%M:%S",time.gmtime(utcdate+zoneoffset))
            if messageid == ref_messageid:
                pass
            else:
                req.hdf[prefix + '.href'] = self.env.href.mailarchive(ref_id)
        req.hdf['mailarc.reflist.count'] = ref_count

        add_stylesheet(req, 'ma/css/mailarchive.css')

        return 'maildetail.cs',None

    def month_add(self,year,month,add_month):
        month = month + add_month
        while month >12 or month <1:
            if month > 12:
                month = month - 12
                year = year + 1
            else :
                month = month + 12
                year = year - 1

    # Internal methods
    def _render_list(self, req, db, thread_flag , month):
        target_category = req.args.get('category', '')
        #month = req.args.get('month', '')
        #this_month = time.strftime("%Y/%m/%d %H:%M:%S",time.gmtime(utcdate+zoneoffset))

        ids = ['',]

        title, description, sql = ('ML名','MLの説明','select * from mailarc')
        req.hdf['mailarc.mode'] = 'list'
        mesid_prefix = {}

        cursor = db.cursor()
        cursor.execute("SELECT category,mlid,yearmonth,count FROM mailarc_category ORDER BY mlid,yearmonth  DESC")
        for category,mlid,yearmonth,count in cursor:
            if target_category == '':
                target_category = category 

            prefix = 'mailarc.category.items.'+mlid
            req.hdf[prefix + '.name'] = mlid
            
            prefix = prefix + ".items."+yearmonth
            req.hdf[prefix + '.year'] = yearmonth[:4]
            req.hdf[prefix + '.month'] = yearmonth[4:]
            req.hdf[prefix + '.count'] = str(count)
            if category == target_category:
                req.hdf['mailarc.name'] = mlid
                req.hdf['mailarc.year'] = yearmonth[:4]
                req.hdf['mailarc.month'] = yearmonth[4:]
            else:
                req.hdf[prefix + '.href'] = self._get_category_href(category)

        row_idx = 0
        thread_flag = True
        cursor.execute("SELECT id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,threadparent,threadroot FROM mailarc WHERE category=%s ORDER BY utcdate",(target_category.encode('utf-8'),))
        lastcounts = {}
        lastids = {}
        for id,messageid,utcdate,zoneoffset,subject,fromname,fromaddr,thread_parent,thread_root in cursor:

            prefix = 'mailarc.items'
            pre_prefix = ''
            
            
            ids.append(str(id))
            
            if thread_flag == True:
                parents = thread_parent.split(' ')
                for parent in parents:
                    if mesid_prefix.has_key(parent) == True:
                        self.log.debug('Thread:%s' % parent)
                        prefix,parent_id = mesid_prefix[parent] 
                        prefix = prefix +  '.items'

                        ids.pop()
                        if lastids.has_key(prefix) == True:
                            ids.insert(ids.index(lastids[prefix])+1,str(id))
                        else:
                            ids.insert(ids.index(parent_id)+1,str(id))
                            
                        break

                
            if lastcounts.has_key(prefix) == False:
                lastcounts[prefix] = 0
            else:
                lastcounts[prefix] = lastcounts[prefix] + 1
            
                
            lastids[prefix] = str(id)
            
            prefix = prefix + '.%d' % ( lastcounts[prefix] )
            mesid_prefix[messageid] = prefix,str(id)

            self.log.debug((prefix,messageid,thread_parent,utcdate,zoneoffset,subject,fromname,fromaddr))
            row_idx += 1

            #date
            if zoneoffset == '':
                zoneoffset = 0
            localdate = time.strftime("%Y/%m/%d %H:%M:%S",time.gmtime(utcdate+zoneoffset))
            zone = ''
            if zoneoffset >0:
                zone = ' +' + time.strftime('%H%M',time.gmtime(zoneoffset))
            elif zoneoffset < 0:
                zone = ' -' + time.strftime('%H%M',time.gmtime(-1*zoneoffset))

            #req.hdf[prefix + '.messageid'] = messageid
            req.hdf[prefix + '.attr.subject'] = subject
            req.hdf[prefix + '.attr.subject.mail_href'] = self.env.href.mailarchive(id)
            if fromname=='':
                req.hdf[prefix + '.attr.from'] = re.match('(.+?)@',fromaddr).group(1)
            else:
                req.hdf[prefix + '.attr.from'] = fromname

            req.hdf[prefix + '.attr.senddate'] = localdate + zone
            req.hdf[prefix + '.attr.threadparent'] = thread_parent
            req.hdf[prefix + '.attr.threadroot'] = thread_root
            #ids.append(prefix+':'+str(id))
            self.log.debug('[%s] = %s' % (prefix,subject))

        if req.args.has_key('sort'):
            sortCol = req.args.get('sort')
            colIndex = None
            hiddenCols = 0
            for x in range(len(cols)):
                colName = cols[x][0]
                if colName == sortCol:
                    colIndex = x
                if colName.startswith('__') and colName.endswith('__'):
                    hiddenCols += 1
            if colIndex != None:
                k = 'report.headers.%d.asc' % (colIndex - hiddenCols)
                asc = req.args.get('asc', None)
                if asc:
                    sorter = ColumnSorter(colIndex, int(asc))
                    req.hdf[k] = asc
                else:
                    sorter = ColumnSorter(colIndex)
                    req.hdf[k] = 1
                rows.sort(sorter.sort)

        idstext = ''.join(['%s ' % id for id in ids])
        self.log.debug("Idtext: %s" % idstext)
        req.session['mailarc_mails'] = idstext
        req.session['mailarc_category_href'] = self._get_category_href(target_category)

        req.hdf['mailarc.numrows'] = row_idx

        add_stylesheet(req, 'ma/css/mailarchive.css')

        return 'mailarchive.cs', None

class MailAttachmentModule(AttachmentModule):
    implements(IRequestHandler)

    # IReqestHandler methods

    def match_request(self, req):
        match = re.match(r'^/attachment/(mailarchive)(?:/(.*))?$', req.path_info)
        if match:
            req.args['type'] = match.group(1)
            req.args['path'] = match.group(2)
            return 1

    def process_request(self, req):
        parent_type = req.args.get('type')
        path = req.args.get('path')
        if not parent_type or not path:
            raise TracError('Bad request')
        if not parent_type in ['mailarchive']:
            raise TracError('Unknown attachment type')

        segments = path.split('/')
        parent_id = '/'.join(segments[:-1])
        filename = segments[-1]
        if len(segments) == 1 or not filename:
            raise TracError('Bad request')            
        attachment = Attachment(self.env, parent_type, parent_id, filename)



        self._render_view(req, attachment)

        add_stylesheet(req, 'common/css/code.css')
        return 'attachment.cs', None
        
    def _render_view(self, req, attachment):
        #perm_map = {'ticket': 'TICKET_VIEW', 'wiki': 'WIKI_VIEW'}
        #req.perm.assert_permission(perm_map[attachment.parent_type])

        req.check_modified(attachment.time)

        # Render HTML view
        text, link = self._get_parent_link(attachment)
        add_link(req, 'up', link, text)

        req.hdf['title'] = attachment.title
        req.hdf['attachment'] = attachment_to_hdf(self.env, None, req, attachment)
        req.hdf['attachment.parent'] = {
            'type': attachment.parent_type, 'id': attachment.parent_id,
            'name': text, 'href': link,
        }

        #perm_map = {'ticket': 'TICKET_ADMIN', 'wiki': 'WIKI_DELETE'}
        #if req.perm.has_permission(perm_map[attachment.parent_type]):
        #    req.hdf['attachment.can_delete'] = 1

        fd = attachment.open()
        try:
            mimeview = Mimeview(self.env)
            max_preview_size = mimeview.max_preview_size()
            data = fd.read(max_preview_size)

            mime_type = get_mimetype(attachment.filename) or \
                        'application/octet-stream'
            self.log.debug("Rendering preview of file %s with mime-type %s"
                           % (attachment.filename, mime_type))

            raw_href = attachment.href(format='raw')
            add_link(req, 'alternate', raw_href, 'Original Format', mime_type)
            req.hdf['attachment.raw_href'] = raw_href

            format = req.args.get('format')
            render_unsafe = self.config.getbool('attachment',
                                                'render_unsafe_content')
            binary = not detect_unicode(data) and is_binary(data)

            if format in ('raw', 'txt'): # Send raw file
                if not render_unsafe and not binary:
                    # Force browser to download HTML/SVG/etc pages that may
                    # contain malicious code enabling XSS attacks
                    req.send_header('Content-Disposition', 'attachment;' +
                                    'filename=' + attachment.filename)
                charset = mimeview.get_charset(data, mime_type)
                if render_unsafe and not binary and format == 'txt':
                    mime_type = 'text/plain'
                req.send_file(attachment.path,
                              mime_type + ';charset=' + charset)

            if render_unsafe and not binary:
                add_link(req, 'alternate', attachment.href(format='txt'),
                         'Plain Text', mime_type)

            hdf = mimeview.preview_to_hdf(req, mime_type, None, data,
                                          attachment.filename, None,
                                          annotations=['lineno'])
            req.hdf['attachment'] = hdf
        finally:
            fd.close()
                    