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, "@");
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';
217 debug(DEBUG_APPERROR, "parse error: empty port number, probably from extraneous ':'.");
223 strcpy(port, "2401");
226 /* Make p point to path component, starting with '/'. */
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, BUFSIZ);
233 debug(DEBUG_TCP, "user:%s server:%s port:%s pass:%s full_root:%s", user, server, port, pass, full_root);
235 if ((ctx->read_fd = tcp_create_socket(REUSE_ADDR)) < 0)
238 ctx->write_fd = dup(ctx->read_fd);
239 if (ctx->write_fd < 0)
242 if (tcp_connect(ctx->read_fd, server, atoi(port)) < 0)
245 send_string(ctx, "BEGIN AUTH REQUEST\n");
246 send_string(ctx, "%s\n", p);
247 send_string(ctx, "%s\n", user);
248 send_string(ctx, "%s\n", pass);
249 send_string(ctx, "END AUTH REQUEST\n");
251 if (!read_response(ctx, "I LOVE YOU"))
254 strcpy_a(ctx->root, p, PATH_MAX);
255 ctx->is_pserver = true;
266 static CvsServerCtx * open_ctx_forked(CvsServerCtx * ctx, const char * p_root)
269 char * p = root, *tok, *rep;
270 char execcmd[PATH_MAX];
274 const char * cvs_server = getenv("CVS_SERVER");
279 strcpy_a(root, p_root, PATH_MAX);
281 /* if there's a ':', it's remote */
282 tok = strsep(&p, ":");
287 /* coverity[tainted_data] */
288 const char * cvs_rsh = getenv("CVS_RSH");
293 tok2 = strsep(&tok, "@");
296 snprintf(execcmd, PATH_MAX, "%s -l %s %s %s server", cvs_rsh, tok2, tok, cvs_server);
298 snprintf(execcmd, PATH_MAX, "%s %s %s server", cvs_rsh, tok2, cvs_server);
304 snprintf(execcmd, PATH_MAX, "%s server", cvs_server);
308 if (pipe(to_cvs) < 0)
310 debug(DEBUG_SYSERROR, "cvsclient: failed to create pipe to_cvs");
314 if (pipe(from_cvs) < 0)
316 debug(DEBUG_SYSERROR, "cvsclient: failed to create pipe from_cvs");
320 debug(DEBUG_TCP, "forked cmdline: %s", execcmd);
322 if ((pid = fork()) < 0)
324 debug(DEBUG_SYSERROR, "cvsclient: can't fork");
327 else if (pid == 0) /* child */
339 if (dup(to_cvs[0]) < 0) {
340 debug(DEBUG_APPERROR, "cvsclient: dup of input failed");
344 if (dup(from_cvs[1]) < 0) {
345 debug(DEBUG_APPERROR, "cvsclient: dup of output failed");
349 execv("/bin/sh",argp);
351 debug(DEBUG_APPERROR, "cvsclient: fatal: shouldn't be reached");
357 ctx->read_fd = from_cvs[0];
358 ctx->write_fd = to_cvs[1];
360 strcpy_a(ctx->root, rep, PATH_MAX);
375 void close_cvs_server(CvsServerCtx * ctx)
377 /* FIXME: some sort of flushing should be done for non-compressed case */
385 * there shouldn't be anything left, but we do want
386 * to send an 'end of stream' marker, (if such a thing
391 ctx->zout.next_out = (unsigned char *)buff;
392 ctx->zout.avail_out = BUFSIZ;
393 ret = deflate(&ctx->zout, Z_FINISH);
395 if ((ret == Z_OK || ret == Z_STREAM_END) && ctx->zout.avail_out != BUFSIZ)
397 len = BUFSIZ - ctx->zout.avail_out;
398 if (writen(ctx->write_fd, buff, len) != len)
399 debug(DEBUG_APPERROR, "cvsclient: zout: error writing final state");
401 //hexdump(buff, len, "cvsclient: zout: sending unsent data");
403 } while (ret == Z_OK);
405 if ((ret = deflateEnd(&ctx->zout)) != Z_OK)
406 debug(DEBUG_APPERROR, "cvsclient: zout: deflateEnd error: %s: %s",
407 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zout.msg);
410 /* we're done writing now */
411 debug(DEBUG_TCP, "cvsclient: closing cvs server write connection %d", ctx->write_fd);
412 close(ctx->write_fd);
415 * if this is pserver, then read_fd is a bi-directional socket.
416 * we want to shutdown the write side, just to make sure the
421 debug(DEBUG_TCP, "cvsclient: shutdown on read socket");
422 if (shutdown(ctx->read_fd, SHUT_WR) < 0)
423 debug(DEBUG_SYSERROR, "cvsclient: error with shutdown on pserver socket");
428 int ret = Z_OK, len, eof = 0;
429 unsigned char buff[BUFSIZ];
431 /* read to the 'eof'/'eos' marker. there are two states we
432 * track, looking for Z_STREAM_END (application level EOS)
433 * and EOF on socket. Both should happen at the same time,
434 * but we need to do the read first, the first time through
435 * the loop, but we want to do one read after getting Z_STREAM_END
436 * too. so this loop has really ugly exit conditions.
441 * if there's nothing in the avail_in, and we
442 * inflated everything last pass (avail_out != 0)
443 * then slurp some more from the descriptor,
444 * if we get EOF, exit the loop
446 if (ctx->zin.avail_in == 0 && ctx->zin.avail_out != 0)
448 debug(DEBUG_TCP, "cvsclient: doing final slurp");
449 len = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
450 debug(DEBUG_TCP, "cvsclient: did final slurp: %d", len);
458 /* put the data into the inflate input stream */
459 ctx->zin.next_in = ctx->zread_buff;
460 ctx->zin.avail_in = len;
464 * if the last time through we got Z_STREAM_END, and we
465 * get back here, it means we should've gotten EOF but
468 if (ret == Z_STREAM_END)
471 ctx->zin.next_out = buff;
472 ctx->zin.avail_out = BUFSIZ;
474 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
475 len = BUFSIZ - ctx->zin.avail_out;
477 if (ret == Z_BUF_ERROR)
478 debug(DEBUG_APPERROR, "Z_BUF_ERROR");
480 if (ret == Z_OK && len == 0)
481 debug(DEBUG_TCP, "cvsclient: no data out of inflate");
483 if (ret == Z_STREAM_END)
484 debug(DEBUG_TCP, "cvsclient: got Z_STREAM_END");
486 if ((ret == Z_OK || ret == Z_STREAM_END) && len > 0)
487 hexdump((char *)buff, BUFSIZ - ctx->zin.avail_out, "cvsclient: zin: unread data at close");
490 if (ret != Z_STREAM_END)
491 debug(DEBUG_APPERROR, "cvsclient: zin: Z_STREAM_END not encountered (premature EOF?)");
494 debug(DEBUG_APPERROR, "cvsclient: zin: EOF not encountered (premature Z_STREAM_END?)");
496 if ((ret = inflateEnd(&ctx->zin)) != Z_OK)
497 debug(DEBUG_APPERROR, "cvsclient: zin: inflateEnd error: %s: %s",
498 (ret == Z_STREAM_ERROR) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx->zin.msg ? ctx->zin.msg : "");
501 debug(DEBUG_TCP, "cvsclient: closing cvs server read connection %d", ctx->read_fd);
507 static void get_cvspass(char * pass, const char * root, int passbuflen)
509 char cvspass[PATH_MAX];
515 if (!(home = getenv("HOME")))
517 debug(DEBUG_APPERROR, "HOME environment variable not set");
521 if (snprintf(cvspass, PATH_MAX, "%s/.cvspass", home) >= PATH_MAX)
523 debug(DEBUG_APPERROR, "prefix buffer overflow");
527 if ((fp = fopen(cvspass, "r")))
530 int len = strlen(root);
532 while (fgets(buff, BUFSIZ, fp))
534 /* FIXME: what does /1 mean? */
535 if (strncmp(buff, "/1 ", 3) != 0)
538 if (strncmp(buff + 3, root, len) == 0)
540 strcpy_a(pass, buff + 3 + len + 1, passbuflen);
553 static void send_string(CvsServerCtx * ctx, const char * str, ...)
556 unsigned char buff[BUFSIZ];
560 len = vsnprintf((char *)buff, BUFSIZ, str, ap);
565 debug(DEBUG_APPERROR, "cvsclient: command send string overflow");
571 unsigned char zbuff[BUFSIZ];
573 if (ctx->zout.avail_in != 0)
575 debug(DEBUG_APPERROR, "cvsclient: zout: last output command not flushed");
579 ctx->zout.next_in = buff;
580 ctx->zout.avail_in = len;
581 ctx->zout.avail_out = 0;
583 while (ctx->zout.avail_in > 0 || ctx->zout.avail_out == 0)
587 ctx->zout.next_out = zbuff;
588 ctx->zout.avail_out = BUFSIZ;
590 /* FIXME: for the arguments before a command, flushing is counterproductive */
591 ret = deflate(&ctx->zout, Z_SYNC_FLUSH);
595 len = BUFSIZ - ctx->zout.avail_out;
597 if (writen(ctx->write_fd, zbuff, len) != len)
599 debug(DEBUG_SYSERROR, "cvsclient: zout: can't write");
605 debug(DEBUG_APPERROR, "cvsclient: zout: error %d %s", ret, ctx->zout.msg);
611 if (writen(ctx->write_fd, buff, len) != len)
613 debug(DEBUG_SYSERROR, "cvsclient: can't send command");
618 debug(DEBUG_TCP, "string: '%s' sent", buff);
621 static int refill_buffer(CvsServerCtx * ctx)
625 if (ctx->head != ctx->tail)
627 debug(DEBUG_APPERROR, "cvsclient: refill_buffer called on non-empty buffer");
631 ctx->head = ctx->read_buff;
638 /* if there was leftover buffer room, it's time to slurp more data */
641 if (ctx->zin.avail_out > 0)
643 if (ctx->zin.avail_in != 0)
645 debug(DEBUG_APPERROR, "cvsclient: zin: expect 0 avail_in");
648 zlen = read(ctx->read_fd, ctx->zread_buff, RD_BUFF_SIZE);
649 ctx->zin.next_in = ctx->zread_buff;
650 ctx->zin.avail_in = zlen;
653 ctx->zin.next_out = (unsigned char *)ctx->head;
654 ctx->zin.avail_out = len;
656 /* FIXME: we don't always need Z_SYNC_FLUSH, do we? */
657 ret = inflate(&ctx->zin, Z_SYNC_FLUSH);
659 while (ctx->zin.avail_out == len);
663 ctx->tail = ctx->head + (len - ctx->zin.avail_out);
667 debug(DEBUG_APPERROR, "cvsclient: zin: error %d %s", ret, ctx->zin.msg);
673 len = read(ctx->read_fd, ctx->head, len);
674 ctx->tail = (len <= 0) ? ctx->head : ctx->head + len;
680 static int read_line(CvsServerCtx * ctx, char * p, int maxlen)
689 if (ctx->head == ctx->tail)
690 if (refill_buffer(ctx) <= 0)
693 /* break out without advancing head if buffer is exhausted */
694 if (maxlen == 1 || (*p = *ctx->head++) == '\n')
708 static int read_response(CvsServerCtx * ctx, const char * str)
710 /* FIXME: more than 1 char at a time */
713 if (read_line(ctx, resp, BUFSIZ) < 0)
716 debug(DEBUG_TCP, "response '%s' read", resp);
718 return (strcmp(resp, str) == 0);
721 static void ctx_to_fp(CvsServerCtx * ctx, FILE * fp)
727 read_line(ctx, line, BUFSIZ);
728 debug(DEBUG_TCP, "ctx_to_fp: %s", line);
729 if (memcmp(line, "M ", 2) == 0)
732 fprintf(fp, "%s\n", line + 2);
734 else if (memcmp(line, "E ", 2) == 0)
736 debug(DEBUG_TCP, "%s", line + 2);
738 else if (strncmp(line, "ok", 2) == 0 || strncmp(line, "error", 5) == 0)
748 void cvs_rdiff(CvsServerCtx * ctx,
749 const char * rep, const char * file,
750 const char * rev1, const char * rev2)
752 /* NOTE: opts are ignored for rdiff, '-u' is always used */
754 send_string(ctx, "Argument -u\n");
755 send_string(ctx, "Argument -r\n");
756 send_string(ctx, "Argument %s\n", rev1);
757 send_string(ctx, "Argument -r\n");
758 send_string(ctx, "Argument %s\n", rev2);
759 send_string(ctx, "Argument %s%s\n", rep, file);
760 send_string(ctx, "rdiff\n");
762 ctx_to_fp(ctx, stdout);
765 void cvs_update(CvsServerCtx * ctx, const char * rep, const char * file, const char * rev, bool kk, FILE *fp)
768 send_string(ctx, "Argument -kk\n");
769 send_string(ctx, "Argument -p\n");
770 send_string(ctx, "Argument -r\n");
771 send_string(ctx, "Argument %s\n", rev);
772 send_string(ctx, "Argument %s/%s\n", rep, file);
773 send_string(ctx, "co\n");
778 static bool parse_patch_arg(char * arg, char ** str)
780 char *tok, *tok2 = "";
781 tok = strsep(str, " ");
787 debug(DEBUG_APPERROR, "diff_opts parse error: no '-' starting argument: %s", *str);
791 /* if it's not 'long format' argument, we can process it efficiently */
794 debug(DEBUG_APPERROR, "diff_opts parse_error: long format args not supported");
798 /* see if command wants two args and they're separated by ' ' */
799 if (tok[2] == 0 && strchr("BdDFgiorVxYz", tok[1]))
801 tok2 = strsep(str, " ");
804 debug(DEBUG_APPERROR, "diff_opts parse_error: argument %s requires two arguments", tok);
809 snprintf(arg, 32, "%s%s", tok, tok2);
813 void cvs_diff(CvsServerCtx * ctx,
814 const char * rep, const char * file,
815 const char * rev1, const char * rev2, const char * opts)
817 char argstr[BUFSIZ], *p = argstr;
819 char file_buff[PATH_MAX], *basename;
821 strzncpy(argstr, opts, BUFSIZ);
822 while (parse_patch_arg(arg, &p))
823 send_string(ctx, "Argument %s\n", arg);
825 send_string(ctx, "Argument -r\n");
826 send_string(ctx, "Argument %s\n", rev1);
827 send_string(ctx, "Argument -r\n");
828 send_string(ctx, "Argument %s\n", rev2);
831 * we need to separate the 'basename' of file in order to
832 * generate the Directory directive(s)
834 strzncpy(file_buff, file, PATH_MAX);
835 if ((basename = strrchr(file_buff, '/')))
838 send_string(ctx, "Directory %s/%s\n", rep, file_buff);
839 send_string(ctx, "%s/%s/%s\n", ctx->root, rep, file_buff);
843 send_string(ctx, "Directory %s\n", rep, file_buff);
844 send_string(ctx, "%s/%s\n", ctx->root, rep);
847 send_string(ctx, "Directory .\n");
848 send_string(ctx, "%s\n", ctx->root);
849 send_string(ctx, "Argument %s/%s\n", rep, file);
850 send_string(ctx, "diff\n");
852 ctx_to_fp(ctx, stdout);
856 * FIXME: the design of this sucks. It was originally designed to fork a subprocess
857 * which read the cvs response and send it back through a pipe the main process,
858 * which fdopen(3)ed the other end, and just used regular fgets. This however
859 * didn't work because the reads of compressed data in the child process altered
860 * the compression state, and there was no way to resynchronize that state with
861 * the parent process. We could use threads...
863 FILE * cvs_rlog_open(CvsServerCtx * ctx, const char * rep)
865 send_string(ctx, "Argument %s\n", rep);
866 send_string(ctx, "rlog\n");
869 * FIXME: is it possible to create a 'fake' FILE * whose 'refill'
875 char * cvs_rlog_fgets(char * buff, int buflen, CvsServerCtx * ctx)
881 len = read_line(ctx, lbuff, BUFSIZ);
882 debug(DEBUG_TCP, "cvsclient: rlog: read %s", lbuff);
884 if (memcmp(lbuff, "M ", 2) == 0)
886 memcpy(buff, lbuff + 2, len - 2);
887 buff[len - 2 ] = '\n';
890 else if (memcmp(lbuff, "E ", 2) == 0)
892 debug(DEBUG_TCP, "%s", lbuff + 2);
895 else if (strcmp(lbuff, "ok") == 0 || strncmp(lbuff, "error", 5) == 0)
897 debug(DEBUG_TCP, "cvsclient: rlog: got command completion");
904 void cvs_rlog_close(CvsServerCtx * ctx)
908 void cvs_version(CvsServerCtx * ctx, char * client_version, char * server_version, int cvlen, int svlen)
911 strcpy_a(client_version, "Client: Concurrent Versions System (CVS) 99.99.99 (client/server) cvs-direct", cvlen);
912 send_string(ctx, "version\n");
913 read_line(ctx, lbuff, BUFSIZ);
914 if (memcmp(lbuff, "M ", 2) == 0)
915 snprintf(server_version, svlen, "Server: %s", lbuff + 2);
917 debug(DEBUG_APPERROR, "cvsclient: didn't read version: %s", lbuff);
919 read_line(ctx, lbuff, BUFSIZ);
920 if (strstr(lbuff,"CVSACL")!=NULL) {
921 read_line(ctx, lbuff, BUFSIZ);
923 if (strcmp(lbuff, "ok") != 0)
924 debug(DEBUG_APPERROR, "cvsclient: protocol error reading version");
926 debug(DEBUG_TCP, "cvsclient: client version %s", client_version);
927 debug(DEBUG_TCP, "cvsclient: server version %s", server_version);