2 * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc.
3 * See COPYING file for license information
8 char * strsep(char **str
, const char *delim
);
15 #include <sys/socket.h>
16 #include <cbtcommon/debug.h>
17 #include <cbtcommon/text_util.h>
18 #include <cbtcommon/tcpsocket.h>
19 #include <cbtcommon/sio.h>
21 #include "cvs_direct.h"
24 #define RD_BUFF_SIZE 4096
34 /* buffered reads from descriptor */
35 char read_buff
[RD_BUFF_SIZE
];
43 /* when reading compressed data, the compressed data buffer */
44 char zread_buff
[RD_BUFF_SIZE
];
47 static void get_cvspass(char *, const char *);
48 static void send_string(CvsServerCtx
*, const char *, ...);
49 static int read_response(CvsServerCtx
*, const char *);
50 static void ctx_to_fp(CvsServerCtx
* ctx
, FILE * fp
);
51 static int read_line(CvsServerCtx
* ctx
, char * p
);
53 static CvsServerCtx
* open_ctx_pserver(CvsServerCtx
*, const char *);
54 static CvsServerCtx
* open_ctx_forked(CvsServerCtx
*, const char *);
56 CvsServerCtx
* open_cvs_server(char * p_root
, int compress
)
58 CvsServerCtx
* ctx
= (CvsServerCtx
*)malloc(sizeof(*ctx
));
60 char * p
= root
, *tok
;
65 ctx
->head
= ctx
->tail
= ctx
->read_buff
;
66 ctx
->read_fd
= ctx
->write_fd
= -1;
72 memset(&ctx
->zout
, 0, sizeof(z_stream
));
73 memset(&ctx
->zin
, 0, sizeof(z_stream
));
76 * to 'prime' the reads, make it look like there was output
77 * room available (i.e. we have processed all pending compressed
80 ctx
->zin
.avail_out
= 1;
82 if (deflateInit(&ctx
->zout
, compress
) != Z_OK
)
88 if (inflateInit(&ctx
->zin
) != Z_OK
)
90 deflateEnd(&ctx
->zout
);
98 tok
= strsep(&p
, ":");
100 /* if root string looks like :pserver:... then the first token will be empty */
101 if (strlen(tok
) == 0)
103 char * method
= strsep(&p
, ":");
104 if (strcmp(method
, "pserver") == 0)
106 ctx
= open_ctx_pserver(ctx
, p
);
108 else if (strstr("local:ext:fork:server", method
))
110 /* handle all of these via fork, even local */
111 ctx
= open_ctx_forked(ctx
, p
);
115 debug(DEBUG_APPERROR
, "cvs_direct: unsupported cvs access method: %s", method
);
122 ctx
= open_ctx_forked(ctx
, p_root
);
129 send_string(ctx
, "Root %s\n", ctx
->root
);
131 /* this is taken from 1.11.1p1 trace - but with Mbinary removed. we can't handle it (yet!) */
132 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
);
134 send_string(ctx
, "valid-requests\n");
136 /* check for the commands we will issue */
137 read_line(ctx
, buff
);
138 if (strncmp(buff
, "Valid-requests", 14) != 0)
140 debug(DEBUG_APPERROR
, "cvs_direct: bad response to valid-requests command");
141 close_cvs_server(ctx
);
145 if (!strstr(buff
, " version") ||
146 !strstr(buff
, " rlog") ||
147 !strstr(buff
, " rdiff") ||
148 !strstr(buff
, " diff") ||
149 !strstr(buff
, " co"))
151 debug(DEBUG_APPERROR
, "cvs_direct: cvs server too old for cvs_direct");
152 close_cvs_server(ctx
);
156 read_line(ctx
, buff
);
157 if (strcmp(buff
, "ok") != 0)
159 debug(DEBUG_APPERROR
, "cvs_direct: bad ok trailer to valid-requests command");
160 close_cvs_server(ctx
);
164 /* this is myterious but 'mandatory' */
165 send_string(ctx
, "UseUnchanged\n");
169 send_string(ctx
, "Gzip-stream %d\n", compress
);
173 debug(DEBUG_APPMSG1
, "cvs_direct initialized to CVSROOT %s", ctx
->root
);
179 static CvsServerCtx
* open_ctx_pserver(CvsServerCtx
* ctx
, const char * p_root
)
182 char full_root
[PATH_MAX
];
183 char * p
= root
, *tok
, *tok2
;
189 strcpy(root
, p_root
);
191 tok
= strsep(&p
, ":");
192 if (strlen(tok
) == 0 || !p
)
194 debug(DEBUG_APPERROR
, "parse error on third token");
198 tok2
= strsep(&tok
, "@");
199 if (!strlen(tok2
) || (!tok
|| !strlen(tok
)))
201 debug(DEBUG_APPERROR
, "parse error on user@server in pserver");
210 tok
= strchr(p
, '/');
213 debug(DEBUG_APPERROR
, "parse error: expecting / in root");
217 memset(port
, 0, sizeof(port
));
218 memcpy(port
, p
, tok
- p
);
224 strcpy(port
, "2401");
227 /* the line from .cvspass is fully qualified, so rebuild */
228 snprintf(full_root
, PATH_MAX
, ":pserver:%s@%s:%s%s", user
, server
, port
, p
);
229 get_cvspass(pass
, full_root
);
231 debug(DEBUG_TCP
, "user:%s server:%s port:%s pass:%s full_root:%s", user
, server
, port
, pass
, full_root
);
233 if ((ctx
->read_fd
= tcp_create_socket(REUSE_ADDR
)) < 0)
236 ctx
->write_fd
= dup(ctx
->read_fd
);
238 if (tcp_connect(ctx
->read_fd
, server
, atoi(port
)) < 0)
241 send_string(ctx
, "BEGIN AUTH REQUEST\n");
242 send_string(ctx
, "%s\n", p
);
243 send_string(ctx
, "%s\n", user
);
244 send_string(ctx
, "%s\n", pass
);
245 send_string(ctx
, "END AUTH REQUEST\n");
247 if (!read_response(ctx
, "I LOVE YOU"))
250 strcpy(ctx
->root
, p
);
262 static CvsServerCtx
* open_ctx_forked(CvsServerCtx
* ctx
, const char * p_root
)
265 debug(DEBUG_SYSERROR
, "cvs_direct: fork not supported on MinGW");
268 char * p
= root
, *tok
, *tok2
, *rep
;
269 char execcmd
[PATH_MAX
];
273 const char * cvs_server
= getenv("CVS_SERVER");
278 strcpy(root
, p_root
);
280 /* if there's a ':', it's remote */
281 tok
= strsep(&p
, ":");
285 const char * cvs_rsh
= getenv("CVS_RSH");
290 tok2
= strsep(&tok
, "@");
293 snprintf(execcmd
, PATH_MAX
, "%s -l %s %s %s server", cvs_rsh
, tok2
, tok
, cvs_server
);
295 snprintf(execcmd
, PATH_MAX
, "%s %s %s server", cvs_rsh
, tok2
, cvs_server
);
301 snprintf(execcmd
, PATH_MAX
, "%s server", cvs_server
);
305 if (pipe(to_cvs
) < 0)
307 debug(DEBUG_SYSERROR
, "cvs_direct: failed to create pipe to_cvs");
311 if (pipe(from_cvs
) < 0)
313 debug(DEBUG_SYSERROR
, "cvs_direct: failed to create pipe from_cvs");
317 debug(DEBUG_TCP
, "forked cmdline: %s", execcmd
);
319 if ((pid
= fork()) < 0)
321 debug(DEBUG_SYSERROR
, "cvs_direct: can't fork");
324 else if (pid
== 0) /* child */
340 execv("/bin/sh",argp
);
342 debug(DEBUG_APPERROR
, "cvs_direct: fatal: shouldn't be reached");
348 ctx
->read_fd
= from_cvs
[0];
349 ctx
->write_fd
= to_cvs
[1];
351 strcpy(ctx
->root
, rep
);
367 void close_cvs_server(CvsServerCtx
* ctx
)
369 /* FIXME: some sort of flushing should be done for non-compressed case */
377 * there shouldn't be anything left, but we do want
378 * to send an 'end of stream' marker, (if such a thing
383 ctx
->zout
.next_out
= buff
;
384 ctx
->zout
.avail_out
= BUFSIZ
;
385 ret
= deflate(&ctx
->zout
, Z_FINISH
);
387 if ((ret
== Z_OK
|| ret
== Z_STREAM_END
) && ctx
->zout
.avail_out
!= BUFSIZ
)
389 len
= BUFSIZ
- ctx
->zout
.avail_out
;
390 if (writen(ctx
->write_fd
, buff
, len
) != len
)
391 debug(DEBUG_APPERROR
, "cvs_direct: zout: error writing final state");
393 //hexdump(buff, len, "cvs_direct: zout: sending unsent data");
395 } while (ret
== Z_OK
);
397 if ((ret
= deflateEnd(&ctx
->zout
)) != Z_OK
)
398 debug(DEBUG_APPERROR
, "cvs_direct: zout: deflateEnd error: %s: %s",
399 (ret
== Z_STREAM_ERROR
) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx
->zout
.msg
);
402 /* we're done writing now */
403 debug(DEBUG_TCP
, "cvs_direct: closing cvs server write connection %d", ctx
->write_fd
);
404 close(ctx
->write_fd
);
407 * if this is pserver, then read_fd is a bi-directional socket.
408 * we want to shutdown the write side, just to make sure the
413 debug(DEBUG_TCP
, "cvs_direct: shutdown on read socket");
414 if (shutdown(ctx
->read_fd
, SHUT_WR
) < 0)
415 debug(DEBUG_SYSERROR
, "cvs_direct: error with shutdown on pserver socket");
420 int ret
= Z_OK
, len
, eof
= 0;
423 /* read to the 'eof'/'eos' marker. there are two states we
424 * track, looking for Z_STREAM_END (application level EOS)
425 * and EOF on socket. Both should happen at the same time,
426 * but we need to do the read first, the first time through
427 * the loop, but we want to do one read after getting Z_STREAM_END
428 * too. so this loop has really ugly exit conditions.
433 * if there's nothing in the avail_in, and we
434 * inflated everything last pass (avail_out != 0)
435 * then slurp some more from the descriptor,
436 * if we get EOF, exit the loop
438 if (ctx
->zin
.avail_in
== 0 && ctx
->zin
.avail_out
!= 0)
440 debug(DEBUG_TCP
, "cvs_direct: doing final slurp");
441 len
= read(ctx
->read_fd
, ctx
->zread_buff
, RD_BUFF_SIZE
);
442 debug(DEBUG_TCP
, "cvs_direct: did final slurp: %d", len
);
450 /* put the data into the inflate input stream */
451 ctx
->zin
.next_in
= ctx
->zread_buff
;
452 ctx
->zin
.avail_in
= len
;
456 * if the last time through we got Z_STREAM_END, and we
457 * get back here, it means we should've gotten EOF but
460 if (ret
== Z_STREAM_END
)
463 ctx
->zin
.next_out
= buff
;
464 ctx
->zin
.avail_out
= BUFSIZ
;
466 ret
= inflate(&ctx
->zin
, Z_SYNC_FLUSH
);
467 len
= BUFSIZ
- ctx
->zin
.avail_out
;
469 if (ret
== Z_BUF_ERROR
)
470 debug(DEBUG_APPERROR
, "Z_BUF_ERROR");
472 if (ret
== Z_OK
&& len
== 0)
473 debug(DEBUG_TCP
, "cvs_direct: no data out of inflate");
475 if (ret
== Z_STREAM_END
)
476 debug(DEBUG_TCP
, "cvs_direct: got Z_STREAM_END");
478 if ((ret
== Z_OK
|| ret
== Z_STREAM_END
) && len
> 0)
479 hexdump(buff
, BUFSIZ
- ctx
->zin
.avail_out
, "cvs_direct: zin: unread data at close");
482 if (ret
!= Z_STREAM_END
)
483 debug(DEBUG_APPERROR
, "cvs_direct: zin: Z_STREAM_END not encountered (premature EOF?)");
486 debug(DEBUG_APPERROR
, "cvs_direct: zin: EOF not encountered (premature Z_STREAM_END?)");
488 if ((ret
= inflateEnd(&ctx
->zin
)) != Z_OK
)
489 debug(DEBUG_APPERROR
, "cvs_direct: zin: inflateEnd error: %s: %s",
490 (ret
== Z_STREAM_ERROR
) ? "Z_STREAM_ERROR":"Z_DATA_ERROR", ctx
->zin
.msg
? ctx
->zin
.msg
: "");
493 debug(DEBUG_TCP
, "cvs_direct: closing cvs server read connection %d", ctx
->read_fd
);
499 static void get_cvspass(char * pass
, const char * root
)
501 char cvspass
[PATH_MAX
];
507 if (!(home
= getenv("HOME")))
509 debug(DEBUG_APPERROR
, "HOME environment variable not set");
513 if (snprintf(cvspass
, PATH_MAX
, "%s/.cvspass", home
) >= PATH_MAX
)
515 debug(DEBUG_APPERROR
, "prefix buffer overflow");
519 if ((fp
= fopen(cvspass
, "r")))
522 int len
= strlen(root
);
524 while (fgets(buff
, BUFSIZ
, fp
))
526 /* FIXME: what does /1 mean? */
527 if (strncmp(buff
, "/1 ", 3) != 0)
530 if (strncmp(buff
+ 3, root
, len
) == 0)
532 strcpy(pass
, buff
+ 3 + len
+ 1);
545 static void send_string(CvsServerCtx
* ctx
, const char * str
, ...)
553 len
= vsnprintf(buff
, BUFSIZ
, str
, ap
);
556 debug(DEBUG_APPERROR
, "cvs_direct: command send string overflow");
564 if (ctx
->zout
.avail_in
!= 0)
566 debug(DEBUG_APPERROR
, "cvs_direct: zout: last output command not flushed");
570 ctx
->zout
.next_in
= buff
;
571 ctx
->zout
.avail_in
= len
;
572 ctx
->zout
.avail_out
= 0;
574 while (ctx
->zout
.avail_in
> 0 || ctx
->zout
.avail_out
== 0)
578 ctx
->zout
.next_out
= zbuff
;
579 ctx
->zout
.avail_out
= BUFSIZ
;
581 /* FIXME: for the arguments before a command, flushing is counterproductive */
582 ret
= deflate(&ctx
->zout
, Z_SYNC_FLUSH
);
586 len
= BUFSIZ
- ctx
->zout
.avail_out
;
588 if (writen(ctx
->write_fd
, zbuff
, len
) != len
)
590 debug(DEBUG_SYSERROR
, "cvs_direct: zout: can't write");
596 debug(DEBUG_APPERROR
, "cvs_direct: zout: error %d %s", ret
, ctx
->zout
.msg
);
602 if (writen(ctx
->write_fd
, buff
, len
) != len
)
604 debug(DEBUG_SYSERROR
, "cvs_direct: can't send command");
609 debug(DEBUG_TCP
, "string: '%s' sent", buff
);
612 static int refill_buffer(CvsServerCtx
* ctx
)
616 if (ctx
->head
!= ctx
->tail
)
618 debug(DEBUG_APPERROR
, "cvs_direct: refill_buffer called on non-empty buffer");
622 ctx
->head
= ctx
->read_buff
;
629 /* if there was leftover buffer room, it's time to slurp more data */
632 if (ctx
->zin
.avail_out
> 0)
634 if (ctx
->zin
.avail_in
!= 0)
636 debug(DEBUG_APPERROR
, "cvs_direct: zin: expect 0 avail_in");
639 zlen
= read(ctx
->read_fd
, ctx
->zread_buff
, RD_BUFF_SIZE
);
640 ctx
->zin
.next_in
= ctx
->zread_buff
;
641 ctx
->zin
.avail_in
= zlen
;
644 ctx
->zin
.next_out
= ctx
->head
;
645 ctx
->zin
.avail_out
= len
;
647 /* FIXME: we don't always need Z_SYNC_FLUSH, do we? */
648 ret
= inflate(&ctx
->zin
, Z_SYNC_FLUSH
);
650 while (ctx
->zin
.avail_out
== len
);
654 ctx
->tail
= ctx
->head
+ (len
- ctx
->zin
.avail_out
);
658 debug(DEBUG_APPERROR
, "cvs_direct: zin: error %d %s", ret
, ctx
->zin
.msg
);
664 len
= read(ctx
->read_fd
, ctx
->head
, len
);
665 ctx
->tail
= (len
<= 0) ? ctx
->head
: ctx
->head
+ len
;
671 static int read_line(CvsServerCtx
* ctx
, char * p
)
676 if (ctx
->head
== ctx
->tail
)
677 if (refill_buffer(ctx
) <= 0)
694 static int read_response(CvsServerCtx
* ctx
, const char * str
)
696 /* FIXME: more than 1 char at a time */
699 if (read_line(ctx
, resp
) < 0)
702 debug(DEBUG_TCP
, "response '%s' read", resp
);
704 return (strcmp(resp
, str
) == 0);
707 static void ctx_to_fp(CvsServerCtx
* ctx
, FILE * fp
)
713 read_line(ctx
, line
);
714 debug(DEBUG_TCP
, "ctx_to_fp: %s", line
);
715 if (memcmp(line
, "M ", 2) == 0)
718 fprintf(fp
, "%s\n", line
+ 2);
720 else if (memcmp(line
, "E ", 2) == 0)
722 debug(DEBUG_APPMSG1
, "%s", line
+ 2);
724 else if (strncmp(line
, "ok", 2) == 0 || strncmp(line
, "error", 5) == 0)
734 void cvs_rdiff(CvsServerCtx
* ctx
,
735 const char * rep
, const char * file
,
736 const char * rev1
, const char * rev2
)
738 /* NOTE: opts are ignored for rdiff, '-u' is always used */
740 send_string(ctx
, "Argument -u\n");
741 send_string(ctx
, "Argument -r\n");
742 send_string(ctx
, "Argument %s\n", rev1
);
743 send_string(ctx
, "Argument -r\n");
744 send_string(ctx
, "Argument %s\n", rev2
);
745 send_string(ctx
, "Argument %s%s\n", rep
, file
);
746 send_string(ctx
, "rdiff\n");
748 ctx_to_fp(ctx
, stdout
);
751 void cvs_rupdate(CvsServerCtx
* ctx
, const char * rep
, const char * file
, const char * rev
, int create
, const char * opts
)
754 char cmdbuff
[BUFSIZ
];
756 snprintf(cmdbuff
, BUFSIZ
, "diff %s %s /dev/null %s | sed -e '%s s|^\\([+-][+-][+-]\\) -|\\1 %s/%s|g'",
757 opts
, create
?"":"-", create
?"-":"", create
?"2":"1", rep
, file
);
759 debug(DEBUG_TCP
, "cmdbuff: %s", cmdbuff
);
761 if (!(fp
= popen(cmdbuff
, "w")))
763 debug(DEBUG_APPERROR
, "cvs_direct: popen for diff failed: %s", cmdbuff
);
767 send_string(ctx
, "Argument -p\n");
768 send_string(ctx
, "Argument -r\n");
769 send_string(ctx
, "Argument %s\n", rev
);
770 send_string(ctx
, "Argument %s/%s\n", rep
, file
);
771 send_string(ctx
, "co\n");
778 static int 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 juts 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
, const char * date_str
)
865 /* note: use of the date_str is handled in a non-standard, cvsps specific way */
866 if (date_str
&& date_str
[0])
868 send_string(ctx
, "Argument -d\n", rep
);
869 send_string(ctx
, "Argument %s<1 Jan 2038 05:00:00 -0000\n", date_str
);
870 send_string(ctx
, "Argument -d\n", rep
);
871 send_string(ctx
, "Argument %s\n", date_str
);
874 send_string(ctx
, "Argument %s\n", rep
);
875 send_string(ctx
, "rlog\n");
878 * FIXME: is it possible to create a 'fake' FILE * whose 'refill'
884 char * cvs_rlog_fgets(char * buff
, int buflen
, CvsServerCtx
* ctx
)
889 len
= read_line(ctx
, lbuff
);
890 debug(DEBUG_TCP
, "cvs_direct: rlog: read %s", lbuff
);
892 if (memcmp(lbuff
, "M ", 2) == 0)
894 memcpy(buff
, lbuff
+ 2, len
- 2);
895 buff
[len
- 2 ] = '\n';
898 else if (memcmp(lbuff
, "E ", 2) == 0)
900 debug(DEBUG_APPMSG1
, "%s", lbuff
+ 2);
902 else if (strcmp(lbuff
, "ok") == 0 ||strcmp(lbuff
, "error") == 0)
904 debug(DEBUG_TCP
, "cvs_direct: rlog: got command completion");
911 void cvs_rlog_close(CvsServerCtx
* ctx
)
915 void cvs_version(CvsServerCtx
* ctx
, char * client_version
, char * server_version
)
918 strcpy(client_version
, "Client: Concurrent Versions System (CVS) 99.99.99 (client/server) cvs-direct");
919 send_string(ctx
, "version\n");
920 read_line(ctx
, lbuff
);
921 if (memcmp(lbuff
, "M ", 2) == 0)
922 sprintf(server_version
, "Server: %s", lbuff
+ 2);
924 debug(DEBUG_APPERROR
, "cvs_direct: didn't read version: %s", lbuff
);
926 read_line(ctx
, lbuff
);
927 if (strcmp(lbuff
, "ok") != 0)
928 debug(DEBUG_APPERROR
, "cvs_direct: protocol error reading version");
930 debug(DEBUG_TCP
, "cvs_direct: client version %s", client_version
);
931 debug(DEBUG_TCP
, "cvs_direct: server version %s", server_version
);