]>
Commit | Line | Data |
---|---|---|
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 |
41 | static 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 */ |
56 | static 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 | 72 | static 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 | ||
86 | static 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 | 103 | static 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 |
148 | out: |
149 | if (error < GIT_SUCCESS) | |
150 | free(pkt); | |
151 | else | |
152 | *out = (git_pkt *)pkt; | |
153 | ||
154 | return error; | |
155 | } | |
156 | ||
cbf742ac | 157 | static 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 | 193 | int 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 |
255 | void 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 |
265 | int 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 | 278 | int 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 | 309 | int 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 | 317 | int 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 | } |