def _get_base_path(self, attachment): # TODO: adapt to new models (needs extra properties to use event TZ) obj = linked_object = attachment.folder.object paths = [] while obj != self.event: start_date = _get_start_dt(obj) if start_date is not None: if isinstance(obj, SubContribution): paths.append( secure_filename( '{}_{}'.format(obj.position, obj.title), '')) else: time = format_time(start_date, format='HHmm', timezone=self.event.timezone) paths.append( secure_filename( '{}_{}'.format(to_unicode(time), obj.title), '')) else: if isinstance(obj, SubContribution): paths.append( secure_filename( '{}){}'.format(obj.position, obj.title), unicode(obj.id))) else: paths.append(secure_filename(obj.title, unicode(obj.id))) obj = _get_obj_parent(obj) linked_obj_start_date = _get_start_dt(linked_object) if attachment.folder.object != self.event and linked_obj_start_date is not None: paths.append( secure_filename(linked_obj_start_date.strftime('%Y%m%d_%A'), '')) return reversed(paths)
def _prepare_folder_structure(self, item): paper_title = secure_filename( '{}_{}'.format(item.paper.contribution.friendly_id, item.paper.contribution.title), 'paper') file_name = secure_filename('{}_{}'.format(item.id, item.filename), 'paper') return os.path.join( *self._adjust_path_length([paper_title, file_name]))
def _prepare_folder_structure(self, item): abstract_title = secure_filename( '{}_{}'.format(item.abstract.title, unicode(item.abstract.id)), 'abstract') file_name = secure_filename( '{}_{}'.format(unicode(item.id), item.filename), item.filename) return os.path.join( *self._adjust_path_length([abstract_title, file_name]))
def _prepare_folder_structure(self, attachment): registration = attachment.registration regform_title = secure_filename(attachment.registration.registration_form.title, 'registration_form') registrant_name = secure_filename("{}_{}".format(registration.get_full_name(), unicode(registration.friendly_id)), registration.friendly_id) file_name = secure_filename("{}_{}_{}".format(attachment.field_data.field.title, attachment.field_data.field_id, attachment.filename), attachment.filename) return os.path.join(*self._adjust_path_length([regform_title, registrant_name, file_name]))
def add_abstract_files(abstract, files, log_action=True): if not files: return for f in files: filename = secure_filename(f.filename, 'attachment') content_type = mimetypes.guess_type( f.filename)[0] or f.mimetype or 'application/octet-stream' abstract_file = AbstractFile(filename=filename, content_type=content_type, abstract=abstract) abstract_file.save(f.stream) db.session.flush() if log_action: logger.info('%d abstract file(s) added to %s by %s', len(files), abstract, session.user) num = len(files) if num == 1: msg = 'Added file to abstract {}'.format(abstract.verbose_title) else: msg = 'Added {} files to abstract {}'.format( num, abstract.verbose_title) abstract.event.log( EventLogRealm.management, EventLogKind.positive, 'Abstracts', msg, session.user, data={'Files': ', '.join(f.filename for f in abstract.files)})
def _process(self): defaults = FormDefaults(self.attachment, protected=self.attachment.is_self_protected, skip_attrs={'file'}) if self.attachment.type == AttachmentType.file: form = EditAttachmentFileForm(linked_object=self.object, obj=defaults, file=self.attachment) else: form = EditAttachmentLinkForm(linked_object=self.object, obj=defaults) if form.validate_on_submit(): folder = form.folder.data or AttachmentFolder.get_or_create_default(linked_object=self.object) logger.info('Attachment %s edited by %s', self.attachment, session.user) form.populate_obj(self.attachment, skip={'acl', 'file'}) self.attachment.folder = folder if self.attachment.is_self_protected: # can't use `=` because of https://bitbucket.org/zzzeek/sqlalchemy/issues/3583 self.attachment.acl |= form.acl.data self.attachment.acl &= form.acl.data # files need special handling; links are already updated in `populate_obj` if self.attachment.type == AttachmentType.file: file = form.file.data['added'] if file: self.attachment.file = AttachmentFile(user=session.user, content_type=file.mimetype, filename=secure_filename(file.filename, 'attachment')) self.attachment.file.save(file.stream) signals.attachments.attachment_updated.send(self.attachment, user=session.user) flash(_("The attachment \"{name}\" has been updated").format(name=self.attachment.title), 'success') return jsonify_data(attachment_list=_render_attachment_list(self.object)) template = ('attachments/upload.html' if self.attachment.type == AttachmentType.file else 'attachments/add_link.html') return jsonify_template(template, form=form, existing_attachment=self.attachment, action=url_for('.modify_attachment', self.attachment), protection_message=_render_protection_message(self.object), folders_protection_info=_get_folders_protection_info(self.object))
def _process(self): f = request.files['logo'] try: img = Image.open(f) except IOError: flash(_('You cannot upload this file as a logo.'), 'error') return jsonify_data(content=None) if img.format.lower() not in {'jpeg', 'png', 'gif'}: flash(_('The file has an invalid format ({format})').format(format=img.format), 'error') return jsonify_data(content=None) if img.mode == 'CMYK': flash(_('The logo you uploaded is using the CMYK colorspace and has been converted to RGB. Please check if ' 'the colors are correct and convert it manually if necessary.'), 'warning') img = img.convert('RGB') image_bytes = BytesIO() img.save(image_bytes, 'PNG') image_bytes.seek(0) content = image_bytes.read() self.event.logo = content self.event.logo_metadata = { 'hash': crc32(content), 'size': len(content), 'filename': os.path.splitext(secure_filename(f.filename, 'logo'))[0] + '.png', 'content_type': 'image/png' } flash(_('New logo saved'), 'success') logger.info("New logo '%s' uploaded by %s (%s)", f.filename, session.user, self.event) return jsonify_data(content=get_logo_data(self.event))
def _process(self): filename = '{}-category.ics'.format( secure_filename(self.category.title, str(self.category.id))) buf = serialize_categories_ical([self.category.id], session.user, Event.end_dt >= (now_utc() - timedelta(weeks=4))) return send_file(filename, buf, 'text/calendar')
def _process(self): filename = '{}-category.atom'.format( secure_filename(self.category.title, str(self.category.id))) buf = serialize_category_atom( self.category, url_for(request.endpoint, self.category, _external=True), session.user, Event.end_dt >= now_utc()) return send_file(filename, buf, 'application/atom+xml')
def _store_paper_template_file(template, file): content_type = mimetypes.guess_type(file.filename)[0] or file.mimetype or 'application/octet-stream' filename = secure_filename(file.filename, 'template') # reset fields in case an existing file is replaced so we can save() again template.storage_backend = None template.storage_file_id = None template.size = None template.content_type = content_type template.filename = filename template.save(file.stream)
def _prepare_folder_structure(self, item): attachment = item.attachment event_dir = secure_filename(self.event.title, None) segments = [event_dir] if event_dir else [] if _get_start_dt(attachment.folder.object) is None: segments.append('Unscheduled') segments.extend(self._get_base_path(attachment)) if not attachment.folder.is_default: segments.append( secure_filename(attachment.folder.title, unicode(attachment.folder.id))) segments.append(attachment.file.filename) path = os.path.join(*self._adjust_path_length(filter(None, segments))) while path in self.used_filenames: # prepend the id if there's a path collision segments[-1] = '{}-{}'.format(attachment.id, segments[-1]) path = os.path.join( *self._adjust_path_length(filter(None, segments))) return path
def _process(self): f = request.files['css_file'] self.event.stylesheet = to_unicode(f.read()).strip() self.event.stylesheet_metadata = { 'hash': crc32(self.event.stylesheet), 'size': len(self.event.stylesheet), 'filename': secure_filename(f.filename, 'stylesheet.css') } db.session.flush() flash(_('New CSS file saved. Do not forget to enable it ("Use custom CSS") after verifying that it is correct ' 'using the preview.'), 'success') logger.info('CSS file for %s uploaded by %s', self.event, session.user) return jsonify_data(content=get_css_file_data(self.event))
def create_paper_revision(paper, submitter, files): revision = PaperRevision(paper=paper, submitter=submitter) for f in files: filename = secure_filename(f.filename, 'paper') content_type = mimetypes.guess_type(f.filename)[0] or f.mimetype or 'application/octet-stream' pf = PaperFile(filename=filename, content_type=content_type, paper_revision=revision, _contribution=paper.contribution) pf.save(f.stream) db.session.flush() db.session.expire(revision._contribution, ['_paper_last_revision']) notify_paper_revision_submission(revision) logger.info('Paper revision %r submitted by %r', revision, session.user) paper.event.log(EventLogRealm.management, EventLogKind.positive, 'Papers', "Paper revision {} submitted for contribution {} ({})" .format(revision.id, paper.contribution.title, paper.contribution.friendly_id), session.user) return revision
def _process(self): f = request.files['file'] filename = secure_filename(f.filename, 'image') data = BytesIO() shutil.copyfileobj(f, data) data.seek(0) try: image_type = Image.open(data).format.lower() except IOError: # Invalid image data return jsonify(error="Invalid image data!") data.seek(0) if image_type not in {'jpeg', 'gif', 'png'}: return jsonify(error="File format not accepted!") content_type = 'image/' + image_type image = DesignerImageFile(template=self.template, filename=filename, content_type=content_type) self.template.background_image = image image.save(data) flash(_("The image has been uploaded"), 'success') return jsonify_data(image_url=image.download_url)
def _process_POST(self): f = request.files[self.IMAGE_TYPE] try: img = Image.open(f) except IOError: flash(_('You cannot upload this file as an icon/logo.'), 'error') return jsonify_data(content=None) if img.format.lower() not in {'jpeg', 'png', 'gif'}: flash( _('The file has an invalid format ({format})').format( format=img.format), 'error') return jsonify_data(content=None) if img.mode == 'CMYK': flash( _('The image you uploaded is using the CMYK colorspace and has been converted to RGB. ' 'Please check if the colors are correct and convert it manually if necessary.' ), 'warning') img = img.convert('RGB') img = self._resize(img) image_bytes = BytesIO() img.save(image_bytes, 'PNG') image_bytes.seek(0) content = image_bytes.read() metadata = { 'hash': crc32(content), 'size': len(content), 'filename': os.path.splitext(secure_filename(f.filename, self.IMAGE_TYPE))[0] + '.png', 'content_type': 'image/png' } self._set_image(content, metadata) flash(self.SAVED_FLASH_MSG, 'success') logger.info("New {} '%s' uploaded by %s (%s)".format(self.IMAGE_TYPE), f.filename, session.user, self.category) return jsonify_data( content=get_image_data(self.IMAGE_TYPE, self.category))
def process_form_data(self, registration, value, old_data=None, billable_items_locked=False): data = {'field_data': self.form_item.current_data} if not value: return data file_ = value['uploaded_file'] if file_: # we have a file -> always save it data['file'] = { 'data': file_.stream, 'name': secure_filename(file_.filename, 'attachment'), 'content_type': mimetypes.guess_type(file_.filename)[0] or file_.mimetype or 'application/octet-stream' } elif not value['keep_existing']: data['file'] = None return data
def _process(self): files = request.files.getlist('image') num = 0 for f in files: filename = secure_filename(f.filename, 'image') data = BytesIO() shutil.copyfileobj(f, data) data.seek(0) try: image_type = Image.open(data).format.lower() except IOError: # Invalid image data continue data.seek(0) # XXX: mpo is basically JPEG and JPEGs from some cameras are (wrongfully) detected as mpo if image_type == 'mpo': image_type = 'jpeg' elif image_type not in {'jpeg', 'gif', 'png'}: flash( _("The image '{name}' has an invalid type ({type}); only JPG, GIF and PNG are allowed." ).format(name=f.filename, type=image_type), 'error') continue content_type = 'image/' + image_type image = ImageFile(event=self.event, filename=filename, content_type=content_type) image.save(data) num += 1 db.session.flush() logger.info('Image %s uploaded by %s', image, session.user) signals.event_management.image_created.send(image, user=session.user) flash( ngettext("The image has been uploaded", "{count} images have been uploaded", num).format(count=len(files)), 'success') return jsonify_data(image_list=_render_image_list(self.event))
def _process(self): form = AddAttachmentFilesForm(linked_object=self.object) if form.validate_on_submit(): files = form.files.data folder = form.folder.data or AttachmentFolder.get_or_create_default(linked_object=self.object) for f in files: filename = secure_filename(f.filename, 'attachment') attachment = Attachment(folder=folder, user=session.user, title=to_unicode(f.filename), type=AttachmentType.file, protection_mode=form.protection_mode.data) if attachment.is_self_protected: attachment.acl = form.acl.data content_type = mimetypes.guess_type(f.filename)[0] or f.mimetype or 'application/octet-stream' attachment.file = AttachmentFile(user=session.user, filename=filename, content_type=content_type) attachment.file.save(f.stream) db.session.add(attachment) db.session.flush() logger.info('Attachment %s uploaded by %s', attachment, session.user) signals.attachments.attachment_created.send(attachment, user=session.user) flash(ngettext("The attachment has been uploaded", "{count} attachments have been uploaded", len(files)) .format(count=len(files)), 'success') return jsonify_data(attachment_list=_render_attachment_list(self.object)) return jsonify_template('attachments/upload.html', form=form, action=url_for('.upload', self.object), protection_message=_render_protection_message(self.object), folders_protection_info=_get_folders_protection_info(self.object))
def _prepare_folder_structure(self, item): file_name = secure_filename( '{}_{}'.format(unicode(item.id), item.filename), item.filename) return os.path.join(*self._adjust_path_length([file_name]))
def test_secure_filename(filename, expected): assert secure_filename(filename, 'fallback') == expected
def send_file(name, path_or_fd, mimetype, last_modified=None, no_cache=True, inline=None, conditional=False, safe=True): """Sends a file to the user. `name` is required and should be the filename visible to the user. `path_or_fd` is either the physical path to the file or a file-like object (e.g. a StringIO). `mimetype` SHOULD be a proper MIME type such as image/png. It may also be an fossir-style file type such as JPG. `last_modified` may contain a unix timestamp or datetime object indicating the last modification of the file. `no_cache` can be set to False to disable no-cache headers. `inline` defaults to true except for certain filetypes like XML and CSV. It SHOULD be set to false only when you want to force the user's browser to download the file. Usually it is much nicer if e.g. a PDF file can be displayed inline so don't disable it unless really necessary. `conditional` is very useful when sending static files such as CSS/JS/images. It will allow the browser to retrieve the file only if it has been modified (based on mtime and size). `safe` adds some basic security features such a adding a content-security-policy and forcing inline=False for text/html mimetypes """ name = secure_filename(name, 'file') assert '/' in mimetype if inline is None: inline = mimetype not in ('text/csv', 'text/xml', 'application/xml') if request.user_agent.platform == 'android': # Android is just full of fail when it comes to inline content-disposition... inline = False if _is_office_mimetype(mimetype): inline = False if safe and mimetype in ('text/html', 'image/svg+xml'): inline = False try: rv = _send_file(path_or_fd, mimetype=mimetype, as_attachment=not inline, attachment_filename=name, conditional=conditional) except IOError: if not current_app.debug: raise raise NotFound('File not found: %s' % path_or_fd) if safe: rv.headers.add('Content-Security-Policy', "script-src 'self'; object-src 'self'") if inline: # send_file does not add this header if as_attachment is False rv.headers.add('Content-Disposition', 'inline', filename=name) if last_modified: if not isinstance(last_modified, int): last_modified = int(time.mktime(last_modified.timetuple())) rv.last_modified = last_modified if no_cache: del rv.expires del rv.cache_control.max_age rv.cache_control.public = False rv.cache_control.private = True rv.cache_control.no_cache = True return rv