Annotation of src/usr.bin/cvs/resp.c, Revision 1.68
1.68 ! xsa 1: /* $OpenBSD: resp.c,v 1.67 2005/12/30 16:47:36 joris 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.24 jfb 323: int fd, len;
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.36 jfb 336:
1.24 jfb 337: len = snprintf(statpath, sizeof(statpath), "%s/%s", line,
1.1 jfb 338: CVS_PATH_STATICENTRIES);
1.24 jfb 339: if (len == -1 || len >= (int)sizeof(statpath)) {
340: cvs_log(LP_ERR,
341: "path overflow for Entries.static specification");
342: return (-1);
343: }
1.1 jfb 344:
1.48 xsa 345: if (cvs_noexec == 0) {
1.35 xsa 346: if ((type == CVS_RESP_CLRSTATDIR) &&
1.51 xsa 347: (cvs_unlink(statpath) == -1)) {
1.1 jfb 348: return (-1);
1.35 xsa 349: } else if (type == CVS_RESP_SETSTATDIR) {
1.40 joris 350: fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0644);
1.35 xsa 351: if (fd == -1) {
352: cvs_log(LP_ERRNO,
353: "failed to set static directory on %s",
354: line);
355: return (-1);
356: }
357: (void)close(fd);
358:
1.1 jfb 359: }
360: }
361:
362: return (0);
363: }
364:
365: /*
366: * cvs_resp_sticky()
367: *
1.4 jfb 368: * Handler for the `Clear-sticky' and `Set-sticky' responses. If the
1.47 joris 369: * specified directory doesn't exist, we create it.
1.1 jfb 370: */
371: static int
372: cvs_resp_sticky(struct cvsroot *root, int type, char *line)
373: {
1.47 joris 374: char buf[MAXPATHLEN];
1.4 jfb 375:
376: /* get the remote path */
1.67 joris 377: cvs_getln(root, buf, sizeof(buf));
1.1 jfb 378:
1.4 jfb 379: STRIP_SLASH(line);
1.36 jfb 380:
1.47 joris 381: if (cvs_resp_createdir(line) < 0)
382: return (-1);
383:
384: return (0);
385: }
386:
387: /*
388: * Shared code for cvs_resp[static, sticky]
389: *
390: * Looks if the directory requested exists, if it doesn't it will
391: * create it plus all administrative files as well.
392: */
393: static int
394: cvs_resp_createdir(char *line)
395: {
1.53 joris 396: int l;
1.47 joris 397: CVSFILE *base, *cf;
398: CVSENTRIES *entf;
399: struct stat st;
400: struct cvs_ent *ent;
1.56 xsa 401: char *file, subdir[MAXPATHLEN], buf[CVS_ENT_MAXLINELEN];
1.47 joris 402:
1.52 joris 403: entf = NULL;
404: cf = NULL;
1.54 joris 405:
406: /*
407: * we do not want to handle the '.' case,
408: * so return early.
409: */
410: if (!strcmp(line, "."))
411: return (0);
412:
1.4 jfb 413: cvs_splitpath(line, subdir, sizeof(subdir), &file);
1.47 joris 414: base = cvs_file_loadinfo(subdir, CF_NOFILES, NULL, NULL, 1);
415: if (base == NULL)
1.4 jfb 416: return (-1);
1.1 jfb 417:
1.47 joris 418: /*
419: * If <line> doesn't exist, we create it.
420: */
421: if (stat(line, &st) == -1) {
422: if (errno != ENOENT) {
1.62 xsa 423: cvs_log(LP_ERRNO, "failed to stat `%s'", line);
1.19 jfb 424: return (-1);
425: }
1.1 jfb 426:
1.47 joris 427: cf = cvs_file_create(base, line, DT_DIR, 0755);
1.52 joris 428: } else {
429: cf = cvs_file_loadinfo(line, CF_NOFILES, NULL, NULL, 1);
430: }
431:
432: if (cf == NULL) {
433: cvs_file_free(base);
434: return (-1);
435: }
436:
437: /*
438: * If the Entries file for the parent is already
439: * open, operate on that, instead of reopening it
440: * and invalidating the opened list.
441: */
442: if (!strcmp(subdir, cvs_resp_lastdir))
443: entf = cvs_resp_lastent;
444: else
445: entf = cvs_ent_open(subdir, O_WRONLY);
1.5 jfb 446:
1.52 joris 447: /*
448: * see if the entry is still present. If not, we add it again.
449: */
450: if (entf != NULL) {
451: if ((ent = cvs_ent_get(entf, cf->cf_name)) == NULL) {
452: l = snprintf(buf, sizeof(buf), "D/%s////", cf->cf_name);
453: if (l == -1 || l >= (int)sizeof(buf)) {
454: cvs_file_free(cf);
455: cvs_file_free(base);
456: return (-1);
1.40 joris 457: }
1.44 joris 458:
1.52 joris 459: ent = cvs_ent_parse(buf);
460: if (ent == NULL)
461: cvs_log(LP_ERR,
462: "failed to create directory entry");
463: else
464: cvs_ent_add(entf, ent);
1.5 jfb 465: }
1.47 joris 466:
1.52 joris 467: if (strcmp(subdir, cvs_resp_lastdir))
468: cvs_ent_close(entf);
1.1 jfb 469: }
470:
1.52 joris 471: cvs_file_free(cf);
1.47 joris 472: cvs_file_free(base);
1.1 jfb 473: return (0);
474: }
475:
476:
477: /*
478: * cvs_resp_newentry()
479: *
480: * Handler for the `New-entry' response and `Checked-in' responses.
1.9 jfb 481: * In the case of `New-entry', we expect the entry line
1.1 jfb 482: */
483: static int
484: cvs_resp_newentry(struct cvsroot *root, int type, char *line)
485: {
1.55 xsa 486: char entbuf[CVS_ENT_MAXLINELEN];
1.9 jfb 487: struct cvs_ent *ent;
1.1 jfb 488:
489: /* get the remote path */
1.67 joris 490: cvs_getln(root, entbuf, sizeof(entbuf));
1.1 jfb 491:
492: /* get the new Entries line */
1.67 joris 493: cvs_getln(root, entbuf, sizeof(entbuf));
1.1 jfb 494:
1.52 joris 495: if (resp_check_dir(root, line) < 0)
1.1 jfb 496: return (-1);
1.42 joris 497:
1.9 jfb 498: if (type == CVS_RESP_NEWENTRY) {
1.42 joris 499: cvs_ent_addln(cvs_resp_lastent, entbuf);
1.11 deraadt 500: } else if (type == CVS_RESP_CHECKEDIN) {
1.9 jfb 501: ent = cvs_ent_parse(entbuf);
502: if (ent == NULL) {
503: cvs_log(LP_ERR, "failed to parse entry");
504: return (-1);
505: }
506:
507: /* timestamp it to now */
508: ent->ce_mtime = time(&(ent->ce_mtime));
509:
510: /* replace the current entry with the one we just received */
1.63 joris 511: (void)cvs_ent_remove(cvs_resp_lastent, ent->ce_name, 0);
1.9 jfb 512:
1.42 joris 513: cvs_ent_add(cvs_resp_lastent, ent);
1.9 jfb 514: }
1.1 jfb 515:
516: return (0);
517: }
518:
519:
520: /*
521: * cvs_resp_cksum()
522: *
523: * Handler for the `Checksum' response. We store the checksum received for
524: * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>.
525: * Upon next file reception, the handler checks to see if there is a stored
526: * checksum.
527: * The file handler must make sure that the checksums match and free the
528: * checksum buffer once it's done to indicate there is no further checksum.
529: */
530: static int
531: cvs_resp_cksum(struct cvsroot *root, int type, char *line)
532: {
533: if (cvs_fcksum != NULL) {
534: cvs_log(LP_WARN, "unused checksum");
1.64 joris 535: xfree(cvs_fcksum);
1.1 jfb 536: }
537:
1.64 joris 538: cvs_fcksum = xstrdup(line);
1.14 jfb 539:
540: return (0);
541: }
542:
543:
544: /*
545: * cvs_resp_copyfile()
546: *
547: * Handler for the `Copy-file' response, which is used to copy the contents
548: * of a file to another file for which the name is provided. The CVS protocol
549: * documentation states that this response is only used prior to a `Merged'
550: * response to create a backup of the file.
551: */
552: static int
553: cvs_resp_copyfile(struct cvsroot *root, int type, char *line)
554: {
1.24 jfb 555: int len;
1.67 joris 556: char path[MAXPATHLEN], newpath[MAXPATHLEN];
557: char newname[MAXNAMLEN], *file;
1.14 jfb 558:
1.16 jfb 559: /* read the remote path of the file to copy and its new name */
1.67 joris 560: cvs_getln(root, path, sizeof(path));
561: cvs_getln(root, newname, sizeof(newname));
1.14 jfb 562:
1.67 joris 563: if ((file = basename(path)) == NULL)
564: fatal("no base file name in Copy-file path");
1.16 jfb 565:
1.24 jfb 566: len = snprintf(path, sizeof(path), "%s%s", line, file);
1.67 joris 567: if (len == -1 || len >= (int)sizeof(path))
568: fatal("source path overflow in Copy-file response");
569:
1.24 jfb 570: len = snprintf(newpath, sizeof(newpath), "%s%s", line, newname);
1.67 joris 571: if (len == -1 || len >= (int)sizeof(path))
572: fatal("destination path overflow in Copy-file response");
1.16 jfb 573:
574: if (rename(path, newpath) == -1) {
1.67 joris 575: fatal("failed to rename %s to %s: %s",
576: path, newpath, strerror(errno));
1.1 jfb 577: }
578:
579: return (0);
580: }
581:
582:
583: /*
584: * cvs_resp_modtime()
585: *
586: * Handler for the `Mod-time' file update modifying response. The timestamp
587: * given is used to set the last modification time on the next file that
588: * will be received.
589: */
590: static int
591: cvs_resp_modtime(struct cvsroot *root, int type, char *line)
592: {
1.34 jfb 593: cvs_modtime = cvs_date_parse(line);
1.1 jfb 594: return (0);
595: }
596:
597:
598: /*
599: * cvs_resp_updated()
600: *
1.4 jfb 601: * Handler for the `Updated', `Update-existing', `Created', `Merged' and
602: * `Patched' responses, which all have a very similar format.
1.1 jfb 603: */
604: static int
605: cvs_resp_updated(struct cvsroot *root, int type, char *line)
606: {
1.17 jfb 607: int ret;
1.1 jfb 608: mode_t fmode;
1.4 jfb 609: char path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN];
1.1 jfb 610: BUF *fbuf;
1.45 xsa 611: struct cvs_ent *ent;
1.2 jfb 612: struct timeval tv[2];
1.1 jfb 613:
1.4 jfb 614: STRIP_SLASH(line);
615:
1.1 jfb 616: /* read the remote path of the file */
1.67 joris 617: cvs_getln(root, path, sizeof(path));
1.1 jfb 618:
619: /* read the new entry */
1.67 joris 620: cvs_getln(root, path, sizeof(path));
1.4 jfb 621:
1.45 xsa 622: if ((ent = cvs_ent_parse(path)) == NULL)
1.4 jfb 623: return (-1);
1.67 joris 624:
1.45 xsa 625: ret = snprintf(path, sizeof(path), "%s/%s", line, ent->ce_name);
1.67 joris 626: if (ret == -1 || ret >= (int)sizeof(path))
627: fatal("Entries path overflow in response");
628:
1.25 joris 629: ret = 0;
1.1 jfb 630:
1.52 joris 631: /*
632: * Please be sure the directory does exist.
633: */
634: if (cvs_resp_createdir(line) < 0)
635: return (-1);
636:
637: if (resp_check_dir(root, line) < 0)
1.12 jfb 638: return (-1);
639:
1.15 jfb 640: if (cvs_modtime != CVS_DATE_DMSEC) {
1.45 xsa 641: ent->ce_mtime = cvs_modtime;
1.15 jfb 642: } else
1.45 xsa 643: ent->ce_mtime = time(&(ent->ce_mtime));
1.15 jfb 644:
645: if ((type == CVS_RESP_UPDEXIST) || (type == CVS_RESP_UPDATED) ||
1.18 jfb 646: (type == CVS_RESP_MERGED) || (type == CVS_RESP_CREATED)) {
1.63 joris 647: if ((cvs_ent_remove(cvs_resp_lastent, ent->ce_name, 0) < 0) &&
1.30 joris 648: (type != CVS_RESP_CREATED)) {
649: cvs_log(LP_WARN, "failed to remove entry for '%s`",
1.45 xsa 650: ent->ce_name);
1.30 joris 651: }
1.15 jfb 652: }
1.12 jfb 653:
1.67 joris 654: cvs_ent_add(cvs_resp_lastent, ent);
1.15 jfb 655:
1.67 joris 656: fbuf = cvs_recvfile(root, &fmode);
1.65 xsa 657: cvs_buf_write(fbuf, path, fmode);
1.17 jfb 658: cvs_buf_free(fbuf);
1.2 jfb 659:
1.12 jfb 660: if (cvs_modtime != CVS_DATE_DMSEC) {
661: tv[0].tv_sec = (long)cvs_modtime;
662: tv[0].tv_usec = 0;
663: tv[1].tv_sec = (long)cvs_modtime;
664: tv[1].tv_usec = 0;
665: if (utimes(path, tv) == -1)
666: cvs_log(LP_ERRNO, "failed to set file timestamps");
667: }
1.34 jfb 668:
669: /* invalidate last received timestamp */
670: cvs_modtime = CVS_DATE_DMSEC;
1.1 jfb 671:
672: /* now see if there is a checksum */
673: if (cvs_fcksum != NULL) {
1.17 jfb 674: if (cvs_cksum(path, cksum_buf, sizeof(cksum_buf)) < 0)
675: ret = -1;
676: else if (strcmp(cksum_buf, cvs_fcksum) != 0) {
1.1 jfb 677: cvs_log(LP_ERR, "checksum error on received file");
678: (void)unlink(line);
1.17 jfb 679: ret = -1;
1.1 jfb 680: }
681:
1.64 joris 682: xfree(cvs_fcksum);
1.1 jfb 683: cvs_fcksum = NULL;
684: }
685:
1.17 jfb 686: return (ret);
1.1 jfb 687: }
688:
689:
690: /*
691: * cvs_resp_removed()
692: *
1.10 jfb 693: * Handler for the `Removed' and `Remove-entry' responses. The `Removed'
694: * response is received when both a file and its entry need to be removed from
695: * the local copy. The `Remove-entry' is received in cases where the file is
696: * already gone but there is still an entry to remove in the Entries file.
1.1 jfb 697: */
698: static int
699: cvs_resp_removed(struct cvsroot *root, int type, char *line)
700: {
1.29 joris 701: int l;
1.67 joris 702: char buf[MAXPATHLEN], base[MAXPATHLEN];
703: char fpath[MAXPATHLEN], *file;
1.1 jfb 704:
1.67 joris 705: cvs_getln(root, buf, sizeof(buf));
1.29 joris 706:
707: cvs_splitpath(buf, base, sizeof(base), &file);
708: l = snprintf(fpath, sizeof(fpath), "%s/%s", line, file);
1.67 joris 709: if (l == -1 || l >= (int)sizeof(fpath))
710: fatal("cvs_resp_removed: overflow in path");
1.29 joris 711:
1.52 joris 712: if (resp_check_dir(root, line) < 0)
1.42 joris 713: return (-1);
1.10 jfb 714:
1.63 joris 715: (void)cvs_ent_remove(cvs_resp_lastent, file, 0);
1.32 joris 716: if ((type == CVS_RESP_REMOVED) && ((unlink(fpath) == -1) &&
717: errno != ENOENT)) {
1.29 joris 718: cvs_log(LP_ERRNO, "failed to unlink `%s'", file);
1.1 jfb 719: return (-1);
1.10 jfb 720: }
1.1 jfb 721:
722: return (0);
723: }
724:
725:
726: /*
727: * cvs_resp_mode()
728: *
729: * Handler for the `Mode' response.
730: */
731: static int
732: cvs_resp_mode(struct cvsroot *root, int type, char *line)
733: {
1.67 joris 734: cvs_strtomode(line, &cvs_lastmode);
1.1 jfb 735: return (0);
736: }
737:
738:
739: /*
740: * cvs_resp_modxpand()
741: *
742: * Handler for the `Module-expansion' response.
743: */
744: static int
745: cvs_resp_modxpand(struct cvsroot *root, int type, char *line)
746: {
747: return (0);
748: }
749:
750: /*
751: * cvs_resp_rcsdiff()
752: *
753: * Handler for the `Rcs-diff' response.
754: */
755: static int
756: cvs_resp_rcsdiff(struct cvsroot *root, int type, char *line)
757: {
1.24 jfb 758: int len;
1.56 xsa 759: char file[MAXPATHLEN];
760: char buf[CVS_ENT_MAXLINELEN], cksum_buf[CVS_CKSUM_LEN];
1.1 jfb 761: char *fname, *orig, *patch;
762: mode_t fmode;
763: BUF *res, *fcont, *patchbuf;
764: CVSENTRIES *entf;
765: struct cvs_ent *ent;
766:
767: /* get remote path and build local path of file to be patched */
1.67 joris 768: cvs_getln(root, buf, sizeof(buf));
1.43 joris 769:
1.1 jfb 770: fname = strrchr(buf, '/');
771: if (fname == NULL)
772: fname = buf;
1.24 jfb 773: len = snprintf(file, sizeof(file), "%s%s", line, fname);
1.67 joris 774: if (len == -1 || len >= (int)sizeof(file))
775: fatal("path overflow in Rcs-diff response");
1.1 jfb 776:
777: /* get updated entry fields */
1.67 joris 778: cvs_getln(root, buf, sizeof(buf));
1.43 joris 779:
1.1 jfb 780: ent = cvs_ent_parse(buf);
1.13 tedu 781: if (ent == NULL)
1.1 jfb 782: return (-1);
783:
784: patchbuf = cvs_recvfile(root, &fmode);
785: fcont = cvs_buf_load(file, BUF_AUTOEXT);
786: if (fcont == NULL)
787: return (-1);
788:
789: cvs_buf_putc(patchbuf, '\0');
790: cvs_buf_putc(fcont, '\0');
791: orig = cvs_buf_release(fcont);
792: patch = cvs_buf_release(patchbuf);
793:
1.61 joris 794: res = cvs_patchfile(orig, patch, rcs_patch_lines);
1.1 jfb 795: if (res == NULL)
796: return (-1);
797:
798: cvs_buf_write(res, file, fmode);
799:
800: /* now see if there is a checksum */
801: if (cvs_fcksum != NULL) {
802: if (cvs_cksum(file, cksum_buf, sizeof(cksum_buf)) < 0) {
803: }
804:
805: if (strcmp(cksum_buf, cvs_fcksum) != 0) {
806: cvs_log(LP_ERR, "checksum error on received file");
807: (void)unlink(file);
808: }
809:
1.64 joris 810: xfree(cvs_fcksum);
1.1 jfb 811: cvs_fcksum = NULL;
812: }
813:
814: /* update revision in entries */
815: entf = cvs_ent_open(line, O_WRONLY);
816: if (entf == NULL)
817: return (-1);
818:
819: cvs_ent_close(entf);
820:
821: return (0);
822: }
823:
824:
825: /*
826: * cvs_resp_template()
827: *
828: * Handler for the `Template' response.
829: */
830: static int
831: cvs_resp_template(struct cvsroot *root, int type, char *line)
832: {
833: mode_t mode;
834: BUF *tmpl;
835:
836: tmpl = cvs_recvfile(root, &mode);
1.67 joris 837: cvs_buf_free(tmpl);
1.42 joris 838:
839: return (0);
840: }
841:
842: /*
843: * Check if <dir> is the same as the last
844: * received directory, if it's not, switch Entry files.
845: */
846: static int
1.52 joris 847: resp_check_dir(struct cvsroot *root, const char *dir)
1.42 joris 848: {
1.52 joris 849: int l;
1.42 joris 850: size_t len;
1.52 joris 851: char cvspath[MAXPATHLEN], repo[MAXPATHLEN];
852: struct stat st;
853:
854: /*
855: * Make sure the CVS directory exists.
856: */
857: l = snprintf(cvspath, sizeof(cvspath), "%s/%s", dir, CVS_PATH_CVSDIR);
858: if (l == -1 || l >= (int)sizeof(cvspath))
1.67 joris 859: fatal("resp_check_dir: path overflow");
1.52 joris 860:
861: if (stat(cvspath, &st) == -1) {
862: if (errno != ENOENT)
863: return (-1);
864: if (cvs_repo_base != NULL) {
865: l = snprintf(repo, sizeof(repo), "%s/%s", cvs_repo_base,
866: dir);
867: if (l == -1 || l >= (int)sizeof(repo))
1.67 joris 868: fatal("resp_check_dir: path overflow");
1.52 joris 869: } else {
1.67 joris 870: len = strlcpy(repo, dir, sizeof(repo));
871: if (len >= sizeof(repo))
872: fatal("resp_check_dir: path truncation");
1.52 joris 873: }
874:
1.58 xsa 875: if (cvs_mkadmin(dir, root->cr_str, repo, NULL, NULL, 0) < 0)
1.52 joris 876: return (-1);
877: }
1.42 joris 878:
879: if (strcmp(dir, cvs_resp_lastdir)) {
880: if (cvs_resp_lastent != NULL)
881: cvs_ent_close(cvs_resp_lastent);
882: cvs_resp_lastent = cvs_ent_open(dir, O_WRONLY);
883: if (cvs_resp_lastent == NULL)
884: return (-1);
885:
886: len = strlcpy(cvs_resp_lastdir, dir, sizeof(cvs_resp_lastdir));
1.67 joris 887: if (len >= sizeof(cvs_resp_lastdir))
888: fatal("resp_check_dir: path truncation");
1.42 joris 889: } else {
890: /* make sure the old one is still open */
891: if (cvs_resp_lastent == NULL) {
892: cvs_resp_lastent = cvs_ent_open(cvs_resp_lastdir,
893: O_WRONLY);
894: if (cvs_resp_lastent == NULL)
895: return (-1);
896: }
897: }
1.1 jfb 898:
899: return (0);
900: }