def check_qtwebkit(self): '''Check that QML applications don't use QtWebKit''' if not self.is_click and not self.is_snap1: return t = 'info' n = self._get_check_name('qml_application_uses_QtWebKit') s = "OK" link = None qmls = [] pat_mv = re.compile(r'\n\s*import\s+QtWebKit') for i in self.qml_files: qml = open_file_read(i).read() if pat_mv.search(qml): qmls.append(os.path.relpath(i, self.unpack_dir)) if len(qmls) > 0: t = 'warn' s = "Found files that use unsupported QtWebKit (should use " + \ "UbuntuWebview (Ubuntu.Components.Extras.Browser >= " + \ "0.2) or Oxide instead): %s" % " ,".join(qmls) link = "http://askubuntu.com/questions/417342/what-does-functional-qml-application-uses-qtwebkit-mean/417343" self._add_result(t, n, s, link) t = 'info' n = self._get_check_name('qml_application_uses_UbuntuWebView_0.2') s = "OK" link = None if self.manifest is not None and \ self.manifest['framework'] == "ubuntu-sdk-13.10": s = "SKIPPED (Oxide not available in ubuntu-sdk-13.10)" else: qmls = [] pat_mv = re.compile( r'\n\s*import\s+Ubuntu\.Components\.Extras\.Browser\s+0\.1\s*\n' ) for i in self.qml_files: qml = open_file_read(i).read() if pat_mv.search(qml): qmls.append(os.path.relpath(i, self.unpack_dir)) if len(qmls) > 0: t = 'warn' s = "Found files that use unsupported QtWebKit via " + \ "'Ubuntu.Components.Extras.Browser 0.1' (should use " + \ "Ubuntu.Components.Extras.Browser >= 0.2 or " + \ "Oxide instead): %s" % " ,".join(qmls) link = "http://askubuntu.com/questions/417342/what-does-functional-qml-application-uses-qtwebkit-mean/417343" self._add_result(t, n, s, link)
def _extract_framework_policy(self): '''Get framework policy files''' policy_dict = dict() unknown = [] fpdir = os.path.join(self.unpack_dir, "meta", "framework-policy") for i in glob.glob("%s/*" % fpdir): rel_i = os.path.basename(i) if not os.path.isdir(i) or rel_i not in self.framework_policy_dirs: unknown.append(os.path.relpath(i, self.unpack_dir)) continue policy_dict[rel_i] = dict() for j in glob.glob("%s/*" % i): rel_j = os.path.basename(j) if not os.path.isdir(j) or \ rel_j not in self.framework_policy_subdirs: unknown.append(os.path.relpath(j, self.unpack_dir)) continue policy_dict[rel_i][rel_j] = dict() for k in glob.glob("%s/*" % j): rel_k = os.path.basename(k) if not os.path.isfile(k): unknown.append(os.path.relpath(k, self.unpack_dir)) continue fh = open_file_read(k) policy_dict[rel_i][rel_j][rel_k] = fh.read() fh.close() return (policy_dict, unknown)
def _extract_push_helper(self, app): '''Get push-helper hook content''' c = self.manifest['hooks'][app]['push-helper'] fn = os.path.join(self.unpack_dir, c) bn = os.path.basename(fn) if not os.path.exists(fn): error("Could not find '%s'" % bn) fh = open_file_read(fn) contents = "" for line in fh.readlines(): contents += line fh.close() try: jd = json.loads(contents) except Exception as e: error("push-helper json unparseable: %s (%s):\n%s" % (bn, str(e), contents)) if not isinstance(jd, dict): error("push-helper json is malformed: %s:\n%s" % (bn, contents)) return (fn, jd)
def check_desktop_duplicate_entries(self): '''Check desktop for duplicate entries''' if not self.is_click and not self.is_snap1: return for app in sorted(self.desktop_entries): found = [] dupes = [] t = 'info' n = self._get_check_name('duplicate_keys', app=app) s = 'OK' fn = self._get_desktop_filename(app) content = open_file_read(fn).readlines() for line in content: tmp = line.split('=') if len(tmp) < 2: continue if tmp[0] in found: dupes.append(tmp[0]) else: found.append(tmp[0]) if len(dupes) > 0: t = 'error' s = 'found duplicate keys: %s' % ",".join(dupes) self._add_result(t, n, s)
def _extract_desktop_entry(self, app): '''Get DesktopEntry for desktop file and verify it''' d = self.manifest['hooks'][app]['desktop'] fn = os.path.join(self.unpack_dir, d) bn = os.path.basename(fn) if not os.path.exists(fn): error("Could not find '%s'" % bn) fh = open_file_read(fn) contents = "" for line in fh.readlines(): contents += line fh.close() try: de = DesktopEntry(fn) except xdgParsingError as e: error("desktop file unparseable: %s (%s):\n%s" % (bn, str(e), contents)) try: de.parse(fn) except Exception as e: error("desktop file unparseable: %s (%s):\n%s" % (bn, str(e), contents)) return de, fn
def _extract_url_dispatcher(self, app): '''Get url dispatcher json''' u = self.manifest['hooks'][app]['urls'] if not u: error("'urls' definition is empty for '%s'" % app) fn = os.path.join(self.unpack_dir, u) if not os.path.isfile(fn): error("'%s' is not a file" % fn) bn = os.path.basename(fn) if not os.path.exists(fn): error("Could not find '%s'" % bn) fh = open_file_read(fn) contents = "" for line in fh.readlines(): contents += line fh.close() try: jd = json.loads(contents) except Exception as e: error("url-dispatcher json unparseable: %s (%s):\n%s" % (bn, str(e), contents)) if not isinstance(jd, list): error("url-dispatcher json is malformed: %s:\n%s" % (bn, contents)) return (fn, jd)
def _extract_webapp_manifests(self): '''Extract webapp manifest file''' files = sorted( glob.glob("%s/unity-webapps-*/manifest.json" % self.unpack_dir)) manifests = dict() for fn in files: key = os.path.relpath(fn, self.unpack_dir) try: manifests[key] = json.load(open_file_read(fn)) except Exception: manifests[key] = None error("Could not parse '%s'" % fn, do_exit=False) return manifests
def _extract_framework(self, app): '''Get framework for app''' rel = self.manifest['hooks'][app]['framework'] fn = os.path.join(self.unpack_dir, rel) if not os.path.exists(fn): error("Could not find '%s'" % rel) data = dict() fh = open_file_read(fn) for line in fh.readlines(): tmp = line.split(':') if len(tmp) != 2: continue data[tmp[0].strip()] = tmp[1].strip() fh.close() return (fn, data)
def _extract_account(self, app, account_type): '''Extract accounts''' if self.manifest is None: return a = self.manifest['hooks'][app][account_type] fn = os.path.join(self.unpack_dir, a) bn = os.path.basename(fn) if not os.path.exists(fn): error("Could not find '%s'" % bn) # qml-plugin points to a QML file, so just set that we have the # the hook present for now if account_type == "account-qml-plugin": return (fn, True) elif account_type == "accounts": fh = open_file_read(fn) contents = "" for line in fh.readlines(): contents += line fh.close() try: jd = json.loads(contents) except Exception as e: error("accounts json unparseable: %s (%s):\n%s" % (bn, str(e), contents)) if not isinstance(jd, dict): error("accounts json is malformed: %s:\n%s" % (bn, contents)) return (fn, jd) else: try: tree = etree.parse(fn) xml = tree.getroot() except Exception as e: error("accounts xml unparseable: %s (%s)" % (bn, str(e))) return (fn, xml)
def check_friends(self): '''Check that QML applications don't use deprecated Friends API''' if not self.is_click and not self.is_snap1: return t = 'info' n = self._get_check_name('qml_application_uses_friends') s = "OK" link = None qmls = [] pat_mv = re.compile(r'\n\s*import\s+Friends') for i in self.qml_files: qml = open_file_read(i).read() if pat_mv.search(qml): qmls.append(os.path.relpath(i, self.unpack_dir)) if len(qmls) > 0: t = 'error' s = "Found files that use deprecated Friends API: %s" % " ,".join( qmls) link = "http://askubuntu.com/questions/497551/what-does-functional-qml-application-uses-friends-mean" self._add_result(t, n, s, link)
def check_applicationName(self): '''Check applicationName matches click manifest''' if not self.is_click and not self.is_snap1: return if self.manifest is None: return t = 'info' n = self._get_check_name('qml_applicationName_matches_manifest') s = "OK" link = None # find file with MainView in the QML mv = '\s*MainView\s*(\s+{)?' pat_mv = re.compile(r'\n%s' % mv) qmls = dict() for i in self.qml_files: qml = open_file_read(i).read() if pat_mv.search(qml): qmls[i] = qml # LP: #1256841 - QML apps with C++ using QSettings shouldn't # typically set applicationName in the QML for i in self.pkg_bin_files: f = open(i, 'rb') data = str(binascii.b2a_qp(f.read())) f.close() if 'QSettings' in data: s = "OK (binary uses QSettings)" self._add_result(t, n, s) return if len(self.qml_files) == 0: s = "OK (not QML)" self._add_result(t, n, s) return elif len(qmls) == 0: s = "SKIP: could not find MainView in QML files" self._add_result(t, n, s) return pat_mvl = re.compile(r'^%s' % mv) pat_appname = re.compile(r'^\s*applicationName\s*:\s*["\']') ok = False appnames = dict() for k in qmls.keys(): in_mainview = False for line in qmls[k].splitlines(): if in_mainview and pat_appname.search(line): appname = line.split(':', 1)[1].strip('"\' \t\n\r\f\v;') appnames[os.path.relpath(k, self.unpack_dir)] = appname if appname == self.click_pkgname: ok = True break elif pat_mvl.search(line): in_mainview = True if ok: break if len(appnames) == 0 or not ok: if len(self.pkg_bin_files) == 0: t = "warn" link = 'http://askubuntu.com/questions/417371/what-does-functional-qml-applicationname-matches-manifest-mean/417372' if len(appnames) == 0: s = "could not find applicationName in: %s" % \ ", ".join(sorted(list(map( lambda x: os.path.relpath(x, self.unpack_dir), qmls)))) else: # not ok s = "click manifest name '%s' not found in: " % \ self.click_pkgname + "%s" % \ ", ".join(sorted(list(map( lambda x: "%s ('%s')" % (x, appnames[x]), appnames)))) if len(self.pkg_bin_files) == 0: s += ". Application may not work properly when confined." else: s += ". May be ok (detected as compiled application)." self._add_result(t, n, s, link)