예제 #1
0
파일: __init__.py 프로젝트: Claret-Srl/core
def execute_script(hass, name, data=None):
    """Execute a script."""
    filename = f"{name}.py"
    raise_if_invalid_filename(filename)
    with open(hass.config.path(FOLDER, filename)) as fil:
        source = fil.read()
    execute(hass, filename, source, data)
예제 #2
0
    async def post(self, request: web.Request) -> web.Response:
        """Handle upload."""
        if not request["hass_user"].is_admin:
            raise Unauthorized()

        # Increase max payload
        request._client_max_size = MAX_UPLOAD_SIZE  # pylint: disable=protected-access

        try:
            data = self.schema(dict(await request.post()))
        except vol.Invalid as err:
            LOGGER.error("Received invalid upload data: %s", err)
            raise web.HTTPBadRequest() from err

        try:
            item = MediaSourceItem.from_uri(self.hass,
                                            data["media_content_id"])
        except ValueError as err:
            LOGGER.error("Received invalid upload data: %s", err)
            raise web.HTTPBadRequest() from err

        try:
            source_dir_id, location = self.source.async_parse_identifier(item)
        except Unresolvable as err:
            LOGGER.error("Invalid local source ID")
            raise web.HTTPBadRequest() from err

        uploaded_file: FileField = data["file"]

        if not uploaded_file.content_type.startswith(
            ("image/", "video/", "audio/")):
            LOGGER.error("Content type not allowed")
            raise vol.Invalid("Only images and video are allowed")

        try:
            raise_if_invalid_filename(uploaded_file.filename)
        except ValueError as err:
            LOGGER.error("Invalid filename")
            raise web.HTTPBadRequest() from err

        try:
            await self.hass.async_add_executor_job(
                self._move_file,
                self.source.async_full_path(source_dir_id, location),
                uploaded_file,
            )
        except ValueError as err:
            LOGGER.error("Moving upload failed: %s", err)
            raise web.HTTPBadRequest() from err

        return self.json({
            "media_content_id":
            f"{data['media_content_id']}/{uploaded_file.filename}"
        })
예제 #3
0
 def get_media_key(self, device_id: str, event: ImageEventBase) -> str:
     """Return the filename to use for a new event."""
     # Convert a nest device id to a home assistant device id
     device_id_str = (self._devices.get(device_id,
                                        f"{device_id}-unknown_device")
                      if self._devices else "unknown_device")
     event_id_str = event.event_session_id
     try:
         raise_if_invalid_filename(event_id_str)
     except ValueError:
         event_id_str = ""
     time_str = str(int(event.timestamp.timestamp()))
     event_type_str = EVENT_NAME_MAP.get(event.event_type, "event")
     suffix = "jpg" if event.event_image_type == EventImageType.IMAGE else "mp4"
     return f"{device_id_str}/{time_str}-{event_id_str}-{event_type_str}.{suffix}"
예제 #4
0
    def async_parse_identifier(self, item: MediaSourceItem) -> Tuple[str, str]:
        """Parse identifier."""
        if not item.identifier:
            # Empty source_dir_id and location
            return "", ""

        source_dir_id, location = item.identifier.split("/", 1)
        if source_dir_id not in self.hass.config.media_dirs:
            raise Unresolvable("Unknown source directory.")

        try:
            raise_if_invalid_filename(location)
        except ValueError as err:
            raise Unresolvable("Invalid path.") from err

        return source_dir_id, location
예제 #5
0
def test_raise_if_invalid_filename():
    """Test raise_if_invalid_filename."""
    assert util.raise_if_invalid_filename("test") is None

    with pytest.raises(ValueError):
        util.raise_if_invalid_filename("/test")

    with pytest.raises(ValueError):
        util.raise_if_invalid_filename("..test")

    with pytest.raises(ValueError):
        util.raise_if_invalid_filename("\\test")

    with pytest.raises(ValueError):
        util.raise_if_invalid_filename("\\../test")
예제 #6
0
    async def _upload_file(self, request: web.Request) -> web.Response:
        """Handle uploaded file."""
        # Increase max payload
        request._client_max_size = MAX_SIZE  # pylint: disable=protected-access

        data = await request.post()
        file_field = data.get("file")

        if not isinstance(file_field, web.FileField):
            raise vol.Invalid("Expected a file")

        try:
            raise_if_invalid_filename(file_field.filename)
        except ValueError as err:
            raise web.HTTPBadRequest from err

        hass: HomeAssistant = request.app["hass"]
        file_id = ulid_hex()

        if DOMAIN not in hass.data:
            hass.data[DOMAIN] = await FileUploadData.create(hass)

        file_upload_data: FileUploadData = hass.data[DOMAIN]
        file_dir = file_upload_data.file_dir(file_id)

        def _sync_work() -> None:
            file_dir.mkdir()

            # MyPy forgets about the isinstance check because we're in a function scope
            assert isinstance(file_field, web.FileField)

            with (file_dir / file_field.filename).open("wb") as target_fileobj:
                shutil.copyfileobj(file_field.file, target_fileobj)

        await hass.async_add_executor_job(_sync_work)

        file_upload_data.files[file_id] = file_field.filename

        return self.json({"file_id": file_id})
예제 #7
0
    async def get(self, request: web.Request, source_dir_id: str,
                  location: str) -> web.FileResponse:
        """Start a GET request."""
        try:
            raise_if_invalid_filename(location)
        except ValueError as err:
            raise web.HTTPBadRequest() from err

        if source_dir_id not in self.hass.config.media_dirs:
            raise web.HTTPNotFound()

        media_path = self.source.async_full_path(source_dir_id, location)

        # Check that the file exists
        if not media_path.is_file():
            raise web.HTTPNotFound()

        # Check that it's a media file
        mime_type, _ = mimetypes.guess_type(str(media_path))
        if not mime_type or mime_type.split("/")[0] not in MEDIA_MIME_TYPES:
            raise web.HTTPNotFound()

        return web.FileResponse(media_path)
예제 #8
0
        def do_download():
            """Download the file."""
            try:
                url = service.data[ATTR_URL]

                subdir = service.data.get(ATTR_SUBDIR)

                filename = service.data.get(ATTR_FILENAME)

                overwrite = service.data.get(ATTR_OVERWRITE)

                if subdir:
                    # Check the path
                    raise_if_invalid_path(subdir)

                final_path = None

                req = requests.get(url, stream=True, timeout=10)

                if req.status_code != HTTP_OK:
                    _LOGGER.warning(
                        "downloading '%s' failed, status_code=%d", url, req.status_code
                    )
                    hass.bus.fire(
                        f"{DOMAIN}_{DOWNLOAD_FAILED_EVENT}",
                        {"url": url, "filename": filename},
                    )

                else:
                    if filename is None and "content-disposition" in req.headers:
                        match = re.findall(
                            r"filename=(\S+)", req.headers["content-disposition"]
                        )

                        if match:
                            filename = match[0].strip("'\" ")

                    if not filename:
                        filename = os.path.basename(url).strip()

                    if not filename:
                        filename = "ha_download"

                    # Check the filename
                    raise_if_invalid_filename(filename)

                    # Do we want to download to subdir, create if needed
                    if subdir:
                        subdir_path = os.path.join(download_path, subdir)

                        # Ensure subdir exist
                        if not os.path.isdir(subdir_path):
                            os.makedirs(subdir_path)

                        final_path = os.path.join(subdir_path, filename)

                    else:
                        final_path = os.path.join(download_path, filename)

                    path, ext = os.path.splitext(final_path)

                    # If file exist append a number.
                    # We test filename, filename_2..
                    if not overwrite:
                        tries = 1
                        final_path = path + ext
                        while os.path.isfile(final_path):
                            tries += 1

                            final_path = f"{path}_{tries}.{ext}"

                    _LOGGER.debug("%s -> %s", url, final_path)

                    with open(final_path, "wb") as fil:
                        for chunk in req.iter_content(1024):
                            fil.write(chunk)

                    _LOGGER.debug("Downloading of %s done", url)
                    hass.bus.fire(
                        f"{DOMAIN}_{DOWNLOAD_COMPLETED_EVENT}",
                        {"url": url, "filename": filename},
                    )

            except requests.exceptions.ConnectionError:
                _LOGGER.exception("ConnectionError occurred for %s", url)
                hass.bus.fire(
                    f"{DOMAIN}_{DOWNLOAD_FAILED_EVENT}",
                    {"url": url, "filename": filename},
                )

                # Remove file if we started downloading but failed
                if final_path and os.path.isfile(final_path):
                    os.remove(final_path)
            except ValueError:
                _LOGGER.exception("Invalid value")
                hass.bus.fire(
                    f"{DOMAIN}_{DOWNLOAD_FAILED_EVENT}",
                    {"url": url, "filename": filename},
                )

                # Remove file if we started downloading but failed
                if final_path and os.path.isfile(final_path):
                    os.remove(final_path)