Annotation of src/usr.bin/cvs/proto.c, Revision 1.5
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"
61:
62:
1.5 ! jfb 63: #define CVS_MTSTK_MAXDEPTH 16
! 64:
! 65:
1.1 jfb 66:
67: extern int verbosity;
68: extern int cvs_compress;
69: extern char *cvs_rsh;
70: extern int cvs_trace;
71: extern int cvs_nolog;
72: extern int cvs_readonly;
73:
74: extern struct cvsroot *cvs_root;
75:
76:
77:
78: static int cvs_resp_validreq (int, char *);
79: static int cvs_resp_cksum (int, char *);
1.4 jfb 80: static int cvs_resp_modtime (int, char *);
1.1 jfb 81: static int cvs_resp_m (int, char *);
82: static int cvs_resp_ok (int, char *);
83: static int cvs_resp_error (int, char *);
84: static int cvs_resp_statdir (int, char *);
1.5 ! jfb 85: static int cvs_resp_sticky (int, char *);
1.1 jfb 86: static int cvs_resp_newentry (int, char *);
87: static int cvs_resp_updated (int, char *);
88: static int cvs_resp_removed (int, char *);
89: static int cvs_resp_mode (int, char *);
1.5 ! jfb 90: static int cvs_resp_modxpand (int, char *);
1.1 jfb 91:
92:
93:
94:
95: struct cvs_req {
96: int req_id;
97: char req_str[32];
98: u_int req_flags;
99: int (*req_hdlr)(int, char *);
100: } cvs_requests[] = {
101: { CVS_REQ_DIRECTORY, "Directory", 0, NULL },
102: { CVS_REQ_MAXDOTDOT, "Max-dotdot", 0, NULL },
103: { CVS_REQ_STATICDIR, "Static-directory", 0, NULL },
104: { CVS_REQ_STICKY, "Sticky", 0, NULL },
105: { CVS_REQ_ENTRY, "Entry", 0, NULL },
106: { CVS_REQ_ENTRYEXTRA, "EntryExtra", 0, NULL },
107: { CVS_REQ_CHECKINTIME, "Checkin-time", 0, NULL },
108: { CVS_REQ_MODIFIED, "Modified", 0, NULL },
109: { CVS_REQ_ISMODIFIED, "Is-modified", 0, NULL },
110: { CVS_REQ_UNCHANGED, "Unchanged", 0, NULL },
111: { CVS_REQ_USEUNCHANGED, "UseUnchanged", 0, NULL },
112: { CVS_REQ_NOTIFY, "Notify", 0, NULL },
113: { CVS_REQ_NOTIFYUSER, "NotifyUser", 0, NULL },
114: { CVS_REQ_QUESTIONABLE, "Questionable", 0, NULL },
115: { CVS_REQ_CASE, "Case", 0, NULL },
116: { CVS_REQ_UTF8, "Utf8", 0, NULL },
117: { CVS_REQ_ARGUMENT, "Argument", 0, NULL },
118: { CVS_REQ_ARGUMENTX, "Argumentx", 0, NULL },
119: { CVS_REQ_GLOBALOPT, "Global_option", 0, NULL },
120: { CVS_REQ_GZIPSTREAM, "Gzip-stream", 0, NULL },
121: { CVS_REQ_READCVSRC2, "read-cvsrc2", 0, NULL },
122: { CVS_REQ_READWRAP, "read-cvswrappers", 0, NULL },
123: { CVS_REQ_READIGNORE, "read-cvsignore", 0, NULL },
124: { CVS_REQ_ERRIFREADER, "Error-If-Reader", 0, NULL },
125: { CVS_REQ_VALIDRCSOPT, "Valid-RcsOptions", 0, NULL },
126: { CVS_REQ_SET, "Set", 0, NULL },
127: { CVS_REQ_XPANDMOD, "expand-modules", 0, NULL },
128: { CVS_REQ_LOG, "log", 0, NULL },
129: { CVS_REQ_CO, "co", 0, NULL },
130: { CVS_REQ_EXPORT, "export", 0, NULL },
131: { CVS_REQ_RANNOTATE, "rannotate", 0, NULL },
132: { CVS_REQ_RDIFF, "rdiff", 0, NULL },
133: { CVS_REQ_RLOG, "rlog", 0, NULL },
134: { CVS_REQ_RTAG, "rtag", 0, NULL },
135: { CVS_REQ_INIT, "init", 0, NULL },
136: { CVS_REQ_UPDATE, "update", 0, NULL },
137: { CVS_REQ_HISTORY, "history", 0, NULL },
138: { CVS_REQ_IMPORT, "import", 0, NULL },
139: { CVS_REQ_ADD, "add", 0, NULL },
140: { CVS_REQ_REMOVE, "remove", 0, NULL },
141: { CVS_REQ_RELEASE, "release", 0, NULL },
142: { CVS_REQ_ROOT, "Root", 0, NULL },
143: { CVS_REQ_VALIDRESP, "Valid-responses", 0, NULL },
144: { CVS_REQ_VALIDREQ, "valid-requests", 0, NULL },
145: { CVS_REQ_VERSION, "version", 0, NULL },
146: { CVS_REQ_NOOP, "noop", 0, NULL },
147: { CVS_REQ_DIFF, "diff", 0, NULL },
148: };
149:
150:
151: struct cvs_resp {
152: u_int resp_id;
153: char resp_str[32];
154: int (*resp_hdlr)(int, char *);
155: } cvs_responses[] = {
156: { CVS_RESP_OK, "ok", cvs_resp_ok },
157: { CVS_RESP_ERROR, "error", cvs_resp_error },
158: { CVS_RESP_VALIDREQ, "Valid-requests", cvs_resp_validreq },
159: { CVS_RESP_M, "M", cvs_resp_m },
160: { CVS_RESP_MBINARY, "Mbinary", cvs_resp_m },
161: { CVS_RESP_MT, "MT", cvs_resp_m },
162: { CVS_RESP_E, "E", cvs_resp_m },
163: { CVS_RESP_F, "F", cvs_resp_m },
164: { CVS_RESP_CREATED, "Created", cvs_resp_updated },
165: { CVS_RESP_UPDATED, "Updated", cvs_resp_updated },
166: { CVS_RESP_UPDEXIST, "Update-existing", cvs_resp_updated },
167: { CVS_RESP_REMOVED, "Removed", cvs_resp_removed },
168: { CVS_RESP_MERGED, "Merged", NULL },
169: { CVS_RESP_CKSUM, "Checksum", cvs_resp_cksum },
170: { CVS_RESP_CLRSTATDIR, "Clear-static-directory", cvs_resp_statdir },
171: { CVS_RESP_SETSTATDIR, "Set-static-directory", cvs_resp_statdir },
172: { CVS_RESP_NEWENTRY, "New-entry", cvs_resp_newentry },
173: { CVS_RESP_CHECKEDIN, "Checked-in", cvs_resp_newentry },
174: { CVS_RESP_MODE, "Mode", cvs_resp_mode },
1.4 jfb 175: { CVS_RESP_MODTIME, "Mod-time", cvs_resp_modtime },
1.5 ! jfb 176: { CVS_RESP_MODXPAND, "Module-expansion", cvs_resp_modxpand },
! 177: { CVS_RESP_SETSTICKY, "Set-sticky", cvs_resp_sticky },
! 178: { CVS_RESP_CLRSTICKY, "Clear-sticky", cvs_resp_sticky },
1.1 jfb 179: };
180:
181:
1.5 ! jfb 182: static char *cvs_mt_stack[CVS_MTSTK_MAXDEPTH];
! 183: static u_int cvs_mtstk_depth = 0;
! 184:
! 185:
1.1 jfb 186: #define CVS_NBREQ (sizeof(cvs_requests)/sizeof(cvs_requests[0]))
187: #define CVS_NBRESP (sizeof(cvs_responses)/sizeof(cvs_responses[0]))
188:
189: /* mask of requets supported by server */
190: static u_char cvs_server_validreq[CVS_REQ_MAX + 1];
191:
1.5 ! jfb 192: /*
! 193: * Local and remote directory used by the `Directory' request.
! 194: */
! 195: char cvs_ldir[MAXPATHLEN];
! 196: char cvs_rdir[MAXPATHLEN];
! 197:
! 198: char *cvs_fcksum = NULL;
! 199:
! 200: mode_t cvs_lastmode = 0;
! 201:
1.1 jfb 202:
203: /*
204: * cvs_req_getbyid()
205: *
206: */
207:
208: const char*
209: cvs_req_getbyid(int reqid)
210: {
211: u_int i;
212:
213: for (i = 0; i < CVS_NBREQ; i++)
214: if (cvs_requests[i].req_id == reqid)
215: return (cvs_requests[i].req_str);
216: return (NULL);
217: }
218:
219:
220: /*
221: * cvs_req_getbyname()
222: */
223:
224: int
225: cvs_req_getbyname(const char *rname)
226: {
227: u_int i;
228:
229: for (i = 0; i < CVS_NBREQ; i++)
230: if (strcmp(cvs_requests[i].req_str, rname) == 0)
231: return (cvs_requests[i].req_id);
232:
233: return (-1);
234: }
235:
236:
237: /*
238: * cvs_req_getvalid()
239: *
240: * Build a space-separated list of all the requests that this protocol
241: * implementation supports.
242: */
243:
244: char*
245: cvs_req_getvalid(void)
246: {
247: u_int i;
248: size_t len;
249: char *vrstr;
250: BUF *buf;
251:
252: buf = cvs_buf_alloc(512, BUF_AUTOEXT);
253: if (buf == NULL)
254: return (NULL);
255:
256: cvs_buf_set(buf, cvs_requests[0].req_str,
257: strlen(cvs_requests[0].req_str), 0);
258:
259: for (i = 1; i < CVS_NBREQ; i++) {
260: if ((cvs_buf_putc(buf, ' ') < 0) ||
261: (cvs_buf_append(buf, cvs_requests[i].req_str,
262: strlen(cvs_requests[i].req_str)) < 0)) {
263: cvs_buf_free(buf);
264: return (NULL);
265: }
266: }
267:
268: /* NUL-terminate */
269: if (cvs_buf_putc(buf, '\0') < 0) {
270: cvs_buf_free(buf);
271: return (NULL);
272: }
273:
274: len = cvs_buf_size(buf);
275: vrstr = (char *)malloc(len);
276:
277: cvs_buf_copy(buf, 0, vrstr, len);
278:
279: cvs_buf_free(buf);
280:
281: return (vrstr);
282: }
283:
284:
285: /*
286: * cvs_req_handle()
287: *
288: * Generic request handler dispatcher.
289: */
290:
291: int
292: cvs_req_handle(char *line)
293: {
294: return (0);
295: }
296:
297:
298: /*
299: * cvs_resp_getbyid()
300: *
301: */
302:
303: const char*
304: cvs_resp_getbyid(int respid)
305: {
306: u_int i;
307:
308: for (i = 0; i < CVS_NBREQ; i++)
309: if (cvs_responses[i].resp_id == respid)
310: return (cvs_responses[i].resp_str);
311: return (NULL);
312: }
313:
314:
315: /*
316: * cvs_resp_getbyname()
317: */
318:
319: int
320: cvs_resp_getbyname(const char *rname)
321: {
322: u_int i;
323:
324: for (i = 0; i < CVS_NBREQ; i++)
325: if (strcmp(cvs_responses[i].resp_str, rname) == 0)
326: return (cvs_responses[i].resp_id);
327:
328: return (-1);
329: }
330:
331:
332: /*
333: * cvs_resp_getvalid()
334: *
335: * Build a space-separated list of all the responses that this protocol
336: * implementation supports.
337: */
338:
339: char*
340: cvs_resp_getvalid(void)
341: {
342: u_int i;
343: size_t len;
344: char *vrstr;
345: BUF *buf;
346:
347: buf = cvs_buf_alloc(512, BUF_AUTOEXT);
348: if (buf == NULL)
349: return (NULL);
350:
351: cvs_buf_set(buf, cvs_responses[0].resp_str,
352: strlen(cvs_responses[0].resp_str), 0);
353:
354: for (i = 1; i < CVS_NBRESP; i++) {
355: if ((cvs_buf_putc(buf, ' ') < 0) ||
356: (cvs_buf_append(buf, cvs_responses[i].resp_str,
357: strlen(cvs_responses[i].resp_str)) < 0)) {
358: cvs_buf_free(buf);
359: return (NULL);
360: }
361: }
362:
363: /* NUL-terminate */
364: if (cvs_buf_putc(buf, '\0') < 0) {
365: cvs_buf_free(buf);
366: return (NULL);
367: }
368:
369: len = cvs_buf_size(buf);
370: vrstr = (char *)malloc(len);
371:
372: cvs_buf_copy(buf, 0, vrstr, len);
373: cvs_buf_free(buf);
374:
375: return (vrstr);
376: }
377:
378:
379: /*
380: * cvs_resp_handle()
381: *
382: * Generic response handler dispatcher. The handler expects the first line
383: * of the command as single argument.
384: * Returns the return value of the command on success, or -1 on failure.
385: */
386:
387: int
388: cvs_resp_handle(char *line)
389: {
390: u_int i;
391: size_t len;
392: char *cp, *cmd;
393:
394: cmd = line;
395:
396: cp = strchr(cmd, ' ');
397: if (cp != NULL)
398: *(cp++) = '\0';
399:
400: for (i = 0; i < CVS_NBRESP; i++) {
401: if (strcmp(cvs_responses[i].resp_str, cmd) == 0)
402: return (*cvs_responses[i].resp_hdlr)
403: (cvs_responses[i].resp_id, cp);
404: }
405:
406: /* unhandled */
407: return (-1);
408: }
409:
410:
411: /*
412: * cvs_client_sendinfo()
413: *
414: * Initialize the connection status by first requesting the list of
415: * supported requests from the server. Then, we send the CVSROOT variable
416: * with the `Root' request.
417: * Returns 0 on success, or -1 on failure.
418: */
419:
420: static int
421: cvs_client_sendinfo(void)
422: {
423: /* first things first, get list of valid requests from server */
424: if (cvs_client_sendreq(CVS_REQ_VALIDREQ, NULL, 1) < 0) {
425: cvs_log(LP_ERR, "failed to get valid requests from server");
426: return (-1);
427: }
428:
429: /* now share our global options with the server */
430: if (verbosity == 1)
431: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-q", 0);
432: else if (verbosity == 0)
433: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-Q", 0);
434:
435: if (cvs_nolog)
436: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-l", 0);
437: if (cvs_readonly)
438: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-r", 0);
439: if (cvs_trace)
440: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-t", 0);
441:
442: /* now send the CVSROOT to the server */
443: if (cvs_client_sendreq(CVS_REQ_ROOT, cvs_root->cr_dir, 0) < 0)
444: return (-1);
445:
446: /* not sure why, but we have to send this */
447: if (cvs_client_sendreq(CVS_REQ_USEUNCHANGED, NULL, 0) < 0)
448: return (-1);
449:
450: return (0);
451: }
452:
453:
454: /*
455: * cvs_resp_validreq()
456: *
457: * Handler for the `Valid-requests' response. The list of valid requests is
458: * split on spaces and each request's entry in the valid request array is set
459: * to 1 to indicate the validity.
460: * Returns 0 on success, or -1 on failure.
461: */
462:
463: static int
464: cvs_resp_validreq(int type, char *line)
465: {
466: int i;
467: char *sp, *ep;
468:
469: /* parse the requests */
470: sp = line;
471: do {
472: ep = strchr(sp, ' ');
473: if (ep != NULL)
474: *ep = '\0';
475:
476: i = cvs_req_getbyname(sp);
477: if (i != -1)
478: cvs_server_validreq[i] = 1;
479:
480: if (ep != NULL)
481: sp = ep + 1;
482: } while (ep != NULL);
483:
484: return (0);
485: }
486:
487:
488: /*
489: * cvs_resp_m()
490: *
1.4 jfb 491: * Handler for the `M', 'MT', `F' and `E' responses.
1.1 jfb 492: */
493:
494: static int
495: cvs_resp_m(int type, char *line)
496: {
1.5 ! jfb 497: char *cp;
1.1 jfb 498: FILE *stream;
499:
500: stream = NULL;
501:
502: switch (type) {
503: case CVS_RESP_F:
504: fflush(stderr);
505: return (0);
506: case CVS_RESP_M:
507: stream = stdout;
508: break;
509: case CVS_RESP_E:
510: stream = stderr;
511: break;
512: case CVS_RESP_MT:
1.5 ! jfb 513: if (*line == '+') {
! 514: if (cvs_mtstk_depth == CVS_MTSTK_MAXDEPTH) {
! 515: cvs_log(LP_ERR,
! 516: "MT scope stack has reached max depth");
! 517: return (-1);
! 518: }
! 519: cvs_mt_stack[cvs_mtstk_depth++] = strdup(line + 1);
! 520: if (cvs_mt_stack[cvs_mtstk_depth] == NULL) {
! 521: cvs_mtstk_depth--;
! 522: return (-1);
! 523: }
! 524: }
! 525: else if (*line == '-') {
! 526: if (cvs_mtstk_depth == 0) {
! 527: cvs_log(LP_ERR, "MT scope stack underflow");
! 528: return (-1);
! 529: }
! 530: else if (strcmp(line,
! 531: cvs_mt_stack[cvs_mtstk_depth]) != 0) {
! 532: cvs_log(LP_ERR, "mismatch in MT scope stack");
! 533: return (-1);
! 534: }
! 535: free(cvs_mt_stack[cvs_mtstk_depth--]);
! 536: }
! 537: else {
! 538: if (strcmp(line, "newline") == 0)
! 539: putc('\n', stdout);
! 540: else if (strncmp(line, "fname ", 6) == 0)
! 541: printf("%s", line + 6);
! 542: else {
! 543: /* assume text */
! 544: cp = strchr(line, ' ');
! 545: if (cp != NULL)
! 546: printf("%s", cp + 1);
! 547: }
! 548: }
! 549:
1.4 jfb 550: break;
1.1 jfb 551: case CVS_RESP_MBINARY:
1.5 ! jfb 552: cvs_log(LP_WARN, "Mbinary not supported in client yet");
1.1 jfb 553: break;
554: }
555:
556: fputs(line, stream);
557: fputc('\n', stream);
558:
559: return (0);
560: }
561:
562:
563: /*
564: * cvs_resp_ok()
565: *
566: * Handler for the `ok' response. This handler's job is to
567: */
568:
569: static int
570: cvs_resp_ok(int type, char *line)
571: {
572: return (1);
573: }
574:
575:
576: /*
577: * cvs_resp_error()
578: *
579: * Handler for the `error' response. This handler's job is to
580: */
581:
582: static int
583: cvs_resp_error(int type, char *line)
584: {
585: return (1);
586: }
587:
588:
589: /*
590: * cvs_resp_statdir()
591: *
592: * Handler for the `Clear-static-directory' and `Set-static-directory'
593: * responses.
594: */
595:
596: static int
597: cvs_resp_statdir(int type, char *line)
598: {
599: int fd;
600: char statpath[MAXPATHLEN];
601:
602: snprintf(statpath, sizeof(statpath), "%s/%s", line,
603: CVS_PATH_STATICENTRIES);
604:
605: if ((type == CVS_RESP_CLRSTATDIR) &&
1.5 ! jfb 606: (unlink(statpath) == -1) && (errno != ENOENT)) {
1.1 jfb 607: cvs_log(LP_ERRNO, "failed to unlink %s file",
608: CVS_PATH_STATICENTRIES);
609: return (-1);
610: }
611: else if (type == CVS_RESP_SETSTATDIR) {
1.5 ! jfb 612: fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0400);
1.1 jfb 613: if (fd == -1) {
614: cvs_log(LP_ERRNO, "failed to create %s file",
615: CVS_PATH_STATICENTRIES);
616: return (-1);
617: }
618: (void)close(fd);
1.5 ! jfb 619:
! 620: }
! 621:
! 622: return (0);
! 623: }
! 624:
! 625: /*
! 626: * cvs_resp_sticky()
! 627: *
! 628: * Handler for the `Clear-sticky' and `Set-sticky' responses.
! 629: */
! 630:
! 631: static int
! 632: cvs_resp_sticky(int type, char *line)
! 633: {
! 634: if (type == CVS_RESP_CLRSTICKY) {
! 635: }
! 636: else if (type == CVS_RESP_SETSTICKY) {
1.1 jfb 637: }
638:
639: return (0);
640: }
641:
642:
643: /*
644: * cvs_resp_newentry()
645: *
646: * Handler for the `New-entry' response and `Checked-in' responses.
647: */
648:
649: static int
650: cvs_resp_newentry(int type, char *line)
651: {
652: char entbuf[128], path[MAXPATHLEN];
653: CVSENTRIES *entfile;
654:
1.2 jfb 655: snprintf(path, sizeof(path), "%s/" CVS_PATH_ENTRIES, line);
1.1 jfb 656:
657: /* get the remote path */
658: cvs_client_getln(entbuf, sizeof(entbuf));
659:
660: /* get the new Entries line */
661: if (cvs_client_getln(entbuf, sizeof(entbuf)) < 0)
662: return (-1);
663:
1.2 jfb 664: entfile = cvs_ent_open(path, O_WRONLY);
1.1 jfb 665: if (entfile == NULL)
666: return (-1);
1.3 jfb 667: cvs_ent_addln(entfile, entbuf);
1.1 jfb 668: cvs_ent_close(entfile);
669:
670: return (0);
671: }
672:
673:
674: /*
675: * cvs_resp_cksum()
676: *
677: * Handler for the `Checksum' response. We store the checksum received for
678: * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>.
679: * Upon next file reception, the handler checks to see if there is a stored
680: * checksum.
681: * The file handler must make sure that the checksums match and free the
682: * checksum buffer once it's done to indicate there is no further checksum.
683: */
684:
685: static int
686: cvs_resp_cksum(int type, char *line)
687: {
688: if (cvs_fcksum != NULL) {
689: cvs_log(LP_WARN, "unused checksum");
690: free(cvs_fcksum);
691: }
692:
693: cvs_fcksum = strdup(line);
694: if (cvs_fcksum == NULL) {
695: cvs_log(LP_ERRNO, "failed to copy checksum string");
696: return (-1);
697: }
698:
1.4 jfb 699: return (0);
700: }
701:
702:
703: /*
704: * cvs_resp_modtime()
705: *
706: * Handler for the `Mod-time' file update modifying response. The timestamp
707: * given is used to set the last modification time on the next file that
708: * will be received.
709: */
710:
711: static int
712: cvs_resp_modtime(int type, char *line)
713: {
1.1 jfb 714: return (0);
715: }
716:
717:
718: /*
719: * cvs_resp_updated()
720: *
721: * Handler for the `Updated' response.
722: */
723:
724: static int
725: cvs_resp_updated(int type, char *line)
726: {
727: char cksum_buf[CVS_CKSUM_LEN];
728:
729: if (type == CVS_RESP_CREATED) {
730: }
731: else if (type == CVS_RESP_UPDEXIST) {
732: }
733: else if (type == CVS_RESP_UPDATED) {
734: }
735:
736: if (cvs_recvfile(line) < 0) {
737: return (-1);
738: }
739:
740: /* now see if there is a checksum */
741: if (cvs_cksum(line, cksum_buf, sizeof(cksum_buf)) < 0) {
742: }
743:
744: if (strcmp(cksum_buf, cvs_fcksum) != 0) {
745: cvs_log(LP_ERR, "checksum error on received file");
746: (void)unlink(line);
747: }
748:
749: free(cvs_fcksum);
750: cvs_fcksum = NULL;
751:
752: return (0);
753: }
754:
755:
756: /*
757: * cvs_resp_removed()
758: *
759: * Handler for the `Updated' response.
760: */
761:
762: static int
763: cvs_resp_removed(int type, char *line)
764: {
765: return (0);
766: }
767:
768:
769: /*
770: * cvs_resp_mode()
771: *
772: * Handler for the `Mode' response.
773: */
774:
775: static int
776: cvs_resp_mode(int type, char *line)
777: {
778: if (cvs_strtomode(line, &cvs_lastmode) < 0) {
779: return (-1);
780: }
1.5 ! jfb 781: return (0);
! 782: }
! 783:
! 784:
! 785: /*
! 786: * cvs_resp_modxpand()
! 787: *
! 788: * Handler for the `Module-expansion' response.
! 789: */
! 790:
! 791: static int
! 792: cvs_resp_modxpand(int type, char *line)
! 793: {
1.1 jfb 794: return (0);
795: }
796:
797:
798: /*
799: * cvs_sendfile()
800: *
801: * Send the mode and size of a file followed by the file's contents.
802: * Returns 0 on success, or -1 on failure.
803: */
804:
805: int
806: cvs_sendfile(const char *path)
807: {
808: int fd;
809: ssize_t ret;
810: char buf[4096];
811: struct stat st;
812:
813: if (stat(path, &st) == -1) {
814: cvs_log(LP_ERRNO, "failed to stat `%s'", path);
815: return (-1);
816: }
817:
818: fd = open(path, O_RDONLY, 0);
819: if (fd == -1) {
820: return (-1);
821: }
822:
823: if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
824: return (-1);
825:
826: cvs_client_sendln(buf);
827: snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
828: cvs_client_sendln(buf);
829:
830: while ((ret = read(fd, buf, sizeof(buf))) != 0) {
831: if (ret == -1) {
832: cvs_log(LP_ERRNO, "failed to read file `%s'", path);
833: return (-1);
834: }
835:
836: cvs_client_sendraw(buf, (size_t)ret);
837:
838: }
839:
840: (void)close(fd);
841:
842: return (0);
843: }
844:
845:
846: /*
847: * cvs_recvfile()
848: *
849: * Receive the mode and size of a file followed the file's contents and
850: * create or update the file whose path is <path> with the received
851: * information.
852: */
853:
854: int
855: cvs_recvfile(const char *path)
856: {
857: int fd;
858: mode_t mode;
859: size_t len;
860: ssize_t ret;
861: off_t fsz, cnt;
862: char buf[4096], *ep;
863:
864: if ((cvs_client_getln(buf, sizeof(buf)) < 0) ||
865: (cvs_strtomode(buf, &mode) < 0)) {
866: return (-1);
867: }
868:
869: cvs_client_getln(buf, sizeof(buf));
870:
871: fsz = (off_t)strtol(buf, &ep, 10);
872: if (*ep != '\0') {
873: cvs_log(LP_ERR, "parse error in file size transmission");
874: return (-1);
875: }
876:
877: fd = open(path, O_RDONLY, mode);
878: if (fd == -1) {
879: cvs_log(LP_ERRNO, "failed to open `%s'", path);
880: return (-1);
881: }
882:
883: cnt = 0;
884: do {
885: len = MIN(sizeof(buf), (size_t)(fsz - cnt));
886: ret = cvs_client_recvraw(buf, len);
887: if (ret == -1) {
888: (void)close(fd);
889: (void)unlink(path);
890: return (-1);
891: }
892:
893: if (write(fd, buf, (size_t)ret) == -1) {
894: cvs_log(LP_ERRNO,
895: "failed to write contents to file `%s'", path);
896: (void)close(fd);
897: (void)unlink(path);
898: return (-1);
899: }
900:
901: cnt += (off_t)ret;
902: } while (cnt < fsz);
903:
904: (void)close(fd);
905:
906: return (0);
907: }