Annotation of src/usr.bin/cvs/resp.c, Revision 1.69
1.69 ! xsa 1: /* $OpenBSD: resp.c,v 1.68 2006/01/02 08:11:56 xsa Exp $ */
1.1 jfb 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:
1.68 xsa 27: #include "includes.h"
1.1 jfb 28:
29: #include "buf.h"
30: #include "cvs.h"
31: #include "log.h"
32: #include "proto.h"
33:
34:
1.50 xsa 35: #define CVS_MTSTK_MAXDEPTH 16
1.4 jfb 36:
37:
1.1 jfb 38: static int cvs_resp_validreq (struct cvsroot *, int, char *);
39: static int cvs_resp_cksum (struct cvsroot *, int, char *);
40: static int cvs_resp_modtime (struct cvsroot *, int, char *);
41: static int cvs_resp_m (struct cvsroot *, int, char *);
42: static int cvs_resp_ok (struct cvsroot *, int, char *);
43: static int cvs_resp_error (struct cvsroot *, int, char *);
44: static int cvs_resp_statdir (struct cvsroot *, int, char *);
45: static int cvs_resp_sticky (struct cvsroot *, int, char *);
46: static int cvs_resp_newentry (struct cvsroot *, int, char *);
47: static int cvs_resp_updated (struct cvsroot *, int, char *);
48: static int cvs_resp_removed (struct cvsroot *, int, char *);
49: static int cvs_resp_mode (struct cvsroot *, int, char *);
50: static int cvs_resp_modxpand (struct cvsroot *, int, char *);
51: static int cvs_resp_rcsdiff (struct cvsroot *, int, char *);
52: static int cvs_resp_template (struct cvsroot *, int, char *);
1.14 jfb 53: static int cvs_resp_copyfile (struct cvsroot *, int, char *);
1.47 joris 54: static int cvs_resp_createdir (char *);
1.1 jfb 55:
56: struct cvs_resphdlr {
1.60 reyk 57: int (*hdlr)(struct cvsroot *, int, char *);
1.1 jfb 58: } cvs_resp_swtab[CVS_RESP_MAX + 1] = {
59: { NULL },
60: { cvs_resp_ok },
61: { cvs_resp_error },
62: { cvs_resp_validreq },
63: { cvs_resp_newentry },
64: { cvs_resp_newentry },
65: { cvs_resp_cksum },
1.14 jfb 66: { cvs_resp_copyfile },
1.1 jfb 67: { cvs_resp_updated },
68: { cvs_resp_updated },
69: { cvs_resp_updated }, /* 10 */
70: { cvs_resp_updated },
71: { cvs_resp_updated },
72: { cvs_resp_rcsdiff },
73: { cvs_resp_mode },
74: { cvs_resp_modtime },
75: { cvs_resp_removed },
76: { cvs_resp_removed },
77: { cvs_resp_statdir },
78: { cvs_resp_statdir },
79: { cvs_resp_sticky }, /* 20 */
80: { cvs_resp_sticky },
81: { cvs_resp_template },
82: { NULL },
83: { NULL },
84: { NULL },
85: { cvs_resp_modxpand },
86: { NULL },
87: { cvs_resp_m },
88: { cvs_resp_m },
89: { cvs_resp_m }, /* 30 */
90: { cvs_resp_m },
91: { cvs_resp_m },
92: };
93:
1.42 joris 94: /*
95: * Instead of opening and closing the Entry file all the time,
96: * which caused a huge CPU load and slowed down everything,
97: * we keep the Entry file for the directory we are working in
98: * open until we encounter a new directory.
99: */
100: static char cvs_resp_lastdir[MAXPATHLEN] = "";
101: static CVSENTRIES *cvs_resp_lastent = NULL;
1.52 joris 102: static int resp_check_dir(struct cvsroot *, const char *);
1.1 jfb 103:
104: /*
105: * The MT command uses scoping to tag the data. Whenever we encouter a '+',
106: * we push the name of the tag on the stack, and we pop it when we encounter
107: * a '-' with the same name.
108: */
109:
110: static char *cvs_mt_stack[CVS_MTSTK_MAXDEPTH];
111: static u_int cvs_mtstk_depth = 0;
112:
1.12 jfb 113: static time_t cvs_modtime = CVS_DATE_DMSEC;
1.1 jfb 114:
115:
116: /* last checksum received */
117: char *cvs_fcksum = NULL;
118:
119: mode_t cvs_lastmode = 0;
120:
121: /* hack to receive the remote version without outputting it */
122: extern u_int cvs_version_sent;
123:
124:
125: /*
126: * cvs_resp_handle()
127: *
128: * Generic response handler dispatcher. The handler expects the first line
129: * of the command as single argument.
130: * Returns the return value of the command on success, or -1 on failure.
131: */
132: int
133: cvs_resp_handle(struct cvsroot *root, char *line)
134: {
1.15 jfb 135: int ret;
1.1 jfb 136: char *cp, *cmd;
137: struct cvs_resp *resp;
138:
139: cmd = line;
140:
141: cp = strchr(cmd, ' ');
142: if (cp != NULL)
143: *(cp++) = '\0';
144:
145: resp = cvs_resp_getbyname(cmd);
146: if (resp == NULL) {
147: return (-1);
1.11 deraadt 148: } else if (cvs_resp_swtab[resp->resp_id].hdlr == NULL) {
1.20 jfb 149: cvs_log(LP_ERR, "handler for `%s' not implemented", cmd);
1.1 jfb 150: return (-1);
151: }
152:
1.15 jfb 153: ret = (*cvs_resp_swtab[resp->resp_id].hdlr)(root, resp->resp_id, cp);
154:
155: if (ret == -1)
156: cvs_log(LP_ERR, "error in handling of `%s' response", cmd);
157:
158: return (ret);
1.1 jfb 159: }
160:
161:
162: /*
163: * cvs_resp_validreq()
164: *
165: * Handler for the `Valid-requests' response. The list of valid requests is
166: * split on spaces and each request's entry in the valid request array is set
167: * to 1 to indicate the validity.
168: * Returns 0 on success, or -1 on failure.
169: */
170: static int
171: cvs_resp_validreq(struct cvsroot *root, int type, char *line)
172: {
173: char *sp, *ep;
174: struct cvs_req *req;
175:
176: /* parse the requests */
177: sp = line;
178: do {
179: ep = strchr(sp, ' ');
180: if (ep != NULL)
181: *ep = '\0';
182:
183: req = cvs_req_getbyname(sp);
184: if (req != NULL)
185: CVS_SETVR(root, req->req_id);
186:
187: if (ep != NULL)
188: sp = ep + 1;
189: } while (ep != NULL);
190:
191: return (0);
192: }
193:
194:
195: /*
196: * cvs_resp_m()
197: *
198: * Handler for the `M', 'MT', `F' and `E' responses.
199: */
200: static int
201: cvs_resp_m(struct cvsroot *root, int type, char *line)
202: {
203: char *cp;
204: FILE *stream;
205:
206: stream = NULL;
207:
208: switch (type) {
209: case CVS_RESP_F:
210: fflush(stderr);
211: return (0);
212: case CVS_RESP_M:
213: if (cvs_version_sent) {
214: /*
215: * Instead of outputting the line, we save it as the
216: * remote server's version string.
217: */
218: cvs_version_sent = 0;
1.64 joris 219: root->cr_version = xstrdup(line);
1.1 jfb 220: return (0);
221: }
222: stream = stdout;
223: break;
224: case CVS_RESP_E:
225: stream = stderr;
226: break;
227: case CVS_RESP_MT:
228: if (*line == '+') {
229: if (cvs_mtstk_depth == CVS_MTSTK_MAXDEPTH) {
230: cvs_log(LP_ERR,
231: "MT scope stack has reached max depth");
232: return (-1);
233: }
1.64 joris 234: cvs_mt_stack[cvs_mtstk_depth] = xstrdup(line + 1);
1.1 jfb 235: cvs_mtstk_depth++;
1.11 deraadt 236: } else if (*line == '-') {
1.1 jfb 237: if (cvs_mtstk_depth == 0) {
238: cvs_log(LP_ERR, "MT scope stack underflow");
239: return (-1);
1.11 deraadt 240: } else if (strcmp(line + 1,
1.1 jfb 241: cvs_mt_stack[cvs_mtstk_depth - 1]) != 0) {
242: cvs_log(LP_ERR, "mismatch in MT scope stack");
243: return (-1);
244: }
1.66 joris 245: xfree(cvs_mt_stack[--cvs_mtstk_depth]);
1.11 deraadt 246: } else {
1.1 jfb 247: if (strcmp(line, "newline") == 0)
248: putc('\n', stdout);
1.57 xsa 249: else if (strncmp(line, "fname ", (size_t)6) == 0)
1.1 jfb 250: printf("%s", line + 6);
251: else {
252: /* assume text */
253: cp = strchr(line, ' ');
254: if (cp != NULL)
255: printf("%s", cp + 1);
256: }
257: }
258:
259: return (0);
260: case CVS_RESP_MBINARY:
261: cvs_log(LP_WARN, "Mbinary not supported in client yet");
262: break;
263: }
264:
265: fputs(line, stream);
266: fputc('\n', stream);
267:
268: return (0);
269: }
270:
271:
272: /*
273: * cvs_resp_ok()
274: *
1.13 tedu 275: * Handler for the `ok' response. This handler's job is to
1.1 jfb 276: */
277: static int
278: cvs_resp_ok(struct cvsroot *root, int type, char *line)
279: {
1.42 joris 280: /*
281: * If we still have an Entry file open, close it now.
282: */
283: if (cvs_resp_lastent != NULL)
284: cvs_ent_close(cvs_resp_lastent);
1.13 tedu 285:
1.1 jfb 286: return (1);
287: }
288:
289:
290: /*
291: * cvs_resp_error()
292: *
1.13 tedu 293: * Handler for the `error' response. This handler's job is to
1.31 joris 294: * show the error message given by the server.
1.1 jfb 295: */
296: static int
297: cvs_resp_error(struct cvsroot *root, int type, char *line)
298: {
1.33 joris 299: if (line == NULL)
300: return (1);
1.31 joris 301:
302: /* XXX - GNU cvs sends an empty error message
303: * at the end of the diff command, even for successfull
304: * diff.
305: */
306: if ((strlen(line) == 1) && (*line == ' '))
307: return (1);
1.13 tedu 308:
1.7 jfb 309: fprintf(stderr, "%s\n", line);
1.1 jfb 310: return (1);
311: }
312:
313:
314: /*
315: * cvs_resp_statdir()
316: *
317: * Handler for the `Clear-static-directory' and `Set-static-directory'
318: * responses.
319: */
320: static int
321: cvs_resp_statdir(struct cvsroot *root, int type, char *line)
322: {
1.69 ! xsa 323: int fd;
1.1 jfb 324: char rpath[MAXPATHLEN], statpath[MAXPATHLEN];
325:
1.3 jfb 326: /* remote directory line */
1.67 joris 327: cvs_getln(root, rpath, sizeof(rpath));
1.1 jfb 328:
1.36 jfb 329: STRIP_SLASH(line);
330:
1.47 joris 331: /*
332: * Create the directory if it does not exist.
333: */
334: if (cvs_resp_createdir(line) < 0)
335: return (-1);
1.69 ! xsa 336: if (strlcpy(statpath, line, sizeof(statpath)) >= sizeof(statpath) ||
! 337: strlcat(statpath, "/", sizeof(statpath)) >= sizeof(statpath) ||
! 338: strlcat(statpath, CVS_PATH_STATICENTRIES,
! 339: sizeof(statpath)) >= sizeof(statpath))
! 340: cvs_log(LP_ERR, "Entries.static path truncation");
1.24 jfb 341: return (-1);
1.1 jfb 342:
1.48 xsa 343: if (cvs_noexec == 0) {
1.35 xsa 344: if ((type == CVS_RESP_CLRSTATDIR) &&
1.51 xsa 345: (cvs_unlink(statpath) == -1)) {
1.1 jfb 346: return (-1);
1.35 xsa 347: } else if (type == CVS_RESP_SETSTATDIR) {
1.40 joris 348: fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0644);
1.35 xsa 349: if (fd == -1) {
350: cvs_log(LP_ERRNO,
351: "failed to set static directory on %s",
352: line);
353: return (-1);
354: }
355: (void)close(fd);
356:
1.1 jfb 357: }
358: }
359:
360: return (0);
361: }
362:
363: /*
364: * cvs_resp_sticky()
365: *
1.4 jfb 366: * Handler for the `Clear-sticky' and `Set-sticky' responses. If the
1.47 joris 367: * specified directory doesn't exist, we create it.
1.1 jfb 368: */
369: static int
370: cvs_resp_sticky(struct cvsroot *root, int type, char *line)
371: {
1.47 joris 372: char buf[MAXPATHLEN];
1.4 jfb 373:
374: /* get the remote path */
1.67 joris 375: cvs_getln(root, buf, sizeof(buf));
1.1 jfb 376:
1.4 jfb 377: STRIP_SLASH(line);
1.36 jfb 378:
1.47 joris 379: if (cvs_resp_createdir(line) < 0)
380: return (-1);
381:
382: return (0);
383: }
384:
385: /*
386: * Shared code for cvs_resp[static, sticky]
387: *
388: * Looks if the directory requested exists, if it doesn't it will
389: * create it plus all administrative files as well.
390: */
391: static int
392: cvs_resp_createdir(char *line)
393: {
1.53 joris 394: int l;
1.47 joris 395: CVSFILE *base, *cf;
396: CVSENTRIES *entf;
397: struct stat st;
398: struct cvs_ent *ent;
1.56 xsa 399: char *file, subdir[MAXPATHLEN], buf[CVS_ENT_MAXLINELEN];
1.47 joris 400:
1.52 joris 401: entf = NULL;
402: cf = NULL;
1.54 joris 403:
404: /*
405: * we do not want to handle the '.' case,
406: * so return early.
407: */
408: if (!strcmp(line, "."))
409: return (0);
410:
1.4 jfb 411: cvs_splitpath(line, subdir, sizeof(subdir), &file);
1.47 joris 412: base = cvs_file_loadinfo(subdir, CF_NOFILES, NULL, NULL, 1);
413: if (base == NULL)
1.4 jfb 414: return (-1);
1.1 jfb 415:
1.47 joris 416: /*
417: * If <line> doesn't exist, we create it.
418: */
419: if (stat(line, &st) == -1) {
420: if (errno != ENOENT) {
1.62 xsa 421: cvs_log(LP_ERRNO, "failed to stat `%s'", line);
1.19 jfb 422: return (-1);
423: }
1.1 jfb 424:
1.47 joris 425: cf = cvs_file_create(base, line, DT_DIR, 0755);
1.52 joris 426: } else {
427: cf = cvs_file_loadinfo(line, CF_NOFILES, NULL, NULL, 1);
428: }
429:
430: if (cf == NULL) {
431: cvs_file_free(base);
432: return (-1);
433: }
434:
435: /*
436: * If the Entries file for the parent is already
437: * open, operate on that, instead of reopening it
438: * and invalidating the opened list.
439: */
440: if (!strcmp(subdir, cvs_resp_lastdir))
441: entf = cvs_resp_lastent;
442: else
443: entf = cvs_ent_open(subdir, O_WRONLY);
1.5 jfb 444:
1.52 joris 445: /*
446: * see if the entry is still present. If not, we add it again.
447: */
448: if (entf != NULL) {
449: if ((ent = cvs_ent_get(entf, cf->cf_name)) == NULL) {
450: l = snprintf(buf, sizeof(buf), "D/%s////", cf->cf_name);
451: if (l == -1 || l >= (int)sizeof(buf)) {
452: cvs_file_free(cf);
453: cvs_file_free(base);
454: return (-1);
1.40 joris 455: }
1.44 joris 456:
1.52 joris 457: ent = cvs_ent_parse(buf);
458: if (ent == NULL)
459: cvs_log(LP_ERR,
460: "failed to create directory entry");
461: else
462: cvs_ent_add(entf, ent);
1.5 jfb 463: }
1.47 joris 464:
1.52 joris 465: if (strcmp(subdir, cvs_resp_lastdir))
466: cvs_ent_close(entf);
1.1 jfb 467: }
468:
1.52 joris 469: cvs_file_free(cf);
1.47 joris 470: cvs_file_free(base);
1.1 jfb 471: return (0);
472: }
473:
474:
475: /*
476: * cvs_resp_newentry()
477: *
478: * Handler for the `New-entry' response and `Checked-in' responses.
1.9 jfb 479: * In the case of `New-entry', we expect the entry line
1.1 jfb 480: */
481: static int
482: cvs_resp_newentry(struct cvsroot *root, int type, char *line)
483: {
1.55 xsa 484: char entbuf[CVS_ENT_MAXLINELEN];
1.9 jfb 485: struct cvs_ent *ent;
1.1 jfb 486:
487: /* get the remote path */
1.67 joris 488: cvs_getln(root, entbuf, sizeof(entbuf));
1.1 jfb 489:
490: /* get the new Entries line */
1.67 joris 491: cvs_getln(root, entbuf, sizeof(entbuf));
1.1 jfb 492:
1.52 joris 493: if (resp_check_dir(root, line) < 0)
1.1 jfb 494: return (-1);
1.42 joris 495:
1.9 jfb 496: if (type == CVS_RESP_NEWENTRY) {
1.42 joris 497: cvs_ent_addln(cvs_resp_lastent, entbuf);
1.11 deraadt 498: } else if (type == CVS_RESP_CHECKEDIN) {
1.9 jfb 499: ent = cvs_ent_parse(entbuf);
500: if (ent == NULL) {
501: cvs_log(LP_ERR, "failed to parse entry");
502: return (-1);
503: }
504:
505: /* timestamp it to now */
506: ent->ce_mtime = time(&(ent->ce_mtime));
507:
508: /* replace the current entry with the one we just received */
1.63 joris 509: (void)cvs_ent_remove(cvs_resp_lastent, ent->ce_name, 0);
1.9 jfb 510:
1.42 joris 511: cvs_ent_add(cvs_resp_lastent, ent);
1.9 jfb 512: }
1.1 jfb 513:
514: return (0);
515: }
516:
517:
518: /*
519: * cvs_resp_cksum()
520: *
521: * Handler for the `Checksum' response. We store the checksum received for
522: * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>.
523: * Upon next file reception, the handler checks to see if there is a stored
524: * checksum.
525: * The file handler must make sure that the checksums match and free the
526: * checksum buffer once it's done to indicate there is no further checksum.
527: */
528: static int
529: cvs_resp_cksum(struct cvsroot *root, int type, char *line)
530: {
531: if (cvs_fcksum != NULL) {
532: cvs_log(LP_WARN, "unused checksum");
1.64 joris 533: xfree(cvs_fcksum);
1.1 jfb 534: }
535:
1.64 joris 536: cvs_fcksum = xstrdup(line);
1.14 jfb 537:
538: return (0);
539: }
540:
541:
542: /*
543: * cvs_resp_copyfile()
544: *
545: * Handler for the `Copy-file' response, which is used to copy the contents
546: * of a file to another file for which the name is provided. The CVS protocol
547: * documentation states that this response is only used prior to a `Merged'
548: * response to create a backup of the file.
549: */
550: static int
551: cvs_resp_copyfile(struct cvsroot *root, int type, char *line)
552: {
1.67 joris 553: char path[MAXPATHLEN], newpath[MAXPATHLEN];
554: char newname[MAXNAMLEN], *file;
1.14 jfb 555:
1.16 jfb 556: /* read the remote path of the file to copy and its new name */
1.67 joris 557: cvs_getln(root, path, sizeof(path));
558: cvs_getln(root, newname, sizeof(newname));
1.14 jfb 559:
1.67 joris 560: if ((file = basename(path)) == NULL)
561: fatal("no base file name in Copy-file path");
1.16 jfb 562:
1.69 ! xsa 563: if (strlcpy(path, line, sizeof(path)) >= sizeof(path) ||
! 564: strlcat(path, file, sizeof(path)) >= sizeof(path))
1.67 joris 565: fatal("source path overflow in Copy-file response");
566:
1.69 ! xsa 567: if (strlcpy(newpath, line, sizeof(newpath)) >= sizeof(newpath) ||
! 568: strlcat(newpath, newname, sizeof(newpath)) >= sizeof(newpath))
1.67 joris 569: fatal("destination path overflow in Copy-file response");
1.16 jfb 570:
571: if (rename(path, newpath) == -1) {
1.69 ! xsa 572: fatal("cvs_resp_copyfile: rename: `%s'->`%s': %s",
1.67 joris 573: path, newpath, strerror(errno));
1.1 jfb 574: }
575:
576: return (0);
577: }
578:
579:
580: /*
581: * cvs_resp_modtime()
582: *
583: * Handler for the `Mod-time' file update modifying response. The timestamp
584: * given is used to set the last modification time on the next file that
585: * will be received.
586: */
587: static int
588: cvs_resp_modtime(struct cvsroot *root, int type, char *line)
589: {
1.34 jfb 590: cvs_modtime = cvs_date_parse(line);
1.1 jfb 591: return (0);
592: }
593:
594:
595: /*
596: * cvs_resp_updated()
597: *
1.4 jfb 598: * Handler for the `Updated', `Update-existing', `Created', `Merged' and
599: * `Patched' responses, which all have a very similar format.
1.1 jfb 600: */
601: static int
602: cvs_resp_updated(struct cvsroot *root, int type, char *line)
603: {
1.17 jfb 604: int ret;
1.1 jfb 605: mode_t fmode;
1.4 jfb 606: char path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN];
1.1 jfb 607: BUF *fbuf;
1.45 xsa 608: struct cvs_ent *ent;
1.2 jfb 609: struct timeval tv[2];
1.1 jfb 610:
1.69 ! xsa 611: ret = 0;
! 612:
1.4 jfb 613: STRIP_SLASH(line);
614:
1.1 jfb 615: /* read the remote path of the file */
1.67 joris 616: cvs_getln(root, path, sizeof(path));
1.1 jfb 617:
618: /* read the new entry */
1.67 joris 619: cvs_getln(root, path, sizeof(path));
1.4 jfb 620:
1.45 xsa 621: if ((ent = cvs_ent_parse(path)) == NULL)
1.4 jfb 622: return (-1);
1.67 joris 623:
1.69 ! xsa 624: if (strlcpy(path, line, sizeof(path)) >= sizeof(path) ||
! 625: strlcat(path, "/", sizeof(path)) >= sizeof(path) ||
! 626: strlcat(path, ent->ce_name, sizeof(path)) >= sizeof(path))
1.67 joris 627: fatal("Entries path overflow in response");
628:
1.52 joris 629: /*
630: * Please be sure the directory does exist.
631: */
632: if (cvs_resp_createdir(line) < 0)
633: return (-1);
634:
635: if (resp_check_dir(root, line) < 0)
1.12 jfb 636: return (-1);
637:
1.15 jfb 638: if (cvs_modtime != CVS_DATE_DMSEC) {
1.45 xsa 639: ent->ce_mtime = cvs_modtime;
1.15 jfb 640: } else
1.45 xsa 641: ent->ce_mtime = time(&(ent->ce_mtime));
1.15 jfb 642:
643: if ((type == CVS_RESP_UPDEXIST) || (type == CVS_RESP_UPDATED) ||
1.18 jfb 644: (type == CVS_RESP_MERGED) || (type == CVS_RESP_CREATED)) {
1.63 joris 645: if ((cvs_ent_remove(cvs_resp_lastent, ent->ce_name, 0) < 0) &&
1.30 joris 646: (type != CVS_RESP_CREATED)) {
647: cvs_log(LP_WARN, "failed to remove entry for '%s`",
1.45 xsa 648: ent->ce_name);
1.30 joris 649: }
1.15 jfb 650: }
1.12 jfb 651:
1.67 joris 652: cvs_ent_add(cvs_resp_lastent, ent);
1.15 jfb 653:
1.67 joris 654: fbuf = cvs_recvfile(root, &fmode);
1.65 xsa 655: cvs_buf_write(fbuf, path, fmode);
1.17 jfb 656: cvs_buf_free(fbuf);
1.2 jfb 657:
1.12 jfb 658: if (cvs_modtime != CVS_DATE_DMSEC) {
659: tv[0].tv_sec = (long)cvs_modtime;
660: tv[0].tv_usec = 0;
661: tv[1].tv_sec = (long)cvs_modtime;
662: tv[1].tv_usec = 0;
663: if (utimes(path, tv) == -1)
664: cvs_log(LP_ERRNO, "failed to set file timestamps");
665: }
1.34 jfb 666:
667: /* invalidate last received timestamp */
668: cvs_modtime = CVS_DATE_DMSEC;
1.1 jfb 669:
670: /* now see if there is a checksum */
671: if (cvs_fcksum != NULL) {
1.17 jfb 672: if (cvs_cksum(path, cksum_buf, sizeof(cksum_buf)) < 0)
673: ret = -1;
674: else if (strcmp(cksum_buf, cvs_fcksum) != 0) {
1.1 jfb 675: cvs_log(LP_ERR, "checksum error on received file");
676: (void)unlink(line);
1.17 jfb 677: ret = -1;
1.1 jfb 678: }
679:
1.64 joris 680: xfree(cvs_fcksum);
1.1 jfb 681: cvs_fcksum = NULL;
682: }
683:
1.17 jfb 684: return (ret);
1.1 jfb 685: }
686:
687:
688: /*
689: * cvs_resp_removed()
690: *
1.10 jfb 691: * Handler for the `Removed' and `Remove-entry' responses. The `Removed'
692: * response is received when both a file and its entry need to be removed from
693: * the local copy. The `Remove-entry' is received in cases where the file is
694: * already gone but there is still an entry to remove in the Entries file.
1.1 jfb 695: */
696: static int
697: cvs_resp_removed(struct cvsroot *root, int type, char *line)
698: {
1.67 joris 699: char buf[MAXPATHLEN], base[MAXPATHLEN];
700: char fpath[MAXPATHLEN], *file;
1.1 jfb 701:
1.67 joris 702: cvs_getln(root, buf, sizeof(buf));
1.29 joris 703:
704: cvs_splitpath(buf, base, sizeof(base), &file);
1.69 ! xsa 705:
! 706: if (strlcpy(fpath, line, sizeof(fpath)) >= sizeof(fpath) ||
! 707: strlcat(fpath, "/", sizeof(fpath)) >= sizeof(fpath) ||
! 708: strlcat(fpath, file, sizeof(fpath)) >= sizeof(fpath))
1.67 joris 709: fatal("cvs_resp_removed: overflow in path");
1.29 joris 710:
1.52 joris 711: if (resp_check_dir(root, line) < 0)
1.42 joris 712: return (-1);
1.10 jfb 713:
1.63 joris 714: (void)cvs_ent_remove(cvs_resp_lastent, file, 0);
1.32 joris 715: if ((type == CVS_RESP_REMOVED) && ((unlink(fpath) == -1) &&
716: errno != ENOENT)) {
1.29 joris 717: cvs_log(LP_ERRNO, "failed to unlink `%s'", file);
1.1 jfb 718: return (-1);
1.10 jfb 719: }
1.1 jfb 720:
721: return (0);
722: }
723:
724:
725: /*
726: * cvs_resp_mode()
727: *
728: * Handler for the `Mode' response.
729: */
730: static int
731: cvs_resp_mode(struct cvsroot *root, int type, char *line)
732: {
1.67 joris 733: cvs_strtomode(line, &cvs_lastmode);
1.1 jfb 734: return (0);
735: }
736:
737:
738: /*
739: * cvs_resp_modxpand()
740: *
741: * Handler for the `Module-expansion' response.
742: */
743: static int
744: cvs_resp_modxpand(struct cvsroot *root, int type, char *line)
745: {
746: return (0);
747: }
748:
749: /*
750: * cvs_resp_rcsdiff()
751: *
752: * Handler for the `Rcs-diff' response.
753: */
754: static int
755: cvs_resp_rcsdiff(struct cvsroot *root, int type, char *line)
756: {
1.56 xsa 757: char file[MAXPATHLEN];
758: char buf[CVS_ENT_MAXLINELEN], cksum_buf[CVS_CKSUM_LEN];
1.1 jfb 759: char *fname, *orig, *patch;
760: mode_t fmode;
761: BUF *res, *fcont, *patchbuf;
762: CVSENTRIES *entf;
763: struct cvs_ent *ent;
764:
765: /* get remote path and build local path of file to be patched */
1.67 joris 766: cvs_getln(root, buf, sizeof(buf));
1.43 joris 767:
1.1 jfb 768: fname = strrchr(buf, '/');
769: if (fname == NULL)
770: fname = buf;
1.69 ! xsa 771:
! 772: if (strlcpy(file, line, sizeof(file)) >= sizeof(file) ||
! 773: strlcat(file, fname, sizeof(file)) >= sizeof(file))
! 774: fatal("cvs_resp_rcsdiff: path truncation");
1.1 jfb 775:
776: /* get updated entry fields */
1.67 joris 777: cvs_getln(root, buf, sizeof(buf));
1.43 joris 778:
1.1 jfb 779: ent = cvs_ent_parse(buf);
1.13 tedu 780: if (ent == NULL)
1.1 jfb 781: return (-1);
782:
783: patchbuf = cvs_recvfile(root, &fmode);
784: fcont = cvs_buf_load(file, BUF_AUTOEXT);
785: if (fcont == NULL)
786: return (-1);
787:
788: cvs_buf_putc(patchbuf, '\0');
789: cvs_buf_putc(fcont, '\0');
790: orig = cvs_buf_release(fcont);
791: patch = cvs_buf_release(patchbuf);
792:
1.61 joris 793: res = cvs_patchfile(orig, patch, rcs_patch_lines);
1.1 jfb 794: if (res == NULL)
795: return (-1);
796:
797: cvs_buf_write(res, file, fmode);
798:
799: /* now see if there is a checksum */
800: if (cvs_fcksum != NULL) {
801: if (cvs_cksum(file, cksum_buf, sizeof(cksum_buf)) < 0) {
802: }
803:
804: if (strcmp(cksum_buf, cvs_fcksum) != 0) {
805: cvs_log(LP_ERR, "checksum error on received file");
806: (void)unlink(file);
807: }
808:
1.64 joris 809: xfree(cvs_fcksum);
1.1 jfb 810: cvs_fcksum = NULL;
811: }
812:
813: /* update revision in entries */
814: entf = cvs_ent_open(line, O_WRONLY);
815: if (entf == NULL)
816: return (-1);
817:
818: cvs_ent_close(entf);
819:
820: return (0);
821: }
822:
823:
824: /*
825: * cvs_resp_template()
826: *
827: * Handler for the `Template' response.
828: */
829: static int
830: cvs_resp_template(struct cvsroot *root, int type, char *line)
831: {
832: mode_t mode;
833: BUF *tmpl;
834:
835: tmpl = cvs_recvfile(root, &mode);
1.67 joris 836: cvs_buf_free(tmpl);
1.42 joris 837:
838: return (0);
839: }
840:
841: /*
842: * Check if <dir> is the same as the last
843: * received directory, if it's not, switch Entry files.
844: */
845: static int
1.52 joris 846: resp_check_dir(struct cvsroot *root, const char *dir)
1.42 joris 847: {
1.52 joris 848: char cvspath[MAXPATHLEN], repo[MAXPATHLEN];
849: struct stat st;
850:
851: /*
852: * Make sure the CVS directory exists.
853: */
1.69 ! xsa 854: if (strlcpy(cvspath, dir, sizeof(cvspath)) >= sizeof(cvspath) ||
! 855: strlcat(cvspath, "/", sizeof(cvspath)) >= sizeof(cvspath) ||
! 856: strlcat(cvspath, CVS_PATH_CVSDIR,
! 857: sizeof(cvspath)) >= sizeof(cvspath))
1.67 joris 858: fatal("resp_check_dir: path overflow");
1.52 joris 859:
860: if (stat(cvspath, &st) == -1) {
861: if (errno != ENOENT)
862: return (-1);
863: if (cvs_repo_base != NULL) {
1.69 ! xsa 864: if (strlcpy(repo, cvs_repo_base,
! 865: sizeof(repo)) >= sizeof(repo) ||
! 866: strlcat(repo, "/", sizeof(repo)) >= sizeof(repo) ||
! 867: strlcat(repo, dir, sizeof(repo)) >= sizeof(repo))
1.67 joris 868: fatal("resp_check_dir: path overflow");
1.52 joris 869: } else {
1.69 ! xsa 870: if (strlcpy(repo, dir, sizeof(repo)) >= sizeof(repo))
1.67 joris 871: fatal("resp_check_dir: path truncation");
1.52 joris 872: }
873:
1.58 xsa 874: if (cvs_mkadmin(dir, root->cr_str, repo, NULL, NULL, 0) < 0)
1.52 joris 875: return (-1);
876: }
1.42 joris 877:
878: if (strcmp(dir, cvs_resp_lastdir)) {
879: if (cvs_resp_lastent != NULL)
880: cvs_ent_close(cvs_resp_lastent);
881: cvs_resp_lastent = cvs_ent_open(dir, O_WRONLY);
882: if (cvs_resp_lastent == NULL)
883: return (-1);
884:
1.69 ! xsa 885: if (strlcpy(cvs_resp_lastdir, dir,
! 886: sizeof(cvs_resp_lastdir)) >= sizeof(cvs_resp_lastdir))
1.67 joris 887: fatal("resp_check_dir: path truncation");
1.42 joris 888: } else {
889: /* make sure the old one is still open */
890: if (cvs_resp_lastent == NULL) {
891: cvs_resp_lastent = cvs_ent_open(cvs_resp_lastdir,
892: O_WRONLY);
893: if (cvs_resp_lastent == NULL)
894: return (-1);
895: }
896: }
1.1 jfb 897:
898: return (0);
899: }