#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# TabellioOOo - SCGI server providing preview of ODF files
# Copyright (C) 2007-2010  Parlement de la Communauté française de Belgique
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

import sys
import os
import resource
from optparse import OptionParser
from scgi.scgi_server import SCGIServer, SCGIHandler
import time
import syslog
import socket
import tempfile
import zipfile

try:
    import xml.etree.ElementTree as ET
except ImportError:
    import elementtree.ElementTree as ET

def get_legi_metadata(legi_filename):
    try:
        z = zipfile.ZipFile(legi_filename)
    except zipfile.BadZipfile:
        content = file(legi_filename).read()
    else:
        content_zfile = None
        for zfile in z.namelist():
            if zfile == 'contents.xml':
                content_zfile = zfile
                break
        if content_zfile is None:
            return None
        content = z.read(content_zfile)

    content_tree = ET.ElementTree(ET.fromstring(content))
    metadata = {}
    for property in content_tree.findall('metadata/property'):
        metadata[property.attrib.get('name')] = property.text
    return metadata


class PreviewHandler(SCGIHandler):
    debug = False
    daemon = False

    def handle_connection(self, conn):
        input = conn.makefile('r')
        output = conn.makefile('w')
        env = self.read_env(input)
        bodysize = int(env.get('CONTENT_LENGTH', 0))
        try:
            self.produce(env, bodysize, input, output)
        finally:
            output.close()
            input.close()
            conn.close()

    def produce(self, env, bodysize, input, output):
        tmp_work_dir = os.path.join(tempfile.gettempdir(), 'tabellio')
        if not os.path.exists(tmp_work_dir):
            os.makedirs(tmp_work_dir)

        if self.debug:
            print 'Request received at', time.strftime('[%Y-%m-%d %H:%M]')
            print ' - body size:', bodysize

        if bodysize == 0:
            return self.error_page(output, 'this server only supports POST')

        input_file = input.read(bodysize)

        if not os.path.exists('../legi2pdf/script/db2pdf.py'):
            pdf_file = file('unconfigured.pdf').read()
            print >> output, 'Content-type: application/pdf'
            print >> output, 'Content-length: %s' % len(pdf_file)
            print >> output, ''
            print >> output, pdf_file
            return

        if env.get('REQUEST_URI').endswith('/odt'):
            # legi file as input, odt file as output
            legi_filename = tempfile.mkstemp(suffix='.legi', prefix='l2o-', dir=tmp_work_dir)[1]
            fd = file(legi_filename, 'w')
            fd.write(input_file)
            fd.close()
            os.system('python ../legi2odf/legi2odf.py %s' % legi_filename)

            odt_file = file(legi_filename.replace('.legi', '.odt')).read()

            print >> output, 'Content-type: application/octet-stream'
            print >> output, 'Content-Disposition: attachment'
            print >> output, 'Content-length: %s' % len(odt_file)
            print >> output, ''
            output.write(odt_file)
            return

        odf_file = input_file

        odf_filename = tempfile.mkstemp(suffix='.odt', prefix='o2l-', dir=tmp_work_dir)[1]
        if self.debug:
            print ' - storing ODT file as', odf_filename

        legi_filename = odf_filename.replace('.odt', '.legi')
        pdf_filename = odf_filename.replace('.odt', '.pdf')
        fd = file(odf_filename, 'w')
        fd.write(odf_file)
        fd.close()

        if os.system('python ../odf2legi/odf2legi.py %s' % odf_filename):
            if self.daemon:
                syslog.syslog(syslog.LOG_ERR, 'error in odf2legi')
            else:
                print ' E: error in odf2legi'
            return self.error_page(output, 'Error in odf2legi')
        else:
            if env.get('REQUEST_URI').endswith('/legi'):
                legi_file = file(legi_filename).read()
                if self.debug:
                    print 'Serving result as a legi file (%s)' % legi_filename
                    print '  - body size:', len(legi_file)
                print >> output, 'Content-type: application/octet-stream'
                print >> output, 'Content-Disposition: attachment'
                print >> output, 'Content-length: %s' % len(legi_file)
                print >> output, ''
                output.write(legi_file)
                return

            args = ['--debug', '--latex', '--input=%s' % legi_filename]
            metadata = get_legi_metadata(legi_filename)
            if metadata and metadata.get('typedocid') == 'PARCH':
                args.append('--parchemin')
            cmd = ['python', '../legi2pdf/script/db2pdf.py'] + args
            if os.system(' '.join(cmd)):
                if self.daemon:
                    syslog.syslog(syslog.LOG_ERR, 'error in legi2pdf')
                else:
                    print ' E: error in legi2pdf'
                return self.error_page(output, 'Error in legi2pdf')

        if os.path.exists(pdf_filename):
            pdf_file = file(pdf_filename).read()
        else:
            pdf_file = file('error.pdf').read()
        print >> output, 'Content-type: application/pdf'
        print >> output, 'Content-length: %s' % len(pdf_file)
        print >> output, ''
        output.write(pdf_file)

    def error_page(self, output, message):
        print >> output, 'Content-type: text/plain'
        print >> output, ''
        print >> output, message


def main():
    parser = OptionParser()
    parser.add_option('-p', '--port', dest = 'port', type='int', default = 2151)
    parser.add_option('--debug', action = 'store_true', dest = 'debug')
    parser.add_option('-f', '--foreground', dest='foreground', action='store_true')
    parser.add_option('--pid', dest='pid')
    options, args = parser.parse_args()

    # force current directory to match the script, to make sure it finds the
    # various tools (legi2pdf & odf2legi)
    os.chdir(os.path.abspath(os.path.dirname(__file__)))

    if not os.path.exists('../legi2pdf/script/db2pdf.py'):
        print >> sys.stderr, 'W: legi2pdf not found'

    if not os.path.exists('/tmp/tabellio'):
        os.mkdir('/tmp/tabellio')

    if not options.foreground:
        PreviewHandler.daemon = True
        if os.fork():
            os._exit(0)
        os.setsid()
        maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
        if maxfd == resource.RLIM_INFINITY:
            maxfd = 1024
        for fd in range(maxfd):
            try:
                os.close(fd)
            except OSError:
                pass
        os.open('/dev/null', os.O_RDWR)
        os.dup2(0, 1)
        os.dup2(0, 2)
        if os.fork():
            os._exit(0)
        if options.pid:
            file(options.pid, 'w').write(str(os.getpid()))
        syslog.openlog('tabellio-preview')

    PreviewHandler.debug = options.debug
    try:
        SCGIServer(handler_class = PreviewHandler, port = options.port).serve()
    except socket.error:
        if PreviewHandler.daemon:
            syslog.syslog(syslog.LOG_CRIT, 'socket error (another instance is running?)')
        print >> sys.stderr, 'E: socket error (another instance is running?)'
        sys.exit(1)

if __name__ == '__main__':
    main()

