]> git.proxmox.com Git - libgit2.git/blame - src/pkt.c
Move have sending
[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
7e1a94db
CMN
55static int ack_pkt(git_pkt **out, const char *line, size_t len)
56{
57 git_pkt *pkt;
58
59 pkt = git__malloc(sizeof(git_pkt));
60 if (pkt == NULL)
61 return GIT_ENOMEM;
62
63 pkt->type = GIT_PKT_ACK;
64 *out = pkt;
65
66 return GIT_SUCCESS;
67}
68
69static int nack_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_NACK;
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) {
b31803f3 187 return git__throw(error, "Failed to parse pkt length");
c7c787ce 188 }
f7fc68df 189
7632e249
CMN
190 len = error;
191
192 /*
193 * If we were given a buffer length, then make sure there is
194 * enough in the buffer to satisfy this line
195 */
196 if (bufflen > 0 && bufflen < len)
197 return GIT_ESHORTBUFFER;
198
199 line += PKT_LEN_SIZE;
f7fc68df
CMN
200 /*
201 * TODO: How do we deal with empty lines? Try again? with the next
202 * line?
203 */
7632e249 204 if (len == PKT_LEN_SIZE) {
1d27446c 205 *out = line;
f7fc68df
CMN
206 return GIT_SUCCESS;
207 }
208
1d27446c
CMN
209 if (len == 0) { /* Flush pkt */
210 *out = line;
211 return flush_pkt(head);
f7fc68df
CMN
212 }
213
7632e249 214 len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
f7fc68df 215
7e1a94db
CMN
216 /* Assming the minimal size is actually 4 */
217 if (!git__prefixcmp(line, "ACK"))
218 error = ack_pkt(head, line, len);
219 else if (!git__prefixcmp(line, "NACK"))
220 error = nack_pkt(head);
221 else
222 error = ref_pkt(head, line, len);
b31803f3 223
b31803f3
CMN
224 *out = line + len;
225
226 return error;
f7fc68df 227}
6a9597c5 228
be9fe679
CMN
229void git_pkt_free(git_pkt *pkt)
230{
231 if(pkt->type == GIT_PKT_REF) {
232 git_pkt_ref *p = (git_pkt_ref *) pkt;
be9fe679
CMN
233 free(p->head.name);
234 }
235
236 free(pkt);
237}
238
c4d0fa85
CMN
239int git_pkt_send_flush(int s)
240{
241 char flush[] = "0000";
242
243 return gitno_send(s, flush, STRLEN(flush), 0);
244}
0132cf64
CMN
245
246/*
247 * All "want" packets have the same length and format, so what we do
248 * is overwrite the OID each time.
249 */
250#define WANT_PREFIX "0032want "
251
65fbc48a 252int git_pkt_send_wants(git_headarray *refs, int fd)
0132cf64
CMN
253{
254 unsigned int i;
255 int ret = GIT_SUCCESS;
256 char buf[STRLEN(WANT_PREFIX) + GIT_OID_HEXSZ + 2];
257 git_remote_head *head;
258
259 memcpy(buf, WANT_PREFIX, STRLEN(WANT_PREFIX));
260 buf[sizeof(buf) - 2] = '\n';
261 buf[sizeof(buf) - 1] = '\0';
262
263 for (i = 0; i < refs->len; ++i) {
264 head = refs->heads[i];
b4c90630
CMN
265 if (head->type != GIT_WHN_WANT)
266 continue;
267
0132cf64 268 git_oid_fmt(buf + STRLEN(WANT_PREFIX), &head->oid);
b4c90630
CMN
269 printf("would send %s", buf);
270 }
271
272 /* TODO: git_pkt_send_flush(fd) */
7e1a94db 273 printf("Would send 0000\n");
b4c90630
CMN
274
275 return ret;
276}
277
7e1a94db
CMN
278/*
279 * TODO: this should be a more generic function, maybe to be used by
280 * git_pkt_send_wants, as it's not performance-critical
281 */
b4c90630
CMN
282#define HAVE_PREFIX "0032have "
283
7e1a94db 284int git_pkt_send_have(git_oid *oid, int fd)
b4c90630 285{
b4c90630 286 int ret = GIT_SUCCESS;
7e1a94db 287 char buf[] = "0032have 0000000000000000000000000000000000000000\n";
b4c90630 288
7e1a94db
CMN
289 git_oid_fmt(buf + STRLEN(HAVE_PREFIX), oid);
290 printf("would send %s", buf);
b4c90630 291
7e1a94db
CMN
292 return ret;
293}
b4c90630 294
7e1a94db
CMN
295int git_pkt_send_have(int fd)
296{
297 char buf[] = "0009done\n";
298 printf("Would send %s", buf);
b4c90630 299
7e1a94db 300 return GIT_SUCCESS;
0132cf64 301}