Merge lp:~rhansen/ubuntu/trusty/cvsps/bug1413084 into lp:ubuntu/trusty/cvsps
- Trusty (14.04)
- bug1413084
- Merge into trusty
Proposed by
Richard Hansen
Status: | Approved |
---|---|
Approved by: | Brian Murray |
Approved revision: | 10 |
Proposed branch: | lp:~rhansen/ubuntu/trusty/cvsps/bug1413084 |
Merge into: | lp:ubuntu/trusty/cvsps |
Diff against target: |
1023 lines (+965/-2) 7 files modified
.pc/06-discard-extra-version-lines.patch/cvs_direct.c (+925/-0) .pc/applied-patches (+1/-0) cvs_direct.c (+2/-1) debian/changelog (+7/-0) debian/control (+2/-1) debian/patches/06-discard-extra-version-lines.patch (+27/-0) debian/patches/series (+1/-0) |
To merge this branch: | bzr merge lp:~rhansen/ubuntu/trusty/cvsps/bug1413084 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu branches | Pending | ||
Review via email: mp+249787@code.launchpad.net |
Commit message
Description of the change
Fix for bug #1413084.
To post a comment you must log in.
Unmerged revisions
- 10. By Richard Hansen
-
Discard extra "M" response lines when reading the server's
version. (LP: #1413084)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory '.pc/06-discard-extra-version-lines.patch' |
2 | === added file '.pc/06-discard-extra-version-lines.patch/cvs_direct.c' |
3 | --- .pc/06-discard-extra-version-lines.patch/cvs_direct.c 1970-01-01 00:00:00 +0000 |
4 | +++ .pc/06-discard-extra-version-lines.patch/cvs_direct.c 2015-02-15 23:35:36 +0000 |
5 | @@ -0,0 +1,925 @@ |
6 | +/* |
7 | + * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc. |
8 | + * See COPYING file for license information |
9 | + */ |
10 | + |
11 | +#include <string.h> |
12 | +#include <unistd.h> |
13 | +#include <stdlib.h> |
14 | +#include <limits.h> |
15 | +#include <stdarg.h> |
16 | +#include <zlib.h> |
17 | +#include <sys/socket.h> |
18 | +#include <cbtcommon/debug.h> |
19 | +#include <cbtcommon/text_util.h> |
20 | +#include <cbtcommon/tcpsocket.h> |
21 | +#include <cbtcommon/sio.h> |
22 | + |
23 | +#include "cvs_direct.h" |
24 | +#include "util.h" |
25 | + |
26 | +#define RD_BUFF_SIZE 4096 |
27 | + |
28 | +struct _CvsServerCtx |
29 | +{ |
30 | + int read_fd; |
31 | + int write_fd; |
32 | + char root[PATH_MAX]; |
33 | + |
34 | + int is_pserver; |
35 | + |
36 | + /* buffered reads from descriptor */ |
37 | + char read_buff[RD_BUFF_SIZE]; |
38 | + char * head; |
39 | + char * tail; |
40 | + |
41 | + int compressed; |
42 | + z_stream zout; |
43 | + z_stream zin; |
44 | + |
45 | + /* when reading compressed data, the compressed data buffer */ |
46 | + char zread_buff[RD_BUFF_SIZE]; |
47 | +}; |
48 | + |
49 | +static void get_cvspass(char *, const char *); |
50 | +static void send_string(CvsServerCtx *, const char *, ...); |
51 | +static int read_response(CvsServerCtx *, const char *); |
52 | +static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp); |
53 | +static int read_line(CvsServerCtx * ctx, char * p); |
54 | + |
55 | +static CvsServerCtx * open_ctx_pserver(CvsServerCtx *, const char *); |
56 | +static CvsServerCtx * open_ctx_forked(CvsServerCtx *, const char *); |
57 | + |
58 | +CvsServerCtx * open_cvs_server(char * p_root, int compress) |
59 | +{ |
60 | + CvsServerCtx * ctx = (CvsServerCtx*)malloc(sizeof(*ctx)); |
61 | + char root[PATH_MAX]; |
62 | + char * p = root, *tok; |
63 | + |
64 | + if (!ctx) |
65 | + return NULL; |
66 | + |
67 | + ctx->head = ctx->tail = ctx->read_buff; |
68 | + ctx->read_fd = ctx->write_fd = -1; |
69 | + ctx->compressed = 0; |
70 | + ctx->is_pserver = 0; |
71 | + |
72 | + if (compress) |
73 | + { |
74 | + memset(&ctx->zout, 0, sizeof(z_stream)); |
75 | + memset(&ctx->zin, 0, sizeof(z_stream)); |
76 | + |
77 | + /* |
78 | + * to 'prime' the reads, make it look like there was output |
79 | + * room available (i.e. we have processed all pending compressed |
80 | + * data |
81 | + */ |
82 | + ctx->zin.avail_out = 1; |
83 | + |
84 | + if (deflateInit(&ctx->zout, compress) != Z_OK) |
85 | + { |
86 | + free(ctx); |
87 | + return NULL; |
88 | + } |
89 | + |
90 | + if (inflateInit(&ctx->zin) != Z_OK) |
91 | + { |
92 | + deflateEnd(&ctx->zout); |
93 | + free(ctx); |
94 | + return NULL; |
95 | + } |
96 | + } |
97 | + |
98 | + strcpy(root, p_root); |
99 | + |
100 | + tok = strsep(&p, ":"); |
101 | + |
102 | + /* if root string looks like :pserver:... then the first token will be empty */ |
103 | + if (strlen(tok) == 0) |
104 | + { |
105 | + char * method = strsep(&p, ":"); |
106 | + if (strcmp(method, "pserver") == 0) |
107 | + { |
108 | + ctx = open_ctx_pserver(ctx, p); |
109 | + } |
110 | + else if (strstr("local:ext:fork:server", method)) |
111 | + { |
112 | + /* handle all of these via fork, even local */ |
113 | + ctx = open_ctx_forked(ctx, p); |
114 | + } |
115 | + else |
116 | + { |
117 | + debug(DEBUG_APPERROR, "cvs_direct: unsupported cvs access method: %s", method); |
118 | + free(ctx); |
119 | + ctx = NULL; |
120 | + } |
121 | + } |
122 | + else |
123 | + { |
124 | + ctx = open_ctx_forked(ctx, p_root); |
125 | + } |
126 | + |
127 | + if (ctx) |
128 | + { |
129 | + char buff[BUFSIZ]; |
130 | + |
131 | + send_string(ctx, "Root %s\n", ctx->root); |
132 | + |
133 | + /* this is taken from 1.11.1p1 trace - but with Mbinary removed. we can't handle it (yet!) */ |
134 | + send_string(ctx, "Valid-responses ok error Valid-requests Checked-in New-entry Checksum Copy-file Updated Created Update-existing Merged Patched Rcs-diff Mode Mod-time Removed Remove-entry Set-static-directory Clear-static-directory Set-sticky Clear-sticky Template Set-checkin-prog Set-update-prog Notified Module-expansion Wrapper-rcsOption M E F\n", ctx->root); |
135 | + |
136 | + send_string(ctx, "valid-requests\n"); |
137 | + |
138 | + /* check for the commands we will issue */ |
139 | + read_line(ctx, buff); |
140 | + if (strncmp(buff, "Valid-requests", 14) != 0) |
141 | + { |
142 | + debug(DEBUG_APPERROR, "cvs_direct: bad response to valid-requests command"); |
143 | + close_cvs_server(ctx); |
144 | + return NULL; |
145 | + } |
146 | + |
147 | + if (!strstr(buff, " version") || |
148 | + !strstr(buff, " rlog") || |
149 | + !strstr(buff, " rdiff") || |
150 | + !strstr(buff, " diff") || |
151 | + !strstr(buff, " co")) |
152 | + { |
153 | + debug(DEBUG_APPERROR, "cvs_direct: cvs server too old for cvs_direct"); |
154 | + close_cvs_server(ctx); |
155 | + return NULL; |
156 | + } |
157 | + |
158 | + read_line(ctx, buff); |
159 | + if (strcmp(buff, "ok") != 0) |
160 | + { |
161 | + debug(DEBUG_APPERROR, "cvs_direct: bad ok trailer to valid-requests command"); |
162 | + close_cvs_server(ctx); |
163 | + return NULL; |
164 | + } |
165 | + |
166 | + /* this is myterious but 'mandatory' */ |
167 | + send_string(ctx, "UseUnchanged\n"); |
168 | + |
169 | + if (compress) |
170 | + { |
171 | + send_string(ctx, "Gzip-stream %d\n", compress); |
172 | + ctx->compressed = 1; |
173 | + } |
174 | + |
175 | + debug(DEBUG_APPMSG1, "cvs_direct initialized to CVSROOT %s", ctx->root); |
176 | + } |
177 | + |
178 | + return ctx; |
179 | +} |
180 | + |
181 | +static CvsServerCtx * open_ctx_pserver(CvsServerCtx * ctx, const char * p_root) |
182 | +{ |
183 | + char root[PATH_MAX]; |
184 | + char full_root[PATH_MAX]; |
185 | + char * p = root, *tok, *tok2; |
186 | + char user[BUFSIZ]; |
187 | + char server[BUFSIZ]; |
188 | + char pass[BUFSIZ]; |
189 | + char port[8]; |
190 | + |
191 | + strcpy(root, p_root); |
192 | + |
193 | + tok = strsep(&p, ":"); |
194 | + if (strlen(tok) == 0 || !p) |
195 | + { |
196 | + debug(DEBUG_APPERROR, "parse error on third token"); |
197 | + goto out_free_err; |
198 | + } |
199 | + |
200 | + tok2 = strsep(&tok, "@"); |
201 | + if (!strlen(tok2) || (!tok || !strlen(tok))) |
202 | + { |
203 | + debug(DEBUG_APPERROR, "parse error on user@server in pserver"); |
204 | + goto out_free_err; |
205 | + } |
206 | + |
207 | + strcpy(user, tok2); |
208 | + strcpy(server, tok); |
209 | + |
210 | + if (*p != '/') |
211 | + { |
212 | + tok = strchr(p, '/'); |
213 | + if (!tok) |
214 | + { |
215 | + debug(DEBUG_APPERROR, "parse error: expecting / in root"); |
216 | + goto out_free_err; |
217 | + } |
218 | + |
219 | + memset(port, 0, sizeof(port)); |
220 | + memcpy(port, p, tok - p); |
221 | + |
222 | + p = tok; |
223 | + } |
224 | + else |
225 | + { |
226 | + strcpy(port, "2401"); |
227 | + } |
228 | + |
229 | + /* the line from .cvspass is fully qualified, so rebuild */ |
230 | + snprintf(full_root, PATH_MAX, ":pserver:%s@%s:%s%s", user, server, port, p); |
231 | + get_cvspass(pass, full_root); |
232 | + |
233 | + debug(DEBUG_TCP, "user:%s server:%s port:%s pass:%s full_root:%s", user, server, port, pass, full_root); |
234 | + |
235 | + if ((ctx->read_fd = tcp_create_socket(REUSE_ADDR)) < 0) |
236 | + goto out_free_err; |
237 | + |
238 | + ctx->write_fd = dup(ctx->read_fd); |
239 | + |
240 | + if (tcp_connect(ctx->read_fd, server, atoi(port)) < 0) |
241 | + goto out_close_err; |
242 | + |
243 | + send_string(ctx, "BEGIN AUTH REQUEST\n"); |
244 | + send_string(ctx, "%s\n", p); |
245 | + send_string(ctx, "%s\n", user); |
246 | + send_string(ctx, "%s\n", pass); |
247 | + send_string(ctx, "END AUTH REQUEST\n"); |
248 | + |
249 | + if (!read_response(ctx, "I LOVE YOU")) |
250 | + goto out_close_err; |
251 | + |
252 | + strcpy(ctx->root, p); |
253 | + ctx->is_pserver = 1; |
254 | + |
255 | + return ctx; |
256 | + |
257 | + out_close_err: |
258 | + close(ctx->read_fd); |
259 | + out_free_err: |
260 | + free(ctx); |
261 | + return NULL; |
262 | +} |
263 | + |
264 | +static CvsServerCtx * open_ctx_forked(CvsServerCtx * ctx, const char * p_root) |
265 | +{ |
266 | + char root[PATH_MAX]; |
267 | + char * p = root, *tok, *tok2, *rep; |
268 | + char execcmd[PATH_MAX]; |
269 | + int to_cvs[2]; |
270 | + int from_cvs[2]; |
271 | + pid_t pid; |
272 | + const char * cvs_server = getenv("CVS_SERVER"); |
273 | + |
274 | + if (!cvs_server) |
275 | + cvs_server = "cvs"; |
276 | + |
277 | + strcpy(root, p_root); |
278 | + |
279 | + /* if there's a ':', it's remote */ |
280 | + tok = strsep(&p, ":"); |
281 | + |
282 | + if (p) |
283 | + { |
284 | + const char * cvs_rsh = getenv("CVS_RSH"); |
285 | + |
286 | + if (!cvs_rsh) |
287 | + cvs_rsh = "rsh"; |
288 | + |
289 | + tok2 = strsep(&tok, "@"); |
290 | + |
291 | + if (tok) |
292 | + snprintf(execcmd, PATH_MAX, "%s -l %s %s %s server", cvs_rsh, tok2, tok, cvs_server); |
293 | + else |
294 | + snprintf(execcmd, PATH_MAX, "%s %s %s server", cvs_rsh, tok2, cvs_server); |
295 | + |
296 | + rep = p; |
297 | + } |
298 | + else |
299 | + { |
300 | + snprintf(execcmd, PATH_MAX, "%s server", cvs_server); |
301 | + rep = tok; |
302 | + } |
303 | + |
304 | + if (pipe(to_cvs) < 0) |
305 | + { |
306 | + debug(DEBUG_SYSERROR, "cvs_direct: failed to create pipe to_cvs"); |
307 | + goto out_free_err; |
308 | + } |
309 | + |
310 | + if (pipe(from_cvs) < 0) |
311 | + { |
312 | + debug(DEBUG_SYSERROR, "cvs_direct: failed to create pipe from_cvs"); |
313 | + goto out_close_err; |
314 | + } |
315 | + |
316 | + debug(DEBUG_TCP, "forked cmdline: %s", execcmd); |
317 | + |
318 | + if ((pid = fork()) < 0) |
319 | + { |
320 | + debug(DEBUG_SYSERROR, "cvs_direct: can't fork"); |
321 | + goto out_close2_err; |
322 | + } |
323 | + else if (pid == 0) /* child */ |
324 | + { |
325 | + char * argp[4]; |
326 | + argp[0] = "sh"; |
327 | + argp[1] = "-c"; |
328 | + argp[2] = execcmd; |
329 | + argp[3] = NULL; |
330 | + |
331 | + close(to_cvs[1]); |
332 | + close(from_cvs[0]); |
333 | + |
334 | + close(0); |
335 | + dup(to_cvs[0]); |
336 | + close(1); |
337 | + dup(from_cvs[1]); |
338 | + |
339 | + execv("/bin/sh",argp); |
340 | + |
341 | + debug(DEBUG_APPERROR, "cvs_direct: fatal: shouldn't be reached"); |
342 | + exit(1); |
343 | + } |
344 | + |
345 | + close(to_cvs[0]); |
346 | + close(from_cvs[1]); |
347 | + ctx->read_fd = from_cvs[0]; |
348 | + ctx->write_fd = to_cvs[1]; |
349 | + |
350 | + strcpy(ctx->root, rep); |
351 | + |
352 | + return ctx; |
353 | + |
354 | + out_close2_err: |
355 | + close(from_cvs[0]); |
356 | + close(from_cvs[1]); |
357 | + out_close_err: |
358 | + close(to_cvs[0]); |
359 | + close(to_cvs[1]); |
360 | + out_free_err: |
361 | + free(ctx); |
362 | + return NULL; |
363 | +} |
364 | + |
365 | +void close_cvs_server(CvsServerCtx * ctx) |
366 | +{ |
367 | + /* FIXME: some sort of flushing should be done for non-compressed case */ |
368 | + |
369 | + if (ctx->compressed) |
370 | + { |
371 | + int ret, len; |
372 | + char buff[BUFSIZ]; |
373 | + |
374 | + /* |
375 | + * there shouldn't be anything left, but we do want |
376 | + * to send an 'end of stream' marker, (if such a thing |
377 | + * actually exists..) |
378 | + */ |
379 | + do |
380 | + { |
381 | + ctx->zout.next_out = buff; |
382 | + ctx->zout.avail_out = BUFSIZ; |
383 | + ret = deflate(&ctx->zout, Z_FINISH); |
384 | + |
385 | + if ((ret == Z_OK || ret == Z_STREAM_END) && ctx->zout.avail_out != BUFSIZ) |
386 | + { |
387 | + len = BUFSIZ - ctx->zout.avail_out; |
388 | + if (writen(ctx->write_fd, buff, len) != len) |
389 | + debug(DEBUG_APPERROR, "cvs_direct: zout: error writing final state"); |
390 | + |
391 | + //hexdump(buff, len, "cvs_direct: zout: sending unsent data"); |
392 | + } |
393 | + } while (ret == Z_OK); |
394 | + |
395 | + if ((ret = deflateEnd(&ctx->zout)) != Z_OK) |
396 | + debug(DEBUG_APPERROR, "cvs_direct: zout: deflateEnd error: %s: %s", |
397 | + (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zout.msg); |
398 | + } |
399 | + |
400 | + /* we're done writing now */ |
401 | + debug(DEBUG_TCP, "cvs_direct: closing cvs server write connection %d", ctx->write_fd); |
402 | + close(ctx->write_fd); |
403 | + |
404 | + /* |
405 | + * if this is pserver, then read_fd is a bi-directional socket. |
406 | + * we want to shutdown the write side, just to make sure the |
407 | + * server get's eof |
408 | + */ |
409 | + if (ctx->is_pserver) |
410 | + { |
411 | + debug(DEBUG_TCP, "cvs_direct: shutdown on read socket"); |
412 | + if (shutdown(ctx->read_fd, SHUT_WR) < 0) |
413 | + debug(DEBUG_SYSERROR, "cvs_direct: error with shutdown on pserver socket"); |
414 | + } |
415 | + |
416 | + if (ctx->compressed) |
417 | + { |
418 | + int ret = Z_OK, len, eof = 0; |
419 | + char buff[BUFSIZ]; |
420 | + |
421 | + /* read to the 'eof'/'eos' marker. there are two states we |
422 | + * track, looking for Z_STREAM_END (application level EOS) |
423 | + * and EOF on socket. Both should happen at the same time, |
424 | + * but we need to do the read first, the first time through |
425 | + * the loop, but we want to do one read after getting Z_STREAM_END |
426 | + * too. so this loop has really ugly exit conditions. |
427 | + */ |
428 | + for(;;) |
429 | + { |
430 | + /* |
431 | + * if there's nothing in the avail_in, and we |
432 | + * inflated everything last pass (avail_out != 0) |
433 | + * then slurp some more from the descriptor, |
434 | + * if we get EOF, exit the loop |
435 | + */ |
436 | + if (ctx->zin.avail_in == 0 && ctx->zin.avail_out != 0) |
437 | + { |
438 | + debug(DEBUG_TCP, "cvs_direct: doing final slurp"); |
439 | + len = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE); |
440 | + debug(DEBUG_TCP, "cvs_direct: did final slurp: %d", len); |
441 | + |
442 | + if (len <= 0) |
443 | + { |
444 | + eof = 1; |
445 | + break; |
446 | + } |
447 | + |
448 | + /* put the data into the inflate input stream */ |
449 | + ctx->zin.next_in = ctx->zread_buff; |
450 | + ctx->zin.avail_in = len; |
451 | + } |
452 | + |
453 | + /* |
454 | + * if the last time through we got Z_STREAM_END, and we |
455 | + * get back here, it means we should've gotten EOF but |
456 | + * didn't |
457 | + */ |
458 | + if (ret == Z_STREAM_END) |
459 | + break; |
460 | + |
461 | + ctx->zin.next_out = buff; |
462 | + ctx->zin.avail_out = BUFSIZ; |
463 | + |
464 | + ret = inflate(&ctx->zin, Z_SYNC_FLUSH); |
465 | + len = BUFSIZ - ctx->zin.avail_out; |
466 | + |
467 | + if (ret == Z_BUF_ERROR) |
468 | + debug(DEBUG_APPERROR, "Z_BUF_ERROR"); |
469 | + |
470 | + if (ret == Z_OK && len == 0) |
471 | + debug(DEBUG_TCP, "cvs_direct: no data out of inflate"); |
472 | + |
473 | + if (ret == Z_STREAM_END) |
474 | + debug(DEBUG_TCP, "cvs_direct: got Z_STREAM_END"); |
475 | + |
476 | + if ((ret == Z_OK || ret == Z_STREAM_END) && len > 0) |
477 | + hexdump(buff, BUFSIZ - ctx->zin.avail_out, "cvs_direct: zin: unread data at close"); |
478 | + } |
479 | + |
480 | + if (ret != Z_STREAM_END) |
481 | + debug(DEBUG_APPERROR, "cvs_direct: zin: Z_STREAM_END not encountered (premature EOF?)"); |
482 | + |
483 | + if (eof == 0) |
484 | + debug(DEBUG_APPERROR, "cvs_direct: zin: EOF not encountered (premature Z_STREAM_END?)"); |
485 | + |
486 | + if ((ret = inflateEnd(&ctx->zin)) != Z_OK) |
487 | + debug(DEBUG_APPERROR, "cvs_direct: zin: inflateEnd error: %s: %s", |
488 | + (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zin.msg ? ctx->zin.msg : ""); |
489 | + } |
490 | + |
491 | + debug(DEBUG_TCP, "cvs_direct: closing cvs server read connection %d", ctx->read_fd); |
492 | + close(ctx->read_fd); |
493 | + |
494 | + free(ctx); |
495 | +} |
496 | + |
497 | +static void get_cvspass(char * pass, const char * root) |
498 | +{ |
499 | + char cvspass[PATH_MAX]; |
500 | + const char * home; |
501 | + FILE * fp; |
502 | + |
503 | + pass[0] = 0; |
504 | + |
505 | + if (!(home = getenv("HOME"))) |
506 | + { |
507 | + debug(DEBUG_APPERROR, "HOME environment variable not set"); |
508 | + exit(1); |
509 | + } |
510 | + |
511 | + if (snprintf(cvspass, PATH_MAX, "%s/.cvspass", home) >= PATH_MAX) |
512 | + { |
513 | + debug(DEBUG_APPERROR, "prefix buffer overflow"); |
514 | + exit(1); |
515 | + } |
516 | + |
517 | + if ((fp = fopen(cvspass, "r"))) |
518 | + { |
519 | + char buff[BUFSIZ]; |
520 | + int len = strlen(root); |
521 | + |
522 | + while (fgets(buff, BUFSIZ, fp)) |
523 | + { |
524 | + /* FIXME: what does /1 mean? */ |
525 | + if (strncmp(buff, "/1 ", 3) != 0) |
526 | + continue; |
527 | + |
528 | + if (strncmp(buff + 3, root, len) == 0) |
529 | + { |
530 | + strcpy(pass, buff + 3 + len + 1); |
531 | + chop(pass); |
532 | + break; |
533 | + } |
534 | + |
535 | + } |
536 | + fclose(fp); |
537 | + } |
538 | + |
539 | + if (!pass[0]) |
540 | + pass[0] = 'A'; |
541 | +} |
542 | + |
543 | +static void send_string(CvsServerCtx * ctx, const char * str, ...) |
544 | +{ |
545 | + int len; |
546 | + char buff[BUFSIZ]; |
547 | + va_list ap; |
548 | + |
549 | + va_start(ap, str); |
550 | + |
551 | + len = vsnprintf(buff, BUFSIZ, str, ap); |
552 | + if (len >= BUFSIZ) |
553 | + { |
554 | + debug(DEBUG_APPERROR, "cvs_direct: command send string overflow"); |
555 | + exit(1); |
556 | + } |
557 | + |
558 | + if (ctx->compressed) |
559 | + { |
560 | + char zbuff[BUFSIZ]; |
561 | + |
562 | + if (ctx->zout.avail_in != 0) |
563 | + { |
564 | + debug(DEBUG_APPERROR, "cvs_direct: zout: last output command not flushed"); |
565 | + exit(1); |
566 | + } |
567 | + |
568 | + ctx->zout.next_in = buff; |
569 | + ctx->zout.avail_in = len; |
570 | + ctx->zout.avail_out = 0; |
571 | + |
572 | + while (ctx->zout.avail_in > 0 || ctx->zout.avail_out == 0) |
573 | + { |
574 | + int ret; |
575 | + |
576 | + ctx->zout.next_out = zbuff; |
577 | + ctx->zout.avail_out = BUFSIZ; |
578 | + |
579 | + /* FIXME: for the arguments before a command, flushing is counterproductive */ |
580 | + ret = deflate(&ctx->zout, Z_SYNC_FLUSH); |
581 | + |
582 | + if (ret == Z_OK) |
583 | + { |
584 | + len = BUFSIZ - ctx->zout.avail_out; |
585 | + |
586 | + if (writen(ctx->write_fd, zbuff, len) != len) |
587 | + { |
588 | + debug(DEBUG_SYSERROR, "cvs_direct: zout: can't write"); |
589 | + exit(1); |
590 | + } |
591 | + } |
592 | + else |
593 | + { |
594 | + debug(DEBUG_APPERROR, "cvs_direct: zout: error %d %s", ret, ctx->zout.msg); |
595 | + } |
596 | + } |
597 | + } |
598 | + else |
599 | + { |
600 | + if (writen(ctx->write_fd, buff, len) != len) |
601 | + { |
602 | + debug(DEBUG_SYSERROR, "cvs_direct: can't send command"); |
603 | + exit(1); |
604 | + } |
605 | + } |
606 | + |
607 | + debug(DEBUG_TCP, "string: '%s' sent", buff); |
608 | +} |
609 | + |
610 | +static int refill_buffer(CvsServerCtx * ctx) |
611 | +{ |
612 | + int len; |
613 | + |
614 | + if (ctx->head != ctx->tail) |
615 | + { |
616 | + debug(DEBUG_APPERROR, "cvs_direct: refill_buffer called on non-empty buffer"); |
617 | + exit(1); |
618 | + } |
619 | + |
620 | + ctx->head = ctx->read_buff; |
621 | + len = RD_BUFF_SIZE; |
622 | + |
623 | + if (ctx->compressed) |
624 | + { |
625 | + int zlen, ret; |
626 | + |
627 | + /* if there was leftover buffer room, it's time to slurp more data */ |
628 | + do |
629 | + { |
630 | + if (ctx->zin.avail_out > 0) |
631 | + { |
632 | + if (ctx->zin.avail_in != 0) |
633 | + { |
634 | + debug(DEBUG_APPERROR, "cvs_direct: zin: expect 0 avail_in"); |
635 | + exit(1); |
636 | + } |
637 | + zlen = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE); |
638 | + ctx->zin.next_in = ctx->zread_buff; |
639 | + ctx->zin.avail_in = zlen; |
640 | + } |
641 | + |
642 | + ctx->zin.next_out = ctx->head; |
643 | + ctx->zin.avail_out = len; |
644 | + |
645 | + /* FIXME: we don't always need Z_SYNC_FLUSH, do we? */ |
646 | + ret = inflate(&ctx->zin, Z_SYNC_FLUSH); |
647 | + } |
648 | + while (ctx->zin.avail_out == len); |
649 | + |
650 | + if (ret == Z_OK) |
651 | + { |
652 | + ctx->tail = ctx->head + (len - ctx->zin.avail_out); |
653 | + } |
654 | + else |
655 | + { |
656 | + debug(DEBUG_APPERROR, "cvs_direct: zin: error %d %s", ret, ctx->zin.msg); |
657 | + exit(1); |
658 | + } |
659 | + } |
660 | + else |
661 | + { |
662 | + len = read(ctx->read_fd, ctx->head, len); |
663 | + ctx->tail = (len <= 0) ? ctx->head : ctx->head + len; |
664 | + } |
665 | + |
666 | + return len; |
667 | +} |
668 | + |
669 | +static int read_line(CvsServerCtx * ctx, char * p) |
670 | +{ |
671 | + int len = 0; |
672 | + while (1) |
673 | + { |
674 | + if (ctx->head == ctx->tail) |
675 | + if (refill_buffer(ctx) <= 0) |
676 | + return -1; |
677 | + |
678 | + *p = *ctx->head++; |
679 | + |
680 | + if (*p == '\n') |
681 | + { |
682 | + *p = 0; |
683 | + break; |
684 | + } |
685 | + p++; |
686 | + len++; |
687 | + } |
688 | + |
689 | + return len; |
690 | +} |
691 | + |
692 | +static int read_response(CvsServerCtx * ctx, const char * str) |
693 | +{ |
694 | + /* FIXME: more than 1 char at a time */ |
695 | + char resp[BUFSIZ]; |
696 | + |
697 | + if (read_line(ctx, resp) < 0) |
698 | + return 0; |
699 | + |
700 | + debug(DEBUG_TCP, "response '%s' read", resp); |
701 | + |
702 | + return (strcmp(resp, str) == 0); |
703 | +} |
704 | + |
705 | +static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp) |
706 | +{ |
707 | + char line[BUFSIZ]; |
708 | + |
709 | + while (1) |
710 | + { |
711 | + read_line(ctx, line); |
712 | + debug(DEBUG_TCP, "ctx_to_fp: %s", line); |
713 | + if (memcmp(line, "M ", 2) == 0) |
714 | + { |
715 | + if (fp) |
716 | + fprintf(fp, "%s\n", line + 2); |
717 | + } |
718 | + else if (memcmp(line, "E ", 2) == 0) |
719 | + { |
720 | + debug(DEBUG_APPMSG1, "%s", line + 2); |
721 | + } |
722 | + else if (strncmp(line, "ok", 2) == 0 || strncmp(line, "error", 5) == 0) |
723 | + { |
724 | + break; |
725 | + } |
726 | + } |
727 | + |
728 | + if (fp) |
729 | + fflush(fp); |
730 | +} |
731 | + |
732 | +void cvs_rdiff(CvsServerCtx * ctx, |
733 | + const char * rep, const char * file, |
734 | + const char * rev1, const char * rev2) |
735 | +{ |
736 | + /* NOTE: opts are ignored for rdiff, '-u' is always used */ |
737 | + |
738 | + send_string(ctx, "Argument -u\n"); |
739 | + send_string(ctx, "Argument -r\n"); |
740 | + send_string(ctx, "Argument %s\n", rev1); |
741 | + send_string(ctx, "Argument -r\n"); |
742 | + send_string(ctx, "Argument %s\n", rev2); |
743 | + send_string(ctx, "Argument %s%s\n", rep, file); |
744 | + send_string(ctx, "rdiff\n"); |
745 | + |
746 | + ctx_to_fp(ctx, stdout); |
747 | +} |
748 | + |
749 | +void cvs_rupdate(CvsServerCtx * ctx, const char * rep, const char * file, const char * rev, int create, const char * opts) |
750 | +{ |
751 | + FILE * fp; |
752 | + char cmdbuff[BUFSIZ]; |
753 | + |
754 | + snprintf(cmdbuff, BUFSIZ, "diff %s %s /dev/null %s | sed -e '%s s|^\\([+-][+-][+-]\\) -|\\1 %s/%s|g'", |
755 | + opts, create?"":"-", create?"-":"", create?"2":"1", rep, file); |
756 | + |
757 | + debug(DEBUG_TCP, "cmdbuff: %s", cmdbuff); |
758 | + |
759 | + if (!(fp = popen(cmdbuff, "w"))) |
760 | + { |
761 | + debug(DEBUG_APPERROR, "cvs_direct: popen for diff failed: %s", cmdbuff); |
762 | + exit(1); |
763 | + } |
764 | + |
765 | + send_string(ctx, "Argument -p\n"); |
766 | + send_string(ctx, "Argument -r\n"); |
767 | + send_string(ctx, "Argument %s\n", rev); |
768 | + send_string(ctx, "Argument %s/%s\n", rep, file); |
769 | + send_string(ctx, "co\n"); |
770 | + |
771 | + ctx_to_fp(ctx, fp); |
772 | + |
773 | + pclose(fp); |
774 | +} |
775 | + |
776 | +static int parse_patch_arg(char * arg, char ** str) |
777 | +{ |
778 | + char *tok, *tok2 = ""; |
779 | + tok = strsep(str, " "); |
780 | + if (!tok) |
781 | + return 0; |
782 | + |
783 | + if (!*tok == '-') |
784 | + { |
785 | + debug(DEBUG_APPERROR, "diff_opts parse error: no '-' starting argument: %s", *str); |
786 | + return 0; |
787 | + } |
788 | + |
789 | + /* if it's not 'long format' argument, we can process it efficiently */ |
790 | + if (tok[1] == '-') |
791 | + { |
792 | + debug(DEBUG_APPERROR, "diff_opts parse_error: long format args not supported"); |
793 | + return 0; |
794 | + } |
795 | + |
796 | + /* see if command wants two args and they're separated by ' ' */ |
797 | + if (tok[2] == 0 && strchr("BdDFgiorVxYz", tok[1])) |
798 | + { |
799 | + tok2 = strsep(str, " "); |
800 | + if (!tok2) |
801 | + { |
802 | + debug(DEBUG_APPERROR, "diff_opts parse_error: argument %s requires two arguments", tok); |
803 | + return 0; |
804 | + } |
805 | + } |
806 | + |
807 | + snprintf(arg, 32, "%s%s", tok, tok2); |
808 | + return 1; |
809 | +} |
810 | + |
811 | +void cvs_diff(CvsServerCtx * ctx, |
812 | + const char * rep, const char * file, |
813 | + const char * rev1, const char * rev2, const char * opts) |
814 | +{ |
815 | + char argstr[BUFSIZ], *p = argstr; |
816 | + char arg[32]; |
817 | + char file_buff[PATH_MAX], *basename; |
818 | + |
819 | + strzncpy(argstr, opts, BUFSIZ); |
820 | + while (parse_patch_arg(arg, &p)) |
821 | + send_string(ctx, "Argument %s\n", arg); |
822 | + |
823 | + send_string(ctx, "Argument -r\n"); |
824 | + send_string(ctx, "Argument %s\n", rev1); |
825 | + send_string(ctx, "Argument -r\n"); |
826 | + send_string(ctx, "Argument %s\n", rev2); |
827 | + |
828 | + /* |
829 | + * we need to separate the 'basename' of file in order to |
830 | + * generate the Directory directive(s) |
831 | + */ |
832 | + strzncpy(file_buff, file, PATH_MAX); |
833 | + if ((basename = strrchr(file_buff, '/'))) |
834 | + { |
835 | + *basename = 0; |
836 | + send_string(ctx, "Directory %s/%s\n", rep, file_buff); |
837 | + send_string(ctx, "%s/%s/%s\n", ctx->root, rep, file_buff); |
838 | + } |
839 | + else |
840 | + { |
841 | + send_string(ctx, "Directory %s\n", rep, file_buff); |
842 | + send_string(ctx, "%s/%s\n", ctx->root, rep); |
843 | + } |
844 | + |
845 | + send_string(ctx, "Directory .\n"); |
846 | + send_string(ctx, "%s\n", ctx->root); |
847 | + send_string(ctx, "Argument %s/%s\n", rep, file); |
848 | + send_string(ctx, "diff\n"); |
849 | + |
850 | + ctx_to_fp(ctx, stdout); |
851 | +} |
852 | + |
853 | +/* |
854 | + * FIXME: the design of this sucks. It was originally designed to fork a subprocess |
855 | + * which read the cvs response and send it back through a pipe the main process, |
856 | + * which fdopen(3)ed the other end, and juts used regular fgets. This however |
857 | + * didn't work because the reads of compressed data in the child process altered |
858 | + * the compression state, and there was no way to resynchronize that state with |
859 | + * the parent process. We could use threads... |
860 | + */ |
861 | +FILE * cvs_rlog_open(CvsServerCtx * ctx, const char * rep, const char * date_str) |
862 | +{ |
863 | + /* note: use of the date_str is handled in a non-standard, cvsps specific way */ |
864 | + if (date_str && date_str[0]) |
865 | + { |
866 | + send_string(ctx, "Argument -d\n", rep); |
867 | + send_string(ctx, "Argument %s<1 Jan 2038 05:00:00 -0000\n", date_str); |
868 | + send_string(ctx, "Argument -d\n", rep); |
869 | + send_string(ctx, "Argument %s\n", date_str); |
870 | + } |
871 | + |
872 | + send_string(ctx, "Argument %s\n", rep); |
873 | + send_string(ctx, "rlog\n"); |
874 | + |
875 | + /* |
876 | + * FIXME: is it possible to create a 'fake' FILE * whose 'refill' |
877 | + * function is below? |
878 | + */ |
879 | + return (FILE*)ctx; |
880 | +} |
881 | + |
882 | +char * cvs_rlog_fgets(char * buff, int buflen, CvsServerCtx * ctx) |
883 | +{ |
884 | + char lbuff[BUFSIZ]; |
885 | + int len; |
886 | + |
887 | + len = read_line(ctx, lbuff); |
888 | + debug(DEBUG_TCP, "cvs_direct: rlog: read %s", lbuff); |
889 | + |
890 | + if (memcmp(lbuff, "M ", 2) == 0) |
891 | + { |
892 | + memcpy(buff, lbuff + 2, len - 2); |
893 | + buff[len - 2 ] = '\n'; |
894 | + buff[len - 1 ] = 0; |
895 | + } |
896 | + else if (memcmp(lbuff, "E ", 2) == 0) |
897 | + { |
898 | + debug(DEBUG_APPMSG1, "%s", lbuff + 2); |
899 | + } |
900 | + else if (strcmp(lbuff, "ok") == 0 ||strcmp(lbuff, "error") == 0) |
901 | + { |
902 | + debug(DEBUG_TCP, "cvs_direct: rlog: got command completion"); |
903 | + return NULL; |
904 | + } |
905 | + |
906 | + return buff; |
907 | +} |
908 | + |
909 | +void cvs_rlog_close(CvsServerCtx * ctx) |
910 | +{ |
911 | +} |
912 | + |
913 | +void cvs_version(CvsServerCtx * ctx, char * client_version, char * server_version) |
914 | +{ |
915 | + char lbuff[BUFSIZ]; |
916 | + strcpy(client_version, "Client: Concurrent Versions System (CVS) 99.99.99 (client/server) cvs-direct"); |
917 | + send_string(ctx, "version\n"); |
918 | + read_line(ctx, lbuff); |
919 | + if (memcmp(lbuff, "M ", 2) == 0) |
920 | + sprintf(server_version, "Server: %s", lbuff + 2); |
921 | + else |
922 | + debug(DEBUG_APPERROR, "cvs_direct: didn't read version: %s", lbuff); |
923 | + |
924 | + read_line(ctx, lbuff); |
925 | + if (strcmp(lbuff, "ok") != 0) |
926 | + debug(DEBUG_APPERROR, "cvs_direct: protocol error reading version"); |
927 | + |
928 | + debug(DEBUG_TCP, "cvs_direct: client version %s", client_version); |
929 | + debug(DEBUG_TCP, "cvs_direct: server version %s", server_version); |
930 | +} |
931 | |
932 | === modified file '.pc/applied-patches' |
933 | --- .pc/applied-patches 2011-04-07 23:18:19 +0000 |
934 | +++ .pc/applied-patches 2015-02-15 23:35:36 +0000 |
935 | @@ -2,3 +2,4 @@ |
936 | 02_dynamicbufferalloc.patch |
937 | 03_diffoptstypo.patch |
938 | 05-inet_addr_fix.patch |
939 | +06-discard-extra-version-lines.patch |
940 | |
941 | === modified file 'cvs_direct.c' |
942 | --- cvs_direct.c 2005-07-28 13:32:58 +0000 |
943 | +++ cvs_direct.c 2015-02-15 23:35:36 +0000 |
944 | @@ -916,7 +916,8 @@ |
945 | else |
946 | debug(DEBUG_APPERROR, "cvs_direct: didn't read version: %s", lbuff); |
947 | |
948 | - read_line(ctx, lbuff); |
949 | + while (strncmp(lbuff, "M ", 2) == 0) |
950 | + read_line(ctx, lbuff); |
951 | if (strcmp(lbuff, "ok") != 0) |
952 | debug(DEBUG_APPERROR, "cvs_direct: protocol error reading version"); |
953 | |
954 | |
955 | === modified file 'debian/changelog' |
956 | --- debian/changelog 2012-10-01 20:57:26 +0000 |
957 | +++ debian/changelog 2015-02-15 23:35:36 +0000 |
958 | @@ -1,3 +1,10 @@ |
959 | +cvsps (2.1-6ubuntu0.14.04.1) trusty; urgency=medium |
960 | + |
961 | + * Discard extra "M" response lines when reading the server's |
962 | + version. (LP: #1413084) |
963 | + |
964 | + -- Richard Hansen <ubuntu-a7x@scientician.org> Wed, 11 Feb 2015 12:13:52 -0800 |
965 | + |
966 | cvsps (2.1-6build1) quantal; urgency=low |
967 | |
968 | * Rebuild for new armel compiler default of ARMv5t. |
969 | |
970 | === modified file 'debian/control' |
971 | --- debian/control 2011-04-07 23:18:19 +0000 |
972 | +++ debian/control 2015-02-15 23:35:36 +0000 |
973 | @@ -1,7 +1,8 @@ |
974 | Source: cvsps |
975 | Section: devel |
976 | Priority: optional |
977 | -Maintainer: Debian QA Group <packages@qa.debian.org> |
978 | +Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
979 | +XSBC-Original-Maintainer: Debian QA Group <packages@qa.debian.org> |
980 | Build-Depends: debhelper (>= 7.0.50~), zlib1g-dev |
981 | Standards-Version: 3.8.0 |
982 | |
983 | |
984 | === added file 'debian/patches/06-discard-extra-version-lines.patch' |
985 | --- debian/patches/06-discard-extra-version-lines.patch 1970-01-01 00:00:00 +0000 |
986 | +++ debian/patches/06-discard-extra-version-lines.patch 2015-02-15 23:35:36 +0000 |
987 | @@ -0,0 +1,27 @@ |
988 | +Subject: Discard extra "M" lines in response to "version" |
989 | +From: Richard Hansen <ubuntu-a7x@scientician.org> |
990 | +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1413084 |
991 | +Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=775883 |
992 | + |
993 | +Some CVS servers print more than one "M" line in response to a |
994 | +"version" command. For example: |
995 | + |
996 | +Client: version |
997 | +Server: M Concurrent Versions System (CVS) 1.12.13 (client/server) |
998 | +Server: M with CVSACL Patch 1.2.5 (cvsacl.sourceforge.net) |
999 | +Server: ok |
1000 | + |
1001 | +This patch causes cvsps to consume all such lines rather than fail. |
1002 | + |
1003 | +--- a/cvs_direct.c |
1004 | ++++ b/cvs_direct.c |
1005 | +@@ -916,7 +916,8 @@ |
1006 | + else |
1007 | + debug(DEBUG_APPERROR, "cvs_direct: didn't read version: %s", lbuff); |
1008 | + |
1009 | +- read_line(ctx, lbuff); |
1010 | ++ while (strncmp(lbuff, "M ", 2) == 0) |
1011 | ++ read_line(ctx, lbuff); |
1012 | + if (strcmp(lbuff, "ok") != 0) |
1013 | + debug(DEBUG_APPERROR, "cvs_direct: protocol error reading version"); |
1014 | + |
1015 | |
1016 | === modified file 'debian/patches/series' |
1017 | --- debian/patches/series 2011-04-07 23:18:19 +0000 |
1018 | +++ debian/patches/series 2015-02-15 23:35:36 +0000 |
1019 | @@ -2,3 +2,4 @@ |
1020 | 02_dynamicbufferalloc.patch |
1021 | 03_diffoptstypo.patch |
1022 | 05-inet_addr_fix.patch |
1023 | +06-discard-extra-version-lines.patch |