def _repair_csum(): # fix all the checksums and file sizes for fw in db.session.query(Firmware): try: with open(fw.filename_absolute, 'rb') as f: checksum_signed_sha1 = hashlib.sha1(f.read()).hexdigest() if checksum_signed_sha1 != fw.checksum_signed_sha1: print('repairing checksum from {} to {}'.format( fw.checksum_signed_sha1, checksum_signed_sha1)) fw.checksum_signed_sha1 = checksum_signed_sha1 fw.mark_dirty() checksum_signed_sha256 = hashlib.sha256(f.read()).hexdigest() if checksum_signed_sha256 != fw.checksum_signed_sha256: print('repairing checksum from {} to {}'.format( fw.checksum_signed_sha256, checksum_signed_sha256)) fw.checksum_signed_sha256 = checksum_signed_sha256 fw.mark_dirty() for md in fw.mds: sz = os.path.getsize(fw.filename_absolute) if sz != md.release_download_size: print('repairing size from {} to {}'.format( md.release_download_size, sz)) md.release_download_size = sz md.fw.mark_dirty() except FileNotFoundError as _: pass # ensure the test has been added for the firmware type if not fw.is_deleted: ploader.ensure_test_for_fw(fw) # all done db.session.commit()
def _ensure_tests(): # ensure the test has been added for the firmware type for fw in db.session.query(Firmware).order_by(Firmware.timestamp): if not fw.is_deleted: ploader.ensure_test_for_fw(fw) db.session.commit()
def _repair(): # fix any timestamps that are incorrect for md in db.session.query(Component).filter( Component.release_timestamp < 1980): fn = _get_absolute_path(md.fw) if not os.path.exists(fn): continue print(fn, md.release_timestamp) try: ufile = UploadedFile(is_strict=False) for cat in db.session.query(Category): ufile.category_map[cat.value] = cat.category_id for pro in db.session.query(Protocol): ufile.protocol_map[pro.value] = pro.protocol_id with open(fn, 'rb') as f: ufile.parse(os.path.basename(fn), f.read()) except MetadataInvalid as e: print('failed to parse file: {}'.format(str(e))) continue for md_local in ufile.fw.mds: if md_local.appstream_id == md.appstream_id: print('repairing timestamp from {} to {}'.format( md.release_timestamp, md_local.release_timestamp)) md.release_timestamp = md_local.release_timestamp md.fw.mark_dirty() # fix all the checksums and file sizes for fw in db.session.query(Firmware): try: with open(fw.filename_absolute, 'rb') as f: checksum_signed_sha1 = hashlib.sha1(f.read()).hexdigest() if checksum_signed_sha1 != fw.checksum_signed_sha1: print('repairing checksum from {} to {}'.format( fw.checksum_signed_sha1, checksum_signed_sha1)) fw.checksum_signed_sha1 = checksum_signed_sha1 fw.mark_dirty() checksum_signed_sha256 = hashlib.sha256(f.read()).hexdigest() if checksum_signed_sha256 != fw.checksum_signed_sha256: print('repairing checksum from {} to {}'.format( fw.checksum_signed_sha256, checksum_signed_sha256)) fw.checksum_signed_sha256 = checksum_signed_sha256 fw.mark_dirty() for md in fw.mds: sz = os.path.getsize(fw.filename_absolute) if sz != md.release_download_size: print('repairing size from {} to {}'.format( md.release_download_size, sz)) md.release_download_size = sz md.fw.mark_dirty() except FileNotFoundError as _: pass # ensure the test has been added for the firmware type if not fw.is_deleted: ploader.ensure_test_for_fw(fw) # all done db.session.commit()
def _ensure_tests(): # ensure the test has been added for the firmware type for firmware_id, in db.session.query(Firmware.firmware_id)\ .order_by(Firmware.timestamp): fw = db.session.query(Firmware)\ .filter(Firmware.firmware_id == firmware_id)\ .one() if not fw.is_deleted: ploader.ensure_test_for_fw(fw) db.session.commit()
def _check_firmware(): # ensure the test has been added for the firmware type fws = db.session.query(Firmware).all() for fw in fws: if fw.is_deleted: continue ploader.ensure_test_for_fw(fw) db.session.commit() # make a list of all the tests that need running test_fws = {} for fw in fws: for test in fw.tests: if test.needs_running: if fw in test_fws: test_fws[fw].append(test) else: test_fws[fw] = [test] # mark all the tests as started for fw in test_fws: for test in test_fws[fw]: print('Marking test %s started for firmware %u...' % (test.plugin_id, fw.firmware_id)) test.started_ts = datetime.datetime.utcnow() db.session.commit() # process each test for fw in test_fws: for test in sorted(test_fws[fw], key=_test_priority_sort_func): plugin = ploader.get_by_id(test.plugin_id) if not plugin: _event_log('No plugin %s' % test.plugin_id) test.ended_ts = datetime.datetime.utcnow() continue if not hasattr(plugin, 'run_test_on_fw'): _event_log('No run_test_on_fw in %s' % test.plugin_id) test.ended_ts = datetime.datetime.utcnow() continue try: print('Running test %s for firmware %s' % (test.plugin_id, fw.firmware_id)) plugin.run_test_on_fw(test, fw) test.ended_ts = datetime.datetime.utcnow() # don't leave a failed task running db.session.commit() except Exception as e: # pylint: disable=broad-except test.ended_ts = datetime.datetime.utcnow() test.add_fail('An exception occurred', str(e)) # unallocate the cached blob as it's no longer needed fw.blob = None # all done db.session.commit()
def _upload_firmware(): # verify the user can upload if not _user_can_upload(g.user): flash('User has not signed legal agreement', 'danger') return redirect(url_for('main.route_dashboard')) # used a custom vendor_id if 'vendor_id' in request.form: try: vendor_id = int(request.form['vendor_id']) except ValueError as e: flash('Failed to upload file: Specified vendor ID %s invalid' % request.form['vendor_id'], 'warning') return redirect(url_for('upload.route_firmware')) vendor = db.session.query(Vendor).filter(Vendor.vendor_id == vendor_id).first() if not vendor: flash('Failed to upload file: Specified vendor ID not found', 'warning') return redirect(url_for('upload.route_firmware')) else: vendor = g.user.vendor # security check if not vendor.check_acl('@upload'): flash('Permission denied: Failed to upload file for vendor: ' 'User with vendor %s cannot upload to vendor %s' % (g.user.vendor.group_id, vendor.group_id), 'warning') return redirect(url_for('upload.route_firmware')) # not correct parameters if not 'target' in request.form: return _error_internal('No target') if not 'file' in request.files: return _error_internal('No file') if request.form['target'] not in ['private', 'embargo', 'testing']: return _error_internal('Target not valid') # find remote, creating if required remote_name = request.form['target'] if remote_name == 'embargo': remote = vendor.remote else: remote = db.session.query(Remote).filter(Remote.name == remote_name).first() if not remote: return _error_internal('No remote for target %s' % remote_name) # if the vendor has uploaded a lot of firmware don't start changing the rules is_strict = len(vendor.fws) < 500 # load in the archive fileitem = request.files['file'] if not fileitem: return _error_internal('No file object') try: ufile = UploadedFile(is_strict=is_strict) for cat in db.session.query(Category): ufile.category_map[cat.value] = cat.category_id for pro in db.session.query(Protocol): ufile.protocol_map[pro.value] = pro.protocol_id for verfmt in db.session.query(Verfmt): ufile.version_formats[verfmt.value] = verfmt ufile.parse(os.path.basename(fileitem.filename), fileitem.read()) except (FileTooLarge, FileTooSmall, FileNotSupported, MetadataInvalid) as e: flash('Failed to upload file: ' + str(e), 'danger') return redirect(request.url) # check the file does not already exist fw = db.session.query(Firmware)\ .filter(or_(Firmware.checksum_upload_sha1 == ufile.fw.checksum_upload_sha1, Firmware.checksum_upload_sha256 == ufile.fw.checksum_upload_sha256)).first() if fw: if fw.check_acl('@view'): flash('Failed to upload file: A file with hash %s already exists' % fw.checksum_upload_sha1, 'warning') return redirect('/lvfs/firmware/%s' % fw.firmware_id) flash('Failed to upload file: Another user has already uploaded this firmware', 'warning') return redirect(url_for('upload.route_firmware')) # check the guid and version does not already exist fws = db.session.query(Firmware).all() fws_already_exist = [] for md in ufile.fw.mds: provides_value = md.guids[0].value fw = _filter_fw_by_id_guid_version(fws, md.appstream_id, provides_value, md.version) if fw: fws_already_exist.append(fw) # all the components existed, so build an error out of all the versions if len(fws_already_exist) == len(ufile.fw.mds): if g.user.check_acl('@robot') and 'auto-delete' in request.form: for fw in fws_already_exist: if fw.remote.is_public: flash('Firmware {} cannot be autodeleted as is in remote {}'.format( fw.firmware_id, fw.remote.name), 'danger') return redirect(url_for('upload.route_firmware')) if fw.user.user_id != g.user.user_id: flash('Firmware was not uploaded by this user', 'danger') return redirect(url_for('upload.route_firmware')) for fw in fws_already_exist: flash('Firmware %i was auto-deleted due to robot upload' % fw.firmware_id) _firmware_delete(fw) else: versions_for_display = [] for fw in fws_already_exist: for md in fw.mds: if not md.version_display in versions_for_display: versions_for_display.append(md.version_display) flash('Failed to upload file: A firmware file for this device with ' 'version %s already exists' % ','.join(versions_for_display), 'danger') return redirect('/lvfs/firmware/%s' % fw.firmware_id) # check if the file dropped a GUID previously supported for umd in ufile.fw.mds: new_guids = [guid.value for guid in umd.guids] for md in db.session.query(Component).\ filter(Component.appstream_id == umd.appstream_id): if md.fw.is_deleted: continue for old_guid in [guid.value for guid in md.guids]: if old_guid in new_guids: continue fw_str = str(md.fw.firmware_id) if g.user.check_acl('@qa') or g.user.check_acl('@robot'): flash('Firmware drops GUID {} previously supported ' 'in firmware {}'.format(old_guid, fw_str), 'warning') else: flash('Firmware would drop GUID {} previously supported ' 'in firmware {}'.format(old_guid, fw_str), 'danger') return redirect(request.url) # allow plugins to copy any extra files from the source archive for cffile in ufile.cabarchive_upload.values(): ploader.archive_copy(ufile.cabarchive_repacked, cffile) # allow plugins to add files ploader.archive_finalize(ufile.cabarchive_repacked, _get_plugin_metadata_for_uploaded_file(ufile)) # dump to a file download_dir = app.config['DOWNLOAD_DIR'] if not os.path.exists(download_dir): os.mkdir(download_dir) fn = os.path.join(download_dir, ufile.fw.filename) cab_data = ufile.cabarchive_repacked.save(compress=True) with open(fn, 'wb') as f: f.write(cab_data) # create parent firmware object settings = _get_settings() target = request.form['target'] fw = ufile.fw fw.vendor = vendor fw.user = g.user fw.addr = _get_client_address() fw.remote = remote fw.checksum_signed_sha1 = hashlib.sha1(cab_data).hexdigest() fw.checksum_signed_sha256 = hashlib.sha256(cab_data).hexdigest() fw.is_dirty = True fw.failure_minimum = settings['default_failure_minimum'] fw.failure_percentage = settings['default_failure_percentage'] # fix name for md in fw.mds: name_fixed = _fix_component_name(md.name, md.developer_name_display) if name_fixed != md.name: flash('Fixed component name from "%s" to "%s"' % (md.name, name_fixed), 'warning') md.name = name_fixed # verify each component has a version format for md in fw.mds: if not md.verfmt_with_fallback: flash('Component {} does not have required LVFS::VersionFormat'.\ format(md.appstream_id), 'warning') # add to database fw.events.append(FirmwareEvent(remote_id=remote.remote_id, user_id=g.user.user_id)) db.session.add(fw) db.session.commit() # ensure the test has been added for the firmware type ploader.ensure_test_for_fw(fw) # send out emails to anyone interested for u in fw.get_possible_users_to_email: if u == g.user: continue if u.get_action('notify-upload-vendor') and u.vendor == fw.vendor: send_email("[LVFS] Firmware has been uploaded", u.email_address, render_template('email-firmware-uploaded.txt', user=u, user_upload=g.user, fw=fw)) elif u.get_action('notify-upload-affiliate'): send_email("[LVFS] Firmware has been uploaded by affiliate", u.email_address, render_template('email-firmware-uploaded.txt', user=u, user_upload=g.user, fw=fw)) flash('Uploaded file %s to %s' % (ufile.fw.filename, target), 'info') # invalidate if target == 'embargo': remote.is_dirty = True g.user.vendor.remote.is_dirty = True db.session.commit() return redirect(url_for('firmware.route_show', firmware_id=fw.firmware_id))
def upload(): """ Upload a .cab file to the LVFS service """ # only accept form data if request.method != 'POST': if not hasattr(g, 'user'): return redirect(url_for('.index')) if not _user_can_upload(g.user): return redirect(url_for('.agreement_show')) vendor_ids = [] vendor = db.session.query(Vendor).filter( Vendor.vendor_id == g.user.vendor_id).first() if vendor: for res in vendor.restrictions: vendor_ids.append(res.value) affiliations = db.session.query(Affiliation).\ filter(Affiliation.vendor_id_odm == g.user.vendor_id).all() return render_template('upload.html', category='firmware', vendor_ids=vendor_ids, affiliations=affiliations) # verify the user can upload if not _user_can_upload(g.user): return _error_permission_denied('User has not signed legal agreement') # used a custom vendor_id if 'vendor_id' in request.form: try: vendor_id = int(request.form['vendor_id']) except ValueError as e: flash( 'Failed to upload file: Specified vendor ID %s invalid' % request.form['vendor_id'], 'warning') return redirect('/lvfs/upload') vendor = db.session.query(Vendor).filter( Vendor.vendor_id == vendor_id).first() if not vendor: flash('Failed to upload file: Specified vendor ID not found', 'warning') return redirect('/lvfs/upload') else: vendor = g.user.vendor # security check if not vendor.check_acl('@upload'): flash( 'Failed to upload file for vendor: Permission denied: ' 'User with vendor %s cannot upload to vendor %s' % (g.user.vendor.group_id, vendor.group_id), 'warning') return redirect('/lvfs/upload') # not correct parameters if not 'target' in request.form: return _error_internal('No target') if not 'file' in request.files: return _error_internal('No file') if request.form['target'] not in ['private', 'embargo', 'testing']: return _error_internal('Target not valid') # find remote, creating if required remote_name = request.form['target'] if remote_name == 'embargo': remote = vendor.remote else: remote = db.session.query(Remote).filter( Remote.name == remote_name).first() if not remote: return _error_internal('No remote for target %s' % remote_name) # if the vendor has uploaded a lot of firmware don't start changing the rules is_strict = len(vendor.fws) < 500 # load in the archive fileitem = request.files['file'] if not fileitem: return _error_internal('No file object') try: ufile = UploadedFile(is_strict=is_strict) for cat in db.session.query(Category).all(): ufile.category_map[cat.value] = cat.category_id for pro in db.session.query(Protocol).all(): ufile.protocol_map[pro.value] = pro.protocol_id ufile.parse(os.path.basename(fileitem.filename), fileitem.read()) except (FileTooLarge, FileTooSmall, FileNotSupported, MetadataInvalid) as e: flash('Failed to upload file: ' + str(e), 'danger') return redirect(request.url) # check the file does not already exist fw = db.session.query(Firmware).filter( Firmware.checksum_upload == ufile.fw.checksum_upload).first() if fw: if fw.check_acl('@view'): flash( 'Failed to upload file: A file with hash %s already exists' % fw.checksum_upload, 'warning') return redirect('/lvfs/firmware/%s' % fw.firmware_id) flash( 'Failed to upload file: Another user has already uploaded this firmware', 'warning') return redirect('/lvfs/upload') # check the guid and version does not already exist fws = db.session.query(Firmware).all() fws_already_exist = [] for md in ufile.fw.mds: provides_value = md.guids[0].value fw = _filter_fw_by_id_guid_version(fws, md.appstream_id, provides_value, md.version) if fw: fws_already_exist.append(fw) # all the components existed, so build an error out of all the versions if len(fws_already_exist) == len(ufile.fw.mds): if g.user.is_robot and 'auto-delete' in request.form: for fw in fws_already_exist: if fw.remote.is_public: return _error_permission_denied( 'Firmware %i cannot be autodeleted as is in remote %s' % (fw.firmware_id, fw.remote.name)) if fw.user.user_id != g.user.user_id: return _error_permission_denied( 'Firmware was not uploaded by this user') for fw in fws_already_exist: flash('Firmware %i was auto-deleted due to robot upload' % fw.firmware_id) _firmware_delete(fw) else: versions_for_display = [] for fw in fws_already_exist: for md in fw.mds: if not md.version_display in versions_for_display: versions_for_display.append(md.version_display) flash( 'Failed to upload file: A firmware file for this device with ' 'version %s already exists' % ','.join(versions_for_display), 'danger') return redirect('/lvfs/firmware/%s' % fw.firmware_id) # check if the file dropped a GUID previously supported dropped_guids = [] for umd in ufile.fw.mds: new_guids = [guid.value for guid in umd.guids] for fw in fws: if fw.is_deleted: continue for md in fw.mds: if md.appstream_id != umd.appstream_id: continue for old_guid in md.guids: if not old_guid.value in new_guids and not old_guid.value in dropped_guids: dropped_guids.append(old_guid.value) if dropped_guids: if g.user.is_qa or g.user.is_robot: flash( 'Firmware drops a GUID previously supported: ' + ','.join(dropped_guids), 'warning') else: flash( 'Firmware would drop a GUID previously supported: ' + ','.join(dropped_guids), 'danger') return redirect(request.url) # allow plugins to copy any extra files from the source archive for cffile in ufile.cabarchive_upload.values(): ploader.archive_copy(ufile.cabarchive_repacked, cffile) # allow plugins to add files ploader.archive_finalize(ufile.cabarchive_repacked, _get_plugin_metadata_for_uploaded_file(ufile)) # dump to a file download_dir = app.config['DOWNLOAD_DIR'] if not os.path.exists(download_dir): os.mkdir(download_dir) fn = os.path.join(download_dir, ufile.fw.filename) cab_data = ufile.cabarchive_repacked.save(compress=True) with open(fn, 'wb') as f: f.write(cab_data) # create parent firmware object settings = _get_settings() target = request.form['target'] fw = ufile.fw fw.vendor_id = vendor.vendor_id fw.user_id = g.user.user_id fw.addr = _get_client_address() fw.remote_id = remote.remote_id fw.checksum_signed = hashlib.sha1(cab_data).hexdigest() fw.checksum_pulp = hashlib.sha256(cab_data).hexdigest() fw.is_dirty = True fw.failure_minimum = settings['default_failure_minimum'] fw.failure_percentage = settings['default_failure_percentage'] # fix name for md in fw.mds: name_fixed = _fix_component_name(md.name, md.developer_name_display) if name_fixed != md.name: flash( 'Fixed component name from "%s" to "%s"' % (md.name, name_fixed), 'warning') md.name = name_fixed # fall back to a version format when unspecified and not semver for md in fw.mds: if not md.version_format and vendor.version_format and md.version.find( '.') == -1: md.version_format = vendor.version_format # add to database fw.events.append(FirmwareEvent(remote.remote_id, g.user.user_id)) db.session.add(fw) db.session.commit() # ensure the test has been added for the firmware type ploader.ensure_test_for_fw(fw) flash('Uploaded file %s to %s' % (ufile.fw.filename, target), 'info') # invalidate if target == 'embargo': remote.is_dirty = True g.user.vendor.remote.is_dirty = True db.session.commit() return redirect(url_for('.firmware_show', firmware_id=fw.firmware_id))
def route_modify(component_id): """ Modifies the component properties """ # find firmware md = db.session.query(Component).filter( Component.component_id == component_id).first() if not md: flash('No component matched!', 'danger') return redirect(url_for('firmware.route_firmware')) # security check if not md.check_acl('@modify-updateinfo'): flash('Permission denied: Insufficient permissions to modify firmware', 'danger') return redirect( url_for('components.route_show', component_id=component_id)) # set new metadata values page = 'overview' retry_all_tests = False if 'screenshot_url' in request.form: md.screenshot_url = request.form['screenshot_url'] if 'protocol_id' in request.form: if md.protocol_id != request.form['protocol_id']: md.protocol_id = request.form['protocol_id'] retry_all_tests = True if 'category_id' in request.form: if md.category_id != request.form['category_id']: md.category_id = request.form['category_id'] retry_all_tests = True if 'screenshot_caption' in request.form: md.screenshot_caption = _sanitize_markdown_text( request.form['screenshot_caption']) if 'install_duration' in request.form: try: md.install_duration = int(request.form['install_duration']) except ValueError as _: md.install_duration = 0 page = 'install_duration' if 'urgency' in request.form: md.release_urgency = request.form['urgency'] page = 'update' if 'description' in request.form: md.release_description = _sanitize_markdown_text( request.form['description']) page = 'update' if 'details_url' in request.form: md.details_url = request.form['details_url'] page = 'update' if 'source_url' in request.form: md.source_url = request.form['source_url'] page = 'update' if 'appstream_id' in request.form: md.appstream_id = request.form['appstream_id'] if 'name' in request.form: md.name = request.form['name'] if 'name_variant_suffix' in request.form: md.name_variant_suffix = request.form['name_variant_suffix'] if 'release_tag' in request.form: md.release_tag = request.form['release_tag'] # the firmware changed protocol if retry_all_tests: for test in md.fw.tests: test.retry() # ensure the test has been added for the new firmware type ploader.ensure_test_for_fw(md.fw) # modify md.fw.mark_dirty() db.session.commit() flash('Component updated', 'info') return redirect( url_for('components.route_show', component_id=component_id, page=page))
def route_promote(firmware_id, target): """ Promote or demote a firmware file from one target to another, for example from testing to stable, or stable to testing. """ # check valid if target not in ['stable', 'testing', 'private', 'embargo']: return _error_internal("Target %s invalid" % target) # check firmware exists in database fw = db.session.query(Firmware).filter( Firmware.firmware_id == firmware_id).first() if not fw: flash('No firmware {} exists'.format(firmware_id), 'danger') return redirect(url_for('firmware.route_firmware')) # security check if not fw.check_acl('@promote-' + target): flash('Permission denied: No QA access to {}'.format(firmware_id), 'danger') return redirect(url_for('firmware.route_show', firmware_id=firmware_id)) # vendor has to fix the problems first if target in ['stable', 'testing'] and fw.problems: probs = [] for problem in fw.problems: if problem.kind not in probs: probs.append(problem.kind) flash( 'Firmware has problems that must be fixed first: %s' % ','.join(probs), 'warning') return redirect( url_for('firmware.route_problems', firmware_id=firmware_id)) # set new remote if target == 'embargo': remote = fw.vendor.remote else: remote = db.session.query(Remote).filter(Remote.name == target).first() if not remote: return _error_internal('No remote for target %s' % target) # same as before if fw.remote.remote_id == remote.remote_id: flash('Cannot move firmware: Firmware already in that target', 'info') return redirect( url_for('firmware.route_target', firmware_id=firmware_id)) # invalidate both the remote it "came from", the one it's "going to" and # also the remote of the vendor that uploaded it remote.is_dirty = True fw.remote.is_dirty = True fw.vendor_odm.remote.is_dirty = True # invalidate the firmware as we're waiting for the metadata generation fw.mark_dirty() # some tests only run when the firmware is in stable ploader.ensure_test_for_fw(fw) # also dirty any ODM remote if uploading on behalf of an OEM if target == 'embargo' and fw.vendor != fw.user.vendor: fw.user.vendor.remote.is_dirty = True # all okay fw.remote_id = remote.remote_id fw.events.append( FirmwareEvent(remote_id=fw.remote_id, user_id=g.user.user_id)) db.session.commit() # send email for u in fw.get_possible_users_to_email: if u == g.user: continue if u.get_action('notify-promote'): send_email( "[LVFS] Firmware has been promoted", u.email_address, render_template('email-firmware-promoted.txt', user=g.user, fw=fw)) flash('Moved firmware', 'info') return redirect(url_for('firmware.route_target', firmware_id=firmware_id))