#!/usr/bin/python

# Copyright (C) 2011 Javier Palacios
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License Version 2
# as published by the Free Software Foundation.
# 
# 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.


__version__ = "1.0"

__usage__ = """%prog [options]"""


from BaseHTTPServer import BaseHTTPRequestHandler , HTTPServer

import os
import repolib
import repolib.cache


class ServerConf ( dict ) :

    def __init__ ( self ) :
        dict.__init__ ( self )

    def append ( self , reporoot , mirror_class=None , source=None ) :
        key = os.path.basename( reporoot )
        if mirror_class :
          url = source
          if key in self.keys() :
            if url != self.source_url(key) :
                repolib.logger.warning( "Directory %s : mismatching urls %s & %s" % ( key , self.source_url(key) , url ) )
            return
          # FIXME : this check is probably redundant
          if not url.endswith('/') :
            repolib.logger.warning( "Repo %s : Fix configuration, url should have a trailing '/'" % key )
            url += "/"
        else :
            reporoot = source
            url = None
        self[key] = reporoot , url

    def __getitem__ ( self , key ) :
        return dict.__getitem__( self , key )[0]

    def source_url ( self , key ) :
        return dict.__getitem__( self , key )[1]

    def read ( self ) :
        for repoconf in repolib.config.get_all_build_configs() :
            try :
                if repoconf['type'] == "snapshot" :
                    repolib.logger.warning( "Repo %s : snapshot repositories not yet supported" % repoconf.name )
                    continue
                repo = repolib.BuildRepository.new( repoconf.name )
                self.append( repoconf.name , source=repo.repo_path() )
            except Exception , ex :
                repolib.logger.warning( "Repo %s : %s" % ( repoconf.name , ex ) )

        for repoconf in repolib.config.get_all_mirror_configs( "class" , "passthru" ) :
            try :
                repo = repolib.MirrorRepository.new( repoconf.name )
                self.append( repo.repo_path() , "passthru" )
            except Exception , ex :
                repolib.logger.warning( "Repo %s : %s" % ( repoconf.name , ex ) )

        for repoconf in repolib.config.get_all_mirror_configs( "class" , "cache" ) :
            try :
                repo = repolib.MirrorRepository.new( repoconf.name )
                self.append( repo.repo_path() , "cache" , repo.base_url() )
            except Exception , ex :
                repolib.logger.warning( "Repo %s : %s" % ( repoconf.name , ex ) )

    def dump ( self , key ) :
        out = [ '' ]
        out.append( 'Alias %s/%s "%s"' % ( repolib.config.web['uri'] , key , self[key] ) )
        out.append( '<Directory "%s">' % self[key] )
        out.append( '   Options Indexes MultiViews' )
        url = self.source_url( key )
        if url :
            out.append( '    SetHandler python-program' )
            out.append( '    PythonOption source_url %s' % url )
            out.append( '    PythonHandler repolib.cache' )
        out.append( '</Directory>' )
        out.append( '' )
        return "\n".join( out )


server_conf = ServerConf()

class Handler ( BaseHTTPRequestHandler ) :

    status = None
    content_type = None

    def sendfile ( self , path ) :
        fd = open( path )
        self.wfile.write( fd.read() )
        fd.close()

    def write ( self , msg ) :
        self.wfile.write( msg )

    def log_error ( self , msg , severity=repolib.cache.apache.APLOG_ERROR ) :
        BaseHTTPRequestHandler.log_error( self , "%s : %s" % ( severity , msg ) )

    def do_GET ( self ) :

        repo , uri = os.path.normpath(self.path).strip('/').split('/',1)

        if not server_conf.has_key( repo ) :
            self.status = 404
            self.log_error( "Unknown repo '%s'" % repo )

        else :

            local_path = os.path.join( server_conf[repo] , uri )

            source_url = server_conf.source_url( repo )
            if source_url :

                remote_url = repolib.urljoin( source_url , uri )
                retcode = repolib.cache.get_file( self , local_path , remote_url )

                if retcode :
                    if self.status and self.status != retcode :
                        self.log_error( "Return code '%s' didn't match status '%s'" % ( retcode , self.status ) )
                    self.status = retcode

            else :
                if os.path.isfile( local_path ) :
                    self.status = 200
                    self.sendfile( local_path )
                else :
                    self.status = 404
                    self.log_error( "%s not found" % self.path )

        self.send_response( self.status )

        if self.content_type :
            self.send_header( "Content-Type" , self.content_type )

        self.end_headers()


import optparse

def option_parser () :
    version_string = "%%prog %s" % __version__
    parser = optparse.OptionParser( usage=__usage__ , version=version_string )
    parser.set_defaults( port = 8080 )
    parser.set_defaults( outfile = os.path.join( repolib.config.web['conf'] , "conf.d" , "repomirror.conf" ) )
    parser.add_option( "--port" , type=int ,
        help="listen port for server (defaults 8080)" )
    parser.add_option( "--apache" , action="store_true" ,
        help="dump apache configuration" )
    parser.add_option( "--outfile" , metavar="FILENAME" ,
        help="file for configuration dump [defaults to ServerRoot+conf.d/repomirror.conf]" )
    return parser


def main ( opts ) :
    if opts.apache :
        fd = open( opts.outfile , "w" )
        for k in server_conf :
            fd.write( server_conf.dump(k) )
        fd.close()
    else :
      try :
        server = HTTPServer( ('',opts.port) , Handler )
        server.serve_forever()
      except KeyboardInterrupt , ex :
        server.socket.close()

if __name__ == "__main__" :

    server_conf.read()

    parser = option_parser()
    opts , args = parser.parse_args()

    if args :
        parser.print_help()
        sys.exit(1)

    main( opts )

