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

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