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