]> git.proxmox.com Git - libgit2.git/blame - src/pkt.c
Only wait for pack if we need it
[libgit2.git] / src / pkt.c
CommitLineData
f7fc68df
CMN
1/*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
5 *
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
14 *
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
bdd18829
VM
26#include "common.h"
27
f7fc68df
CMN
28#include "git2/types.h"
29#include "git2/errors.h"
b4c90630
CMN
30#include "git2/refs.h"
31#include "git2/revwalk.h"
f7fc68df 32
bdd18829 33#include "pkt.h"
f7fc68df 34#include "util.h"
c4d0fa85 35#include "netops.h"
f7fc68df 36
7632e249
CMN
37#include <ctype.h>
38
39#define PKT_LEN_SIZE 4
40
1d27446c
CMN
41static int flush_pkt(git_pkt **out)
42{
43 git_pkt *pkt;
44
45 pkt = git__malloc(sizeof(git_pkt));
46 if (pkt == NULL)
47 return GIT_ENOMEM;
48
49 pkt->type = GIT_PKT_FLUSH;
50 *out = pkt;
51
52 return GIT_SUCCESS;
53}
54
da290220
CMN
55/* the rest of the line will be useful for multi_ack */
56static int ack_pkt(git_pkt **out, const char *GIT_UNUSED(line), size_t GIT_UNUSED(len))
7e1a94db
CMN
57{
58 git_pkt *pkt;
da290220
CMN
59 GIT_UNUSED_ARG(line);
60 GIT_UNUSED_ARG(len);
7e1a94db
CMN
61
62 pkt = git__malloc(sizeof(git_pkt));
63 if (pkt == NULL)
64 return GIT_ENOMEM;
65
66 pkt->type = GIT_PKT_ACK;
67 *out = pkt;
68
69 return GIT_SUCCESS;
70}
71
da290220 72static int nak_pkt(git_pkt **out)
7e1a94db
CMN
73{
74 git_pkt *pkt;
75
76 pkt = git__malloc(sizeof(git_pkt));
77 if (pkt == NULL)
78 return GIT_ENOMEM;
79
da290220
CMN
80 pkt->type = GIT_PKT_NAK;
81 *out = pkt;
82
83 return GIT_SUCCESS;
84}
85
86static int pack_pkt(git_pkt **out)
87{
88 git_pkt *pkt;
89
90 pkt = git__malloc(sizeof(git_pkt));
91 if (pkt == NULL)
92 return GIT_ENOMEM;
93
94 pkt->type = GIT_PKT_PACK;
7e1a94db
CMN
95 *out = pkt;
96
97 return GIT_SUCCESS;
98}
99
b31803f3
CMN
100/*
101 * Parse an other-ref line.
102 */
7e1a94db 103static int ref_pkt(git_pkt **out, const char *line, size_t len)
b31803f3
CMN
104{
105 git_pkt_ref *pkt;
7632e249 106 int error, has_caps = 0;
b31803f3
CMN
107
108 pkt = git__malloc(sizeof(git_pkt_ref));
109 if (pkt == NULL)
110 return GIT_ENOMEM;
111
8b9e8de5 112 memset(pkt, 0x0, sizeof(git_pkt_ref));
b31803f3
CMN
113 pkt->type = GIT_PKT_REF;
114 error = git_oid_fromstr(&pkt->head.oid, line);
115 if (error < GIT_SUCCESS) {
116 error = git__throw(error, "Failed to parse reference ID");
117 goto out;
118 }
119
120 /* Check for a bit of consistency */
121 if (line[GIT_OID_HEXSZ] != ' ') {
122 error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ref. No SP");
123 goto out;
124 }
125
8b9e8de5 126 /* Jump from the name */
b31803f3 127 line += GIT_OID_HEXSZ + 1;
8b9e8de5 128 len -= (GIT_OID_HEXSZ + 1);
b31803f3 129
7632e249
CMN
130 if (strlen(line) < len)
131 has_caps = 1;
132
133 if (line[len - 1] == '\n')
134 --len;
b31803f3 135
7632e249 136 pkt->head.name = git__malloc(len + 1);
b31803f3
CMN
137 if (pkt->head.name == NULL) {
138 error = GIT_ENOMEM;
139 goto out;
140 }
7632e249
CMN
141 memcpy(pkt->head.name, line, len);
142 pkt->head.name[len] = '\0';
b31803f3 143
7632e249
CMN
144 if (has_caps) {
145 pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
8b9e8de5
CMN
146 }
147
b31803f3
CMN
148out:
149 if (error < GIT_SUCCESS)
150 free(pkt);
151 else
152 *out = (git_pkt *)pkt;
153
154 return error;
155}
156
cbf742ac 157static ssize_t parse_len(const char *line)
7632e249
CMN
158{
159 char num[PKT_LEN_SIZE + 1];
160 int i, error;
161 long len;
162 const char *num_end;
163
164 memcpy(num, line, PKT_LEN_SIZE);
165 num[PKT_LEN_SIZE] = '\0';
166
167 for (i = 0; i < PKT_LEN_SIZE; ++i) {
168 if (!isxdigit(num[i]))
169 return GIT_ENOTNUM;
170 }
171
172 error = git__strtol32(&len, num, &num_end, 16);
173 if (error < GIT_SUCCESS) {
174 return error;
175 }
176
177 return (unsigned int) len;
178}
179
f7fc68df
CMN
180/*
181 * As per the documentation, the syntax is:
182 *
183 * pkt-line = data-pkt / flush-pkt
184 * data-pkt = pkt-len pkt-payload
185 * pkt-len = 4*(HEXDIG)
186 * pkt-payload = (pkt-len -4)*(OCTET)
187 * flush-pkt = "0000"
188 *
189 * Which means that the first four bytes are the length of the line,
190 * in ASCII hexadecimal (including itself)
191 */
192
cbf742ac 193int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t bufflen)
f7fc68df
CMN
194{
195 int error = GIT_SUCCESS;
cbf742ac 196 size_t len;
f7fc68df 197
7632e249
CMN
198 /* Not even enough for the length */
199 if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
200 return GIT_ESHORTBUFFER;
78fae478 201
7632e249 202 error = parse_len(line);
c7c787ce 203 if (error < GIT_SUCCESS) {
da290220
CMN
204 /*
205 * If we fail to parse the length, it might be because the
206 * server is trying to send us the packfile already.
207 */
208 if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) {
209 *out = line;
210 return pack_pkt(head);
211 }
212
b31803f3 213 return git__throw(error, "Failed to parse pkt length");
c7c787ce 214 }
f7fc68df 215
7632e249
CMN
216 len = error;
217
218 /*
219 * If we were given a buffer length, then make sure there is
220 * enough in the buffer to satisfy this line
221 */
222 if (bufflen > 0 && bufflen < len)
223 return GIT_ESHORTBUFFER;
224
225 line += PKT_LEN_SIZE;
f7fc68df
CMN
226 /*
227 * TODO: How do we deal with empty lines? Try again? with the next
228 * line?
229 */
7632e249 230 if (len == PKT_LEN_SIZE) {
1d27446c 231 *out = line;
f7fc68df
CMN
232 return GIT_SUCCESS;
233 }
234
1d27446c
CMN
235 if (len == 0) { /* Flush pkt */
236 *out = line;
237 return flush_pkt(head);
f7fc68df
CMN
238 }
239
7632e249 240 len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
f7fc68df 241
7e1a94db
CMN
242 /* Assming the minimal size is actually 4 */
243 if (!git__prefixcmp(line, "ACK"))
244 error = ack_pkt(head, line, len);
da290220
CMN
245 else if (!git__prefixcmp(line, "NAK"))
246 error = nak_pkt(head);
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
c4d0fa85
CMN
265int git_pkt_send_flush(int s)
266{
267 char flush[] = "0000";
268
269 return gitno_send(s, flush, STRLEN(flush), 0);
270}
0132cf64
CMN
271
272/*
273 * All "want" packets have the same length and format, so what we do
274 * is overwrite the OID each time.
275 */
276#define WANT_PREFIX "0032want "
277
65fbc48a 278int git_pkt_send_wants(git_headarray *refs, int fd)
0132cf64
CMN
279{
280 unsigned int i;
281 int ret = GIT_SUCCESS;
282 char buf[STRLEN(WANT_PREFIX) + GIT_OID_HEXSZ + 2];
283 git_remote_head *head;
284
285 memcpy(buf, WANT_PREFIX, STRLEN(WANT_PREFIX));
286 buf[sizeof(buf) - 2] = '\n';
287 buf[sizeof(buf) - 1] = '\0';
288
289 for (i = 0; i < refs->len; ++i) {
290 head = refs->heads[i];
b4c90630
CMN
291 if (head->type != GIT_WHN_WANT)
292 continue;
293
0132cf64 294 git_oid_fmt(buf + STRLEN(WANT_PREFIX), &head->oid);
da290220 295 gitno_send(fd, buf, STRLEN(buf), 0);
b4c90630
CMN
296 }
297
da290220 298 git_pkt_send_flush(fd);
b4c90630
CMN
299
300 return ret;
301}
302
7e1a94db
CMN
303/*
304 * TODO: this should be a more generic function, maybe to be used by
305 * git_pkt_send_wants, as it's not performance-critical
306 */
b4c90630
CMN
307#define HAVE_PREFIX "0032have "
308
7e1a94db 309int git_pkt_send_have(git_oid *oid, int fd)
b4c90630 310{
7e1a94db 311 char buf[] = "0032have 0000000000000000000000000000000000000000\n";
b4c90630 312
7e1a94db 313 git_oid_fmt(buf + STRLEN(HAVE_PREFIX), oid);
da290220 314 return gitno_send(fd, buf, STRLEN(buf), 0);
7e1a94db 315}
b4c90630 316
da290220 317int git_pkt_send_done(int fd)
7e1a94db
CMN
318{
319 char buf[] = "0009done\n";
b4c90630 320
da290220 321 return gitno_send(fd, buf, STRLEN(buf), 0);
0132cf64 322}