| OLD | NEW | 
 | (Empty) | 
|    1 # This Source Code Form is subject to the terms of the Mozilla Public |  | 
|    2 # License, v. 2.0. If a copy of the MPL was not distributed with this |  | 
|    3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |  | 
|    4  |  | 
|    5 import base64 |  | 
|    6 import ConfigParser |  | 
|    7 import json |  | 
|    8 import os |  | 
|    9 import re |  | 
|   10 from urlparse import urlparse |  | 
|   11  |  | 
|   12 from packager import readMetadata, getDefaultFileName, getBuildVersion, getTempl
     ate, Files |  | 
|   13 from packagerChrome import convertJS, import_locales, getIgnoredFiles, getPackag
     eFiles, defaultLocale, createScriptPage |  | 
|   14  |  | 
|   15  |  | 
|   16 def processFile(path, data, params): |  | 
|   17     return data |  | 
|   18  |  | 
|   19  |  | 
|   20 def createManifest(params, files): |  | 
|   21     template = getTemplate('Info.plist.tmpl', autoEscape=True) |  | 
|   22     metadata = params['metadata'] |  | 
|   23     catalog = json.loads(files['_locales/%s/messages.json' % defaultLocale]) |  | 
|   24  |  | 
|   25     def parse_section(section, depth=1): |  | 
|   26         result = {} |  | 
|   27  |  | 
|   28         if not metadata.has_section(section): |  | 
|   29             return result |  | 
|   30  |  | 
|   31         for opt in metadata.options(section): |  | 
|   32             bits = opt.split('_', depth) |  | 
|   33             key = bits.pop().replace('_', ' ').title() |  | 
|   34             val = metadata.get(section, opt) |  | 
|   35  |  | 
|   36             try: |  | 
|   37                 val = int(val) |  | 
|   38             except ValueError: |  | 
|   39                 try: |  | 
|   40                     val = float(val) |  | 
|   41                 except ValueError: |  | 
|   42                     pass |  | 
|   43  |  | 
|   44             reduce(lambda d, x: d.setdefault(x, {}), bits, result)[key] = val |  | 
|   45  |  | 
|   46         return result |  | 
|   47  |  | 
|   48     def get_optional(*args): |  | 
|   49         try: |  | 
|   50             return metadata.get(*args) |  | 
|   51         except ConfigParser.Error: |  | 
|   52             return None |  | 
|   53  |  | 
|   54     allowedDomains = set() |  | 
|   55     allowAllDomains = False |  | 
|   56     allowSecurePages = False |  | 
|   57  |  | 
|   58     for perm in metadata.get('general', 'permissions').split(): |  | 
|   59         if perm == '<all_urls>': |  | 
|   60             allowAllDomains = True |  | 
|   61             allowSecurePages = True |  | 
|   62             continue |  | 
|   63  |  | 
|   64         url = urlparse(perm) |  | 
|   65  |  | 
|   66         if url.scheme == 'https': |  | 
|   67             allowSecurePages = True |  | 
|   68         elif url.scheme != 'http': |  | 
|   69             continue |  | 
|   70  |  | 
|   71         if '*' in url.hostname: |  | 
|   72             allowAllDomains = True |  | 
|   73             continue |  | 
|   74  |  | 
|   75         allowedDomains.add(url.hostname) |  | 
|   76  |  | 
|   77     return template.render( |  | 
|   78         basename=metadata.get('general', 'basename'), |  | 
|   79         version=params['version'], |  | 
|   80         releaseBuild=params['releaseBuild'], |  | 
|   81         name=catalog['name']['message'], |  | 
|   82         description=catalog['description']['message'], |  | 
|   83         author=get_optional('general', 'author'), |  | 
|   84         homepage=get_optional('general', 'homepage'), |  | 
|   85         updateURL=get_optional('general', 'updateURL'), |  | 
|   86         allowedDomains=allowedDomains, |  | 
|   87         allowAllDomains=allowAllDomains, |  | 
|   88         allowSecurePages=allowSecurePages, |  | 
|   89         startScripts=(get_optional('contentScripts', 'document_start') or '').sp
     lit(), |  | 
|   90         endScripts=(get_optional('contentScripts', 'document_end') or '').split(
     ), |  | 
|   91         menus=parse_section('menus', 2), |  | 
|   92         toolbarItems=parse_section('toolbar_items'), |  | 
|   93         popovers=parse_section('popovers'), |  | 
|   94         developerIdentifier=params.get('developerIdentifier') |  | 
|   95     ).encode('utf-8') |  | 
|   96  |  | 
|   97  |  | 
|   98 def createInfoModule(params): |  | 
|   99     template = getTemplate('safariInfo.js.tmpl') |  | 
|  100     return template.render(params).encode('utf-8') |  | 
|  101  |  | 
|  102  |  | 
|  103 def _get_sequence(data): |  | 
|  104     from Crypto.Util import asn1 |  | 
|  105     sequence = asn1.DerSequence() |  | 
|  106     sequence.decode(data) |  | 
|  107     return sequence |  | 
|  108  |  | 
|  109  |  | 
|  110 def get_developer_identifier(certs): |  | 
|  111     for cert in certs: |  | 
|  112         # See https://tools.ietf.org/html/rfc5280#section-4 |  | 
|  113         tbscertificate = _get_sequence(base64.b64decode(cert))[0] |  | 
|  114         subject = _get_sequence(tbscertificate)[5] |  | 
|  115  |  | 
|  116         # We could decode the subject but since we have to apply a regular |  | 
|  117         # expression on CN entry anyway we can just skip that. |  | 
|  118         m = re.search(r'Safari Developer: \((\S*?)\)', subject) |  | 
|  119         if m: |  | 
|  120             return m.group(1) |  | 
|  121  |  | 
|  122     raise Exception('No Safari developer certificate found in chain') |  | 
|  123  |  | 
|  124  |  | 
|  125 def createBuild(baseDir, type, outFile=None, buildNum=None, releaseBuild=False, 
     keyFile=None, devenv=False): |  | 
|  126     metadata = readMetadata(baseDir, type) |  | 
|  127     version = getBuildVersion(baseDir, metadata, releaseBuild, buildNum) |  | 
|  128  |  | 
|  129     if not outFile: |  | 
|  130         outFile = getDefaultFileName(metadata, version, 'safariextz' if keyFile 
     else 'zip') |  | 
|  131  |  | 
|  132     params = { |  | 
|  133         'type': type, |  | 
|  134         'baseDir': baseDir, |  | 
|  135         'releaseBuild': releaseBuild, |  | 
|  136         'version': version, |  | 
|  137         'devenv': devenv, |  | 
|  138         'metadata': metadata, |  | 
|  139     } |  | 
|  140  |  | 
|  141     mapped = metadata.items('mapping') if metadata.has_section('mapping') else [
     ] |  | 
|  142     files = Files(getPackageFiles(params), getIgnoredFiles(params), |  | 
|  143                   process=lambda path, data: processFile(path, data, params)) |  | 
|  144     files.readMappedFiles(mapped) |  | 
|  145     files.read(baseDir, skip=[opt for opt, _ in mapped]) |  | 
|  146  |  | 
|  147     if metadata.has_section('convert_js'): |  | 
|  148         convertJS(params, files) |  | 
|  149  |  | 
|  150     if metadata.has_section('preprocess'): |  | 
|  151         files.preprocess( |  | 
|  152             [f for f, _ in metadata.items('preprocess')], |  | 
|  153             {'needsExt': True} |  | 
|  154         ) |  | 
|  155  |  | 
|  156     if metadata.has_section('import_locales'): |  | 
|  157         import_locales(params, files) |  | 
|  158  |  | 
|  159     if metadata.has_option('general', 'testScripts'): |  | 
|  160         files['qunit/index.html'] = createScriptPage(params, 'testIndex.html.tmp
     l', |  | 
|  161                                                      ('general', 'testScripts')) |  | 
|  162  |  | 
|  163     if keyFile: |  | 
|  164         from buildtools import xarfile |  | 
|  165         certs, key = xarfile.read_certificates_and_key(keyFile) |  | 
|  166         params['developerIdentifier'] = get_developer_identifier(certs) |  | 
|  167  |  | 
|  168     files['lib/info.js'] = createInfoModule(params) |  | 
|  169     files['background.html'] = createScriptPage(params, 'background.html.tmpl', |  | 
|  170                                                 ('general', 'backgroundScripts')
     ) |  | 
|  171     files['Info.plist'] = createManifest(params, files) |  | 
|  172  |  | 
|  173     dirname = metadata.get('general', 'basename') + '.safariextension' |  | 
|  174     for filename in files.keys(): |  | 
|  175         files[os.path.join(dirname, filename)] = files.pop(filename) |  | 
|  176  |  | 
|  177     if not devenv and keyFile: |  | 
|  178         from buildtools import xarfile |  | 
|  179         xarfile.create(outFile, files, keyFile) |  | 
|  180     else: |  | 
|  181         files.zip(outFile) |  | 
| OLD | NEW |