.. versionadded:: 2016.11.0

    Returns ``True`` if the zip archive is password-protected, ``False`` if
    not. If the specified file is not a ZIP archive, an error will be raised.

    name
        The path / URL of the archive to check.

    clean : False
        Set this value to ``True`` to delete the path referred to by ``name``
        once the contents have been listed. This option should be used with
        care.

        .. note::
            If there is an error listing the archive's contents, the cached
            file will not be removed, to allow for troubleshooting.

    saltenv : base
        Specifies the fileserver environment from which to retrieve
        ``archive``. This is only applicable when ``archive`` is a file from
        the ``salt://`` fileserver.

    source_hash
        If ``name`` is an http(s)/ftp URL and the file exists in the minion's
        file cache, this option can be passed to keep the minion from
        re-downloading the archive if the cached copy matches the specified
        hash.

        .. versionadded:: 2018.3.0

    CLI Examples:

    .. code-block:: bash

            salt '*' archive.is_encrypted /path/to/myfile.zip
            salt '*' archive.is_encrypted salt://foo.zip
            salt '*' archive.is_encrypted salt://foo.zip saltenv=dev
            salt '*' archive.is_encrypted https://domain.tld/myfile.zip clean=True
            salt '*' archive.is_encrypted https://domain.tld/myfile.zip source_hash=f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
            salt '*' archive.is_encrypted ftp://10.1.2.3/foo.zip

def is_encrypted(name, clean=False, saltenv='base', source_hash=None):
    '''
    .. versionadded:: 2016.11.0

    Returns ``True`` if the zip archive is password-protected, ``False`` if
    not. If the specified file is not a ZIP archive, an error will be raised.

    name
        The path / URL of the archive to check.

    clean : False
        Set this value to ``True`` to delete the path referred to by ``name``
        once the contents have been listed. This option should be used with
        care.

        .. note::
            If there is an error listing the archive's contents, the cached
            file will not be removed, to allow for troubleshooting.

    saltenv : base
        Specifies the fileserver environment from which to retrieve
        ``archive``. This is only applicable when ``archive`` is a file from
        the ``salt://`` fileserver.

    source_hash
        If ``name`` is an http(s)/ftp URL and the file exists in the minion's
        file cache, this option can be passed to keep the minion from
        re-downloading the archive if the cached copy matches the specified
        hash.

        .. versionadded:: 2018.3.0

    CLI Examples:

    .. code-block:: bash

            salt '*' archive.is_encrypted /path/to/myfile.zip
            salt '*' archive.is_encrypted salt://foo.zip
            salt '*' archive.is_encrypted salt://foo.zip saltenv=dev
            salt '*' archive.is_encrypted https://domain.tld/myfile.zip clean=True
            salt '*' archive.is_encrypted https://domain.tld/myfile.zip source_hash=f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
            salt '*' archive.is_encrypted ftp://10.1.2.3/foo.zip
    '''
    cached = __salt__['cp.cache_file'](name, saltenv, source_hash=source_hash)
    if not cached:
        raise CommandExecutionError('Failed to cache {0}'.format(name))

    archive_info = {'archive location': cached}
    try:
        with contextlib.closing(zipfile.ZipFile(cached)) as zip_archive:
            zip_archive.testzip()
    except RuntimeError:
        ret = True
    except zipfile.BadZipfile:
        raise CommandExecutionError(
            '{0} is not a ZIP file'.format(name),
            info=archive_info
        )
    except Exception as exc:
        raise CommandExecutionError(exc.__str__(), info=archive_info)
    else:
        ret = False

    if clean:
        try:
            os.remove(cached)
            log.debug('Cleaned cached archive %s', cached)
        except OSError as exc:
            if exc.errno != errno.ENOENT:
                log.warning(
                    'Failed to clean cached archive %s: %s',
                    cached, exc.__str__()
                )
    return ret