#! /usr/bin/python -B
# -*- coding: utf-8 -*-

'''
ubootshell allow you to discuss with the netconsol of u-boot.
'''

# Author:     Maxime Hadjinlian (C) 2013
#             maxime.hadjinlian@gmail.com
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from math import floor
import logging
import os
import readline
# for history and elaborate line editing when using raw_input
# see http://docs.python.org/library/functions.html#raw_input
from select import select
import socket
from struct import pack
import sys
from time import sleep
sys.dont_write_bytecode = True

from network import iface_info, find_free_ip, is_valid_mac, is_valid_ipv4, ipcomm_info

class Ubootshell(object):
    '''
    An instance of UBootshell is a session with the netconsole shell.
     '''

    def __init__(self):
        '''Sets some defaults'''

        self.ip_target = None
        self.bcast_addr = None
        self.mac_target = None
        self.send_port = 4446
        self.receive_port = 4445
        self.uboot_port = 6666
        self.lump_timeout = 120
        self.script = None
        self.do_wait = False
        self.progress = False
        self.debug = False

    def load_script(self, path_file):
        '''
        If we don't want an interactive shell, but a script to be executed.
        '''
        if not os.path.exists(path_file):
            logging.error("%s does not exists." % path_file)
        self.script = path_file

    def do_progress(self, new_progress):
        '''
        If set to true, we will print a progress bar instead of the output.
        '''
        self.progress = new_progress

    # Output example: [=======   ] 75%
    # width defines bar width
    # percent defines current percentage
    def print_progress(self, width, percent):
        marks = floor(width * (percent / 100.0))
        spaces = floor(width - marks)

        loader = '[' + ('=' * int(marks)) + (' ' * int(spaces)) + ']'

        sys.stdout.write("%s %d%%\r" % (loader, percent))
        if percent >= 100:
            sys.stdout.write("\n")
        sys.stdout.flush()

    def wait_at_reboot(self, wait):
        '''
        Set to true, we will wait for the device to reboot completely
        '''
        self.do_wait = wait

    def setup_network(self, net_dict):
        '''
        Give a dict with the following values to setup your network :
        {
            'iface': 'ethX'  # default : eth0
            'bcast_addr' : '255.255.255.0'  # The broadcast address if you need to set it
            'mac_target' : '00:00:00:00:00:00'  # The mac of your product
            'ip_target' : '192.168.1.1'  # The ip to assign to the product
        }
        '''

        if ('mac_target' not in net_dict) or (net_dict['mac_target'] is None):
            logging.info("WARNING : The first product to reboot will be catched !")
            logging.info("It may not be yours if multiple product reboot at the "
                         "same time on your network.")
            net_dict['mac_target'] = "00:00:00:00:00:00"

        try:
            ip, mac, netmask, bcast = iface_info(net_dict['iface'])
        except (IOError, TypeError):
            logging.error("Your network interface is not reachable."
                          " Is %s correct ?" % net_dict['iface'])
            return 1

        # This IP is used afterwards when TFTP'ing files
        if ('ip_target' not in net_dict) or (net_dict['ip_target'] is None):
            if sys.platform == "darwin":
                logging.error("You need to specify an IP to assign to the device.")
                return 1
            net_dict['ip_target'] = find_free_ip(net_dict['iface'], ip, mac, netmask)

        # Check MAC and IP value.
        if not is_valid_mac(net_dict['mac_target']):
            logging.error("Your MAC address is not in the proper format."
                          "\'00:00:00:00:00:00\' format is awaited."
                          "You gave %s" % net_dict['mac_target'])
            return 1
        self.mac_target = net_dict['mac_target']

        if not is_valid_ipv4(bcast):
            logging.error("Your Broadcast IP is not in the proper format."
                          "\'W.X.Y.Z\' format is awaited."
                          "You gave %s" % bcast)
            return 1
        self.bcast_addr = bcast

        if not is_valid_ipv4(net_dict['ip_target']):
            logging.error("Your product IP is not in the proper format."
                          "\'W.X.Y.Z\' format is awaited."
                          "You gave %s" % net_dict['ip_target'])
            return 1
        self.ip_target = net_dict['ip_target']

    def send_lump(self):
        '''
        It will ask the users to reboot the target manually and then
        it will send LUMP packet to a target during 60s.
        '''

        # Create an array with 6 cases, each one is a member (int) of the MAC
        fields_macdest = [int(x, 16) for x in self.mac_target.split(':')]

        # Create an array with 4 cases, each one is a member (int) of the IP
        fields_ip = [int(x) for x in self.ip_target.split('.')]

        # Note : The empty MAC are 8 bytes in length according to the reverse
        # engineering done with WireShark. Don't know why exactly...
        pkt = pack('!I'   # LUMP
                          'L'    # Length of LUMP
                          'I'    # MACD
                          'L'    # Length of MACD
                          'I'    # MAC@
                          'L'    # Length of MAC@ field
                          '2x'   # fill space because MAC take only 6 bytes
                          '6s'   # MAC address of target
                          'I'    # IPS
                          'L'    # Length of IPS
                          'I'    # IP@
                          'L'    # Length of IP@
                          '4s'   # IP of the target
                          'I'    # MACS
                          'L'    # Length of MACS
                          'I'    # MAC address of source
                          'L'    # Length of MAC@
                          '8x',  # Empty MAC
                          0x4C554D50,  # LUMP
                          0x44,
                          0x4D414344,  # MACD
                          0x10,
                          0x4D414340,  # MAC
                          0x8,
                          pack('!6B', *fields_macdest),  # int[] -> byte[]
                          0x49505300,  # IPS
                          0x0C,
                          0x49504000,  # IP
                          0x4,
                          pack('!4B', *fields_ip),  # int[] -> byte[]
                          0x4D414353,  # MACS
                          0x10,
                          0x4D414340,  # MAC
                          0x8)

        logging.debug("Sending some LUMP / Ctrl-C, "
                      "waiting for the NAS to start up")
        logging.info("Please /!\HARD/!\ reboot the device /!\NOW/!\ ")

        timeout = 0
        socket.setdefaulttimeout(60)
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

        try:
            sock.bind(('', self.uboot_port))
        except socket.error, err:
            logging.error("Couldn't be a udp server on port %d : %s",
                          self.uboot_port, err)
            sock.close()
            return None

        lump_ok = False
        while lump_ok is False and timeout < self.lump_timeout:
            sock.sendto(pkt, (self.bcast_addr, self.send_port))
            sleep(0.2)  # Wait for the device to process the LUMP
            #Send Ctrl-C (Code ASCII 3 for EXT equivalent of SIGINT for Unix)
            sock.sendto('\3', (self.bcast_addr, self.uboot_port))
            srecv = select([sock], [], [], 1)
            # data
            if not srecv[0]:
                continue
            try:
                serv_data = sock.recvfrom(1024)
                if serv_data[1][0] != self.ip_target:
                    continue
                serv_data = serv_data[0]
                # check when prompt (Marvell>>) is available,
                # then out to the next while to input command and send them !
                if "Marvell>> " == serv_data:
                    lump_ok = True
                    break
            except (socket.error, KeyboardInterrupt, SystemExit):
                return None
            timeout += 1

        if timeout >= self.lump_timeout:
            logging.debug("Sending LUMP for %ds, no response !",
                           self.lump_timeout)
            lump_ok = False

        sock.close()
        return lump_ok

    def invoke(self, cmd, display=True):
        '''
        send a cmd
        '''

        # Empty command, nothing to do here
        if cmd == "":
            return 42

        exit_list = ['exit', 'reset']
        override = 'Override Env parameters? (y/n)'
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        if cmd in exit_list:
            cmd = 'reset'
            cmd = pack('!' + str(len(cmd)) + 's1s', cmd, '\x0A')
            sock.sendto(cmd, (self.ip_target, self.uboot_port))
            sock.close()
            return 0

        sock.settimeout(10.0)
        try:
            sock.bind(('', self.uboot_port))
        except socket.error, err:
            logging.error("Can't open %d port. (Error : %s)",
                self.receive_port, err)
            sock.close()
            return 0

        #we want to send a cmd to the nas and get the reply in ans
        #every command is completed by \n !
        command = pack('!' + str(len(cmd)) + 's1s', cmd, '\x0A')
        sock.sendto(command, (self.ip_target, self.uboot_port))
        prompt = False
        len_command = 0
        # Don't try to wait for a prompt with bootm
        if cmd == 'bootm':
            sock.close()
            return 42

        while prompt is False:
            srecv = select([sock], [], [], 0.5)
            # data
            if not srecv[0]:
                continue
            try:
                data = sock.recvfrom(1024)
                if data[1][0] != self.ip_target:
                    continue
                recv_data = data[0]
                # check when prompt (Marvell>>) is available,
                if ("Marvell>> " == recv_data or override == recv_data):
                    if override == recv_data:
                        print recv_data
                    prompt = True
                # When sending a command U-Boot return the commands
                # char by char, we do this so we don't display it.
                elif len_command < len(command):
                    len_command += 1
                else:
                    # to handle the printenv and other case
                    # when answer is given one letter at a time...
                    if display:
                        write = sys.stdout.write
                        write(str(recv_data))
                        sys.stdout.flush()
            except (socket.error, KeyboardInterrupt, SystemExit) as err:
                if self.debug:
                    logging.error("Sending command %s on %d : %s",
                        cmd, self.receive_port, err)
        sock.close()
        return 42

    def run(self):
        '''
        Either we execute the script or we create an interactive shell
        to the netconsole.
        '''

        if not self.send_lump():
            logging.debug("LUMP was not sent/receveid by the target")
            return 1

        if self.script is not None:
            with open(self.script, 'r+') as script:
                script_cmd = script.readlines()

            if self.progress:
                # setup progress_bar
                p_width = 60
                p_pas = p_width / len(script_cmd)
                p_percent = 0

            for cmd in script_cmd:

                if self.progress:
                    # update the bar
                    self.print_progress(p_width, p_percent)
                    p_percent += p_pas

                if cmd == '\n' or cmd.startswith('#'):
                    continue

                if not self.progress:
                    print cmd.strip() + " => ",

                self.invoke(cmd.strip(), display=not self.progress)
                sleep(1)  # it seems uboot doesn't like being shaked a bit

            if self.progress:
                self.print_progress(p_width, 100)

            # You can't wait if there is no MAC.
            if self.do_wait and (self.mac_target != "00:00:00:00:00:00"):
                # Some command output may be stuck in the pipe
                sys.stdout.flush()
                # WAIT FOR THE DEVICE TO BOOT
                logging.info("Waiting for your product to reboot...")
                sleep(60 * 7)  # Wait 7mn, it should give the device time to boot.
                # This is done to avoid spamming the network with packet while we are sure it is not necessary.
                ip = ipcomm_info(self.receive_port, self.mac_target, self.ip_target)
                if ip is None:
                    logging.info("Timeout : Unable to get your product IP.")
                    return 1
                logging.info("Your product is available at %s" % ip)
            return 0

        exit_code = 42
        while(exit_code):
            exit_code = self.invoke(raw_input("Marvell>> "), display=True)
        return 0

def main():
    ''' launch everything '''

    import argparse
    from argparse import RawTextHelpFormatter

    parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter)
    parser.add_argument('script', metavar='file', type=str, nargs='?',
                       help=argparse.SUPPRESS)
    parser.add_argument("-m", "--mac", dest="mac", action="store",
                      default=None,
                      help="Address MAC of the targeted device "
                      "(00:00:00:00:00:00)"
                      )
    parser.add_argument("-i", "--iface", dest="iface", action="store",
                      default="eth0",
                      help="Interface to use to send LUMP packet to.\n"
                      "Default is eth0.\n"
                      )
    parser.add_argument("--ip", dest="force_ip", action="store",
                      default=None,
                      help="Specify the IP address to assign to the device."
                      )
    parser.add_argument("-p", "--progress", dest="progress",
                      action="store_const", default=False, const=True,
                      help="Print a pretty progress bar,"
                      " use with a script shebang only.")
    parser.add_argument("-w", "--wait", dest="wait", action="store_const",
                      default=False, const=True,
                      help="Wait for the product to boot.\n"
                      "Note : Require the -m/--mac option to be set.\n")
    parser.add_argument("-D", "--debug", dest="loglevel", action="store_const",
                      const=logging.DEBUG, help="Output debugging information")

    session = Ubootshell()

    if '-D' in sys.argv or '--debug' in sys.argv:
        session.debug = True
        logging.basicConfig(level=logging.DEBUG, format='%(message)s')
    else:
        logging.basicConfig(level=logging.INFO, format='%(message)s')

    options = parser.parse_args()

    setup = {'mac_target': options.mac, 'iface': options.iface}
    if options.force_ip is not None:
        setup['ip_target'] = options.force_ip

    if session.setup_network(setup):
        return 1

    session.wait_at_reboot(options.wait)
    session.do_progress(options.progress)

    if options.script is not None and os.path.isfile(options.script):
        session.load_script(options.script)

    session.run()

    return 0

if __name__ == '__main__':
    if sys.platform != "win32":
        if os.geteuid() != 0:
            print "You must be administrator/root to run this program."
            sys.exit(1)

    try:
        sys.exit(main())
    except (KeyboardInterrupt, EOFError, SystemExit, KeyError):
        pass

#! /usr/bin/python -B
# -*- coding: utf-8 -*-

'''
ubootshell allow you to discuss with the netconsol of u-boot.
'''

# Author:     Maxime Hadjinlian (C) 2013
#             maxime.hadjinlian@gmail.com
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from math import floor
import logging
import os
import readline
# for history and elaborate line editing when using raw_input
# see http://docs.python.org/library/functions.html#raw_input
from select import select
import socket
from struct import pack
import sys
from time import sleep
sys.dont_write_bytecode = True

from network import iface_info, find_free_ip, is_valid_mac, is_valid_ipv4, ipcomm_info

class Ubootshell(object):
    '''
    An instance of UBootshell is a session with the netconsole shell.
     '''

    def __init__(self):
        '''Sets some defaults'''

        self.ip_target = None
        self.bcast_addr = None
        self.mac_target = None
        self.send_port = 4446
        self.receive_port = 4445
        self.uboot_port = 6666
        self.lump_timeout = 120
        self.script = None
        self.do_wait = False
        self.progress = False
        self.debug = False

    def load_script(self, path_file):
        '''
        If we don't want an interactive shell, but a script to be executed.
        '''
        if not os.path.exists(path_file):
            logging.error("%s does not exists." % path_file)
        self.script = path_file

    def do_progress(self, new_progress):
        '''
        If set to true, we will print a progress bar instead of the output.
        '''
        self.progress = new_progress

    # Output example: [=======   ] 75%
    # width defines bar width
    # percent defines current percentage
    def print_progress(self, width, percent):
        marks = floor(width * (percent / 100.0))
        spaces = floor(width - marks)

        loader = '[' + ('=' * int(marks)) + (' ' * int(spaces)) + ']'

        sys.stdout.write("%s %d%%\r" % (loader, percent))
        if percent >= 100:
            sys.stdout.write("\n")
        sys.stdout.flush()

    def wait_at_reboot(self, wait):
        '''
        Set to true, we will wait for the device to reboot completely
        '''
        self.do_wait = wait

    def setup_network(self, net_dict):
        '''
        Give a dict with the following values to setup your network :
        {
            'iface': 'ethX'  # default : eth0
            'bcast_addr' : '255.255.255.0'  # The broadcast address if you need to set it
            'mac_target' : '00:00:00:00:00:00'  # The mac of your product
            'ip_target' : '192.168.1.1'  # The ip to assign to the product
        }
        '''

        if ('mac_target' not in net_dict) or (net_dict['mac_target'] is None):
            logging.info("WARNING : The first product to reboot will be catched !")
            logging.info("It may not be yours if multiple product reboot at the "
                         "same time on your network.")
            net_dict['mac_target'] = "00:00:00:00:00:00"

        try:
            ip, mac, netmask, bcast = iface_info(net_dict['iface'])
        except (IOError, TypeError):
            logging.error("Your network interface is not reachable."
                          " Is %s correct ?" % net_dict['iface'])
            return 1

        # This IP is used afterwards when TFTP'ing files
        if ('ip_target' not in net_dict) or (net_dict['ip_target'] is None):
            if sys.platform == "darwin":
                logging.error("You need to specify an IP to assign to the device.")
                return 1
            net_dict['ip_target'] = find_free_ip(net_dict['iface'], ip, mac, netmask)

        # Check MAC and IP value.
        if not is_valid_mac(net_dict['mac_target']):
            logging.error("Your MAC address is not in the proper format."
                          "\'00:00:00:00:00:00\' format is awaited."
                          "You gave %s" % net_dict['mac_target'])
            return 1
        self.mac_target = net_dict['mac_target']

        if not is_valid_ipv4(bcast):
            logging.error("Your Broadcast IP is not in the proper format."
                          "\'W.X.Y.Z\' format is awaited."
                          "You gave %s" % bcast)
            return 1
        self.bcast_addr = bcast

        if not is_valid_ipv4(net_dict['ip_target']):
            logging.error("Your product IP is not in the proper format."
                          "\'W.X.Y.Z\' format is awaited."
                          "You gave %s" % net_dict['ip_target'])
            return 1
        self.ip_target = net_dict['ip_target']

    def send_lump(self):
        '''
        It will ask the users to reboot the target manually and then
        it will send LUMP packet to a target during 60s.
        '''

        # Create an array with 6 cases, each one is a member (int) of the MAC
        fields_macdest = [int(x, 16) for x in self.mac_target.split(':')]

        # Create an array with 4 cases, each one is a member (int) of the IP
        fields_ip = [int(x) for x in self.ip_target.split('.')]

        # Note : The empty MAC are 8 bytes in length according to the reverse
        # engineering done with WireShark. Don't know why exactly...
        pkt = pack('!I'   # LUMP
                          'L'    # Length of LUMP
                          'I'    # MACD
                          'L'    # Length of MACD
                          'I'    # MAC@
                          'L'    # Length of MAC@ field
                          '2x'   # fill space because MAC take only 6 bytes
                          '6s'   # MAC address of target
                          'I'    # IPS
                          'L'    # Length of IPS
                          'I'    # IP@
                          'L'    # Length of IP@
                          '4s'   # IP of the target
                          'I'    # MACS
                          'L'    # Length of MACS
                          'I'    # MAC address of source
                          'L'    # Length of MAC@
                          '8x',  # Empty MAC
                          0x4C554D50,  # LUMP
                          0x44,
                          0x4D414344,  # MACD
                          0x10,
                          0x4D414340,  # MAC
                          0x8,
                          pack('!6B', *fields_macdest),  # int[] -> byte[]
                          0x49505300,  # IPS
                          0x0C,
                          0x49504000,  # IP
                          0x4,
                          pack('!4B', *fields_ip),  # int[] -> byte[]
                          0x4D414353,  # MACS
                          0x10,
                          0x4D414340,  # MAC
                          0x8)

        logging.debug("Sending some LUMP / Ctrl-C, "
                      "waiting for the NAS to start up")
        logging.info("Please /!\HARD/!\ reboot the device /!\NOW/!\ ")

        timeout = 0
        socket.setdefaulttimeout(60)
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

        try:
            sock.bind(('', self.uboot_port))
        except socket.error, err:
            logging.error("Couldn't be a udp server on port %d : %s",
                          self.uboot_port, err)
            sock.close()
            return None

        lump_ok = False
        while lump_ok is False and timeout < self.lump_timeout:
            sock.sendto(pkt, (self.bcast_addr, self.send_port))
            sleep(0.2)  # Wait for the device to process the LUMP
            #Send Ctrl-C (Code ASCII 3 for EXT equivalent of SIGINT for Unix)
            sock.sendto('\3', (self.bcast_addr, self.uboot_port))
            srecv = select([sock], [], [], 1)
            # data
            if not srecv[0]:
                continue
            try:
                serv_data = sock.recvfrom(1024)
                if serv_data[1][0] != self.ip_target:
                    continue
                serv_data = serv_data[0]
                # check when prompt (Marvell>>) is available,
                # then out to the next while to input command and send them !
                if "Marvell>> " == serv_data:
                    lump_ok = True
                    break
            except (socket.error, KeyboardInterrupt, SystemExit):
                return None
            timeout += 1

        if timeout >= self.lump_timeout:
            logging.debug("Sending LUMP for %ds, no response !",
                           self.lump_timeout)
            lump_ok = False

        sock.close()
        return lump_ok

    def invoke(self, cmd, display=True):
        '''
        send a cmd
        '''

        # Empty command, nothing to do here
        if cmd == "":
            return 42

        exit_list = ['exit', 'reset']
        override = 'Override Env parameters? (y/n)'
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        if cmd in exit_list:
            cmd = 'reset'
            cmd = pack('!' + str(len(cmd)) + 's1s', cmd, '\x0A')
            sock.sendto(cmd, (self.ip_target, self.uboot_port))
            sock.close()
            return 0

        sock.settimeout(10.0)
        try:
            sock.bind(('', self.uboot_port))
        except socket.error, err:
            logging.error("Can't open %d port. (Error : %s)",
                self.receive_port, err)
            sock.close()
            return 0

        #we want to send a cmd to the nas and get the reply in ans
        #every command is completed by \n !
        command = pack('!' + str(len(cmd)) + 's1s', cmd, '\x0A')
        sock.sendto(command, (self.ip_target, self.uboot_port))
        prompt = False
        len_command = 0
        # Don't try to wait for a prompt with bootm
        if 'bootm' in cmd:
            sock.close()
            return 42

        while prompt is False:
            srecv = select([sock], [], [], 0.5)
            # data
            if not srecv[0]:
                continue
            try:
                data = sock.recvfrom(1024)
                if data[1][0] != self.ip_target:
                    continue
                recv_data = data[0]
                # check when prompt (Marvell>>) is available,
                if ("Marvell>> " == recv_data or override == recv_data):
                    if override == recv_data:
                        print recv_data
                    prompt = True
                # When sending a command U-Boot return the commands
                # char by char, we do this so we don't display it.
                elif len_command < len(command):
                    len_command += 1
                else:
                    # to handle the printenv and other case
                    # when answer is given one letter at a time...
                    if display:
                        write = sys.stdout.write
                        write(str(recv_data))
                        sys.stdout.flush()
            except (socket.error, KeyboardInterrupt, SystemExit) as err:
                if self.debug:
                    logging.error("Sending command %s on %d : %s",
                        cmd, self.receive_port, err)
        sock.close()
        return 42

    def run(self):
        '''
        Either we execute the script or we create an interactive shell
        to the netconsole.
        '''

        if not self.send_lump():
            logging.debug("LUMP was not sent/receveid by the target")
            return 1

        if self.script is not None:
            with open(self.script, 'r+') as script:
                script_cmd = script.readlines()

            if self.progress:
                # setup progress_bar
                p_width = 60
                p_pas = p_width / len(script_cmd)
                p_percent = 0

            for cmd in script_cmd:

                if self.progress:
                    # update the bar
                    self.print_progress(p_width, p_percent)
                    p_percent += p_pas

                if cmd == '\n' or cmd.startswith('#'):
                    continue

                if not self.progress:
                    print cmd.strip() + " => ",

                self.invoke(cmd.strip(), display=not self.progress)
                sleep(1)  # it seems uboot doesn't like being shaked a bit

            if self.progress:
                self.print_progress(p_width, 100)

            # You can't wait if there is no MAC.
            if self.do_wait and (self.mac_target != "00:00:00:00:00:00"):
                # Some command output may be stuck in the pipe
                sys.stdout.flush()
                # WAIT FOR THE DEVICE TO BOOT
                logging.info("Waiting for your product to reboot...")
                sleep(60 * 7)  # Wait 7mn, it should give the device time to boot.
                # This is done to avoid spamming the network with packet while we are sure it is not necessary.
                ip = ipcomm_info(self.receive_port, self.mac_target, self.ip_target)
                if ip is None:
                    logging.info("Timeout : Unable to get your product IP.")
                    return 1
                logging.info("Your product is available at %s" % ip)
            return 0

        exit_code = 42
        while(exit_code):
            exit_code = self.invoke(raw_input("Marvell>> "), display=True)
        return 0

def main():
    ''' launch everything '''

    import argparse
    from argparse import RawTextHelpFormatter

    parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter)
    parser.add_argument('script', metavar='file', type=str, nargs='?',
                       help=argparse.SUPPRESS)
    parser.add_argument("-m", "--mac", dest="mac", action="store",
                      default=None,
                      help="Address MAC of the targeted device "
                      "(00:00:00:00:00:00)"
                      )
    parser.add_argument("-i", "--iface", dest="iface", action="store",
                      default="eth0",
                      help="Interface to use to send LUMP packet to.\n"
                      "Default is eth0.\n"
                      )
    parser.add_argument("--ip", dest="force_ip", action="store",
                      default=None,
                      help="Specify the IP address to assign to the device."
                      )
    parser.add_argument("-p", "--progress", dest="progress",
                      action="store_const", default=False, const=True,
                      help="Print a pretty progress bar,"
                      " use with a script shebang only.")
    parser.add_argument("-w", "--wait", dest="wait", action="store_const",
                      default=False, const=True,
                      help="Wait for the product to boot.\n"
                      "Note : Require the -m/--mac option to be set.\n")
    parser.add_argument("-D", "--debug", dest="loglevel", action="store_const",
                      const=logging.DEBUG, help="Output debugging information")

    session = Ubootshell()

    if '-D' in sys.argv or '--debug' in sys.argv:
        session.debug = True
        logging.basicConfig(level=logging.DEBUG, format='%(message)s')
    else:
        logging.basicConfig(level=logging.INFO, format='%(message)s')

    options = parser.parse_args()

    setup = {'mac_target': options.mac, 'iface': options.iface}
    if options.force_ip is not None:
        setup['ip_target'] = options.force_ip

    if session.setup_network(setup):
        return 1

    session.wait_at_reboot(options.wait)
    session.do_progress(options.progress)

    if options.script is not None and os.path.isfile(options.script):
        session.load_script(options.script)

    session.run()

    return 0

if __name__ == '__main__':
    if sys.platform != "win32":
        if os.geteuid() != 0:
            print "You must be administrator/root to run this program."
            sys.exit(1)

    try:
        sys.exit(main())
    except (KeyboardInterrupt, EOFError, SystemExit, KeyError):
        pass


//
// Author:
//   Jb Evain (jbevain@gmail.com)
//
// Copyright (c) 2008 - 2015 Jb Evain
// Copyright (c) 2008 - 2011 Novell, Inc.
//
// Licensed under the MIT/X11 license.
//

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

using Mono.Collections.Generic;

namespace Mono.Cecil {

	public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference);

	public sealed class AssemblyResolveEventArgs : EventArgs {

		readonly AssemblyNameReference reference;

		public AssemblyNameReference AssemblyReference {
			get { return reference; }
		}

		public AssemblyResolveEventArgs (AssemblyNameReference reference)
		{
			this.reference = reference;
		}
	}

#if !NET_CORE
	[Serializable]
#endif
	public sealed class AssemblyResolutionException : FileNotFoundException {

		readonly AssemblyNameReference reference;

		public AssemblyNameReference AssemblyReference {
			get { return reference; }
		}

		public AssemblyResolutionException (AssemblyNameReference reference)
			: this (reference, null)
		{
		}

		public AssemblyResolutionException (AssemblyNameReference reference, Exception innerException)
			: base (string.Format ("Failed to resolve assembly: '{0}'", reference), innerException)
		{
			this.reference = reference;
		}

#if !NET_CORE
		AssemblyResolutionException (
			System.Runtime.Serialization.SerializationInfo info,
			System.Runtime.Serialization.StreamingContext context)
			: base (info, context)
		{
		}
#endif
	}

	public abstract class BaseAssemblyResolver : IAssemblyResolver {

		static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;

		readonly Collection<string> directories;

#if NET_CORE
		// Maps file names of available trusted platform assemblies to their full paths.
		// Internal for testing.
		internal static readonly Lazy<Dictionary<string, string>> TrustedPlatformAssemblies = new Lazy<Dictionary<string, string>> (CreateTrustedPlatformAssemblyMap);
#else
		Collection<string> gac_paths;
#endif

		public void AddSearchDirectory (string directory)
		{
			directories.Add (directory);
		}

		public void RemoveSearchDirectory (string directory)
		{
			directories.Remove (directory);
		}

		public string [] GetSearchDirectories ()
		{
			var directories = new string [this.directories.size];
			Array.Copy (this.directories.items, directories, directories.Length);
			return directories;
		}

		public event AssemblyResolveEventHandler ResolveFailure;

		protected BaseAssemblyResolver ()
		{
			directories = new Collection<string> (2) { ".", "bin" };
		}

		AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
		{
			if (parameters.AssemblyResolver == null)
				parameters.AssemblyResolver = this;

			return ModuleDefinition.ReadModule (file, parameters).Assembly;
		}

		public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
		{
			return Resolve (name, new ReaderParameters ());
		}

		public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
		{
			Mixin.CheckName (name);
			Mixin.CheckParameters (parameters);

			var assembly = SearchDirectory (name, directories, parameters);
			if (assembly != null)
				return assembly;

			if (name.IsRetargetable) {
				// if the reference is retargetable, zero it
				name = new AssemblyNameReference (name.Name, Mixin.ZeroVersion) {
					PublicKeyToken = Empty<byte>.Array,
				};
			}

#if NET_CORE
			assembly = SearchTrustedPlatformAssemblies (name, parameters);
			if (assembly != null)
				return assembly;
#else
			var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
			var framework_dirs = on_mono
				? new [] { framework_dir, Path.Combine (framework_dir, "Facades") }
				: new [] { framework_dir };

			if (IsZero (name.Version)) {
				assembly = SearchDirectory (name, framework_dirs, parameters);
				if (assembly != null)
					return assembly;
			}

			if (name.Name == "mscorlib") {
				assembly = GetCorlib (name, parameters);
				if (assembly != null)
					return assembly;
			}

			assembly = GetAssemblyInGac (name, parameters);
			if (assembly != null)
				return assembly;

			assembly = SearchDirectory (name, framework_dirs, parameters);
			if (assembly != null)
				return assembly;
#endif
			if (ResolveFailure != null) {
				assembly = ResolveFailure (this, name);
				if (assembly != null)
					return assembly;
			}

			throw new AssemblyResolutionException (name);
		}

#if NET_CORE
		AssemblyDefinition SearchTrustedPlatformAssemblies (AssemblyNameReference name, ReaderParameters parameters)
		{
			if (name.IsWindowsRuntime)
				return null;

			if (TrustedPlatformAssemblies.Value.TryGetValue (name.Name, out string path))
				return GetAssembly (path, parameters);

			return null;
		}

		static Dictionary<string, string> CreateTrustedPlatformAssemblyMap ()
		{
			var result = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);

			string paths;

			try {
				paths = (string) AppDomain.CurrentDomain.GetData ("TRUSTED_PLATFORM_ASSEMBLIES");
			} catch {
				paths = null;
			}

			if (paths == null)
				return result;

			foreach (var path in paths.Split (Path.PathSeparator))
				if (string.Equals (Path.GetExtension (path), ".dll", StringComparison.OrdinalIgnoreCase))
					result [Path.GetFileNameWithoutExtension (path)] = path;

			return result;
		}
#endif

		protected virtual AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
		{
			var extensions = name.IsWindowsRuntime ? new [] { ".winmd", ".dll" } : new [] { ".exe", ".dll" };
			foreach (var directory in directories) {
				foreach (var extension in extensions) {
					string file = Path.Combine (directory, name.Name + extension);
					if (!File.Exists (file))
						continue;
					try {
						return GetAssembly (file, parameters);
					} catch (System.BadImageFormatException) {
						continue;
					}
				}
			}

			return null;
		}

		static bool IsZero (Version version)
		{
			return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0;
		}

#if !NET_CORE
		AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters)
		{
			var version = reference.Version;
			var corlib = typeof (object).Assembly.GetName ();
			if (corlib.Version == version || IsZero (version))
				return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters);

			var path = Directory.GetParent (
				Directory.GetParent (
					typeof (object).Module.FullyQualifiedName).FullName
				).FullName;

			if (on_mono) {
				if (version.Major == 1)
					path = Path.Combine (path, "1.0");
				else if (version.Major == 2) {
					if (version.MajorRevision == 5)
						path = Path.Combine (path, "2.1");
					else
						path = Path.Combine (path, "2.0");
				} else if (version.Major == 4)
					path = Path.Combine (path, "4.0");
				else
					throw new NotSupportedException ("Version not supported: " + version);
			} else {
				switch (version.Major) {
				case 1:
					if (version.MajorRevision == 3300)
						path = Path.Combine (path, "v1.0.3705");
					else
						path = Path.Combine (path, "v1.1.4322");
					break;
				case 2:
					path = Path.Combine (path, "v2.0.50727");
					break;
				case 4:
					path = Path.Combine (path, "v4.0.30319");
					break;
				default:
					throw new NotSupportedException ("Version not supported: " + version);
				}
			}

			var file = Path.Combine (path, "mscorlib.dll");
			if (File.Exists (file))
				return GetAssembly (file, parameters);

			if (on_mono && Directory.Exists (path + "-api")) {
				file = Path.Combine (path + "-api", "mscorlib.dll");
				if (File.Exists (file))
					return GetAssembly (file, parameters);
			}

			return null;
		}

		static Collection<string> GetGacPaths ()
		{
			if (on_mono)
				return GetDefaultMonoGacPaths ();

			var paths = new Collection<string> (2);
			var windir = Environment.GetEnvironmentVariable ("WINDIR");
			if (windir == null)
				return paths;

			paths.Add (Path.Combine (windir, "assembly"));
			paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly")));
			return paths;
		}

		static Collection<string> GetDefaultMonoGacPaths ()
		{
			var paths = new Collection<string> (1);
			var gac = GetCurrentMonoGac ();
			if (gac != null)
				paths.Add (gac);

			var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX");
			if (string.IsNullOrEmpty (gac_paths_env))
				return paths;

			var prefixes = gac_paths_env.Split (Path.PathSeparator);
			foreach (var prefix in prefixes) {
				if (string.IsNullOrEmpty (prefix))
					continue;

				var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac");
				if (Directory.Exists (gac_path) && !paths.Contains (gac))
					paths.Add (gac_path);
			}

			return paths;
		}

		static string GetCurrentMonoGac ()
		{
			return Path.Combine (
				Directory.GetParent (
					Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName,
				"gac");
		}

		AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters)
		{
			if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
				return null;

			if (gac_paths == null)
				gac_paths = GetGacPaths ();

			if (on_mono)
				return GetAssemblyInMonoGac (reference, parameters);

			return GetAssemblyInNetGac (reference, parameters);
		}

		AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters)
		{
			for (int i = 0; i < gac_paths.Count; i++) {
				var gac_path = gac_paths [i];
				var file = GetAssemblyFile (reference, string.Empty, gac_path);
				if (File.Exists (file))
					return GetAssembly (file, parameters);
			}

			return null;
		}

		AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters)
		{
			var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" };
			var prefixes = new [] { string.Empty, "v4.0_" };

			for (int i = 0; i < gac_paths.Count; i++) {
				for (int j = 0; j < gacs.Length; j++) {
					var gac = Path.Combine (gac_paths [i], gacs [j]);
					var file = GetAssemblyFile (reference, prefixes [i], gac);
					if (Directory.Exists (gac) && File.Exists (file))
						return GetAssembly (file, parameters);
				}
			}

			return null;
		}

		static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac)
		{
			var gac_folder = new StringBuilder ()
				.Append (prefix)
				.Append (reference.Version)
				.Append ("__");

			for (int i = 0; i < reference.PublicKeyToken.Length; i++)
				gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));

			return Path.Combine (
				Path.Combine (
					Path.Combine (gac, reference.Name), gac_folder.ToString ()),
				reference.Name + ".dll");
		}
#endif
		public void Dispose ()
		{
			Dispose (true);
			GC.SuppressFinalize (this);
		}

		protected virtual void Dispose (bool disposing)
		{
		}
	}
}

//
// Author:
//   Jb Evain (jbevain@gmail.com)
//
// Copyright (c) 2008 - 2015 Jb Evain
// Copyright (c) 2008 - 2011 Novell, Inc.
//
// Licensed under the MIT/X11 license.
//

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

using Mono.Collections.Generic;

namespace Mono.Cecil {

	public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference);

	public sealed class AssemblyResolveEventArgs : EventArgs {

		readonly AssemblyNameReference reference;

		public AssemblyNameReference AssemblyReference {
			get { return reference; }
		}

		public AssemblyResolveEventArgs (AssemblyNameReference reference)
		{
			this.reference = reference;
		}
	}

#if !NET_CORE
	[Serializable]
#endif
	public sealed class AssemblyResolutionException : FileNotFoundException {

		readonly AssemblyNameReference reference;

		public AssemblyNameReference AssemblyReference {
			get { return reference; }
		}

		public AssemblyResolutionException (AssemblyNameReference reference)
			: this (reference, null)
		{
		}

		public AssemblyResolutionException (AssemblyNameReference reference, Exception innerException)
			: base (string.Format ("Failed to resolve assembly: '{0}'", reference), innerException)
		{
			this.reference = reference;
		}

#if !NET_CORE
		AssemblyResolutionException (
			System.Runtime.Serialization.SerializationInfo info,
			System.Runtime.Serialization.StreamingContext context)
			: base (info, context)
		{
		}
#endif
	}

	public abstract class BaseAssemblyResolver : IAssemblyResolver {

		static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;

		readonly Collection<string> directories;

#if NET_CORE
		// Maps file names of available trusted platform assemblies to their full paths.
		// Internal for testing.
		internal static readonly Lazy<Dictionary<string, string>> TrustedPlatformAssemblies = new Lazy<Dictionary<string, string>> (CreateTrustedPlatformAssemblyMap);
#else
		Collection<string> gac_paths;
#endif

		public void AddSearchDirectory (string directory)
		{
			directories.Add (directory);
		}

		public void RemoveSearchDirectory (string directory)
		{
			directories.Remove (directory);
		}

		public string [] GetSearchDirectories ()
		{
			var directories = new string [this.directories.size];
			Array.Copy (this.directories.items, directories, directories.Length);
			return directories;
		}

		public event AssemblyResolveEventHandler ResolveFailure;

		protected BaseAssemblyResolver ()
		{
			directories = new Collection<string> (2) { ".", "bin" };
		}

		AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
		{
			if (parameters.AssemblyResolver == null)
				parameters.AssemblyResolver = this;

			return ModuleDefinition.ReadModule (file, parameters).Assembly;
		}

		public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
		{
			return Resolve (name, new ReaderParameters ());
		}

		public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
		{
			Mixin.CheckName (name);
			Mixin.CheckParameters (parameters);

			var assembly = SearchDirectory (name, directories, parameters);
			if (assembly != null)
				return assembly;

			if (name.IsRetargetable) {
				// if the reference is retargetable, zero it
				name = new AssemblyNameReference (name.Name, Mixin.ZeroVersion) {
					PublicKeyToken = Empty<byte>.Array,
				};
			}

#if NET_CORE
			assembly = SearchTrustedPlatformAssemblies (name, parameters);
			if (assembly != null)
				return assembly;
#else
			var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
			var framework_dirs = on_mono
				? new [] { framework_dir, Path.Combine (framework_dir, "Facades") }
				: new [] { framework_dir };

			if (IsZero (name.Version)) {
				assembly = SearchDirectory (name, framework_dirs, parameters);
				if (assembly != null)
					return assembly;
			}

			if (name.Name == "mscorlib") {
				assembly = GetCorlib (name, parameters);
				if (assembly != null)
					return assembly;
			}

			assembly = GetAssemblyInGac (name, parameters);
			if (assembly != null)
				return assembly;

			assembly = SearchDirectory (name, framework_dirs, parameters);
			if (assembly != null)
				return assembly;
#endif
			if (ResolveFailure != null) {
				assembly = ResolveFailure (this, name);
				if (assembly != null)
					return assembly;
			}

			throw new AssemblyResolutionException (name);
		}

#if NET_CORE
		AssemblyDefinition SearchTrustedPlatformAssemblies (AssemblyNameReference name, ReaderParameters parameters)
		{
			if (name.IsWindowsRuntime)
				return null;

			if (TrustedPlatformAssemblies.Value.TryGetValue (name.Name, out string path))
				return GetAssembly (path, parameters);

			return null;
		}

		static Dictionary<string, string> CreateTrustedPlatformAssemblyMap ()
		{
			var result = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);

			string paths;

			try {
				paths = (string) AppDomain.CurrentDomain.GetData ("TRUSTED_PLATFORM_ASSEMBLIES");
			} catch {
				paths = null;
			}

			if (paths == null)
				return result;

			foreach (var path in paths.Split (Path.PathSeparator))
				if (string.Equals (Path.GetExtension (path), ".dll", StringComparison.OrdinalIgnoreCase))
					result [Path.GetFileNameWithoutExtension (path)] = path;

			return result;
		}
#endif

		protected virtual AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
		{
			var extensions = name.IsWindowsRuntime ? new [] { ".winmd", ".dll" } : new [] { ".dll", ".exe" };
			foreach (var directory in directories) {
				foreach (var extension in extensions) {
					string file = Path.Combine (directory, name.Name + extension);
					if (!File.Exists (file))
						continue;
					try {
						return GetAssembly (file, parameters);
					} catch (System.BadImageFormatException) {
						continue;
					}
				}
			}

			return null;
		}

		static bool IsZero (Version version)
		{
			return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0;
		}

#if !NET_CORE
		AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters)
		{
			var version = reference.Version;
			var corlib = typeof (object).Assembly.GetName ();
			if (corlib.Version == version || IsZero (version))
				return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters);

			var path = Directory.GetParent (
				Directory.GetParent (
					typeof (object).Module.FullyQualifiedName).FullName
				).FullName;

			if (on_mono) {
				if (version.Major == 1)
					path = Path.Combine (path, "1.0");
				else if (version.Major == 2) {
					if (version.MajorRevision == 5)
						path = Path.Combine (path, "2.1");
					else
						path = Path.Combine (path, "2.0");
				} else if (version.Major == 4)
					path = Path.Combine (path, "4.0");
				else
					throw new NotSupportedException ("Version not supported: " + version);
			} else {
				switch (version.Major) {
				case 1:
					if (version.MajorRevision == 3300)
						path = Path.Combine (path, "v1.0.3705");
					else
						path = Path.Combine (path, "v1.1.4322");
					break;
				case 2:
					path = Path.Combine (path, "v2.0.50727");
					break;
				case 4:
					path = Path.Combine (path, "v4.0.30319");
					break;
				default:
					throw new NotSupportedException ("Version not supported: " + version);
				}
			}

			var file = Path.Combine (path, "mscorlib.dll");
			if (File.Exists (file))
				return GetAssembly (file, parameters);

			if (on_mono && Directory.Exists (path + "-api")) {
				file = Path.Combine (path + "-api", "mscorlib.dll");
				if (File.Exists (file))
					return GetAssembly (file, parameters);
			}

			return null;
		}

		static Collection<string> GetGacPaths ()
		{
			if (on_mono)
				return GetDefaultMonoGacPaths ();

			var paths = new Collection<string> (2);
			var windir = Environment.GetEnvironmentVariable ("WINDIR");
			if (windir == null)
				return paths;

			paths.Add (Path.Combine (windir, "assembly"));
			paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly")));
			return paths;
		}

		static Collection<string> GetDefaultMonoGacPaths ()
		{
			var paths = new Collection<string> (1);
			var gac = GetCurrentMonoGac ();
			if (gac != null)
				paths.Add (gac);

			var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX");
			if (string.IsNullOrEmpty (gac_paths_env))
				return paths;

			var prefixes = gac_paths_env.Split (Path.PathSeparator);
			foreach (var prefix in prefixes) {
				if (string.IsNullOrEmpty (prefix))
					continue;

				var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac");
				if (Directory.Exists (gac_path) && !paths.Contains (gac))
					paths.Add (gac_path);
			}

			return paths;
		}

		static string GetCurrentMonoGac ()
		{
			return Path.Combine (
				Directory.GetParent (
					Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName)).FullName,
				"gac");
		}

		AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters)
		{
			if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
				return null;

			if (gac_paths == null)
				gac_paths = GetGacPaths ();

			if (on_mono)
				return GetAssemblyInMonoGac (reference, parameters);

			return GetAssemblyInNetGac (reference, parameters);
		}

		AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters)
		{
			for (int i = 0; i < gac_paths.Count; i++) {
				var gac_path = gac_paths [i];
				var file = GetAssemblyFile (reference, string.Empty, gac_path);
				if (File.Exists (file))
					return GetAssembly (file, parameters);
			}

			return null;
		}

		AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters)
		{
			var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" };
			var prefixes = new [] { string.Empty, "v4.0_" };

			for (int i = 0; i < gac_paths.Count; i++) {
				for (int j = 0; j < gacs.Length; j++) {
					var gac = Path.Combine (gac_paths [i], gacs [j]);
					var file = GetAssemblyFile (reference, prefixes [i], gac);
					if (Directory.Exists (gac) && File.Exists (file))
						return GetAssembly (file, parameters);
				}
			}

			return null;
		}

		static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac)
		{
			var gac_folder = new StringBuilder ()
				.Append (prefix)
				.Append (reference.Version)
				.Append ("__");

			for (int i = 0; i < reference.PublicKeyToken.Length; i++)
				gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));

			return Path.Combine (
				Path.Combine (
					Path.Combine (gac, reference.Name), gac_folder.ToString ()),
				reference.Name + ".dll");
		}
#endif
		public void Dispose ()
		{
			Dispose (true);
			GC.SuppressFinalize (this);
		}

		protected virtual void Dispose (bool disposing)
		{
		}
	}
}


require 'rubygems'
require 'test/unit'
require 'mocha'

$:.unshift File.expand_path('../../lib', __FILE__)
require 'simple_config'

require 'rubygems'
require 'test/unit'
require 'mocha'
require 'mocha/mini_test'

$:.unshift File.expand_path('../../lib', __FILE__)
require 'simple_config'


/*
 * xLazyLoader 1.5 - Plugin for jQuery
 *
 * Load js, css and images asynchron and get different callbacks
 *
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Depends:
 *   jquery.js
 *
 *  Copyright (c) 2010 Oleg Slobodskoi (jsui.de)
 */

;(function($){

$.xLazyLoader =  function ( method, options ) {
    if ( typeof method == 'object' ) {
        options = method;
        method = 'init';
    };
    new xLazyLoader()[method](options);
};

$.xLazyLoader.defaults = {
    js: [], css: [], img: [],
    jsKey: null, cssKey: null, imgKey: null,
    name: null,
    timeout: 20000,
    //success callback for all files
    success: $.noop,
    //error callback - by load errors / timeout
    error: $.noop,
    //complete callbck - by success or errors
    complete: $.noop,
    //success callback for each file
    each: $.noop
};

var head = document.getElementsByTagName("head")[0];

function xLazyLoader ()
{

    var self = this,
        s,
        loaded = [],
        errors = [],
        tTimeout,
        cssTimeout,
        toLoad,
        files = []
    ;

    this.init = function ( options )
    {
        if ( !options ) return;

        s = $.extend({}, $.xLazyLoader.defaults, options);
        toLoad = {js: s.js, css: s.css, img: s.img};

        $.each(toLoad, function( type, f ){
            if ( typeof f == 'string' )
                f = f.split(',');
            files = files.concat(f);
        });

        if ( !files.length ) {
            dispatchCallbacks('error');
            return;
        };

        if (s.timeout) {
            tTimeout = setTimeout(function(){
                var handled = loaded.concat(errors);
                /* search for unhandled files */
                $.each(files, function(i, file){
                    $.inArray(file, handled) == -1 && errors.push(file);
                });
                dispatchCallbacks('error');
            }, s.timeout);
        };

        $.each(toLoad, function(type, urls){
            if ( $.isArray(urls) )
                $.each( urls, function(i, url){
                    load(type, url);
                });
            else if (typeof urls == 'string')
                load(type, urls);
        });

    };

    this.js = function ( src, callback, name, key )
    {
        var $script = $('script[src*="'+src+'"]');
        if ( $script.length ) {
            $script.attr('pending') ? $script.bind('scriptload',callback) : callback();
            return;
        };

        var s = document.createElement('script');
        s.setAttribute("type","text/javascript");
        s.setAttribute('charset', 'UTF-8');
        s.setAttribute("src", src + key);
        s.setAttribute('id', name);
        s.setAttribute('pending', 1);
        // Mozilla only
        s.onerror = addError;

        $(s).bind('scriptload',function(){
            $(this).removeAttr('pending');
            callback();
             //unbind load event
             //timeout because of pending callbacks
            setTimeout(function(){
                $(s).unbind('scriptload');
            },10);
        });

        // jQuery doesn't handles onload event special for script tag,
        var done = false;
        s.onload = s.onreadystatechange = function() {
            if ( !done && ( !this.readyState || /loaded|complete/.test(this.readyState) ) ) {
                done = true;
                // Handle memory leak in IE
                s.onload = s.onreadystatechange = null;
                $(s).trigger('scriptload');
            };
        };
        head.appendChild(s);

    };

    this.css = function ( href, callback, name, key )
    {

        if ( $('link[href*="'+href+'"]').length ) {
            callback();
            return;
        };

        var link = $('<link rel="stylesheet" type="text/css" media="all" href="'+ href + key + '" id="'+name+'"></link>')[0];
        if ( $.browser.msie ) {
            link.onreadystatechange = function () {
                /loaded|complete/.test(link.readyState) && callback();
            };
        } else if ( $.browser.opera ) {
            link.onload = callback;
        } else {
            /*
             * Mozilla, Safari, Chrome
             * unfortunately it is inpossible to check if the stylesheet is really loaded or it is "HTTP/1.0 400 Bad Request"
             * the only way to do this is to check if some special properties  were set, so there is no error callback for stylesheets -
             * it fires alway success
             *
             * There is also no access to sheet properties by crossdomain stylesheets,
             * so we fire callback immediately
             */

            var hostname = location.hostname.replace('www.',''),
                hrefHostname = /http:/.test(href) ? /^(\w+:)?\/\/([^\/?#]+)/.exec( href )[2] : hostname;
            hostname != hrefHostname && $.browser.mozilla ?
                callback()
                :
                //stylesheet is from the same domain or it is not firefox
                (function(){
                    try {
                        link.sheet.cssRules;
                    } catch (e) {
                        cssTimeout = setTimeout(arguments.callee, 20);
                        return;
                    };
                    callback();
                })();
        };

        head.appendChild(link);
    };

    this.img = function ( src, callback, name, key )
    {
        var img = new Image();
        img.onload = callback;
        img.onerror = addError;
        img.src = src + key;
    };

    /* It works only for css */
    this.disable = function ( name )
    {
        $('#lazy-loaded-'+name, head).attr('disabled', 'disabled');
    };

    /* It works only for css */
    this.enable = function ( name )
    {
        $('#lazy-loaded-'+name, head).removeAttr('disabled');
    };

    /*
     * By removing js tag, script ist still living in browser memory,
     * css will be really destroyed
     */
    this.destroy = function ( name )
    {
        $('#lazy-loaded-'+name, head).remove();
    };

    function load ( type, url ) {
        self[type](url, function(status) {
            status == 'error' ? errors.push(url) : loaded.push(url) && s.each(url);
            checkProgress();
        }, 'lazy-loaded-'+ (s.name ? s.name : new Date().getTime()), s[type+'Key'] ? '?key='+s[type+'Key'] : '' );
    };

    function dispatchCallbacks ( status ) {
        s.complete(status, loaded, errors);
        s[status]( status=='error' ? errors : loaded);
        clearTimeout(tTimeout);
        clearTimeout(cssTimeout);
    };

    function checkProgress () {
        if (loaded.length == files.length) dispatchCallbacks('success')
        else if (loaded.length+errors.length == files.length) dispatchCallbacks('error');
    };

    function addError () {
        errors.push(this.src);
        checkProgress();
    };

};

})(jQuery);

/*
 * xLazyLoader 1.5 - Plugin for jQuery
 *
 * Load js, css and images asynchron and get different callbacks
 *
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Depends:
 *   jquery.js
 *
 *  Copyright (c) 2010 Oleg Slobodskoi (jsui.de)
 */

;(function($){

$.xLazyLoader =  function ( method, options ) {
    if ( typeof method == 'object' ) {
        options = method;
        method = 'init';
    };
    new xLazyLoader()[method](options);
};

$.xLazyLoader.defaults = {
    js: [], css: [], img: [],
    jsKey: null, cssKey: null, imgKey: null,
    name: null,
    timeout: 20000,
    //success callback for all files
    success: $.noop,
    //error callback - by load errors / timeout
    error: $.noop,
    //complete callbck - by success or errors
    complete: $.noop,
    //success callback for each file
    each: $.noop
};

var head = document.getElementsByTagName("head")[0];

function xLazyLoader ()
{

    var self = this,
        s,
        loaded = [],
        errors = [],
        tTimeout,
        cssTimeout,
        toLoad,
        files = []
    ;

    this.init = function ( options )
    {
        if ( !options ) return;

        s = $.extend({}, $.xLazyLoader.defaults, options);
        toLoad = {js: s.js, css: s.css, img: s.img};

        $.each(toLoad, function( type, f ){
            if ( typeof f == 'string' )
                f = f.split(',');
            files = files.concat(f);
        });

        if ( !files.length ) {
            dispatchCallbacks('error');
            return;
        };

        if (s.timeout) {
            tTimeout = setTimeout(function(){
                var handled = loaded.concat(errors);
                /* search for unhandled files */
                $.each(files, function(i, file){
                    $.inArray(file, handled) == -1 && errors.push(file);
                });
                dispatchCallbacks('error');
            }, s.timeout);
        };

        $.each(toLoad, function(type, urls){
            if ( $.isArray(urls) )
                $.each( urls, function(i, url){
                    load(type, url);
                });
            else if (typeof urls == 'string')
                load(type, urls);
        });

    };

    this.js = function ( src, callback, name, key )
    {
        var $script = $('script[src*="'+src+'"]');
        if ( $script.length ) {
            $script.attr('pending') ? $script.bind('scriptload',callback) : callback();
            return;
        };

        var s = document.createElement('script');
        s.setAttribute("type","text/javascript");
        s.setAttribute('charset', 'UTF-8');
        s.setAttribute("src", src + key);
        s.setAttribute('id', name);
        s.setAttribute('pending', 1);
        // Mozilla only
        s.onerror = addError;

        $(s).bind('scriptload',function(){
            $(this).removeAttr('pending');
            callback();
             //unbind load event
             //timeout because of pending callbacks
            setTimeout(function(){
                $(s).unbind('scriptload');
            },10);
        });

        // jQuery doesn't handles onload event special for script tag,
        var done = false;
        s.onload = s.onreadystatechange = function() {
            if ( !done && ( !this.readyState || /loaded|complete/.test(this.readyState) ) ) {
                done = true;
                // Handle memory leak in IE
                s.onload = s.onreadystatechange = null;
                $(s).trigger('scriptload');
            };
        };
        head.appendChild(s);

    };

    this.css = function ( href, callback, name, key )
    {

        if ( $('link[href*="'+href+'"]').length ) {
            callback();
            return;
        };

        var link = $('<link rel="stylesheet" type="text/css" media="all" href="'+ href + key + '" id="'+name+'"></link>')[0];
        if ( $.browser.msie ) {
            link.onreadystatechange = function (){
                if (link.readyState == "loaded" || link.readyState == "complete") {
					link.onreadystatechange = null;
					callback();
				}
			}
        } else if ( $.browser.opera ) {
            link.onload = callback;
        } else {
            /*
             * Mozilla, Safari, Chrome
             * unfortunately it is inpossible to check if the stylesheet is really loaded or it is "HTTP/1.0 400 Bad Request"
             * the only way to do this is to check if some special properties  were set, so there is no error callback for stylesheets -
             * it fires alway success
             *
             * There is also no access to sheet properties by crossdomain stylesheets,
             * so we fire callback immediately
             */

            var hostname = location.hostname.replace('www.',''),
                hrefHostname = /http:/.test(href) ? /^(\w+:)?\/\/([^\/?#]+)/.exec( href )[2] : hostname;
            hostname != hrefHostname && $.browser.mozilla ?
                callback()
                :
                //stylesheet is from the same domain or it is not firefox
                (function(){
                    try {
                        link.sheet.cssRules;
                    } catch (e) {
                        cssTimeout = setTimeout(arguments.callee, 20);
                        return;
                    };
                    callback();
                })();
        };

        head.appendChild(link);
    };

    this.img = function ( src, callback, name, key )
    {
        var img = new Image();
        img.onload = callback;
        img.onerror = addError;
        img.src = src + key;
    };

    /* It works only for css */
    this.disable = function ( name )
    {
        $('#lazy-loaded-'+name, head).attr('disabled', 'disabled');
    };

    /* It works only for css */
    this.enable = function ( name )
    {
        $('#lazy-loaded-'+name, head).removeAttr('disabled');
    };

    /*
     * By removing js tag, script ist still living in browser memory,
     * css will be really destroyed
     */
    this.destroy = function ( name )
    {
        $('#lazy-loaded-'+name, head).remove();
    };

    function load ( type, url ) {
        self[type](url, function(status) {
            status == 'error' ? errors.push(url) : loaded.push(url) && s.each(url);
            checkProgress();
        }, 'lazy-loaded-'+ (s.name ? s.name : new Date().getTime()), s[type+'Key'] ? '?key='+s[type+'Key'] : '' );
    };

    function dispatchCallbacks ( status ) {
        s.complete(status, loaded, errors);
        s[status]( status=='error' ? errors : loaded);
        clearTimeout(tTimeout);
        clearTimeout(cssTimeout);
    };

    function checkProgress () {
        if (loaded.length == files.length) dispatchCallbacks('success')
        else if (loaded.length+errors.length == files.length) dispatchCallbacks('error');
    };

    function addError () {
        errors.push(this.src);
        checkProgress();
    };

};

})(jQuery);