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

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.8     ! jfb       202: static time_t cvs_modtime = 0;
1.7       jfb       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.8     ! jfb       760:        int i;
        !           761:        long off;
        !           762:        char sign, mon[8], gmt[8], hr[4], min[4], *ep;
        !           763:        struct tm cvs_tm;
        !           764:
        !           765:        memset(&cvs_tm, 0, sizeof(cvs_tm));
        !           766:        sscanf(line, "%d %8s %d %2d:%2d:%2d %5s", &cvs_tm.tm_mday, mon,
        !           767:            &cvs_tm.tm_year, &cvs_tm.tm_hour, &cvs_tm.tm_min,
        !           768:            &cvs_tm.tm_sec, gmt);
        !           769:        cvs_tm.tm_year -= 1900;
        !           770:
        !           771:        if (*gmt == '-') {
        !           772:                sscanf("%c%2s%2s", &sign, hr, min);
        !           773:                cvs_tm.tm_gmtoff = strtol(hr, &ep, 10);
        !           774:                if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
        !           775:                    (cvs_tm.tm_gmtoff == LONG_MAX) ||
        !           776:                    (*ep != '\0')) {
        !           777:                        cvs_log(LP_ERR,
        !           778:                            "parse error in GMT hours specification `%s'", hr);
        !           779:                        cvs_tm.tm_gmtoff = 0;
        !           780:                }
        !           781:                else {
        !           782:                        /* get seconds */
        !           783:                        cvs_tm.tm_gmtoff *= 3600;
1.7       jfb       784:
1.8     ! jfb       785:                        /* add the minutes */
        !           786:                        off = strtol(min, &ep, 10);
        !           787:                        if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
        !           788:                            (cvs_tm.tm_gmtoff == LONG_MAX) ||
        !           789:                            (*ep != '\0')) {
        !           790:                                cvs_log(LP_ERR,
        !           791:                                    "parse error in GMT minutes "
        !           792:                                    "specification `%s'", min);
        !           793:                        }
        !           794:                        else
        !           795:                                cvs_tm.tm_gmtoff += off * 60;
        !           796:                }
        !           797:        }
        !           798:        if (sign == '-')
        !           799:                cvs_tm.tm_gmtoff = -cvs_tm.tm_gmtoff;
1.7       jfb       800:
1.8     ! jfb       801:        for (i = 0; i < (int)(sizeof(cvs_months)/sizeof(cvs_months[0])); i++) {
        !           802:                if (strcmp(cvs_months[i], mon) == 0) {
        !           803:                        cvs_tm.tm_mon = i;
        !           804:                        break;
        !           805:                }
1.7       jfb       806:        }
                    807:
1.8     ! jfb       808:        cvs_modtime = mktime(&cvs_tm);
1.1       jfb       809:        return (0);
                    810: }
                    811:
                    812:
                    813: /*
                    814:  * cvs_resp_updated()
                    815:  *
1.6       jfb       816:  * Handler for the `Updated' and `Created' responses.
1.1       jfb       817:  */
                    818:
                    819: static int
                    820: cvs_resp_updated(int type, char *line)
                    821: {
1.7       jfb       822:        size_t len;
                    823:        char tbuf[32], path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN];
                    824:        CVSENTRIES *ef;
1.6       jfb       825:        struct cvs_ent *ep;
1.1       jfb       826:
                    827:        if (type == CVS_RESP_CREATED) {
1.6       jfb       828:                /* read the remote path of the file */
                    829:                cvs_client_getln(path, sizeof(path));
                    830:
                    831:                /* read the new entry */
                    832:                cvs_client_getln(path, sizeof(path));
                    833:                ep = cvs_ent_parse(path);
                    834:                if (ep == NULL)
                    835:                        return (-1);
1.7       jfb       836:
                    837:                /* set the timestamp as the last one received from Mod-time */
1.8     ! jfb       838:                ep->ce_timestamp = ctime_r(&cvs_modtime, tbuf);
1.7       jfb       839:                len = strlen(tbuf);
                    840:                if ((len > 0) && (tbuf[len - 1] == '\n'))
                    841:                        tbuf[--len] = '\0';
                    842:
                    843:                ef = cvs_ent_open(line, O_WRONLY);
                    844:                if (ef == NULL)
                    845:                        return (-1);
                    846:
                    847:                cvs_ent_add(ef, ep);
                    848:                cvs_ent_close(ef);
1.1       jfb       849:        }
                    850:        else if (type == CVS_RESP_UPDEXIST) {
                    851:        }
                    852:        else if (type == CVS_RESP_UPDATED) {
                    853:        }
                    854:
1.6       jfb       855:        snprintf(path, sizeof(path), "%s%s", line, ep->ce_name);
                    856:        if (cvs_recvfile(path) < 0) {
1.1       jfb       857:                return (-1);
                    858:        }
                    859:
                    860:        /* now see if there is a checksum */
1.6       jfb       861:        if (cvs_fcksum != NULL) {
                    862:                if (cvs_cksum(line, cksum_buf, sizeof(cksum_buf)) < 0) {
                    863:                }
                    864:
                    865:                if (strcmp(cksum_buf, cvs_fcksum) != 0) {
                    866:                        cvs_log(LP_ERR, "checksum error on received file");
                    867:                        (void)unlink(line);
                    868:                }
1.1       jfb       869:
1.6       jfb       870:                free(cvs_fcksum);
                    871:                cvs_fcksum = NULL;
1.1       jfb       872:        }
                    873:
                    874:        return (0);
                    875: }
                    876:
                    877:
                    878: /*
                    879:  * cvs_resp_removed()
                    880:  *
                    881:  * Handler for the `Updated' response.
                    882:  */
                    883:
                    884: static int
                    885: cvs_resp_removed(int type, char *line)
                    886: {
                    887:        return (0);
                    888: }
                    889:
                    890:
                    891: /*
                    892:  * cvs_resp_mode()
                    893:  *
                    894:  * Handler for the `Mode' response.
                    895:  */
                    896:
                    897: static int
                    898: cvs_resp_mode(int type, char *line)
                    899: {
                    900:        if (cvs_strtomode(line, &cvs_lastmode) < 0) {
                    901:                return (-1);
                    902:        }
1.5       jfb       903:        return (0);
                    904: }
                    905:
                    906:
                    907: /*
                    908:  * cvs_resp_modxpand()
                    909:  *
                    910:  * Handler for the `Module-expansion' response.
                    911:  */
                    912:
                    913: static int
                    914: cvs_resp_modxpand(int type, char *line)
                    915: {
1.1       jfb       916:        return (0);
                    917: }
                    918:
                    919:
                    920: /*
                    921:  * cvs_sendfile()
                    922:  *
                    923:  * Send the mode and size of a file followed by the file's contents.
                    924:  * Returns 0 on success, or -1 on failure.
                    925:  */
                    926:
                    927: int
                    928: cvs_sendfile(const char *path)
                    929: {
                    930:        int fd;
                    931:        ssize_t ret;
                    932:        char buf[4096];
                    933:        struct stat st;
                    934:
                    935:        if (stat(path, &st) == -1) {
                    936:                cvs_log(LP_ERRNO, "failed to stat `%s'", path);
                    937:                return (-1);
                    938:        }
                    939:
                    940:        fd = open(path, O_RDONLY, 0);
                    941:        if (fd == -1) {
                    942:                return (-1);
                    943:        }
                    944:
                    945:        if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
                    946:                return (-1);
                    947:
                    948:        cvs_client_sendln(buf);
                    949:        snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
                    950:        cvs_client_sendln(buf);
                    951:
                    952:        while ((ret = read(fd, buf, sizeof(buf))) != 0) {
                    953:                if (ret == -1) {
                    954:                        cvs_log(LP_ERRNO, "failed to read file `%s'", path);
                    955:                        return (-1);
                    956:                }
                    957:
                    958:                cvs_client_sendraw(buf, (size_t)ret);
                    959:
                    960:        }
                    961:
                    962:        (void)close(fd);
                    963:
                    964:        return (0);
                    965: }
                    966:
                    967:
                    968: /*
                    969:  * cvs_recvfile()
                    970:  *
                    971:  * Receive the mode and size of a file followed the file's contents and
                    972:  * create or update the file whose path is <path> with the received
                    973:  * information.
                    974:  */
                    975:
                    976: int
                    977: cvs_recvfile(const char *path)
                    978: {
                    979:        int fd;
                    980:        mode_t mode;
                    981:        size_t len;
                    982:        ssize_t ret;
                    983:        off_t fsz, cnt;
                    984:        char buf[4096], *ep;
                    985:
                    986:        if ((cvs_client_getln(buf, sizeof(buf)) < 0) ||
                    987:            (cvs_strtomode(buf, &mode) < 0)) {
                    988:                return (-1);
                    989:        }
                    990:
                    991:        cvs_client_getln(buf, sizeof(buf));
                    992:
                    993:        fsz = (off_t)strtol(buf, &ep, 10);
                    994:        if (*ep != '\0') {
                    995:                cvs_log(LP_ERR, "parse error in file size transmission");
                    996:                return (-1);
                    997:        }
                    998:
1.6       jfb       999:        fd = open(path, O_WRONLY|O_CREAT, mode);
1.1       jfb      1000:        if (fd == -1) {
                   1001:                cvs_log(LP_ERRNO, "failed to open `%s'", path);
                   1002:                return (-1);
                   1003:        }
                   1004:
                   1005:        cnt = 0;
                   1006:        do {
                   1007:                len = MIN(sizeof(buf), (size_t)(fsz - cnt));
1.6       jfb      1008:                if (len == 0)
                   1009:                        break;
1.1       jfb      1010:                ret = cvs_client_recvraw(buf, len);
                   1011:                if (ret == -1) {
                   1012:                        (void)close(fd);
                   1013:                        (void)unlink(path);
                   1014:                        return (-1);
                   1015:                }
                   1016:
                   1017:                if (write(fd, buf, (size_t)ret) == -1) {
                   1018:                        cvs_log(LP_ERRNO,
                   1019:                            "failed to write contents to file `%s'", path);
                   1020:                        (void)close(fd);
                   1021:                        (void)unlink(path);
                   1022:                        return (-1);
                   1023:                }
                   1024:
                   1025:                cnt += (off_t)ret;
                   1026:        } while (cnt < fsz);
                   1027:
                   1028:        (void)close(fd);
                   1029:
                   1030:        return (0);
                   1031: }