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

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