Exemplo n.º 1
0
    def __init__(self):
        xdg = XdgUtils()

        # Can we use a custom glib loader?
        self.glibc_loader = os.path.exists("/lib/ld-klik2.so.2")

        # Attempt to load version from svnversion file
        svnversion_path = os.path.join(sys.path[0], os.pardir, "share", "klik",
                                       "svnversion")
        if os.path.exists(svnversion_path):
            f = open(svnversion_path, "r")
            self.version = self.version + "." + f.readline().strip() + "svn"
            f.close()

        # default destination
        self.destination = xdg.get_desktop_dir()

        # Hardcoded default values
        self.desktop_icon_size = 48
        self.debug = False
        self.temp_cache_downloads = False
        self.application_folders = [self.destination]
        self.application_folder_notify = True
        self.always_show_app_chooser = False
        self.default_jail_type = 0

        # Load global, override code
        system_settings_path = os.path.join(sys.path[0], os.pardir, "share",
                                            "klik", "settings.xml")
        self.__load_file(system_settings_path)

        # Load user, overides global
        user_settings_directory = os.path.expanduser("~/.klik")
        user_settings_path = os.path.join(user_settings_directory,
                                          "settings.xml")
        self.__load_file(user_settings_path)

        # Remove duplicates in application folders list
        set = {}
        self.application_folders = [
            set.setdefault(x, x) for x in self.application_folders
            if x not in set
        ]

        # If user settings file doesn't exist, create it
        if not os.path.isdir(user_settings_directory):
            os.mkdir(user_settings_directory)
        if not os.path.exists(user_settings_path):
            shutil.copy(system_settings_path, user_settings_path)

        if os.getenv("KLIK_DEBUG") != None:
            self.debug = (os.getenv("KLIK_DEBUG") == "1")
Exemplo n.º 2
0
    def __init__(self, klikbase):

        # Objects we will use
        self.klik = klikbase
        self.xdg = XdgUtils()

        self.args = None
        self.recipe = None

        # Current install status
        # 0 - not started, 1 - downloading, 2 - compiling cmg, 3 -complete
        self.install_status = 0

        # Load glade file
        self.gladefile = os.path.join(self.klik.sys_path, os.pardir, "share",
                                      "klik", "klik.glade")

        self.wtree = gtk.glade.XML(self.gladefile)
        self.window = self.wtree.get_widget("download_prompt")
        self.window.set_icon_from_file(
            os.path.join(self.klik.sys_path, os.pardir, "share", "klik",
                         "klik-window.png"))
        dic = {
            "on_button_cancel_clicked": self.on_button_cancel_clicked,
            "on_download_prompt_destroy": self.on_download_prompt_destroy
        }
        self.wtree.signal_autoconnect(dic)

        # Initialize download prompt
        self.download_prompt = DownloadPrompt(self)
        self.progress_dialog = ProgressDialog(self)
        self.feedback_dialog = FeedbackDialog(self)
Exemplo n.º 3
0
    def __init__(self, sys_path=sys.path[0]):

        self.sys_path = sys_path

        self.events = KlikBaseEvents()
        self.create = KlikCreate(self)
        self.settings = KlikSettings()
        self.xdg = XdgUtils()
        self.notify = Notify()

        self.create.events.on_download_progress += self.__download_progress
        self.create.events.print_to_stdout += self.print_to_stdout
        self.current_progress_percentage = 0
        self.current_progress_text = ""
        self.temp_paths = []

        # Insure temp path exists
        if not os.path.exists(self.settings.temp_directory_path):
            os.mkdir(self.settings.temp_directory_path)
Exemplo n.º 4
0
	def __init__(self):
	
		xdg = XdgUtils()
	
		# Attempt to load version from svnversion file
		svnversion_path = os.path.join(sys.path[0] , os.pardir, "share", "klik", "svnversion")
		if os.path.exists(svnversion_path):
			f = open(svnversion_path, "r")
			self.version =  self.version + "." + f.readline().strip() + "svn"
			f.close()
	
		# default destination
		self.destination = xdg.get_desktop_dir()
	
		# Hardcoded default values
		self.desktop_icon_size = 48
		self.debug = False
		self.temp_cache_downloads = False
		self.application_folders = [self.destination]
		self.application_folder_notify = True
		self.always_show_app_chooser = False
		self.default_jail_type = 0

		# Load global, override code
		system_settings_path = os.path.join(sys.path[0], os.pardir, "share", "klik", "settings.xml")
		self.__load_file( system_settings_path )

		# Load user, overides global
		user_settings_directory = os.path.expanduser("~/.klik")
		user_settings_path = os.path.join(user_settings_directory, "settings.xml")
		self.__load_file( user_settings_path )

		# Remove duplicates in application folders list
		set = {}
		self.application_folders = [set.setdefault(x,x) for x in self.application_folders if x not in set]

		# if user settings file dosnt exist create it
		if os.path.exists(system_settings_path) and not os.path.exists(user_settings_path):
			if not os.path.isdir(user_settings_directory):
				os.mkdir(user_settings_directory)
			shutil.copy(system_settings_path, user_settings_path)
Exemplo n.º 5
0
	def __init__(self, sys_path=sys.path[0]):

		self.sys_path = sys_path

		self.events = KlikBaseEvents()
		self.create = KlikCreate(self)
		self.settings = KlikSettings()
		self.xdg = XdgUtils()
		self.notify = Notify()

		self.create.events.on_download_progress += self.__download_progress
		self.create.events.print_to_stdout += self.print_to_stdout
		self.current_progress_percentage = 0
		self.current_progress_text = ""
		self.temp_paths = []

		# Insure temp path exists
		if not os.path.exists( self.settings.temp_directory_path ):
			os.mkdir(self.settings.temp_directory_path)
Exemplo n.º 6
0
from optparse import OptionParser

opt = OptionParser()
opt.add_option("--prefix", metavar="<dir>", dest="prefix", default="/opt/klik")

(options, args) = opt.parse_args()

klik_install_path = options.prefix
klik_files_path = sys.path[0]

sys.path.insert(1, klik_files_path + "/klikclient/lib")

# XDG UTILS
from klik.utils.xdg import XdgUtils
xdg = XdgUtils(
    os.path.join(sys.path[0], "klikclient", "lib", "klik", "utils",
                 "xdg-utils"), "SYSTEM")

# Check for root
if xdg.is_root() == False:
    print("You must be root in order to run " + sys.argv[0] + ", exiting")
    sys.exit(1)
else:
    print "Privileged mode detected ..."

# Remove sub folders
print ""
print "Remove klik sub folders ..."
path = os.path.join(klik_install_path, "share/klik")
if not os.path.exists(path):
    shutil.rmtree(path, True)
Exemplo n.º 7
0
class KlikBase(object):
    def __init__(self, sys_path=sys.path[0]):

        self.sys_path = sys_path

        self.events = KlikBaseEvents()
        self.create = KlikCreate(self)
        self.settings = KlikSettings()
        self.xdg = XdgUtils()
        self.notify = Notify()

        self.create.events.on_download_progress += self.__download_progress
        self.create.events.print_to_stdout += self.print_to_stdout
        self.current_progress_percentage = 0
        self.current_progress_text = ""
        self.temp_paths = []

        # Insure temp path exists
        if not os.path.exists(self.settings.temp_directory_path):
            os.mkdir(self.settings.temp_directory_path)

    # Custom print method so we can capture stdout
    def print_to_stdout(self, obj, no_new_line=False):
        if no_new_line:
            sys.stdout.write(obj)
            sys.stdout.flush()
            self.events.print_to_stdout(obj)
        else:
            print obj
            self.events.print_to_stdout(obj + "\n")

    def post_feedback(self, recipe, workingyesno, errorreport, librarytrace,
                      md5sum, comment):

        content = []

        content.append("package=" + urllib.quote(recipe.name))  #
        content.append("version=" + urllib.quote(recipe.version))

        content.append("workingyesno=" + urllib.quote(str(workingyesno)))  #
        content.append("systeminformation=" +
                       urllib.quote(self.system_info()))  #
        content.append("comment=" + urllib.quote(comment))  #

        #  f1 = file(os.path.join(path_1, os.listdir(path_1)[0]) ,'rb')
        # md5.new(f1.read()).digest()
        # f1.close()

        # only post error info on a failure
        if not workingyesno:
            content.append("errorreport=" + urllib.quote(errorreport))
            content.append("librarytrace=" + urllib.quote(librarytrace))
            content.append("recipe=" + urllib.quote(recipe.original_xml))
            #content.append("md5sum=" + urllib.quote( librarytrace ))

        url_parts = urlparse.urlparse(self.settings.default_feedback_url)
        body = "&".join(content)
        h = httplib.HTTP(url_parts[1])
        h.putrequest('POST', url_parts[2])
        h.putheader('content-type', "application/x-www-form-urlencoded")
        h.putheader('content-length', str(len(body)))
        h.endheaders()

        try:
            h.send(body)
            #h.getreply()
            #print h.file.read()
        except:
            pass

    def check_for_updated_recipe(self, recipe):
        try:
            new_recipe = self.get_recipe(recipe.source_uri)

            # Has the recipe changed
            old_md5_hash = md5.new(recipe.original_xml).hexdigest()
            new_md5_hash = md5.new(new_recipe.original_xml).hexdigest()

            if not old_md5_hash == new_md5_hash:
                # Should we also check application version numbers?
                # A recipe might change just to fix a run time error etc...
                return True, new_recipe
        except:
            pass

        # If we cant get a recipe always return false
        return False, None

    def get_recipe(self, application_name):

        # Get information about users system
        plat = platform.system() + " " + platform.machine()
        pyvers = "Python/" + platform.python_version()

        useragent = "klik/%s (%s; %s; %s; %s;) %s (%s)" % (
            self.settings.version, self.xdg.get_is_terminal(), plat,
            self.xdg.get_lang(), self.xdg.get_desktop_enviroment() + "+" +
            "+".join(self.xdg.get_installed_libs()), pyvers,
            self.xdg.get_distribution())

        # Convert to lower case for comparing
        application_name = application_name.lower()

        # Download Recipe
        if application_name.startswith("http://"):
            url = application_name
        else:
            # Strip klik protocol
            if application_name.startswith("klik2://"):
                application_name = application_name[8:]
            elif application_name.startswith("klik2:"):
                application_name = application_name[6:]

            if application_name.startswith("klik://"):
                application_name = application_name[7:]
            elif application_name.startswith("klik:"):
                application_name = application_name[5:]

            url = self.settings.default_download_url + application_name

        print "Recipe URL: " + url
        try:
            request = urllib2.Request(url)
            request.add_header('User-Agent', useragent)
            opener = urllib2.build_opener()
            data = opener.open(request).read()
        except:
            raise SafeException(
                "Error occurred while attempting to download recipe")

        # Load Recipe
        print ""
        print "Parsing recipe..."
        recipe = KlikRecipe()
        recipe.load_from_string(data)
        self.print_recipe(recipe)

        return recipe

    def print_recipe(self, recipe):
        print ""
        print "Application details"
        print "==================="
        print u"Name: %s" % recipe.name
        print u"Version: %s" % recipe.version

        trusted, text = recipe.is_trusted()
        if trusted == True:
            print "Trusted Contact : " + text
        else:
            print "Trust Level : UNTRUSTED - This recipe has not been signed by someone you trust"

        print u"Summary: %s" % recipe.summary
        print u"Description: %s" % recipe.description
        print ""

    def load_recipe(self, recipe_path):
        recipe = KlikRecipe()
        recipe.load_from_file(recipe_path)
        return recipe

    def create_cmg(self, recipe_object):
        return self.create.create(recipe_object, self.settings.destination)

    def execute_cmg(self,
                    command,
                    cmg_path,
                    args,
                    capture_error=False,
                    force_prompt=False):
        ex = KlikExecute(self)
        desktop_object = None

        cmg_version = self.is_valid_cmg(cmg_path)

        if cmg_version == 2:

            # CLI should add commands to line
            if force_prompt or command == None or command.strip() == "":

                # Need to check recipe command
                # Parse out command and parameters
                recipe = self.extract_recipe(cmg_path)

                parse_count = 0
                parse_args = len(args) == 0
                p = re.compile("(('[^']+')|(\"[^\"]+\")|([^\s]+))+")
                for r in re.finditer(p, recipe.command):
                    if parse_count == 0:
                        command = r.group(0)
                    elif parse_args:
                        args.append(r.group(0))
                    parse_count = parse_count + 1

                # look at desktop files
                if force_prompt or command == None or command.strip(
                ) == "" or self.settings.always_show_app_chooser:

                    desktop_objects = self.get_desktop_objects(cmg_path, False)
                    if force_prompt or self.xdg.get_is_x_display(
                    ) and len(desktop_objects) > 1:

                        desktop_object = None

                        # if self.xdg.get_desktop_enviroment() == "GNOME" and :
                        if self.xdg.get_is_gtk_installed():
                            # this dialouge will return the selected desktop object
                            from klik.gtk.multiple import MultipleApplications
                            ma = MultipleApplications(self)
                            desktop_object = ma.load_items(desktop_objects)

                        # QT ?

                        if desktop_object != None:
                            exec_array = desktop_object.get_exec_array()
                            command = exec_array[0]
                        else:
                            return 1, "User Cancelled"

                    else:
                        if len(desktop_objects) > 0:
                            desktop_object = desktop_objects[0]
                            exec_array = desktop_object.get_exec_array()
                            command = exec_array[0]

        return ex.execute(command, cmg_path, args, capture_error, cmg_version)

    def check_for_missing_library(self, command, cmg_path):
        ex = KlikExecute(self)
        return ex.check_for_missing_library(command, cmg_path)

    def create_jailed_cmg(self, cmg_path, jail_type="data"):
        sandbox = KlikSandBox(cmg_path)
        sandbox.create_jail(jail_type)
        return

    def extract_recipe(self, cmg_path):
        temp_path = tempfile.mktemp()
        self.extract_file(cmg_path, "/recipe.xml", temp_path)
        try:
            recipe = self.load_recipe(temp_path)
            os.remove(temp_path)
            return recipe
        except:
            # no recipe
            pass

        return None

    def extract_file(self, cmg_path, file_path_from, file_path_to):
        if self.is_klik1_cmg(cmg_path):
            if file_path_from[0] == "/": file_path_from = file_path_from[1:]
            temp_path = tempfile.mkdtemp('.extract.' + os.environ["USER"],
                                         self.settings.temp_directory_path)
            try:
                try:
                    subprocess.Popen([
                        "/opt/klik/bin/fusecram", "-n", "-p", cmg_path,
                        temp_path, "-s"
                    ]).wait()
                    shutil.copy(os.path.join(temp_path, file_path_from),
                                file_path_to)
                except IOError:
                    return False
            finally:
                subprocess.Popen(["fusermount", "-u", temp_path]).wait()
        else:
            subprocess.Popen([
                "cmginfo", "-f", cmg_path, "-e", file_path_from, "-o",
                file_path_to
            ]).wait()
        return os.path.exists(file_path_to)

    def open_application_folder(self, index=0):
        self.xdg.open(self.settings.application_folders[index])

    def is_valid_cmg(self, path):
        if os.path.isfile(path):
            mtype, entype = mimetypes.guess_type(path)
            if mtype in ["application/x-extension-cmg"]:
                return 2
            else:
                if self.is_klik2_cmg(path):
                    return 2
                if self.is_klik1_cmg(path):
                    return 1
        return 1

    def is_klik2_cmg(self, path):
        # Klik2 Compatability....
        f = open(path, "r")
        f.seek(22)  # Go to the 22nd byte in the file
        magic = str(f.read(28)).strip()
        test = magic.startswith(
            "# Compressed Application Ima"
        ) or magic.startswith("# KLIK2 ISO") or magic.startswith(
            "Compressed Application Image") or magic.startswith("KLIK2 ISO")
        return test

    def is_klik1_cmg(self, path):
        # Klik1 Compatability....
        f = open(path, "r")
        f.seek(16)  # Go to the 16th byte in the file
        test = (str(f.read(16)).strip() == "Compressed ROMFS")
        f.close()
        return test

    def is_valid_recipe(self, filename):
        mtype, entype = mimetypes.guess_type(filename)
        if mtype in [
                "text/xml", "application/xml",
                "application/x-extension-cmg-recipe"
        ]:
            return True
        if mtype == None:
            return filename.endswith(".recipe") or filename.endswith(".xml")
        return False

    def find_sub_directories(self, path, cmg_path):
        directories = []
        p = subprocess.Popen(["cmginfo", "-f", cmg_path, "-l", path, "-v"],
                             stdout=subprocess.PIPE)
        for line in p.stdout:
            line = line.strip()
            if line[0] == "d":
                directories.append(line[2:])
        p.stdout.close()
        return directories

    # Return all the files in a given CMG that match pre and post text
    def find_files(self,
                   cmg_path,
                   pre_text=None,
                   post_text=None,
                   omit_extension=False):
        if omit_extension:
            # If post text contains a file extension, remove it
            if post_text:
                post_text = os.path.splitext(post_text)[0]

        files = []
        p = subprocess.Popen(["isoinfo", "-R", "-f", "-i", cmg_path],
                             stdout=subprocess.PIPE)
        #p = subprocess.Popen(["cmginfo", cmg_path], stdout=subprocess.PIPE) - this is WAY to slow
        for file in p.stdout:
            file = file.strip()
            if post_text != None:
                if omit_extension:
                    file_ends = file.endswith(post_text, 0, -4)
                else:
                    file_ends = file.endswith(post_text)
            if (pre_text == None
                    or file.startswith(pre_text)) and (post_text == None
                                                       or file_ends):
                files.append(file)

        p.stdout.close()

        return files

    def scan_cmg_icons(self, cmg_path, do):
        # Returns a list of icons for a .desktop entry

        icon = do.get("Icon")

        if not icon:
            # No icon specifyed, fallback to CMG icon
            return ["/.DirIcon"]

        else:
            # Check if the icon belongs to the default theme
            theme_icon = None
            if icon[0] != "/" and self.xdg.get_is_gtk_installed():
                try:
                    # never rely on gtk
                    import gtk.utils
                    theme_icon = gtk.utils.find_icon_from_theme(icon)
                except:
                    pass

                if theme_icon:
                    return [theme_icon]

            if not theme_icon:
                # If icon doesn't belong to a theme, search it in the CMG
                icon_files = self.find_files(cmg_path,
                                             "/usr/share/icons/",
                                             icon,
                                             omit_extension=True)
                if icon_files:
                    return icon_files
                else:
                    # Icon doesn't exist, fallback to CMG icon
                    return ["/.DirIcon"]

    # Get desktop objects set to execute cmg
    def get_desktop_objects(self, cmg_path, fix_exec=True):

        # Search application .desktop files
        desktop_files = self.find_files(cmg_path, "/usr/share/applications/",
                                        ".desktop")

        desktop_objects = []
        for file in desktop_files:

            # Extract and parse the .desktop file
            temp_path = tempfile.mktemp()

            if not self.extract_file(cmg_path, file, temp_path):
                print "\t!! Couldn't extract", file

            else:
                do = DesktopParser(temp_path)

                command = do.get("Exec")
                if do.get("Type") == "Application" and command != None:

                    # Parse command and change it to point to CMG
                    if fix_exec:
                        do.set("Exec",
                               "klik exec \"" + cmg_path + "\" " + command)
                    else:
                        do.set("Exec", command)
                    do.set("X-CMG", cmg_path)

                    desktop_objects.append(do)

                os.remove(temp_path)

        # Attempt to generate desktop files from debian menu
        if len(desktop_objects) == 0:
            menu_files = self.find_files(cmg_path, "/usr/share/menu/")

            for menu_file in menu_files:

                # Extract and parse the .desktop file
                temp_path = tempfile.mktemp()

                if not self.extract_file(cmg_path, menu_file, temp_path):
                    print "\t!! Couldn't extract", menu_file

                else:
                    for mo in MenuParser(temp_path).menu_objects:
                        do = DesktopParser()

                        do.set("Type", "Application")
                        if fix_exec:
                            do.set(
                                "Exec",
                                "klik exec \"" + cmg_path + "\" " + mo.command)
                        else:
                            do.set("Exec", mo.command)

                        do.set("X-CMG", cmg_path)
                        do.set("Name", mo.title.capitalize())
                        #do.set("Terminal", recipe.require_terminal)
                        do.set("Icon", mo.icon)
                        do.set("Categories", mo.freedesktop_category)

                        desktop_objects.append(do)

        # Couldn't find any desktop file, build a basic one
        if len(desktop_objects) == 0:

            # With klik2 packages, extract information from recipe
            # With klik1 packages, make up information
            cmg_version = self.is_valid_cmg(cmg_path)

            recipe = None
            if cmg_version == 2:
                recipe = self.extract_recipe(cmg_path)

            do = DesktopParser()

            if fix_exec or (cmg_version == 1):
                do.set("Exec", "klik \"" + cmg_path +
                       "\"")  # dont need to not fix this one..
            else:
                if recipe.command:
                    do.set("Exec", recipe.command)
                else:
                    do.set("Exec", recipe.name)

            if recipe:
                name = recipe.name.capitalize()
                terminal = recipe.require_terminal
            else:
                name = os.path.splitext(
                    os.path.split(cmg_path)[1])[0].capitalize()
                terminal = False

            do.set("Name", name)
            do.set("Terminal", terminal)

            do.set("Type", "Application")
            do.set("Icon", "/.DirIcon")
            do.set("X-CMG", cmg_path)

            # Debtags / categories
            if recipe and recipe.debtags.has_key("use"):
                category = klik.utils.freedesktop.get_freedesktop_category_from_debtag(
                    recipe.debtags["use"])
                if category != None:
                    do.set("Categories", category)

            desktop_objects.append(do)

        return desktop_objects

    def extract_icon(self, cmg_path, file_path_to, image_size):

        if self.extract_file(cmg_path, "/.DirIcon", file_path_to):

            # Check image size
            if image_size > -1:
                try:
                    from klik.utils.images import Images
                    img = Images()
                    # NOTE : image_size is the thumbnail size NOT the icon size....
                    img.check_image_size(file_path_to,
                                         self.settings.desktop_icon_size)

                except Exception, text:
                    pass
            return True
        return False
Exemplo n.º 8
0
opt = OptionParser()
opt.add_option("--prefix",
               metavar="<dir>",
               dest="prefix",
               default = "/opt/klik")

(options, args) = opt.parse_args()

klik_install_path = options.prefix
klik_files_path = sys.path[0]

sys.path.insert(1, klik_files_path + "/klikclient/lib")

# XDG UTILS
from klik.utils.xdg import XdgUtils
xdg = XdgUtils( os.path.join(sys.path[0], "klikclient", "lib", "klik", "utils", "xdg-utils") )

# Check for root
if xdg.is_root() == False:
	print ("You must be root in order to run "+sys.argv[0]+", exiting")
	sys.exit(1)


def check_command_exists( command ):
	path = Popen(["which", command], stdout=PIPE, stderr=open(os.devnull, "w")).communicate()[0].strip()

	if path != "":
		print " - %s: found at %s " % (command, path)
	else:
		print " - %s: NOT FOUND" % (command)
		print ""
Exemplo n.º 9
0
class KlikBase (object):

	def __init__(self, sys_path=sys.path[0]):

		self.sys_path = sys_path

		self.events = KlikBaseEvents()
		self.create = KlikCreate(self)
		self.settings = KlikSettings()
		self.xdg = XdgUtils()
		self.notify = Notify()

		self.create.events.on_download_progress += self.__download_progress
		self.create.events.print_to_stdout += self.print_to_stdout
		self.current_progress_percentage = 0
		self.current_progress_text = ""
		self.temp_paths = []

		# Insure temp path exists
		if not os.path.exists( self.settings.temp_directory_path ):
			os.mkdir(self.settings.temp_directory_path)

	# Custom print method so we can capture stdout
	def print_to_stdout(self, obj, no_new_line=False):
		if no_new_line:
			sys.stdout.write( obj )
			sys.stdout.flush()
			self.events.print_to_stdout( obj )
		else:
			print obj
			self.events.print_to_stdout( obj + "\n")

	def post_feedback(self, recipe, workingyesno, errorreport, librarytrace, md5sum, comment):

		content = []

		content.append("package=" + urllib.quote( recipe.name )) #
		content.append("version=" + urllib.quote( recipe.version ))

		content.append("workingyesno=" + urllib.quote( str(workingyesno) )) #
		content.append("systeminformation=" + urllib.quote( self.system_info() )) #
		content.append("comment=" + urllib.quote( comment )) #

		#  f1 = file(os.path.join(path_1, os.listdir(path_1)[0]) ,'rb')
		# md5.new(f1.read()).digest()
		# f1.close()

		# only post error info on a failure
		if not workingyesno:
			content.append("errorreport=" + urllib.quote( errorreport ))
			content.append("librarytrace=" + urllib.quote( librarytrace ))
			content.append("recipe=" + urllib.quote( recipe.original_xml ))
			#content.append("md5sum=" + urllib.quote( librarytrace ))

		url_parts = urlparse.urlparse(self.settings.default_feedback_url)
		body = "&".join(content)
		h = httplib.HTTP(url_parts[1])
		h.putrequest('POST', url_parts[2])
		h.putheader('content-type', "application/x-www-form-urlencoded")
		h.putheader('content-length', str(len(body)))
		h.endheaders()

		try:
			h.send(body)
			#h.getreply()
			#print h.file.read()
		except:
			pass

	def check_for_updated_recipe(self, recipe):
		try:
			new_recipe = self.get_recipe(recipe.source_uri)
			
			# Has the recipe changed
			old_md5_hash = md5.new(recipe.original_xml).hexdigest()
			new_md5_hash = md5.new(new_recipe.original_xml).hexdigest()
			
			if not old_md5_hash == new_md5_hash:
				# Should we also check application version numbers?
				# A recipe might change just to fix a run time error etc...
				return True, new_recipe
		except:
			pass
		
		# If we cant get a recipe always return false
		return False, None

	def get_recipe(self, application_name ):

		# Get information about users system
		plat = platform.system() + " " + platform.machine()
		pyvers = "Python/"+ platform.python_version()

		useragent = "klik/%s (%s; %s; %s; %s;) %s (%s)" % (self.settings.version, self.xdg.get_is_terminal(), plat, self.xdg.get_lang(),  self.xdg.get_desktop_enviroment()+"+"+ "+".join(self.xdg.get_installed_libs()), pyvers, self.xdg.get_distribution())

		# Convert to lower case for comparing
		application_name = application_name.lower()

		# Download Recipe
		if application_name.startswith("http://"):
			url = application_name
		else:
			# Strip klik protocol
			if application_name.startswith("klik2://"):
				application_name = application_name[8:]
			elif application_name.startswith("klik2:"):
				application_name = application_name[6:]
				
			if application_name.startswith("klik://"):
				application_name = application_name[7:]
			elif application_name.startswith("klik:"):
				application_name = application_name[5:]

			url = self.settings.default_download_url + application_name

		print "Recipe URL: " + url
		try:
			request = urllib2.Request(url)
			request.add_header('User-Agent',useragent)
			opener = urllib2.build_opener()
			data = opener.open(request).read()
		except:
			raise SafeException("Error occurred while attempting to download recipe")

		# Load Recipe
		print ""
		print "Parsing recipe..."
		recipe = KlikRecipe()
		recipe.load_from_string( data )
		self.print_recipe(recipe)

		return recipe

	def print_recipe(self, recipe):
		print ""
		print "Application details"
		print "==================="
		print u"Name: %s" % recipe.name
		print u"Version: %s" % recipe.version

		trusted, text = recipe.is_trusted()
		if trusted == True:
			print "Trusted Contact : " +  text
		else:
			print "Trust Level : UNTRUSTED - This recipe has not been signed by someone you trust"
			
		print u"Summary: %s" % recipe.summary
		print u"Description: %s" % recipe.description
		print ""

	def load_recipe(self, recipe_path):
		recipe = KlikRecipe()
		recipe.load_from_file( recipe_path )
		return recipe

	def create_cmg(self, recipe_object ):
		return self.create.create( recipe_object, self.settings.destination )

	def execute_cmg(self, command, cmg_path, args, capture_error=False, force_prompt=False ):
		ex = KlikExecute(self)
		desktop_object = None
		
		cmg_version = self.is_valid_cmg(cmg_path)
		
		if cmg_version == 2:
		
			# CLI should add commands to line
			if force_prompt or command == None or command.strip() == "":

				# Need to check recipe command
				# Parse out command and parameters
				recipe = self.extract_recipe(cmg_path)
			
				parse_count = 0
				parse_args = len(args) == 0
				p = re.compile("(('[^']+')|(\"[^\"]+\")|([^\s]+))+")
				for r in re.finditer(p, recipe.command):
					if parse_count == 0:
						command = r.group(0)
					elif parse_args:
						args.append(r.group(0))
					parse_count = parse_count + 1

				# look at desktop files
				if force_prompt or command == None or command.strip() == "" or self.settings.always_show_app_chooser:

					desktop_objects = self.get_desktop_objects(cmg_path, False)
					if force_prompt or self.xdg.get_is_x_display() and len(desktop_objects) > 1:

						desktop_object = None

						# if self.xdg.get_desktop_enviroment() == "GNOME" and :
						if self.xdg.get_is_gtk_installed():
							# this dialouge will return the selected desktop object
							from klik.gtk.multiple import MultipleApplications
							ma = MultipleApplications(self)
							desktop_object = ma.load_items( desktop_objects )

						# QT ?

						if desktop_object != None:
							exec_array = desktop_object.get_exec_array()
							command = exec_array[0]
						else:
							return 1, "User Cancelled"
					
				

					else:
						if len(desktop_objects) > 0:
							desktop_object  = desktop_objects[0]
							exec_array = desktop_object.get_exec_array()
							command = exec_array[0]
					
		return ex.execute( command, cmg_path, args, capture_error, cmg_version )
		


	def check_for_missing_library(self, command, cmg_path):
		ex = KlikExecute(self)
		return ex.check_for_missing_library( command, cmg_path )

	def create_jailed_cmg(self, cmg_path, jail_type="data"):
		sandbox = KlikSandBox(cmg_path)
		sandbox.create_jail( jail_type )
		return 

	def extract_recipe(self, cmg_path):
		temp_path = tempfile.mktemp()
		self.extract_file(cmg_path, "/recipe.xml", temp_path)
		try:
			recipe = self.load_recipe( temp_path )
			os.remove(temp_path)
			return recipe
		except:
			# no recipe
			pass
			
		return None
	
	def extract_file(self, cmg_path, file_path_from, file_path_to):
		if self.is_klik1_cmg( cmg_path ):
			if file_path_from[0] == "/": file_path_from = file_path_from[1:]
			temp_path = tempfile.mkdtemp( '.extract.' + os.environ["USER"], self.settings.temp_directory_path )
			try:
				try:
					subprocess.Popen(["/opt/klik/bin/fusecram", "-n", "-p", cmg_path, temp_path, "-s"]).wait()
					shutil.copy( os.path.join(temp_path, file_path_from), file_path_to )
				except IOError:
					return False
			finally:
				subprocess.Popen(["fusermount", "-u", temp_path]).wait()
		else:
			subprocess.Popen(["cmginfo", "-f", cmg_path, "-e", file_path_from, "-o", file_path_to]).wait()
		return os.path.exists( file_path_to )

	def open_application_folder(self, index=0):
		self.xdg.open(self.settings.application_folders[index])

	def is_valid_cmg(self, path):
		if os.path.isfile( path ):
			mtype, entype = mimetypes.guess_type(path)
			if mtype in ["application/x-extension-cmg"]:
				return 2
			else:
				if self.is_klik2_cmg( path ):
					return 2
				if self.is_klik1_cmg( path ):
					return 1
		return 1

	def is_klik2_cmg(self, path):
		# Klik2 Compatability....
		f = open(path, "r")
		f.seek(22)     # Go to the 22nd byte in the file
		magic = str(f.read(28)).strip()
		test = magic.startswith( "# Compressed Application Ima" ) or magic.startswith( "# KLIK2 ISO" ) or magic.startswith( "Compressed Application Image" ) or magic.startswith( "KLIK2 ISO")
		return test
		
	def is_klik1_cmg(self, path):
		# Klik1 Compatability....
		f = open(path, "r")
		f.seek(16)     # Go to the 16th byte in the file
		test = ( str(f.read(16)).strip() == "Compressed ROMFS" )
		f.close()
		return test
		
	def is_valid_recipe(self, filename):
		mtype, entype = mimetypes.guess_type(filename)
		if mtype in [ "text/xml", "application/xml", "application/x-extension-cmg-recipe" ]:
			return True
		if mtype == None:
			return filename.endswith(".recipe") or filename.endswith(".xml") 
		return False 

	def find_sub_directories(self, path, cmg_path):
		directories = []
		p = subprocess.Popen(["cmginfo", "-f", cmg_path, "-l", path, "-v"], stdout=subprocess.PIPE)
		for line in p.stdout:
			line = line.strip()
			if line[0] == "d":
				directories.append( line[2:] )
		p.stdout.close()
		return directories


	# Return all the files in a given CMG that match pre and post text
	def find_files(self, cmg_path, pre_text=None, post_text=None, omit_extension=False):
		if omit_extension:
			# If post text contains a file extension, remove it
			if post_text:
				post_text = os.path.splitext( post_text )[0]

		files = []
		p = subprocess.Popen(["isoinfo", "-R", "-f", "-i", cmg_path], stdout=subprocess.PIPE)
		#p = subprocess.Popen(["cmginfo", cmg_path], stdout=subprocess.PIPE) - this is WAY to slow
		for file in p.stdout:
			file = file.strip()
			if post_text != None:
				if omit_extension:
					file_ends = file.endswith(post_text, 0, -4)
				else:
					file_ends = file.endswith(post_text)
			if (pre_text == None or file.startswith(pre_text)) and (post_text == None or file_ends):
				files.append( file )

		p.stdout.close()

		return files

	def scan_cmg_icons(self, cmg_path, do):
		# Returns a list of icons for a .desktop entry

		icon = do.get( "Icon" )

		if not icon:
			# No icon specifyed, fallback to CMG icon
			return ["/.DirIcon"]

		else:
			# Check if the icon belongs to the default theme
			theme_icon = None
			if icon[0] != "/" and self.xdg.get_is_gtk_installed():
				try:
					# never rely on gtk
					import gtk.utils
					theme_icon = gtk.utils.find_icon_from_theme( icon )
				except:
					pass

				if theme_icon:
					return [theme_icon]

			if not theme_icon:
				# If icon doesn't belong to a theme, search it in the CMG
				icon_files = self.find_files( cmg_path, "/usr/share/icons/", icon, omit_extension=True )
				if icon_files:
					return icon_files
				else:
					# Icon doesn't exist, fallback to CMG icon
					return ["/.DirIcon"]


	# Get desktop objects set to execute cmg
	def get_desktop_objects(self, cmg_path, fix_exec=True):

		# Search application .desktop files
		desktop_files = self.find_files(cmg_path, "/usr/share/applications/", ".desktop")
		
		desktop_objects = []
		for file in desktop_files:

			# Extract and parse the .desktop file
			temp_path = tempfile.mktemp()

			if not self.extract_file( cmg_path, file, temp_path ):
				print "\t!! Couldn't extract", file

			else:
				do = DesktopParser( temp_path )

				command = do.get("Exec")
				if do.get("Type") == "Application" and command != None:

					# Parse command and change it to point to CMG
					if fix_exec:
						do.set( "Exec", "klik exec \"" + cmg_path + "\" " + command )
					else:
						do.set( "Exec", command )
					do.set( "X-CMG", cmg_path )

					desktop_objects.append( do )

				os.remove( temp_path )

		# Attempt to generate desktop files from debian menu
		if len(desktop_objects) == 0:
			menu_files = self.find_files(cmg_path, "/usr/share/menu/")
			
			for menu_file in menu_files:

				# Extract and parse the .desktop file
				temp_path = tempfile.mktemp()

				if not self.extract_file( cmg_path, menu_file, temp_path ):
					print "\t!! Couldn't extract", menu_file

				else:
					for mo in MenuParser( temp_path ).menu_objects:
						do = DesktopParser()

						do.set("Type", "Application")
						if fix_exec:
							do.set("Exec", "klik exec \"" + cmg_path + "\" " + mo.command)
						else:
							do.set("Exec", mo.command)

						do.set( "X-CMG", cmg_path )
						do.set("Name", mo.title.capitalize())
						#do.set("Terminal", recipe.require_terminal)
						do.set("Icon", mo.icon)
						do.set("Categories", mo.freedesktop_category)

						desktop_objects.append( do )

		# Couldn't find any desktop file, build a basic one
		if len(desktop_objects) == 0:
		
			# With klik2 packages, extract information from recipe
			# With klik1 packages, make up information
			cmg_version = self.is_valid_cmg(cmg_path)
			
			recipe = None
			if cmg_version == 2:
				recipe = self.extract_recipe( cmg_path )
		
			do = DesktopParser()
			
			if fix_exec or (cmg_version == 1):
				do.set("Exec", "klik \"" + cmg_path + "\"") # dont need to not fix this one..
			else:
				if recipe.command:
					do.set("Exec", recipe.command)
				else:
					do.set("Exec", recipe.name)
		
			if recipe:
				name = recipe.name.capitalize()
				terminal = recipe.require_terminal
			else:
				name = os.path.splitext( os.path.split( cmg_path )[1] )[0].capitalize()
				terminal = False
			
			do.set("Name", name)
			do.set("Terminal", terminal)
			
			do.set("Type", "Application")
			do.set("Icon", "/.DirIcon")
			do.set("X-CMG", cmg_path)

			# Debtags / categories
			if recipe and recipe.debtags.has_key("use"):
				category = klik.utils.freedesktop.get_freedesktop_category_from_debtag( recipe.debtags["use"] )
				if category != None:
					do.set("Categories", category)

			desktop_objects.append( do )
		
		return desktop_objects

	def extract_icon(self, cmg_path, file_path_to, image_size):

		if self.extract_file(cmg_path, "/.DirIcon", file_path_to):

			# Check image size
			if image_size > -1:
				try:
					from klik.utils.images import Images
					img = Images()
					# NOTE : image_size is the thumbnail size NOT the icon size....
					img.check_image_size(file_path_to,  self.settings.desktop_icon_size)

				except Exception, text:
					pass
			return True
		return False