Commit 6fc6eeb6 by Edward Thomson

remote: introduce `git_remote_connect_options`

The existing mechanism for providing options to remote fetch/push calls,
and subsequently to transports, is unsatisfactory.  It requires an
options structure to avoid breaking the API and callback signatures.

1. Introduce `git_remote_connect_options` to satisfy those needs.

2. Add a new remote connection API, `git_remote_connect_ext` that will
   take this new options structure.  Existing `git_remote_connect` calls
   will proxy to that.  `git_remote_fetch` and `git_remote_push` will
   proxy their fetch/push options to that as well.

3. Define the interaction between `git_remote_connect` and fetch/push.
   Connect _may_ be called before fetch/push, but _need not_ be.  The
   semantics of which options would be used for these operations was
   not specified if you specify options for both connect _and_ fetch.
   Now these are defined that the fetch or push options will be used
   _if_ they were specified.  Otherwise, the connect options will be
   used if they were specified.  Otherwise, the library's defaults will
   be used.

4. Update the transports to understand `git_remote_connect_options`.
   This is a breaking change to the systems API.
parent f99a0d69
......@@ -345,23 +345,6 @@ GIT_EXTERN(size_t) git_remote_refspec_count(const git_remote *remote);
GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, size_t n);
/**
* Open a connection to a remote
*
* The transport is selected based on the URL. The direction argument
* is due to a limitation of the git protocol (over TCP or SSH) which
* starts up a specific binary which can only do the one or the other.
*
* @param remote the remote to connect to
* @param direction GIT_DIRECTION_FETCH if you want to fetch or
* GIT_DIRECTION_PUSH if you want to push
* @param callbacks the callbacks to use for this connection
* @param proxy_opts proxy settings
* @param custom_headers extra HTTP headers to use in this connection
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy_opts, const git_strarray *custom_headers);
/**
* Get the remote repository's reference advertisement list
*
* Get the list of references with which the server responds to a new
......@@ -809,7 +792,93 @@ GIT_EXTERN(int) git_push_options_init(
unsigned int version);
/**
* Download and index the packfile
* Remote creation options structure
*
* Initialize with `GIT_REMOTE_CREATE_OPTIONS_INIT`. Alternatively, you can
* use `git_remote_create_options_init`.
*
*/
typedef struct {
unsigned int version;
/** Callbacks to use for this connection */
git_remote_callbacks callbacks;
/** HTTP Proxy settings */
git_proxy_options proxy_opts;
/** Extra HTTP headers to use in this connection */
git_strarray custom_headers;
} git_remote_connect_options;
#define GIT_REMOTE_CONNECT_OPTIONS_VERSION 1
#define GIT_REMOTE_CONNECT_OPTIONS_INIT { \
GIT_REMOTE_CONNECT_OPTIONS_VERSION, \
GIT_REMOTE_CALLBACKS_INIT, \
GIT_PROXY_OPTIONS_INIT }
/**
* Initialize git_remote_connect_options structure.
*
* Initializes a `git_remote_connect_options` with default values.
* Equivalent to creating an instance with
* `GIT_REMOTE_CONNECT_OPTIONS_INIT`.
*
* @param opts The `git_remote_connect_options` struct to initialize.
* @param version The struct version; pass `GIT_REMOTE_CONNECT_OPTIONS_VERSION`.
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_remote_connect_options_init(
git_remote_connect_options *opts,
unsigned int version);
/**
* Open a connection to a remote.
*
* The transport is selected based on the URL; the direction argument
* is due to a limitation of the git protocol which starts up a
* specific binary which can only do the one or the other.
*
* @param remote the remote to connect to
* @param direction GIT_DIRECTION_FETCH if you want to fetch or
* GIT_DIRECTION_PUSH if you want to push
* @param callbacks the callbacks to use for this connection
* @param proxy_opts proxy settings
* @param custom_headers extra HTTP headers to use in this connection
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_connect(
git_remote *remote,
git_direction direction,
const git_remote_callbacks *callbacks,
const git_proxy_options *proxy_opts,
const git_strarray *custom_headers);
/**
* Open a connection to a remote with extended options.
*
* The transport is selected based on the URL; the direction argument
* is due to a limitation of the git protocol which starts up a
* specific binary which can only do the one or the other.
*
* The given options structure will form the defaults for connection
* options and callback setup. Callers may override these defaults
* by specifying `git_fetch_options` or `git_push_options` in
* subsequent calls.
*
* @param remote the remote to connect to
* @param direction GIT_DIRECTION_FETCH if you want to fetch or
* GIT_DIRECTION_PUSH if you want to push
* @param opts the remote connection options
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_connect_ext(
git_remote *remote,
git_direction direction,
const git_remote_connect_options *opts);
/**
* Download and index the packfile.
*
* Connect to the remote if it hasn't been done yet, negotiate with
* the remote git which objects are missing, download and index the
......@@ -818,19 +887,31 @@ GIT_EXTERN(int) git_push_options_init(
* The .idx file will be created and both it and the packfile with be
* renamed to their final name.
*
* If options are specified and this remote is already connected then
* the existing remote connection options will be discarded and the
* remote will now use the new options.
*
* @param remote the remote
* @param refspecs the refspecs to use for this negotiation and
* download. Use NULL or an empty array to use the base refspecs
* @param opts the options to use for this fetch
* @param opts the options to use for this fetch or NULL
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts);
GIT_EXTERN(int) git_remote_download(
git_remote *remote,
const git_strarray *refspecs,
const git_fetch_options *opts);
/**
* Create a packfile and send it to the server
*
* Connect to the remote if it hasn't been done yet, negotiate with
* the remote git which objects are missing, create a packfile with the missing objects and send it.
* the remote git which objects are missing, create a packfile with
* the missing objects and send it.
*
* If options are specified and this remote is already connected then
* the existing remote connection options will be discarded and the
* remote will now use the new options.
*
* @param remote the remote
* @param refspecs the refspecs to use for this negotiation and
......@@ -838,17 +919,23 @@ GIT_EXTERN(int) git_push_options_init(
* @param opts the options to use for this push
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts);
GIT_EXTERN(int) git_remote_upload(
git_remote *remote,
const git_strarray *refspecs,
const git_push_options *opts);
/**
* Update the tips to the new state
* Update the tips to the new state.
*
* If callbacks are not specified then the callbacks specified to
* `git_remote_connect` will be used (if it was called).
*
* @param remote the remote to update
* @param reflog_message The message to insert into the reflogs. If
* NULL and fetching, the default is "fetch <name>", where <name> is
* the name of the remote (or its url, for in-memory remotes). This
* parameter is ignored when pushing.
* @param callbacks pointer to the callback structure to use
* @param callbacks pointer to the callback structure to use or NULL
* @param update_fetchhead whether to write to FETCH_HEAD. Pass 1 to behave like git.
* @param download_tags what the behaviour for downloading tags is for this fetch. This is
* ignored for push. This must be the same value passed to `git_remote_download()`.
......@@ -862,15 +949,19 @@ GIT_EXTERN(int) git_remote_update_tips(
const char *reflog_message);
/**
* Download new data and update tips
* Download new data and update tips.
*
* Convenience function to connect to a remote, download the data,
* disconnect and update the remote-tracking branches.
*
* If options are specified and this remote is already connected then
* the existing remote connection options will be discarded and the
* remote will now use the new options.
*
* @param remote the remote to fetch from
* @param refspecs the refspecs to use for this fetch. Pass NULL or an
* empty array to use the base refspecs.
* @param opts options to use for this fetch
* @param opts options to use for this fetch or NULL
* @param reflog_message The message to insert into the reflogs. If NULL, the
* default is "fetch"
* @return 0 or an error code
......@@ -882,27 +973,35 @@ GIT_EXTERN(int) git_remote_fetch(
const char *reflog_message);
/**
* Prune tracking refs that are no longer present on remote
* Prune tracking refs that are no longer present on remote.
*
* If callbacks are not specified then the callbacks specified to
* `git_remote_connect` will be used (if it was called).
*
* @param remote the remote to prune
* @param callbacks callbacks to use for this prune
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks);
GIT_EXTERN(int) git_remote_prune(
git_remote *remote,
const git_remote_callbacks *callbacks);
/**
* Perform a push
* Perform a push.
*
* Peform all the steps from a push.
* If options are specified and this remote is already connected then
* the existing remote connection options will be discarded and the
* remote will now use the new options.
*
* @param remote the remote to push to
* @param refspecs the refspecs to use for pushing. If NULL or an empty
* array, the configured refspecs will be used
* @param opts options to use for this push
*/
GIT_EXTERN(int) git_remote_push(git_remote *remote,
const git_strarray *refspecs,
const git_push_options *opts);
GIT_EXTERN(int) git_remote_push(
git_remote *remote,
const git_strarray *refspecs,
const git_push_options *opts);
/**
* Get the statistics structure that is filled in by the fetch operation.
......
......@@ -24,31 +24,9 @@
GIT_BEGIN_DECL
/**
* Flags to pass to transport
*
* Currently unused.
*/
typedef enum {
GIT_TRANSPORTFLAGS_NONE = 0
} git_transport_flags_t;
struct git_transport {
unsigned int version; /**< The struct version */
/** Set progress and error callbacks */
int GIT_CALLBACK(set_callbacks)(
git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
git_transport_certificate_check_cb certificate_check_cb,
void *payload);
/** Set custom headers for HTTP requests */
int GIT_CALLBACK(set_custom_headers)(
git_transport *transport,
const git_strarray *custom_headers);
/**
* Connect the transport to the remote repository, using the given
* direction.
......@@ -56,11 +34,17 @@ struct git_transport {
int GIT_CALLBACK(connect)(
git_transport *transport,
const char *url,
git_credential_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
const git_proxy_options *proxy_opts,
int direction,
int flags);
const git_remote_connect_options *connect_opts);
/**
* Resets the connect options for the given transport. This
* is useful for updating settings or callbacks for an already
* connected transport.
*/
int GIT_CALLBACK(set_connect_opts)(
git_transport *transport,
const git_remote_connect_options *connect_opts);
/**
* Get the list of available references in the remote repository.
......@@ -75,7 +59,9 @@ struct git_transport {
git_transport *transport);
/** Executes the push whose context is in the git_push object. */
int GIT_CALLBACK(push)(git_transport *transport, git_push *push, const git_remote_callbacks *callbacks);
int GIT_CALLBACK(push)(
git_transport *transport,
git_push *push);
/**
* Negotiate a fetch with the remote repository.
......@@ -99,16 +85,11 @@ struct git_transport {
int GIT_CALLBACK(download_pack)(
git_transport *transport,
git_repository *repo,
git_indexer_progress *stats,
git_indexer_progress_cb progress_cb,
void *progress_payload);
git_indexer_progress *stats);
/** Checks to see if the transport is connected */
int GIT_CALLBACK(is_connected)(git_transport *transport);
/** Reads the flags value previously passed into connect() */
int GIT_CALLBACK(read_flags)(git_transport *transport, int *flags);
/** Cancels any outstanding transport operation */
void GIT_CALLBACK(cancel)(git_transport *transport);
......
......@@ -134,21 +134,14 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
remote->refs.length);
}
int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *callbacks)
int git_fetch_download_pack(git_remote *remote)
{
git_transport *t = remote->transport;
git_indexer_progress_cb progress = NULL;
void *payload = NULL;
if (!remote->need_pack)
return 0;
if (callbacks) {
progress = callbacks->transfer_progress;
payload = callbacks->payload;
}
return t->download_pack(t, remote->repo, &remote->stats, progress, payload);
return t->download_pack(t, remote->repo, &remote->stats);
}
int git_fetch_options_init(git_fetch_options *opts, unsigned int version)
......
......@@ -15,7 +15,7 @@
int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts);
int git_fetch_download_pack(git_remote *remote, const git_remote_callbacks *callbacks);
int git_fetch_download_pack(git_remote *remote);
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
......
......@@ -39,8 +39,11 @@ int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src)
return 0;
}
void git_proxy_options_clear(git_proxy_options *opts)
void git_proxy_options_dispose(git_proxy_options *opts)
{
if (!opts)
return;
git__free((char *) opts->url);
opts->url = NULL;
}
......@@ -12,6 +12,6 @@
#include "git2/proxy.h"
extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src);
extern void git_proxy_options_clear(git_proxy_options *opts);
extern void git_proxy_options_dispose(git_proxy_options *opts);
#endif
......@@ -29,19 +29,26 @@ static int push_status_ref_cmp(const void *a, const void *b)
return strcmp(push_status_a->ref, push_status_b->ref);
}
int git_push_new(git_push **out, git_remote *remote)
int git_push_new(git_push **out, git_remote *remote, const git_push_options *opts)
{
git_push *p;
*out = NULL;
GIT_ERROR_CHECK_VERSION(opts, GIT_PUSH_OPTIONS_VERSION, "git_push_options");
p = git__calloc(1, sizeof(*p));
GIT_ERROR_CHECK_ALLOC(p);
p->repo = remote->repo;
p->remote = remote;
p->report_status = 1;
p->pb_parallelism = 1;
p->pb_parallelism = opts ? opts->pb_parallelism : 1;
if (opts) {
GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
memcpy(&p->callbacks, &opts->callbacks, sizeof(git_remote_callbacks));
}
if (git_vector_init(&p->specs, 0, push_spec_rref_cmp) < 0) {
git__free(p);
......@@ -65,20 +72,6 @@ int git_push_new(git_push **out, git_remote *remote)
return 0;
}
int git_push_set_options(git_push *push, const git_push_options *opts)
{
if (!push || !opts)
return -1;
GIT_ERROR_CHECK_VERSION(opts, GIT_PUSH_OPTIONS_VERSION, "git_push_options");
push->pb_parallelism = opts->pb_parallelism;
push->connection.custom_headers = &opts->custom_headers;
push->connection.proxy = &opts->proxy_opts;
return 0;
}
static void free_refspec(push_spec *spec)
{
if (spec == NULL)
......@@ -411,10 +404,11 @@ static int calculate_work(git_push *push)
return 0;
}
static int do_push(git_push *push, const git_remote_callbacks *callbacks)
static int do_push(git_push *push)
{
int error = 0;
git_transport *transport = push->remote->transport;
git_remote_callbacks *callbacks = &push->callbacks;
if (!transport->push) {
git_error_set(GIT_ERROR_NET, "remote transport doesn't support push");
......@@ -446,7 +440,7 @@ static int do_push(git_push *push, const git_remote_callbacks *callbacks)
goto on_error;
if ((error = queue_objects(push)) < 0 ||
(error = transport->push(transport, push, callbacks)) < 0)
(error = transport->push(transport, push)) < 0)
goto on_error;
on_error:
......@@ -472,7 +466,7 @@ static int filter_refs(git_remote *remote)
return 0;
}
int git_push_finish(git_push *push, const git_remote_callbacks *callbacks)
int git_push_finish(git_push *push)
{
int error;
......@@ -482,7 +476,7 @@ int git_push_finish(git_push *push, const git_remote_callbacks *callbacks)
}
if ((error = filter_refs(push->remote)) < 0 ||
(error = do_push(push, callbacks)) < 0)
(error = do_push(push)) < 0)
return error;
if (!push->unpack_ok) {
......
......@@ -41,7 +41,7 @@ struct git_push {
/* options */
unsigned pb_parallelism;
git_remote_connection_opts connection;
git_remote_callbacks callbacks;
};
/**
......@@ -56,22 +56,11 @@ void git_push_status_free(push_status *status);
*
* @param out New push object
* @param remote Remote instance
* @param opts Push options or NULL
*
* @return 0 or an error code
*/
int git_push_new(git_push **out, git_remote *remote);
/**
* Set options on a push object
*
* @param push The push object
* @param opts The options to set on the push object
*
* @return 0 or an error code
*/
int git_push_set_options(
git_push *push,
const git_push_options *opts);
int git_push_new(git_push **out, git_remote *remote, const git_push_options *opts);
/**
* Add a refspec to be pushed
......@@ -104,11 +93,10 @@ int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks);
* order to find out which updates were accepted or rejected.
*
* @param push The push object
* @param callbacks the callbacks to use for this connection
*
* @return 0 or an error code
*/
int git_push_finish(git_push *push, const git_remote_callbacks *callbacks);
int git_push_finish(git_push *push);
/**
* Invoke callback `cb' on each status entry
......
......@@ -16,6 +16,7 @@
#include "refspec.h"
#include "fetchhead.h"
#include "push.h"
#include "proxy.h"
#include "git2/config.h"
#include "git2/types.h"
......@@ -756,54 +757,155 @@ int git_remote__urlfordirection(
return resolve_url(url_out, url, direction, callbacks);
}
static int remote_transport_set_callbacks(git_transport *t, const git_remote_callbacks *cbs)
int git_remote_connect_options_init(
git_remote_connect_options *opts,
unsigned int version)
{
if (!t->set_callbacks || !cbs)
GIT_INIT_STRUCTURE_FROM_TEMPLATE(
opts, version, git_remote_connect_options, GIT_REMOTE_CONNECT_OPTIONS_INIT);
return 0;
}
int git_remote_connect_options_dup(
git_remote_connect_options *dst,
const git_remote_connect_options *src)
{
memcpy(dst, src, sizeof(git_remote_connect_options));
if (git_proxy_options_dup(&dst->proxy_opts, &src->proxy_opts) < 0 ||
git_strarray_copy(&dst->custom_headers, &src->custom_headers) < 0)
return -1;
return 0;
}
void git_remote_connect_options_dispose(git_remote_connect_options *opts)
{
if (!opts)
return;
git_strarray_dispose(&opts->custom_headers);
git_proxy_options_dispose(&opts->proxy_opts);
}
static size_t http_header_name_length(const char *http_header)
{
const char *colon = strchr(http_header, ':');
if (!colon)
return 0;
return colon - http_header;
}
static bool is_malformed_http_header(const char *http_header)
{
const char *c;
size_t name_len;
/* Disallow \r and \n */
if ((c = strchr(http_header, '\r')) != NULL)
return true;
if ((c = strchr(http_header, '\n')) != NULL)
return true;
/* Require a header name followed by : */
if ((name_len = http_header_name_length(http_header)) < 1)
return true;
return false;
}
static char *forbidden_custom_headers[] = {
"User-Agent",
"Host",
"Accept",
"Content-Type",
"Transfer-Encoding",
"Content-Length",
};
static bool is_forbidden_custom_header(const char *custom_header)
{
unsigned long i;
size_t name_len = http_header_name_length(custom_header);
/* Disallow headers that we set */
for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++)
if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0)
return true;
return false;
}
static int validate_custom_headers(const git_strarray *custom_headers)
{
size_t i;
if (!custom_headers)
return 0;
return t->set_callbacks(t, cbs->sideband_progress, NULL,
cbs->certificate_check, cbs->payload);
for (i = 0; i < custom_headers->count; i++) {
if (is_malformed_http_header(custom_headers->strings[i])) {
git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]);
return -1;
}
if (is_forbidden_custom_header(custom_headers->strings[i])) {
git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]);
return -1;
}
}
return 0;
}
static int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers)
int git_remote_connect_options_normalize(
git_remote_connect_options *dst,
const git_remote_connect_options *src)
{
if (!t->set_custom_headers)
git_remote_connect_options_dispose(dst);
if (!src) {
git_remote_connect_options_init(dst, GIT_REMOTE_CONNECT_OPTIONS_VERSION);
return 0;
}
GIT_ERROR_CHECK_VERSION(src, GIT_REMOTE_CONNECT_OPTIONS_VERSION, "git_remote_connect_options");
GIT_ERROR_CHECK_VERSION(&src->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
GIT_ERROR_CHECK_VERSION(&src->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
return t->set_custom_headers(t, custom_headers);
if (validate_custom_headers(&src->custom_headers))
return -1;
return git_remote_connect_options_dup(dst, src);
}
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn)
int git_remote_connect_ext(
git_remote *remote,
git_direction direction,
const git_remote_connect_options *given_opts)
{
git_transport *t;
git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
git_str url = GIT_STR_INIT;
int flags = GIT_TRANSPORTFLAGS_NONE;
git_transport *t;
int error;
void *payload = NULL;
git_credential_acquire_cb credentials = NULL;
git_transport_cb transport = NULL;
GIT_ASSERT_ARG(remote);
if (callbacks) {
GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
credentials = callbacks->credentials;
transport = callbacks->transport;
payload = callbacks->payload;
}
if (given_opts)
memcpy(&opts, given_opts, sizeof(git_remote_connect_options));
if (conn->proxy)
GIT_ERROR_CHECK_VERSION(conn->proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
GIT_ERROR_CHECK_VERSION(&opts.callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
GIT_ERROR_CHECK_VERSION(&opts.proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
t = remote->transport;
if ((error = git_remote__urlfordirection(&url, remote, direction, callbacks)) < 0)
if ((error = git_remote__urlfordirection(&url, remote, direction, &opts.callbacks)) < 0)
goto on_error;
/* If we don't have a transport object yet, and the caller specified a
* custom transport factory, use that */
if (!t && transport &&
(error = transport(&t, remote, payload)) < 0)
if (!t && opts.callbacks.transport &&
(error = opts.callbacks.transport(&t, remote, opts.callbacks.payload)) < 0)
goto on_error;
/* If we still don't have a transport, then use the global
......@@ -811,11 +913,7 @@ int git_remote__connect(git_remote *remote, git_direction direction, const git_r
if (!t && (error = git_transport_new(&t, remote, url.ptr)) < 0)
goto on_error;
if ((error = set_transport_custom_headers(t, conn->custom_headers)) != 0)
goto on_error;
if ((error = remote_transport_set_callbacks(t, callbacks)) < 0 ||
(error = t->connect(t, url.ptr, credentials, payload, conn->proxy, direction, flags)) != 0)
if ((error = t->connect(t, url.ptr, direction, &opts)) != 0)
goto on_error;
remote->transport = t;
......@@ -836,14 +934,25 @@ on_error:
return error;
}
int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy, const git_strarray *custom_headers)
int git_remote_connect(
git_remote *remote,
git_direction direction,
const git_remote_callbacks *callbacks,
const git_proxy_options *proxy,
const git_strarray *custom_headers)
{
git_remote_connection_opts conn;
git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
if (callbacks)
memcpy(&opts.callbacks, callbacks, sizeof(git_remote_callbacks));
conn.proxy = proxy;
conn.custom_headers = custom_headers;
if (proxy)
memcpy(&opts.proxy_opts, proxy, sizeof(git_proxy_options));
return git_remote__connect(remote, direction, callbacks, &conn);
if (custom_headers)
memcpy(&opts.custom_headers, custom_headers, sizeof(git_strarray));
return git_remote_connect_ext(remote, direction, &opts);
}
int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
......@@ -1057,33 +1166,43 @@ static int ls_to_vector(git_vector *out, git_remote *remote)
return 0;
}
int git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts)
{
int error = -1;
size_t i;
git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL;
GIT_ASSERT_ARG(remote);
if (!remote->repo) {
git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
return -1;
#define copy_opts(out, in) \
if (in) { \
(out)->callbacks = (in)->callbacks; \
(out)->proxy_opts = (in)->proxy_opts; \
(out)->custom_headers = (in)->custom_headers; \
}
if (opts) {
GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks;
custom_headers = &opts->custom_headers;
GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
proxy = &opts->proxy_opts;
GIT_INLINE(int) connect_opts_from_fetch_opts(
git_remote_connect_options *out,
const git_fetch_options *fetch_opts)
{
git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
copy_opts(&tmp, fetch_opts);
return git_remote_connect_options_normalize(out, &tmp);
}
static int connect_or_reset_options(
git_remote *remote,
int direction,
git_remote_connect_options *opts)
{
if (!git_remote_connected(remote)) {
return git_remote_connect_ext(remote, direction, opts);
} else {
return remote->transport->set_connect_opts(remote->transport, opts);
}
}
if (!git_remote_connected(remote) &&
(error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) < 0)
goto on_error;
/* Download from an already connected remote. */
static int git_remote__download(
git_remote *remote,
const git_strarray *refspecs,
const git_fetch_options *opts)
{
git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
size_t i;
int error;
if (ls_to_vector(&refs, remote) < 0)
return -1;
......@@ -1116,7 +1235,7 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
git_vector_free(&specs);
if (error < 0)
return error;
goto on_error;
if (remote->push) {
git_push_free(remote->push);
......@@ -1124,9 +1243,9 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
}
if ((error = git_fetch_negotiate(remote, opts)) < 0)
return error;
goto on_error;
return git_fetch_download_pack(remote, cbs);
error = git_fetch_download_pack(remote);
on_error:
git_vector_free(&refs);
......@@ -1135,41 +1254,69 @@ on_error:
return error;
}
int git_remote_download(
git_remote *remote,
const git_strarray *refspecs,
const git_fetch_options *opts)
{
git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
int error;
GIT_ASSERT_ARG(remote);
if (!remote->repo) {
git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
return -1;
}
if (connect_opts_from_fetch_opts(&connect_opts, opts) < 0)
return -1;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
return error;
return git_remote__download(remote, refspecs, opts);
}
int git_remote_fetch(
git_remote *remote,
const git_strarray *refspecs,
const git_fetch_options *opts,
const char *reflog_message)
git_remote *remote,
const git_strarray *refspecs,
const git_fetch_options *opts,
const char *reflog_message)
{
int error, update_fetchhead = 1;
git_remote_autotag_option_t tagopt = remote->download_tags;
bool prune = false;
git_str reflog_msg_buf = GIT_STR_INIT;
const git_remote_callbacks *cbs = NULL;
git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT;
git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
GIT_ASSERT_ARG(remote);
if (!remote->repo) {
git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
return -1;
}
if (connect_opts_from_fetch_opts(&connect_opts, opts) < 0)
return -1;
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
return error;
if (opts) {
GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks;
conn.custom_headers = &opts->custom_headers;
update_fetchhead = opts->update_fetchhead;
tagopt = opts->download_tags;
GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
conn.proxy = &opts->proxy_opts;
}
/* Connect and download everything */
if ((error = git_remote__connect(remote, GIT_DIRECTION_FETCH, cbs, &conn)) != 0)
return error;
error = git_remote_download(remote, refspecs, opts);
error = git_remote__download(remote, refspecs, opts);
/* We don't need to be connected anymore */
git_remote_disconnect(remote);
/* If the download failed, return the error */
if (error != 0)
return error;
goto done;
/* Default reflog message */
if (reflog_message)
......@@ -1180,10 +1327,10 @@ int git_remote_fetch(
}
/* Create "remote/foo" branches for all remote branches */
error = git_remote_update_tips(remote, cbs, update_fetchhead, tagopt, git_str_cstr(&reflog_msg_buf));
error = git_remote_update_tips(remote, &connect_opts.callbacks, update_fetchhead, tagopt, git_str_cstr(&reflog_msg_buf));
git_str_dispose(&reflog_msg_buf);
if (error < 0)
return error;
goto done;
if (opts && opts->prune == GIT_FETCH_PRUNE)
prune = true;
......@@ -1195,8 +1342,10 @@ int git_remote_fetch(
prune = remote->prune_refs;
if (prune)
error = git_remote_prune(remote, cbs);
error = git_remote_prune(remote, &connect_opts.callbacks);
done:
git_remote_connect_options_dispose(&connect_opts);
return error;
}
......@@ -2619,14 +2768,25 @@ done:
return error;
}
int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
GIT_INLINE(int) connect_opts_from_push_opts(
git_remote_connect_options *out,
const git_push_options *push_opts)
{
size_t i;
int error;
git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
copy_opts(&tmp, push_opts);
return git_remote_connect_options_normalize(out, &tmp);
}
int git_remote_upload(
git_remote *remote,
const git_strarray *refspecs,
const git_push_options *opts)
{
git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
git_push *push;
git_refspec *spec;
const git_remote_callbacks *cbs = NULL;
git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT;
size_t i;
int error;
GIT_ASSERT_ARG(remote);
......@@ -2635,14 +2795,10 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
return -1;
}
if (opts) {
cbs = &opts->callbacks;
conn.custom_headers = &opts->custom_headers;
conn.proxy = &opts->proxy_opts;
}
if ((error = connect_opts_from_push_opts(&connect_opts, opts)) < 0)
goto cleanup;
if (!git_remote_connected(remote) &&
(error = git_remote__connect(remote, GIT_DIRECTION_PUSH, cbs, &conn)) < 0)
if ((error = connect_or_reset_options(remote, GIT_DIRECTION_PUSH, &connect_opts)) < 0)
goto cleanup;
free_refspecs(&remote->active_refspecs);
......@@ -2654,14 +2810,11 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
remote->push = NULL;
}
if ((error = git_push_new(&remote->push, remote)) < 0)
return error;
if ((error = git_push_new(&remote->push, remote, opts)) < 0)
goto cleanup;
push = remote->push;
if (opts && (error = git_push_set_options(push, opts)) < 0)
goto cleanup;
if (refspecs && refspecs->count > 0) {
for (i = 0; i < refspecs->count; i++) {
if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0)
......@@ -2676,23 +2829,25 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
}
}
if ((error = git_push_finish(push, cbs)) < 0)
if ((error = git_push_finish(push)) < 0)
goto cleanup;
if (cbs && cbs->push_update_reference &&
(error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0)
if (connect_opts.callbacks.push_update_reference &&
(error = git_push_status_foreach(push, connect_opts.callbacks.push_update_reference, connect_opts.callbacks.payload)) < 0)
goto cleanup;
cleanup:
git_remote_connect_options_dispose(&connect_opts);
return error;
}
int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
int git_remote_push(
git_remote *remote,
const git_strarray *refspecs,
const git_push_options *opts)
{
git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
int error;
const git_remote_callbacks *cbs = NULL;
const git_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL;
GIT_ASSERT_ARG(remote);
......@@ -2701,23 +2856,17 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_
return -1;
}
if (opts) {
GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks;
custom_headers = &opts->custom_headers;
GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
proxy = &opts->proxy_opts;
}
if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
return error;
if (connect_opts_from_push_opts(&connect_opts, opts) < 0)
return -1;
if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
return error;
goto done;
error = git_remote_update_tips(remote, cbs, 0, 0, NULL);
error = git_remote_update_tips(remote, &connect_opts.callbacks, 0, 0, NULL);
done:
git_remote_disconnect(remote);
git_remote_connect_options_dispose(&connect_opts);
return error;
}
......
......@@ -37,15 +37,6 @@ struct git_remote {
int passed_refspecs;
};
typedef struct git_remote_connection_opts {
const git_strarray *custom_headers;
const git_proxy_options *proxy;
} git_remote_connection_opts;
#define GIT_REMOTE_CONNECTION_OPTIONS_INIT { NULL, NULL }
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn);
int git_remote__urlfordirection(git_str *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url);
......@@ -54,4 +45,12 @@ git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *re
int git_remote__default_branch(git_str *out, git_remote *remote);
int git_remote_connect_options_dup(
git_remote_connect_options *dst,
const git_remote_connect_options *src);
int git_remote_connect_options_normalize(
git_remote_connect_options *dst,
const git_remote_connect_options *src);
void git_remote_connect_options_dispose(git_remote_connect_options *opts);
#endif
......@@ -174,6 +174,7 @@ GIT_INLINE(int) handle_remote_auth(
git_http_response *response)
{
http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
if (response->server_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support");
......@@ -187,8 +188,8 @@ GIT_INLINE(int) handle_remote_auth(
transport->owner->url,
response->server_auth_schemetypes,
response->server_auth_credtypes,
transport->owner->cred_acquire_cb,
transport->owner->cred_acquire_payload);
connect_opts->callbacks.credentials,
connect_opts->callbacks.payload);
}
GIT_INLINE(int) handle_proxy_auth(
......@@ -196,6 +197,7 @@ GIT_INLINE(int) handle_proxy_auth(
git_http_response *response)
{
http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
if (response->proxy_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support");
......@@ -206,11 +208,11 @@ GIT_INLINE(int) handle_proxy_auth(
return handle_auth(
&transport->proxy,
SERVER_TYPE_PROXY,
transport->owner->proxy.url,
connect_opts->proxy_opts.url,
response->server_auth_schemetypes,
response->proxy_auth_credtypes,
transport->owner->proxy.credentials,
transport->owner->proxy.payload);
connect_opts->proxy_opts.credentials,
connect_opts->proxy_opts.payload);
}
......@@ -286,6 +288,7 @@ static int lookup_proxy(
bool *out_use,
http_subtransport *transport)
{
git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
const char *proxy;
git_remote *remote;
char *config = NULL;
......@@ -294,9 +297,9 @@ static int lookup_proxy(
*out_use = false;
git_net_url_dispose(&transport->proxy.url);
switch (transport->owner->proxy.type) {
switch (connect_opts->proxy_opts.type) {
case GIT_PROXY_SPECIFIED:
proxy = transport->owner->proxy.url;
proxy = connect_opts->proxy_opts.url;
break;
case GIT_PROXY_AUTO:
......@@ -345,7 +348,7 @@ static int generate_request(
request->credentials = transport->server.cred;
request->proxy = use_proxy ? &transport->proxy.url : NULL;
request->proxy_credentials = transport->proxy.cred;
request->custom_headers = &transport->owner->custom_headers;
request->custom_headers = &transport->owner->connect_opts.custom_headers;
if (stream->service->method == GIT_HTTP_METHOD_POST) {
request->chunked = stream->service->chunked;
......@@ -633,6 +636,7 @@ static int http_action(
git_smart_service_t action)
{
http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
http_stream *stream;
const http_service *service;
int error;
......@@ -664,10 +668,10 @@ static int http_action(
if (!transport->http_client) {
git_http_client_options opts = {0};
opts.server_certificate_check_cb = transport->owner->certificate_check_cb;
opts.server_certificate_check_payload = transport->owner->message_cb_payload;
opts.proxy_certificate_check_cb = transport->owner->proxy.certificate_check;
opts.proxy_certificate_check_payload = transport->owner->proxy.payload;
opts.server_certificate_check_cb = connect_opts->callbacks.certificate_check;
opts.server_certificate_check_payload = connect_opts->callbacks.payload;
opts.proxy_certificate_check_cb = connect_opts->proxy_opts.certificate_check;
opts.proxy_certificate_check_payload = connect_opts->proxy_opts.payload;
if (git_http_client_new(&transport->http_client, &opts) < 0)
return -1;
......
......@@ -34,12 +34,9 @@ typedef struct {
git_remote *owner;
char *url;
int direction;
int flags;
git_atomic32 cancelled;
git_repository *repo;
git_transport_message_cb progress_cb;
git_transport_message_cb error_cb;
void *message_cb_payload;
git_remote_connect_options connect_opts;
git_vector refs;
unsigned connected : 1,
have_refs : 1;
......@@ -200,30 +197,26 @@ on_error:
static int local_connect(
git_transport *transport,
const char *url,
git_credential_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
const git_proxy_options *proxy,
int direction, int flags)
int direction,
const git_remote_connect_options *connect_opts)
{
git_repository *repo;
int error;
transport_local *t = (transport_local *) transport;
transport_local *t = (transport_local *)transport;
const char *path;
git_str buf = GIT_STR_INIT;
GIT_UNUSED(cred_acquire_cb);
GIT_UNUSED(cred_acquire_payload);
GIT_UNUSED(proxy);
if (t->connected)
return 0;
if (git_remote_connect_options_normalize(&t->connect_opts, connect_opts) < 0)
return -1;
free_heads(&t->refs);
t->url = git__strdup(url);
GIT_ERROR_CHECK_ALLOC(t->url);
t->direction = direction;
t->flags = flags;
/* 'url' may be a url or path; convert to a path */
if ((error = git_fs_path_from_url_or_path(&buf, url)) < 0) {
......@@ -249,6 +242,20 @@ static int local_connect(
return 0;
}
static int local_set_connect_opts(
git_transport *transport,
const git_remote_connect_options *connect_opts)
{
transport_local *t = (transport_local *)transport;
if (!t->connected) {
git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
return -1;
}
return git_remote_connect_options_normalize(&t->connect_opts, connect_opts);
}
static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_local *t = (transport_local *)transport;
......@@ -337,10 +344,10 @@ static int transfer_to_push_transfer(const git_indexer_progress *stats, void *pa
static int local_push(
git_transport *transport,
git_push *push,
const git_remote_callbacks *cbs)
git_push *push)
{
transport_local *t = (transport_local *)transport;
git_remote_callbacks *cbs = &t->connect_opts.callbacks;
git_repository *remote_repo = NULL;
push_spec *spec;
char *url = NULL;
......@@ -349,8 +356,6 @@ static int local_push(
int error;
size_t j;
GIT_UNUSED(cbs);
/* 'push->remote->url' may be a url or path; convert to a path */
if ((error = git_fs_path_from_url_or_path(&buf, push->remote->url)) < 0) {
git_str_dispose(&buf);
......@@ -440,12 +445,11 @@ static int local_push(
}
if (push->specs.length) {
int flags = t->flags;
url = git__strdup(t->url);
if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url,
NULL, NULL, NULL, GIT_DIRECTION_PUSH, flags))
GIT_DIRECTION_PUSH, NULL))
goto on_error;
}
......@@ -482,7 +486,7 @@ static int local_counting(int stage, unsigned int current, unsigned int total, v
transport_local *t = payload;
int error;
if (!t->progress_cb)
if (!t->connect_opts.callbacks.sideband_progress)
return 0;
if (stage == GIT_PACKBUILDER_ADDING_OBJECTS) {
......@@ -500,9 +504,19 @@ static int local_counting(int stage, unsigned int current, unsigned int total, v
if (git_str_oom(&progress_info))
return -1;
error = t->progress_cb(git_str_cstr(&progress_info), (int)git_str_len(&progress_info), t->message_cb_payload);
git_str_dispose(&progress_info);
if (progress_info.size > INT_MAX) {
git_error_set(GIT_ERROR_NET, "remote sent overly large progress data");
git_str_dispose(&progress_info);
return -1;
}
error = t->connect_opts.callbacks.sideband_progress(
progress_info.ptr,
(int)progress_info.size,
t->connect_opts.callbacks.payload);
git_str_dispose(&progress_info);
return error;
}
......@@ -532,9 +546,7 @@ static int foreach_reference_cb(git_reference *reference, void *payload)
static int local_download_pack(
git_transport *transport,
git_repository *repo,
git_indexer_progress *stats,
git_indexer_progress_cb progress_cb,
void *progress_payload)
git_indexer_progress *stats)
{
transport_local *t = (transport_local*)transport;
git_revwalk *walk = NULL;
......@@ -545,9 +557,11 @@ static int local_download_pack(
git_odb_writepack *writepack = NULL;
git_odb *odb = NULL;
git_str progress_info = GIT_STR_INIT;
foreach_data data = {0};
if ((error = git_revwalk_new(&walk, t->repo)) < 0)
goto cleanup;
git_revwalk_sorting(walk, GIT_SORT_TIME);
if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
......@@ -583,44 +597,56 @@ static int local_download_pack(
if ((error = git_packbuilder_insert_walk(pack, walk)))
goto cleanup;
if ((error = git_str_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0)
goto cleanup;
if (t->progress_cb &&
(error = t->progress_cb(git_str_cstr(&progress_info), (int)git_str_len(&progress_info), t->message_cb_payload)) < 0)
goto cleanup;
if (t->connect_opts.callbacks.sideband_progress) {
if ((error = git_str_printf(
&progress_info,
counting_objects_fmt,
git_packbuilder_object_count(pack))) < 0 ||
(error = t->connect_opts.callbacks.sideband_progress(
progress_info.ptr,
(int)progress_info.size,
t->connect_opts.callbacks.payload)) < 0)
goto cleanup;
}
/* Walk the objects, building a packfile */
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
goto cleanup;
/* One last one with the newline */
git_str_clear(&progress_info);
git_str_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack));
if ((error = git_str_putc(&progress_info, '\n')) < 0)
goto cleanup;
if (t->progress_cb &&
(error = t->progress_cb(git_str_cstr(&progress_info), (int)git_str_len(&progress_info), t->message_cb_payload)) < 0)
goto cleanup;
if (t->connect_opts.callbacks.sideband_progress) {
git_str_clear(&progress_info);
if ((error = git_str_printf(
&progress_info,
counting_objects_fmt,
git_packbuilder_object_count(pack))) < 0 ||
(error = git_str_putc(&progress_info, '\n')) < 0 ||
(error = t->connect_opts.callbacks.sideband_progress(
progress_info.ptr,
(int)progress_info.size,
t->connect_opts.callbacks.payload)) < 0)
goto cleanup;
}
if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0)
if ((error = git_odb_write_pack(
&writepack,
odb,
t->connect_opts.callbacks.transfer_progress,
t->connect_opts.callbacks.payload)) < 0)
goto cleanup;
/* Write the data to the ODB */
{
foreach_data data = {0};
data.stats = stats;
data.progress_cb = progress_cb;
data.progress_payload = progress_payload;
data.writepack = writepack;
data.stats = stats;
data.progress_cb = t->connect_opts.callbacks.transfer_progress;
data.progress_payload = t->connect_opts.callbacks.payload;
data.writepack = writepack;
/* autodetect */
git_packbuilder_set_threads(pack, 0);
/* autodetect */
git_packbuilder_set_threads(pack, 0);
if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
goto cleanup;
}
if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
goto cleanup;
error = writepack->commit(writepack, stats);
......@@ -632,24 +658,6 @@ cleanup:
return error;
}
static int local_set_callbacks(
git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
git_transport_certificate_check_cb certificate_check_cb,
void *message_cb_payload)
{
transport_local *t = (transport_local *)transport;
GIT_UNUSED(certificate_check_cb);
t->progress_cb = progress_cb;
t->error_cb = error_cb;
t->message_cb_payload = message_cb_payload;
return 0;
}
static int local_is_connected(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
......@@ -657,15 +665,6 @@ static int local_is_connected(git_transport *transport)
return t->connected;
}
static int local_read_flags(git_transport *transport, int *flags)
{
transport_local *t = (transport_local *)transport;
*flags = t->flags;
return 0;
}
static void local_cancel(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
......@@ -720,8 +719,8 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
GIT_ERROR_CHECK_ALLOC(t);
t->parent.version = GIT_TRANSPORT_VERSION;
t->parent.set_callbacks = local_set_callbacks;
t->parent.connect = local_connect;
t->parent.set_connect_opts = local_set_connect_opts;
t->parent.negotiate_fetch = local_negotiate_fetch;
t->parent.download_pack = local_download_pack;
t->parent.push = local_push;
......@@ -729,7 +728,6 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
t->parent.free = local_free;
t->parent.ls = local_ls;
t->parent.is_connected = local_is_connected;
t->parent.read_flags = local_read_flags;
t->parent.cancel = local_cancel;
if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) {
......
......@@ -56,101 +56,6 @@ GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransp
return 0;
}
static int git_smart__set_callbacks(
git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
git_transport_certificate_check_cb certificate_check_cb,
void *message_cb_payload)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
t->progress_cb = progress_cb;
t->error_cb = error_cb;
t->certificate_check_cb = certificate_check_cb;
t->message_cb_payload = message_cb_payload;
return 0;
}
static size_t http_header_name_length(const char *http_header)
{
const char *colon = strchr(http_header, ':');
if (!colon)
return 0;
return colon - http_header;
}
static bool is_malformed_http_header(const char *http_header)
{
const char *c;
size_t name_len;
/* Disallow \r and \n */
c = strchr(http_header, '\r');
if (c)
return true;
c = strchr(http_header, '\n');
if (c)
return true;
/* Require a header name followed by : */
name_len = http_header_name_length(http_header);
if (name_len < 1)
return true;
return false;
}
static char *forbidden_custom_headers[] = {
"User-Agent",
"Host",
"Accept",
"Content-Type",
"Transfer-Encoding",
"Content-Length",
};
static bool is_forbidden_custom_header(const char *custom_header)
{
unsigned long i;
size_t name_len = http_header_name_length(custom_header);
/* Disallow headers that we set */
for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++)
if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0)
return true;
return false;
}
static int git_smart__set_custom_headers(
git_transport *transport,
const git_strarray *custom_headers)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
size_t i;
if (t->custom_headers.count)
git_strarray_dispose(&t->custom_headers);
if (!custom_headers)
return 0;
for (i = 0; i < custom_headers->count; i++) {
if (is_malformed_http_header(custom_headers->strings[i])) {
git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]);
return -1;
}
if (is_forbidden_custom_header(custom_headers->strings[i])) {
git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]);
return -1;
}
}
return git_strarray_copy(&t->custom_headers, custom_headers);
}
int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
{
size_t i;
......@@ -206,11 +111,8 @@ static void free_symrefs(git_vector *symrefs)
static int git_smart__connect(
git_transport *transport,
const char *url,
git_credential_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
const git_proxy_options *proxy,
int direction,
int flags)
const git_remote_connect_options *connect_opts)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_smart_subtransport_stream *stream;
......@@ -223,24 +125,19 @@ static int git_smart__connect(
if (git_smart__reset_stream(t, true) < 0)
return -1;
if (git_remote_connect_options_normalize(&t->connect_opts, connect_opts) < 0)
return -1;
t->url = git__strdup(url);
GIT_ERROR_CHECK_ALLOC(t->url);
git_proxy_options_clear(&t->proxy);
if (git_proxy_options_dup(&t->proxy, proxy) < 0)
return -1;
t->direction = direction;
t->flags = flags;
t->cred_acquire_cb = cred_acquire_cb;
t->cred_acquire_payload = cred_acquire_payload;
if (GIT_DIRECTION_FETCH == t->direction)
if (GIT_DIRECTION_FETCH == t->direction) {
service = GIT_SERVICE_UPLOADPACK_LS;
else if (GIT_DIRECTION_PUSH == t->direction)
} else if (GIT_DIRECTION_PUSH == t->direction) {
service = GIT_SERVICE_RECEIVEPACK_LS;
else {
} else {
git_error_set(GIT_ERROR_NET, "invalid direction");
return -1;
}
......@@ -315,6 +212,20 @@ cleanup:
return error;
}
static int git_smart__set_connect_opts(
git_transport *transport,
const git_remote_connect_options *opts)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
if (!t->connected) {
git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
return -1;
}
return git_remote_connect_options_normalize(&t->connect_opts, opts);
}
static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
......@@ -401,15 +312,6 @@ static int git_smart__is_connected(git_transport *transport)
return t->connected;
}
static int git_smart__read_flags(git_transport *transport, int *flags)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
*flags = t->flags;
return 0;
}
static int git_smart__close(git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
......@@ -465,9 +367,8 @@ static void git_smart__free(git_transport *transport)
git_pkt_free(p);
git_vector_free(refs);
git__free((char *)t->proxy.url);
git_strarray_dispose(&t->custom_headers);
git_remote_connect_options_dispose(&t->connect_opts);
git__free(t);
}
......@@ -482,34 +383,30 @@ static int ref_name_cmp(const void *a, const void *b)
int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_remote_connect_options *connect_opts = &t->connect_opts;
GIT_ASSERT_ARG(transport);
GIT_ASSERT_ARG(cert);
GIT_ASSERT_ARG(hostname);
if (!t->certificate_check_cb)
if (!connect_opts->callbacks.certificate_check)
return GIT_PASSTHROUGH;
return t->certificate_check_cb(cert, valid, hostname, t->message_cb_payload);
return connect_opts->callbacks.certificate_check(cert, valid, hostname, connect_opts->callbacks.payload);
}
int git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
git_remote_connect_options *connect_opts = &t->connect_opts;
GIT_ASSERT_ARG(out);
GIT_ASSERT_ARG(transport);
if (!t->cred_acquire_cb)
if (!connect_opts->callbacks.credentials)
return GIT_PASSTHROUGH;
return t->cred_acquire_cb(out, t->url, user, methods, t->cred_acquire_payload);
}
int git_transport_smart_proxy_options(git_proxy_options *out, git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
return git_proxy_options_dup(out, &t->proxy);
return connect_opts->callbacks.credentials(out, t->url, user, methods, connect_opts->callbacks.payload);
}
int git_transport_smart(git_transport **out, git_remote *owner, void *param)
......@@ -524,9 +421,8 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
GIT_ERROR_CHECK_ALLOC(t);
t->parent.version = GIT_TRANSPORT_VERSION;
t->parent.set_callbacks = git_smart__set_callbacks;
t->parent.set_custom_headers = git_smart__set_custom_headers;
t->parent.connect = git_smart__connect;
t->parent.set_connect_opts = git_smart__set_connect_opts;
t->parent.close = git_smart__close;
t->parent.free = git_smart__free;
t->parent.negotiate_fetch = git_smart__negotiate_fetch;
......@@ -534,7 +430,6 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
t->parent.push = git_smart__push;
t->parent.ls = git_smart__ls;
t->parent.is_connected = git_smart__is_connected;
t->parent.read_flags = git_smart__read_flags;
t->parent.cancel = git_smart__cancel;
t->owner = owner;
......
......@@ -137,16 +137,8 @@ typedef struct {
git_transport parent;
git_remote *owner;
char *url;
git_credential_acquire_cb cred_acquire_cb;
void *cred_acquire_payload;
git_proxy_options proxy;
git_remote_connect_options connect_opts;
int direction;
int flags;
git_transport_message_cb progress_cb;
git_transport_message_cb error_cb;
git_transport_certificate_check_cb certificate_check_cb;
void *message_cb_payload;
git_strarray custom_headers;
git_smart_subtransport *wrapped;
git_smart_subtransport_stream *current_stream;
transport_smart_caps caps;
......@@ -157,8 +149,8 @@ typedef struct {
packetsize_cb packetsize_cb;
void *packetsize_payload;
unsigned rpc : 1,
have_refs : 1,
connected : 1;
have_refs : 1,
connected : 1;
gitno_buffer buffer;
char buffer_data[65536];
} transport_smart;
......@@ -166,7 +158,7 @@ typedef struct {
/* smart_protocol.c */
int git_smart__store_refs(transport_smart *t, int flushes);
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs);
int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs);
int git_smart__push(git_transport *transport, git_push *push);
int git_smart__negotiate_fetch(
git_transport *transport,
......@@ -177,9 +169,7 @@ int git_smart__negotiate_fetch(
int git_smart__download_pack(
git_transport *transport,
git_repository *repo,
git_indexer_progress *stats,
git_indexer_progress_cb progress_cb,
void *progress_payload);
git_indexer_progress *stats);
/* smart.c */
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
......
......@@ -512,9 +512,7 @@ static int network_packetsize(size_t received, void *payload)
int git_smart__download_pack(
git_transport *transport,
git_repository *repo,
git_indexer_progress *stats,
git_indexer_progress_cb progress_cb,
void *progress_payload)
git_indexer_progress *stats)
{
transport_smart *t = (transport_smart *)transport;
gitno_buffer *buf = &t->buffer;
......@@ -523,6 +521,10 @@ int git_smart__download_pack(
int error = 0;
struct network_packetsize_payload npp = {0};
// TODO
git_indexer_progress_cb progress_cb = t->connect_opts.callbacks.transfer_progress;
void *progress_payload = t->connect_opts.callbacks.payload;
memset(stats, 0, sizeof(git_indexer_progress));
if (progress_cb) {
......@@ -568,7 +570,7 @@ int git_smart__download_pack(
git_error_clear();
error = GIT_EUSER;
} else if (pkt->type == GIT_PKT_PROGRESS) {
if (t->progress_cb) {
if (t->connect_opts.callbacks.sideband_progress) {
git_pkt_progress *p = (git_pkt_progress *) pkt;
if (p->len > INT_MAX) {
......@@ -577,7 +579,7 @@ int git_smart__download_pack(
goto done;
}
error = t->progress_cb(p->data, (int)p->len, t->message_cb_payload);
error = t->connect_opts.callbacks.sideband_progress(p->data, (int)p->len, t->connect_opts.callbacks.payload);
}
} else if (pkt->type == GIT_PKT_DATA) {
git_pkt_data *p = (git_pkt_data *) pkt;
......@@ -811,7 +813,7 @@ static int parse_report(transport_smart *transport, git_push *push)
error = -1;
break;
case GIT_PKT_PROGRESS:
if (transport->progress_cb) {
if (transport->connect_opts.callbacks.sideband_progress) {
git_pkt_progress *p = (git_pkt_progress *) pkt;
if (p->len > INT_MAX) {
......@@ -820,7 +822,7 @@ static int parse_report(transport_smart *transport, git_push *push)
goto done;
}
error = transport->progress_cb(p->data, (int)p->len, transport->message_cb_payload);
error = transport->connect_opts.callbacks.sideband_progress(p->data, (int)p->len, transport->connect_opts.callbacks.payload);
}
break;
default:
......@@ -987,9 +989,10 @@ static int stream_thunk(void *buf, size_t size, void *data)
return error;
}
int git_smart__push(git_transport *transport, git_push *push, const git_remote_callbacks *cbs)
int git_smart__push(git_transport *transport, git_push *push)
{
transport_smart *t = (transport_smart *)transport;
git_remote_callbacks *cbs = &t->connect_opts.callbacks;
struct push_packbuilder_payload packbuilder_payload = {0};
git_str pktline = GIT_STR_INIT;
int error = 0, need_pack = 0;
......
......@@ -441,11 +441,15 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char *
int error, no_callback = 0;
git_credential *cred = NULL;
if (!t->owner->cred_acquire_cb) {
if (!t->owner->connect_opts.callbacks.credentials) {
no_callback = 1;
} else {
error = t->owner->cred_acquire_cb(&cred, t->owner->url, user, auth_methods,
t->owner->cred_acquire_payload);
error = t->owner->connect_opts.callbacks.credentials(
&cred,
t->owner->url,
user,
auth_methods,
t->owner->connect_opts.callbacks.payload);
if (error == GIT_PASSTHROUGH) {
no_callback = 1;
......@@ -558,7 +562,7 @@ post_extract:
if ((error = _git_ssh_session_create(&session, s->io)) < 0)
goto done;
if (t->owner->certificate_check_cb != NULL) {
if (t->owner->connect_opts.callbacks.certificate_check != NULL) {
git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
const char *key;
size_t cert_len;
......@@ -632,7 +636,11 @@ post_extract:
cert_ptr = &cert;
error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, urldata.host, t->owner->message_cb_payload);
error = t->owner->connect_opts.callbacks.certificate_check(
(git_cert *)cert_ptr,
0,
urldata.host,
t->owner->connect_opts.callbacks.payload);
if (error < 0 && error != GIT_PASSTHROUGH) {
if (!git_error_last())
......
......@@ -293,14 +293,14 @@ static int certificate_check(winhttp_stream *s, int valid)
git_cert_x509 cert;
/* If there is no override, we should fail if WinHTTP doesn't think it's fine */
if (t->owner->certificate_check_cb == NULL && !valid) {
if (t->owner->connect_opts.callbacks.certificate_check == NULL && !valid) {
if (!git_error_last())
git_error_set(GIT_ERROR_HTTP, "unknown certificate check failure");
return GIT_ECERTIFICATE;
}
if (t->owner->certificate_check_cb == NULL || git__strcmp(t->server.url.scheme, "https") != 0)
if (t->owner->connect_opts.callbacks.certificate_check == NULL || git__strcmp(t->server.url.scheme, "https") != 0)
return 0;
if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) {
......@@ -312,7 +312,7 @@ static int certificate_check(winhttp_stream *s, int valid)
cert.parent.cert_type = GIT_CERT_X509;
cert.data = cert_ctx->pbCertEncoded;
cert.len = cert_ctx->cbCertEncoded;
error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->server.url.host, t->owner->message_cb_payload);
error = t->owner->connect_opts.callbacks.certificate_check((git_cert *) &cert, valid, t->server.url.host, t->owner->connect_opts.callbacks.payload);
CertFreeCertificateContext(cert_ctx);
if (error == GIT_PASSTHROUGH)
......@@ -426,7 +426,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error;
}
proxy_opts = &t->owner->proxy;
proxy_opts = &t->owner->connect_opts.proxy_opts;
if (proxy_opts->type == GIT_PROXY_AUTO) {
/* Set proxy if necessary */
if (git_remote__http_proxy(&proxy_url, t->owner->owner, &t->server.url) < 0)
......@@ -560,10 +560,10 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
}
for (i = 0; i < t->owner->custom_headers.count; i++) {
if (t->owner->custom_headers.strings[i]) {
for (i = 0; i < t->owner->connect_opts.custom_headers.count; i++) {
if (t->owner->connect_opts.custom_headers.strings[i]) {
git_str_clear(&buf);
git_str_puts(&buf, t->owner->custom_headers.strings[i]);
git_str_puts(&buf, t->owner->connect_opts.custom_headers.strings[i]);
if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
git_error_set(GIT_ERROR_OS, "failed to convert custom header to wide characters");
goto on_error;
......@@ -577,14 +577,6 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
}
/* If requested, disable certificate validation */
if (strcmp(t->server.url.scheme, "https") == 0) {
int flags;
if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
goto on_error;
}
if ((error = apply_credentials(s->request, &t->server.url, WINHTTP_AUTH_TARGET_SERVER, t->server.cred, t->server.auth_mechanisms)) < 0)
goto on_error;
......@@ -1218,8 +1210,8 @@ replay:
int error = acquire_credentials(s->request,
&t->server,
t->owner->url,
t->owner->cred_acquire_cb,
t->owner->cred_acquire_payload);
t->owner->connect_opts.callbacks.credentials,
t->owner->connect_opts.callbacks.payload);
if (error < 0) {
return error;
......@@ -1231,9 +1223,9 @@ replay:
} else if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) {
int error = acquire_credentials(s->request,
&t->proxy,
t->owner->proxy.url,
t->owner->proxy.credentials,
t->owner->proxy.payload);
t->owner->connect_opts.proxy_opts.url,
t->owner->connect_opts.proxy_opts.credentials,
t->owner->connect_opts.proxy_opts.payload);
if (error < 0) {
return error;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment