Commit a32d684f866e7710fd139d69ad5d1a8d0ae527c4

Vicent Marti 2014-04-22T15:07:18

Merge pull request #2282 from libgit2/cmn/remote-easier-bind A few niceties for binding authors

diff --git a/include/git2/remote.h b/include/git2/remote.h
index d57321f..ddde3e8 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -468,6 +468,9 @@ struct git_remote_callbacks {
 	/**
 	 * This will be called if the remote host requires
 	 * authentication in order to connect to it.
+	 *
+	 * Returning GIT_PASSTHROUGH will make libgit2 behave as
+	 * though this field isn't set.
 	 */
 	int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types,	void *data);
 
@@ -520,6 +523,17 @@ GIT_EXTERN(int) git_remote_init_callbacks(
 GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
 
 /**
+ * Retrieve the current callback structure
+ *
+ * This provides read access to the callbacks structure as the remote
+ * sees it.
+ *
+ * @param remote the remote to query
+ * @return a pointer to the callbacks structure
+ */
+GIT_EXTERN(const git_remote_callbacks *) git_remote_get_callbacks(git_remote *remote);
+
+/**
  * Get the statistics structure that is filled in by the fetch operation.
  */
 GIT_EXTERN(const git_transfer_progress *) git_remote_stats(git_remote *remote);
diff --git a/src/remote.c b/src/remote.c
index 243086b..c23a464 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -1253,6 +1253,13 @@ int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *cal
 	return 0;
 }
 
+const git_remote_callbacks *git_remote_get_callbacks(git_remote *remote)
+{
+	assert(remote);
+
+	return &remote->callbacks;
+}
+
 int git_remote_set_transport(git_remote *remote, git_transport *transport)
 {
 	assert(remote && transport);
diff --git a/src/transports/http.c b/src/transports/http.c
index c6aaeb9..a7eff73 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -248,6 +248,7 @@ static int on_headers_complete(http_parser *parser)
 	http_subtransport *t = ctx->t;
 	http_stream *s = ctx->s;
 	git_buf buf = GIT_BUF_INIT;
+	int error = 0, no_callback = 0;
 
 	/* Both parse_header_name and parse_header_value are populated
 	 * and ready for consumption. */
@@ -256,29 +257,43 @@ static int on_headers_complete(http_parser *parser)
 			return t->parse_error = PARSE_ERROR_GENERIC;
 
 	/* Check for an authentication failure. */
+
 	if (parser->status_code == 401 &&
-		get_verb == s->verb &&
-		t->owner->cred_acquire_cb) {
-		int allowed_types = 0;
+	    get_verb == s->verb) {
+		if (!t->owner->cred_acquire_payload) {
+			no_callback = 1;
+		} else {
+			int allowed_types = 0;
+
+			if (parse_unauthorized_response(&t->www_authenticate,
+							&allowed_types, &t->auth_mechanism) < 0)
+				return t->parse_error = PARSE_ERROR_GENERIC;
+
+			if (allowed_types &&
+			    (!t->cred || 0 == (t->cred->credtype & allowed_types))) {
+
+				error = t->owner->cred_acquire_cb(&t->cred,
+								  t->owner->url,
+								  t->connection_data.user,
+								  allowed_types,
+								  t->owner->cred_acquire_payload);
+
+				if (error == GIT_PASSTHROUGH) {
+					no_callback = 1;
+				} else if (error < 0) {
+					return PARSE_ERROR_GENERIC;
+				} else {
+					assert(t->cred);
+
+					/* Successfully acquired a credential. */
+					return t->parse_error = PARSE_ERROR_REPLAY;
+				}
+			}
+		}
 
-		if (parse_unauthorized_response(&t->www_authenticate,
-			&allowed_types, &t->auth_mechanism) < 0)
+		if (no_callback) {
+			giterr_set(GITERR_NET, "authentication required but no callback set");
 			return t->parse_error = PARSE_ERROR_GENERIC;
-
-		if (allowed_types &&
-			(!t->cred || 0 == (t->cred->credtype & allowed_types))) {
-
-			if (t->owner->cred_acquire_cb(&t->cred,
-					t->owner->url,
-					t->connection_data.user,
-					allowed_types,
-					t->owner->cred_acquire_payload) < 0)
-				return PARSE_ERROR_GENERIC;
-
-			assert(t->cred);
-
-			/* Successfully acquired a credential. */
-			return t->parse_error = PARSE_ERROR_REPLAY;
 		}
 	}
 
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index dea9902..b403727 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -387,6 +387,7 @@ static int _git_ssh_setup_conn(
 {
 	char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
 	const char *default_port="22";
+	int no_callback = 0;
 	ssh_stream *s;
 	LIBSSH2_SESSION* session=NULL;
 	LIBSSH2_CHANNEL* channel=NULL;
@@ -413,24 +414,31 @@ static int _git_ssh_setup_conn(
 	if (user && pass) {
 		if (git_cred_userpass_plaintext_new(&t->cred, user, pass) < 0)
 			goto on_error;
-	} else if (t->owner->cred_acquire_cb) {
-		if (t->owner->cred_acquire_cb(
-				&t->cred, t->owner->url, user,
-				GIT_CREDTYPE_USERPASS_PLAINTEXT |
-				GIT_CREDTYPE_SSH_KEY |
-				GIT_CREDTYPE_SSH_INTERACTIVE |
-				GIT_CREDTYPE_SSH_CUSTOM,
-				t->owner->cred_acquire_payload) < 0)
+	} else if (!t->owner->cred_acquire_cb) {
+		no_callback = 1;
+	} else {
+		int error;
+		error = t->owner->cred_acquire_cb(&t->cred, t->owner->url, user,
+			GIT_CREDTYPE_USERPASS_PLAINTEXT |
+			GIT_CREDTYPE_SSH_KEY | GIT_CREDTYPE_SSH_CUSTOM |
+			GIT_CREDTYPE_SSH_INTERACTIVE,
+			t->owner->cred_acquire_payload);
+
+		if (error == GIT_PASSTHROUGH)
+			no_callback = 1;
+		else if (error < 0)
 			goto on_error;
-
-		if (!t->cred) {
+		else if (!t->cred) {
 			giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
 			goto on_error;
 		}
-	} else {
-		giterr_set(GITERR_SSH, "Cannot set up SSH connection without credentials");
+	}
+
+	if (no_callback) {
+		giterr_set(GITERR_SSH, "authentication required but no callback set");
 		goto on_error;
 	}
+
 	assert(t->cred);
 
 	if (_git_ssh_session_create(&session, s->socket) < 0)