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