[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.5

1.1       jfb         1: /*     $OpenBSD$       */
                      2: /*
                      3:  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
                      4:  * All rights reserved.
                      5:  *
                      6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following conditions
                      8:  * are met:
                      9:  *
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. The name of the author may not be used to endorse or promote products
                     13:  *    derived from this software without specific prior written permission.
                     14:  *
                     15:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
                     16:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
                     17:  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
                     18:  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
                     19:  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
                     20:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
                     21:  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     22:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
                     23:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
                     24:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     25:  */
                     26: /*
                     27:  * CVS client/server protocol
                     28:  * ==========================
                     29:  *
                     30:  * The following code implements the CVS client/server protocol, which is
                     31:  * documented at the following URL:
                     32:  *     http://www.loria.fr/~molli/cvs/doc/cvsclient_toc.html
                     33:  *
                     34:  * The protocol is split up into two parts; the first part is the client side
                     35:  * of things and is composed of all the response handlers, which are all named
                     36:  * with a prefix of "cvs_resp_".  The second part is the set of request
                     37:  * handlers used by the server.  These handlers process the request and
                     38:  * generate the appropriate response to send back.  The prefix for request
                     39:  * handlers is "cvs_req_".
                     40:  *
                     41:  */
                     42:
                     43: #include <sys/types.h>
                     44: #include <sys/stat.h>
                     45:
                     46: #include <fcntl.h>
                     47: #include <stdio.h>
                     48: #include <errno.h>
                     49: #include <stdlib.h>
                     50: #include <unistd.h>
                     51: #include <signal.h>
                     52: #include <string.h>
                     53: #include <sysexits.h>
                     54: #ifdef CVS_ZLIB
                     55: #include <zlib.h>
                     56: #endif
                     57:
                     58: #include "buf.h"
                     59: #include "cvs.h"
                     60: #include "log.h"
                     61:
                     62:
1.5     ! jfb        63: #define CVS_MTSTK_MAXDEPTH   16
        !            64:
        !            65:
1.1       jfb        66:
                     67: extern int   verbosity;
                     68: extern int   cvs_compress;
                     69: extern char *cvs_rsh;
                     70: extern int   cvs_trace;
                     71: extern int   cvs_nolog;
                     72: extern int   cvs_readonly;
                     73:
                     74: extern struct cvsroot *cvs_root;
                     75:
                     76:
                     77:
                     78: static int  cvs_resp_validreq  (int, char *);
                     79: static int  cvs_resp_cksum     (int, char *);
1.4       jfb        80: static int  cvs_resp_modtime   (int, char *);
1.1       jfb        81: static int  cvs_resp_m         (int, char *);
                     82: static int  cvs_resp_ok        (int, char *);
                     83: static int  cvs_resp_error     (int, char *);
                     84: static int  cvs_resp_statdir   (int, char *);
1.5     ! jfb        85: static int  cvs_resp_sticky    (int, char *);
1.1       jfb        86: static int  cvs_resp_newentry  (int, char *);
                     87: static int  cvs_resp_updated   (int, char *);
                     88: static int  cvs_resp_removed   (int, char *);
                     89: static int  cvs_resp_mode      (int, char *);
1.5     ! jfb        90: static int  cvs_resp_modxpand  (int, char *);
1.1       jfb        91:
                     92:
                     93:
                     94:
                     95: struct cvs_req {
                     96:        int      req_id;
                     97:        char     req_str[32];
                     98:        u_int    req_flags;
                     99:        int    (*req_hdlr)(int, char *);
                    100: } cvs_requests[] = {
                    101:        { CVS_REQ_DIRECTORY,     "Directory",         0,  NULL },
                    102:        { CVS_REQ_MAXDOTDOT,     "Max-dotdot",        0,  NULL },
                    103:        { CVS_REQ_STATICDIR,     "Static-directory",  0,  NULL },
                    104:        { CVS_REQ_STICKY,        "Sticky",            0,  NULL },
                    105:        { CVS_REQ_ENTRY,         "Entry",             0,  NULL },
                    106:        { CVS_REQ_ENTRYEXTRA,    "EntryExtra",        0,  NULL },
                    107:        { CVS_REQ_CHECKINTIME,   "Checkin-time",      0,  NULL },
                    108:        { CVS_REQ_MODIFIED,      "Modified",          0,  NULL },
                    109:        { CVS_REQ_ISMODIFIED,    "Is-modified",       0,  NULL },
                    110:        { CVS_REQ_UNCHANGED,     "Unchanged",         0,  NULL },
                    111:        { CVS_REQ_USEUNCHANGED,  "UseUnchanged",      0,  NULL },
                    112:        { CVS_REQ_NOTIFY,        "Notify",            0,  NULL },
                    113:        { CVS_REQ_NOTIFYUSER,    "NotifyUser",        0,  NULL },
                    114:        { CVS_REQ_QUESTIONABLE,  "Questionable",      0,  NULL },
                    115:        { CVS_REQ_CASE,          "Case",              0,  NULL },
                    116:        { CVS_REQ_UTF8,          "Utf8",              0,  NULL },
                    117:        { CVS_REQ_ARGUMENT,      "Argument",          0,  NULL },
                    118:        { CVS_REQ_ARGUMENTX,     "Argumentx",         0,  NULL },
                    119:        { CVS_REQ_GLOBALOPT,     "Global_option",     0,  NULL },
                    120:        { CVS_REQ_GZIPSTREAM,    "Gzip-stream",       0,  NULL },
                    121:        { CVS_REQ_READCVSRC2,    "read-cvsrc2",       0,  NULL },
                    122:        { CVS_REQ_READWRAP,      "read-cvswrappers",  0,  NULL },
                    123:        { CVS_REQ_READIGNORE,    "read-cvsignore",    0,  NULL },
                    124:        { CVS_REQ_ERRIFREADER,   "Error-If-Reader",   0,  NULL },
                    125:        { CVS_REQ_VALIDRCSOPT,   "Valid-RcsOptions",  0,  NULL },
                    126:        { CVS_REQ_SET,           "Set",               0,  NULL },
                    127:        { CVS_REQ_XPANDMOD,      "expand-modules",    0,  NULL },
                    128:        { CVS_REQ_LOG,           "log",               0,  NULL },
                    129:        { CVS_REQ_CO,            "co",                0,  NULL },
                    130:        { CVS_REQ_EXPORT,        "export",            0,  NULL },
                    131:        { CVS_REQ_RANNOTATE,     "rannotate",         0,  NULL },
                    132:        { CVS_REQ_RDIFF,         "rdiff",             0,  NULL },
                    133:        { CVS_REQ_RLOG,          "rlog",              0,  NULL },
                    134:        { CVS_REQ_RTAG,          "rtag",              0,  NULL },
                    135:        { CVS_REQ_INIT,          "init",              0,  NULL },
                    136:        { CVS_REQ_UPDATE,        "update",            0,  NULL },
                    137:        { CVS_REQ_HISTORY,       "history",           0,  NULL },
                    138:        { CVS_REQ_IMPORT,        "import",            0,  NULL },
                    139:        { CVS_REQ_ADD,           "add",               0,  NULL },
                    140:        { CVS_REQ_REMOVE,        "remove",            0,  NULL },
                    141:        { CVS_REQ_RELEASE,       "release",           0,  NULL },
                    142:        { CVS_REQ_ROOT,          "Root",              0,  NULL },
                    143:        { CVS_REQ_VALIDRESP,     "Valid-responses",   0,  NULL },
                    144:        { CVS_REQ_VALIDREQ,      "valid-requests",    0,  NULL },
                    145:        { CVS_REQ_VERSION,       "version",           0,  NULL },
                    146:        { CVS_REQ_NOOP,          "noop",              0,  NULL },
                    147:        { CVS_REQ_DIFF,          "diff",              0,  NULL },
                    148: };
                    149:
                    150:
                    151: struct cvs_resp {
                    152:        u_int  resp_id;
                    153:        char   resp_str[32];
                    154:        int  (*resp_hdlr)(int, char *);
                    155: } cvs_responses[] = {
                    156:        { CVS_RESP_OK,         "ok",                     cvs_resp_ok       },
                    157:        { CVS_RESP_ERROR,      "error",                  cvs_resp_error    },
                    158:        { CVS_RESP_VALIDREQ,   "Valid-requests",         cvs_resp_validreq },
                    159:        { CVS_RESP_M,          "M",                      cvs_resp_m        },
                    160:        { CVS_RESP_MBINARY,    "Mbinary",                cvs_resp_m        },
                    161:        { CVS_RESP_MT,         "MT",                     cvs_resp_m        },
                    162:        { CVS_RESP_E,          "E",                      cvs_resp_m        },
                    163:        { CVS_RESP_F,          "F",                      cvs_resp_m        },
                    164:        { CVS_RESP_CREATED,    "Created",                cvs_resp_updated  },
                    165:        { CVS_RESP_UPDATED,    "Updated",                cvs_resp_updated  },
                    166:        { CVS_RESP_UPDEXIST,   "Update-existing",        cvs_resp_updated  },
                    167:        { CVS_RESP_REMOVED,    "Removed",                cvs_resp_removed  },
                    168:        { CVS_RESP_MERGED,     "Merged",                 NULL              },
                    169:        { CVS_RESP_CKSUM,      "Checksum",               cvs_resp_cksum    },
                    170:        { CVS_RESP_CLRSTATDIR, "Clear-static-directory", cvs_resp_statdir  },
                    171:        { CVS_RESP_SETSTATDIR, "Set-static-directory",   cvs_resp_statdir  },
                    172:        { CVS_RESP_NEWENTRY,   "New-entry",              cvs_resp_newentry },
                    173:        { CVS_RESP_CHECKEDIN,  "Checked-in",             cvs_resp_newentry },
                    174:        { CVS_RESP_MODE,       "Mode",                   cvs_resp_mode     },
1.4       jfb       175:        { CVS_RESP_MODTIME,    "Mod-time",               cvs_resp_modtime  },
1.5     ! jfb       176:        { CVS_RESP_MODXPAND,   "Module-expansion",       cvs_resp_modxpand },
        !           177:        { CVS_RESP_SETSTICKY,  "Set-sticky",             cvs_resp_sticky   },
        !           178:        { CVS_RESP_CLRSTICKY,  "Clear-sticky",           cvs_resp_sticky   },
1.1       jfb       179: };
                    180:
                    181:
1.5     ! jfb       182: static char *cvs_mt_stack[CVS_MTSTK_MAXDEPTH];
        !           183: static u_int cvs_mtstk_depth = 0;
        !           184:
        !           185:
1.1       jfb       186: #define CVS_NBREQ   (sizeof(cvs_requests)/sizeof(cvs_requests[0]))
                    187: #define CVS_NBRESP  (sizeof(cvs_responses)/sizeof(cvs_responses[0]))
                    188:
                    189: /* mask of requets supported by server */
                    190: static u_char  cvs_server_validreq[CVS_REQ_MAX + 1];
                    191:
1.5     ! jfb       192: /*
        !           193:  * Local and remote directory used by the `Directory' request.
        !           194:  */
        !           195: char  cvs_ldir[MAXPATHLEN];
        !           196: char  cvs_rdir[MAXPATHLEN];
        !           197:
        !           198: char *cvs_fcksum = NULL;
        !           199:
        !           200: mode_t  cvs_lastmode = 0;
        !           201:
1.1       jfb       202:
                    203: /*
                    204:  * cvs_req_getbyid()
                    205:  *
                    206:  */
                    207:
                    208: const char*
                    209: cvs_req_getbyid(int reqid)
                    210: {
                    211:        u_int i;
                    212:
                    213:        for (i = 0; i < CVS_NBREQ; i++)
                    214:                if (cvs_requests[i].req_id == reqid)
                    215:                        return (cvs_requests[i].req_str);
                    216:        return (NULL);
                    217: }
                    218:
                    219:
                    220: /*
                    221:  * cvs_req_getbyname()
                    222:  */
                    223:
                    224: int
                    225: cvs_req_getbyname(const char *rname)
                    226: {
                    227:        u_int i;
                    228:
                    229:        for (i = 0; i < CVS_NBREQ; i++)
                    230:                if (strcmp(cvs_requests[i].req_str, rname) == 0)
                    231:                        return (cvs_requests[i].req_id);
                    232:
                    233:        return (-1);
                    234: }
                    235:
                    236:
                    237: /*
                    238:  * cvs_req_getvalid()
                    239:  *
                    240:  * Build a space-separated list of all the requests that this protocol
                    241:  * implementation supports.
                    242:  */
                    243:
                    244: char*
                    245: cvs_req_getvalid(void)
                    246: {
                    247:        u_int i;
                    248:        size_t len;
                    249:        char *vrstr;
                    250:        BUF *buf;
                    251:
                    252:        buf = cvs_buf_alloc(512, BUF_AUTOEXT);
                    253:        if (buf == NULL)
                    254:                return (NULL);
                    255:
                    256:        cvs_buf_set(buf, cvs_requests[0].req_str,
                    257:            strlen(cvs_requests[0].req_str), 0);
                    258:
                    259:        for (i = 1; i < CVS_NBREQ; i++) {
                    260:                if ((cvs_buf_putc(buf, ' ') < 0) ||
                    261:                    (cvs_buf_append(buf, cvs_requests[i].req_str,
                    262:                    strlen(cvs_requests[i].req_str)) < 0)) {
                    263:                        cvs_buf_free(buf);
                    264:                        return (NULL);
                    265:                }
                    266:        }
                    267:
                    268:        /* NUL-terminate */
                    269:        if (cvs_buf_putc(buf, '\0') < 0) {
                    270:                cvs_buf_free(buf);
                    271:                return (NULL);
                    272:        }
                    273:
                    274:        len = cvs_buf_size(buf);
                    275:        vrstr = (char *)malloc(len);
                    276:
                    277:        cvs_buf_copy(buf, 0, vrstr, len);
                    278:
                    279:        cvs_buf_free(buf);
                    280:
                    281:        return (vrstr);
                    282: }
                    283:
                    284:
                    285: /*
                    286:  * cvs_req_handle()
                    287:  *
                    288:  * Generic request handler dispatcher.
                    289:  */
                    290:
                    291: int
                    292: cvs_req_handle(char *line)
                    293: {
                    294:        return (0);
                    295: }
                    296:
                    297:
                    298: /*
                    299:  * cvs_resp_getbyid()
                    300:  *
                    301:  */
                    302:
                    303: const char*
                    304: cvs_resp_getbyid(int respid)
                    305: {
                    306:        u_int i;
                    307:
                    308:        for (i = 0; i < CVS_NBREQ; i++)
                    309:                if (cvs_responses[i].resp_id == respid)
                    310:                        return (cvs_responses[i].resp_str);
                    311:        return (NULL);
                    312: }
                    313:
                    314:
                    315: /*
                    316:  * cvs_resp_getbyname()
                    317:  */
                    318:
                    319: int
                    320: cvs_resp_getbyname(const char *rname)
                    321: {
                    322:        u_int i;
                    323:
                    324:        for (i = 0; i < CVS_NBREQ; i++)
                    325:                if (strcmp(cvs_responses[i].resp_str, rname) == 0)
                    326:                        return (cvs_responses[i].resp_id);
                    327:
                    328:        return (-1);
                    329: }
                    330:
                    331:
                    332: /*
                    333:  * cvs_resp_getvalid()
                    334:  *
                    335:  * Build a space-separated list of all the responses that this protocol
                    336:  * implementation supports.
                    337:  */
                    338:
                    339: char*
                    340: cvs_resp_getvalid(void)
                    341: {
                    342:        u_int i;
                    343:        size_t len;
                    344:        char *vrstr;
                    345:        BUF *buf;
                    346:
                    347:        buf = cvs_buf_alloc(512, BUF_AUTOEXT);
                    348:        if (buf == NULL)
                    349:                return (NULL);
                    350:
                    351:        cvs_buf_set(buf, cvs_responses[0].resp_str,
                    352:            strlen(cvs_responses[0].resp_str), 0);
                    353:
                    354:        for (i = 1; i < CVS_NBRESP; i++) {
                    355:                if ((cvs_buf_putc(buf, ' ') < 0) ||
                    356:                    (cvs_buf_append(buf, cvs_responses[i].resp_str,
                    357:                    strlen(cvs_responses[i].resp_str)) < 0)) {
                    358:                        cvs_buf_free(buf);
                    359:                        return (NULL);
                    360:                }
                    361:        }
                    362:
                    363:        /* NUL-terminate */
                    364:        if (cvs_buf_putc(buf, '\0') < 0) {
                    365:                cvs_buf_free(buf);
                    366:                return (NULL);
                    367:        }
                    368:
                    369:        len = cvs_buf_size(buf);
                    370:        vrstr = (char *)malloc(len);
                    371:
                    372:        cvs_buf_copy(buf, 0, vrstr, len);
                    373:        cvs_buf_free(buf);
                    374:
                    375:        return (vrstr);
                    376: }
                    377:
                    378:
                    379: /*
                    380:  * cvs_resp_handle()
                    381:  *
                    382:  * Generic response handler dispatcher.  The handler expects the first line
                    383:  * of the command as single argument.
                    384:  * Returns the return value of the command on success, or -1 on failure.
                    385:  */
                    386:
                    387: int
                    388: cvs_resp_handle(char *line)
                    389: {
                    390:        u_int i;
                    391:        size_t len;
                    392:        char *cp, *cmd;
                    393:
                    394:        cmd = line;
                    395:
                    396:        cp = strchr(cmd, ' ');
                    397:        if (cp != NULL)
                    398:                *(cp++) = '\0';
                    399:
                    400:        for (i = 0; i < CVS_NBRESP; i++) {
                    401:                if (strcmp(cvs_responses[i].resp_str, cmd) == 0)
                    402:                        return (*cvs_responses[i].resp_hdlr)
                    403:                            (cvs_responses[i].resp_id, cp);
                    404:        }
                    405:
                    406:        /* unhandled */
                    407:        return (-1);
                    408: }
                    409:
                    410:
                    411: /*
                    412:  * cvs_client_sendinfo()
                    413:  *
                    414:  * Initialize the connection status by first requesting the list of
                    415:  * supported requests from the server.  Then, we send the CVSROOT variable
                    416:  * with the `Root' request.
                    417:  * Returns 0 on success, or -1 on failure.
                    418:  */
                    419:
                    420: static int
                    421: cvs_client_sendinfo(void)
                    422: {
                    423:        /* first things first, get list of valid requests from server */
                    424:        if (cvs_client_sendreq(CVS_REQ_VALIDREQ, NULL, 1) < 0) {
                    425:                cvs_log(LP_ERR, "failed to get valid requests from server");
                    426:                return (-1);
                    427:        }
                    428:
                    429:        /* now share our global options with the server */
                    430:        if (verbosity == 1)
                    431:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-q", 0);
                    432:        else if (verbosity == 0)
                    433:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-Q", 0);
                    434:
                    435:        if (cvs_nolog)
                    436:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-l", 0);
                    437:        if (cvs_readonly)
                    438:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-r", 0);
                    439:        if (cvs_trace)
                    440:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-t", 0);
                    441:
                    442:        /* now send the CVSROOT to the server */
                    443:        if (cvs_client_sendreq(CVS_REQ_ROOT, cvs_root->cr_dir, 0) < 0)
                    444:                return (-1);
                    445:
                    446:        /* not sure why, but we have to send this */
                    447:        if (cvs_client_sendreq(CVS_REQ_USEUNCHANGED, NULL, 0) < 0)
                    448:                return (-1);
                    449:
                    450:        return (0);
                    451: }
                    452:
                    453:
                    454: /*
                    455:  * cvs_resp_validreq()
                    456:  *
                    457:  * Handler for the `Valid-requests' response.  The list of valid requests is
                    458:  * split on spaces and each request's entry in the valid request array is set
                    459:  * to 1 to indicate the validity.
                    460:  * Returns 0 on success, or -1 on failure.
                    461:  */
                    462:
                    463: static int
                    464: cvs_resp_validreq(int type, char *line)
                    465: {
                    466:        int i;
                    467:        char *sp, *ep;
                    468:
                    469:        /* parse the requests */
                    470:        sp = line;
                    471:        do {
                    472:                ep = strchr(sp, ' ');
                    473:                if (ep != NULL)
                    474:                        *ep = '\0';
                    475:
                    476:                i = cvs_req_getbyname(sp);
                    477:                if (i != -1)
                    478:                        cvs_server_validreq[i] = 1;
                    479:
                    480:                if (ep != NULL)
                    481:                        sp = ep + 1;
                    482:        } while (ep != NULL);
                    483:
                    484:        return (0);
                    485: }
                    486:
                    487:
                    488: /*
                    489:  * cvs_resp_m()
                    490:  *
1.4       jfb       491:  * Handler for the `M', 'MT', `F' and `E' responses.
1.1       jfb       492:  */
                    493:
                    494: static int
                    495: cvs_resp_m(int type, char *line)
                    496: {
1.5     ! jfb       497:        char *cp;
1.1       jfb       498:        FILE *stream;
                    499:
                    500:        stream = NULL;
                    501:
                    502:        switch (type) {
                    503:        case CVS_RESP_F:
                    504:                fflush(stderr);
                    505:                return (0);
                    506:        case CVS_RESP_M:
                    507:                stream = stdout;
                    508:                break;
                    509:        case CVS_RESP_E:
                    510:                stream = stderr;
                    511:                break;
                    512:        case CVS_RESP_MT:
1.5     ! jfb       513:                if (*line == '+') {
        !           514:                        if (cvs_mtstk_depth == CVS_MTSTK_MAXDEPTH) {
        !           515:                                cvs_log(LP_ERR,
        !           516:                                    "MT scope stack has reached max depth");
        !           517:                                return (-1);
        !           518:                        }
        !           519:                        cvs_mt_stack[cvs_mtstk_depth++] = strdup(line + 1);
        !           520:                        if (cvs_mt_stack[cvs_mtstk_depth] == NULL) {
        !           521:                                cvs_mtstk_depth--;
        !           522:                                return (-1);
        !           523:                        }
        !           524:                }
        !           525:                else if (*line == '-') {
        !           526:                        if (cvs_mtstk_depth == 0) {
        !           527:                                cvs_log(LP_ERR, "MT scope stack underflow");
        !           528:                                return (-1);
        !           529:                        }
        !           530:                        else if (strcmp(line,
        !           531:                            cvs_mt_stack[cvs_mtstk_depth]) != 0) {
        !           532:                                cvs_log(LP_ERR, "mismatch in MT scope stack");
        !           533:                                return (-1);
        !           534:                        }
        !           535:                        free(cvs_mt_stack[cvs_mtstk_depth--]);
        !           536:                }
        !           537:                else {
        !           538:                        if (strcmp(line, "newline") == 0)
        !           539:                                putc('\n', stdout);
        !           540:                        else if (strncmp(line, "fname ", 6) == 0)
        !           541:                                printf("%s", line + 6);
        !           542:                        else {
        !           543:                                /* assume text */
        !           544:                                cp = strchr(line, ' ');
        !           545:                                if (cp != NULL)
        !           546:                                        printf("%s", cp + 1);
        !           547:                        }
        !           548:                }
        !           549:
1.4       jfb       550:                break;
1.1       jfb       551:        case CVS_RESP_MBINARY:
1.5     ! jfb       552:                cvs_log(LP_WARN, "Mbinary not supported in client yet");
1.1       jfb       553:                break;
                    554:        }
                    555:
                    556:        fputs(line, stream);
                    557:        fputc('\n', stream);
                    558:
                    559:        return (0);
                    560: }
                    561:
                    562:
                    563: /*
                    564:  * cvs_resp_ok()
                    565:  *
                    566:  * Handler for the `ok' response.  This handler's job is to
                    567:  */
                    568:
                    569: static int
                    570: cvs_resp_ok(int type, char *line)
                    571: {
                    572:        return (1);
                    573: }
                    574:
                    575:
                    576: /*
                    577:  * cvs_resp_error()
                    578:  *
                    579:  * Handler for the `error' response.  This handler's job is to
                    580:  */
                    581:
                    582: static int
                    583: cvs_resp_error(int type, char *line)
                    584: {
                    585:        return (1);
                    586: }
                    587:
                    588:
                    589: /*
                    590:  * cvs_resp_statdir()
                    591:  *
                    592:  * Handler for the `Clear-static-directory' and `Set-static-directory'
                    593:  * responses.
                    594:  */
                    595:
                    596: static int
                    597: cvs_resp_statdir(int type, char *line)
                    598: {
                    599:        int fd;
                    600:        char statpath[MAXPATHLEN];
                    601:
                    602:        snprintf(statpath, sizeof(statpath), "%s/%s", line,
                    603:            CVS_PATH_STATICENTRIES);
                    604:
                    605:        if ((type == CVS_RESP_CLRSTATDIR) &&
1.5     ! jfb       606:            (unlink(statpath) == -1) && (errno != ENOENT)) {
1.1       jfb       607:                cvs_log(LP_ERRNO, "failed to unlink %s file",
                    608:                    CVS_PATH_STATICENTRIES);
                    609:                return (-1);
                    610:        }
                    611:        else if (type == CVS_RESP_SETSTATDIR) {
1.5     ! jfb       612:                fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0400);
1.1       jfb       613:                if (fd == -1) {
                    614:                        cvs_log(LP_ERRNO, "failed to create %s file",
                    615:                            CVS_PATH_STATICENTRIES);
                    616:                        return (-1);
                    617:                }
                    618:                (void)close(fd);
1.5     ! jfb       619:
        !           620:        }
        !           621:
        !           622:        return (0);
        !           623: }
        !           624:
        !           625: /*
        !           626:  * cvs_resp_sticky()
        !           627:  *
        !           628:  * Handler for the `Clear-sticky' and `Set-sticky' responses.
        !           629:  */
        !           630:
        !           631: static int
        !           632: cvs_resp_sticky(int type, char *line)
        !           633: {
        !           634:        if (type == CVS_RESP_CLRSTICKY) {
        !           635:        }
        !           636:        else if (type == CVS_RESP_SETSTICKY) {
1.1       jfb       637:        }
                    638:
                    639:        return (0);
                    640: }
                    641:
                    642:
                    643: /*
                    644:  * cvs_resp_newentry()
                    645:  *
                    646:  * Handler for the `New-entry' response and `Checked-in' responses.
                    647:  */
                    648:
                    649: static int
                    650: cvs_resp_newentry(int type, char *line)
                    651: {
                    652:        char entbuf[128], path[MAXPATHLEN];
                    653:        CVSENTRIES *entfile;
                    654:
1.2       jfb       655:        snprintf(path, sizeof(path), "%s/" CVS_PATH_ENTRIES, line);
1.1       jfb       656:
                    657:        /* get the remote path */
                    658:        cvs_client_getln(entbuf, sizeof(entbuf));
                    659:
                    660:        /* get the new Entries line */
                    661:        if (cvs_client_getln(entbuf, sizeof(entbuf)) < 0)
                    662:                return (-1);
                    663:
1.2       jfb       664:        entfile = cvs_ent_open(path, O_WRONLY);
1.1       jfb       665:        if (entfile == NULL)
                    666:                return (-1);
1.3       jfb       667:        cvs_ent_addln(entfile, entbuf);
1.1       jfb       668:        cvs_ent_close(entfile);
                    669:
                    670:        return (0);
                    671: }
                    672:
                    673:
                    674: /*
                    675:  * cvs_resp_cksum()
                    676:  *
                    677:  * Handler for the `Checksum' response.  We store the checksum received for
                    678:  * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>.
                    679:  * Upon next file reception, the handler checks to see if there is a stored
                    680:  * checksum.
                    681:  * The file handler must make sure that the checksums match and free the
                    682:  * checksum buffer once it's done to indicate there is no further checksum.
                    683:  */
                    684:
                    685: static int
                    686: cvs_resp_cksum(int type, char *line)
                    687: {
                    688:        if (cvs_fcksum != NULL) {
                    689:                cvs_log(LP_WARN, "unused checksum");
                    690:                free(cvs_fcksum);
                    691:        }
                    692:
                    693:        cvs_fcksum = strdup(line);
                    694:        if (cvs_fcksum == NULL) {
                    695:                cvs_log(LP_ERRNO, "failed to copy checksum string");
                    696:                return (-1);
                    697:        }
                    698:
1.4       jfb       699:        return (0);
                    700: }
                    701:
                    702:
                    703: /*
                    704:  * cvs_resp_modtime()
                    705:  *
                    706:  * Handler for the `Mod-time' file update modifying response.  The timestamp
                    707:  * given is used to set the last modification time on the next file that
                    708:  * will be received.
                    709:  */
                    710:
                    711: static int
                    712: cvs_resp_modtime(int type, char *line)
                    713: {
1.1       jfb       714:        return (0);
                    715: }
                    716:
                    717:
                    718: /*
                    719:  * cvs_resp_updated()
                    720:  *
                    721:  * Handler for the `Updated' response.
                    722:  */
                    723:
                    724: static int
                    725: cvs_resp_updated(int type, char *line)
                    726: {
                    727:        char cksum_buf[CVS_CKSUM_LEN];
                    728:
                    729:        if (type == CVS_RESP_CREATED) {
                    730:        }
                    731:        else if (type == CVS_RESP_UPDEXIST) {
                    732:        }
                    733:        else if (type == CVS_RESP_UPDATED) {
                    734:        }
                    735:
                    736:        if (cvs_recvfile(line) < 0) {
                    737:                return (-1);
                    738:        }
                    739:
                    740:        /* now see if there is a checksum */
                    741:        if (cvs_cksum(line, cksum_buf, sizeof(cksum_buf)) < 0) {
                    742:        }
                    743:
                    744:        if (strcmp(cksum_buf, cvs_fcksum) != 0) {
                    745:                cvs_log(LP_ERR, "checksum error on received file");
                    746:                (void)unlink(line);
                    747:        }
                    748:
                    749:        free(cvs_fcksum);
                    750:        cvs_fcksum = NULL;
                    751:
                    752:        return (0);
                    753: }
                    754:
                    755:
                    756: /*
                    757:  * cvs_resp_removed()
                    758:  *
                    759:  * Handler for the `Updated' response.
                    760:  */
                    761:
                    762: static int
                    763: cvs_resp_removed(int type, char *line)
                    764: {
                    765:        return (0);
                    766: }
                    767:
                    768:
                    769: /*
                    770:  * cvs_resp_mode()
                    771:  *
                    772:  * Handler for the `Mode' response.
                    773:  */
                    774:
                    775: static int
                    776: cvs_resp_mode(int type, char *line)
                    777: {
                    778:        if (cvs_strtomode(line, &cvs_lastmode) < 0) {
                    779:                return (-1);
                    780:        }
1.5     ! jfb       781:        return (0);
        !           782: }
        !           783:
        !           784:
        !           785: /*
        !           786:  * cvs_resp_modxpand()
        !           787:  *
        !           788:  * Handler for the `Module-expansion' response.
        !           789:  */
        !           790:
        !           791: static int
        !           792: cvs_resp_modxpand(int type, char *line)
        !           793: {
1.1       jfb       794:        return (0);
                    795: }
                    796:
                    797:
                    798: /*
                    799:  * cvs_sendfile()
                    800:  *
                    801:  * Send the mode and size of a file followed by the file's contents.
                    802:  * Returns 0 on success, or -1 on failure.
                    803:  */
                    804:
                    805: int
                    806: cvs_sendfile(const char *path)
                    807: {
                    808:        int fd;
                    809:        ssize_t ret;
                    810:        char buf[4096];
                    811:        struct stat st;
                    812:
                    813:        if (stat(path, &st) == -1) {
                    814:                cvs_log(LP_ERRNO, "failed to stat `%s'", path);
                    815:                return (-1);
                    816:        }
                    817:
                    818:        fd = open(path, O_RDONLY, 0);
                    819:        if (fd == -1) {
                    820:                return (-1);
                    821:        }
                    822:
                    823:        if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
                    824:                return (-1);
                    825:
                    826:        cvs_client_sendln(buf);
                    827:        snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
                    828:        cvs_client_sendln(buf);
                    829:
                    830:        while ((ret = read(fd, buf, sizeof(buf))) != 0) {
                    831:                if (ret == -1) {
                    832:                        cvs_log(LP_ERRNO, "failed to read file `%s'", path);
                    833:                        return (-1);
                    834:                }
                    835:
                    836:                cvs_client_sendraw(buf, (size_t)ret);
                    837:
                    838:        }
                    839:
                    840:        (void)close(fd);
                    841:
                    842:        return (0);
                    843: }
                    844:
                    845:
                    846: /*
                    847:  * cvs_recvfile()
                    848:  *
                    849:  * Receive the mode and size of a file followed the file's contents and
                    850:  * create or update the file whose path is <path> with the received
                    851:  * information.
                    852:  */
                    853:
                    854: int
                    855: cvs_recvfile(const char *path)
                    856: {
                    857:        int fd;
                    858:        mode_t mode;
                    859:        size_t len;
                    860:        ssize_t ret;
                    861:        off_t fsz, cnt;
                    862:        char buf[4096], *ep;
                    863:
                    864:        if ((cvs_client_getln(buf, sizeof(buf)) < 0) ||
                    865:            (cvs_strtomode(buf, &mode) < 0)) {
                    866:                return (-1);
                    867:        }
                    868:
                    869:        cvs_client_getln(buf, sizeof(buf));
                    870:
                    871:        fsz = (off_t)strtol(buf, &ep, 10);
                    872:        if (*ep != '\0') {
                    873:                cvs_log(LP_ERR, "parse error in file size transmission");
                    874:                return (-1);
                    875:        }
                    876:
                    877:        fd = open(path, O_RDONLY, mode);
                    878:        if (fd == -1) {
                    879:                cvs_log(LP_ERRNO, "failed to open `%s'", path);
                    880:                return (-1);
                    881:        }
                    882:
                    883:        cnt = 0;
                    884:        do {
                    885:                len = MIN(sizeof(buf), (size_t)(fsz - cnt));
                    886:                ret = cvs_client_recvraw(buf, len);
                    887:                if (ret == -1) {
                    888:                        (void)close(fd);
                    889:                        (void)unlink(path);
                    890:                        return (-1);
                    891:                }
                    892:
                    893:                if (write(fd, buf, (size_t)ret) == -1) {
                    894:                        cvs_log(LP_ERRNO,
                    895:                            "failed to write contents to file `%s'", path);
                    896:                        (void)close(fd);
                    897:                        (void)unlink(path);
                    898:                        return (-1);
                    899:                }
                    900:
                    901:                cnt += (off_t)ret;
                    902:        } while (cnt < fsz);
                    903:
                    904:        (void)close(fd);
                    905:
                    906:        return (0);
                    907: }