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.
12 static int git_smart__recv_cb(gitno_buffer
*buf
)
14 transport_smart
*t
= (transport_smart
*) buf
->cb_data
;
15 size_t old_len
, bytes_read
;
18 assert(t
->current_stream
);
20 old_len
= buf
->offset
;
22 if ((error
= t
->current_stream
->read(t
->current_stream
, buf
->data
+ buf
->offset
, buf
->len
- buf
->offset
, &bytes_read
)) < 0)
25 buf
->offset
+= bytes_read
;
27 if (t
->packetsize_cb
&& !t
->cancelled
.val
) {
28 error
= t
->packetsize_cb(bytes_read
, t
->packetsize_payload
);
30 git_atomic_set(&t
->cancelled
, 1);
35 return (int)(buf
->offset
- old_len
);
38 GIT_INLINE(int) git_smart__reset_stream(transport_smart
*t
, bool close_subtransport
)
40 if (t
->current_stream
) {
41 t
->current_stream
->free(t
->current_stream
);
42 t
->current_stream
= NULL
;
45 if (close_subtransport
&&
46 t
->wrapped
->close(t
->wrapped
) < 0)
52 static int git_smart__set_callbacks(
53 git_transport
*transport
,
54 git_transport_message_cb progress_cb
,
55 git_transport_message_cb error_cb
,
56 git_transport_certificate_check_cb certificate_check_cb
,
57 void *message_cb_payload
)
59 transport_smart
*t
= (transport_smart
*)transport
;
61 t
->progress_cb
= progress_cb
;
62 t
->error_cb
= error_cb
;
63 t
->certificate_check_cb
= certificate_check_cb
;
64 t
->message_cb_payload
= message_cb_payload
;
69 int git_smart__update_heads(transport_smart
*t
, git_vector
*symrefs
)
74 git_vector_clear(&t
->heads
);
75 git_vector_foreach(&t
->refs
, i
, pkt
) {
76 git_pkt_ref
*ref
= (git_pkt_ref
*) pkt
;
77 if (pkt
->type
!= GIT_PKT_REF
)
82 git_buf buf
= GIT_BUF_INIT
;
86 git_vector_foreach(symrefs
, j
, spec
) {
88 if (git_refspec_src_matches(spec
, ref
->head
.name
) &&
89 !(error
= git_refspec_transform(&buf
, spec
, ref
->head
.name
)))
90 ref
->head
.symref_target
= git_buf_detach(&buf
);
99 if (git_vector_insert(&t
->heads
, &ref
->head
) < 0)
106 static void free_symrefs(git_vector
*symrefs
)
111 git_vector_foreach(symrefs
, i
, spec
) {
112 git_refspec__free(spec
);
116 git_vector_free(symrefs
);
119 static int git_smart__connect(
120 git_transport
*transport
,
122 git_cred_acquire_cb cred_acquire_cb
,
123 void *cred_acquire_payload
,
127 transport_smart
*t
= (transport_smart
*)transport
;
128 git_smart_subtransport_stream
*stream
;
133 git_smart_service_t service
;
135 if (git_smart__reset_stream(t
, true) < 0)
138 t
->url
= git__strdup(url
);
139 GITERR_CHECK_ALLOC(t
->url
);
141 t
->direction
= direction
;
143 t
->cred_acquire_cb
= cred_acquire_cb
;
144 t
->cred_acquire_payload
= cred_acquire_payload
;
146 if (GIT_DIRECTION_FETCH
== t
->direction
)
147 service
= GIT_SERVICE_UPLOADPACK_LS
;
148 else if (GIT_DIRECTION_PUSH
== t
->direction
)
149 service
= GIT_SERVICE_RECEIVEPACK_LS
;
151 giterr_set(GITERR_NET
, "Invalid direction");
155 if ((error
= t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, service
)) < 0)
158 /* Save off the current stream (i.e. socket) that we are working with */
159 t
->current_stream
= stream
;
161 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
163 /* 2 flushes for RPC; 1 for stateful */
164 if ((error
= git_smart__store_refs(t
, t
->rpc
? 2 : 1)) < 0)
167 /* Strip the comment packet for RPC */
169 pkt
= (git_pkt
*)git_vector_get(&t
->refs
, 0);
171 if (!pkt
|| GIT_PKT_COMMENT
!= pkt
->type
) {
172 giterr_set(GITERR_NET
, "Invalid response");
175 /* Remove the comment pkt from the list */
176 git_vector_remove(&t
->refs
, 0);
181 /* We now have loaded the refs. */
184 first
= (git_pkt_ref
*)git_vector_get(&t
->refs
, 0);
186 if ((error
= git_vector_init(&symrefs
, 1, NULL
)) < 0)
189 /* Detect capabilities */
190 if (git_smart__detect_caps(first
, &t
->caps
, &symrefs
) < 0)
193 /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
194 if (1 == t
->refs
.length
&& !strcmp(first
->head
.name
, "capabilities^{}") &&
195 git_oid_iszero(&first
->head
.oid
)) {
196 git_vector_clear(&t
->refs
);
197 git_pkt_free((git_pkt
*)first
);
200 /* Keep a list of heads for _ls */
201 git_smart__update_heads(t
, &symrefs
);
203 free_symrefs(&symrefs
);
205 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
208 /* We're now logically connected. */
214 static int git_smart__ls(const git_remote_head
***out
, size_t *size
, git_transport
*transport
)
216 transport_smart
*t
= (transport_smart
*)transport
;
219 giterr_set(GITERR_NET
, "The transport has not yet loaded the refs");
223 *out
= (const git_remote_head
**) t
->heads
.contents
;
224 *size
= t
->heads
.length
;
229 int git_smart__negotiation_step(git_transport
*transport
, void *data
, size_t len
)
231 transport_smart
*t
= (transport_smart
*)transport
;
232 git_smart_subtransport_stream
*stream
;
235 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
238 if (GIT_DIRECTION_FETCH
!= t
->direction
) {
239 giterr_set(GITERR_NET
, "This operation is only valid for fetch");
243 if ((error
= t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, GIT_SERVICE_UPLOADPACK
)) < 0)
246 /* If this is a stateful implementation, the stream we get back should be the same */
247 assert(t
->rpc
|| t
->current_stream
== stream
);
249 /* Save off the current stream (i.e. socket) that we are working with */
250 t
->current_stream
= stream
;
252 if ((error
= stream
->write(stream
, (const char *)data
, len
)) < 0)
255 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
260 int git_smart__get_push_stream(transport_smart
*t
, git_smart_subtransport_stream
**stream
)
264 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
267 if (GIT_DIRECTION_PUSH
!= t
->direction
) {
268 giterr_set(GITERR_NET
, "This operation is only valid for push");
272 if ((error
= t
->wrapped
->action(stream
, t
->wrapped
, t
->url
, GIT_SERVICE_RECEIVEPACK
)) < 0)
275 /* If this is a stateful implementation, the stream we get back should be the same */
276 assert(t
->rpc
|| t
->current_stream
== *stream
);
278 /* Save off the current stream (i.e. socket) that we are working with */
279 t
->current_stream
= *stream
;
281 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
286 static void git_smart__cancel(git_transport
*transport
)
288 transport_smart
*t
= (transport_smart
*)transport
;
290 git_atomic_set(&t
->cancelled
, 1);
293 static int git_smart__is_connected(git_transport
*transport
)
295 transport_smart
*t
= (transport_smart
*)transport
;
300 static int git_smart__read_flags(git_transport
*transport
, int *flags
)
302 transport_smart
*t
= (transport_smart
*)transport
;
309 static int git_smart__close(git_transport
*transport
)
311 transport_smart
*t
= (transport_smart
*)transport
;
312 git_vector
*common
= &t
->common
;
316 git_smart_subtransport_stream
*stream
;
317 const char flush
[] = "0000";
320 * If we're still connected at this point and not using RPC,
321 * we should say goodbye by sending a flush, or git-daemon
322 * will complain that we disconnected unexpectedly.
324 if (t
->connected
&& !t
->rpc
&&
325 !t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, GIT_SERVICE_UPLOADPACK
)) {
326 t
->current_stream
->write(t
->current_stream
, flush
, 4);
329 ret
= git_smart__reset_stream(t
, true);
331 git_vector_foreach(common
, i
, p
)
334 git_vector_free(common
);
346 static void git_smart__free(git_transport
*transport
)
348 transport_smart
*t
= (transport_smart
*)transport
;
349 git_vector
*refs
= &t
->refs
;
353 /* Make sure that the current stream is closed, if we have one. */
354 git_smart__close(transport
);
356 /* Free the subtransport */
357 t
->wrapped
->free(t
->wrapped
);
359 git_vector_free(&t
->heads
);
360 git_vector_foreach(refs
, i
, p
)
363 git_vector_free(refs
);
368 static int ref_name_cmp(const void *a
, const void *b
)
370 const git_pkt_ref
*ref_a
= a
, *ref_b
= b
;
372 return strcmp(ref_a
->head
.name
, ref_b
->head
.name
);
375 int git_transport_smart_certificate_check(git_transport
*transport
, git_cert
*cert
, int valid
, const char *hostname
)
377 transport_smart
*t
= (transport_smart
*)transport
;
379 return t
->certificate_check_cb(cert
, valid
, hostname
, t
->message_cb_payload
);
382 int git_transport_smart_credentials(git_cred
**out
, git_transport
*transport
, const char *user
, int methods
)
384 transport_smart
*t
= (transport_smart
*)transport
;
386 return t
->cred_acquire_cb(out
, t
->url
, user
, methods
, t
->cred_acquire_payload
);
389 int git_transport_smart(git_transport
**out
, git_remote
*owner
, void *param
)
392 git_smart_subtransport_definition
*definition
= (git_smart_subtransport_definition
*)param
;
397 t
= git__calloc(1, sizeof(transport_smart
));
398 GITERR_CHECK_ALLOC(t
);
400 t
->parent
.version
= GIT_TRANSPORT_VERSION
;
401 t
->parent
.set_callbacks
= git_smart__set_callbacks
;
402 t
->parent
.connect
= git_smart__connect
;
403 t
->parent
.close
= git_smart__close
;
404 t
->parent
.free
= git_smart__free
;
405 t
->parent
.negotiate_fetch
= git_smart__negotiate_fetch
;
406 t
->parent
.download_pack
= git_smart__download_pack
;
407 t
->parent
.push
= git_smart__push
;
408 t
->parent
.ls
= git_smart__ls
;
409 t
->parent
.is_connected
= git_smart__is_connected
;
410 t
->parent
.read_flags
= git_smart__read_flags
;
411 t
->parent
.cancel
= git_smart__cancel
;
414 t
->rpc
= definition
->rpc
;
416 if (git_vector_init(&t
->refs
, 16, ref_name_cmp
) < 0) {
421 if (git_vector_init(&t
->heads
, 16, ref_name_cmp
) < 0) {
426 if (definition
->callback(&t
->wrapped
, &t
->parent
, definition
->param
) < 0) {
431 *out
= (git_transport
*) t
;