[BACK]Return to proto.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / cvs

Annotation of src/usr.bin/cvs/proto.c, Revision 1.4

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