Merge lp:~rhansen/ubuntu/trusty/cvsps/bug1413084 into lp:ubuntu/trusty/cvsps

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
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+249787@code.launchpad.net

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

Subscribers

People subscribed via source and target branches