2 * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc.
3 * See COPYING file for license information
13 #include <sys/socket.h>
16 #include "tcpsocket.h"
18 #include "cvsclient.h"
21 #define RD_BUFF_SIZE 4096
31 /* buffered reads from descriptor */
32 char read_buff[RD_BUFF_SIZE];
40 /* when reading compressed data, the compressed data buffer */
41 unsigned char zread_buff[RD_BUFF_SIZE];
44 static void get_cvspass(char *, const char *, int len);
45 static void send_string(CvsServerCtx *, const char *, ...);
46 static int read_response(CvsServerCtx *, const char *);
47 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp);
48 static int read_line(CvsServerCtx * ctx, char * p, int len);
50 static CvsServerCtx * open_ctx_pserver(CvsServerCtx *, const char *);
51 static CvsServerCtx * open_ctx_forked(CvsServerCtx *, const char *);
53 CvsServerCtx * open_cvs_server(char * p_root, int compress)
55 CvsServerCtx * ctx = (CvsServerCtx*)malloc(sizeof(*ctx));
57 char * p = root, *tok;
62 ctx->head = ctx->tail = ctx->read_buff;
63 ctx->read_fd = ctx->write_fd = -1;
64 ctx->compressed = false;
65 ctx->is_pserver = false;
69 memset(&ctx->zout, 0, sizeof(z_stream));
70 memset(&ctx->zin, 0, sizeof(z_stream));
73 * to 'prime' the reads, make it look like there was output
74 * room available (i.e. we have processed all pending compressed
77 ctx->zin.avail_out = 1;
79 if (deflateInit(&ctx->zout, compress) != Z_OK)
85 if (inflateInit(&ctx->zin) != Z_OK)
87 deflateEnd(&ctx->zout);
93 strcpy_a(root, p_root, PATH_MAX);
95 tok = strsep(&p, ":");
97 /* if root string looks like :pserver:... then the first token will be empty */
100 char * method = strsep(&p, ":");
101 if (strcmp(method, "pserver") == 0)
103 ctx = open_ctx_pserver(ctx, p);
105 else if (strstr("local:ext:fork:server", method))
107 /* handle all of these via fork, even local */
108 ctx = open_ctx_forked(ctx, p);
112 debug(DEBUG_APPERROR, "cvsclient: unsupported cvs access method: %s", method);
119 ctx = open_ctx_forked(ctx, p_root);
126 send_string(ctx, "Root %s\n", ctx->root);
128 /* this is taken from 1.11.1p1 trace - but with Mbinary removed. we can't handle it (yet!) */
129 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);
131 send_string(ctx, "valid-requests\n");
133 /* check for the commands we will issue */
134 read_line(ctx, buff, BUFSIZ);
135 if (strncmp(buff, "Valid-requests", 14) != 0)
137 debug(DEBUG_APPERROR, "cvsclient: bad response '%s' to valid-requests command", buff);
138 close_cvs_server(ctx);
142 if (!strstr(buff, " version") ||
143 !strstr(buff, " rlog") ||
144 !strstr(buff, " diff") ||
145 !strstr(buff, " co"))
147 debug(DEBUG_APPERROR, "cvsclient: cvs server too old for cvsclient");
148 close_cvs_server(ctx);
152 read_line(ctx, buff, BUFSIZ);
153 if (strcmp(buff, "ok") != 0)
155 debug(DEBUG_APPERROR, "cvsclient: bad ok trailer to valid-requests command");
156 close_cvs_server(ctx);
160 /* this is myterious but 'mandatory' */
161 send_string(ctx, "UseUnchanged\n");
165 send_string(ctx, "Gzip-stream %d\n", compress);
166 ctx->compressed = true;
169 debug(DEBUG_STATUS, "cvsclient: initialized to CVSROOT %s", ctx->root);
175 static CvsServerCtx * open_ctx_pserver(CvsServerCtx * ctx, const char * p_root)
178 char full_root[PATH_MAX];
179 char * p = root, *tok, *tok2;
185 strcpy_a(root, p_root, PATH_MAX);
187 /* parse initial "user@server" portion of p. */
188 tok = strsep(&p, "@");
190 p += strcspn(p, ":/"); /* server part ends at first ':' or '/'. */
191 if (!tok || !tok2 || !strlen(tok) || 0 >= (p - tok2))
193 debug(DEBUG_APPERROR, "parse error on user@server in pserver");
198 memcpy(server, tok2, p - tok2);
199 server[p - tok2] = '\0';
201 /* p now points to ':' or '/' following server part. */
202 tok = strchr(p, '/'); /* find start of path */
205 debug(DEBUG_APPERROR, "parse error: expecting / in root");
209 if (*p == ':') /* port number specified. Ends at tok. */
212 memcpy(port, p, tok - p);
213 port[tok - p] = '\0';
217 strcpy(port, "2401");
220 /* Make p point to path component, starting with '/'. */
223 /* the line from .cvspass is fully qualified, so rebuild */
224 snprintf(full_root, PATH_MAX, ":pserver:%s@%s:%s%s", user, server, port, p);
225 get_cvspass(pass, full_root, BUFSIZ);
227 debug(DEBUG_TCP, "user:%s server:%s port:%s pass:%s full_root:%s", user, server, port, pass, full_root);
229 if ((ctx->read_fd = tcp_create_socket(REUSE_ADDR)) < 0)
232 ctx->write_fd = dup(ctx->read_fd);
233 if (ctx->write_fd < 0)
236 if (tcp_connect(ctx->read_fd, server, atoi(port)) < 0)
239 send_string(ctx, "BEGIN AUTH REQUEST\n");
240 send_string(ctx, "%s\n", p);
241 send_string(ctx, "%s\n", user);
242 send_string(ctx, "%s\n", pass);
243 send_string(ctx, "END AUTH REQUEST\n");
245 if (!read_response(ctx, "I LOVE YOU"))
248 strcpy_a(ctx->root, p, PATH_MAX);
249 ctx->is_pserver = true;
260 static CvsServerCtx * open_ctx_forked(CvsServerCtx * ctx, const char * p_root)
263 char * p = root, *tok, *rep;
264 char execcmd[PATH_MAX];
268 const char * cvs_server = getenv("CVS_SERVER");
273 strcpy_a(root, p_root, PATH_MAX);
275 /* if there's a ':', it's remote */
276 tok = strsep(&p, ":");
281 /* coverity[tainted_data] */
282 const char * cvs_rsh = getenv("CVS_RSH");
287 tok2 = strsep(&tok, "@");
290 snprintf(execcmd, PATH_MAX, "%s -l %s %s %s server", cvs_rsh, tok2, tok, cvs_server);
292 snprintf(execcmd, PATH_MAX, "%s %s %s server", cvs_rsh, tok2, cvs_server);
298 snprintf(execcmd, PATH_MAX, "%s server", cvs_server);
302 if (pipe(to_cvs) < 0)
304 debug(DEBUG_SYSERROR, "cvsclient: failed to create pipe to_cvs");
308 if (pipe(from_cvs) < 0)
310 debug(DEBUG_SYSERROR, "cvsclient: failed to create pipe from_cvs");
314 debug(DEBUG_TCP, "forked cmdline: %s", execcmd);
316 if ((pid = fork()) < 0)
318 debug(DEBUG_SYSERROR, "cvsclient: can't fork");
321 else if (pid == 0) /* child */
333 if (dup(to_cvs[0]) < 0) {
334 debug(DEBUG_APPERROR, "cvsclient: dup of input failed");
338 if (dup(from_cvs[1]) < 0) {
339 debug(DEBUG_APPERROR, "cvsclient: dup of output failed");
343 execv("/bin/sh",argp);
345 debug(DEBUG_APPERROR, "cvsclient: fatal: shouldn't be reached");
351 ctx->read_fd = from_cvs[0];
352 ctx->write_fd = to_cvs[1];
354 strcpy_a(ctx->root, rep, PATH_MAX);
369 void close_cvs_server(CvsServerCtx * ctx)
371 /* FIXME: some sort of flushing should be done for non-compressed case */
379 * there shouldn't be anything left, but we do want
380 * to send an 'end of stream' marker, (if such a thing
385 ctx->zout.next_out = (unsigned char *)buff;
386 ctx->zout.avail_out = BUFSIZ;
387 ret = deflate(&ctx->zout, Z_FINISH);
389 if ((ret == Z_OK || ret == Z_STREAM_END) && ctx->zout.avail_out != BUFSIZ)
391 len = BUFSIZ - ctx->zout.avail_out;
392 if (writen(ctx->write_fd, buff, len) != len)
393 debug(DEBUG_APPERROR, "cvsclient: zout: error writing final state");
395 //hexdump(buff, len, "cvsclient: zout: sending unsent data");
397 } while (ret == Z_OK);
399 if ((ret = deflateEnd(&ctx->zout)) != Z_OK)
400 debug(DEBUG_APPERROR, "cvsclient: zout: deflateEnd error: %s: %s",
401 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zout.msg);
404 /* we're done writing now */
405 debug(DEBUG_TCP, "cvsclient: closing cvs server write connection %d", ctx->write_fd);
406 close(ctx->write_fd);
409 * if this is pserver, then read_fd is a bi-directional socket.
410 * we want to shutdown the write side, just to make sure the
415 debug(DEBUG_TCP, "cvsclient: shutdown on read socket");
416 if (shutdown(ctx->read_fd, SHUT_WR) < 0)
417 debug(DEBUG_SYSERROR, "cvsclient: error with shutdown on pserver socket");
422 int ret = Z_OK, len, eof = 0;
423 unsigned char buff[BUFSIZ];
425 /* read to the 'eof'/'eos' marker. there are two states we
426 * track, looking for Z_STREAM_END (application level EOS)
427 * and EOF on socket. Both should happen at the same time,
428 * but we need to do the read first, the first time through
429 * the loop, but we want to do one read after getting Z_STREAM_END
430 * too. so this loop has really ugly exit conditions.
435 * if there's nothing in the avail_in, and we
436 * inflated everything last pass (avail_out != 0)
437 * then slurp some more from the descriptor,
438 * if we get EOF, exit the loop
440 if (ctx->zin.avail_in == 0 && ctx->zin.avail_out != 0)
442 debug(DEBUG_TCP, "cvsclient: doing final slurp");
443 len = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
444 debug(DEBUG_TCP, "cvsclient: did final slurp: %d", len);
452 /* put the data into the inflate input stream */
453 ctx->zin.next_in = ctx->zread_buff;
454 ctx->zin.avail_in = len;
458 * if the last time through we got Z_STREAM_END, and we
459 * get back here, it means we should've gotten EOF but
462 if (ret == Z_STREAM_END)
465 ctx->zin.next_out = buff;
466 ctx->zin.avail_out = BUFSIZ;
468 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
469 len = BUFSIZ - ctx->zin.avail_out;
471 if (ret == Z_BUF_ERROR)
472 debug(DEBUG_APPERROR, "Z_BUF_ERROR");
474 if (ret == Z_OK && len == 0)
475 debug(DEBUG_TCP, "cvsclient: no data out of inflate");
477 if (ret == Z_STREAM_END)
478 debug(DEBUG_TCP, "cvsclient: got Z_STREAM_END");
480 if ((ret == Z_OK || ret == Z_STREAM_END) && len > 0)
481 hexdump((char *)buff, BUFSIZ - ctx->zin.avail_out, "cvsclient: zin: unread data at close");
484 if (ret != Z_STREAM_END)
485 debug(DEBUG_APPERROR, "cvsclient: zin: Z_STREAM_END not encountered (premature EOF?)");
488 debug(DEBUG_APPERROR, "cvsclient: zin: EOF not encountered (premature Z_STREAM_END?)");
490 if ((ret = inflateEnd(&ctx->zin)) != Z_OK)
491 debug(DEBUG_APPERROR, "cvsclient: zin: inflateEnd error: %s: %s",
492 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zin.msg ? ctx->zin.msg : "");
495 debug(DEBUG_TCP, "cvsclient: closing cvs server read connection %d", ctx->read_fd);
501 static void get_cvspass(char * pass, const char * root, int passbuflen)
503 char cvspass[PATH_MAX];
509 if (!(home = getenv("HOME")))
511 debug(DEBUG_APPERROR, "HOME environment variable not set");
515 if (snprintf(cvspass, PATH_MAX, "%s/.cvspass", home) >= PATH_MAX)
517 debug(DEBUG_APPERROR, "prefix buffer overflow");
521 if ((fp = fopen(cvspass, "r")))
524 int len = strlen(root);
526 while (fgets(buff, BUFSIZ, fp))
528 /* FIXME: what does /1 mean? */
529 if (strncmp(buff, "/1 ", 3) != 0)
532 if (strncmp(buff + 3, root, len) == 0)
534 strcpy_a(pass, buff + 3 + len + 1, passbuflen);
547 static void send_string(CvsServerCtx * ctx, const char * str, ...)
550 unsigned char buff[BUFSIZ];
554 len = vsnprintf((char *)buff, BUFSIZ, str, ap);
559 debug(DEBUG_APPERROR, "cvsclient: command send string overflow");
565 unsigned char zbuff[BUFSIZ];
567 if (ctx->zout.avail_in != 0)
569 debug(DEBUG_APPERROR, "cvsclient: zout: last output command not flushed");
573 ctx->zout.next_in = buff;
574 ctx->zout.avail_in = len;
575 ctx->zout.avail_out = 0;
577 while (ctx->zout.avail_in > 0 || ctx->zout.avail_out == 0)
581 ctx->zout.next_out = zbuff;
582 ctx->zout.avail_out = BUFSIZ;
584 /* FIXME: for the arguments before a command, flushing is counterproductive */
585 ret = deflate(&ctx->zout, Z_SYNC_FLUSH);
589 len = BUFSIZ - ctx->zout.avail_out;
591 if (writen(ctx->write_fd, zbuff, len) != len)
593 debug(DEBUG_SYSERROR, "cvsclient: zout: can't write");
599 debug(DEBUG_APPERROR, "cvsclient: zout: error %d %s", ret, ctx->zout.msg);
605 if (writen(ctx->write_fd, buff, len) != len)
607 debug(DEBUG_SYSERROR, "cvsclient: can't send command");
612 debug(DEBUG_TCP, "string: '%s' sent", buff);
615 static int refill_buffer(CvsServerCtx * ctx)
619 if (ctx->head != ctx->tail)
621 debug(DEBUG_APPERROR, "cvsclient: refill_buffer called on non-empty buffer");
625 ctx->head = ctx->read_buff;
632 /* if there was leftover buffer room, it's time to slurp more data */
635 if (ctx->zin.avail_out > 0)
637 if (ctx->zin.avail_in != 0)
639 debug(DEBUG_APPERROR, "cvsclient: zin: expect 0 avail_in");
642 zlen = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
643 ctx->zin.next_in = ctx->zread_buff;
644 ctx->zin.avail_in = zlen;
647 ctx->zin.next_out = (unsigned char *)ctx->head;
648 ctx->zin.avail_out = len;
650 /* FIXME: we don't always need Z_SYNC_FLUSH, do we? */
651 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
653 while (ctx->zin.avail_out == len);
657 ctx->tail = ctx->head + (len - ctx->zin.avail_out);
661 debug(DEBUG_APPERROR, "cvsclient: zin: error %d %s", ret, ctx->zin.msg);
667 len = read(ctx->read_fd, ctx->head, len);
668 ctx->tail = (len <= 0) ? ctx->head : ctx->head + len;
674 static int read_line(CvsServerCtx * ctx, char * p, int maxlen)
683 if (ctx->head == ctx->tail)
684 if (refill_buffer(ctx) <= 0)
687 /* break out without advancing head if buffer is exhausted */
688 if (maxlen == 1 || (*p = *ctx->head++) == '\n')
702 static int read_response(CvsServerCtx * ctx, const char * str)
704 /* FIXME: more than 1 char at a time */
707 if (read_line(ctx, resp, BUFSIZ) < 0)
710 debug(DEBUG_TCP, "response '%s' read", resp);
712 return (strcmp(resp, str) == 0);
715 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp)
721 read_line(ctx, line, BUFSIZ);
722 debug(DEBUG_TCP, "ctx_to_fp: %s", line);
723 if (memcmp(line, "M ", 2) == 0)
726 fprintf(fp, "%s\n", line + 2);
728 else if (memcmp(line, "E ", 2) == 0)
730 debug(DEBUG_TCP, "%s", line + 2);
732 else if (strncmp(line, "ok", 2) == 0 || strncmp(line, "error", 5) == 0)
742 void cvs_rdiff(CvsServerCtx * ctx,
743 const char * rep, const char * file,
744 const char * rev1, const char * rev2)
746 /* NOTE: opts are ignored for rdiff, '-u' is always used */
748 send_string(ctx, "Argument -u\n");
749 send_string(ctx, "Argument -r\n");
750 send_string(ctx, "Argument %s\n", rev1);
751 send_string(ctx, "Argument -r\n");
752 send_string(ctx, "Argument %s\n", rev2);
753 send_string(ctx, "Argument %s%s\n", rep, file);
754 send_string(ctx, "rdiff\n");
756 ctx_to_fp(ctx, stdout);
759 void cvs_update(CvsServerCtx * ctx, const char * rep, const char * file, const char * rev, bool kk, 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)
859 send_string(ctx, "Argument %s\n", rep);
860 send_string(ctx, "rlog\n");
863 * FIXME: is it possible to create a 'fake' FILE * whose 'refill'
869 char * cvs_rlog_fgets(char * buff, int buflen, CvsServerCtx * ctx)
874 len = read_line(ctx, lbuff, BUFSIZ);
875 debug(DEBUG_TCP, "cvsclient: rlog: read %s", lbuff);
877 if (memcmp(lbuff, "M ", 2) == 0)
879 memcpy(buff, lbuff + 2, len - 2);
880 buff[len - 2 ] = '\n';
883 else if (memcmp(lbuff, "E ", 2) == 0)
885 debug(DEBUG_TCP, "%s", lbuff + 2);
887 else if (strcmp(lbuff, "ok") == 0 || strncmp(lbuff, "error", 5) == 0)
889 debug(DEBUG_TCP, "cvsclient: rlog: got command completion");
896 void cvs_rlog_close(CvsServerCtx * ctx)
900 void cvs_version(CvsServerCtx * ctx, char * client_version, char * server_version, int cvlen, int svlen)
903 strcpy_a(client_version, "Client: Concurrent Versions System (CVS) 99.99.99 (client/server) cvs-direct", cvlen);
904 send_string(ctx, "version\n");
905 read_line(ctx, lbuff, BUFSIZ);
906 if (memcmp(lbuff, "M ", 2) == 0)
907 snprintf(server_version, svlen, "Server: %s", lbuff + 2);
909 debug(DEBUG_APPERROR, "cvsclient: didn't read version: %s", lbuff);
911 read_line(ctx, lbuff, BUFSIZ);
912 if (strstr(lbuff,"CVSACL")!=NULL) {
913 read_line(ctx, lbuff, BUFSIZ);
915 if (strcmp(lbuff, "ok") != 0)
916 debug(DEBUG_APPERROR, "cvsclient: protocol error reading version");
918 debug(DEBUG_TCP, "cvsclient: client version %s", client_version);
919 debug(DEBUG_TCP, "cvsclient: server version %s", server_version);