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

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"
1.14      jfb        61: #include "file.h"
1.13      jfb        62: #include "proto.h"
1.1       jfb        63:
                     64:
1.5       jfb        65: #define CVS_MTSTK_MAXDEPTH   16
                     66:
1.11      jfb        67: /* request flags */
                     68: #define CVS_REQF_RESP    0x01
                     69:
                     70:
1.5       jfb        71:
1.1       jfb        72:
                     73: extern int   verbosity;
                     74: extern int   cvs_compress;
                     75: extern char *cvs_rsh;
                     76: extern int   cvs_trace;
                     77: extern int   cvs_nolog;
                     78: extern int   cvs_readonly;
                     79:
                     80:
                     81:
1.13      jfb        82: static int  cvs_resp_validreq  (struct cvsroot *, int, char *);
                     83: static int  cvs_resp_cksum     (struct cvsroot *, int, char *);
                     84: static int  cvs_resp_modtime   (struct cvsroot *, int, char *);
                     85: static int  cvs_resp_m         (struct cvsroot *, int, char *);
                     86: static int  cvs_resp_ok        (struct cvsroot *, int, char *);
                     87: static int  cvs_resp_error     (struct cvsroot *, int, char *);
                     88: static int  cvs_resp_statdir   (struct cvsroot *, int, char *);
                     89: static int  cvs_resp_sticky    (struct cvsroot *, int, char *);
                     90: static int  cvs_resp_newentry  (struct cvsroot *, int, char *);
                     91: static int  cvs_resp_updated   (struct cvsroot *, int, char *);
                     92: static int  cvs_resp_removed   (struct cvsroot *, int, char *);
                     93: static int  cvs_resp_mode      (struct cvsroot *, int, char *);
                     94: static int  cvs_resp_modxpand  (struct cvsroot *, int, char *);
1.14      jfb        95: static int  cvs_resp_rcsdiff   (struct cvsroot *, int, char *);
1.16      jfb        96: static int  cvs_resp_template  (struct cvsroot *, int, char *);
1.1       jfb        97:
1.13      jfb        98: static int  cvs_initlog   (void);
1.1       jfb        99:
1.7       jfb       100: static const char *cvs_months[] = {
1.9       jfb       101:        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                    102:        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1.7       jfb       103: };
                    104:
                    105:
                    106:
1.1       jfb       107: struct cvs_req {
                    108:        int      req_id;
                    109:        char     req_str[32];
                    110:        u_int    req_flags;
1.13      jfb       111:        int     (*req_hdlr)(int, char *);
1.1       jfb       112: } cvs_requests[] = {
                    113:        { CVS_REQ_DIRECTORY,     "Directory",         0,  NULL },
                    114:        { CVS_REQ_MAXDOTDOT,     "Max-dotdot",        0,  NULL },
                    115:        { CVS_REQ_STATICDIR,     "Static-directory",  0,  NULL },
                    116:        { CVS_REQ_STICKY,        "Sticky",            0,  NULL },
                    117:        { CVS_REQ_ENTRY,         "Entry",             0,  NULL },
                    118:        { CVS_REQ_ENTRYEXTRA,    "EntryExtra",        0,  NULL },
                    119:        { CVS_REQ_CHECKINTIME,   "Checkin-time",      0,  NULL },
                    120:        { CVS_REQ_MODIFIED,      "Modified",          0,  NULL },
                    121:        { CVS_REQ_ISMODIFIED,    "Is-modified",       0,  NULL },
                    122:        { CVS_REQ_UNCHANGED,     "Unchanged",         0,  NULL },
                    123:        { CVS_REQ_USEUNCHANGED,  "UseUnchanged",      0,  NULL },
                    124:        { CVS_REQ_NOTIFY,        "Notify",            0,  NULL },
                    125:        { CVS_REQ_NOTIFYUSER,    "NotifyUser",        0,  NULL },
                    126:        { CVS_REQ_QUESTIONABLE,  "Questionable",      0,  NULL },
                    127:        { CVS_REQ_CASE,          "Case",              0,  NULL },
                    128:        { CVS_REQ_UTF8,          "Utf8",              0,  NULL },
                    129:        { CVS_REQ_ARGUMENT,      "Argument",          0,  NULL },
                    130:        { CVS_REQ_ARGUMENTX,     "Argumentx",         0,  NULL },
                    131:        { CVS_REQ_GLOBALOPT,     "Global_option",     0,  NULL },
                    132:        { CVS_REQ_GZIPSTREAM,    "Gzip-stream",       0,  NULL },
                    133:        { CVS_REQ_READCVSRC2,    "read-cvsrc2",       0,  NULL },
                    134:        { CVS_REQ_READWRAP,      "read-cvswrappers",  0,  NULL },
                    135:        { CVS_REQ_READIGNORE,    "read-cvsignore",    0,  NULL },
                    136:        { CVS_REQ_ERRIFREADER,   "Error-If-Reader",   0,  NULL },
                    137:        { CVS_REQ_VALIDRCSOPT,   "Valid-RcsOptions",  0,  NULL },
                    138:        { CVS_REQ_SET,           "Set",               0,  NULL },
1.11      jfb       139:        { CVS_REQ_XPANDMOD,      "expand-modules",    CVS_REQF_RESP,  NULL },
1.1       jfb       140:        { CVS_REQ_LOG,           "log",               0,  NULL },
1.11      jfb       141:        { CVS_REQ_CO,            "co",                CVS_REQF_RESP,  NULL },
                    142:        { CVS_REQ_EXPORT,        "export",            CVS_REQF_RESP,  NULL },
1.1       jfb       143:        { CVS_REQ_RANNOTATE,     "rannotate",         0,  NULL },
                    144:        { CVS_REQ_RDIFF,         "rdiff",             0,  NULL },
                    145:        { CVS_REQ_RLOG,          "rlog",              0,  NULL },
1.11      jfb       146:        { CVS_REQ_RTAG,          "rtag",              CVS_REQF_RESP,  NULL },
                    147:        { CVS_REQ_INIT,          "init",              CVS_REQF_RESP,  NULL },
                    148:        { CVS_REQ_STATUS,        "status",            CVS_REQF_RESP,  NULL },
                    149:        { CVS_REQ_UPDATE,        "update",            CVS_REQF_RESP,  NULL },
1.1       jfb       150:        { CVS_REQ_HISTORY,       "history",           0,  NULL },
1.11      jfb       151:        { CVS_REQ_IMPORT,        "import",            CVS_REQF_RESP,  NULL },
                    152:        { CVS_REQ_ADD,           "add",               CVS_REQF_RESP,  NULL },
                    153:        { CVS_REQ_REMOVE,        "remove",            CVS_REQF_RESP,  NULL },
                    154:        { CVS_REQ_RELEASE,       "release",           CVS_REQF_RESP,  NULL },
1.1       jfb       155:        { CVS_REQ_ROOT,          "Root",              0,  NULL },
1.13      jfb       156:        { CVS_REQ_VALIDRESP,     "Valid-responses",   0,  NULL },
1.11      jfb       157:        { CVS_REQ_VALIDREQ,      "valid-requests",    CVS_REQF_RESP,  NULL },
                    158:        { CVS_REQ_VERSION,       "version",           CVS_REQF_RESP,  NULL },
                    159:        { CVS_REQ_NOOP,          "noop",              CVS_REQF_RESP,  NULL },
                    160:        { CVS_REQ_DIFF,          "diff",              CVS_REQF_RESP,  NULL },
1.1       jfb       161: };
                    162:
                    163:
                    164: struct cvs_resp {
                    165:        u_int  resp_id;
                    166:        char   resp_str[32];
1.13      jfb       167:        int  (*resp_hdlr)(struct cvsroot *, int, char *);
1.1       jfb       168: } cvs_responses[] = {
                    169:        { CVS_RESP_OK,         "ok",                     cvs_resp_ok       },
                    170:        { CVS_RESP_ERROR,      "error",                  cvs_resp_error    },
                    171:        { CVS_RESP_VALIDREQ,   "Valid-requests",         cvs_resp_validreq },
                    172:        { CVS_RESP_M,          "M",                      cvs_resp_m        },
                    173:        { CVS_RESP_MBINARY,    "Mbinary",                cvs_resp_m        },
                    174:        { CVS_RESP_MT,         "MT",                     cvs_resp_m        },
                    175:        { CVS_RESP_E,          "E",                      cvs_resp_m        },
                    176:        { CVS_RESP_F,          "F",                      cvs_resp_m        },
                    177:        { CVS_RESP_CREATED,    "Created",                cvs_resp_updated  },
                    178:        { CVS_RESP_UPDATED,    "Updated",                cvs_resp_updated  },
                    179:        { CVS_RESP_UPDEXIST,   "Update-existing",        cvs_resp_updated  },
1.15      jfb       180:        { CVS_RESP_MERGED,     "Merged",                 cvs_resp_updated  },
1.1       jfb       181:        { CVS_RESP_REMOVED,    "Removed",                cvs_resp_removed  },
                    182:        { CVS_RESP_CKSUM,      "Checksum",               cvs_resp_cksum    },
                    183:        { CVS_RESP_CLRSTATDIR, "Clear-static-directory", cvs_resp_statdir  },
                    184:        { CVS_RESP_SETSTATDIR, "Set-static-directory",   cvs_resp_statdir  },
                    185:        { CVS_RESP_NEWENTRY,   "New-entry",              cvs_resp_newentry },
                    186:        { CVS_RESP_CHECKEDIN,  "Checked-in",             cvs_resp_newentry },
                    187:        { CVS_RESP_MODE,       "Mode",                   cvs_resp_mode     },
1.4       jfb       188:        { CVS_RESP_MODTIME,    "Mod-time",               cvs_resp_modtime  },
1.5       jfb       189:        { CVS_RESP_MODXPAND,   "Module-expansion",       cvs_resp_modxpand },
                    190:        { CVS_RESP_SETSTICKY,  "Set-sticky",             cvs_resp_sticky   },
                    191:        { CVS_RESP_CLRSTICKY,  "Clear-sticky",           cvs_resp_sticky   },
1.14      jfb       192:        { CVS_RESP_RCSDIFF,    "Rcs-diff",               cvs_resp_rcsdiff  },
1.16      jfb       193:        { CVS_RESP_TEMPLATE,   "Template",               cvs_resp_template },
1.1       jfb       194: };
                    195:
1.17    ! jfb       196: #define CVS_NBREQ   (sizeof(cvs_requests)/sizeof(cvs_requests[0]))
        !           197: #define CVS_NBRESP  (sizeof(cvs_responses)/sizeof(cvs_responses[0]))
        !           198:
        !           199:
        !           200: /*
        !           201:  * The MT command uses scoping to tag the data.  Whenever we encouter a '+',
        !           202:  * we push the name of the tag on the stack, and we pop it when we encounter
        !           203:  * a '-' with the same name.
        !           204:  */
1.1       jfb       205:
1.5       jfb       206: static char *cvs_mt_stack[CVS_MTSTK_MAXDEPTH];
                    207: static u_int cvs_mtstk_depth = 0;
                    208:
1.8       jfb       209: static time_t cvs_modtime = 0;
1.7       jfb       210:
1.5       jfb       211:
1.1       jfb       212: /* mask of requets supported by server */
                    213: static u_char  cvs_server_validreq[CVS_REQ_MAX + 1];
                    214:
1.17    ! jfb       215: /* last checksum received */
1.5       jfb       216: char *cvs_fcksum = NULL;
                    217:
                    218: mode_t  cvs_lastmode = 0;
                    219:
1.17    ! jfb       220: /* hack to receive the remote version without outputting it */
        !           221: static u_int cvs_version_sent = 0;
        !           222:
        !           223:
1.13      jfb       224: static char  cvs_proto_buf[4096];
                    225:
                    226: /*
                    227:  * Output files for protocol logging when the CVS_CLIENT_LOG enviroment
                    228:  * variable is set.
                    229:  */
                    230: static int   cvs_server_logon = 0;
                    231: static FILE *cvs_server_inlog = NULL;
                    232: static FILE *cvs_server_outlog = NULL;
                    233:
                    234:
                    235: /*
                    236:  * cvs_connect()
                    237:  *
                    238:  * Open a client connection to the cvs server whose address is given in
                    239:  * the <root> variable.  The method used to connect depends on the
                    240:  * setting of the CVS_RSH variable.
1.17    ! jfb       241:  * Once the connection has been established, we first send the list of
        !           242:  * responses we support and request the list of supported requests from the
        !           243:  * server.  Then, a version request is sent and various global flags are sent.
1.13      jfb       244:  * Returns 0 on success, or -1 on failure.
                    245:  */
                    246:
                    247: int
                    248: cvs_connect(struct cvsroot *root)
                    249: {
                    250:        int argc, infd[2], outfd[2];
                    251:        pid_t pid;
                    252:        char *argv[16], *cvs_server_cmd, *vresp;
                    253:
                    254:        if (pipe(infd) == -1) {
                    255:                cvs_log(LP_ERRNO,
                    256:                    "failed to create input pipe for client connection");
                    257:                return (-1);
                    258:        }
                    259:
                    260:        if (pipe(outfd) == -1) {
                    261:                cvs_log(LP_ERRNO,
                    262:                    "failed to create output pipe for client connection");
                    263:                (void)close(infd[0]);
                    264:                (void)close(infd[1]);
                    265:                return (-1);
                    266:        }
                    267:
                    268:        pid = fork();
                    269:        if (pid == -1) {
                    270:                cvs_log(LP_ERRNO, "failed to fork for cvs server connection");
                    271:                return (-1);
                    272:        }
                    273:        if (pid == 0) {
                    274:                if ((dup2(infd[0], STDIN_FILENO) == -1) ||
                    275:                    (dup2(outfd[1], STDOUT_FILENO) == -1)) {
                    276:                        cvs_log(LP_ERRNO,
                    277:                            "failed to setup standard streams for cvs server");
                    278:                        return (-1);
                    279:                }
                    280:                (void)close(infd[1]);
                    281:                (void)close(outfd[0]);
                    282:
                    283:                argc = 0;
                    284:                argv[argc++] = cvs_rsh;
                    285:
                    286:                if (root->cr_user != NULL) {
                    287:                        argv[argc++] = "-l";
                    288:                        argv[argc++] = root->cr_user;
                    289:                }
                    290:
                    291:
                    292:                cvs_server_cmd = getenv("CVS_SERVER");
                    293:                if (cvs_server_cmd == NULL)
                    294:                        cvs_server_cmd = "cvs";
                    295:
                    296:                argv[argc++] = root->cr_host;
                    297:                argv[argc++] = cvs_server_cmd;
                    298:                argv[argc++] = "server";
                    299:                argv[argc] = NULL;
                    300:
                    301:                execvp(argv[0], argv);
                    302:                cvs_log(LP_ERRNO, "failed to exec");
                    303:                exit(EX_OSERR);
                    304:        }
                    305:
                    306:        /* we are the parent */
                    307:        (void)close(infd[0]);
                    308:        (void)close(outfd[1]);
                    309:
                    310:        root->cr_srvin = fdopen(infd[1], "w");
                    311:        if (root->cr_srvin == NULL) {
                    312:                cvs_log(LP_ERRNO, "failed to create pipe stream");
                    313:                return (-1);
                    314:        }
                    315:
                    316:        root->cr_srvout = fdopen(outfd[0], "r");
                    317:        if (root->cr_srvout == NULL) {
                    318:                cvs_log(LP_ERRNO, "failed to create pipe stream");
                    319:                return (-1);
                    320:        }
                    321:
                    322:        /* make the streams line-buffered */
                    323:        (void)setvbuf(root->cr_srvin, NULL, _IOLBF, 0);
                    324:        (void)setvbuf(root->cr_srvout, NULL, _IOLBF, 0);
                    325:
                    326:        cvs_initlog();
                    327:
                    328:        /*
                    329:         * Send the server the list of valid responses, then ask for valid
                    330:         * requests.
                    331:         */
                    332:
                    333:        vresp = cvs_resp_getvalid();
                    334:        if (vresp == NULL) {
                    335:                cvs_log(LP_ERR, "can't generate list of valid responses");
                    336:                return (-1);
                    337:        }
                    338:
                    339:        if (cvs_sendreq(root, CVS_REQ_VALIDRESP, vresp) < 0) {
                    340:        }
                    341:        free(vresp);
                    342:
                    343:        if (cvs_sendreq(root, CVS_REQ_VALIDREQ, NULL) < 0) {
                    344:                cvs_log(LP_ERR, "failed to get valid requests from server");
                    345:                return (-1);
                    346:        }
                    347:
1.17    ! jfb       348:        if (cvs_sendreq(root, CVS_REQ_VERSION, NULL) < 0)
        !           349:                cvs_log(LP_ERR, "failed to get remote version");
        !           350:
1.13      jfb       351:        /* now share our global options with the server */
                    352:        if (verbosity == 1)
                    353:                cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-q");
                    354:        else if (verbosity == 0)
                    355:                cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-Q");
                    356:
                    357:        if (cvs_nolog)
                    358:                cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-l");
                    359:        if (cvs_readonly)
                    360:                cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-r");
                    361:        if (cvs_trace)
                    362:                cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-t");
                    363:
                    364:        /* now send the CVSROOT to the server */
                    365:        if (cvs_sendreq(root, CVS_REQ_ROOT, root->cr_dir) < 0)
                    366:                return (-1);
                    367:
                    368:        /* not sure why, but we have to send this */
                    369:        if (cvs_sendreq(root, CVS_REQ_USEUNCHANGED, NULL) < 0)
                    370:                return (-1);
                    371:
                    372: #ifdef CVS_ZLIB
                    373:        /* if compression was requested, initialize it */
                    374: #endif
                    375:
                    376:        cvs_log(LP_DEBUG, "connected to %s", root->cr_host);
                    377:
                    378:        return (0);
                    379: }
                    380:
                    381:
                    382: /*
                    383:  * cvs_disconnect()
                    384:  *
                    385:  * Disconnect from the cvs server.
                    386:  */
                    387:
                    388: void
                    389: cvs_disconnect(struct cvsroot *root)
                    390: {
                    391:        cvs_log(LP_DEBUG, "closing connection to %s", root->cr_host);
                    392:        if (root->cr_srvin != NULL) {
                    393:                (void)fclose(root->cr_srvin);
                    394:                root->cr_srvin = NULL;
                    395:        }
                    396:        if (root->cr_srvout != NULL) {
                    397:                (void)fclose(root->cr_srvout);
                    398:                root->cr_srvout = NULL;
                    399:        }
                    400: }
                    401:
1.1       jfb       402:
                    403: /*
                    404:  * cvs_req_getbyid()
                    405:  *
                    406:  */
                    407:
1.13      jfb       408: struct cvs_req*
1.1       jfb       409: cvs_req_getbyid(int reqid)
                    410: {
                    411:        u_int i;
                    412:
                    413:        for (i = 0; i < CVS_NBREQ; i++)
                    414:                if (cvs_requests[i].req_id == reqid)
1.13      jfb       415:                        return &(cvs_requests[i]);
1.1       jfb       416:        return (NULL);
                    417: }
                    418:
                    419:
                    420: /*
                    421:  * cvs_req_getbyname()
                    422:  */
                    423:
1.13      jfb       424: struct cvs_req*
1.1       jfb       425: cvs_req_getbyname(const char *rname)
                    426: {
                    427:        u_int i;
                    428:
                    429:        for (i = 0; i < CVS_NBREQ; i++)
                    430:                if (strcmp(cvs_requests[i].req_str, rname) == 0)
1.13      jfb       431:                        return &(cvs_requests[i]);
1.1       jfb       432:
1.13      jfb       433:        return (NULL);
1.1       jfb       434: }
                    435:
                    436:
                    437: /*
                    438:  * cvs_req_getvalid()
                    439:  *
                    440:  * Build a space-separated list of all the requests that this protocol
                    441:  * implementation supports.
                    442:  */
                    443:
                    444: char*
                    445: cvs_req_getvalid(void)
                    446: {
                    447:        u_int i;
                    448:        size_t len;
                    449:        char *vrstr;
                    450:        BUF *buf;
                    451:
                    452:        buf = cvs_buf_alloc(512, BUF_AUTOEXT);
                    453:        if (buf == NULL)
                    454:                return (NULL);
                    455:
                    456:        cvs_buf_set(buf, cvs_requests[0].req_str,
                    457:            strlen(cvs_requests[0].req_str), 0);
                    458:
                    459:        for (i = 1; i < CVS_NBREQ; i++) {
                    460:                if ((cvs_buf_putc(buf, ' ') < 0) ||
                    461:                    (cvs_buf_append(buf, cvs_requests[i].req_str,
                    462:                    strlen(cvs_requests[i].req_str)) < 0)) {
                    463:                        cvs_buf_free(buf);
                    464:                        return (NULL);
                    465:                }
                    466:        }
                    467:
                    468:        /* NUL-terminate */
                    469:        if (cvs_buf_putc(buf, '\0') < 0) {
                    470:                cvs_buf_free(buf);
                    471:                return (NULL);
                    472:        }
                    473:
                    474:        len = cvs_buf_size(buf);
                    475:        vrstr = (char *)malloc(len);
                    476:
                    477:        cvs_buf_copy(buf, 0, vrstr, len);
                    478:
                    479:        cvs_buf_free(buf);
                    480:
                    481:        return (vrstr);
                    482: }
                    483:
                    484:
                    485: /*
                    486:  * cvs_req_handle()
                    487:  *
                    488:  * Generic request handler dispatcher.
                    489:  */
                    490:
                    491: int
                    492: cvs_req_handle(char *line)
                    493: {
1.16      jfb       494:        u_int i;
                    495:        char *cp, *cmd;
                    496:
                    497:        cmd = line;
                    498:
                    499:        cp = strchr(cmd, ' ');
                    500:        if (cp != NULL)
                    501:                *(cp++) = '\0';
                    502:
                    503:        for (i = 0; i < CVS_NBREQ; i++) {
                    504:                if (strcmp(cvs_requests[i].req_str, cmd) == 0) {
                    505:                        if (cvs_requests[i].req_hdlr == NULL) {
                    506:                                cvs_log(LP_ERR,
                    507:                                    "unimplemented request handler for `%s'",
                    508:                                    cmd);
                    509:                                break;
                    510:                        }
                    511:                        else
                    512:                                return (*cvs_requests[i].req_hdlr)
                    513:                                    (cvs_requests[i].req_id, cp);
                    514:                }
                    515:        }
                    516:
                    517:        /* unhandled */
                    518:        return (-1);
1.1       jfb       519: }
                    520:
                    521:
                    522: /*
                    523:  * cvs_resp_getbyid()
                    524:  *
                    525:  */
                    526:
1.13      jfb       527: struct cvs_resp*
1.1       jfb       528: cvs_resp_getbyid(int respid)
                    529: {
                    530:        u_int i;
                    531:
                    532:        for (i = 0; i < CVS_NBREQ; i++)
1.13      jfb       533:                if (cvs_responses[i].resp_id == (u_int)respid)
                    534:                        return &(cvs_responses[i]);
1.1       jfb       535:        return (NULL);
                    536: }
                    537:
                    538:
                    539: /*
                    540:  * cvs_resp_getbyname()
                    541:  */
                    542:
1.13      jfb       543: struct cvs_resp*
1.1       jfb       544: cvs_resp_getbyname(const char *rname)
                    545: {
                    546:        u_int i;
                    547:
                    548:        for (i = 0; i < CVS_NBREQ; i++)
                    549:                if (strcmp(cvs_responses[i].resp_str, rname) == 0)
1.13      jfb       550:                        return &(cvs_responses[i]);
1.1       jfb       551:
1.13      jfb       552:        return (NULL);
1.1       jfb       553: }
                    554:
                    555:
                    556: /*
                    557:  * cvs_resp_getvalid()
                    558:  *
                    559:  * Build a space-separated list of all the responses that this protocol
                    560:  * implementation supports.
                    561:  */
                    562:
                    563: char*
                    564: cvs_resp_getvalid(void)
                    565: {
                    566:        u_int i;
                    567:        size_t len;
                    568:        char *vrstr;
                    569:        BUF *buf;
                    570:
                    571:        buf = cvs_buf_alloc(512, BUF_AUTOEXT);
                    572:        if (buf == NULL)
                    573:                return (NULL);
                    574:
                    575:        cvs_buf_set(buf, cvs_responses[0].resp_str,
                    576:            strlen(cvs_responses[0].resp_str), 0);
                    577:
                    578:        for (i = 1; i < CVS_NBRESP; i++) {
                    579:                if ((cvs_buf_putc(buf, ' ') < 0) ||
                    580:                    (cvs_buf_append(buf, cvs_responses[i].resp_str,
                    581:                    strlen(cvs_responses[i].resp_str)) < 0)) {
                    582:                        cvs_buf_free(buf);
                    583:                        return (NULL);
                    584:                }
                    585:        }
                    586:
                    587:        /* NUL-terminate */
                    588:        if (cvs_buf_putc(buf, '\0') < 0) {
                    589:                cvs_buf_free(buf);
                    590:                return (NULL);
                    591:        }
                    592:
                    593:        len = cvs_buf_size(buf);
                    594:        vrstr = (char *)malloc(len);
                    595:
                    596:        cvs_buf_copy(buf, 0, vrstr, len);
                    597:        cvs_buf_free(buf);
                    598:
                    599:        return (vrstr);
                    600: }
                    601:
                    602:
                    603: /*
                    604:  * cvs_resp_handle()
                    605:  *
                    606:  * Generic response handler dispatcher.  The handler expects the first line
                    607:  * of the command as single argument.
                    608:  * Returns the return value of the command on success, or -1 on failure.
                    609:  */
                    610:
                    611: int
1.13      jfb       612: cvs_resp_handle(struct cvsroot *root, char *line)
1.1       jfb       613: {
                    614:        u_int i;
                    615:        char *cp, *cmd;
                    616:
                    617:        cmd = line;
                    618:
                    619:        cp = strchr(cmd, ' ');
                    620:        if (cp != NULL)
                    621:                *(cp++) = '\0';
                    622:
                    623:        for (i = 0; i < CVS_NBRESP; i++) {
1.16      jfb       624:                if (strcmp(cvs_responses[i].resp_str, cmd) == 0) {
                    625:                        if (cvs_responses[i].resp_hdlr == NULL) {
                    626:                                cvs_log(LP_ERRNO,
                    627:                                    "unimplemented response handler for `%s'",
                    628:                                    cmd);
                    629:                                return (-1);
                    630:                        }
                    631:                        else
                    632:                                return (*cvs_responses[i].resp_hdlr)
                    633:                                    (root, cvs_responses[i].resp_id, cp);
                    634:                }
1.1       jfb       635:        }
                    636:
                    637:        /* unhandled */
                    638:        return (-1);
                    639: }
                    640:
                    641:
                    642: /*
                    643:  * cvs_resp_validreq()
                    644:  *
                    645:  * Handler for the `Valid-requests' response.  The list of valid requests is
                    646:  * split on spaces and each request's entry in the valid request array is set
                    647:  * to 1 to indicate the validity.
                    648:  * Returns 0 on success, or -1 on failure.
                    649:  */
                    650:
                    651: static int
1.13      jfb       652: cvs_resp_validreq(struct cvsroot *root, int type, char *line)
1.1       jfb       653: {
                    654:        char *sp, *ep;
1.13      jfb       655:        struct cvs_req *req;
1.1       jfb       656:
                    657:        /* parse the requests */
                    658:        sp = line;
                    659:        do {
                    660:                ep = strchr(sp, ' ');
                    661:                if (ep != NULL)
                    662:                        *ep = '\0';
                    663:
1.13      jfb       664:                req = cvs_req_getbyname(sp);
                    665:                if (req != NULL)
                    666:                        cvs_server_validreq[req->req_id] = 1;
1.1       jfb       667:
                    668:                if (ep != NULL)
                    669:                        sp = ep + 1;
                    670:        } while (ep != NULL);
                    671:
                    672:        return (0);
                    673: }
                    674:
                    675:
                    676: /*
                    677:  * cvs_resp_m()
                    678:  *
1.4       jfb       679:  * Handler for the `M', 'MT', `F' and `E' responses.
1.1       jfb       680:  */
                    681:
                    682: static int
1.13      jfb       683: cvs_resp_m(struct cvsroot *root, int type, char *line)
1.1       jfb       684: {
1.5       jfb       685:        char *cp;
1.1       jfb       686:        FILE *stream;
                    687:
                    688:        stream = NULL;
                    689:
                    690:        switch (type) {
                    691:        case CVS_RESP_F:
                    692:                fflush(stderr);
                    693:                return (0);
                    694:        case CVS_RESP_M:
1.17    ! jfb       695:                if (cvs_version_sent) {
        !           696:                        /*
        !           697:                         * Instead of outputting the line, we save it as the
        !           698:                         * remote server's version string.
        !           699:                         */
        !           700:                        cvs_version_sent = 0;
        !           701:                        root->cr_version = strdup(line);
        !           702:                        return (0);
        !           703:                }
1.1       jfb       704:                stream = stdout;
                    705:                break;
                    706:        case CVS_RESP_E:
                    707:                stream = stderr;
                    708:                break;
                    709:        case CVS_RESP_MT:
1.5       jfb       710:                if (*line == '+') {
                    711:                        if (cvs_mtstk_depth == CVS_MTSTK_MAXDEPTH) {
                    712:                                cvs_log(LP_ERR,
                    713:                                    "MT scope stack has reached max depth");
                    714:                                return (-1);
                    715:                        }
1.6       jfb       716:                        cvs_mt_stack[cvs_mtstk_depth] = strdup(line + 1);
                    717:                        if (cvs_mt_stack[cvs_mtstk_depth] == NULL)
1.5       jfb       718:                                return (-1);
1.6       jfb       719:                        cvs_mtstk_depth++;
1.5       jfb       720:                }
                    721:                else if (*line == '-') {
                    722:                        if (cvs_mtstk_depth == 0) {
                    723:                                cvs_log(LP_ERR, "MT scope stack underflow");
                    724:                                return (-1);
                    725:                        }
1.6       jfb       726:                        else if (strcmp(line + 1,
                    727:                            cvs_mt_stack[cvs_mtstk_depth - 1]) != 0) {
1.5       jfb       728:                                cvs_log(LP_ERR, "mismatch in MT scope stack");
                    729:                                return (-1);
                    730:                        }
                    731:                        free(cvs_mt_stack[cvs_mtstk_depth--]);
                    732:                }
                    733:                else {
                    734:                        if (strcmp(line, "newline") == 0)
                    735:                                putc('\n', stdout);
                    736:                        else if (strncmp(line, "fname ", 6) == 0)
                    737:                                printf("%s", line + 6);
                    738:                        else {
                    739:                                /* assume text */
                    740:                                cp = strchr(line, ' ');
                    741:                                if (cp != NULL)
                    742:                                        printf("%s", cp + 1);
                    743:                        }
                    744:                }
                    745:
1.6       jfb       746:                return (0);
1.1       jfb       747:        case CVS_RESP_MBINARY:
1.5       jfb       748:                cvs_log(LP_WARN, "Mbinary not supported in client yet");
1.1       jfb       749:                break;
                    750:        }
                    751:
                    752:        fputs(line, stream);
                    753:        fputc('\n', stream);
                    754:
                    755:        return (0);
                    756: }
                    757:
                    758:
                    759: /*
                    760:  * cvs_resp_ok()
                    761:  *
                    762:  * Handler for the `ok' response.  This handler's job is to
                    763:  */
                    764:
                    765: static int
1.13      jfb       766: cvs_resp_ok(struct cvsroot *root, int type, char *line)
1.1       jfb       767: {
                    768:        return (1);
                    769: }
                    770:
                    771:
                    772: /*
                    773:  * cvs_resp_error()
                    774:  *
                    775:  * Handler for the `error' response.  This handler's job is to
                    776:  */
                    777:
                    778: static int
1.13      jfb       779: cvs_resp_error(struct cvsroot *root, int type, char *line)
1.1       jfb       780: {
                    781:        return (1);
                    782: }
                    783:
                    784:
                    785: /*
                    786:  * cvs_resp_statdir()
                    787:  *
                    788:  * Handler for the `Clear-static-directory' and `Set-static-directory'
                    789:  * responses.
                    790:  */
                    791:
                    792: static int
1.13      jfb       793: cvs_resp_statdir(struct cvsroot *root, int type, char *line)
1.1       jfb       794: {
                    795:        int fd;
1.6       jfb       796:        char rpath[MAXPATHLEN], statpath[MAXPATHLEN];
                    797:
1.13      jfb       798:        cvs_getln(root, rpath, sizeof(rpath));
1.1       jfb       799:
                    800:        snprintf(statpath, sizeof(statpath), "%s/%s", line,
                    801:            CVS_PATH_STATICENTRIES);
                    802:
                    803:        if ((type == CVS_RESP_CLRSTATDIR) &&
1.5       jfb       804:            (unlink(statpath) == -1) && (errno != ENOENT)) {
1.1       jfb       805:                cvs_log(LP_ERRNO, "failed to unlink %s file",
                    806:                    CVS_PATH_STATICENTRIES);
                    807:                return (-1);
                    808:        }
                    809:        else if (type == CVS_RESP_SETSTATDIR) {
1.5       jfb       810:                fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0400);
1.1       jfb       811:                if (fd == -1) {
                    812:                        cvs_log(LP_ERRNO, "failed to create %s file",
                    813:                            CVS_PATH_STATICENTRIES);
                    814:                        return (-1);
                    815:                }
                    816:                (void)close(fd);
1.5       jfb       817:
                    818:        }
                    819:
                    820:        return (0);
                    821: }
                    822:
                    823: /*
                    824:  * cvs_resp_sticky()
                    825:  *
                    826:  * Handler for the `Clear-sticky' and `Set-sticky' responses.
                    827:  */
                    828:
                    829: static int
1.13      jfb       830: cvs_resp_sticky(struct cvsroot *root, int type, char *line)
1.5       jfb       831: {
1.9       jfb       832:        size_t len;
1.6       jfb       833:        char rpath[MAXPATHLEN];
                    834:        struct stat st;
1.7       jfb       835:        CVSFILE *cf;
1.6       jfb       836:
1.9       jfb       837:        /* remove trailing slash */
                    838:        len = strlen(line);
                    839:        if ((len > 0) && (line[len - 1] == '/'))
                    840:                line[--len] = '\0';
                    841:
1.6       jfb       842:        /* get the remote path */
1.13      jfb       843:        cvs_getln(root, rpath, sizeof(rpath));
1.6       jfb       844:
                    845:        /* if the directory doesn't exist, create it */
                    846:        if (stat(line, &st) == -1) {
1.7       jfb       847:                /* attempt to create it */
1.6       jfb       848:                if (errno != ENOENT) {
                    849:                        cvs_log(LP_ERRNO, "failed to stat %s", line);
                    850:                }
1.7       jfb       851:                else {
                    852:                        cf = cvs_file_create(line, DT_DIR, 0755);
                    853:                        if (cf == NULL)
                    854:                                return (-1);
                    855:                        cf->cf_ddat->cd_repo = strdup(line);
1.14      jfb       856:                        cf->cf_ddat->cd_root = root;
                    857:                        root->cr_ref++;
1.7       jfb       858:                        cvs_mkadmin(cf, 0755);
                    859:
                    860:                        cvs_file_free(cf);
1.6       jfb       861:                }
                    862:        }
                    863:
1.5       jfb       864:        if (type == CVS_RESP_CLRSTICKY) {
                    865:        }
                    866:        else if (type == CVS_RESP_SETSTICKY) {
1.1       jfb       867:        }
                    868:
                    869:        return (0);
                    870: }
                    871:
                    872:
                    873: /*
                    874:  * cvs_resp_newentry()
                    875:  *
                    876:  * Handler for the `New-entry' response and `Checked-in' responses.
                    877:  */
                    878:
                    879: static int
1.13      jfb       880: cvs_resp_newentry(struct cvsroot *root, int type, char *line)
1.1       jfb       881: {
1.15      jfb       882:        char entbuf[128];
1.1       jfb       883:        CVSENTRIES *entfile;
                    884:
                    885:        /* get the remote path */
1.13      jfb       886:        cvs_getln(root, entbuf, sizeof(entbuf));
1.1       jfb       887:
                    888:        /* get the new Entries line */
1.13      jfb       889:        if (cvs_getln(root, entbuf, sizeof(entbuf)) < 0)
1.1       jfb       890:                return (-1);
                    891:
1.15      jfb       892:        entfile = cvs_ent_open(line, O_WRONLY);
1.1       jfb       893:        if (entfile == NULL)
                    894:                return (-1);
1.3       jfb       895:        cvs_ent_addln(entfile, entbuf);
1.1       jfb       896:        cvs_ent_close(entfile);
                    897:
                    898:        return (0);
                    899: }
                    900:
                    901:
                    902: /*
                    903:  * cvs_resp_cksum()
                    904:  *
                    905:  * Handler for the `Checksum' response.  We store the checksum received for
                    906:  * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>.
                    907:  * Upon next file reception, the handler checks to see if there is a stored
                    908:  * checksum.
                    909:  * The file handler must make sure that the checksums match and free the
                    910:  * checksum buffer once it's done to indicate there is no further checksum.
                    911:  */
                    912:
                    913: static int
1.13      jfb       914: cvs_resp_cksum(struct cvsroot *root, int type, char *line)
1.1       jfb       915: {
                    916:        if (cvs_fcksum != NULL) {
                    917:                cvs_log(LP_WARN, "unused checksum");
                    918:                free(cvs_fcksum);
                    919:        }
                    920:
                    921:        cvs_fcksum = strdup(line);
                    922:        if (cvs_fcksum == NULL) {
                    923:                cvs_log(LP_ERRNO, "failed to copy checksum string");
                    924:                return (-1);
                    925:        }
                    926:
1.4       jfb       927:        return (0);
                    928: }
                    929:
                    930:
                    931: /*
                    932:  * cvs_resp_modtime()
                    933:  *
                    934:  * Handler for the `Mod-time' file update modifying response.  The timestamp
                    935:  * given is used to set the last modification time on the next file that
                    936:  * will be received.
                    937:  */
                    938:
                    939: static int
1.13      jfb       940: cvs_resp_modtime(struct cvsroot *root, int type, char *line)
1.4       jfb       941: {
1.8       jfb       942:        int i;
                    943:        long off;
                    944:        char sign, mon[8], gmt[8], hr[4], min[4], *ep;
                    945:        struct tm cvs_tm;
                    946:
                    947:        memset(&cvs_tm, 0, sizeof(cvs_tm));
1.9       jfb       948:        sscanf(line, "%d %3s %d %2d:%2d:%2d %5s", &cvs_tm.tm_mday, mon,
1.8       jfb       949:            &cvs_tm.tm_year, &cvs_tm.tm_hour, &cvs_tm.tm_min,
                    950:            &cvs_tm.tm_sec, gmt);
                    951:        cvs_tm.tm_year -= 1900;
1.9       jfb       952:        cvs_tm.tm_isdst = -1;
1.8       jfb       953:
                    954:        if (*gmt == '-') {
1.9       jfb       955:                sscanf(gmt, "%c%2s%2s", &sign, hr, min);
1.8       jfb       956:                cvs_tm.tm_gmtoff = strtol(hr, &ep, 10);
                    957:                if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
                    958:                    (cvs_tm.tm_gmtoff == LONG_MAX) ||
                    959:                    (*ep != '\0')) {
                    960:                        cvs_log(LP_ERR,
                    961:                            "parse error in GMT hours specification `%s'", hr);
                    962:                        cvs_tm.tm_gmtoff = 0;
                    963:                }
                    964:                else {
                    965:                        /* get seconds */
                    966:                        cvs_tm.tm_gmtoff *= 3600;
1.7       jfb       967:
1.8       jfb       968:                        /* add the minutes */
                    969:                        off = strtol(min, &ep, 10);
                    970:                        if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
                    971:                            (cvs_tm.tm_gmtoff == LONG_MAX) ||
                    972:                            (*ep != '\0')) {
                    973:                                cvs_log(LP_ERR,
                    974:                                    "parse error in GMT minutes "
                    975:                                    "specification `%s'", min);
                    976:                        }
                    977:                        else
                    978:                                cvs_tm.tm_gmtoff += off * 60;
                    979:                }
                    980:        }
                    981:        if (sign == '-')
                    982:                cvs_tm.tm_gmtoff = -cvs_tm.tm_gmtoff;
1.7       jfb       983:
1.8       jfb       984:        for (i = 0; i < (int)(sizeof(cvs_months)/sizeof(cvs_months[0])); i++) {
                    985:                if (strcmp(cvs_months[i], mon) == 0) {
                    986:                        cvs_tm.tm_mon = i;
                    987:                        break;
                    988:                }
1.7       jfb       989:        }
                    990:
1.8       jfb       991:        cvs_modtime = mktime(&cvs_tm);
1.1       jfb       992:        return (0);
                    993: }
                    994:
                    995:
                    996: /*
                    997:  * cvs_resp_updated()
                    998:  *
1.6       jfb       999:  * Handler for the `Updated' and `Created' responses.
1.1       jfb      1000:  */
                   1001:
                   1002: static int
1.13      jfb      1003: cvs_resp_updated(struct cvsroot *root, int type, char *line)
1.1       jfb      1004: {
1.7       jfb      1005:        size_t len;
1.14      jfb      1006:        mode_t fmode;
1.7       jfb      1007:        char tbuf[32], path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN];
1.14      jfb      1008:        BUF *fbuf;
1.7       jfb      1009:        CVSENTRIES *ef;
1.6       jfb      1010:        struct cvs_ent *ep;
1.1       jfb      1011:
1.13      jfb      1012:        ep = NULL;
                   1013:
1.15      jfb      1014:        len = strlen(tbuf);
                   1015:        if ((len > 0) && (tbuf[len - 1] == '\n'))
                   1016:                tbuf[--len] = '\0';
                   1017:
                   1018:        /* read the remote path of the file */
                   1019:        cvs_getln(root, path, sizeof(path));
                   1020:
                   1021:        /* read the new entry */
                   1022:        cvs_getln(root, path, sizeof(path));
                   1023:        ep = cvs_ent_parse(path);
                   1024:        if (ep == NULL)
                   1025:                return (-1);
                   1026:        snprintf(path, sizeof(path), "%s/%s", line, ep->ce_name);
1.6       jfb      1027:
1.14      jfb      1028:
1.15      jfb      1029:        if (type == CVS_RESP_CREATED) {
1.7       jfb      1030:                /* set the timestamp as the last one received from Mod-time */
1.8       jfb      1031:                ep->ce_timestamp = ctime_r(&cvs_modtime, tbuf);
1.7       jfb      1032:
                   1033:                ef = cvs_ent_open(line, O_WRONLY);
                   1034:                if (ef == NULL)
                   1035:                        return (-1);
                   1036:
                   1037:                cvs_ent_add(ef, ep);
                   1038:                cvs_ent_close(ef);
1.1       jfb      1039:        }
                   1040:        else if (type == CVS_RESP_UPDEXIST) {
                   1041:        }
                   1042:        else if (type == CVS_RESP_UPDATED) {
                   1043:        }
                   1044:
1.14      jfb      1045:        fbuf = cvs_recvfile(root, &fmode);
                   1046:        if (fbuf == NULL)
1.1       jfb      1047:                return (-1);
1.14      jfb      1048:
                   1049:        cvs_buf_write(fbuf, path, fmode);
1.1       jfb      1050:
                   1051:        /* now see if there is a checksum */
1.6       jfb      1052:        if (cvs_fcksum != NULL) {
1.14      jfb      1053:                if (cvs_cksum(path, cksum_buf, sizeof(cksum_buf)) < 0) {
1.6       jfb      1054:                }
                   1055:
                   1056:                if (strcmp(cksum_buf, cvs_fcksum) != 0) {
                   1057:                        cvs_log(LP_ERR, "checksum error on received file");
                   1058:                        (void)unlink(line);
                   1059:                }
1.1       jfb      1060:
1.6       jfb      1061:                free(cvs_fcksum);
                   1062:                cvs_fcksum = NULL;
1.1       jfb      1063:        }
                   1064:
                   1065:        return (0);
                   1066: }
                   1067:
                   1068:
                   1069: /*
                   1070:  * cvs_resp_removed()
                   1071:  *
                   1072:  * Handler for the `Updated' response.
                   1073:  */
                   1074:
                   1075: static int
1.13      jfb      1076: cvs_resp_removed(struct cvsroot *root, int type, char *line)
1.1       jfb      1077: {
                   1078:        return (0);
                   1079: }
                   1080:
                   1081:
                   1082: /*
                   1083:  * cvs_resp_mode()
                   1084:  *
                   1085:  * Handler for the `Mode' response.
                   1086:  */
                   1087:
                   1088: static int
1.13      jfb      1089: cvs_resp_mode(struct cvsroot *root, int type, char *line)
1.1       jfb      1090: {
                   1091:        if (cvs_strtomode(line, &cvs_lastmode) < 0) {
                   1092:                return (-1);
                   1093:        }
1.5       jfb      1094:        return (0);
                   1095: }
                   1096:
                   1097:
                   1098: /*
                   1099:  * cvs_resp_modxpand()
                   1100:  *
                   1101:  * Handler for the `Module-expansion' response.
                   1102:  */
                   1103:
                   1104: static int
1.13      jfb      1105: cvs_resp_modxpand(struct cvsroot *root, int type, char *line)
1.5       jfb      1106: {
1.1       jfb      1107:        return (0);
                   1108: }
                   1109:
1.14      jfb      1110: /*
                   1111:  * cvs_resp_rcsdiff()
                   1112:  *
                   1113:  * Handler for the `Rcs-diff' response.
                   1114:  */
                   1115:
                   1116: static int
                   1117: cvs_resp_rcsdiff(struct cvsroot *root, int type, char *line)
                   1118: {
                   1119:        char file[MAXPATHLEN], buf[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN];
                   1120:        char *fname, *orig, *patch;
                   1121:        mode_t fmode;
                   1122:        BUF *res, *fcont, *patchbuf;
                   1123:        CVSENTRIES *entf;
                   1124:        struct cvs_ent *ent;
                   1125:
                   1126:        /* get remote path and build local path of file to be patched */
                   1127:        cvs_getln(root, buf, sizeof(buf));
                   1128:        fname = strrchr(buf, '/');
                   1129:        if (fname == NULL)
                   1130:                fname = buf;
                   1131:        snprintf(file, sizeof(file), "%s%s", line, fname);
                   1132:
                   1133:        /* get updated entry fields */
                   1134:        cvs_getln(root, buf, sizeof(buf));
                   1135:        ent = cvs_ent_parse(buf);
                   1136:        if (ent == NULL) {
                   1137:                return (-1);
                   1138:        }
                   1139:
                   1140:        patchbuf = cvs_recvfile(root, &fmode);
                   1141:        fcont = cvs_buf_load(file, BUF_AUTOEXT);
                   1142:        if (fcont == NULL)
                   1143:                return (-1);
                   1144:
                   1145:        cvs_buf_putc(patchbuf, '\0');
                   1146:        cvs_buf_putc(fcont, '\0');
                   1147:        orig = cvs_buf_release(fcont);
                   1148:        patch = cvs_buf_release(patchbuf);
                   1149:
                   1150:        res = rcs_patch(orig, patch);
                   1151:        if (res == NULL)
                   1152:                return (-1);
                   1153:
                   1154:        cvs_buf_write(res, file, fmode);
                   1155:
                   1156:        /* now see if there is a checksum */
                   1157:        if (cvs_fcksum != NULL) {
                   1158:                if (cvs_cksum(file, cksum_buf, sizeof(cksum_buf)) < 0) {
                   1159:                }
                   1160:
                   1161:                if (strcmp(cksum_buf, cvs_fcksum) != 0) {
                   1162:                        cvs_log(LP_ERR, "checksum error on received file");
                   1163:                        (void)unlink(file);
                   1164:                }
                   1165:
                   1166:                free(cvs_fcksum);
                   1167:                cvs_fcksum = NULL;
                   1168:        }
                   1169:
                   1170:        /* update revision in entries */
                   1171:        entf = cvs_ent_open(line, O_WRONLY);
                   1172:        if (entf == NULL)
                   1173:                return (-1);
                   1174:
                   1175:        cvs_ent_close(entf);
1.16      jfb      1176:
                   1177:        return (0);
                   1178: }
                   1179:
                   1180:
                   1181: /*
                   1182:  * cvs_resp_template()
                   1183:  *
                   1184:  * Handler for the `Template' response.
                   1185:  */
                   1186:
                   1187: static int
                   1188: cvs_resp_template(struct cvsroot *root, int type, char *line)
                   1189: {
                   1190:        mode_t mode;
                   1191:        BUF *tmpl;
                   1192:
                   1193:        tmpl = cvs_recvfile(root, &mode);
                   1194:        if (tmpl == NULL)
                   1195:                return (-1);
1.14      jfb      1196:
                   1197:        return (0);
                   1198: }
                   1199:
1.1       jfb      1200:
                   1201: /*
                   1202:  * cvs_sendfile()
                   1203:  *
                   1204:  * Send the mode and size of a file followed by the file's contents.
                   1205:  * Returns 0 on success, or -1 on failure.
                   1206:  */
                   1207:
                   1208: int
1.13      jfb      1209: cvs_sendfile(struct cvsroot *root, const char *path)
1.1       jfb      1210: {
                   1211:        int fd;
                   1212:        ssize_t ret;
                   1213:        char buf[4096];
                   1214:        struct stat st;
                   1215:
                   1216:        if (stat(path, &st) == -1) {
                   1217:                cvs_log(LP_ERRNO, "failed to stat `%s'", path);
                   1218:                return (-1);
                   1219:        }
                   1220:
                   1221:        fd = open(path, O_RDONLY, 0);
                   1222:        if (fd == -1) {
                   1223:                return (-1);
                   1224:        }
                   1225:
                   1226:        if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
                   1227:                return (-1);
                   1228:
1.13      jfb      1229:        cvs_sendln(root, buf);
1.1       jfb      1230:        snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
1.13      jfb      1231:        cvs_sendln(root, buf);
1.1       jfb      1232:
                   1233:        while ((ret = read(fd, buf, sizeof(buf))) != 0) {
                   1234:                if (ret == -1) {
                   1235:                        cvs_log(LP_ERRNO, "failed to read file `%s'", path);
                   1236:                        return (-1);
                   1237:                }
                   1238:
1.13      jfb      1239:                cvs_sendraw(root, buf, (size_t)ret);
1.1       jfb      1240:
                   1241:        }
                   1242:
                   1243:        (void)close(fd);
                   1244:
                   1245:        return (0);
                   1246: }
                   1247:
                   1248:
                   1249: /*
                   1250:  * cvs_recvfile()
                   1251:  *
                   1252:  * Receive the mode and size of a file followed the file's contents and
                   1253:  * create or update the file whose path is <path> with the received
                   1254:  * information.
                   1255:  */
                   1256:
1.14      jfb      1257: BUF*
                   1258: cvs_recvfile(struct cvsroot *root, mode_t *mode)
1.1       jfb      1259: {
                   1260:        size_t len;
                   1261:        ssize_t ret;
                   1262:        off_t fsz, cnt;
                   1263:        char buf[4096], *ep;
1.14      jfb      1264:        BUF *fbuf;
                   1265:
                   1266:        fbuf = cvs_buf_alloc(sizeof(buf), BUF_AUTOEXT);
                   1267:        if (fbuf == NULL)
                   1268:                return (NULL);
1.1       jfb      1269:
1.13      jfb      1270:        if ((cvs_getln(root, buf, sizeof(buf)) < 0) ||
1.14      jfb      1271:            (cvs_strtomode(buf, mode) < 0)) {
                   1272:                return (NULL);
1.1       jfb      1273:        }
                   1274:
1.13      jfb      1275:        cvs_getln(root, buf, sizeof(buf));
1.1       jfb      1276:
                   1277:        fsz = (off_t)strtol(buf, &ep, 10);
                   1278:        if (*ep != '\0') {
                   1279:                cvs_log(LP_ERR, "parse error in file size transmission");
1.14      jfb      1280:                return (NULL);
1.1       jfb      1281:        }
                   1282:
                   1283:        cnt = 0;
                   1284:        do {
                   1285:                len = MIN(sizeof(buf), (size_t)(fsz - cnt));
1.6       jfb      1286:                if (len == 0)
                   1287:                        break;
1.13      jfb      1288:                ret = cvs_recvraw(root, buf, len);
1.1       jfb      1289:                if (ret == -1) {
1.14      jfb      1290:                        cvs_buf_free(fbuf);
                   1291:                        return (NULL);
1.1       jfb      1292:                }
                   1293:
1.14      jfb      1294:                if (cvs_buf_append(fbuf, buf, (size_t)ret) == -1) {
                   1295:                        cvs_log(LP_ERR,
                   1296:                            "failed to append received file data");
                   1297:                        cvs_buf_free(fbuf);
                   1298:                        return (NULL);
1.1       jfb      1299:                }
                   1300:
                   1301:                cnt += (off_t)ret;
                   1302:        } while (cnt < fsz);
                   1303:
1.14      jfb      1304:        return (fbuf);
1.1       jfb      1305: }
1.12      jfb      1306:
                   1307:
                   1308: /*
                   1309:  * cvs_sendreq()
                   1310:  *
                   1311:  * Send a request to the server of type <rid>, with optional arguments
                   1312:  * contained in <arg>, which should not be terminated by a newline.
                   1313:  * Returns 0 on success, or -1 on failure.
                   1314:  */
                   1315:
                   1316: int
1.13      jfb      1317: cvs_sendreq(struct cvsroot *root, u_int rid, const char *arg)
1.12      jfb      1318: {
                   1319:        int ret;
1.13      jfb      1320:        struct cvs_req *req;
1.12      jfb      1321:
                   1322:        if (root->cr_srvin == NULL) {
                   1323:                cvs_log(LP_ERR, "cannot send request: Not connected");
                   1324:                return (-1);
                   1325:        }
                   1326:
1.13      jfb      1327:        req = cvs_req_getbyid(rid);
                   1328:        if (req == NULL) {
1.12      jfb      1329:                cvs_log(LP_ERR, "unsupported request type %u", rid);
                   1330:                return (-1);
                   1331:        }
                   1332:
1.13      jfb      1333:        snprintf(cvs_proto_buf, sizeof(cvs_proto_buf), "%s%s%s\n",
                   1334:            req->req_str, (arg == NULL) ? "" : " ", (arg == NULL) ? "" : arg);
1.12      jfb      1335:
                   1336:        if (cvs_server_inlog != NULL)
1.13      jfb      1337:                fputs(cvs_proto_buf, cvs_server_inlog);
1.12      jfb      1338:
1.13      jfb      1339:        ret = fputs(cvs_proto_buf, root->cr_srvin);
1.12      jfb      1340:        if (ret == EOF) {
                   1341:                cvs_log(LP_ERRNO, "failed to send request to server");
                   1342:                return (-1);
                   1343:        }
1.17    ! jfb      1344:
        !          1345:        if (rid == CVS_REQ_VERSION)
        !          1346:                cvs_version_sent = 1;
1.12      jfb      1347:
1.13      jfb      1348:        if (req->req_flags & CVS_REQF_RESP)
1.12      jfb      1349:                ret = cvs_getresp(root);
                   1350:
1.13      jfb      1351:        return (ret);
1.12      jfb      1352: }
                   1353:
                   1354:
                   1355: /*
                   1356:  * cvs_getresp()
                   1357:  *
                   1358:  * Get a response from the server.  This call will actually read and handle
                   1359:  * responses from the server until one of the response handlers returns
                   1360:  * non-zero (either an error occured or the end of the response was reached).
                   1361:  * Returns the number of handled commands on success, or -1 on failure.
                   1362:  */
                   1363:
                   1364: int
1.13      jfb      1365: cvs_getresp(struct cvsroot *root)
1.12      jfb      1366: {
1.13      jfb      1367:        int nbcmd, ret;
                   1368:        size_t len;
1.12      jfb      1369:
                   1370:        nbcmd = 0;
                   1371:
                   1372:        do {
                   1373:                /* wait for incoming data */
1.13      jfb      1374:                if (fgets(cvs_proto_buf, sizeof(cvs_proto_buf),
1.12      jfb      1375:                    root->cr_srvout) == NULL) {
                   1376:                        if (feof(root->cr_srvout))
                   1377:                                return (0);
                   1378:                        cvs_log(LP_ERRNO,
                   1379:                            "failed to read response from server");
                   1380:                        return (-1);
                   1381:                }
                   1382:
                   1383:                if (cvs_server_outlog != NULL)
1.13      jfb      1384:                        fputs(cvs_proto_buf, cvs_server_outlog);
1.12      jfb      1385:
1.13      jfb      1386:                if ((len = strlen(cvs_proto_buf)) != 0) {
                   1387:                        if (cvs_proto_buf[len - 1] != '\n') {
1.12      jfb      1388:                                /* truncated line */
                   1389:                        }
                   1390:                        else
1.13      jfb      1391:                                cvs_proto_buf[--len] = '\0';
1.12      jfb      1392:                }
                   1393:
1.13      jfb      1394:                ret = cvs_resp_handle(root, cvs_proto_buf);
1.12      jfb      1395:                nbcmd++;
                   1396:        } while (ret == 0);
                   1397:
                   1398:        if (ret > 0)
                   1399:                ret = nbcmd;
                   1400:        return (ret);
                   1401: }
                   1402:
                   1403:
                   1404: /*
1.13      jfb      1405:  * cvs_getln()
                   1406:  *
                   1407:  * Get a line from the server's output and store it in <lbuf>.  The terminating
                   1408:  * newline character is stripped from the result.
                   1409:  */
                   1410:
                   1411: int
                   1412: cvs_getln(struct cvsroot *root, char *lbuf, size_t len)
                   1413: {
                   1414:        size_t rlen;
                   1415:
                   1416:        if (fgets(lbuf, len, root->cr_srvout) == NULL) {
                   1417:                if (ferror(root->cr_srvout)) {
                   1418:                        cvs_log(LP_ERRNO, "failed to read line from server");
                   1419:                        return (-1);
                   1420:                }
                   1421:
                   1422:                if (feof(root->cr_srvout))
                   1423:                        *lbuf = '\0';
                   1424:        }
                   1425:
                   1426:        if (cvs_server_outlog != NULL)
                   1427:                fputs(lbuf, cvs_server_outlog);
                   1428:
                   1429:        rlen = strlen(lbuf);
                   1430:        if ((rlen > 0) && (lbuf[rlen - 1] == '\n'))
                   1431:                lbuf[--rlen] = '\0';
                   1432:
                   1433:        return (0);
                   1434: }
                   1435:
                   1436: #ifdef notyet
                   1437: /*
1.12      jfb      1438:  * cvs_sendresp()
                   1439:  *
                   1440:  * Send a response to the client of type <rid>, with optional arguments
                   1441:  * contained in <arg>, which should not be terminated by a newline.
                   1442:  * Returns 0 on success, or -1 on failure.
                   1443:  */
                   1444:
                   1445: int
                   1446: cvs_sendresp(u_int rid, const char *arg)
                   1447: {
                   1448:        int ret;
                   1449:        size_t len;
                   1450:        const char *resp;
                   1451:
                   1452:        resp = cvs_resp_getbyid(rid);
1.13      jfb      1453:        if (resp == NULL) {
1.12      jfb      1454:                cvs_log(LP_ERR, "unsupported response type %u", rid);
                   1455:                return (-1);
                   1456:        }
                   1457:
1.13      jfb      1458:        snprintf(cvs_proto_buf, sizeof(cvs_proto_buf), "%s %s\n", resp,
1.12      jfb      1459:            (arg == NULL) ? "" : arg);
                   1460:
                   1461:        ret = fputs(resp, stdout);
                   1462:        if (ret == EOF) {
                   1463:                cvs_log(LP_ERRNO, "failed to send response to client");
                   1464:        }
                   1465:        else {
                   1466:                if (arg != NULL)
                   1467:                        ret = fprintf(stdout, " %s", arg);
                   1468:                putc('\n', stdout);
                   1469:        }
                   1470:        return (0);
                   1471: }
                   1472:
                   1473:
                   1474: /*
                   1475:  * cvs_getreq()
                   1476:  *
                   1477:  * Get a request from the client.
                   1478:  */
                   1479:
                   1480: int
                   1481: cvs_getreq(void)
                   1482: {
                   1483:        int nbcmd;
                   1484:
                   1485:        nbcmd = 0;
                   1486:
                   1487:        do {
                   1488:                /* wait for incoming data */
1.13      jfb      1489:                if (fgets(cvs_proto_buf, sizeof(cvs_proto_buf),
1.12      jfb      1490:                    stdin) == NULL) {
                   1491:                        if (feof(stdin))
                   1492:                                return (0);
                   1493:                        cvs_log(LP_ERRNO,
                   1494:                            "failed to read request from client");
                   1495:                        return (-1);
                   1496:                }
                   1497:
1.13      jfb      1498:                if ((len = strlen(cvs_proto_buf)) != 0) {
                   1499:                        if (cvs_proto_buf[len - 1] != '\n') {
1.12      jfb      1500:                                /* truncated line */
                   1501:                        }
                   1502:                        else
1.13      jfb      1503:                                cvs_proto_buf[--len] = '\0';
1.12      jfb      1504:                }
                   1505:
1.13      jfb      1506:                ret = cvs_resp_handle(cvs_proto_buf);
1.12      jfb      1507:        } while (ret == 0);
                   1508: }
                   1509: #endif
                   1510:
                   1511:
                   1512: /*
                   1513:  * cvs_sendln()
                   1514:  *
                   1515:  * Send a single line <line> string to the server.  The line is sent as is,
                   1516:  * without any modifications.
                   1517:  * Returns 0 on success, or -1 on failure.
                   1518:  */
                   1519:
                   1520: int
                   1521: cvs_sendln(struct cvsroot *root, const char *line)
                   1522: {
                   1523:        int nl;
                   1524:        size_t len;
                   1525:
                   1526:        nl = 0;
                   1527:        len = strlen(line);
                   1528:
                   1529:        if ((len > 0) && (line[len - 1] != '\n'))
                   1530:                nl = 1;
                   1531:
                   1532:        if (cvs_server_inlog != NULL) {
                   1533:                fputs(line, cvs_server_inlog);
                   1534:                if (nl)
                   1535:                        fputc('\n', cvs_server_inlog);
                   1536:        }
                   1537:        fputs(line, root->cr_srvin);
                   1538:        if (nl)
                   1539:                fputc('\n', root->cr_srvin);
                   1540:
                   1541:        return (0);
                   1542: }
                   1543:
                   1544:
                   1545: /*
1.13      jfb      1546:  * cvs_sendraw()
1.12      jfb      1547:  *
                   1548:  * Send the first <len> bytes from the buffer <src> to the server.
                   1549:  */
                   1550:
                   1551: int
1.13      jfb      1552: cvs_sendraw(struct cvsroot *root, const void *src, size_t len)
1.12      jfb      1553: {
                   1554:        if (cvs_server_inlog != NULL)
                   1555:                fwrite(src, sizeof(char), len, cvs_server_inlog);
1.13      jfb      1556:        if (fwrite(src, sizeof(char), len, root->cr_srvin) < len) {
1.12      jfb      1557:                return (-1);
                   1558:        }
                   1559:
                   1560:        return (0);
                   1561: }
                   1562:
                   1563:
                   1564: /*
1.13      jfb      1565:  * cvs_recvraw()
1.12      jfb      1566:  *
                   1567:  * Receive the first <len> bytes from the buffer <src> to the server.
                   1568:  */
                   1569:
                   1570: ssize_t
1.13      jfb      1571: cvs_recvraw(struct cvsroot *root, void *dst, size_t len)
1.12      jfb      1572: {
                   1573:        size_t ret;
                   1574:
1.13      jfb      1575:        ret = fread(dst, sizeof(char), len, root->cr_srvout);
1.12      jfb      1576:        if (ret == 0)
                   1577:                return (-1);
                   1578:        if (cvs_server_outlog != NULL)
                   1579:                fwrite(dst, sizeof(char), len, cvs_server_outlog);
                   1580:        return (ssize_t)ret;
                   1581: }
                   1582:
                   1583:
                   1584: /*
1.13      jfb      1585:  * cvs_senddir()
1.12      jfb      1586:  *
                   1587:  * Send a `Directory' request along with the 2 paths that follow it.
                   1588:  */
                   1589:
                   1590: int
1.13      jfb      1591: cvs_senddir(struct cvsroot *root, CVSFILE *dir)
1.12      jfb      1592: {
1.13      jfb      1593:        char buf[MAXPATHLEN];
1.12      jfb      1594:
1.13      jfb      1595:        if (dir->cf_ddat->cd_repo == NULL)
                   1596:                strlcpy(buf, root->cr_dir, sizeof(buf));
                   1597:        else
                   1598:                snprintf(buf, sizeof(buf), "%s/%s", root->cr_dir,
                   1599:                    dir->cf_ddat->cd_repo);
1.12      jfb      1600:
1.13      jfb      1601:        if ((cvs_sendreq(root, CVS_REQ_DIRECTORY, dir->cf_path) < 0) ||
                   1602:            (cvs_sendln(root, buf) < 0))
1.12      jfb      1603:                return (-1);
                   1604:
                   1605:        return (0);
                   1606: }
                   1607:
                   1608:
                   1609: /*
1.13      jfb      1610:  * cvs_sendarg()
1.12      jfb      1611:  *
                   1612:  * Send the argument <arg> to the server.  The argument <append> is used to
                   1613:  * determine if the argument should be simply appended to the last argument
                   1614:  * sent or if it should be created as a new argument (0).
                   1615:  */
                   1616:
                   1617: int
1.13      jfb      1618: cvs_sendarg(struct cvsroot *root, const char *arg, int append)
1.12      jfb      1619: {
1.13      jfb      1620:        return cvs_sendreq(root, ((append == 0) ?
                   1621:            CVS_REQ_ARGUMENT : CVS_REQ_ARGUMENTX), arg);
1.12      jfb      1622: }
                   1623:
                   1624:
                   1625: /*
1.13      jfb      1626:  * cvs_sendentry()
1.12      jfb      1627:  *
                   1628:  * Send an `Entry' request to the server along with the mandatory fields from
                   1629:  * the CVS entry <ent> (which are the name and revision).
                   1630:  */
                   1631:
                   1632: int
1.13      jfb      1633: cvs_sendentry(struct cvsroot *root, const struct cvs_ent *ent)
1.12      jfb      1634: {
                   1635:        char ebuf[128], numbuf[64];
                   1636:
                   1637:        snprintf(ebuf, sizeof(ebuf), "/%s/%s///", ent->ce_name,
                   1638:            rcsnum_tostr(ent->ce_rev, numbuf, sizeof(numbuf)));
                   1639:
1.13      jfb      1640:        return cvs_sendreq(root, CVS_REQ_ENTRY, ebuf);
1.12      jfb      1641: }
                   1642:
                   1643:
                   1644: /*
1.13      jfb      1645:  * cvs_initlog()
1.12      jfb      1646:  *
                   1647:  * Initialize protocol logging if the CVS_CLIENT_LOG environment variable is
                   1648:  * set.  In this case, the variable's value is used as a path to which the
                   1649:  * appropriate suffix is added (".in" for server input and ".out" for server
                   1650:  * output.
                   1651:  * Returns 0 on success, or -1 on failure.
                   1652:  */
                   1653:
                   1654: static int
1.13      jfb      1655: cvs_initlog(void)
1.12      jfb      1656: {
                   1657:        char *env, fpath[MAXPATHLEN];
                   1658:
1.13      jfb      1659:        /* avoid doing it more than once */
                   1660:        if (cvs_server_logon)
                   1661:                return (0);
                   1662:
1.12      jfb      1663:        env = getenv("CVS_CLIENT_LOG");
                   1664:        if (env == NULL)
                   1665:                return (0);
                   1666:
                   1667:        strlcpy(fpath, env, sizeof(fpath));
                   1668:        strlcat(fpath, ".in", sizeof(fpath));
                   1669:        cvs_server_inlog = fopen(fpath, "w");
                   1670:        if (cvs_server_inlog == NULL) {
                   1671:                cvs_log(LP_ERRNO, "failed to open server input log `%s'",
                   1672:                    fpath);
                   1673:                return (-1);
                   1674:        }
                   1675:
                   1676:        strlcpy(fpath, env, sizeof(fpath));
                   1677:        strlcat(fpath, ".out", sizeof(fpath));
                   1678:        cvs_server_outlog = fopen(fpath, "w");
                   1679:        if (cvs_server_outlog == NULL) {
                   1680:                cvs_log(LP_ERRNO, "failed to open server output log `%s'",
                   1681:                    fpath);
                   1682:                return (-1);
                   1683:        }
                   1684:
                   1685:        /* make the streams line-buffered */
                   1686:        setvbuf(cvs_server_inlog, NULL, _IOLBF, 0);
                   1687:        setvbuf(cvs_server_outlog, NULL, _IOLBF, 0);
                   1688:
1.13      jfb      1689:        cvs_server_logon = 1;
1.12      jfb      1690:
                   1691:        return (0);
                   1692: }