#####
##### This file is part of Mail-in-a-Box-LDAP which is released under the
##### terms of the GNU Affero General Public License as published by the
##### Free Software Foundation, either version 3 of the License, or (at
##### your option) any later version. See file LICENSE or go to
##### https://github.com/downtownallday/mailinabox-ldap for full license
##### details.
#####

verify_file_sha1sum() {
    local FILE="$1"
    local HASH="$2"
    local output_error_what="${3:-}"
    CHECKSUM="$HASH  $FILE"
    if ! echo "$CHECKSUM" | sha1sum --check --strict > /dev/null; then
        if [ ! -z "$output_error_what" ]; then
            echo "------------------------------------------------------------"
		    echo "$output_error_what unexpected checksum."
		    echo "Found:"
		    sha1sum "$FILE"
		    echo
		    echo "Expected:"
		    echo "$HASH"
        fi
        return 1
    else
        return 0
    fi
}


download_link() {
    # download a link (URL) and cache it
    #
    # arguments:
    #  1: the url to download
    #
    #  2: where to send output
    #     'to-stdout': the function dumps the url contents to stdout
    #     'to-file': the function stores url contents in a file. the
    #                name of the file is returned in global variable
    #                DOWNLOAD_FILE. if caching is not enabled, the
    #                caller is responsible for deleting the file when
    #                it is no longer needed.
    #
    #  3: whether to cache the request or not    
    #      'use-cache': the download will be cached to the directory
    #                   specified in the 5th argument or to the
    #                   default directory in global variable
    #                   DOWNLOAD_CACHE_DIR
    #      'no-cache': do not cache (implied if no explicit or default
    #                  cache directory are set)
    #
    #  4: the file name to use for the cache. this could be a hash of
    #     the url to ensure uniqueness, or a name for a file that
    #     might be used across download sites. if not specified, the
    #     basename of the url is used.
    #
    #  5: the directory used to cache downloads. if not specified, the
    #     directory in DOWNLOAD_CACHE_DIR is used. If neither are set,
    #     no caching will occur.
    #
    #  6: the expected sha1 hash of the download [optional]. if output
    #  option 'to-stdout' is specified, this argument is ignored.
    #
    # The function returns:
    #    0 if successful
    #    1 if downloading failed
    #    2 for hash mismatch
    #
    local url="$1"
    local output_to="${2:-to-stdout}"
    local cache="${3:-use-cache}"
    local cache_file_name="${4:-$(basename "$url")}"
    local cache_dir="${5:-${DOWNLOAD_CACHE_DIR:-}}"
    local expected_hash="${6:-}"
    
    #say_verbose "download_link: $url (cache=$cache, output_to=$output_to)" 1>&2
    
    if [ -z "$cache_dir" ]; then
        say_debug "No cache directory configured, not caching" 1>&2
        cache="no-cache"
        
    elif [ "$cache" == "use-cache" ]; then
        mkdir -p "$cache_dir" >/dev/null
        if [ $? -ne 0 ]; then
            say_verbose "Could not create cache dir, not caching" 1>&2
            cache="no-cache"
        fi
        if [ ! -w "$cache_dir" ]; then
            say_verbose "Cache dir is not writable, not caching" 1>&2
            cache="no-cache"
        fi
    fi

    #
    # do not use the cache
    #
    if [ "$cache" != "use-cache" ]; then
        if [ "$output_to" == "to-stdout" ]; then
            DOWNLOAD_FILE=""
            DOWNLOAD_FILE_REMOVE="false"
            curl -s "$url"
            [ $? -ne 0 ] && return 1
            return 0
        
        fi
        
        DOWNLOAD_FILE="/tmp/download_file.$$.$(date +%s)"
        DOWNLOAD_FILE_REMOVE="true"
        rm -f "$DOWNLOAD_FILE"
        say_verbose "Download $url" 1>&2
        curl -s "$url" > "$DOWNLOAD_FILE"
        [ $? -ne 0 ] && return 1
        if [ ! -z "$expected_hash" ] && \
               ! verify_file_sha1sum "$DOWNLOAD_FILE" "$expected_hash" "Download of $url"
        then
		    rm -f "$DOWNLOAD_FILE"
            DOWNLOAD_FILE=""
            DOWNLOAD_FILE_REMOVE="false"
		    return 2
	    fi
        return 0
    fi

    
    #
    # use the cache
    #
    local cache_dst="$cache_dir/$cache_file_name"
    local tmp_dst="/tmp/download_file.$$.$(date +%s)"
    local code=1
    
    rm -f "$tmp_dst"
    
    if [ -e "$cache_dst" ]; then
        # cache file exists, download with 'if-modified-since'
        say_verbose "Download (if-modified-since) $url" 1>&2
        curl -z "$cache_dst" -s "$url" > "$tmp_dst"
        code=$?
        
        if [ $code -eq 0 ]; then
            if [ -s "$tmp_dst" ]; then
                # non-empty download file, cache it
                say_verbose "Modifed - caching to: $cache_dst" 1>&2
                rm -f "$cache_dst" >/dev/null && \
                    mv "$tmp_dst" "$cache_dst" >/dev/null
                code=$?
                
            else
                # cache file is up-to-date
                say_verbose "Not modifed" 1>&2
                rm -f "$tmp_dst" >/dev/null
            fi
        fi
        
    else
        # cache file does not exist
        say_verbose "Download $url" 1>&2
        curl -s "$url" > "$tmp_dst"
        code=$?
        if [ $code -eq 0 ]; then
            say_verbose "Caching to: $cache_dst" 1>&2
            rm -f "$cache_dst" >/dev/null && \
                mv "$tmp_dst" "$cache_dst" >/dev/null
            code=$?
        else
            rm -f "$tmp_dst" >/dev/null
        fi
    fi
    
    if [ $code -eq 0 ]; then
        if [ "$output_to" == "to-stdout" ]; then
            DOWNLOAD_FILE=""
            DOWNLOAD_FILE_REMOVE="false"
            cat "$cache_dst"
            [ $? -eq 0 ] && return 0
            return 1
        else
            DOWNLOAD_FILE="$cache_dst"
            DOWNLOAD_FILE_REMOVE="false"
            if [ ! -z "$expected_hash" ] && \
                   ! verify_file_sha1sum "$DOWNLOAD_FILE" "$expected_hash" "Download of $url"
            then
		        rm -f "$DOWNLOAD_FILE"
                DOWNLOAD_FILE=""
		        return 2
	        fi
        fi

        return 0
        
    else
        return 1
    fi
}


get_nc_download_url() {
    # This function returns a url where Nextcloud can be downloaded
    # for the version specified. The url is placed into global
    # variable DOWNLOAD_URL.
    #
    # Specify the version desired to 3 positions as the first argument
    # with no leading "v". eg: "19.0.0", or leave the first argument
    # blank for a url to the latest version for a fresh install. If
    # the latest minor version of a specific major version is desired,
    # set global variable REQUIRED_NC_FOR_FRESH_INSTALLS to
    # "latest-$major", for example "latest-20".
    #
    # Unless DOWNLOAD_NEXTCLOUD_FROM_GITHUB is set to "true", this
    # function always returns a link directed at Nextcloud's download
    # servers.
    #
    # requires that jq is installed on the system for Github downloads
    # when argument 1 (the nextcloud version) is not specified
    #
    # specify the archive extension to download as the second argument
    # for example, "zip" or "tar.bz2". Defaults to "tar.bz2"
    #
    # on return:
    #   DOWNLOAD_URL contains the url for the requested download
    #   DOWNLOAD_URL_CACHE_ID contains an id that should be passed to
    #      the download_link function as the cache_file_name argument
    #   the return code is always 0
    #
    
    local ver="${1:-}"
    local ext="${2:-tar.bz2}"
    local url=""
    local url_cache_id=""

    if [ "${DOWNLOAD_NEXTCLOUD_FROM_GITHUB:-false}" == "true" ]; then
        # use Github REST API to obtain latest version and link. if
        # unsuccessful, fall back to using Nextcloud
        local github_ver=""
        if [ ! -z "$ver" ]; then
            github_ver="v${ver}"
            url="https://github.com/nextcloud/server/releases/download/${github_ver}/nextcloud-${ver}.${ext#.}"
            url_cache_id="nextcloud-${ver}.${ext#.}"
            
        elif [ -x "/usr/bin/jq" ]; then
            local latest="${REQUIRED_NC_FOR_FRESH_INSTALLS:-latest}"

            if [ "$latest" == "latest" ]; then
                github_ver=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/nextcloud/server/tags 2>/dev/null | /usr/bin/jq  -r '.[].name' | grep -v -i -E '(RC|beta)' | head -1)  #eg: "v20.0.1"
            else
                local major=$(awk -F- '{print $2}' <<<"$latest")
                github_ver=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/nextcloud/server/tags 2>/dev/null | /usr/bin/jq  -r '.[].name' | grep "^v$major\\." | grep -v -i -E '(RC|beta)' | head -1)  #eg: "v20.0.1"
            fi

            if [ $? -ne 0 ]; then
                say_verbose "Github API call failed! Using Nextcloud's server."
                # fall through and use nextcloud's download site
            else
                local github_plain_ver=$(awk -Fv '{print $2}' <<<"$github_ver")
                url="https://github.com/nextcloud/server/releases/download/$github_ver/nextcloud-${github_plain_ver}.${ext#.}"
                url_cache_id="nextcloud-${github_plain_ver}.${ext#.}"

            fi
        fi

        if [ ! -z "$url" ]; then
            # ensure the download exists - sometimes Github releases
            # only have sources and not a .bz2 file. In that case we
            # have to revert to using nextcloud's download server
            local http_status
            http_status="$(curl -s -L --head -w "%{http_code}" "$url" |tail -1)"
            local code=$?
            if [ $code -ne 0 ]; then
                say_verbose "Problem contacting Github to verify a download url ($code)"
                url=""
                
            elif [ "$http_status" != "403" -a "$http_status" != "200" ]; then
                say_verbose "Github doesn't have a download for $github_ver ($http_status)"
                url=""
                
            else
                # Github returns an html page with a redirect link
                # .. we have to extract the link
                local content
                content=$(download_link "$url" to-stdout no-cache)
                if [ $? -ne 0 ]; then
                    say_verbose "Unable to get Github download redir page"
                    url=""
                    
                else
                    #say_verbose "Got github redirect page content: $content"
                    content=$(python3 -c "import xml.etree.ElementTree as ET; tree=ET.fromstring(r'$content'); els=tree.findall('.//a'); print(els[0].attrib['href'])" 2>/dev/null)
                    if [ $? -ne 0 ]; then
                        say_verbose "Unable to parse Github redirect html"
                        url=""
                        
                    else
                        say_debug "Github redirected to $content"
                        url="$content"
                    fi
                fi
            fi
        fi
    fi
    

    if [ -z "$url" ]; then
        if [ -z "$ver" ]; then
            ver=${REQUIRED_NC_FOR_FRESH_INSTALLS:-latest}
        fi
        
        case "$ver" in
            latest )
                url="https://download.nextcloud.com/server/releases/latest.${ext#.}"
                url_cache_id="latest.${ext#.}"
                ;;

            *rc* )
                url="https://download.nextcloud.com/server/prereleases/nextcloud-${ver}.${ext#.}"
                url_cache_id="nextcloud-${ver}.${ext#.}"
                ;;
            
            * )
                url="https://download.nextcloud.com/server/releases/nextcloud-${ver}.${ext#.}"
                url_cache_id="nextcloud-${ver}.${ext#.}"
        esac
    fi

    DOWNLOAD_URL="$url"
    DOWNLOAD_URL_CACHE_ID="$url_cache_id"
    return 0
}



install_composer() {
    if [ ! -x /usr/local/bin/composer ]; then
        pushd /usr/local/bin >/dev/null
        curl -sS https://getcomposer.org/installer | hide_output php${PHP_VER}
        mv composer.phar composer
        popd >/dev/null
    else
        hide_output /usr/local/bin/composer selfupdate
    fi
}