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