Ejemplo n.º 1
0
Archivo: views.py Proyecto: abayer/hue
def display(request, path):
  """
  Implements displaying part of a file.

  GET arguments are length, offset, mode, compression and encoding
  with reasonable defaults chosen.

  Note that display by length and offset are on bytes, not on characters.

  TODO(philip): Could easily built-in file type detection
  (perhaps using something similar to file(1)), as well
  as more advanced binary-file viewing capability (de-serialize
  sequence files, decompress gzipped text files, etc.).
  There exists a python-magic package to interface with libmagic.
  """
  path = _unquote_path(path)
  if not request.fs.isfile(path):
    raise PopupException("Not a file: '%s'" % (path,))

  stats = request.fs.stats(path)
  encoding = request.GET.get('encoding') or i18n.get_site_encoding()

  # I'm mixing URL-based parameters and traditional
  # HTTP GET parameters, since URL-based parameters
  # can't naturally be optional.

  # Need to deal with possibility that length is not present
  # because the offset came in via the toolbar manual byte entry.
  end = request.GET.get("end")
  if end:
    end = int(end)
  begin = request.GET.get("begin", 1)
  if begin:
    # Subtract one to zero index for file read
    begin = int(begin) - 1
  if end:
    offset = begin
    length = end - begin
    if begin >= end:
      raise PopupException("First byte to display must be before last byte to display.")
  else:
    length = int(request.GET.get("length", DEFAULT_CHUNK_SIZE_BYTES))
    # Display first block by default.
    offset = int(request.GET.get("offset", 0))

  mode = request.GET.get("mode")
  compression = request.GET.get("compression")

  if mode and mode not in ["binary", "text"]:
    raise PopupException("Mode must be one of 'binary' or 'text'.")
  if offset < 0:
    raise PopupException("Offset may not be less than zero.")
  if length < 0:
    raise PopupException("Length may not be less than zero.")
  if length > MAX_CHUNK_SIZE_BYTES:
    raise PopupException("Cannot request chunks greater than %d bytes" % MAX_CHUNK_SIZE_BYTES)

  # Auto gzip detection, unless we are explicitly told to view binary
  if not compression and mode != 'binary':
    if path.endswith('.gz') and detect_gzip(request.fs.open(path).read(2)):
      compression = 'gzip'
      offset = 0
    else:
      compression = 'none'

  f = request.fs.open(path)

  if compression == 'gzip':
    if offset and offset != 0:
      raise PopupException("We don't support offset and gzip Compression")
    try:
      try:
        contents = GzipFile('', 'r', 0, StringIO(f.read())).read(length)
      except:
        logging.warn("Could not decompress file at %s" % path, exc_info=True)
        contents = ''
        raise PopupException("Failed to decompress file")
    finally:
      f.close()
  else:
    try:
      f.seek(offset)
      contents = f.read(length)
    finally:
      f.close()

  # Get contents as string for text mode, or at least try
  uni_contents = None
  if not mode or mode == 'text':
    uni_contents = unicode(contents, encoding, errors='replace')
    is_binary = uni_contents.find(i18n.REPLACEMENT_CHAR) != -1
    # Auto-detect mode
    if not mode:
      mode = is_binary and 'binary' or 'text'

  # Get contents as bytes
  if mode == "binary":
    xxd_out = list(xxd.xxd(offset, contents, BYTES_PER_LINE, BYTES_PER_SENTENCE))

  dirname = posixpath.dirname(path)
  # Start with index-like data:
  data = _massage_stats(request, request.fs.stats(path))
  # And add a view structure:
  data["success"] = True
  data["view"] = {
    'offset': offset,
    'length': length,
    'end': offset + len(contents),
    'dirname': dirname,
    'mode': mode,
    'compression': compression,
    'size': stats['size']
  }
  data["filename"] = os.path.basename(path)
  data["editable"] = stats['size'] < MAX_FILEEDITOR_SIZE
  if mode == "binary":
    # This might be the wrong thing for ?format=json; doing the
    # xxd'ing in javascript might be more compact, or sending a less
    # intermediate representation...
    logger.debug("xxd: " + str(xxd_out))
    data['view']['xxd'] = xxd_out
    data['view']['masked_binary_data'] =  False
  else:
    data['view']['contents'] = uni_contents
    data['view']['masked_binary_data'] = is_binary

  return render_with_toolbars("display.mako", request, data)
Ejemplo n.º 2
0
def display(request, path):
    """
    Implements displaying part of a file.

    GET arguments are length, offset, mode, compression and encoding
    with reasonable defaults chosen.

    Note that display by length and offset are on bytes, not on characters.

    TODO(philip): Could easily built-in file type detection
    (perhaps using something similar to file(1)), as well
    as more advanced binary-file viewing capability (de-serialize
    sequence files, decompress gzipped text files, etc.).
    There exists a python-magic package to interface with libmagic.
    """
    if not request.fs.isfile(path):
        raise PopupException(_("Not a file: '%(path)s'") % {'path': path})

    mimetype = mimetypes.guess_type(path)[0]

    if mimetype is not None and INLINE_DISPLAY_MIMETYPE.search(mimetype):
      path_enc = urlencode(path)
      return redirect(reverse('filebrowser.views.download', args=[path_enc]) + '?disposition=inline')

    stats = request.fs.stats(path)
    encoding = request.GET.get('encoding') or i18n.get_site_encoding()

    # I'm mixing URL-based parameters and traditional
    # HTTP GET parameters, since URL-based parameters
    # can't naturally be optional.

    # Need to deal with possibility that length is not present
    # because the offset came in via the toolbar manual byte entry.
    end = request.GET.get("end")
    if end:
        end = int(end)
    begin = request.GET.get("begin", 1)
    if begin:
        # Subtract one to zero index for file read
        begin = int(begin) - 1
    if end:
        offset = begin
        length = end - begin
        if begin >= end:
            raise PopupException(_("First byte to display must be before last byte to display."))
    else:
        length = int(request.GET.get("length", DEFAULT_CHUNK_SIZE_BYTES))
        # Display first block by default.
        offset = int(request.GET.get("offset", 0))

    mode = request.GET.get("mode")
    compression = request.GET.get("compression")

    if mode and mode not in ["binary", "text"]:
        raise PopupException(_("Mode must be one of 'binary' or 'text'."))
    if offset < 0:
        raise PopupException(_("Offset may not be less than zero."))
    if length < 0:
        raise PopupException(_("Length may not be less than zero."))
    if length > MAX_CHUNK_SIZE_BYTES:
        raise PopupException(_("Cannot request chunks greater than %(bytes)d bytes.") % {'bytes': MAX_CHUNK_SIZE_BYTES})

    # Do not decompress in binary mode.
    if mode == 'binary':
        compression = 'none'
        # Read out based on meta.
    compression, offset, length, contents =\
    read_contents(compression, path, request.fs, offset, length)

    # Get contents as string for text mode, or at least try
    uni_contents = None
    if not mode or mode == 'text':
        uni_contents = unicode(contents, encoding, errors='replace')
        is_binary = uni_contents.find(i18n.REPLACEMENT_CHAR) != -1
        # Auto-detect mode
        if not mode:
            mode = is_binary and 'binary' or 'text'

    # Get contents as bytes
    if mode == "binary":
        xxd_out = list(xxd.xxd(offset, contents, BYTES_PER_LINE, BYTES_PER_SENTENCE))

    dirname = posixpath.dirname(path)
    # Start with index-like data:
    data = _massage_stats(request, request.fs.stats(path))
    # And add a view structure:
    data["success"] = True
    data["view"] = {
        'offset': offset,
        'length': length,
        'end': offset + len(contents),
        'dirname': dirname,
        'mode': mode,
        'compression': compression,
        'size': stats['size'],
        'max_chunk_size': str(MAX_CHUNK_SIZE_BYTES)
    }
    data["filename"] = os.path.basename(path)
    data["editable"] = stats['size'] < MAX_FILEEDITOR_SIZE
    if mode == "binary":
        # This might be the wrong thing for ?format=json; doing the
        # xxd'ing in javascript might be more compact, or sending a less
        # intermediate representation...
        logger.debug("xxd: " + str(xxd_out))
        data['view']['xxd'] = xxd_out
        data['view']['masked_binary_data'] = False
    else:
        data['view']['contents'] = uni_contents
        data['view']['masked_binary_data'] = is_binary

    data['breadcrumbs'] = parse_breadcrumbs(path)

    return render("display.mako", request, data)
Ejemplo n.º 3
0
def display(request, path):
    """
  Implements displaying part of a file.

  GET arguments are length, offset, mode, compression and encoding
  with reasonable defaults chosen.

  Note that display by length and offset are on bytes, not on characters.

  TODO(philip): Could easily built-in file type detection
  (perhaps using something similar to file(1)), as well
  as more advanced binary-file viewing capability (de-serialize
  sequence files, decompress gzipped text files, etc.).
  There exists a python-magic package to interface with libmagic.
  """
    path = _unquote_path(path)
    if not request.fs.isfile(path):
        raise PopupException("Not a file: '%s'" % (path, ))

    stats = request.fs.stats(path)
    encoding = request.GET.get('encoding') or i18n.get_site_encoding()

    # I'm mixing URL-based parameters and traditional
    # HTTP GET parameters, since URL-based parameters
    # can't naturally be optional.

    # Need to deal with possibility that length is not present
    # because the offset came in via the toolbar manual byte entry.
    end = request.GET.get("end")
    if end:
        end = int(end)
    begin = request.GET.get("begin", 1)
    if begin:
        # Subtract one to zero index for file read
        begin = int(begin) - 1
    if end:
        offset = begin
        length = end - begin
        if begin >= end:
            raise PopupException(
                "First byte to display must be before last byte to display.")
    else:
        length = int(request.GET.get("length", DEFAULT_CHUNK_SIZE_BYTES))
        # Display first block by default.
        offset = int(request.GET.get("offset", 0))

    mode = request.GET.get("mode")
    compression = request.GET.get("compression")

    if mode and mode not in ["binary", "text"]:
        raise PopupException("Mode must be one of 'binary' or 'text'.")
    if offset < 0:
        raise PopupException("Offset may not be less than zero.")
    if length < 0:
        raise PopupException("Length may not be less than zero.")
    if length > MAX_CHUNK_SIZE_BYTES:
        raise PopupException("Cannot request chunks greater than %d bytes" %
                             MAX_CHUNK_SIZE_BYTES)

    # Auto gzip detection, unless we are explicitly told to view binary
    if not compression and mode != 'binary':
        if path.endswith('.gz') and detect_gzip(request.fs.open(path).read(2)):
            compression = 'gzip'
            offset = 0
        else:
            compression = 'none'

    f = request.fs.open(path)

    if compression == 'gzip':
        if offset and offset != 0:
            raise PopupException(
                "We don't support offset and gzip Compression")
        try:
            try:
                contents = GzipFile('', 'r', 0,
                                    StringIO(f.read())).read(length)
            except:
                logging.warn("Could not decompress file at %s" % path,
                             exc_info=True)
                contents = ''
                raise PopupException("Failed to decompress file")
        finally:
            f.close()
    else:
        try:
            f.seek(offset)
            contents = f.read(length)
        finally:
            f.close()

    # Get contents as string for text mode, or at least try
    uni_contents = None
    if not mode or mode == 'text':
        uni_contents = unicode(contents, encoding, errors='replace')
        is_binary = uni_contents.find(i18n.REPLACEMENT_CHAR) != -1
        # Auto-detect mode
        if not mode:
            mode = is_binary and 'binary' or 'text'

    # Get contents as bytes
    if mode == "binary":
        xxd_out = list(
            xxd.xxd(offset, contents, BYTES_PER_LINE, BYTES_PER_SENTENCE))

    dirname = posixpath.dirname(path)
    # Start with index-like data:
    data = _massage_stats(request, request.fs.stats(path))
    # And add a view structure:
    data["success"] = True
    data["view"] = {
        'offset': offset,
        'length': length,
        'end': offset + len(contents),
        'dirname': dirname,
        'mode': mode,
        'compression': compression,
        'size': stats['size']
    }
    data["filename"] = os.path.basename(path)
    data["editable"] = stats['size'] < MAX_FILEEDITOR_SIZE
    if mode == "binary":
        # This might be the wrong thing for ?format=json; doing the
        # xxd'ing in javascript might be more compact, or sending a less
        # intermediate representation...
        logger.debug("xxd: " + str(xxd_out))
        data['view']['xxd'] = xxd_out
        data['view']['masked_binary_data'] = False
    else:
        data['view']['contents'] = uni_contents
        data['view']['masked_binary_data'] = is_binary

    return render_with_toolbars("display.mako", request, data)