2 * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc.
3 * See COPYING file for license information
13 #include <sys/socket.h>
16 #include "text_util.h"
17 #include "tcpsocket.h"
19 #include "cvsclient.h"
22 #define RD_BUFF_SIZE 4096
32 /* buffered reads from descriptor */
33 char read_buff[RD_BUFF_SIZE];
41 /* when reading compressed data, the compressed data buffer */
42 unsigned char zread_buff[RD_BUFF_SIZE];
45 static void get_cvspass(char *, const char *, int len);
46 static void send_string(CvsServerCtx *, const char *, ...);
47 static int read_response(CvsServerCtx *, const char *);
48 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp);
49 static int read_line(CvsServerCtx * ctx, char * p, int len);
51 static CvsServerCtx * open_ctx_pserver(CvsServerCtx *, const char *);
52 static CvsServerCtx * open_ctx_forked(CvsServerCtx *, const char *);
54 CvsServerCtx * open_cvs_server(char * p_root, int compress)
56 CvsServerCtx * ctx = (CvsServerCtx*)malloc(sizeof(*ctx));
58 char * p = root, *tok;
63 ctx->head = ctx->tail = ctx->read_buff;
64 ctx->read_fd = ctx->write_fd = -1;
65 ctx->compressed = false;
66 ctx->is_pserver = false;
70 memset(&ctx->zout, 0, sizeof(z_stream));
71 memset(&ctx->zin, 0, sizeof(z_stream));
74 * to 'prime' the reads, make it look like there was output
75 * room available (i.e. we have processed all pending compressed
78 ctx->zin.avail_out = 1;
80 if (deflateInit(&ctx->zout, compress) != Z_OK)
86 if (inflateInit(&ctx->zin) != Z_OK)
88 deflateEnd(&ctx->zout);
94 strcpy_a(root, p_root, PATH_MAX);
96 tok = strsep(&p, ":");
98 /* if root string looks like :pserver:... then the first token will be empty */
101 char * method = strsep(&p, ":");
102 if (strcmp(method, "pserver") == 0)
104 ctx = open_ctx_pserver(ctx, p);
106 else if (strstr("local:ext:fork:server", method))
108 /* handle all of these via fork, even local */
109 ctx = open_ctx_forked(ctx, p);
113 debug(DEBUG_APPERROR, "cvsclient: unsupported cvs access method: %s", method);
120 ctx = open_ctx_forked(ctx, p_root);
127 send_string(ctx, "Root %s\n", ctx->root);
129 /* this is taken from 1.11.1p1 trace - but with Mbinary removed. we can't handle it (yet!) */
130 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);
132 send_string(ctx, "valid-requests\n");
134 /* check for the commands we will issue */
135 read_line(ctx, buff, BUFSIZ);
136 if (strncmp(buff, "Valid-requests", 14) != 0)
138 debug(DEBUG_APPERROR, "cvsclient: bad response to valid-requests command");
139 close_cvs_server(ctx);
143 if (!strstr(buff, " version") ||
144 !strstr(buff, " rlog") ||
145 !strstr(buff, " diff") ||
146 !strstr(buff, " co"))
148 debug(DEBUG_APPERROR, "cvsclient: cvs server too old for cvsclient");
149 close_cvs_server(ctx);
153 read_line(ctx, buff, BUFSIZ);
154 if (strcmp(buff, "ok") != 0)
156 debug(DEBUG_APPERROR, "cvsclient: bad ok trailer to valid-requests command");
157 close_cvs_server(ctx);
161 /* this is myterious but 'mandatory' */
162 send_string(ctx, "UseUnchanged\n");
166 send_string(ctx, "Gzip-stream %d\n", compress);
167 ctx->compressed = true;
170 debug(DEBUG_APPMSG2, "cvsclient: initialized to CVSROOT %s", ctx->root);
176 static CvsServerCtx * open_ctx_pserver(CvsServerCtx * ctx, const char * p_root)
179 char full_root[PATH_MAX];
180 char * p = root, *tok, *tok2;
186 strcpy_a(root, p_root, PATH_MAX);
188 /* parse initial "user@server" portion of p. */
189 tok = strsep(&p, "@");
191 p += strcspn(p, ":/"); /* server part ends at first ':' or '/'. */
192 if (!tok || !tok2 || !strlen(tok) || 0 >= (p - tok2))
194 debug(DEBUG_APPERROR, "parse error on user@server in pserver");
199 memcpy(server, tok2, p - tok2);
200 server[p - tok2] = '\0';
202 /* p now points to ':' or '/' following server part. */
203 tok = strchr(p, '/'); /* find start of path */
206 debug(DEBUG_APPERROR, "parse error: expecting / in root");
210 if (*p == ':') /* port number specified. Ends at tok. */
213 memcpy(port, p, tok - p);
214 port[tok - p] = '\0';
218 strcpy(port, "2401");
221 /* Make p point to path component, starting with '/'. */
224 /* the line from .cvspass is fully qualified, so rebuild */
225 snprintf(full_root, PATH_MAX, ":pserver:%s@%s:%s%s", user, server, port, p);
226 get_cvspass(pass, full_root, BUFSIZ);
228 debug(DEBUG_TCP, "user:%s server:%s port:%s pass:%s full_root:%s", user, server, port, pass, full_root);
230 if ((ctx->read_fd = tcp_create_socket(REUSE_ADDR)) < 0)
233 ctx->write_fd = dup(ctx->read_fd);
234 if (ctx->write_fd < 0)
237 if (tcp_connect(ctx->read_fd, server, atoi(port)) < 0)
240 send_string(ctx, "BEGIN AUTH REQUEST\n");
241 send_string(ctx, "%s\n", p);
242 send_string(ctx, "%s\n", user);
243 send_string(ctx, "%s\n", pass);
244 send_string(ctx, "END AUTH REQUEST\n");
246 if (!read_response(ctx, "I LOVE YOU"))
249 strcpy_a(ctx->root, p, PATH_MAX);
250 ctx->is_pserver = true;
261 static CvsServerCtx * open_ctx_forked(CvsServerCtx * ctx, const char * p_root)
264 char * p = root, *tok, *rep;
265 char execcmd[PATH_MAX];
269 const char * cvs_server = getenv("CVS_SERVER");
274 strcpy_a(root, p_root, PATH_MAX);
276 /* if there's a ':', it's remote */
277 tok = strsep(&p, ":");
282 /* coverity[tainted_data] */
283 const char * cvs_rsh = getenv("CVS_RSH");
288 tok2 = strsep(&tok, "@");
291 snprintf(execcmd, PATH_MAX, "%s -l %s %s %s server", cvs_rsh, tok2, tok, cvs_server);
293 snprintf(execcmd, PATH_MAX, "%s %s %s server", cvs_rsh, tok2, cvs_server);
299 snprintf(execcmd, PATH_MAX, "%s server", cvs_server);
303 if (pipe(to_cvs) < 0)
305 debug(DEBUG_SYSERROR, "cvsclient: failed to create pipe to_cvs");
309 if (pipe(from_cvs) < 0)
311 debug(DEBUG_SYSERROR, "cvsclient: failed to create pipe from_cvs");
315 debug(DEBUG_TCP, "forked cmdline: %s", execcmd);
317 if ((pid = fork()) < 0)
319 debug(DEBUG_SYSERROR, "cvsclient: can't fork");
322 else if (pid == 0) /* child */
334 if (dup(to_cvs[0]) < 0) {
335 debug(DEBUG_APPERROR, "cvsclient: dup of input failed");
339 if (dup(from_cvs[1]) < 0) {
340 debug(DEBUG_APPERROR, "cvsclient: dup of output failed");
344 execv("/bin/sh",argp);
346 debug(DEBUG_APPERROR, "cvsclient: fatal: shouldn't be reached");
352 ctx->read_fd = from_cvs[0];
353 ctx->write_fd = to_cvs[1];
355 strcpy_a(ctx->root, rep, PATH_MAX);
370 void close_cvs_server(CvsServerCtx * ctx)
372 /* FIXME: some sort of flushing should be done for non-compressed case */
380 * there shouldn't be anything left, but we do want
381 * to send an 'end of stream' marker, (if such a thing
386 ctx->zout.next_out = (unsigned char *)buff;
387 ctx->zout.avail_out = BUFSIZ;
388 ret = deflate(&ctx->zout, Z_FINISH);
390 if ((ret == Z_OK || ret == Z_STREAM_END) && ctx->zout.avail_out != BUFSIZ)
392 len = BUFSIZ - ctx->zout.avail_out;
393 if (writen(ctx->write_fd, buff, len) != len)
394 debug(DEBUG_APPERROR, "cvsclient: zout: error writing final state");
396 //hexdump(buff, len, "cvsclient: zout: sending unsent data");
398 } while (ret == Z_OK);
400 if ((ret = deflateEnd(&ctx->zout)) != Z_OK)
401 debug(DEBUG_APPERROR, "cvsclient: zout: deflateEnd error: %s: %s",
402 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zout.msg);
405 /* we're done writing now */
406 debug(DEBUG_TCP, "cvsclient: closing cvs server write connection %d", ctx->write_fd);
407 close(ctx->write_fd);
410 * if this is pserver, then read_fd is a bi-directional socket.
411 * we want to shutdown the write side, just to make sure the
416 debug(DEBUG_TCP, "cvsclient: shutdown on read socket");
417 if (shutdown(ctx->read_fd, SHUT_WR) < 0)
418 debug(DEBUG_SYSERROR, "cvsclient: error with shutdown on pserver socket");
423 int ret = Z_OK, len, eof = 0;
424 unsigned char buff[BUFSIZ];
426 /* read to the 'eof'/'eos' marker. there are two states we
427 * track, looking for Z_STREAM_END (application level EOS)
428 * and EOF on socket. Both should happen at the same time,
429 * but we need to do the read first, the first time through
430 * the loop, but we want to do one read after getting Z_STREAM_END
431 * too. so this loop has really ugly exit conditions.
436 * if there's nothing in the avail_in, and we
437 * inflated everything last pass (avail_out != 0)
438 * then slurp some more from the descriptor,
439 * if we get EOF, exit the loop
441 if (ctx->zin.avail_in == 0 && ctx->zin.avail_out != 0)
443 debug(DEBUG_TCP, "cvsclient: doing final slurp");
444 len = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
445 debug(DEBUG_TCP, "cvsclient: did final slurp: %d", len);
453 /* put the data into the inflate input stream */
454 ctx->zin.next_in = ctx->zread_buff;
455 ctx->zin.avail_in = len;
459 * if the last time through we got Z_STREAM_END, and we
460 * get back here, it means we should've gotten EOF but
463 if (ret == Z_STREAM_END)
466 ctx->zin.next_out = buff;
467 ctx->zin.avail_out = BUFSIZ;
469 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
470 len = BUFSIZ - ctx->zin.avail_out;
472 if (ret == Z_BUF_ERROR)
473 debug(DEBUG_APPERROR, "Z_BUF_ERROR");
475 if (ret == Z_OK && len == 0)
476 debug(DEBUG_TCP, "cvsclient: no data out of inflate");
478 if (ret == Z_STREAM_END)
479 debug(DEBUG_TCP, "cvsclient: got Z_STREAM_END");
481 if ((ret == Z_OK || ret == Z_STREAM_END) && len > 0)
482 hexdump((char *)buff, BUFSIZ - ctx->zin.avail_out, "cvsclient: zin: unread data at close");
485 if (ret != Z_STREAM_END)
486 debug(DEBUG_APPERROR, "cvsclient: zin: Z_STREAM_END not encountered (premature EOF?)");
489 debug(DEBUG_APPERROR, "cvsclient: zin: EOF not encountered (premature Z_STREAM_END?)");
491 if ((ret = inflateEnd(&ctx->zin)) != Z_OK)
492 debug(DEBUG_APPERROR, "cvsclient: zin: inflateEnd error: %s: %s",
493 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zin.msg ? ctx->zin.msg : "");
496 debug(DEBUG_TCP, "cvsclient: closing cvs server read connection %d", ctx->read_fd);
502 static void get_cvspass(char * pass, const char * root, int passbuflen)
504 char cvspass[PATH_MAX];
510 if (!(home = getenv("HOME")))
512 debug(DEBUG_APPERROR, "HOME environment variable not set");
516 if (snprintf(cvspass, PATH_MAX, "%s/.cvspass", home) >= PATH_MAX)
518 debug(DEBUG_APPERROR, "prefix buffer overflow");
522 if ((fp = fopen(cvspass, "r")))
525 int len = strlen(root);
527 while (fgets(buff, BUFSIZ, fp))
529 /* FIXME: what does /1 mean? */
530 if (strncmp(buff, "/1 ", 3) != 0)
533 if (strncmp(buff + 3, root, len) == 0)
535 strcpy_a(pass, buff + 3 + len + 1, passbuflen);
548 static void send_string(CvsServerCtx * ctx, const char * str, ...)
551 unsigned char buff[BUFSIZ];
555 len = vsnprintf((char *)buff, BUFSIZ, str, ap);
560 debug(DEBUG_APPERROR, "cvsclient: command send string overflow");
566 unsigned char zbuff[BUFSIZ];
568 if (ctx->zout.avail_in != 0)
570 debug(DEBUG_APPERROR, "cvsclient: zout: last output command not flushed");
574 ctx->zout.next_in = buff;
575 ctx->zout.avail_in = len;
576 ctx->zout.avail_out = 0;
578 while (ctx->zout.avail_in > 0 || ctx->zout.avail_out == 0)
582 ctx->zout.next_out = zbuff;
583 ctx->zout.avail_out = BUFSIZ;
585 /* FIXME: for the arguments before a command, flushing is counterproductive */
586 ret = deflate(&ctx->zout, Z_SYNC_FLUSH);
590 len = BUFSIZ - ctx->zout.avail_out;
592 if (writen(ctx->write_fd, zbuff, len) != len)
594 debug(DEBUG_SYSERROR, "cvsclient: zout: can't write");
600 debug(DEBUG_APPERROR, "cvsclient: zout: error %d %s", ret, ctx->zout.msg);
606 if (writen(ctx->write_fd, buff, len) != len)
608 debug(DEBUG_SYSERROR, "cvsclient: can't send command");
613 debug(DEBUG_TCP, "string: '%s' sent", buff);
616 static int refill_buffer(CvsServerCtx * ctx)
620 if (ctx->head != ctx->tail)
622 debug(DEBUG_APPERROR, "cvsclient: refill_buffer called on non-empty buffer");
626 ctx->head = ctx->read_buff;
633 /* if there was leftover buffer room, it's time to slurp more data */
636 if (ctx->zin.avail_out > 0)
638 if (ctx->zin.avail_in != 0)
640 debug(DEBUG_APPERROR, "cvsclient: zin: expect 0 avail_in");
643 zlen = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
644 ctx->zin.next_in = ctx->zread_buff;
645 ctx->zin.avail_in = zlen;
648 ctx->zin.next_out = (unsigned char *)ctx->head;
649 ctx->zin.avail_out = len;
651 /* FIXME: we don't always need Z_SYNC_FLUSH, do we? */
652 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
654 while (ctx->zin.avail_out == len);
658 ctx->tail = ctx->head + (len - ctx->zin.avail_out);
662 debug(DEBUG_APPERROR, "cvsclient: zin: error %d %s", ret, ctx->zin.msg);
668 len = read(ctx->read_fd, ctx->head, len);
669 ctx->tail = (len <= 0) ? ctx->head : ctx->head + len;
675 static int read_line(CvsServerCtx * ctx, char * p, int maxlen)
684 if (ctx->head == ctx->tail)
685 if (refill_buffer(ctx) <= 0)
688 /* break out without advancing head if buffer is exhausted */
689 if (maxlen == 1 || (*p = *ctx->head++) == '\n')
703 static int read_response(CvsServerCtx * ctx, const char * str)
705 /* FIXME: more than 1 char at a time */
708 if (read_line(ctx, resp, BUFSIZ) < 0)
711 debug(DEBUG_TCP, "response '%s' read", resp);
713 return (strcmp(resp, str) == 0);
716 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp)
722 read_line(ctx, line, BUFSIZ);
723 debug(DEBUG_TCP, "ctx_to_fp: %s", line);
724 if (memcmp(line, "M ", 2) == 0)
727 fprintf(fp, "%s\n", line + 2);
729 else if (memcmp(line, "E ", 2) == 0)
731 debug(DEBUG_TCP, "%s", line + 2);
733 else if (strncmp(line, "ok", 2) == 0 || strncmp(line, "error", 5) == 0)
743 void cvs_rdiff(CvsServerCtx * ctx,
744 const char * rep, const char * file,
745 const char * rev1, const char * rev2)
747 /* NOTE: opts are ignored for rdiff, '-u' is always used */
749 send_string(ctx, "Argument -u\n");
750 send_string(ctx, "Argument -r\n");
751 send_string(ctx, "Argument %s\n", rev1);
752 send_string(ctx, "Argument -r\n");
753 send_string(ctx, "Argument %s\n", rev2);
754 send_string(ctx, "Argument %s%s\n", rep, file);
755 send_string(ctx, "rdiff\n");
757 ctx_to_fp(ctx, stdout);
760 void cvs_rupdate(CvsServerCtx * ctx, const char * rep, const char * file, const char * rev, FILE *fp)
762 send_string(ctx, "Argument -kk\n");
763 send_string(ctx, "Argument -p\n");
764 send_string(ctx, "Argument -r\n");
765 send_string(ctx, "Argument %s\n", rev);
766 send_string(ctx, "Argument %s/%s\n", rep, file);
767 send_string(ctx, "co\n");
772 static bool parse_patch_arg(char * arg, char ** str)
774 char *tok, *tok2 = "";
775 tok = strsep(str, " ");
781 debug(DEBUG_APPERROR, "diff_opts parse error: no '-' starting argument: %s", *str);
785 /* if it's not 'long format' argument, we can process it efficiently */
788 debug(DEBUG_APPERROR, "diff_opts parse_error: long format args not supported");
792 /* see if command wants two args and they're separated by ' ' */
793 if (tok[2] == 0 && strchr("BdDFgiorVxYz", tok[1]))
795 tok2 = strsep(str, " ");
798 debug(DEBUG_APPERROR, "diff_opts parse_error: argument %s requires two arguments", tok);
803 snprintf(arg, 32, "%s%s", tok, tok2);
807 void cvs_diff(CvsServerCtx * ctx,
808 const char * rep, const char * file,
809 const char * rev1, const char * rev2, const char * opts)
811 char argstr[BUFSIZ], *p = argstr;
813 char file_buff[PATH_MAX], *basename;
815 strzncpy(argstr, opts, BUFSIZ);
816 while (parse_patch_arg(arg, &p))
817 send_string(ctx, "Argument %s\n", arg);
819 send_string(ctx, "Argument -r\n");
820 send_string(ctx, "Argument %s\n", rev1);
821 send_string(ctx, "Argument -r\n");
822 send_string(ctx, "Argument %s\n", rev2);
825 * we need to separate the 'basename' of file in order to
826 * generate the Directory directive(s)
828 strzncpy(file_buff, file, PATH_MAX);
829 if ((basename = strrchr(file_buff, '/')))
832 send_string(ctx, "Directory %s/%s\n", rep, file_buff);
833 send_string(ctx, "%s/%s/%s\n", ctx->root, rep, file_buff);
837 send_string(ctx, "Directory %s\n", rep, file_buff);
838 send_string(ctx, "%s/%s\n", ctx->root, rep);
841 send_string(ctx, "Directory .\n");
842 send_string(ctx, "%s\n", ctx->root);
843 send_string(ctx, "Argument %s/%s\n", rep, file);
844 send_string(ctx, "diff\n");
846 ctx_to_fp(ctx, stdout);
850 * FIXME: the design of this sucks. It was originally designed to fork a subprocess
851 * which read the cvs response and send it back through a pipe the main process,
852 * which fdopen(3)ed the other end, and just used regular fgets. This however
853 * didn't work because the reads of compressed data in the child process altered
854 * the compression state, and there was no way to resynchronize that state with
855 * the parent process. We could use threads...
857 FILE * cvs_rlog_open(CvsServerCtx * ctx, const char * rep, const char * date_str)
859 /* note: use of the date_str is handled in a non-standard, cvsps specific way */
860 if (date_str && date_str[0])
862 send_string(ctx, "Argument -d\n", rep);
863 send_string(ctx, "Argument %s<1 Jan 2038 05:00:00 -0000\n", date_str);
864 send_string(ctx, "Argument -d\n", rep);
865 send_string(ctx, "Argument %s\n", date_str);
868 send_string(ctx, "Argument %s\n", rep);
869 send_string(ctx, "rlog\n");
872 * FIXME: is it possible to create a 'fake' FILE * whose 'refill'
878 char * cvs_rlog_fgets(char * buff, int buflen, CvsServerCtx * ctx)
883 len = read_line(ctx, lbuff, BUFSIZ);
884 debug(DEBUG_TCP, "cvsclient: rlog: read %s", lbuff);
886 if (memcmp(lbuff, "M ", 2) == 0)
888 memcpy(buff, lbuff + 2, len - 2);
889 buff[len - 2 ] = '\n';
892 else if (memcmp(lbuff, "E ", 2) == 0)
894 debug(DEBUG_TCP, "%s", lbuff + 2);
896 else if (strcmp(lbuff, "ok") == 0 || strncmp(lbuff, "error", 5) == 0)
898 debug(DEBUG_TCP, "cvsclient: rlog: got command completion");
905 void cvs_rlog_close(CvsServerCtx * ctx)
909 void cvs_version(CvsServerCtx * ctx, char * client_version, char * server_version, int cvlen, int svlen)
912 strcpy_a(client_version, "Client: Concurrent Versions System (CVS) 99.99.99 (client/server) cvs-direct", cvlen);
913 send_string(ctx, "version\n");
914 read_line(ctx, lbuff, BUFSIZ);
915 if (memcmp(lbuff, "M ", 2) == 0)
916 snprintf(server_version, svlen, "Server: %s", lbuff + 2);
918 debug(DEBUG_APPERROR, "cvsclient: didn't read version: %s", lbuff);
920 read_line(ctx, lbuff, BUFSIZ);
921 if (strstr(lbuff,"CVSACL")!=NULL) {
922 read_line(ctx, lbuff, BUFSIZ);
924 if (strcmp(lbuff, "ok") != 0)
925 debug(DEBUG_APPERROR, "cvsclient: protocol error reading version");
927 debug(DEBUG_TCP, "cvsclient: client version %s", client_version);
928 debug(DEBUG_TCP, "cvsclient: server version %s", server_version);