]>
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 | ||
7e1a94db CMN |
55 | static 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 | ||
69 | static 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 | 86 | static 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 |
131 | out: |
132 | if (error < GIT_SUCCESS) | |
133 | free(pkt); | |
134 | else | |
135 | *out = (git_pkt *)pkt; | |
136 | ||
137 | return error; | |
138 | } | |
139 | ||
cbf742ac | 140 | static 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 | 176 | int 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 |
229 | void 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 |
239 | int 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 | 252 | int 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 | 284 | int 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 |
295 | int 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 | } |