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.
13 static int git_smart__recv_cb(gitno_buffer
*buf
)
15 transport_smart
*t
= (transport_smart
*) buf
->cb_data
;
16 size_t old_len
, bytes_read
;
19 assert(t
->current_stream
);
21 old_len
= buf
->offset
;
23 if ((error
= t
->current_stream
->read(t
->current_stream
, buf
->data
+ buf
->offset
, buf
->len
- buf
->offset
, &bytes_read
)) < 0)
26 buf
->offset
+= bytes_read
;
28 if (t
->packetsize_cb
&& !t
->cancelled
.val
) {
29 error
= t
->packetsize_cb(bytes_read
, t
->packetsize_payload
);
31 git_atomic_set(&t
->cancelled
, 1);
36 return (int)(buf
->offset
- old_len
);
39 GIT_INLINE(int) git_smart__reset_stream(transport_smart
*t
, bool close_subtransport
)
41 if (t
->current_stream
) {
42 t
->current_stream
->free(t
->current_stream
);
43 t
->current_stream
= NULL
;
46 if (close_subtransport
&&
47 t
->wrapped
->close(t
->wrapped
) < 0)
53 static int git_smart__set_callbacks(
54 git_transport
*transport
,
55 git_transport_message_cb progress_cb
,
56 git_transport_message_cb error_cb
,
57 git_transport_certificate_check_cb certificate_check_cb
,
58 void *message_cb_payload
)
60 transport_smart
*t
= (transport_smart
*)transport
;
62 t
->progress_cb
= progress_cb
;
63 t
->error_cb
= error_cb
;
64 t
->certificate_check_cb
= certificate_check_cb
;
65 t
->message_cb_payload
= message_cb_payload
;
70 static int http_header_name_length(const char *http_header
)
72 const char *colon
= strchr(http_header
, ':');
75 return colon
- http_header
;
78 static bool is_malformed_http_header(const char *http_header
)
84 c
= strchr(http_header
, '\r');
87 c
= strchr(http_header
, '\n');
91 // Require a header name followed by :
92 name_len
= http_header_name_length(http_header
);
99 static char *forbidden_custom_headers
[] = {
108 static bool is_forbidden_custom_header(const char *custom_header
)
111 int name_len
= http_header_name_length(custom_header
);
113 // Disallow headers that we set
114 for (i
= 0; i
< ARRAY_SIZE(forbidden_custom_headers
); i
++)
115 if (strncmp(forbidden_custom_headers
[i
], custom_header
, name_len
) == 0)
121 static int git_smart__set_custom_headers(
122 git_transport
*transport
,
123 const git_strarray
*custom_headers
)
125 transport_smart
*t
= (transport_smart
*)transport
;
128 if (t
->custom_headers
.count
)
129 git_strarray_free(&t
->custom_headers
);
134 for (i
= 0; i
< custom_headers
->count
; i
++) {
135 if (is_malformed_http_header(custom_headers
->strings
[i
])) {
136 giterr_set(GITERR_INVALID
, "custom HTTP header '%s' is malformed", custom_headers
->strings
[i
]);
139 if (is_forbidden_custom_header(custom_headers
->strings
[i
])) {
140 giterr_set(GITERR_INVALID
, "custom HTTP header '%s' is already set by libgit2", custom_headers
->strings
[i
]);
145 return git_strarray_copy(&t
->custom_headers
, custom_headers
);
148 int git_smart__update_heads(transport_smart
*t
, git_vector
*symrefs
)
153 git_vector_clear(&t
->heads
);
154 git_vector_foreach(&t
->refs
, i
, pkt
) {
155 git_pkt_ref
*ref
= (git_pkt_ref
*) pkt
;
156 if (pkt
->type
!= GIT_PKT_REF
)
161 git_buf buf
= GIT_BUF_INIT
;
165 git_vector_foreach(symrefs
, j
, spec
) {
167 if (git_refspec_src_matches(spec
, ref
->head
.name
) &&
168 !(error
= git_refspec_transform(&buf
, spec
, ref
->head
.name
)))
169 ref
->head
.symref_target
= git_buf_detach(&buf
);
178 if (git_vector_insert(&t
->heads
, &ref
->head
) < 0)
185 static void free_symrefs(git_vector
*symrefs
)
190 git_vector_foreach(symrefs
, i
, spec
) {
191 git_refspec__free(spec
);
195 git_vector_free(symrefs
);
198 static int git_smart__connect(
199 git_transport
*transport
,
201 git_cred_acquire_cb cred_acquire_cb
,
202 void *cred_acquire_payload
,
203 const git_proxy_options
*proxy
,
207 transport_smart
*t
= (transport_smart
*)transport
;
208 git_smart_subtransport_stream
*stream
;
213 git_smart_service_t service
;
215 if (git_smart__reset_stream(t
, true) < 0)
218 t
->url
= git__strdup(url
);
219 GITERR_CHECK_ALLOC(t
->url
);
221 if (git_proxy_options_dup(&t
->proxy
, proxy
) < 0)
224 t
->direction
= direction
;
226 t
->cred_acquire_cb
= cred_acquire_cb
;
227 t
->cred_acquire_payload
= cred_acquire_payload
;
229 if (GIT_DIRECTION_FETCH
== t
->direction
)
230 service
= GIT_SERVICE_UPLOADPACK_LS
;
231 else if (GIT_DIRECTION_PUSH
== t
->direction
)
232 service
= GIT_SERVICE_RECEIVEPACK_LS
;
234 giterr_set(GITERR_NET
, "invalid direction");
238 if ((error
= t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, service
)) < 0)
241 /* Save off the current stream (i.e. socket) that we are working with */
242 t
->current_stream
= stream
;
244 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
246 /* 2 flushes for RPC; 1 for stateful */
247 if ((error
= git_smart__store_refs(t
, t
->rpc
? 2 : 1)) < 0)
250 /* Strip the comment packet for RPC */
252 pkt
= (git_pkt
*)git_vector_get(&t
->refs
, 0);
254 if (!pkt
|| GIT_PKT_COMMENT
!= pkt
->type
) {
255 giterr_set(GITERR_NET
, "invalid response");
258 /* Remove the comment pkt from the list */
259 git_vector_remove(&t
->refs
, 0);
264 /* We now have loaded the refs. */
267 first
= (git_pkt_ref
*)git_vector_get(&t
->refs
, 0);
269 if ((error
= git_vector_init(&symrefs
, 1, NULL
)) < 0)
272 /* Detect capabilities */
273 if (git_smart__detect_caps(first
, &t
->caps
, &symrefs
) < 0)
276 /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
277 if (1 == t
->refs
.length
&& !strcmp(first
->head
.name
, "capabilities^{}") &&
278 git_oid_iszero(&first
->head
.oid
)) {
279 git_vector_clear(&t
->refs
);
280 git_pkt_free((git_pkt
*)first
);
283 /* Keep a list of heads for _ls */
284 git_smart__update_heads(t
, &symrefs
);
286 free_symrefs(&symrefs
);
288 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
291 /* We're now logically connected. */
297 static int git_smart__ls(const git_remote_head
***out
, size_t *size
, git_transport
*transport
)
299 transport_smart
*t
= (transport_smart
*)transport
;
302 giterr_set(GITERR_NET
, "the transport has not yet loaded the refs");
306 *out
= (const git_remote_head
**) t
->heads
.contents
;
307 *size
= t
->heads
.length
;
312 int git_smart__negotiation_step(git_transport
*transport
, void *data
, size_t len
)
314 transport_smart
*t
= (transport_smart
*)transport
;
315 git_smart_subtransport_stream
*stream
;
318 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
321 if (GIT_DIRECTION_FETCH
!= t
->direction
) {
322 giterr_set(GITERR_NET
, "this operation is only valid for fetch");
326 if ((error
= t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, GIT_SERVICE_UPLOADPACK
)) < 0)
329 /* If this is a stateful implementation, the stream we get back should be the same */
330 assert(t
->rpc
|| t
->current_stream
== stream
);
332 /* Save off the current stream (i.e. socket) that we are working with */
333 t
->current_stream
= stream
;
335 if ((error
= stream
->write(stream
, (const char *)data
, len
)) < 0)
338 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
343 int git_smart__get_push_stream(transport_smart
*t
, git_smart_subtransport_stream
**stream
)
347 if (t
->rpc
&& git_smart__reset_stream(t
, false) < 0)
350 if (GIT_DIRECTION_PUSH
!= t
->direction
) {
351 giterr_set(GITERR_NET
, "this operation is only valid for push");
355 if ((error
= t
->wrapped
->action(stream
, t
->wrapped
, t
->url
, GIT_SERVICE_RECEIVEPACK
)) < 0)
358 /* If this is a stateful implementation, the stream we get back should be the same */
359 assert(t
->rpc
|| t
->current_stream
== *stream
);
361 /* Save off the current stream (i.e. socket) that we are working with */
362 t
->current_stream
= *stream
;
364 gitno_buffer_setup_callback(&t
->buffer
, t
->buffer_data
, sizeof(t
->buffer_data
), git_smart__recv_cb
, t
);
369 static void git_smart__cancel(git_transport
*transport
)
371 transport_smart
*t
= (transport_smart
*)transport
;
373 git_atomic_set(&t
->cancelled
, 1);
376 static int git_smart__is_connected(git_transport
*transport
)
378 transport_smart
*t
= (transport_smart
*)transport
;
383 static int git_smart__read_flags(git_transport
*transport
, int *flags
)
385 transport_smart
*t
= (transport_smart
*)transport
;
392 static int git_smart__close(git_transport
*transport
)
394 transport_smart
*t
= (transport_smart
*)transport
;
395 git_vector
*common
= &t
->common
;
399 git_smart_subtransport_stream
*stream
;
400 const char flush
[] = "0000";
403 * If we're still connected at this point and not using RPC,
404 * we should say goodbye by sending a flush, or git-daemon
405 * will complain that we disconnected unexpectedly.
407 if (t
->connected
&& !t
->rpc
&&
408 !t
->wrapped
->action(&stream
, t
->wrapped
, t
->url
, GIT_SERVICE_UPLOADPACK
)) {
409 t
->current_stream
->write(t
->current_stream
, flush
, 4);
412 ret
= git_smart__reset_stream(t
, true);
414 git_vector_foreach(common
, i
, p
)
417 git_vector_free(common
);
429 static void git_smart__free(git_transport
*transport
)
431 transport_smart
*t
= (transport_smart
*)transport
;
432 git_vector
*refs
= &t
->refs
;
436 /* Make sure that the current stream is closed, if we have one. */
437 git_smart__close(transport
);
439 /* Free the subtransport */
440 t
->wrapped
->free(t
->wrapped
);
442 git_vector_free(&t
->heads
);
443 git_vector_foreach(refs
, i
, p
)
446 git_vector_free(refs
);
447 git__free((char *)t
->proxy
.url
);
449 git_strarray_free(&t
->custom_headers
);
454 static int ref_name_cmp(const void *a
, const void *b
)
456 const git_pkt_ref
*ref_a
= a
, *ref_b
= b
;
458 return strcmp(ref_a
->head
.name
, ref_b
->head
.name
);
461 int git_transport_smart_certificate_check(git_transport
*transport
, git_cert
*cert
, int valid
, const char *hostname
)
463 transport_smart
*t
= (transport_smart
*)transport
;
465 return t
->certificate_check_cb(cert
, valid
, hostname
, t
->message_cb_payload
);
468 int git_transport_smart_credentials(git_cred
**out
, git_transport
*transport
, const char *user
, int methods
)
470 transport_smart
*t
= (transport_smart
*)transport
;
472 return t
->cred_acquire_cb(out
, t
->url
, user
, methods
, t
->cred_acquire_payload
);
475 int git_transport_smart(git_transport
**out
, git_remote
*owner
, void *param
)
478 git_smart_subtransport_definition
*definition
= (git_smart_subtransport_definition
*)param
;
483 t
= git__calloc(1, sizeof(transport_smart
));
484 GITERR_CHECK_ALLOC(t
);
486 t
->parent
.version
= GIT_TRANSPORT_VERSION
;
487 t
->parent
.set_callbacks
= git_smart__set_callbacks
;
488 t
->parent
.set_custom_headers
= git_smart__set_custom_headers
;
489 t
->parent
.connect
= git_smart__connect
;
490 t
->parent
.close
= git_smart__close
;
491 t
->parent
.free
= git_smart__free
;
492 t
->parent
.negotiate_fetch
= git_smart__negotiate_fetch
;
493 t
->parent
.download_pack
= git_smart__download_pack
;
494 t
->parent
.push
= git_smart__push
;
495 t
->parent
.ls
= git_smart__ls
;
496 t
->parent
.is_connected
= git_smart__is_connected
;
497 t
->parent
.read_flags
= git_smart__read_flags
;
498 t
->parent
.cancel
= git_smart__cancel
;
501 t
->rpc
= definition
->rpc
;
503 if (git_vector_init(&t
->refs
, 16, ref_name_cmp
) < 0) {
508 if (git_vector_init(&t
->heads
, 16, ref_name_cmp
) < 0) {
513 if (definition
->callback(&t
->wrapped
, &t
->parent
, definition
->param
) < 0) {
518 *out
= (git_transport
*) t
;