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