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.
15 static int git_smart__recv_cb(gitno_buffer
*buf
)
17 transport_smart
*t
= (transport_smart
*) buf
->cb_data
;
18 size_t old_len
, bytes_read
;
21 assert(t
->current_stream
);
23 old_len
= buf
->offset
;
25 if ((error
= t
->current_stream
->read(t
->current_stream
, buf
->data
+ buf
->offset
, buf
->len
- buf
->offset
, &bytes_read
)) < 0)
28 buf
->offset
+= bytes_read
;
30 if (t
->packetsize_cb
&& !t
->cancelled
.val
) {
31 error
= t
->packetsize_cb(bytes_read
, t
->packetsize_payload
);
33 git_atomic_set(&t
->cancelled
, 1);
38 return (int)(buf
->offset
- old_len
);
41 GIT_INLINE(int) git_smart__reset_stream(transport_smart
*t
, bool close_subtransport
)
43 if (t
->current_stream
) {
44 t
->current_stream
->free(t
->current_stream
);
45 t
->current_stream
= NULL
;
48 if (close_subtransport
) {
52 if (t
->wrapped
->close(t
->wrapped
) < 0)
59 static int git_smart__set_callbacks(
60 git_transport
*transport
,
61 git_transport_message_cb progress_cb
,
62 git_transport_message_cb error_cb
,
63 git_transport_certificate_check_cb certificate_check_cb
,
64 void *message_cb_payload
)
66 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
68 t
->progress_cb
= progress_cb
;
69 t
->error_cb
= error_cb
;
70 t
->certificate_check_cb
= certificate_check_cb
;
71 t
->message_cb_payload
= message_cb_payload
;
76 static size_t http_header_name_length(const char *http_header
)
78 const char *colon
= strchr(http_header
, ':');
81 return colon
- http_header
;
84 static bool is_malformed_http_header(const char *http_header
)
89 /* Disallow \r and \n */
90 c
= strchr(http_header
, '\r');
93 c
= strchr(http_header
, '\n');
97 /* Require a header name followed by : */
98 name_len
= http_header_name_length(http_header
);
105 static char *forbidden_custom_headers
[] = {
114 static bool is_forbidden_custom_header(const char *custom_header
)
117 size_t name_len
= http_header_name_length(custom_header
);
119 /* Disallow headers that we set */
120 for (i
= 0; i
< ARRAY_SIZE(forbidden_custom_headers
); i
++)
121 if (strncmp(forbidden_custom_headers
[i
], custom_header
, name_len
) == 0)
127 static int git_smart__set_custom_headers(
128 git_transport
*transport
,
129 const git_strarray
*custom_headers
)
131 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
134 if (t
->custom_headers
.count
)
135 git_strarray_dispose(&t
->custom_headers
);
140 for (i
= 0; i
< custom_headers
->count
; i
++) {
141 if (is_malformed_http_header(custom_headers
->strings
[i
])) {
142 git_error_set(GIT_ERROR_INVALID
, "custom HTTP header '%s' is malformed", custom_headers
->strings
[i
]);
145 if (is_forbidden_custom_header(custom_headers
->strings
[i
])) {
146 git_error_set(GIT_ERROR_INVALID
, "custom HTTP header '%s' is already set by libgit2", custom_headers
->strings
[i
]);
151 return git_strarray_copy(&t
->custom_headers
, custom_headers
);
154 int git_smart__update_heads(transport_smart
*t
, git_vector
*symrefs
)
159 git_vector_clear(&t
->heads
);
160 git_vector_foreach(&t
->refs
, i
, pkt
) {
161 git_pkt_ref
*ref
= (git_pkt_ref
*) pkt
;
162 if (pkt
->type
!= GIT_PKT_REF
)
167 git_buf buf
= GIT_BUF_INIT
;
171 git_vector_foreach(symrefs
, j
, spec
) {
173 if (git_refspec_src_matches(spec
, ref
->head
.name
) &&
174 !(error
= git_refspec_transform(&buf
, spec
, ref
->head
.name
))) {
175 git__free(ref
->head
.symref_target
);
176 ref
->head
.symref_target
= git_buf_detach(&buf
);
180 git_buf_dispose(&buf
);
186 if (git_vector_insert(&t
->heads
, &ref
->head
) < 0)
193 static void free_symrefs(git_vector
*symrefs
)
198 git_vector_foreach(symrefs
, i
, spec
) {
199 git_refspec__dispose(spec
);
203 git_vector_free(symrefs
);
206 static int git_smart__connect(
207 git_transport
*transport
,
209 git_credential_acquire_cb cred_acquire_cb
,
210 void *cred_acquire_payload
,
211 const git_proxy_options
*proxy
,
215 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
216 git_smart_subtransport_stream
*stream
;
221 git_smart_service_t service
;
223 if (git_smart__reset_stream(t
, true) < 0)
226 t
->url
= git__strdup(url
);
227 GIT_ERROR_CHECK_ALLOC(t
->url
);
229 if (git_proxy_options_dup(&t
->proxy
, proxy
) < 0)
232 t
->direction
= direction
;
234 t
->cred_acquire_cb
= cred_acquire_cb
;
235 t
->cred_acquire_payload
= cred_acquire_payload
;
237 if (GIT_DIRECTION_FETCH
== t
->direction
)
238 service
= GIT_SERVICE_UPLOADPACK_LS
;
239 else if (GIT_DIRECTION_PUSH
== t
->direction
)
240 service
= GIT_SERVICE_RECEIVEPACK_LS
;
242 git_error_set(GIT_ERROR_NET
, "invalid direction");
246 if ((error
= t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, service
)) < 0)
249 /* Save off the current stream (i.e. socket) that we are working with */
250 t
->current_stream
= stream
;
252 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
254 /* 2 flushes for RPC; 1 for stateful */
255 if ((error
= git_smart__store_refs(t
, t
->rpc
? 2 : 1)) < 0)
258 /* Strip the comment packet for RPC */
260 pkt
= (git_pkt
*)git_vector_get(&t
->refs
, 0);
262 if (!pkt
|| GIT_PKT_COMMENT
!= pkt
->type
) {
263 git_error_set(GIT_ERROR_NET
, "invalid response");
266 /* Remove the comment pkt from the list */
267 git_vector_remove(&t
->refs
, 0);
272 /* We now have loaded the refs. */
275 pkt
= (git_pkt
*)git_vector_get(&t
->refs
, 0);
276 if (pkt
&& GIT_PKT_REF
!= pkt
->type
) {
277 git_error_set(GIT_ERROR_NET
, "invalid response");
280 first
= (git_pkt_ref
*)pkt
;
282 if ((error
= git_vector_init(&symrefs
, 1, NULL
)) < 0)
285 /* Detect capabilities */
286 if ((error
= git_smart__detect_caps(first
, &t
->caps
, &symrefs
)) == 0) {
287 /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
288 if (1 == t
->refs
.length
&& !strcmp(first
->head
.name
, "capabilities^{}") &&
289 git_oid_is_zero(&first
->head
.oid
)) {
290 git_vector_clear(&t
->refs
);
291 git_pkt_free((git_pkt
*)first
);
294 /* Keep a list of heads for _ls */
295 git_smart__update_heads(t
, &symrefs
);
296 } else if (error
== GIT_ENOTFOUND
) {
297 /* There was no ref packet received, or the cap list was empty */
300 git_error_set(GIT_ERROR_NET
, "invalid response");
304 if (t
->rpc
&& (error
= git_smart__reset_stream(t
, false)) < 0)
307 /* We're now logically connected. */
311 free_symrefs(&symrefs
);
316 static int git_smart__ls(const git_remote_head
***out
, size_t *size
, git_transport
*transport
)
318 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
321 git_error_set(GIT_ERROR_NET
, "the transport has not yet loaded the refs");
325 *out
= (const git_remote_head
**) t
->heads
.contents
;
326 *size
= t
->heads
.length
;
331 int git_smart__negotiation_step(git_transport
*transport
, void *data
, size_t len
)
333 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
334 git_smart_subtransport_stream
*stream
;
337 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
340 if (GIT_DIRECTION_FETCH
!= t
->direction
) {
341 git_error_set(GIT_ERROR_NET
, "this operation is only valid for fetch");
345 if ((error
= t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, GIT_SERVICE_UPLOADPACK
)) < 0)
348 /* If this is a stateful implementation, the stream we get back should be the same */
349 assert(t
->rpc
|| t
->current_stream
== stream
);
351 /* Save off the current stream (i.e. socket) that we are working with */
352 t
->current_stream
= stream
;
354 if ((error
= stream
->write(stream
, (const char *)data
, len
)) < 0)
357 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
362 int git_smart__get_push_stream(transport_smart
*t
, git_smart_subtransport_stream
**stream
)
366 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
369 if (GIT_DIRECTION_PUSH
!= t
->direction
) {
370 git_error_set(GIT_ERROR_NET
, "this operation is only valid for push");
374 if ((error
= t
->wrapped
->action(stream
, t
->wrapped
, t
->url
, GIT_SERVICE_RECEIVEPACK
)) < 0)
377 /* If this is a stateful implementation, the stream we get back should be the same */
378 assert(t
->rpc
|| t
->current_stream
== *stream
);
380 /* Save off the current stream (i.e. socket) that we are working with */
381 t
->current_stream
= *stream
;
383 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
388 static void git_smart__cancel(git_transport
*transport
)
390 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
392 git_atomic_set(&t
->cancelled
, 1);
395 static int git_smart__is_connected(git_transport
*transport
)
397 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
402 static int git_smart__read_flags(git_transport
*transport
, int *flags
)
404 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
411 static int git_smart__close(git_transport
*transport
)
413 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
414 git_vector
*common
= &t
->common
;
418 git_smart_subtransport_stream
*stream
;
419 const char flush
[] = "0000";
422 * If we're still connected at this point and not using RPC,
423 * we should say goodbye by sending a flush, or git-daemon
424 * will complain that we disconnected unexpectedly.
426 if (t
->connected
&& !t
->rpc
&&
427 !t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, GIT_SERVICE_UPLOADPACK
)) {
428 t
->current_stream
->write(t
->current_stream
, flush
, 4);
431 ret
= git_smart__reset_stream(t
, true);
433 git_vector_foreach(common
, i
, p
)
436 git_vector_free(common
);
448 static void git_smart__free(git_transport
*transport
)
450 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
451 git_vector
*refs
= &t
->refs
;
455 /* Make sure that the current stream is closed, if we have one. */
456 git_smart__close(transport
);
458 /* Free the subtransport */
459 t
->wrapped
->free(t
->wrapped
);
461 git_vector_free(&t
->heads
);
462 git_vector_foreach(refs
, i
, p
)
465 git_vector_free(refs
);
466 git__free((char *)t
->proxy
.url
);
468 git_strarray_dispose(&t
->custom_headers
);
473 static int ref_name_cmp(const void *a
, const void *b
)
475 const git_pkt_ref
*ref_a
= a
, *ref_b
= b
;
477 return strcmp(ref_a
->head
.name
, ref_b
->head
.name
);
480 int git_transport_smart_certificate_check(git_transport
*transport
, git_cert
*cert
, int valid
, const char *hostname
)
482 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
484 assert(transport
&& cert
&& hostname
);
486 if (!t
->certificate_check_cb
)
487 return GIT_PASSTHROUGH
;
489 return t
->certificate_check_cb(cert
, valid
, hostname
, t
->message_cb_payload
);
492 int git_transport_smart_credentials(git_credential
**out
, git_transport
*transport
, const char *user
, int methods
)
494 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
496 assert(out
&& transport
);
498 if (!t
->cred_acquire_cb
)
499 return GIT_PASSTHROUGH
;
501 return t
->cred_acquire_cb(out
, t
->url
, user
, methods
, t
->cred_acquire_payload
);
504 int git_transport_smart_proxy_options(git_proxy_options
*out
, git_transport
*transport
)
506 transport_smart
*t
= GIT_CONTAINER_OF(transport
, transport_smart
, parent
);
507 return git_proxy_options_dup(out
, &t
->proxy
);
510 int git_transport_smart(git_transport
**out
, git_remote
*owner
, void *param
)
513 git_smart_subtransport_definition
*definition
= (git_smart_subtransport_definition
*)param
;
518 t
= git__calloc(1, sizeof(transport_smart
));
519 GIT_ERROR_CHECK_ALLOC(t
);
521 t
->parent
.version
= GIT_TRANSPORT_VERSION
;
522 t
->parent
.set_callbacks
= git_smart__set_callbacks
;
523 t
->parent
.set_custom_headers
= git_smart__set_custom_headers
;
524 t
->parent
.connect
= git_smart__connect
;
525 t
->parent
.close
= git_smart__close
;
526 t
->parent
.free
= git_smart__free
;
527 t
->parent
.negotiate_fetch
= git_smart__negotiate_fetch
;
528 t
->parent
.download_pack
= git_smart__download_pack
;
529 t
->parent
.push
= git_smart__push
;
530 t
->parent
.ls
= git_smart__ls
;
531 t
->parent
.is_connected
= git_smart__is_connected
;
532 t
->parent
.read_flags
= git_smart__read_flags
;
533 t
->parent
.cancel
= git_smart__cancel
;
536 t
->rpc
= definition
->rpc
;
538 if (git_vector_init(&t
->refs
, 16, ref_name_cmp
) < 0) {
543 if (git_vector_init(&t
->heads
, 16, ref_name_cmp
) < 0) {
548 if (definition
->callback(&t
->wrapped
, &t
->parent
, definition
->param
) < 0) {
553 *out
= (git_transport
*) t
;