2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
11 #include "git2/sys/transport.h"
13 #include "socket_stream.h"
15 #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
17 static const char prefix_git
[] = "git://";
18 static const char cmd_uploadpack
[] = "git-upload-pack";
19 static const char cmd_receivepack
[] = "git-receive-pack";
22 git_smart_subtransport_stream parent
;
26 unsigned sent_command
: 1;
30 git_smart_subtransport parent
;
32 git_proto_stream
*current_stream
;
36 * Create a git protocol request.
38 * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
40 static int gen_proto(git_buf
*request
, const char *cmd
, const char *url
)
43 char host
[] = "host=";
46 delim
= strchr(url
, '/');
48 giterr_set(GITERR_NET
, "Malformed URL");
54 delim
= strchr(url
, ':');
56 delim
= strchr(url
, '/');
58 len
= 4 + strlen(cmd
) + 1 + strlen(repo
) + 1 + strlen(host
) + (delim
- url
) + 1;
60 git_buf_grow(request
, len
);
61 git_buf_printf(request
, "%04x%s %s%c%s",
62 (unsigned int)(len
& 0x0FFFF), cmd
, repo
, 0, host
);
63 git_buf_put(request
, url
, delim
- url
);
64 git_buf_putc(request
, '\0');
66 if (git_buf_oom(request
))
72 static int send_command(git_proto_stream
*s
)
75 git_buf request
= GIT_BUF_INIT
;
77 error
= gen_proto(&request
, s
->cmd
, s
->url
);
81 error
= git_stream_write(s
->io
, request
.ptr
, request
.size
, 0);
86 git_buf_free(&request
);
90 static int git_proto_stream_read(
91 git_smart_subtransport_stream
*stream
,
97 git_proto_stream
*s
= (git_proto_stream
*)stream
;
102 if (!s
->sent_command
&& (error
= send_command(s
)) < 0)
105 gitno_buffer_setup_fromstream(s
->io
, &buf
, buffer
, buf_size
);
107 if ((error
= gitno_recv(&buf
)) < 0)
110 *bytes_read
= buf
.offset
;
115 static int git_proto_stream_write(
116 git_smart_subtransport_stream
*stream
,
121 git_proto_stream
*s
= (git_proto_stream
*)stream
;
123 if (!s
->sent_command
&& (error
= send_command(s
)) < 0)
126 return git_stream_write(s
->io
, buffer
, len
, 0);
129 static void git_proto_stream_free(git_smart_subtransport_stream
*stream
)
131 git_proto_stream
*s
= (git_proto_stream
*)stream
;
132 git_subtransport
*t
= OWNING_SUBTRANSPORT(s
);
137 t
->current_stream
= NULL
;
139 git_stream_close(s
->io
);
140 git_stream_free(s
->io
);
145 static int git_proto_stream_alloc(
151 git_smart_subtransport_stream
**stream
)
158 s
= git__calloc(1, sizeof(git_proto_stream
));
159 GITERR_CHECK_ALLOC(s
);
161 s
->parent
.subtransport
= &t
->parent
;
162 s
->parent
.read
= git_proto_stream_read
;
163 s
->parent
.write
= git_proto_stream_write
;
164 s
->parent
.free
= git_proto_stream_free
;
167 s
->url
= git__strdup(url
);
174 if ((git_socket_stream_new(&s
->io
, host
, port
)) < 0)
177 GITERR_CHECK_VERSION(s
->io
, GIT_STREAM_VERSION
, "git_stream");
179 *stream
= &s
->parent
;
183 static int _git_uploadpack_ls(
186 git_smart_subtransport_stream
**stream
)
188 char *host
=NULL
, *port
=NULL
, *path
=NULL
, *user
=NULL
, *pass
=NULL
;
189 const char *stream_url
= url
;
195 if (!git__prefixcmp(url
, prefix_git
))
196 stream_url
+= strlen(prefix_git
);
198 if ((error
= gitno_extract_url_parts(&host
, &port
, &path
, &user
, &pass
, url
, GIT_DEFAULT_PORT
)) < 0)
201 error
= git_proto_stream_alloc(t
, stream_url
, cmd_uploadpack
, host
, port
, stream
);
211 git_proto_stream_free(*stream
);
215 s
= (git_proto_stream
*) *stream
;
216 if ((error
= git_stream_connect(s
->io
)) < 0) {
217 git_proto_stream_free(*stream
);
221 t
->current_stream
= s
;
226 static int _git_uploadpack(
229 git_smart_subtransport_stream
**stream
)
233 if (t
->current_stream
) {
234 *stream
= &t
->current_stream
->parent
;
238 giterr_set(GITERR_NET
, "Must call UPLOADPACK_LS before UPLOADPACK");
242 static int _git_receivepack_ls(
245 git_smart_subtransport_stream
**stream
)
247 char *host
=NULL
, *port
=NULL
, *path
=NULL
, *user
=NULL
, *pass
=NULL
;
248 const char *stream_url
= url
;
253 if (!git__prefixcmp(url
, prefix_git
))
254 stream_url
+= strlen(prefix_git
);
256 if ((error
= gitno_extract_url_parts(&host
, &port
, &path
, &user
, &pass
, url
, GIT_DEFAULT_PORT
)) < 0)
259 error
= git_proto_stream_alloc(t
, stream_url
, cmd_receivepack
, host
, port
, stream
);
268 git_proto_stream_free(*stream
);
272 s
= (git_proto_stream
*) *stream
;
274 if ((error
= git_stream_connect(s
->io
)) < 0)
277 t
->current_stream
= s
;
282 static int _git_receivepack(
285 git_smart_subtransport_stream
**stream
)
289 if (t
->current_stream
) {
290 *stream
= &t
->current_stream
->parent
;
294 giterr_set(GITERR_NET
, "Must call RECEIVEPACK_LS before RECEIVEPACK");
298 static int _git_action(
299 git_smart_subtransport_stream
**stream
,
300 git_smart_subtransport
*subtransport
,
302 git_smart_service_t action
)
304 git_subtransport
*t
= (git_subtransport
*) subtransport
;
307 case GIT_SERVICE_UPLOADPACK_LS
:
308 return _git_uploadpack_ls(t
, url
, stream
);
310 case GIT_SERVICE_UPLOADPACK
:
311 return _git_uploadpack(t
, url
, stream
);
313 case GIT_SERVICE_RECEIVEPACK_LS
:
314 return _git_receivepack_ls(t
, url
, stream
);
316 case GIT_SERVICE_RECEIVEPACK
:
317 return _git_receivepack(t
, url
, stream
);
324 static int _git_close(git_smart_subtransport
*subtransport
)
326 git_subtransport
*t
= (git_subtransport
*) subtransport
;
328 assert(!t
->current_stream
);
335 static void _git_free(git_smart_subtransport
*subtransport
)
337 git_subtransport
*t
= (git_subtransport
*) subtransport
;
339 assert(!t
->current_stream
);
344 int git_smart_subtransport_git(git_smart_subtransport
**out
, git_transport
*owner
, void *param
)
353 t
= git__calloc(1, sizeof(git_subtransport
));
354 GITERR_CHECK_ALLOC(t
);
357 t
->parent
.action
= _git_action
;
358 t
->parent
.close
= _git_close
;
359 t
->parent
.free
= _git_free
;
361 *out
= (git_smart_subtransport
*) t
;