Annotation of src/usr.bin/cvs/proto.c, Revision 1.16
1.1 jfb 1: /* $OpenBSD$ */
2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: *
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26: /*
27: * CVS client/server protocol
28: * ==========================
29: *
30: * The following code implements the CVS client/server protocol, which is
31: * documented at the following URL:
32: * http://www.loria.fr/~molli/cvs/doc/cvsclient_toc.html
33: *
34: * The protocol is split up into two parts; the first part is the client side
35: * of things and is composed of all the response handlers, which are all named
36: * with a prefix of "cvs_resp_". The second part is the set of request
37: * handlers used by the server. These handlers process the request and
38: * generate the appropriate response to send back. The prefix for request
39: * handlers is "cvs_req_".
40: *
41: */
42:
43: #include <sys/types.h>
44: #include <sys/stat.h>
45:
46: #include <fcntl.h>
47: #include <stdio.h>
48: #include <errno.h>
49: #include <stdlib.h>
50: #include <unistd.h>
51: #include <signal.h>
52: #include <string.h>
53: #include <sysexits.h>
54: #ifdef CVS_ZLIB
55: #include <zlib.h>
56: #endif
57:
58: #include "buf.h"
59: #include "cvs.h"
60: #include "log.h"
1.14 jfb 61: #include "file.h"
1.13 jfb 62: #include "proto.h"
1.1 jfb 63:
64:
1.5 jfb 65: #define CVS_MTSTK_MAXDEPTH 16
66:
1.11 jfb 67: /* request flags */
68: #define CVS_REQF_RESP 0x01
69:
70:
1.5 jfb 71:
1.1 jfb 72:
73: extern int verbosity;
74: extern int cvs_compress;
75: extern char *cvs_rsh;
76: extern int cvs_trace;
77: extern int cvs_nolog;
78: extern int cvs_readonly;
79:
80:
81:
1.13 jfb 82: static int cvs_resp_validreq (struct cvsroot *, int, char *);
83: static int cvs_resp_cksum (struct cvsroot *, int, char *);
84: static int cvs_resp_modtime (struct cvsroot *, int, char *);
85: static int cvs_resp_m (struct cvsroot *, int, char *);
86: static int cvs_resp_ok (struct cvsroot *, int, char *);
87: static int cvs_resp_error (struct cvsroot *, int, char *);
88: static int cvs_resp_statdir (struct cvsroot *, int, char *);
89: static int cvs_resp_sticky (struct cvsroot *, int, char *);
90: static int cvs_resp_newentry (struct cvsroot *, int, char *);
91: static int cvs_resp_updated (struct cvsroot *, int, char *);
92: static int cvs_resp_removed (struct cvsroot *, int, char *);
93: static int cvs_resp_mode (struct cvsroot *, int, char *);
94: static int cvs_resp_modxpand (struct cvsroot *, int, char *);
1.14 jfb 95: static int cvs_resp_rcsdiff (struct cvsroot *, int, char *);
1.16 ! jfb 96: static int cvs_resp_template (struct cvsroot *, int, char *);
1.1 jfb 97:
1.13 jfb 98: static int cvs_initlog (void);
1.1 jfb 99:
1.7 jfb 100: static const char *cvs_months[] = {
1.9 jfb 101: "Jan", "Feb", "Mar", "Apr", "May", "Jun",
102: "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1.7 jfb 103: };
104:
105:
106:
1.1 jfb 107: struct cvs_req {
108: int req_id;
109: char req_str[32];
110: u_int req_flags;
1.13 jfb 111: int (*req_hdlr)(int, char *);
1.1 jfb 112: } cvs_requests[] = {
113: { CVS_REQ_DIRECTORY, "Directory", 0, NULL },
114: { CVS_REQ_MAXDOTDOT, "Max-dotdot", 0, NULL },
115: { CVS_REQ_STATICDIR, "Static-directory", 0, NULL },
116: { CVS_REQ_STICKY, "Sticky", 0, NULL },
117: { CVS_REQ_ENTRY, "Entry", 0, NULL },
118: { CVS_REQ_ENTRYEXTRA, "EntryExtra", 0, NULL },
119: { CVS_REQ_CHECKINTIME, "Checkin-time", 0, NULL },
120: { CVS_REQ_MODIFIED, "Modified", 0, NULL },
121: { CVS_REQ_ISMODIFIED, "Is-modified", 0, NULL },
122: { CVS_REQ_UNCHANGED, "Unchanged", 0, NULL },
123: { CVS_REQ_USEUNCHANGED, "UseUnchanged", 0, NULL },
124: { CVS_REQ_NOTIFY, "Notify", 0, NULL },
125: { CVS_REQ_NOTIFYUSER, "NotifyUser", 0, NULL },
126: { CVS_REQ_QUESTIONABLE, "Questionable", 0, NULL },
127: { CVS_REQ_CASE, "Case", 0, NULL },
128: { CVS_REQ_UTF8, "Utf8", 0, NULL },
129: { CVS_REQ_ARGUMENT, "Argument", 0, NULL },
130: { CVS_REQ_ARGUMENTX, "Argumentx", 0, NULL },
131: { CVS_REQ_GLOBALOPT, "Global_option", 0, NULL },
132: { CVS_REQ_GZIPSTREAM, "Gzip-stream", 0, NULL },
133: { CVS_REQ_READCVSRC2, "read-cvsrc2", 0, NULL },
134: { CVS_REQ_READWRAP, "read-cvswrappers", 0, NULL },
135: { CVS_REQ_READIGNORE, "read-cvsignore", 0, NULL },
136: { CVS_REQ_ERRIFREADER, "Error-If-Reader", 0, NULL },
137: { CVS_REQ_VALIDRCSOPT, "Valid-RcsOptions", 0, NULL },
138: { CVS_REQ_SET, "Set", 0, NULL },
1.11 jfb 139: { CVS_REQ_XPANDMOD, "expand-modules", CVS_REQF_RESP, NULL },
1.1 jfb 140: { CVS_REQ_LOG, "log", 0, NULL },
1.11 jfb 141: { CVS_REQ_CO, "co", CVS_REQF_RESP, NULL },
142: { CVS_REQ_EXPORT, "export", CVS_REQF_RESP, NULL },
1.1 jfb 143: { CVS_REQ_RANNOTATE, "rannotate", 0, NULL },
144: { CVS_REQ_RDIFF, "rdiff", 0, NULL },
145: { CVS_REQ_RLOG, "rlog", 0, NULL },
1.11 jfb 146: { CVS_REQ_RTAG, "rtag", CVS_REQF_RESP, NULL },
147: { CVS_REQ_INIT, "init", CVS_REQF_RESP, NULL },
148: { CVS_REQ_STATUS, "status", CVS_REQF_RESP, NULL },
149: { CVS_REQ_UPDATE, "update", CVS_REQF_RESP, NULL },
1.1 jfb 150: { CVS_REQ_HISTORY, "history", 0, NULL },
1.11 jfb 151: { CVS_REQ_IMPORT, "import", CVS_REQF_RESP, NULL },
152: { CVS_REQ_ADD, "add", CVS_REQF_RESP, NULL },
153: { CVS_REQ_REMOVE, "remove", CVS_REQF_RESP, NULL },
154: { CVS_REQ_RELEASE, "release", CVS_REQF_RESP, NULL },
1.1 jfb 155: { CVS_REQ_ROOT, "Root", 0, NULL },
1.13 jfb 156: { CVS_REQ_VALIDRESP, "Valid-responses", 0, NULL },
1.11 jfb 157: { CVS_REQ_VALIDREQ, "valid-requests", CVS_REQF_RESP, NULL },
158: { CVS_REQ_VERSION, "version", CVS_REQF_RESP, NULL },
159: { CVS_REQ_NOOP, "noop", CVS_REQF_RESP, NULL },
160: { CVS_REQ_DIFF, "diff", CVS_REQF_RESP, NULL },
1.1 jfb 161: };
162:
163:
164: struct cvs_resp {
165: u_int resp_id;
166: char resp_str[32];
1.13 jfb 167: int (*resp_hdlr)(struct cvsroot *, int, char *);
1.1 jfb 168: } cvs_responses[] = {
169: { CVS_RESP_OK, "ok", cvs_resp_ok },
170: { CVS_RESP_ERROR, "error", cvs_resp_error },
171: { CVS_RESP_VALIDREQ, "Valid-requests", cvs_resp_validreq },
172: { CVS_RESP_M, "M", cvs_resp_m },
173: { CVS_RESP_MBINARY, "Mbinary", cvs_resp_m },
174: { CVS_RESP_MT, "MT", cvs_resp_m },
175: { CVS_RESP_E, "E", cvs_resp_m },
176: { CVS_RESP_F, "F", cvs_resp_m },
177: { CVS_RESP_CREATED, "Created", cvs_resp_updated },
178: { CVS_RESP_UPDATED, "Updated", cvs_resp_updated },
179: { CVS_RESP_UPDEXIST, "Update-existing", cvs_resp_updated },
1.15 jfb 180: { CVS_RESP_MERGED, "Merged", cvs_resp_updated },
1.1 jfb 181: { CVS_RESP_REMOVED, "Removed", cvs_resp_removed },
182: { CVS_RESP_CKSUM, "Checksum", cvs_resp_cksum },
183: { CVS_RESP_CLRSTATDIR, "Clear-static-directory", cvs_resp_statdir },
184: { CVS_RESP_SETSTATDIR, "Set-static-directory", cvs_resp_statdir },
185: { CVS_RESP_NEWENTRY, "New-entry", cvs_resp_newentry },
186: { CVS_RESP_CHECKEDIN, "Checked-in", cvs_resp_newentry },
187: { CVS_RESP_MODE, "Mode", cvs_resp_mode },
1.4 jfb 188: { CVS_RESP_MODTIME, "Mod-time", cvs_resp_modtime },
1.5 jfb 189: { CVS_RESP_MODXPAND, "Module-expansion", cvs_resp_modxpand },
190: { CVS_RESP_SETSTICKY, "Set-sticky", cvs_resp_sticky },
191: { CVS_RESP_CLRSTICKY, "Clear-sticky", cvs_resp_sticky },
1.14 jfb 192: { CVS_RESP_RCSDIFF, "Rcs-diff", cvs_resp_rcsdiff },
1.16 ! jfb 193: { CVS_RESP_TEMPLATE, "Template", cvs_resp_template },
1.1 jfb 194: };
195:
196:
1.5 jfb 197: static char *cvs_mt_stack[CVS_MTSTK_MAXDEPTH];
198: static u_int cvs_mtstk_depth = 0;
199:
1.8 jfb 200: static time_t cvs_modtime = 0;
1.7 jfb 201:
1.5 jfb 202:
1.1 jfb 203: #define CVS_NBREQ (sizeof(cvs_requests)/sizeof(cvs_requests[0]))
204: #define CVS_NBRESP (sizeof(cvs_responses)/sizeof(cvs_responses[0]))
205:
206: /* mask of requets supported by server */
207: static u_char cvs_server_validreq[CVS_REQ_MAX + 1];
208:
1.5 jfb 209: char *cvs_fcksum = NULL;
210:
211: mode_t cvs_lastmode = 0;
212:
1.13 jfb 213: static char cvs_proto_buf[4096];
214:
215: /*
216: * Output files for protocol logging when the CVS_CLIENT_LOG enviroment
217: * variable is set.
218: */
219: static int cvs_server_logon = 0;
220: static FILE *cvs_server_inlog = NULL;
221: static FILE *cvs_server_outlog = NULL;
222:
223:
224: /*
225: * cvs_connect()
226: *
227: * Open a client connection to the cvs server whose address is given in
228: * the <root> variable. The method used to connect depends on the
229: * setting of the CVS_RSH variable.
230: * Returns 0 on success, or -1 on failure.
231: */
232:
233: int
234: cvs_connect(struct cvsroot *root)
235: {
236: int argc, infd[2], outfd[2];
237: pid_t pid;
238: char *argv[16], *cvs_server_cmd, *vresp;
239:
240: if (pipe(infd) == -1) {
241: cvs_log(LP_ERRNO,
242: "failed to create input pipe for client connection");
243: return (-1);
244: }
245:
246: if (pipe(outfd) == -1) {
247: cvs_log(LP_ERRNO,
248: "failed to create output pipe for client connection");
249: (void)close(infd[0]);
250: (void)close(infd[1]);
251: return (-1);
252: }
253:
254: pid = fork();
255: if (pid == -1) {
256: cvs_log(LP_ERRNO, "failed to fork for cvs server connection");
257: return (-1);
258: }
259: if (pid == 0) {
260: if ((dup2(infd[0], STDIN_FILENO) == -1) ||
261: (dup2(outfd[1], STDOUT_FILENO) == -1)) {
262: cvs_log(LP_ERRNO,
263: "failed to setup standard streams for cvs server");
264: return (-1);
265: }
266: (void)close(infd[1]);
267: (void)close(outfd[0]);
268:
269: argc = 0;
270: argv[argc++] = cvs_rsh;
271:
272: if (root->cr_user != NULL) {
273: argv[argc++] = "-l";
274: argv[argc++] = root->cr_user;
275: }
276:
277:
278: cvs_server_cmd = getenv("CVS_SERVER");
279: if (cvs_server_cmd == NULL)
280: cvs_server_cmd = "cvs";
281:
282: argv[argc++] = root->cr_host;
283: argv[argc++] = cvs_server_cmd;
284: argv[argc++] = "server";
285: argv[argc] = NULL;
286:
287: execvp(argv[0], argv);
288: cvs_log(LP_ERRNO, "failed to exec");
289: exit(EX_OSERR);
290: }
291:
292: /* we are the parent */
293: (void)close(infd[0]);
294: (void)close(outfd[1]);
295:
296: root->cr_srvin = fdopen(infd[1], "w");
297: if (root->cr_srvin == NULL) {
298: cvs_log(LP_ERRNO, "failed to create pipe stream");
299: return (-1);
300: }
301:
302: root->cr_srvout = fdopen(outfd[0], "r");
303: if (root->cr_srvout == NULL) {
304: cvs_log(LP_ERRNO, "failed to create pipe stream");
305: return (-1);
306: }
307:
308: /* make the streams line-buffered */
309: (void)setvbuf(root->cr_srvin, NULL, _IOLBF, 0);
310: (void)setvbuf(root->cr_srvout, NULL, _IOLBF, 0);
311:
312: cvs_initlog();
313:
314: /*
315: * Send the server the list of valid responses, then ask for valid
316: * requests.
317: */
318:
319: vresp = cvs_resp_getvalid();
320: if (vresp == NULL) {
321: cvs_log(LP_ERR, "can't generate list of valid responses");
322: return (-1);
323: }
324:
325: if (cvs_sendreq(root, CVS_REQ_VALIDRESP, vresp) < 0) {
326: }
327: free(vresp);
328:
329: if (cvs_sendreq(root, CVS_REQ_VALIDREQ, NULL) < 0) {
330: cvs_log(LP_ERR, "failed to get valid requests from server");
331: return (-1);
332: }
333:
334: /* now share our global options with the server */
335: if (verbosity == 1)
336: cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-q");
337: else if (verbosity == 0)
338: cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-Q");
339:
340: if (cvs_nolog)
341: cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-l");
342: if (cvs_readonly)
343: cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-r");
344: if (cvs_trace)
345: cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-t");
346:
347: /* now send the CVSROOT to the server */
348: if (cvs_sendreq(root, CVS_REQ_ROOT, root->cr_dir) < 0)
349: return (-1);
350:
351: /* not sure why, but we have to send this */
352: if (cvs_sendreq(root, CVS_REQ_USEUNCHANGED, NULL) < 0)
353: return (-1);
354:
355: #ifdef CVS_ZLIB
356: /* if compression was requested, initialize it */
357: #endif
358:
359: cvs_log(LP_DEBUG, "connected to %s", root->cr_host);
360:
361: return (0);
362: }
363:
364:
365: /*
366: * cvs_disconnect()
367: *
368: * Disconnect from the cvs server.
369: */
370:
371: void
372: cvs_disconnect(struct cvsroot *root)
373: {
374: cvs_log(LP_DEBUG, "closing connection to %s", root->cr_host);
375: if (root->cr_srvin != NULL) {
376: (void)fclose(root->cr_srvin);
377: root->cr_srvin = NULL;
378: }
379: if (root->cr_srvout != NULL) {
380: (void)fclose(root->cr_srvout);
381: root->cr_srvout = NULL;
382: }
383: }
384:
1.1 jfb 385:
386: /*
387: * cvs_req_getbyid()
388: *
389: */
390:
1.13 jfb 391: struct cvs_req*
1.1 jfb 392: cvs_req_getbyid(int reqid)
393: {
394: u_int i;
395:
396: for (i = 0; i < CVS_NBREQ; i++)
397: if (cvs_requests[i].req_id == reqid)
1.13 jfb 398: return &(cvs_requests[i]);
1.1 jfb 399: return (NULL);
400: }
401:
402:
403: /*
404: * cvs_req_getbyname()
405: */
406:
1.13 jfb 407: struct cvs_req*
1.1 jfb 408: cvs_req_getbyname(const char *rname)
409: {
410: u_int i;
411:
412: for (i = 0; i < CVS_NBREQ; i++)
413: if (strcmp(cvs_requests[i].req_str, rname) == 0)
1.13 jfb 414: return &(cvs_requests[i]);
1.1 jfb 415:
1.13 jfb 416: return (NULL);
1.1 jfb 417: }
418:
419:
420: /*
421: * cvs_req_getvalid()
422: *
423: * Build a space-separated list of all the requests that this protocol
424: * implementation supports.
425: */
426:
427: char*
428: cvs_req_getvalid(void)
429: {
430: u_int i;
431: size_t len;
432: char *vrstr;
433: BUF *buf;
434:
435: buf = cvs_buf_alloc(512, BUF_AUTOEXT);
436: if (buf == NULL)
437: return (NULL);
438:
439: cvs_buf_set(buf, cvs_requests[0].req_str,
440: strlen(cvs_requests[0].req_str), 0);
441:
442: for (i = 1; i < CVS_NBREQ; i++) {
443: if ((cvs_buf_putc(buf, ' ') < 0) ||
444: (cvs_buf_append(buf, cvs_requests[i].req_str,
445: strlen(cvs_requests[i].req_str)) < 0)) {
446: cvs_buf_free(buf);
447: return (NULL);
448: }
449: }
450:
451: /* NUL-terminate */
452: if (cvs_buf_putc(buf, '\0') < 0) {
453: cvs_buf_free(buf);
454: return (NULL);
455: }
456:
457: len = cvs_buf_size(buf);
458: vrstr = (char *)malloc(len);
459:
460: cvs_buf_copy(buf, 0, vrstr, len);
461:
462: cvs_buf_free(buf);
463:
464: return (vrstr);
465: }
466:
467:
468: /*
469: * cvs_req_handle()
470: *
471: * Generic request handler dispatcher.
472: */
473:
474: int
475: cvs_req_handle(char *line)
476: {
1.16 ! jfb 477: u_int i;
! 478: char *cp, *cmd;
! 479:
! 480: cmd = line;
! 481:
! 482: cp = strchr(cmd, ' ');
! 483: if (cp != NULL)
! 484: *(cp++) = '\0';
! 485:
! 486: for (i = 0; i < CVS_NBREQ; i++) {
! 487: if (strcmp(cvs_requests[i].req_str, cmd) == 0) {
! 488: if (cvs_requests[i].req_hdlr == NULL) {
! 489: cvs_log(LP_ERR,
! 490: "unimplemented request handler for `%s'",
! 491: cmd);
! 492: break;
! 493: }
! 494: else
! 495: return (*cvs_requests[i].req_hdlr)
! 496: (cvs_requests[i].req_id, cp);
! 497: }
! 498: }
! 499:
! 500: /* unhandled */
! 501: return (-1);
1.1 jfb 502: }
503:
504:
505: /*
506: * cvs_resp_getbyid()
507: *
508: */
509:
1.13 jfb 510: struct cvs_resp*
1.1 jfb 511: cvs_resp_getbyid(int respid)
512: {
513: u_int i;
514:
515: for (i = 0; i < CVS_NBREQ; i++)
1.13 jfb 516: if (cvs_responses[i].resp_id == (u_int)respid)
517: return &(cvs_responses[i]);
1.1 jfb 518: return (NULL);
519: }
520:
521:
522: /*
523: * cvs_resp_getbyname()
524: */
525:
1.13 jfb 526: struct cvs_resp*
1.1 jfb 527: cvs_resp_getbyname(const char *rname)
528: {
529: u_int i;
530:
531: for (i = 0; i < CVS_NBREQ; i++)
532: if (strcmp(cvs_responses[i].resp_str, rname) == 0)
1.13 jfb 533: return &(cvs_responses[i]);
1.1 jfb 534:
1.13 jfb 535: return (NULL);
1.1 jfb 536: }
537:
538:
539: /*
540: * cvs_resp_getvalid()
541: *
542: * Build a space-separated list of all the responses that this protocol
543: * implementation supports.
544: */
545:
546: char*
547: cvs_resp_getvalid(void)
548: {
549: u_int i;
550: size_t len;
551: char *vrstr;
552: BUF *buf;
553:
554: buf = cvs_buf_alloc(512, BUF_AUTOEXT);
555: if (buf == NULL)
556: return (NULL);
557:
558: cvs_buf_set(buf, cvs_responses[0].resp_str,
559: strlen(cvs_responses[0].resp_str), 0);
560:
561: for (i = 1; i < CVS_NBRESP; i++) {
562: if ((cvs_buf_putc(buf, ' ') < 0) ||
563: (cvs_buf_append(buf, cvs_responses[i].resp_str,
564: strlen(cvs_responses[i].resp_str)) < 0)) {
565: cvs_buf_free(buf);
566: return (NULL);
567: }
568: }
569:
570: /* NUL-terminate */
571: if (cvs_buf_putc(buf, '\0') < 0) {
572: cvs_buf_free(buf);
573: return (NULL);
574: }
575:
576: len = cvs_buf_size(buf);
577: vrstr = (char *)malloc(len);
578:
579: cvs_buf_copy(buf, 0, vrstr, len);
580: cvs_buf_free(buf);
581:
582: return (vrstr);
583: }
584:
585:
586: /*
587: * cvs_resp_handle()
588: *
589: * Generic response handler dispatcher. The handler expects the first line
590: * of the command as single argument.
591: * Returns the return value of the command on success, or -1 on failure.
592: */
593:
594: int
1.13 jfb 595: cvs_resp_handle(struct cvsroot *root, char *line)
1.1 jfb 596: {
597: u_int i;
598: char *cp, *cmd;
599:
600: cmd = line;
601:
602: cp = strchr(cmd, ' ');
603: if (cp != NULL)
604: *(cp++) = '\0';
605:
606: for (i = 0; i < CVS_NBRESP; i++) {
1.16 ! jfb 607: if (strcmp(cvs_responses[i].resp_str, cmd) == 0) {
! 608: if (cvs_responses[i].resp_hdlr == NULL) {
! 609: cvs_log(LP_ERRNO,
! 610: "unimplemented response handler for `%s'",
! 611: cmd);
! 612: return (-1);
! 613: }
! 614: else
! 615: return (*cvs_responses[i].resp_hdlr)
! 616: (root, cvs_responses[i].resp_id, cp);
! 617: }
1.1 jfb 618: }
619:
620: /* unhandled */
621: return (-1);
622: }
623:
624:
625: /*
626: * cvs_resp_validreq()
627: *
628: * Handler for the `Valid-requests' response. The list of valid requests is
629: * split on spaces and each request's entry in the valid request array is set
630: * to 1 to indicate the validity.
631: * Returns 0 on success, or -1 on failure.
632: */
633:
634: static int
1.13 jfb 635: cvs_resp_validreq(struct cvsroot *root, int type, char *line)
1.1 jfb 636: {
637: char *sp, *ep;
1.13 jfb 638: struct cvs_req *req;
1.1 jfb 639:
640: /* parse the requests */
641: sp = line;
642: do {
643: ep = strchr(sp, ' ');
644: if (ep != NULL)
645: *ep = '\0';
646:
1.13 jfb 647: req = cvs_req_getbyname(sp);
648: if (req != NULL)
649: cvs_server_validreq[req->req_id] = 1;
1.1 jfb 650:
651: if (ep != NULL)
652: sp = ep + 1;
653: } while (ep != NULL);
654:
655: return (0);
656: }
657:
658:
659: /*
660: * cvs_resp_m()
661: *
1.4 jfb 662: * Handler for the `M', 'MT', `F' and `E' responses.
1.1 jfb 663: */
664:
665: static int
1.13 jfb 666: cvs_resp_m(struct cvsroot *root, int type, char *line)
1.1 jfb 667: {
1.5 jfb 668: char *cp;
1.1 jfb 669: FILE *stream;
670:
671: stream = NULL;
672:
673: switch (type) {
674: case CVS_RESP_F:
675: fflush(stderr);
676: return (0);
677: case CVS_RESP_M:
678: stream = stdout;
679: break;
680: case CVS_RESP_E:
681: stream = stderr;
682: break;
683: case CVS_RESP_MT:
1.5 jfb 684: if (*line == '+') {
685: if (cvs_mtstk_depth == CVS_MTSTK_MAXDEPTH) {
686: cvs_log(LP_ERR,
687: "MT scope stack has reached max depth");
688: return (-1);
689: }
1.6 jfb 690: cvs_mt_stack[cvs_mtstk_depth] = strdup(line + 1);
691: if (cvs_mt_stack[cvs_mtstk_depth] == NULL)
1.5 jfb 692: return (-1);
1.6 jfb 693: cvs_mtstk_depth++;
1.5 jfb 694: }
695: else if (*line == '-') {
696: if (cvs_mtstk_depth == 0) {
697: cvs_log(LP_ERR, "MT scope stack underflow");
698: return (-1);
699: }
1.6 jfb 700: else if (strcmp(line + 1,
701: cvs_mt_stack[cvs_mtstk_depth - 1]) != 0) {
1.5 jfb 702: cvs_log(LP_ERR, "mismatch in MT scope stack");
703: return (-1);
704: }
705: free(cvs_mt_stack[cvs_mtstk_depth--]);
706: }
707: else {
708: if (strcmp(line, "newline") == 0)
709: putc('\n', stdout);
710: else if (strncmp(line, "fname ", 6) == 0)
711: printf("%s", line + 6);
712: else {
713: /* assume text */
714: cp = strchr(line, ' ');
715: if (cp != NULL)
716: printf("%s", cp + 1);
717: }
718: }
719:
1.6 jfb 720: return (0);
1.1 jfb 721: case CVS_RESP_MBINARY:
1.5 jfb 722: cvs_log(LP_WARN, "Mbinary not supported in client yet");
1.1 jfb 723: break;
724: }
725:
726: fputs(line, stream);
727: fputc('\n', stream);
728:
729: return (0);
730: }
731:
732:
733: /*
734: * cvs_resp_ok()
735: *
736: * Handler for the `ok' response. This handler's job is to
737: */
738:
739: static int
1.13 jfb 740: cvs_resp_ok(struct cvsroot *root, int type, char *line)
1.1 jfb 741: {
742: return (1);
743: }
744:
745:
746: /*
747: * cvs_resp_error()
748: *
749: * Handler for the `error' response. This handler's job is to
750: */
751:
752: static int
1.13 jfb 753: cvs_resp_error(struct cvsroot *root, int type, char *line)
1.1 jfb 754: {
755: return (1);
756: }
757:
758:
759: /*
760: * cvs_resp_statdir()
761: *
762: * Handler for the `Clear-static-directory' and `Set-static-directory'
763: * responses.
764: */
765:
766: static int
1.13 jfb 767: cvs_resp_statdir(struct cvsroot *root, int type, char *line)
1.1 jfb 768: {
769: int fd;
1.6 jfb 770: char rpath[MAXPATHLEN], statpath[MAXPATHLEN];
771:
1.13 jfb 772: cvs_getln(root, rpath, sizeof(rpath));
1.1 jfb 773:
774: snprintf(statpath, sizeof(statpath), "%s/%s", line,
775: CVS_PATH_STATICENTRIES);
776:
777: if ((type == CVS_RESP_CLRSTATDIR) &&
1.5 jfb 778: (unlink(statpath) == -1) && (errno != ENOENT)) {
1.1 jfb 779: cvs_log(LP_ERRNO, "failed to unlink %s file",
780: CVS_PATH_STATICENTRIES);
781: return (-1);
782: }
783: else if (type == CVS_RESP_SETSTATDIR) {
1.5 jfb 784: fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0400);
1.1 jfb 785: if (fd == -1) {
786: cvs_log(LP_ERRNO, "failed to create %s file",
787: CVS_PATH_STATICENTRIES);
788: return (-1);
789: }
790: (void)close(fd);
1.5 jfb 791:
792: }
793:
794: return (0);
795: }
796:
797: /*
798: * cvs_resp_sticky()
799: *
800: * Handler for the `Clear-sticky' and `Set-sticky' responses.
801: */
802:
803: static int
1.13 jfb 804: cvs_resp_sticky(struct cvsroot *root, int type, char *line)
1.5 jfb 805: {
1.9 jfb 806: size_t len;
1.6 jfb 807: char rpath[MAXPATHLEN];
808: struct stat st;
1.7 jfb 809: CVSFILE *cf;
1.6 jfb 810:
1.9 jfb 811: /* remove trailing slash */
812: len = strlen(line);
813: if ((len > 0) && (line[len - 1] == '/'))
814: line[--len] = '\0';
815:
1.6 jfb 816: /* get the remote path */
1.13 jfb 817: cvs_getln(root, rpath, sizeof(rpath));
1.6 jfb 818:
819: /* if the directory doesn't exist, create it */
820: if (stat(line, &st) == -1) {
1.7 jfb 821: /* attempt to create it */
1.6 jfb 822: if (errno != ENOENT) {
823: cvs_log(LP_ERRNO, "failed to stat %s", line);
824: }
1.7 jfb 825: else {
826: cf = cvs_file_create(line, DT_DIR, 0755);
827: if (cf == NULL)
828: return (-1);
829: cf->cf_ddat->cd_repo = strdup(line);
1.14 jfb 830: cf->cf_ddat->cd_root = root;
831: root->cr_ref++;
1.7 jfb 832: cvs_mkadmin(cf, 0755);
833:
834: cvs_file_free(cf);
1.6 jfb 835: }
836: }
837:
1.5 jfb 838: if (type == CVS_RESP_CLRSTICKY) {
839: }
840: else if (type == CVS_RESP_SETSTICKY) {
1.1 jfb 841: }
842:
843: return (0);
844: }
845:
846:
847: /*
848: * cvs_resp_newentry()
849: *
850: * Handler for the `New-entry' response and `Checked-in' responses.
851: */
852:
853: static int
1.13 jfb 854: cvs_resp_newentry(struct cvsroot *root, int type, char *line)
1.1 jfb 855: {
1.15 jfb 856: char entbuf[128];
1.1 jfb 857: CVSENTRIES *entfile;
858:
859: /* get the remote path */
1.13 jfb 860: cvs_getln(root, entbuf, sizeof(entbuf));
1.1 jfb 861:
862: /* get the new Entries line */
1.13 jfb 863: if (cvs_getln(root, entbuf, sizeof(entbuf)) < 0)
1.1 jfb 864: return (-1);
865:
1.15 jfb 866: entfile = cvs_ent_open(line, O_WRONLY);
1.1 jfb 867: if (entfile == NULL)
868: return (-1);
1.3 jfb 869: cvs_ent_addln(entfile, entbuf);
1.1 jfb 870: cvs_ent_close(entfile);
871:
872: return (0);
873: }
874:
875:
876: /*
877: * cvs_resp_cksum()
878: *
879: * Handler for the `Checksum' response. We store the checksum received for
880: * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>.
881: * Upon next file reception, the handler checks to see if there is a stored
882: * checksum.
883: * The file handler must make sure that the checksums match and free the
884: * checksum buffer once it's done to indicate there is no further checksum.
885: */
886:
887: static int
1.13 jfb 888: cvs_resp_cksum(struct cvsroot *root, int type, char *line)
1.1 jfb 889: {
890: if (cvs_fcksum != NULL) {
891: cvs_log(LP_WARN, "unused checksum");
892: free(cvs_fcksum);
893: }
894:
895: cvs_fcksum = strdup(line);
896: if (cvs_fcksum == NULL) {
897: cvs_log(LP_ERRNO, "failed to copy checksum string");
898: return (-1);
899: }
900:
1.4 jfb 901: return (0);
902: }
903:
904:
905: /*
906: * cvs_resp_modtime()
907: *
908: * Handler for the `Mod-time' file update modifying response. The timestamp
909: * given is used to set the last modification time on the next file that
910: * will be received.
911: */
912:
913: static int
1.13 jfb 914: cvs_resp_modtime(struct cvsroot *root, int type, char *line)
1.4 jfb 915: {
1.8 jfb 916: int i;
917: long off;
918: char sign, mon[8], gmt[8], hr[4], min[4], *ep;
919: struct tm cvs_tm;
920:
921: memset(&cvs_tm, 0, sizeof(cvs_tm));
1.9 jfb 922: sscanf(line, "%d %3s %d %2d:%2d:%2d %5s", &cvs_tm.tm_mday, mon,
1.8 jfb 923: &cvs_tm.tm_year, &cvs_tm.tm_hour, &cvs_tm.tm_min,
924: &cvs_tm.tm_sec, gmt);
925: cvs_tm.tm_year -= 1900;
1.9 jfb 926: cvs_tm.tm_isdst = -1;
1.8 jfb 927:
928: if (*gmt == '-') {
1.9 jfb 929: sscanf(gmt, "%c%2s%2s", &sign, hr, min);
1.8 jfb 930: cvs_tm.tm_gmtoff = strtol(hr, &ep, 10);
931: if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
932: (cvs_tm.tm_gmtoff == LONG_MAX) ||
933: (*ep != '\0')) {
934: cvs_log(LP_ERR,
935: "parse error in GMT hours specification `%s'", hr);
936: cvs_tm.tm_gmtoff = 0;
937: }
938: else {
939: /* get seconds */
940: cvs_tm.tm_gmtoff *= 3600;
1.7 jfb 941:
1.8 jfb 942: /* add the minutes */
943: off = strtol(min, &ep, 10);
944: if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
945: (cvs_tm.tm_gmtoff == LONG_MAX) ||
946: (*ep != '\0')) {
947: cvs_log(LP_ERR,
948: "parse error in GMT minutes "
949: "specification `%s'", min);
950: }
951: else
952: cvs_tm.tm_gmtoff += off * 60;
953: }
954: }
955: if (sign == '-')
956: cvs_tm.tm_gmtoff = -cvs_tm.tm_gmtoff;
1.7 jfb 957:
1.8 jfb 958: for (i = 0; i < (int)(sizeof(cvs_months)/sizeof(cvs_months[0])); i++) {
959: if (strcmp(cvs_months[i], mon) == 0) {
960: cvs_tm.tm_mon = i;
961: break;
962: }
1.7 jfb 963: }
964:
1.8 jfb 965: cvs_modtime = mktime(&cvs_tm);
1.1 jfb 966: return (0);
967: }
968:
969:
970: /*
971: * cvs_resp_updated()
972: *
1.6 jfb 973: * Handler for the `Updated' and `Created' responses.
1.1 jfb 974: */
975:
976: static int
1.13 jfb 977: cvs_resp_updated(struct cvsroot *root, int type, char *line)
1.1 jfb 978: {
1.7 jfb 979: size_t len;
1.14 jfb 980: mode_t fmode;
1.7 jfb 981: char tbuf[32], path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN];
1.14 jfb 982: BUF *fbuf;
1.7 jfb 983: CVSENTRIES *ef;
1.6 jfb 984: struct cvs_ent *ep;
1.1 jfb 985:
1.13 jfb 986: ep = NULL;
987:
1.15 jfb 988: len = strlen(tbuf);
989: if ((len > 0) && (tbuf[len - 1] == '\n'))
990: tbuf[--len] = '\0';
991:
992: /* read the remote path of the file */
993: cvs_getln(root, path, sizeof(path));
994:
995: /* read the new entry */
996: cvs_getln(root, path, sizeof(path));
997: ep = cvs_ent_parse(path);
998: if (ep == NULL)
999: return (-1);
1000: snprintf(path, sizeof(path), "%s/%s", line, ep->ce_name);
1.6 jfb 1001:
1.14 jfb 1002:
1.15 jfb 1003: if (type == CVS_RESP_CREATED) {
1.7 jfb 1004: /* set the timestamp as the last one received from Mod-time */
1.8 jfb 1005: ep->ce_timestamp = ctime_r(&cvs_modtime, tbuf);
1.7 jfb 1006:
1007: ef = cvs_ent_open(line, O_WRONLY);
1008: if (ef == NULL)
1009: return (-1);
1010:
1011: cvs_ent_add(ef, ep);
1012: cvs_ent_close(ef);
1.1 jfb 1013: }
1014: else if (type == CVS_RESP_UPDEXIST) {
1015: }
1016: else if (type == CVS_RESP_UPDATED) {
1017: }
1018:
1.14 jfb 1019: fbuf = cvs_recvfile(root, &fmode);
1020: if (fbuf == NULL)
1.1 jfb 1021: return (-1);
1.14 jfb 1022:
1023: cvs_buf_write(fbuf, path, fmode);
1.1 jfb 1024:
1025: /* now see if there is a checksum */
1.6 jfb 1026: if (cvs_fcksum != NULL) {
1.14 jfb 1027: if (cvs_cksum(path, cksum_buf, sizeof(cksum_buf)) < 0) {
1.6 jfb 1028: }
1029:
1030: if (strcmp(cksum_buf, cvs_fcksum) != 0) {
1031: cvs_log(LP_ERR, "checksum error on received file");
1032: (void)unlink(line);
1033: }
1.1 jfb 1034:
1.6 jfb 1035: free(cvs_fcksum);
1036: cvs_fcksum = NULL;
1.1 jfb 1037: }
1038:
1039: return (0);
1040: }
1041:
1042:
1043: /*
1044: * cvs_resp_removed()
1045: *
1046: * Handler for the `Updated' response.
1047: */
1048:
1049: static int
1.13 jfb 1050: cvs_resp_removed(struct cvsroot *root, int type, char *line)
1.1 jfb 1051: {
1052: return (0);
1053: }
1054:
1055:
1056: /*
1057: * cvs_resp_mode()
1058: *
1059: * Handler for the `Mode' response.
1060: */
1061:
1062: static int
1.13 jfb 1063: cvs_resp_mode(struct cvsroot *root, int type, char *line)
1.1 jfb 1064: {
1065: if (cvs_strtomode(line, &cvs_lastmode) < 0) {
1066: return (-1);
1067: }
1.5 jfb 1068: return (0);
1069: }
1070:
1071:
1072: /*
1073: * cvs_resp_modxpand()
1074: *
1075: * Handler for the `Module-expansion' response.
1076: */
1077:
1078: static int
1.13 jfb 1079: cvs_resp_modxpand(struct cvsroot *root, int type, char *line)
1.5 jfb 1080: {
1.1 jfb 1081: return (0);
1082: }
1083:
1.14 jfb 1084: /*
1085: * cvs_resp_rcsdiff()
1086: *
1087: * Handler for the `Rcs-diff' response.
1088: */
1089:
1090: static int
1091: cvs_resp_rcsdiff(struct cvsroot *root, int type, char *line)
1092: {
1093: char file[MAXPATHLEN], buf[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN];
1094: char *fname, *orig, *patch;
1095: mode_t fmode;
1096: BUF *res, *fcont, *patchbuf;
1097: CVSENTRIES *entf;
1098: struct cvs_ent *ent;
1099:
1100: /* get remote path and build local path of file to be patched */
1101: cvs_getln(root, buf, sizeof(buf));
1102: fname = strrchr(buf, '/');
1103: if (fname == NULL)
1104: fname = buf;
1105: snprintf(file, sizeof(file), "%s%s", line, fname);
1106:
1107: /* get updated entry fields */
1108: cvs_getln(root, buf, sizeof(buf));
1109: ent = cvs_ent_parse(buf);
1110: if (ent == NULL) {
1111: return (-1);
1112: }
1113:
1114: patchbuf = cvs_recvfile(root, &fmode);
1115: fcont = cvs_buf_load(file, BUF_AUTOEXT);
1116: if (fcont == NULL)
1117: return (-1);
1118:
1119: cvs_buf_putc(patchbuf, '\0');
1120: cvs_buf_putc(fcont, '\0');
1121: orig = cvs_buf_release(fcont);
1122: patch = cvs_buf_release(patchbuf);
1123:
1124: res = rcs_patch(orig, patch);
1125: if (res == NULL)
1126: return (-1);
1127:
1128: cvs_buf_write(res, file, fmode);
1129:
1130: /* now see if there is a checksum */
1131: if (cvs_fcksum != NULL) {
1132: if (cvs_cksum(file, cksum_buf, sizeof(cksum_buf)) < 0) {
1133: }
1134:
1135: if (strcmp(cksum_buf, cvs_fcksum) != 0) {
1136: cvs_log(LP_ERR, "checksum error on received file");
1137: (void)unlink(file);
1138: }
1139:
1140: free(cvs_fcksum);
1141: cvs_fcksum = NULL;
1142: }
1143:
1144: /* update revision in entries */
1145: entf = cvs_ent_open(line, O_WRONLY);
1146: if (entf == NULL)
1147: return (-1);
1148:
1149: cvs_ent_close(entf);
1.16 ! jfb 1150:
! 1151: return (0);
! 1152: }
! 1153:
! 1154:
! 1155: /*
! 1156: * cvs_resp_template()
! 1157: *
! 1158: * Handler for the `Template' response.
! 1159: */
! 1160:
! 1161: static int
! 1162: cvs_resp_template(struct cvsroot *root, int type, char *line)
! 1163: {
! 1164: mode_t mode;
! 1165: BUF *tmpl;
! 1166:
! 1167: tmpl = cvs_recvfile(root, &mode);
! 1168: if (tmpl == NULL)
! 1169: return (-1);
1.14 jfb 1170:
1171: return (0);
1172: }
1173:
1.1 jfb 1174:
1175: /*
1176: * cvs_sendfile()
1177: *
1178: * Send the mode and size of a file followed by the file's contents.
1179: * Returns 0 on success, or -1 on failure.
1180: */
1181:
1182: int
1.13 jfb 1183: cvs_sendfile(struct cvsroot *root, const char *path)
1.1 jfb 1184: {
1185: int fd;
1186: ssize_t ret;
1187: char buf[4096];
1188: struct stat st;
1189:
1190: if (stat(path, &st) == -1) {
1191: cvs_log(LP_ERRNO, "failed to stat `%s'", path);
1192: return (-1);
1193: }
1194:
1195: fd = open(path, O_RDONLY, 0);
1196: if (fd == -1) {
1197: return (-1);
1198: }
1199:
1200: if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
1201: return (-1);
1202:
1.13 jfb 1203: cvs_sendln(root, buf);
1.1 jfb 1204: snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
1.13 jfb 1205: cvs_sendln(root, buf);
1.1 jfb 1206:
1207: while ((ret = read(fd, buf, sizeof(buf))) != 0) {
1208: if (ret == -1) {
1209: cvs_log(LP_ERRNO, "failed to read file `%s'", path);
1210: return (-1);
1211: }
1212:
1.13 jfb 1213: cvs_sendraw(root, buf, (size_t)ret);
1.1 jfb 1214:
1215: }
1216:
1217: (void)close(fd);
1218:
1219: return (0);
1220: }
1221:
1222:
1223: /*
1224: * cvs_recvfile()
1225: *
1226: * Receive the mode and size of a file followed the file's contents and
1227: * create or update the file whose path is <path> with the received
1228: * information.
1229: */
1230:
1.14 jfb 1231: BUF*
1232: cvs_recvfile(struct cvsroot *root, mode_t *mode)
1.1 jfb 1233: {
1234: size_t len;
1235: ssize_t ret;
1236: off_t fsz, cnt;
1237: char buf[4096], *ep;
1.14 jfb 1238: BUF *fbuf;
1239:
1240: fbuf = cvs_buf_alloc(sizeof(buf), BUF_AUTOEXT);
1241: if (fbuf == NULL)
1242: return (NULL);
1.1 jfb 1243:
1.13 jfb 1244: if ((cvs_getln(root, buf, sizeof(buf)) < 0) ||
1.14 jfb 1245: (cvs_strtomode(buf, mode) < 0)) {
1246: return (NULL);
1.1 jfb 1247: }
1248:
1.13 jfb 1249: cvs_getln(root, buf, sizeof(buf));
1.1 jfb 1250:
1251: fsz = (off_t)strtol(buf, &ep, 10);
1252: if (*ep != '\0') {
1253: cvs_log(LP_ERR, "parse error in file size transmission");
1.14 jfb 1254: return (NULL);
1.1 jfb 1255: }
1256:
1257: cnt = 0;
1258: do {
1259: len = MIN(sizeof(buf), (size_t)(fsz - cnt));
1.6 jfb 1260: if (len == 0)
1261: break;
1.13 jfb 1262: ret = cvs_recvraw(root, buf, len);
1.1 jfb 1263: if (ret == -1) {
1.14 jfb 1264: cvs_buf_free(fbuf);
1265: return (NULL);
1.1 jfb 1266: }
1267:
1.14 jfb 1268: if (cvs_buf_append(fbuf, buf, (size_t)ret) == -1) {
1269: cvs_log(LP_ERR,
1270: "failed to append received file data");
1271: cvs_buf_free(fbuf);
1272: return (NULL);
1.1 jfb 1273: }
1274:
1275: cnt += (off_t)ret;
1276: } while (cnt < fsz);
1277:
1.14 jfb 1278: return (fbuf);
1.1 jfb 1279: }
1.12 jfb 1280:
1281:
1282: /*
1283: * cvs_sendreq()
1284: *
1285: * Send a request to the server of type <rid>, with optional arguments
1286: * contained in <arg>, which should not be terminated by a newline.
1287: * Returns 0 on success, or -1 on failure.
1288: */
1289:
1290: int
1.13 jfb 1291: cvs_sendreq(struct cvsroot *root, u_int rid, const char *arg)
1.12 jfb 1292: {
1293: int ret;
1.13 jfb 1294: struct cvs_req *req;
1.12 jfb 1295:
1296: if (root->cr_srvin == NULL) {
1297: cvs_log(LP_ERR, "cannot send request: Not connected");
1298: return (-1);
1299: }
1300:
1.13 jfb 1301: req = cvs_req_getbyid(rid);
1302: if (req == NULL) {
1.12 jfb 1303: cvs_log(LP_ERR, "unsupported request type %u", rid);
1304: return (-1);
1305: }
1306:
1.13 jfb 1307: snprintf(cvs_proto_buf, sizeof(cvs_proto_buf), "%s%s%s\n",
1308: req->req_str, (arg == NULL) ? "" : " ", (arg == NULL) ? "" : arg);
1.12 jfb 1309:
1310: if (cvs_server_inlog != NULL)
1.13 jfb 1311: fputs(cvs_proto_buf, cvs_server_inlog);
1.12 jfb 1312:
1.13 jfb 1313: ret = fputs(cvs_proto_buf, root->cr_srvin);
1.12 jfb 1314: if (ret == EOF) {
1315: cvs_log(LP_ERRNO, "failed to send request to server");
1316: return (-1);
1317: }
1318:
1.13 jfb 1319: if (req->req_flags & CVS_REQF_RESP)
1.12 jfb 1320: ret = cvs_getresp(root);
1321:
1.13 jfb 1322: return (ret);
1.12 jfb 1323: }
1324:
1325:
1326: /*
1327: * cvs_getresp()
1328: *
1329: * Get a response from the server. This call will actually read and handle
1330: * responses from the server until one of the response handlers returns
1331: * non-zero (either an error occured or the end of the response was reached).
1332: * Returns the number of handled commands on success, or -1 on failure.
1333: */
1334:
1335: int
1.13 jfb 1336: cvs_getresp(struct cvsroot *root)
1.12 jfb 1337: {
1.13 jfb 1338: int nbcmd, ret;
1339: size_t len;
1.12 jfb 1340:
1341: nbcmd = 0;
1342:
1343: do {
1344: /* wait for incoming data */
1.13 jfb 1345: if (fgets(cvs_proto_buf, sizeof(cvs_proto_buf),
1.12 jfb 1346: root->cr_srvout) == NULL) {
1347: if (feof(root->cr_srvout))
1348: return (0);
1349: cvs_log(LP_ERRNO,
1350: "failed to read response from server");
1351: return (-1);
1352: }
1353:
1354: if (cvs_server_outlog != NULL)
1.13 jfb 1355: fputs(cvs_proto_buf, cvs_server_outlog);
1.12 jfb 1356:
1.13 jfb 1357: if ((len = strlen(cvs_proto_buf)) != 0) {
1358: if (cvs_proto_buf[len - 1] != '\n') {
1.12 jfb 1359: /* truncated line */
1360: }
1361: else
1.13 jfb 1362: cvs_proto_buf[--len] = '\0';
1.12 jfb 1363: }
1364:
1.13 jfb 1365: ret = cvs_resp_handle(root, cvs_proto_buf);
1.12 jfb 1366: nbcmd++;
1367: } while (ret == 0);
1368:
1369: if (ret > 0)
1370: ret = nbcmd;
1371: return (ret);
1372: }
1373:
1374:
1375: /*
1.13 jfb 1376: * cvs_getln()
1377: *
1378: * Get a line from the server's output and store it in <lbuf>. The terminating
1379: * newline character is stripped from the result.
1380: */
1381:
1382: int
1383: cvs_getln(struct cvsroot *root, char *lbuf, size_t len)
1384: {
1385: size_t rlen;
1386:
1387: if (fgets(lbuf, len, root->cr_srvout) == NULL) {
1388: if (ferror(root->cr_srvout)) {
1389: cvs_log(LP_ERRNO, "failed to read line from server");
1390: return (-1);
1391: }
1392:
1393: if (feof(root->cr_srvout))
1394: *lbuf = '\0';
1395: }
1396:
1397: if (cvs_server_outlog != NULL)
1398: fputs(lbuf, cvs_server_outlog);
1399:
1400: rlen = strlen(lbuf);
1401: if ((rlen > 0) && (lbuf[rlen - 1] == '\n'))
1402: lbuf[--rlen] = '\0';
1403:
1404: return (0);
1405: }
1406:
1407: #ifdef notyet
1408: /*
1.12 jfb 1409: * cvs_sendresp()
1410: *
1411: * Send a response to the client of type <rid>, with optional arguments
1412: * contained in <arg>, which should not be terminated by a newline.
1413: * Returns 0 on success, or -1 on failure.
1414: */
1415:
1416: int
1417: cvs_sendresp(u_int rid, const char *arg)
1418: {
1419: int ret;
1420: size_t len;
1421: const char *resp;
1422:
1423: resp = cvs_resp_getbyid(rid);
1.13 jfb 1424: if (resp == NULL) {
1.12 jfb 1425: cvs_log(LP_ERR, "unsupported response type %u", rid);
1426: return (-1);
1427: }
1428:
1.13 jfb 1429: snprintf(cvs_proto_buf, sizeof(cvs_proto_buf), "%s %s\n", resp,
1.12 jfb 1430: (arg == NULL) ? "" : arg);
1431:
1432: ret = fputs(resp, stdout);
1433: if (ret == EOF) {
1434: cvs_log(LP_ERRNO, "failed to send response to client");
1435: }
1436: else {
1437: if (arg != NULL)
1438: ret = fprintf(stdout, " %s", arg);
1439: putc('\n', stdout);
1440: }
1441: return (0);
1442: }
1443:
1444:
1445: /*
1446: * cvs_getreq()
1447: *
1448: * Get a request from the client.
1449: */
1450:
1451: int
1452: cvs_getreq(void)
1453: {
1454: int nbcmd;
1455:
1456: nbcmd = 0;
1457:
1458: do {
1459: /* wait for incoming data */
1.13 jfb 1460: if (fgets(cvs_proto_buf, sizeof(cvs_proto_buf),
1.12 jfb 1461: stdin) == NULL) {
1462: if (feof(stdin))
1463: return (0);
1464: cvs_log(LP_ERRNO,
1465: "failed to read request from client");
1466: return (-1);
1467: }
1468:
1.13 jfb 1469: if ((len = strlen(cvs_proto_buf)) != 0) {
1470: if (cvs_proto_buf[len - 1] != '\n') {
1.12 jfb 1471: /* truncated line */
1472: }
1473: else
1.13 jfb 1474: cvs_proto_buf[--len] = '\0';
1.12 jfb 1475: }
1476:
1.13 jfb 1477: ret = cvs_resp_handle(cvs_proto_buf);
1.12 jfb 1478: } while (ret == 0);
1479: }
1480: #endif
1481:
1482:
1483: /*
1484: * cvs_sendln()
1485: *
1486: * Send a single line <line> string to the server. The line is sent as is,
1487: * without any modifications.
1488: * Returns 0 on success, or -1 on failure.
1489: */
1490:
1491: int
1492: cvs_sendln(struct cvsroot *root, const char *line)
1493: {
1494: int nl;
1495: size_t len;
1496:
1497: nl = 0;
1498: len = strlen(line);
1499:
1500: if ((len > 0) && (line[len - 1] != '\n'))
1501: nl = 1;
1502:
1503: if (cvs_server_inlog != NULL) {
1504: fputs(line, cvs_server_inlog);
1505: if (nl)
1506: fputc('\n', cvs_server_inlog);
1507: }
1508: fputs(line, root->cr_srvin);
1509: if (nl)
1510: fputc('\n', root->cr_srvin);
1511:
1512: return (0);
1513: }
1514:
1515:
1516: /*
1.13 jfb 1517: * cvs_sendraw()
1.12 jfb 1518: *
1519: * Send the first <len> bytes from the buffer <src> to the server.
1520: */
1521:
1522: int
1.13 jfb 1523: cvs_sendraw(struct cvsroot *root, const void *src, size_t len)
1.12 jfb 1524: {
1525: if (cvs_server_inlog != NULL)
1526: fwrite(src, sizeof(char), len, cvs_server_inlog);
1.13 jfb 1527: if (fwrite(src, sizeof(char), len, root->cr_srvin) < len) {
1.12 jfb 1528: return (-1);
1529: }
1530:
1531: return (0);
1532: }
1533:
1534:
1535: /*
1.13 jfb 1536: * cvs_recvraw()
1.12 jfb 1537: *
1538: * Receive the first <len> bytes from the buffer <src> to the server.
1539: */
1540:
1541: ssize_t
1.13 jfb 1542: cvs_recvraw(struct cvsroot *root, void *dst, size_t len)
1.12 jfb 1543: {
1544: size_t ret;
1545:
1.13 jfb 1546: ret = fread(dst, sizeof(char), len, root->cr_srvout);
1.12 jfb 1547: if (ret == 0)
1548: return (-1);
1549: if (cvs_server_outlog != NULL)
1550: fwrite(dst, sizeof(char), len, cvs_server_outlog);
1551: return (ssize_t)ret;
1552: }
1553:
1554:
1555: /*
1.13 jfb 1556: * cvs_senddir()
1.12 jfb 1557: *
1558: * Send a `Directory' request along with the 2 paths that follow it.
1559: */
1560:
1561: int
1.13 jfb 1562: cvs_senddir(struct cvsroot *root, CVSFILE *dir)
1.12 jfb 1563: {
1.13 jfb 1564: char buf[MAXPATHLEN];
1.12 jfb 1565:
1.13 jfb 1566: if (dir->cf_ddat->cd_repo == NULL)
1567: strlcpy(buf, root->cr_dir, sizeof(buf));
1568: else
1569: snprintf(buf, sizeof(buf), "%s/%s", root->cr_dir,
1570: dir->cf_ddat->cd_repo);
1.12 jfb 1571:
1.13 jfb 1572: if ((cvs_sendreq(root, CVS_REQ_DIRECTORY, dir->cf_path) < 0) ||
1573: (cvs_sendln(root, buf) < 0))
1.12 jfb 1574: return (-1);
1575:
1576: return (0);
1577: }
1578:
1579:
1580: /*
1.13 jfb 1581: * cvs_sendarg()
1.12 jfb 1582: *
1583: * Send the argument <arg> to the server. The argument <append> is used to
1584: * determine if the argument should be simply appended to the last argument
1585: * sent or if it should be created as a new argument (0).
1586: */
1587:
1588: int
1.13 jfb 1589: cvs_sendarg(struct cvsroot *root, const char *arg, int append)
1.12 jfb 1590: {
1.13 jfb 1591: return cvs_sendreq(root, ((append == 0) ?
1592: CVS_REQ_ARGUMENT : CVS_REQ_ARGUMENTX), arg);
1.12 jfb 1593: }
1594:
1595:
1596: /*
1.13 jfb 1597: * cvs_sendentry()
1.12 jfb 1598: *
1599: * Send an `Entry' request to the server along with the mandatory fields from
1600: * the CVS entry <ent> (which are the name and revision).
1601: */
1602:
1603: int
1.13 jfb 1604: cvs_sendentry(struct cvsroot *root, const struct cvs_ent *ent)
1.12 jfb 1605: {
1606: char ebuf[128], numbuf[64];
1607:
1608: snprintf(ebuf, sizeof(ebuf), "/%s/%s///", ent->ce_name,
1609: rcsnum_tostr(ent->ce_rev, numbuf, sizeof(numbuf)));
1610:
1.13 jfb 1611: return cvs_sendreq(root, CVS_REQ_ENTRY, ebuf);
1.12 jfb 1612: }
1613:
1614:
1615: /*
1.13 jfb 1616: * cvs_initlog()
1.12 jfb 1617: *
1618: * Initialize protocol logging if the CVS_CLIENT_LOG environment variable is
1619: * set. In this case, the variable's value is used as a path to which the
1620: * appropriate suffix is added (".in" for server input and ".out" for server
1621: * output.
1622: * Returns 0 on success, or -1 on failure.
1623: */
1624:
1625: static int
1.13 jfb 1626: cvs_initlog(void)
1.12 jfb 1627: {
1628: char *env, fpath[MAXPATHLEN];
1629:
1.13 jfb 1630: /* avoid doing it more than once */
1631: if (cvs_server_logon)
1632: return (0);
1633:
1.12 jfb 1634: env = getenv("CVS_CLIENT_LOG");
1635: if (env == NULL)
1636: return (0);
1637:
1638: strlcpy(fpath, env, sizeof(fpath));
1639: strlcat(fpath, ".in", sizeof(fpath));
1640: cvs_server_inlog = fopen(fpath, "w");
1641: if (cvs_server_inlog == NULL) {
1642: cvs_log(LP_ERRNO, "failed to open server input log `%s'",
1643: fpath);
1644: return (-1);
1645: }
1646:
1647: strlcpy(fpath, env, sizeof(fpath));
1648: strlcat(fpath, ".out", sizeof(fpath));
1649: cvs_server_outlog = fopen(fpath, "w");
1650: if (cvs_server_outlog == NULL) {
1651: cvs_log(LP_ERRNO, "failed to open server output log `%s'",
1652: fpath);
1653: return (-1);
1654: }
1655:
1656: /* make the streams line-buffered */
1657: setvbuf(cvs_server_inlog, NULL, _IOLBF, 0);
1658: setvbuf(cvs_server_outlog, NULL, _IOLBF, 0);
1659:
1.13 jfb 1660: cvs_server_logon = 1;
1.12 jfb 1661:
1662: return (0);
1663: }