mollusk 0e4acfb8f2 fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
2018-06-11 03:28:36 -07:00

266 lines
11 KiB
Julia

# This file is a part of Julia. License is MIT: https://julialang.org/license
"""Mirror callback function
Function sets `+refs/*:refs/*` refspecs and `mirror` flag for remote reference.
"""
function mirror_callback(remote::Ptr{Ptr{Void}}, repo_ptr::Ptr{Void},
name::Cstring, url::Cstring, payload::Ptr{Void})
# Create the remote with a mirroring url
fetch_spec = "+refs/*:refs/*"
err = ccall((:git_remote_create_with_fetchspec, :libgit2), Cint,
(Ptr{Ptr{Void}}, Ptr{Void}, Cstring, Cstring, Cstring),
remote, repo_ptr, name, url, fetch_spec)
err != 0 && return Cint(err)
# And set the configuration option to true for the push command
config = GitConfig(GitRepo(repo_ptr,false))
name_str = unsafe_string(name)
err= try set!(config, "remote.$name_str.mirror", true)
catch -1
finally close(config)
end
err != 0 && return Cint(err)
return Cint(0)
end
function authenticate_ssh(creds::SSHCredentials, libgit2credptr::Ptr{Ptr{Void}},
username_ptr, schema, host)
isusedcreds = checkused!(creds)
# Note: The same SSHCredentials can be used to authenticate separate requests using the
# same credential cache. e.g. using Pkg.update when there are two private packages.
errcls, errmsg = Error.last_error()
if errcls != Error.None
# Check if we used ssh-agent
if creds.usesshagent == "U"
creds.usesshagent = "E" # reported ssh-agent error, disables ssh agent use for the future
end
end
# first try ssh-agent if credentials support its usage
if creds.usesshagent == "Y" || creds.usesshagent == "U"
err = ccall((:git_cred_ssh_key_from_agent, :libgit2), Cint,
(Ptr{Ptr{Void}}, Cstring), libgit2credptr, username_ptr)
creds.usesshagent = "U" # used ssh-agent only one time
err == 0 && return Cint(0)
end
if creds.prompt_if_incorrect
# if username is not provided, then prompt for it
username = if username_ptr == Cstring(C_NULL)
uname = creds.user # check if credentials were already used
!isusedcreds ? uname : prompt("Username for '$schema$host'", default=uname)
else
unsafe_string(username_ptr)
end
isempty(username) && return Cint(Error.EAUTH)
# For SSH we need a private key location
privatekey = if haskey(ENV,"SSH_KEY_PATH")
ENV["SSH_KEY_PATH"]
else
keydefpath = creds.prvkey # check if credentials were already used
if isempty(keydefpath) || isusedcreds
defaultkeydefpath = joinpath(homedir(),".ssh","id_rsa")
if isempty(keydefpath) && isfile(defaultkeydefpath)
keydefpath = defaultkeydefpath
else
keydefpath =
prompt("Private key location for '$schema$username@$host'", default=keydefpath)
end
end
keydefpath
end
# If the private key changed, invalidate the cached public key
(privatekey != creds.prvkey) &&
(creds.pubkey = "")
# For SSH we need a public key location, look for environment vars SSH_* as well
publickey = if haskey(ENV,"SSH_PUB_KEY_PATH")
ENV["SSH_PUB_KEY_PATH"]
else
keydefpath = creds.pubkey # check if credentials were already used
if isempty(keydefpath) || isusedcreds
if isempty(keydefpath)
keydefpath = privatekey*".pub"
end
if !isfile(keydefpath)
prompt("Public key location for '$schema$username@$host'", default=keydefpath)
end
end
keydefpath
end
passphrase_required = true
if !isfile(privatekey)
warn("Private key not found")
else
# In encrypted private keys, the second line is "Proc-Type: 4,ENCRYPTED"
open(privatekey) do f
readline(f)
passphrase_required = readline(f) == "Proc-Type: 4,ENCRYPTED"
end
end
passphrase = if haskey(ENV,"SSH_KEY_PASS")
ENV["SSH_KEY_PASS"]
else
passdef = creds.pass # check if credentials were already used
if passphrase_required && (isempty(passdef) || isusedcreds)
if is_windows()
passdef = Base.winprompt(
"Your SSH Key requires a password, please enter it now:",
"Passphrase required", privatekey; prompt_username = false)
isnull(passdef) && return Cint(Error.EAUTH)
passdef = Base.get(passdef)[2]
else
passdef = prompt("Passphrase for $privatekey", password=true)
end
end
passdef
end
((creds.user != username) || (creds.pass != passphrase) ||
(creds.prvkey != privatekey) || (creds.pubkey != publickey)) && reset!(creds)
creds.user = username # save credentials
creds.prvkey = privatekey # save credentials
creds.pubkey = publickey # save credentials
creds.pass = passphrase
else
isusedcreds && return Cint(Error.EAUTH)
end
return ccall((:git_cred_ssh_key_new, :libgit2), Cint,
(Ptr{Ptr{Void}}, Cstring, Cstring, Cstring, Cstring),
libgit2credptr, creds.user, creds.pubkey, creds.prvkey, creds.pass)
end
function authenticate_userpass(creds::UserPasswordCredentials, libgit2credptr::Ptr{Ptr{Void}},
schema, host, urlusername)
isusedcreds = checkused!(creds)
if creds.prompt_if_incorrect
username = creds.user
userpass = creds.pass
if is_windows()
if isempty(username) || isempty(userpass) || isusedcreds
res = Base.winprompt("Please enter your credentials for '$schema$host'", "Credentials required",
isempty(username) ? urlusername : username; prompt_username = true)
isnull(res) && return Cint(Error.EAUTH)
username, userpass = Base.get(res)
end
elseif isusedcreds
username = prompt("Username for '$schema$host'",
default=isempty(username) ? urlusername : username)
userpass = prompt("Password for '$schema$username@$host'", password=true)
end
((creds.user != username) || (creds.pass != userpass)) && reset!(creds)
creds.user = username # save credentials
creds.pass = userpass # save credentials
isempty(username) && isempty(userpass) && return Cint(Error.EAUTH)
else
isusedcreds && return Cint(Error.EAUTH)
end
return ccall((:git_cred_userpass_plaintext_new, :libgit2), Cint,
(Ptr{Ptr{Void}}, Cstring, Cstring),
libgit2credptr, creds.user, creds.pass)
end
"""Credentials callback function
Function provides different credential acquisition functionality w.r.t. a connection protocol.
If a payload is provided then `payload_ptr` should contain a `LibGit2.AbstractCredentials` object.
For `LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT` type, if the payload contains fields:
`user` & `pass`, they are used to create authentication credentials.
Empty `user` name and `pass`word trigger an authentication error.
For `LibGit2.Consts.CREDTYPE_SSH_KEY` type, if the payload contains fields:
`user`, `prvkey`, `pubkey` & `pass`, they are used to create authentication credentials.
Empty `user` name triggers an authentication error.
Credentials are checked in the following order (if supported):
- ssh key pair (`ssh-agent` if specified in payload's `usesshagent` field)
- plain text
**Note**: Due to the specifics of the `libgit2` authentication procedure, when
authentication fails, this function is called again without any indication whether
authentication was successful or not. To avoid an infinite loop from repeatedly
using the same faulty credentials, the `checkused!` function can be called. This
function returns `true` if the credentials were used.
Using credentials triggers a user prompt for (re)entering required information.
`UserPasswordCredentials` and `CachedCredentials` are implemented using a call
counting strategy that prevents repeated usage of faulty credentials.
"""
function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring,
username_ptr::Cstring,
allowed_types::Cuint, payload_ptr::Ptr{Void})
err = Cint(0)
url = unsafe_string(url_ptr)
# parse url for schema and host
urlparts = match(URL_REGEX, url)
schema = urlparts[:scheme] === nothing ? "" : urlparts[:scheme] * "://"
urlusername = urlparts[:user] === nothing ? "" : urlparts[:user]
host = urlparts[:host]
# get credentials object from payload pointer
@assert payload_ptr != C_NULL
creds = unsafe_pointer_to_objref(payload_ptr)
explicit = !isnull(creds[]) && !isa(Base.get(creds[]), CachedCredentials)
# use ssh key or ssh-agent
if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY))
sshcreds = get_creds!(creds, "ssh://$host", reset!(SSHCredentials(true), -1))
if isa(sshcreds, SSHCredentials)
err = authenticate_ssh(sshcreds, libgit2credptr, username_ptr, schema, host)
err == 0 && return err
end
end
if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT))
defaultcreds = reset!(UserPasswordCredentials(true), -1)
credid = "$schema$host"
upcreds = get_creds!(creds, credid, defaultcreds)
# If there were stored SSH credentials, but we ended up here that must
# mean that something went wrong. Replace the SSH credentials by user/pass
# credentials
if !isa(upcreds, UserPasswordCredentials)
upcreds = defaultcreds
isa(Base.get(creds[]), CachedCredentials) && (Base.get(creds[]).creds[credid] = upcreds)
end
return authenticate_userpass(upcreds, libgit2credptr, schema, host, urlusername)
end
# No authentication method we support succeeded. The most likely cause is
# that explicit credentials were passed in, but said credentials are incompatible
# with the remote host.
if err == 0
if explicit
warn("The explicitly provided credentials were incompatible with " *
"the server's supported authentication methods")
end
err = Cint(Error.EAUTH)
end
return err
end
function fetchhead_foreach_callback(ref_name::Cstring, remote_url::Cstring,
oid_ptr::Ptr{GitHash}, is_merge::Cuint, payload::Ptr{Void})
fhead_vec = unsafe_pointer_to_objref(payload)::Vector{FetchHead}
push!(fhead_vec, FetchHead(unsafe_string(ref_name), unsafe_string(remote_url),
unsafe_load(oid_ptr), is_merge == 1))
return Cint(0)
end
"C function pointer for `mirror_callback`"
mirror_cb() = cfunction(mirror_callback, Cint, Tuple{Ptr{Ptr{Void}}, Ptr{Void}, Cstring, Cstring, Ptr{Void}})
"C function pointer for `credentials_callback`"
credentials_cb() = cfunction(credentials_callback, Cint, Tuple{Ptr{Ptr{Void}}, Cstring, Cstring, Cuint, Ptr{Void}})
"C function pointer for `fetchhead_foreach_callback`"
fetchhead_foreach_cb() = cfunction(fetchhead_foreach_callback, Cint, Tuple{Cstring, Cstring, Ptr{GitHash}, Cuint, Ptr{Void}})