#!/usr/bin/python # Copyright (c) 2009 Benjamin Drung # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import csv import optparse import os import stat import subprocess import sys import zipfile import RDF LICENSE_PATTERN_LIST = ( "copying", "gpl.txt", "licence", "license", "licence.txt", "license.txt" ) # error codes COMMAND_LINE_SYNTAX_ERROR = 1 XPI_FILE_DOES_NOT_EXISTS = 2 def get_query_field_id_as_list(rdf_path, query_string): ret = [] model = RDF.Model() parser = RDF.Parser(name="rdfxml") parser.parse_into_model(model, "file:" + rdf_path) query = RDF.Query("PREFIX em: " + \ query_string, query_language="sparql") results = query.execute(model) for result in results: ret.append(result["id"].literal_value["string"]) return ret def get_target_applications(install_rdf): target_applications = get_query_field_id_as_list(install_rdf, "SELECT ?id WHERE { ?x1 em:targetApplication ?x2 . ?x2 em:id ?id }") return target_applications def get_extension_id(install_rdf): extension_ids = set(get_query_field_id_as_list(install_rdf, "SELECT ?id WHERE {?x1 em:targetApplication ?x2 . ?x1 em:id ?id }")) return extension_ids.pop() def get_arch(package): lines = open("debian/control").readlines() package_lines = filter(lambda x: x.find("Package:") >= 0, lines) packages = map(lambda x: x[x.find(":")+1:].strip(), package_lines) architecture_lines = filter(lambda x: x.find("Architecture:") >= 0, lines) architectures = map(lambda x: x[x.find(":")+1:].strip(), architecture_lines) (_, arch) = filter(lambda (x, y): x == package, zip(packages, architectures))[0] return arch def get_mode(filename): statinfo = os.stat(filename) mode = statinfo[stat.ST_MODE] return mode & 0777 def get_xul_apps(): csvfile = open("/usr/share/mozilla-devscripts/xul-app-data.csv") csv_reader = csv.DictReader(csvfile) rows = [] for row in csv_reader: rows.append(row) return rows def install_xpi(script_name, package, xpi_file, exclude, install_dir, links, correct_permissions, remove_licenses, system_prefs, verbose=False): # get xpi file content list if not os.path.isfile(xpi_file): print >> sys.stderr, "%s: Error: xpi file %s does not exist." % \ (script_name, xpi_file) sys.exit(XPI_FILE_DOES_NOT_EXISTS) zfobj = zipfile.ZipFile(xpi_file) xpi_content = zfobj.namelist() # determine installation directory if get_arch(package) == "all": lib_share_dir = "share" else: lib_share_dir = "lib" if install_dir is None: install_dir = os.path.join("usr", lib_share_dir, "xul-ext", package.replace("xul-ext-", "")) copy_dir = os.path.join("debian", package, install_dir.strip("/")) if verbose: print "%s: install directory: %s" % (script_name, install_dir) # remove documented license files if remove_licenses: for name in filter(lambda x: not x.endswith('/'), xpi_content): basename = os.path.basename(name).lower() if basename in LICENSE_PATTERN_LIST: exclude.append(name) print "%s: exclude license file %s" % (script_name, name) # create directory and extract xpi file if not os.path.isdir(copy_dir): os.makedirs(copy_dir) command = ["unzip", "-o", "-d", copy_dir, xpi_file] if len(exclude) > 0: command.append("-x") command.extend(exclude) print " ".join(command) subprocess.call(command) # correct permissons of files to 644 and directories to 755 if correct_permissions: for name in xpi_content: filename = os.path.join(copy_dir, name) if os.path.exists(filename): mode = get_mode(filename) if os.path.isdir(filename) and mode != 0755: print "%s: correct permission from %s to %s of %s" % \ (script_name, oct(mode), oct(0755), name) os.chmod(filename, 0755) elif os.path.isfile(filename): header = open(filename, "r").read(2) if header != "#!" and mode != 0644: # file without shebang print "%s: correct permission from %s to %s of %s" % \ (script_name, oct(mode), oct(0644), name) os.chmod(filename, 0644) elif header == "#!" and mode != 0755: # file with shebang print "%s: correct permission from %s to %s of %s" % \ (script_name, oct(mode), oct(0755), name) os.chmod(filename, 0755) # create a system preference file in /etc if system_prefs: # search for preference files pref_dir = os.path.join("defaults", "preferences") preferences = filter(lambda f: os.path.dirname(f) == pref_dir and \ f.endswith(".js"), xpi_content) if len(preferences) > 0: prefdir = os.path.join("etc", "xul-ext") full_prefdir = os.path.join("debian", package, prefdir) if not os.path.exists(full_prefdir): os.makedirs(full_prefdir) prefname = package.replace("xul-ext-", "") + ".js" # create system preference file f = open(os.path.join(full_prefdir, prefname), "w") if os.path.isfile(os.path.join("debian", package + ".js")): # use debian/package.js as configuration file if it exists content = open(os.path.join("debian", package + ".js")).read() # replace @INSTALLDIR@ by the actual installation directory content = content.replace("@INSTALLDIR@", os.path.join("/", install_dir)) f.write(content) else: f.write("// Place your preferences for " + package + " in this file.\n") f.write("// You can override here the preferences specified " "in\n") map(lambda x: f.write("// " + os.path.join("/", install_dir, x) + "\n"), preferences) f.close() link_source = os.path.join(prefdir, prefname) link_target = os.path.join(install_dir, "defaults", "preferences", "000system.js") command = ["dh_link", "-p" + package, link_source, link_target] if verbose: print " ".join(command) subprocess.call(command) # get symlinks list extension_id = get_extension_id(os.path.join(copy_dir, "install.rdf")) filename = os.path.join(copy_dir, "install.rdf") target_applications = get_target_applications(filename) for target_application in target_applications: destination = os.path.join("/usr", lib_share_dir, "mozilla/extensions", target_application, extension_id) links.add(destination) # create symlinks for link in links: command = ["dh_link", "-p" + package, install_dir, link] print " ".join(command) subprocess.call(command) def get_first_package(): lines = open("debian/control").readlines() package_lines = filter(lambda x: x.find("Package:") >= 0, lines) packages = map(lambda x: x[x.find(":")+1:].strip(), package_lines) return packages[0] def main(): usage = "%s [options] " % (os.path.basename(sys.argv[0])) epilog = "See %s(1) for more info." % (os.path.basename(sys.argv[0])) parser = optparse.OptionParser(usage=usage, epilog=epilog) parser.add_option("--disable-system-prefs", help="do not create a system preference file in /etc", dest="system_prefs", action="store_false", default=True) parser.add_option("-x", "--exclude", metavar="FILE", help="do not install specified FILE", dest="exclude", action="append", default=list()) parser.add_option("-i", "--install-dir", metavar="DIRECTORY", help="install extension into the specified DIRECTORY", dest="install_dir") parser.add_option("-l", "--link", metavar="DIRECTORY", help="link from DIRECTORY to extension directory", dest="links", action="append", default=list()) parser.add_option("-p", "--package", metavar="PACKAGE", help="install the extension into specified PACKAGE", dest="package", default=get_first_package()) parser.add_option("--preserve-permissions", help="do not adjust the file permissions", dest="correct_permissions", action="store_false", default=True) parser.add_option("-r", "--remove-license-files", help="do not install license files", dest="remove_licenses", action="store_true", default=False) parser.add_option("-v", "--verbose", help="print more information", dest="verbose", action="store_true", default=False) (options, args) = parser.parse_args() script_name = os.path.basename(sys.argv[0]) if len(args) == 0: print >> sys.stderr, "%s: Error: No xpi file specified." % (script_name) sys.exit(COMMAND_LINE_SYNTAX_ERROR) elif len(args) > 1: print >> sys.stderr, "%s: Error: Multiple xpi files specified: %s" % \ (script_name, ", ".join(args)) sys.exit(COMMAND_LINE_SYNTAX_ERROR) if options.verbose: print script_name + ": Install %s into package %s." % \ (args[0], options.package) install_xpi(script_name, options.package, args[0], options.exclude, options.install_dir, set(options.links), options.correct_permissions, options.remove_licenses, options.system_prefs, options.verbose) if __name__ == "__main__": main()