]> git.proxmox.com Git - libgit2.git/blame - src/pkt.c
http: parse the response from the server
[libgit2.git] / src / pkt.c
CommitLineData
f7fc68df 1/*
bb742ede 2 * Copyright (C) 2009-2011 the libgit2 contributors
f7fc68df 3 *
bb742ede
VM
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.
f7fc68df
CMN
6 */
7
bdd18829
VM
8#include "common.h"
9
f7fc68df
CMN
10#include "git2/types.h"
11#include "git2/errors.h"
b4c90630
CMN
12#include "git2/refs.h"
13#include "git2/revwalk.h"
f7fc68df 14
bdd18829 15#include "pkt.h"
f7fc68df 16#include "util.h"
c4d0fa85 17#include "netops.h"
84dd3820 18#include "posix.h"
34bfb4b0 19#include "buffer.h"
f7fc68df 20
7632e249
CMN
21#include <ctype.h>
22
23#define PKT_LEN_SIZE 4
24
1d27446c
CMN
25static int flush_pkt(git_pkt **out)
26{
27 git_pkt *pkt;
28
29 pkt = git__malloc(sizeof(git_pkt));
30 if (pkt == NULL)
31 return GIT_ENOMEM;
32
33 pkt->type = GIT_PKT_FLUSH;
34 *out = pkt;
35
36 return GIT_SUCCESS;
37}
38
da290220
CMN
39/* the rest of the line will be useful for multi_ack */
40static int ack_pkt(git_pkt **out, const char *GIT_UNUSED(line), size_t GIT_UNUSED(len))
7e1a94db
CMN
41{
42 git_pkt *pkt;
da290220
CMN
43 GIT_UNUSED_ARG(line);
44 GIT_UNUSED_ARG(len);
7e1a94db
CMN
45
46 pkt = git__malloc(sizeof(git_pkt));
47 if (pkt == NULL)
48 return GIT_ENOMEM;
49
50 pkt->type = GIT_PKT_ACK;
51 *out = pkt;
52
53 return GIT_SUCCESS;
54}
55
da290220 56static int nak_pkt(git_pkt **out)
7e1a94db
CMN
57{
58 git_pkt *pkt;
59
60 pkt = git__malloc(sizeof(git_pkt));
61 if (pkt == NULL)
62 return GIT_ENOMEM;
63
da290220
CMN
64 pkt->type = GIT_PKT_NAK;
65 *out = pkt;
66
67 return GIT_SUCCESS;
68}
69
70static int pack_pkt(git_pkt **out)
71{
72 git_pkt *pkt;
73
74 pkt = git__malloc(sizeof(git_pkt));
75 if (pkt == NULL)
76 return GIT_ENOMEM;
77
78 pkt->type = GIT_PKT_PACK;
7e1a94db
CMN
79 *out = pkt;
80
81 return GIT_SUCCESS;
82}
83
b76f7522
CMN
84static int comment_pkt(git_pkt **out, const char *line, size_t len)
85{
86 git_pkt_comment *pkt;
87
88 pkt = git__malloc(sizeof(git_pkt_comment) + len + 1);
89 if (pkt == NULL)
90 return GIT_ENOMEM;
91
92 pkt->type = GIT_PKT_COMMENT;
93 memcpy(pkt->comment, line, len);
94 pkt->comment[len] = '\0';
95
96 *out = (git_pkt *) pkt;
97
98 return GIT_SUCCESS;
99}
100
b31803f3
CMN
101/*
102 * Parse an other-ref line.
103 */
7e1a94db 104static int ref_pkt(git_pkt **out, const char *line, size_t len)
b31803f3
CMN
105{
106 git_pkt_ref *pkt;
76a9081d 107 int error;
b31803f3
CMN
108
109 pkt = git__malloc(sizeof(git_pkt_ref));
110 if (pkt == NULL)
111 return GIT_ENOMEM;
112
8b9e8de5 113 memset(pkt, 0x0, sizeof(git_pkt_ref));
b31803f3
CMN
114 pkt->type = GIT_PKT_REF;
115 error = git_oid_fromstr(&pkt->head.oid, line);
116 if (error < GIT_SUCCESS) {
117 error = git__throw(error, "Failed to parse reference ID");
118 goto out;
119 }
120
121 /* Check for a bit of consistency */
122 if (line[GIT_OID_HEXSZ] != ' ') {
123 error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ref. No SP");
124 goto out;
125 }
126
8b9e8de5 127 /* Jump from the name */
b31803f3 128 line += GIT_OID_HEXSZ + 1;
8b9e8de5 129 len -= (GIT_OID_HEXSZ + 1);
b31803f3 130
7632e249
CMN
131 if (line[len - 1] == '\n')
132 --len;
b31803f3 133
7632e249 134 pkt->head.name = git__malloc(len + 1);
b31803f3
CMN
135 if (pkt->head.name == NULL) {
136 error = GIT_ENOMEM;
137 goto out;
138 }
7632e249
CMN
139 memcpy(pkt->head.name, line, len);
140 pkt->head.name[len] = '\0';
b31803f3 141
76a9081d 142 if (strlen(pkt->head.name) < len) {
7632e249 143 pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
8b9e8de5
CMN
144 }
145
b31803f3
CMN
146out:
147 if (error < GIT_SUCCESS)
148 free(pkt);
149 else
150 *out = (git_pkt *)pkt;
151
152 return error;
153}
154
cbf742ac 155static ssize_t parse_len(const char *line)
7632e249
CMN
156{
157 char num[PKT_LEN_SIZE + 1];
158 int i, error;
ad196c6a 159 int len;
7632e249
CMN
160 const char *num_end;
161
162 memcpy(num, line, PKT_LEN_SIZE);
163 num[PKT_LEN_SIZE] = '\0';
164
165 for (i = 0; i < PKT_LEN_SIZE; ++i) {
166 if (!isxdigit(num[i]))
167 return GIT_ENOTNUM;
168 }
169
170 error = git__strtol32(&len, num, &num_end, 16);
171 if (error < GIT_SUCCESS) {
172 return error;
173 }
174
175 return (unsigned int) len;
176}
177
f7fc68df
CMN
178/*
179 * As per the documentation, the syntax is:
180 *
87d9869f
VM
181 * pkt-line = data-pkt / flush-pkt
182 * data-pkt = pkt-len pkt-payload
183 * pkt-len = 4*(HEXDIG)
f7fc68df 184 * pkt-payload = (pkt-len -4)*(OCTET)
87d9869f 185 * flush-pkt = "0000"
f7fc68df
CMN
186 *
187 * Which means that the first four bytes are the length of the line,
188 * in ASCII hexadecimal (including itself)
189 */
190
cbf742ac 191int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t bufflen)
f7fc68df
CMN
192{
193 int error = GIT_SUCCESS;
cbf742ac 194 size_t len;
f7fc68df 195
7632e249
CMN
196 /* Not even enough for the length */
197 if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
198 return GIT_ESHORTBUFFER;
78fae478 199
7632e249 200 error = parse_len(line);
c7c787ce 201 if (error < GIT_SUCCESS) {
da290220
CMN
202 /*
203 * If we fail to parse the length, it might be because the
204 * server is trying to send us the packfile already.
205 */
206 if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) {
207 *out = line;
208 return pack_pkt(head);
209 }
210
b31803f3 211 return git__throw(error, "Failed to parse pkt length");
c7c787ce 212 }
f7fc68df 213
7632e249
CMN
214 len = error;
215
216 /*
217 * If we were given a buffer length, then make sure there is
218 * enough in the buffer to satisfy this line
219 */
220 if (bufflen > 0 && bufflen < len)
221 return GIT_ESHORTBUFFER;
222
223 line += PKT_LEN_SIZE;
f7fc68df
CMN
224 /*
225 * TODO: How do we deal with empty lines? Try again? with the next
226 * line?
227 */
7632e249 228 if (len == PKT_LEN_SIZE) {
1d27446c 229 *out = line;
f7fc68df
CMN
230 return GIT_SUCCESS;
231 }
232
1d27446c
CMN
233 if (len == 0) { /* Flush pkt */
234 *out = line;
235 return flush_pkt(head);
f7fc68df
CMN
236 }
237
7632e249 238 len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
f7fc68df 239
7e1a94db
CMN
240 /* Assming the minimal size is actually 4 */
241 if (!git__prefixcmp(line, "ACK"))
242 error = ack_pkt(head, line, len);
da290220
CMN
243 else if (!git__prefixcmp(line, "NAK"))
244 error = nak_pkt(head);
b76f7522
CMN
245 else if (*line == '#')
246 error = comment_pkt(head, line, len);
7e1a94db
CMN
247 else
248 error = ref_pkt(head, line, len);
b31803f3 249
b31803f3
CMN
250 *out = line + len;
251
252 return error;
f7fc68df 253}
6a9597c5 254
be9fe679
CMN
255void git_pkt_free(git_pkt *pkt)
256{
257 if(pkt->type == GIT_PKT_REF) {
258 git_pkt_ref *p = (git_pkt_ref *) pkt;
be9fe679
CMN
259 free(p->head.name);
260 }
261
262 free(pkt);
263}
264
34bfb4b0 265int git_pkt_send_flush(int s, int chunked)
c4d0fa85
CMN
266{
267 char flush[] = "0000";
34bfb4b0 268 int error;
c4d0fa85 269
34bfb4b0
CMN
270 if (chunked) {
271 error = gitno_send_chunk_size(s, strlen(flush));
272 if (error < GIT_SUCCESS)
273 return git__rethrow(error, "Failed to send chunk size");
274 }
932669b8 275 return gitno_send(s, flush, strlen(flush), 0);
c4d0fa85 276}
0132cf64 277
34bfb4b0 278static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps, int fd, int chunked)
0437d991
CMN
279{
280 char capstr[20]; /* Longer than we need */
281 char oid[GIT_OID_HEXSZ +1] = {0}, *cmd;
282 int error, len;
34bfb4b0 283 git_buf buf = GIT_BUF_INIT;
0437d991
CMN
284
285 if (caps->ofs_delta)
286 strcpy(capstr, GIT_CAP_OFS_DELTA);
287
932669b8 288 len = strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + strlen(capstr) + 1 /* LF */;
0437d991
CMN
289 cmd = git__malloc(len + 1);
290 if (cmd == NULL)
291 return GIT_ENOMEM;
292
293 git_oid_fmt(oid, &head->oid);
34bfb4b0
CMN
294 git_buf_printf(&buf, "%04xwant %s%c%s\n", len, oid, 0, capstr);
295 if (chunked) {
296 error = gitno_send_chunk_size(fd, len);
297 if (error < GIT_SUCCESS)
298 return git__rethrow(error, "Failed to send first want chunk size");
299 }
300 error = gitno_send(fd, git_buf_cstr(&buf), len, 0);
0437d991
CMN
301 free(cmd);
302 return error;
303}
304
0132cf64
CMN
305/*
306 * All "want" packets have the same length and format, so what we do
307 * is overwrite the OID each time.
308 */
309#define WANT_PREFIX "0032want "
310
34bfb4b0 311int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd, int chunked)
0132cf64 312{
427ca3d3
CMN
313 unsigned int i = 0;
314 int error = GIT_SUCCESS;
85b91652 315 char buf[sizeof(WANT_PREFIX) + GIT_OID_HEXSZ + 1];
0132cf64
CMN
316 git_remote_head *head;
317
932669b8 318 memcpy(buf, WANT_PREFIX, strlen(WANT_PREFIX));
0132cf64
CMN
319 buf[sizeof(buf) - 2] = '\n';
320 buf[sizeof(buf) - 1] = '\0';
321
427ca3d3
CMN
322 /* If there are common caps, find the first one */
323 if (caps->common) {
324 for (; i < refs->len; ++i) {
325 head = refs->heads[i];
326 if (head->local)
327 continue;
328 else
329 break;
330 }
331
34bfb4b0 332 error = send_want_with_caps(refs->heads[i], caps, fd, chunked);
427ca3d3
CMN
333 if (error < GIT_SUCCESS)
334 return git__rethrow(error, "Failed to send want pkt with caps");
335 /* Increase it here so it's correct whether we run this or not */
336 i++;
0437d991
CMN
337 }
338
427ca3d3 339 /* Continue from where we left off */
0437d991 340 for (; i < refs->len; ++i) {
0132cf64 341 head = refs->heads[i];
1564db11
CMN
342 if (head->local)
343 continue;
b4c90630 344
932669b8 345 git_oid_fmt(buf + strlen(WANT_PREFIX), &head->oid);
34bfb4b0
CMN
346 if (chunked) {
347 error = gitno_send_chunk_size(fd, strlen(buf));
348 if (error < GIT_SUCCESS)
349 return git__rethrow(error, "Failed to send want chunk size");
350 }
932669b8 351 error = gitno_send(fd, buf, strlen(buf), 0);
34bfb4b0 352 if (error < GIT_SUCCESS)
922bc225 353 return git__rethrow(error, "Failed to send want pkt");
b4c90630
CMN
354 }
355
34bfb4b0 356 return git_pkt_send_flush(fd, chunked);
b4c90630
CMN
357}
358
7e1a94db
CMN
359/*
360 * TODO: this should be a more generic function, maybe to be used by
361 * git_pkt_send_wants, as it's not performance-critical
362 */
b4c90630
CMN
363#define HAVE_PREFIX "0032have "
364
34bfb4b0 365int git_pkt_send_have(git_oid *oid, int fd, int chunked)
b4c90630 366{
7e1a94db 367 char buf[] = "0032have 0000000000000000000000000000000000000000\n";
34bfb4b0 368 int error;
b4c90630 369
34bfb4b0
CMN
370 if (chunked) {
371 error = gitno_send_chunk_size(fd, strlen(buf));
372 if (error < GIT_SUCCESS)
373 return git__rethrow(error, "Failed to send chunk size");
374 }
932669b8
KS
375 git_oid_fmt(buf + strlen(HAVE_PREFIX), oid);
376 return gitno_send(fd, buf, strlen(buf), 0);
7e1a94db 377}
b4c90630 378
34bfb4b0 379int git_pkt_send_done(int fd, int chunked)
7e1a94db
CMN
380{
381 char buf[] = "0009done\n";
34bfb4b0 382 int error;
b4c90630 383
34bfb4b0
CMN
384 if (chunked) {
385 error = gitno_send_chunk_size(fd, strlen(buf));
386 if (error < GIT_SUCCESS)
387 return git__rethrow(error, "Failed to send chunk size");
388 }
932669b8 389 return gitno_send(fd, buf, strlen(buf), 0);
0132cf64 390}