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

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