about summary refs log tree commit diff
path: root/scripts/nix-prefetch-url.in
blob: 31170fa953eaf848114f7e84325811acfdb56cc1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#! @shell@ -e

url=$1
expHash=$2

# needed to make it work on NixOS
export PATH=$PATH:@coreutils@

hashType=$NIX_HASH_ALGO
if test -z "$hashType"; then
    hashType=sha256
fi

hashFormat=
if test "$hashType" != "md5"; then
    hashFormat=--base32
fi

if test -z "$url"; then
    echo "syntax: nix-prefetch-url URL [EXPECTED-HASH]" >&2
    exit 1
fi

# Handle escaped characters in the URI.  `+', `=' and `?' are the only
# characters that are valid in Nix store path names but have a special
# meaning in URIs.
name=$(basename "$url" | @sed@ -e 's/%2b/+/g' -e 's/%3d/=/g' -e 's/%3f/\?/g')
if test -z "$name"; then echo "invalid url"; exit 1; fi


# If the hash was given, a file with that hash may already be in the
# store.
if test -n "$expHash"; then
    finalPath=$(@bindir@/nix-store --print-fixed-path "$hashType" "$expHash" "$name")
    if ! @bindir@/nix-store --check-validity "$finalPath" 2> /dev/null; then
        finalPath=
    fi
    hash=$expHash
fi


mkTempDir() {
    if test -n "$tmpPath"; then return; fi
    local i=0
    while true; do
        if test -z "$TMPDIR"; then TMPDIR=/tmp; fi
        tmpPath=$TMPDIR/nix-prefetch-url-$$-$i
        if mkdir "$tmpPath"; then break; fi
        # !!! to bad we can't check for ENOENT in mkdir, so this check
        # is slightly racy (it bombs out if somebody just removed
        # $tmpPath...).
        if ! test -e "$tmpPath"; then exit 1; fi
        i=$((i + 1))
    done
    trap removeTempDir EXIT SIGINT SIGQUIT
}

removeTempDir() {
    if test -n "$tmpPath"; then
        rm -rf "$tmpPath" || true
    fi
}


doDownload() {
    @curl@ $cacheFlags --fail --location --max-redirs 20 --disable-epsv \
        --cookie-jar $tmpPath/cookies "$url" -o $tmpFile
}


# Hack to support the mirror:// scheme from Nixpkgs.
if test "${url:0:9}" = "mirror://"; then
    if test -z "$NIXPKGS_ALL"; then
        echo "Resolving mirror:// URLs requires Nixpkgs.  Please point \$NIXPKGS_ALL at a Nixpkgs tree." >&2
        exit 1
    fi

    mkTempDir
    nix-build "$NIXPKGS_ALL" -A resolveMirrorURLs --argstr url "$url" -o $tmpPath/urls > /dev/null

    expanded=($(cat $tmpPath/urls))
    if test "${#expanded[*]}" = 0; then
        echo "$0: cannot resolve $url." >&2
        exit 1
    fi

    echo "$url expands to ${expanded[*]} (using ${expanded[0]})" >&2
    url="${expanded[0]}"
fi


# If we don't know the hash or a file with that hash doesn't exist,
# download the file and add it to the store.
if test -z "$finalPath"; then

    mkTempDir
    tmpFile=$tmpPath/$name

    # Optionally do timestamp-based caching of the download.
    # Actually, the only thing that we cache in $NIX_DOWNLOAD_CACHE is
    # the hash and the timestamp of the file at $url.  The caching of
    # the file *contents* is done in Nix store, where it can be
    # garbage-collected independently.
    if test -n "$NIX_DOWNLOAD_CACHE"; then
        echo -n "$url" > $tmpPath/url
        urlHash=$(@bindir@/nix-hash --type sha256 --base32 --flat $tmpPath/url)
        echo "$url" > "$NIX_DOWNLOAD_CACHE/$urlHash.url"
        cachedHashFN="$NIX_DOWNLOAD_CACHE/$urlHash.$hashType"
        cachedTimestampFN="$NIX_DOWNLOAD_CACHE/$urlHash.stamp"
        cacheFlags="--remote-time"
        if test -e "$cachedTimestampFN" -a -e "$cachedHashFN"; then
            # Only download the file if it is newer than the cached version.
            cacheFlags="$cacheFlags --time-cond $cachedTimestampFN"
        fi
    fi

    # Perform the download.
    doDownload

    if test -n "$NIX_DOWNLOAD_CACHE" -a ! -e $tmpFile; then
        # Curl didn't create $tmpFile, so apparently there's no newer
        # file on the server.
        hash=$(cat $cachedHashFN)
        finalPath=$(@bindir@/nix-store --print-fixed-path "$hashType" "$hash" "$name") 
        if ! @bindir@/nix-store --check-validity "$finalPath" 2> /dev/null; then
            echo "cached contents of \`$url' disappeared, redownloading..." >&2
            finalPath=
            cacheFlags="--remote-time"
            doDownload
        fi
    fi

    if test -z "$finalPath"; then

        # Compute the hash.
        hash=$(@bindir@/nix-hash --type "$hashType" $hashFormat --flat $tmpFile)
        if ! test -n "$QUIET"; then echo "hash is $hash" >&2; fi

        if test -n "$NIX_DOWNLOAD_CACHE"; then
            echo $hash > $cachedHashFN
            touch -r $tmpFile $cachedTimestampFN
        fi

        # Add the downloaded file to the Nix store.
        finalPath=$(@bindir@/nix-store --add-fixed "$hashType" $tmpFile)

        if test -n "$expHash" -a "$expHash" != "$hash"; then
            echo "hash mismatch for URL \`$url'" >&2
            exit 1
        fi
        
    fi
fi


if ! test -n "$QUIET"; then echo "path is $finalPath" >&2; fi

echo $hash

if test -n "$PRINT_PATH"; then
    echo $finalPath
fi