| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 # coding: utf-8 | 
|  | 2 | 
|  | 3 # This file is part of the Adblock Plus build tools, | 
|  | 4 # Copyright (C) 2006-2013 Eyeo GmbH | 
|  | 5 # | 
|  | 6 # Adblock Plus is free software: you can redistribute it and/or modify | 
|  | 7 # it under the terms of the GNU General Public License version 3 as | 
|  | 8 # published by the Free Software Foundation. | 
|  | 9 # | 
|  | 10 # Adblock Plus is distributed in the hope that it will be useful, | 
|  | 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 13 # GNU General Public License for more details. | 
|  | 14 # | 
|  | 15 # You should have received a copy of the GNU General Public License | 
|  | 16 # along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|  | 17 | 
|  | 18 import os | 
|  | 19 import re | 
|  | 20 import json | 
|  | 21 import ConfigParser | 
|  | 22 from urlparse import urlparse | 
|  | 23 | 
|  | 24 from packager import readMetadata, getDefaultFileName, getBuildVersion, getTempl
     ate, Files | 
|  | 25 from packagerChrome import convertJS, importGeckoLocales, getIgnoredFiles, getPa
     ckageFiles, defaultLocale | 
|  | 26 | 
|  | 27 def processFile(path, data, params): | 
|  | 28   return data | 
|  | 29 | 
|  | 30 def createManifest(params, files): | 
|  | 31   template = getTemplate('Info.plist.tmpl', autoEscape=True) | 
|  | 32   metadata = params['metadata'] | 
|  | 33   catalog = json.loads(files['_locales/%s/messages.json' % defaultLocale]) | 
|  | 34 | 
|  | 35   def parse_section(section, depth=1): | 
|  | 36     result = {} | 
|  | 37 | 
|  | 38     if not metadata.has_section(section): | 
|  | 39       return result | 
|  | 40 | 
|  | 41     for opt in metadata.options(section): | 
|  | 42       bits = opt.split('_', depth) | 
|  | 43       key = bits.pop().replace('_', ' ').title() | 
|  | 44       val = metadata.get(section, opt) | 
|  | 45 | 
|  | 46       try: | 
|  | 47         val = int(val) | 
|  | 48       except ValueError: | 
|  | 49         try: | 
|  | 50           val = float(val) | 
|  | 51         except ValueError: | 
|  | 52           pass | 
|  | 53 | 
|  | 54       reduce(lambda d, x: d.setdefault(x, {}), bits, result)[key] = val | 
|  | 55 | 
|  | 56     return result | 
|  | 57 | 
|  | 58   def get_optional(*args): | 
|  | 59     try: | 
|  | 60       return metadata.get(*args) | 
|  | 61     except ConfigParser.Error: | 
|  | 62       return None | 
|  | 63 | 
|  | 64   allowedDomains = set() | 
|  | 65   allowAllDomains = False | 
|  | 66   allowSecurePages = False | 
|  | 67 | 
|  | 68   for perm in metadata.get('general', 'permissions').split(): | 
|  | 69     if perm == '<all_urls>': | 
|  | 70       allowAllDomains = True | 
|  | 71       allowSecurePages = True | 
|  | 72       continue | 
|  | 73 | 
|  | 74     url = urlparse(perm) | 
|  | 75 | 
|  | 76     if url.scheme == 'https': | 
|  | 77       allowSecurePages = True | 
|  | 78     elif url.scheme != 'http': | 
|  | 79       continue | 
|  | 80 | 
|  | 81     if '*' in url.hostname: | 
|  | 82       allowAllDomains = True | 
|  | 83       continue | 
|  | 84 | 
|  | 85     allowedDomains.add(url.hostname) | 
|  | 86 | 
|  | 87   return template.render( | 
|  | 88     basename=metadata.get('general', 'basename'), | 
|  | 89     version=params['version'], | 
|  | 90     shortVersion=metadata.get('general', 'version'), | 
|  | 91     releaseBuild=params['releaseBuild'], | 
|  | 92     name=catalog['name']['message'], | 
|  | 93     description=catalog['description']['message'], | 
|  | 94     author=get_optional('general', 'author'), | 
|  | 95     homepage=get_optional('general', 'homepage'), | 
|  | 96     updateURL=get_optional('general', 'updateURL'), | 
|  | 97     allowedDomains=allowedDomains, | 
|  | 98     allowAllDomains=allowAllDomains, | 
|  | 99     allowSecurePages=allowSecurePages, | 
|  | 100     startScripts=(get_optional('contentScripts', 'document_start') or '').split(
     ), | 
|  | 101     endScripts=(get_optional('contentScripts', 'document_end') or '').split(), | 
|  | 102     menus=parse_section('menus', 2), | 
|  | 103     toolbarItems=parse_section('toolbar_items'), | 
|  | 104     popovers=parse_section('popovers') | 
|  | 105   ).encode('utf-8') | 
|  | 106 | 
|  | 107 def createBackgroundPage(params): | 
|  | 108   template = getTemplate('background.html.tmpl', autoEscape=True) | 
|  | 109   return template.render( | 
|  | 110     backgroundScripts=params['metadata'].get( | 
|  | 111       'general', 'backgroundScripts' | 
|  | 112     ).split() | 
|  | 113   ).encode('utf-8') | 
|  | 114 | 
|  | 115 def createInfoModule(params): | 
|  | 116   template = getTemplate('safariInfo.js.tmpl') | 
|  | 117   return template.render(params).encode('utf-8'); | 
|  | 118 | 
|  | 119 def createSignedXarArchive(outFile, files, keyFile): | 
|  | 120   import subprocess | 
|  | 121   import tempfile | 
|  | 122   import shutil | 
|  | 123   import M2Crypto | 
|  | 124 | 
|  | 125   # write files to temporary directory and create a xar archive | 
|  | 126   dirname = tempfile.mkdtemp() | 
|  | 127   try: | 
|  | 128     for filename, contents in files.iteritems(): | 
|  | 129       path = os.path.join(dirname, filename) | 
|  | 130 | 
|  | 131       try: | 
|  | 132         os.makedirs(os.path.dirname(path)) | 
|  | 133       except OSError: | 
|  | 134         pass | 
|  | 135 | 
|  | 136       with open(path, 'wb') as file: | 
|  | 137         file.write(contents) | 
|  | 138 | 
|  | 139     subprocess.check_output( | 
|  | 140       ['xar', '-czf', os.path.abspath(outFile), '--distribution'] + os.listdir(d
     irname), | 
|  | 141       cwd=dirname | 
|  | 142     ) | 
|  | 143   finally: | 
|  | 144     shutil.rmtree(dirname) | 
|  | 145 | 
|  | 146   certificate_filenames = [] | 
|  | 147   try: | 
|  | 148     # load key and certificates from the all-in-one key file | 
|  | 149     # and write each certificate in DER format to a seperate | 
|  | 150     # temporary file, that they can be passed to xar | 
|  | 151     bio = M2Crypto.BIO.openfile(keyFile) | 
|  | 152     try: | 
|  | 153       key = M2Crypto.RSA.load_key_bio(bio) | 
|  | 154 | 
|  | 155       bio.reset() | 
|  | 156       while True: | 
|  | 157         try: | 
|  | 158           cert = M2Crypto.X509.load_cert_bio(bio) | 
|  | 159         except M2Crypto.X509.X509Error: | 
|  | 160           break | 
|  | 161 | 
|  | 162         fd, filename = tempfile.mkstemp() | 
|  | 163         try: | 
|  | 164           certificate_filenames.append(filename) | 
|  | 165           os.write(fd, cert.as_der()) | 
|  | 166         finally: | 
|  | 167           os.close(fd) | 
|  | 168     finally: | 
|  | 169       bio.close() | 
|  | 170 | 
|  | 171     # add certificates and placeholder signature | 
|  | 172     # to the xar archive, and get data to sign | 
|  | 173     fd, digestinfo_filename = tempfile.mkstemp() | 
|  | 174     os.close(fd) | 
|  | 175     try: | 
|  | 176       subprocess.check_call( | 
|  | 177         [ | 
|  | 178           'xar', '--sign', '-f', outFile, | 
|  | 179           '--digestinfo-to-sign', digestinfo_filename, | 
|  | 180           '--sig-size', str(len(key.private_encrypt('', M2Crypto.RSA.pkcs1_paddi
     ng))) | 
|  | 181         ] + [ | 
|  | 182           arg for cert in certificate_filenames for arg in ('--cert-loc', cert) | 
|  | 183         ] | 
|  | 184       ) | 
|  | 185 | 
|  | 186       with open(digestinfo_filename, 'rb') as file: | 
|  | 187         digestinfo = file.read() | 
|  | 188     finally: | 
|  | 189       os.unlink(digestinfo_filename) | 
|  | 190   finally: | 
|  | 191     for filename in certificate_filenames: | 
|  | 192       os.unlink(filename) | 
|  | 193 | 
|  | 194   # sign data and inject signature into xar archive | 
|  | 195   fd, signature_filename = tempfile.mkstemp() | 
|  | 196   try: | 
|  | 197     try: | 
|  | 198       os.write(fd, key.private_encrypt( | 
|  | 199         digestinfo, | 
|  | 200         M2Crypto.RSA.pkcs1_padding | 
|  | 201       )) | 
|  | 202     finally: | 
|  | 203       os.close(fd) | 
|  | 204 | 
|  | 205     subprocess.check_call(['xar', '--inject-sig', signature_filename, '-f', outF
     ile]) | 
|  | 206   finally: | 
|  | 207     os.unlink(signature_filename) | 
|  | 208 | 
|  | 209 def createBuild(baseDir, type, outFile=None, buildNum=None, releaseBuild=False, 
     keyFile=None): | 
|  | 210   metadata = readMetadata(baseDir, type) | 
|  | 211   version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) | 
|  | 212 | 
|  | 213   if not outFile: | 
|  | 214     outFile = getDefaultFileName(baseDir, metadata, version, 'safariextz' if key
     File else 'zip') | 
|  | 215 | 
|  | 216   params = { | 
|  | 217     'type': type, | 
|  | 218     'baseDir': baseDir, | 
|  | 219     'releaseBuild': releaseBuild, | 
|  | 220     'version': version, | 
|  | 221     'devenv': False, | 
|  | 222     'metadata': metadata, | 
|  | 223   } | 
|  | 224 | 
|  | 225   files = Files(getPackageFiles(params), getIgnoredFiles(params), | 
|  | 226                 process=lambda path, data: processFile(path, data, params)) | 
|  | 227   if metadata.has_section('mapping'): | 
|  | 228     files.readMappedFiles(metadata.items('mapping')) | 
|  | 229   files.read(baseDir) | 
|  | 230 | 
|  | 231   if metadata.has_section('convert_js'): | 
|  | 232     convertJS(params, files) | 
|  | 233 | 
|  | 234   if metadata.has_section('convert_img'): | 
|  | 235     from imageConversion import convertImages | 
|  | 236     convertImages(params, files) | 
|  | 237 | 
|  | 238   if metadata.has_section('preprocess'): | 
|  | 239     files.preprocess( | 
|  | 240       [f for f, _ in metadata.items('preprocess')], | 
|  | 241       {'needsExt': True} | 
|  | 242     ) | 
|  | 243 | 
|  | 244   if metadata.has_section('import_locales'): | 
|  | 245     importGeckoLocales(params, files) | 
|  | 246 | 
|  | 247   files['lib/info.js'] = createInfoModule(params) | 
|  | 248   files['background.html'] = createBackgroundPage(params) | 
|  | 249   files['Info.plist'] = createManifest(params, files) | 
|  | 250 | 
|  | 251   dirname = metadata.get('general', 'basename') + '.safariextension' | 
|  | 252   for filename in files.keys(): | 
|  | 253     files[os.path.join(dirname, filename)] = files.pop(filename) | 
|  | 254 | 
|  | 255   if keyFile: | 
|  | 256     createSignedXarArchive(outFile, files, keyFile) | 
|  | 257   else: | 
|  | 258     files.zip(outFile) | 
| OLD | NEW | 
|---|