Commit 7261d9837eb2ec521349a4e897d3236b35dbf094

Brad Morgan 2013-05-05T13:36:11

Added support for ssh:// urls

diff --git a/src/transport.c b/src/transport.c
index 0d2a860..6a8e67d 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -18,21 +18,22 @@ typedef struct transport_definition {
 	void *param;
 } transport_definition;
 
-static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL };
-static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL };
-
 static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1 };
 static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0 };
 static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0 };
 
+static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL };
+#ifdef GIT_WIN32
+static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL };
+#endif
+static transport_definition ssh_transport_definition = { "ssh://", 1, git_transport_smart, &ssh_subtransport_definition };
+
 static transport_definition transports[] = {
 	{"git://", 1, git_transport_smart, &git_subtransport_definition},
 	{"http://", 1, git_transport_smart, &http_subtransport_definition},
 	{"https://", 1, git_transport_smart, &http_subtransport_definition},
 	{"file://", 1, git_transport_local, NULL},
-	{"git+ssh://", 1, git_transport_smart, &ssh_subtransport_definition},
-	{"ssh+git://", 1, git_transport_smart, &ssh_subtransport_definition},
-	{"git@", 1, git_transport_smart, &ssh_subtransport_definition},
+	{"ssh://", 1, git_transport_smart, &ssh_subtransport_definition},
 	{NULL, 0, 0}
 };
 
@@ -75,7 +76,7 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
 	/* It could be a SSH remote path. Check to see if there's a :
 	 * SSH is an unsupported transport mechanism in this version of libgit2 */
 	if (!definition && strrchr(url, ':'))
-		definition = &dummy_transport_definition;
+		definition = &ssh_transport_definition;
 
 	/* Check to see if the path points to a file on the local file system */
 	if (!definition && git_path_exists(url) && git_path_isdir(url))
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 6e99e00..6e81c25 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -15,7 +15,7 @@
 #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
 
 static const char prefix_ssh[] = "ssh://";
-static const char prefix_git[] = "git@";
+static const char default_user[] = "git";
 static const char cmd_uploadpack[] = "git-upload-pack";
 static const char cmd_receivepack[] = "git-receive-pack";
 
@@ -26,7 +26,6 @@ typedef struct {
 	LIBSSH2_CHANNEL *channel;
 	const char *cmd;
 	char *url;
-	char *path;
 	unsigned sent_command : 1;
 } ssh_stream;
 
@@ -42,8 +41,21 @@ typedef struct {
  *
  * For example: git-upload-pack '/libgit2/libgit2'
  */
-static int gen_proto(git_buf *request, const char *cmd, const char *repo)
+static int gen_proto(git_buf *request, const char *cmd, const char *url)
 {
+	char *repo;
+	
+	if (!git__prefixcmp(url, prefix_ssh)) {
+		url = url + strlen(prefix_ssh);
+		repo = strchr(url, '/');
+	} else {
+		repo = strchr(url, ':');
+	}
+	
+	if (!repo) {
+		return -1;
+	}
+	
 	int len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
 	
 	git_buf_grow(request, len);
@@ -61,7 +73,7 @@ static int send_command(ssh_stream *s)
 	int error;
 	git_buf request = GIT_BUF_INIT;
 	
-	error = gen_proto(&request, s->cmd, s->path);
+	error = gen_proto(&request, s->cmd, s->url);
 	if (error < 0)
 		goto cleanup;
 	
@@ -183,18 +195,15 @@ static int ssh_stream_alloc(
 	return 0;
 }
 
-/* Temp */
-static int gitssh_extract_url_parts(
+static int git_ssh_extract_url_parts(
 	char **host,
 	char **username,
-	char **path,
 	const char *url)
 {
 	char *colon, *at;
 	const char *start;
     
     colon = strchr(url, ':');
-	at = strchr(url, '@');
 	
 	if (colon == NULL) {
 		giterr_set(GITERR_NET, "Malformed URL: missing :");
@@ -202,15 +211,15 @@ static int gitssh_extract_url_parts(
 	}
 	
 	start = url;
+	at = strchr(url, '@');
 	if (at) {
 		start = at+1;
 		*username = git__substrdup(url, at - url);
 	} else {
-		*username = "git";
+		*username = git__strdup(default_user);
 	}
 	
 	*host = git__substrdup(start, colon - start);
-	*path = colon+1;
 	
 	return 0;
 }
@@ -222,7 +231,8 @@ static int _git_ssh_setup_conn(
 	git_smart_subtransport_stream **stream
 )
 {
-	char *host, *user=NULL;
+	char *host, *port, *user=NULL, *pass=NULL;
+	const char *default_port = "22";
 	ssh_stream *s;
 	
 	*stream = NULL;
@@ -231,21 +241,35 @@ static int _git_ssh_setup_conn(
 	
 	s = (ssh_stream *)*stream;
 	
-	if (gitssh_extract_url_parts(&host, &user, &s->path, url) < 0)
-		goto on_error;
+	if (!git__prefixcmp(url, prefix_ssh)) {
+		url = url + strlen(prefix_ssh);
+		if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0)
+			return -1;
+	} else {
+		if (git_ssh_extract_url_parts(&host, &user, url) < 0)
+			goto on_error;
+		port = git__strdup(default_port);
+	}
 	
-	if (gitno_connect(&s->socket, host, "22", 0) < 0)
+	if (gitno_connect(&s->socket, host, port, 0) < 0)
 		goto on_error;
 	
-	if (t->owner->cred_acquire_cb(&t->cred,
-			t->owner->url,
-			user,
-			GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE,
-			t->owner->cred_acquire_payload) < 0)
-		return -1;
-	
+	if (user && pass) {
+		git_cred_userpass_plaintext_new(&t->cred, user, pass);
+	} else {
+		if (t->owner->cred_acquire_cb(&t->cred,
+				t->owner->url,
+				user,
+				GIT_CREDTYPE_USERPASS_PLAINTEXT | GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE,
+				t->owner->cred_acquire_payload) < 0)
+			return -1;
+	}
 	assert(t->cred);
 	
+	if (!user) {
+		user = git__strdup(default_user);
+	}
+	
 	git_cred_ssh_keyfile_passphrase *cred = (git_cred_ssh_keyfile_passphrase *)t->cred;
 	
 	LIBSSH2_SESSION* session = libssh2_session_init();