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)
729 read_line(ctx, line, BUFSIZ);
730 debug(DEBUG_TCP, "ctx_to_fp: %s", line);
732 if (strncmp (line, "error ", 6) == 0)
734 debug(DEBUG_APPERROR, "ctx_to_fp: error: %s", line);
738 /* EOF. likely delete file */
739 if (strcmp (line, "ok") == 0)
742 /* wait for raw data size */
743 conv = strtol(line, &sz_e, 10);
744 if (conv == LONG_MIN || conv == LONG_MAX || *sz_e != '\0')
747 debug(DEBUG_TCP, "ctx_to_fp: file size: %ld", conv);
751 long bs = ctx->tail - ctx->head;
752 if (bs > conv) bs = conv;
754 fwrite (ctx->head, 1, bs, fp);
760 if (refill_buffer(ctx) <= 0)
762 debug(DEBUG_APPERROR, "ctx_to_fp: refill_buffer error");
768 read_line(ctx, line, BUFSIZ);
769 if (strcmp (line, "ok") != 0)
771 debug(DEBUG_APPERROR, "ctx_to_fp: error: expected 'ok' at EOF but got: %s", line);
782 void cvs_rdiff(CvsServerCtx * ctx,
783 const char * rep, const char * file,
784 const char * rev1, const char * rev2)
786 /* NOTE: opts are ignored for rdiff, '-u' is always used */
788 send_string(ctx, "Argument -u\n");
789 send_string(ctx, "Argument -r\n");
790 send_string(ctx, "Argument %s\n", rev1);
791 send_string(ctx, "Argument -r\n");
792 send_string(ctx, "Argument %s\n", rev2);
793 send_string(ctx, "Argument %s%s\n", rep, file);
794 send_string(ctx, "rdiff\n");
796 ctx_to_fp(ctx, stdout);
799 void cvs_update(CvsServerCtx * ctx, const char * rep, const char * file, const char * rev, bool kk, FILE *fp)
802 send_string(ctx, "Argument -kk\n");
803 send_string(ctx, "Argument -r\n");
804 send_string(ctx, "Argument %s\n", rev);
805 send_string(ctx, "Argument %s/%s\n", rep, file);
806 send_string(ctx, "co\n");
811 static bool parse_patch_arg(char * arg, char ** str)
813 char *tok, *tok2 = "";
814 tok = strsep(str, " ");
820 debug(DEBUG_APPERROR, "diff_opts parse error: no '-' starting argument: %s", *str);
824 /* if it's not 'long format' argument, we can process it efficiently */
827 debug(DEBUG_APPERROR, "diff_opts parse_error: long format args not supported");
831 /* see if command wants two args and they're separated by ' ' */
832 if (tok[2] == 0 && strchr("BdDFgiorVxYz", tok[1]))
834 tok2 = strsep(str, " ");
837 debug(DEBUG_APPERROR, "diff_opts parse_error: argument %s requires two arguments", tok);
842 snprintf(arg, 32, "%s%s", tok, tok2);
846 void cvs_diff(CvsServerCtx * ctx,
847 const char * rep, const char * file,
848 const char * rev1, const char * rev2, const char * opts)
850 char argstr[BUFSIZ], *p = argstr;
852 char file_buff[PATH_MAX], *basename;
854 strzncpy(argstr, opts, BUFSIZ);
855 while (parse_patch_arg(arg, &p))
856 send_string(ctx, "Argument %s\n", arg);
858 send_string(ctx, "Argument -r\n");
859 send_string(ctx, "Argument %s\n", rev1);
860 send_string(ctx, "Argument -r\n");
861 send_string(ctx, "Argument %s\n", rev2);
864 * we need to separate the 'basename' of file in order to
865 * generate the Directory directive(s)
867 strzncpy(file_buff, file, PATH_MAX);
868 if ((basename = strrchr(file_buff, '/')))
871 send_string(ctx, "Directory %s/%s\n", rep, file_buff);
872 send_string(ctx, "%s/%s/%s\n", ctx->root, rep, file_buff);
876 send_string(ctx, "Directory %s\n", rep, file_buff);
877 send_string(ctx, "%s/%s\n", ctx->root, rep);
880 send_string(ctx, "Directory .\n");
881 send_string(ctx, "%s\n", ctx->root);
882 send_string(ctx, "Argument %s/%s\n", rep, file);
883 send_string(ctx, "diff\n");
885 ctx_to_fp(ctx, stdout);
889 * FIXME: the design of this sucks. It was originally designed to fork a subprocess
890 * which read the cvs response and send it back through a pipe the main process,
891 * which fdopen(3)ed the other end, and just used regular fgets. This however
892 * didn't work because the reads of compressed data in the child process altered
893 * the compression state, and there was no way to resynchronize that state with
894 * the parent process. We could use threads...
896 FILE * cvs_rlog_open(CvsServerCtx * ctx, const char * rep)
898 send_string(ctx, "Argument %s\n", rep);
899 send_string(ctx, "rlog\n");
902 * FIXME: is it possible to create a 'fake' FILE * whose 'refill'
908 char * cvs_rlog_fgets(char * buff, int buflen, CvsServerCtx * ctx)
914 len = read_line(ctx, lbuff, BUFSIZ);
915 debug(DEBUG_TCP, "cvsclient: rlog: read %s", lbuff);
917 if (memcmp(lbuff, "M ", 2) == 0)
919 memcpy(buff, lbuff + 2, len - 2);
920 buff[len - 2 ] = '\n';
923 else if (memcmp(lbuff, "E ", 2) == 0)
925 debug(DEBUG_TCP, "%s", lbuff + 2);
928 else if (strcmp(lbuff, "ok") == 0 || strncmp(lbuff, "error", 5) == 0)
930 debug(DEBUG_TCP, "cvsclient: rlog: got command completion");
937 void cvs_rlog_close(CvsServerCtx * ctx)
941 void cvs_version(CvsServerCtx * ctx, char * client_version, char * server_version, int cvlen, int svlen)
944 strcpy_a(client_version, "Client: Concurrent Versions System (CVS) 99.99.99 (client/server) cvs-direct", cvlen);
945 send_string(ctx, "version\n");
946 read_line(ctx, lbuff, BUFSIZ);
947 if (memcmp(lbuff, "M ", 2) == 0)
948 snprintf(server_version, svlen, "Server: %s", lbuff + 2);
950 debug(DEBUG_APPERROR, "cvsclient: didn't read version: %s", lbuff);
952 read_line(ctx, lbuff, BUFSIZ);
953 if (strstr(lbuff,"CVSACL")!=NULL) {
954 read_line(ctx, lbuff, BUFSIZ);
956 if (strcmp(lbuff, "ok") != 0)
957 debug(DEBUG_APPERROR, "cvsclient: protocol error reading version");
959 debug(DEBUG_TCP, "cvsclient: client version %s", client_version);
960 debug(DEBUG_TCP, "cvsclient: server version %s", server_version);