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/remote.h"
16 static int git_smart__recv_cb(gitno_buffer
*buf
)
18 transport_smart
*t
= (transport_smart
*) buf
->cb_data
;
19 size_t old_len
, bytes_read
;
22 GIT_ASSERT(t
->current_stream
);
24 old_len
= buf
->offset
;
26 if ((error
= t
->current_stream
->read(t
->current_stream
, buf
->data
+ buf
->offset
, buf
->len
- buf
->offset
, &bytes_read
)) < 0)
29 buf
->offset
+= bytes_read
;
31 if (t
->packetsize_cb
&& !t
->cancelled
.val
) {
32 error
= t
->packetsize_cb(bytes_read
, t
->packetsize_payload
);
34 git_atomic32_set(&t
->cancelled
, 1);
39 return (int)(buf
->offset
- old_len
);
42 GIT_INLINE(int) git_smart__reset_stream(transport_smart
*t
, bool close_subtransport
)
44 if (t
->current_stream
) {
45 t
->current_stream
->free(t
->current_stream
);
46 t
->current_stream
= NULL
;
49 if (close_subtransport
) {
53 if (t
->wrapped
->close(t
->wrapped
) < 0)
60 int git_smart__update_heads(transport_smart
*t
, git_vector
*symrefs
)
65 git_vector_clear(&t
->heads
);
66 git_vector_foreach(&t
->refs
, i
, pkt
) {
67 git_pkt_ref
*ref
= (git_pkt_ref
*) pkt
;
68 if (pkt
->type
!= GIT_PKT_REF
)
73 git_str buf
= GIT_STR_INIT
;
77 git_vector_foreach(symrefs
, j
, spec
) {
79 if (git_refspec_src_matches(spec
, ref
->head
.name
) &&
80 !(error
= git_refspec__transform(&buf
, spec
, ref
->head
.name
))) {
81 git__free(ref
->head
.symref_target
);
82 ref
->head
.symref_target
= git_str_detach(&buf
);
86 git_str_dispose(&buf
);
92 if (git_vector_insert(&t
->heads
, &ref
->head
) < 0)
99 static void free_symrefs(git_vector
*symrefs
)
104 git_vector_foreach(symrefs
, i
, spec
) {
105 git_refspec__dispose(spec
);
109 git_vector_free(symrefs
);
112 static int git_smart__connect(
113 git_transport
*transport
,
116 const git_remote_connect_options
*connect_opts
)
118 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
119 git_smart_subtransport_stream
*stream
;
124 git_smart_service_t service
;
126 if (git_smart__reset_stream(t
, true) < 0)
129 if (git_remote_connect_options_normalize(&t
->connect_opts
, t
->owner
->repo
, connect_opts
) < 0)
132 t
->url
= git__strdup(url
);
133 GIT_ERROR_CHECK_ALLOC(t
->url
);
135 t
->direction
= direction
;
137 if (GIT_DIRECTION_FETCH
== t
->direction
) {
138 service
= GIT_SERVICE_UPLOADPACK_LS
;
139 } else if (GIT_DIRECTION_PUSH
== t
->direction
) {
140 service
= GIT_SERVICE_RECEIVEPACK_LS
;
142 git_error_set(GIT_ERROR_NET
, "invalid direction");
146 if ((error
= t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, service
)) < 0)
149 /* Save off the current stream (i.e. socket) that we are working with */
150 t
->current_stream
= stream
;
152 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
154 /* 2 flushes for RPC; 1 for stateful */
155 if ((error
= git_smart__store_refs(t
, t
->rpc
? 2 : 1)) < 0)
158 /* Strip the comment packet for RPC */
160 pkt
= (git_pkt
*)git_vector_get(&t
->refs
, 0);
162 if (!pkt
|| GIT_PKT_COMMENT
!= pkt
->type
) {
163 git_error_set(GIT_ERROR_NET
, "invalid response");
166 /* Remove the comment pkt from the list */
167 git_vector_remove(&t
->refs
, 0);
172 /* We now have loaded the refs. */
175 pkt
= (git_pkt
*)git_vector_get(&t
->refs
, 0);
176 if (pkt
&& GIT_PKT_REF
!= pkt
->type
) {
177 git_error_set(GIT_ERROR_NET
, "invalid response");
180 first
= (git_pkt_ref
*)pkt
;
182 if ((error
= git_vector_init(&symrefs
, 1, NULL
)) < 0)
185 /* Detect capabilities */
186 if ((error
= git_smart__detect_caps(first
, &t
->caps
, &symrefs
)) == 0) {
187 /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
188 if (1 == t
->refs
.length
&& !strcmp(first
->head
.name
, "capabilities^{}") &&
189 git_oid_is_zero(&first
->head
.oid
)) {
190 git_vector_clear(&t
->refs
);
191 git_pkt_free((git_pkt
*)first
);
194 /* Keep a list of heads for _ls */
195 git_smart__update_heads(t
, &symrefs
);
196 } else if (error
== GIT_ENOTFOUND
) {
197 /* There was no ref packet received, or the cap list was empty */
200 git_error_set(GIT_ERROR_NET
, "invalid response");
204 if (t
->rpc
&& (error
= git_smart__reset_stream(t
, false)) < 0)
207 /* We're now logically connected. */
211 free_symrefs(&symrefs
);
216 static int git_smart__set_connect_opts(
217 git_transport
*transport
,
218 const git_remote_connect_options
*opts
)
220 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
223 git_error_set(GIT_ERROR_NET
, "cannot reconfigure a transport that is not connected");
227 return git_remote_connect_options_normalize(&t
->connect_opts
, t
->owner
->repo
, opts
);
230 static int git_smart__capabilities(unsigned int *capabilities
, git_transport
*transport
)
232 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
236 if (t
->caps
.want_tip_sha1
)
237 *capabilities
|= GIT_REMOTE_CAPABILITY_TIP_OID
;
239 if (t
->caps
.want_reachable_sha1
)
240 *capabilities
|= GIT_REMOTE_CAPABILITY_REACHABLE_OID
;
245 static int git_smart__ls(const git_remote_head
***out
, size_t *size
, git_transport
*transport
)
247 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
250 git_error_set(GIT_ERROR_NET
, "the transport has not yet loaded the refs");
254 *out
= (const git_remote_head
**) t
->heads
.contents
;
255 *size
= t
->heads
.length
;
260 int git_smart__negotiation_step(git_transport
*transport
, void *data
, size_t len
)
262 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
263 git_smart_subtransport_stream
*stream
;
266 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
269 if (GIT_DIRECTION_FETCH
!= t
->direction
) {
270 git_error_set(GIT_ERROR_NET
, "this operation is only valid for fetch");
274 if ((error
= t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, GIT_SERVICE_UPLOADPACK
)) < 0)
277 /* If this is a stateful implementation, the stream we get back should be the same */
278 GIT_ASSERT(t
->rpc
|| t
->current_stream
== stream
);
280 /* Save off the current stream (i.e. socket) that we are working with */
281 t
->current_stream
= stream
;
283 if ((error
= stream
->write(stream
, (const char *)data
, len
)) < 0)
286 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
291 int git_smart__get_push_stream(transport_smart
*t
, git_smart_subtransport_stream
**stream
)
295 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
298 if (GIT_DIRECTION_PUSH
!= t
->direction
) {
299 git_error_set(GIT_ERROR_NET
, "this operation is only valid for push");
303 if ((error
= t
->wrapped
->action(stream
, t
->wrapped
, t
->url
, GIT_SERVICE_RECEIVEPACK
)) < 0)
306 /* If this is a stateful implementation, the stream we get back should be the same */
307 GIT_ASSERT(t
->rpc
|| t
->current_stream
== *stream
);
309 /* Save off the current stream (i.e. socket) that we are working with */
310 t
->current_stream
= *stream
;
312 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
317 static void git_smart__cancel(git_transport
*transport
)
319 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
321 git_atomic32_set(&t
->cancelled
, 1);
324 static int git_smart__is_connected(git_transport
*transport
)
326 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
331 static int git_smart__close(git_transport
*transport
)
333 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
334 git_vector
*common
= &t
->common
;
338 git_smart_subtransport_stream
*stream
;
339 const char flush
[] = "0000";
342 * If we're still connected at this point and not using RPC,
343 * we should say goodbye by sending a flush, or git-daemon
344 * will complain that we disconnected unexpectedly.
346 if (t
->connected
&& !t
->rpc
&&
347 !t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, GIT_SERVICE_UPLOADPACK
)) {
348 t
->current_stream
->write(t
->current_stream
, flush
, 4);
351 ret
= git_smart__reset_stream(t
, true);
353 git_vector_foreach(common
, i
, p
)
356 git_vector_free(common
);
368 static void git_smart__free(git_transport
*transport
)
370 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
371 git_vector
*refs
= &t
->refs
;
375 /* Make sure that the current stream is closed, if we have one. */
376 git_smart__close(transport
);
378 /* Free the subtransport */
379 t
->wrapped
->free(t
->wrapped
);
381 git_vector_free(&t
->heads
);
382 git_vector_foreach(refs
, i
, p
)
385 git_vector_free(refs
);
387 git_remote_connect_options_dispose(&t
->connect_opts
);
392 static int ref_name_cmp(const void *a
, const void *b
)
394 const git_pkt_ref
*ref_a
= a
, *ref_b
= b
;
396 return strcmp(ref_a
->head
.name
, ref_b
->head
.name
);
399 int git_transport_smart_certificate_check(git_transport
*transport
, git_cert
*cert
, int valid
, const char *hostname
)
401 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
402 git_remote_connect_options
*connect_opts
= &t
->connect_opts
;
404 GIT_ASSERT_ARG(transport
);
405 GIT_ASSERT_ARG(cert
);
406 GIT_ASSERT_ARG(hostname
);
408 if (!connect_opts
->callbacks
.certificate_check
)
409 return GIT_PASSTHROUGH
;
411 return connect_opts
->callbacks
.certificate_check(cert
, valid
, hostname
, connect_opts
->callbacks
.payload
);
414 int git_transport_smart_credentials(git_credential
**out
, git_transport
*transport
, const char *user
, int methods
)
416 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
417 git_remote_connect_options
*connect_opts
= &t
->connect_opts
;
420 GIT_ASSERT_ARG(transport
);
422 if (!connect_opts
->callbacks
.credentials
)
423 return GIT_PASSTHROUGH
;
425 return connect_opts
->callbacks
.credentials(out
, t
->url
, user
, methods
, connect_opts
->callbacks
.payload
);
428 int git_transport_remote_connect_options(
429 git_remote_connect_options
*out
,
430 git_transport
*transport
)
432 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
435 GIT_ASSERT_ARG(transport
);
437 return git_remote_connect_options_dup(out
, &t
->connect_opts
);
440 int git_transport_smart(git_transport
**out
, git_remote
*owner
, void *param
)
443 git_smart_subtransport_definition
*definition
= (git_smart_subtransport_definition
*)param
;
448 t
= git__calloc(1, sizeof(transport_smart
));
449 GIT_ERROR_CHECK_ALLOC(t
);
451 t
->parent
.version
= GIT_TRANSPORT_VERSION
;
452 t
->parent
.connect
= git_smart__connect
;
453 t
->parent
.set_connect_opts
= git_smart__set_connect_opts
;
454 t
->parent
.capabilities
= git_smart__capabilities
;
455 t
->parent
.close
= git_smart__close
;
456 t
->parent
.free
= git_smart__free
;
457 t
->parent
.negotiate_fetch
= git_smart__negotiate_fetch
;
458 t
->parent
.download_pack
= git_smart__download_pack
;
459 t
->parent
.push
= git_smart__push
;
460 t
->parent
.ls
= git_smart__ls
;
461 t
->parent
.is_connected
= git_smart__is_connected
;
462 t
->parent
.cancel
= git_smart__cancel
;
465 t
->rpc
= definition
->rpc
;
467 if (git_vector_init(&t
->refs
, 16, ref_name_cmp
) < 0) {
472 if (git_vector_init(&t
->heads
, 16, ref_name_cmp
) < 0) {
477 if (definition
->callback(&t
->wrapped
, &t
->parent
, definition
->param
) < 0) {
482 *out
= (git_transport
*) t
;