[BACK]Return to req.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / cvs

Annotation of src/usr.bin/cvs/req.c, Revision 1.44

1.44    ! deraadt     1: /*     $OpenBSD: req.c,v 1.43 2006/03/28 07:17:17 ray Exp $    */
1.1       jfb         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:
1.40      xsa        27: #include "includes.h"
1.1       jfb        28:
                     29: #include "buf.h"
                     30: #include "cvs.h"
                     31: #include "log.h"
                     32: #include "proto.h"
                     33:
                     34:
1.22      joris      35: extern char *cvs_rootstr;
1.1       jfb        36: extern int   cvs_compress;
                     37: extern char *cvs_rsh;
                     38: extern int   cvs_trace;
                     39: extern int   cvs_nolog;
                     40: extern int   cvs_readonly;
                     41:
                     42:
1.29      xsa        43: static int     cvs_req_set(int, char *);
                     44: static int     cvs_req_noop(int, char *);
                     45: static int     cvs_req_root(int, char *);
                     46: static int     cvs_req_validreq(int, char *);
                     47: static int     cvs_req_validresp(int, char *);
                     48: static int     cvs_req_expandmod(int, char *);
                     49: static int     cvs_req_directory(int, char *);
                     50: static int     cvs_req_useunchanged(int, char *);
                     51: static int     cvs_req_case(int, char *);
                     52: static int     cvs_req_argument(int, char *);
                     53: static int     cvs_req_globalopt(int, char *);
                     54: static int     cvs_req_gzipstream(int, char *);
                     55: static int     cvs_req_entry(int, char *);
                     56: static int     cvs_req_filestate(int, char *);
1.11      jfb        57:
1.29      xsa        58: static int     cvs_req_command(int, char *);
1.1       jfb        59:
                     60:
                     61: struct cvs_reqhdlr {
1.32      reyk       62:        int     (*hdlr)(int, char *);
1.1       jfb        63: } cvs_req_swtab[CVS_REQ_MAX + 1] = {
1.11      jfb        64:        { NULL                  },
                     65:        { cvs_req_root          },
                     66:        { cvs_req_validreq      },
                     67:        { cvs_req_validresp     },
                     68:        { cvs_req_directory     },
                     69:        { NULL                  },
                     70:        { NULL                  },
                     71:        { NULL                  },
1.13      jfb        72:        { cvs_req_entry         },
1.11      jfb        73:        { NULL                  },
                     74:        { NULL                  },      /* 10 */
1.13      jfb        75:        { cvs_req_filestate     },
                     76:        { cvs_req_filestate     },
                     77:        { cvs_req_filestate     },
1.11      jfb        78:        { cvs_req_useunchanged  },
                     79:        { NULL                  },
                     80:        { NULL                  },
1.13      jfb        81:        { cvs_req_filestate     },
1.11      jfb        82:        { cvs_req_case          },
                     83:        { NULL                  },
                     84:        { cvs_req_argument      },      /* 20 */
                     85:        { cvs_req_argument      },
                     86:        { cvs_req_globalopt     },
                     87:        { cvs_req_gzipstream    },
                     88:        { NULL                  },
                     89:        { NULL                  },
                     90:        { NULL                  },
                     91:        { NULL                  },
                     92:        { NULL                  },
                     93:        { NULL                  },
                     94:        { NULL                  },      /* 30 */
                     95:        { NULL                  },
                     96:        { NULL                  },
                     97:        { NULL                  },
                     98:        { cvs_req_set           },
1.12      jfb        99:        { cvs_req_expandmod     },
1.11      jfb       100:        { cvs_req_command       },
                    101:        { NULL                  },
                    102:        { NULL                  },
                    103:        { NULL                  },
                    104:        { NULL                  },      /* 40 */
                    105:        { NULL                  },
                    106:        { NULL                  },
                    107:        { NULL                  },
                    108:        { NULL                  },
1.17      joris     109:        { cvs_req_command       },
1.11      jfb       110:        { cvs_req_command       },
                    111:        { NULL                  },
                    112:        { cvs_req_command       },
1.22      joris     113:        { cvs_req_command       },
1.27      xsa       114:        { cvs_req_command       },      /* 50 */
                    115:        { cvs_req_command       },
                    116:        { cvs_req_command       },
                    117:        { cvs_req_command       },
                    118:        { cvs_req_command       },
                    119:        { cvs_req_command       },
1.11      jfb       120:        { cvs_req_command       },
                    121:        { cvs_req_command       },
                    122:        { cvs_req_command       },
1.25      xsa       123:        { cvs_req_command       },
1.11      jfb       124:        { cvs_req_command       },      /* 60 */
                    125:        { NULL                  },
                    126:        { cvs_req_command       },
                    127:        { cvs_req_command       },
1.12      jfb       128:        { cvs_req_noop          },
1.27      xsa       129:        { cvs_req_command       },
                    130:        { cvs_req_command       },
                    131:        { cvs_req_command       },
                    132:        { cvs_req_command       },
1.11      jfb       133:        { cvs_req_command       },
1.1       jfb       134: };
                    135:
                    136:
                    137:
                    138: /*
1.2       jfb       139:  * Argument array built by `Argument' and `Argumentx' requests.
                    140:  */
1.22      joris     141: static char *cvs_req_args[CVS_PROTO_MAXARG];
                    142:
                    143: /* start at 1, because 0 will be the command name */
                    144: static int  cvs_req_nargs = 1;
1.2       jfb       145:
1.22      joris     146: static char *cvs_req_modulename;
1.11      jfb       147: static char *cvs_req_rootpath;
1.14      joris     148: static char *cvs_req_currentdir;
                    149: extern char cvs_server_tmpdir[MAXPATHLEN];
1.22      joris     150: static CVSENTRIES *cvs_req_entf;
1.2       jfb       151:
                    152: /*
1.1       jfb       153:  * cvs_req_handle()
                    154:  *
                    155:  * Generic request handler dispatcher.  The handler expects the first line
                    156:  * of the command as single argument.
                    157:  * Returns the return value of the command on success, or -1 on failure.
                    158:  */
                    159: int
                    160: cvs_req_handle(char *line)
                    161: {
                    162:        char *cp, *cmd;
                    163:        struct cvs_req *req;
                    164:
                    165:        cmd = line;
                    166:
                    167:        cp = strchr(cmd, ' ');
                    168:        if (cp != NULL)
                    169:                *(cp++) = '\0';
                    170:
1.36      xsa       171:        if ((req = cvs_req_getbyname(cmd)) == NULL)
                    172:                fatal("cvs_req_handle: cvs_req_getbyname failed");
                    173:        else if (cvs_req_swtab[req->req_id].hdlr == NULL)
                    174:                fatal("handler for `%s' not implemented", cmd);
1.1       jfb       175:
                    176:        return (*cvs_req_swtab[req->req_id].hdlr)(req->req_id, cp);
                    177: }
                    178:
1.12      jfb       179: /*
                    180:  * cvs_req_noop()
                    181:  */
                    182: static int
                    183: cvs_req_noop(int reqid, char *line)
                    184: {
1.41      xsa       185:        cvs_sendresp(CVS_RESP_OK, NULL);
1.12      jfb       186:        return (0);
                    187: }
                    188:
1.1       jfb       189:
                    190: static int
                    191: cvs_req_root(int reqid, char *line)
                    192: {
1.11      jfb       193:        if (cvs_req_rootpath != NULL) {
                    194:                cvs_log(LP_ERR, "duplicate Root request received");
1.13      jfb       195:                cvs_printf("Protocol error: Duplicate Root request");
1.11      jfb       196:                return (-1);
                    197:        }
1.9       jfb       198:
1.34      joris     199:        cvs_req_rootpath = xstrdup(line);
1.22      joris     200:        cvs_rootstr = cvs_req_rootpath;
                    201:
1.4       jfb       202:        return (0);
                    203: }
                    204:
                    205:
                    206: static int
                    207: cvs_req_validreq(int reqid, char *line)
                    208: {
                    209:        char *vreq;
                    210:
                    211:        vreq = cvs_req_getvalid();
                    212:        if (vreq == NULL)
                    213:                return (-1);
                    214:
1.41      xsa       215:        cvs_sendresp(CVS_RESP_VALIDREQ, vreq);
                    216:        cvs_sendresp(CVS_RESP_OK, NULL);
1.4       jfb       217:
                    218:        return (0);
                    219: }
                    220:
                    221: static int
                    222: cvs_req_validresp(int reqid, char *line)
                    223: {
                    224:        char *sp, *ep;
                    225:        struct cvs_resp *resp;
1.1       jfb       226:
1.4       jfb       227:        sp = line;
                    228:        do {
                    229:                ep = strchr(sp, ' ');
                    230:                if (ep != NULL)
                    231:                        *(ep++) = '\0';
                    232:
                    233:                resp = cvs_resp_getbyname(sp);
                    234:                if (resp != NULL)
                    235:                        ;
                    236:
                    237:                if (ep != NULL)
                    238:                        sp = ep + 1;
                    239:        } while (ep != NULL);
1.1       jfb       240:
                    241:        return (0);
                    242: }
                    243:
                    244: static int
                    245: cvs_req_directory(int reqid, char *line)
                    246: {
1.22      joris     247:        int pwd;
                    248:        size_t dirlen;
1.11      jfb       249:        char rdir[MAXPATHLEN];
1.22      joris     250:        char *repo, *s, *p;
                    251:
                    252:        pwd = (!strcmp(line, "."));
1.1       jfb       253:
1.39      joris     254:        cvs_getln(NULL, rdir, sizeof(rdir));
1.33      joris     255:
                    256:        STRIP_SLASH(rdir);
1.11      jfb       257:
1.14      joris     258:        if (cvs_req_currentdir != NULL)
1.34      joris     259:                xfree(cvs_req_currentdir);
1.14      joris     260:
1.34      joris     261:        cvs_req_currentdir = xstrdup(rdir);
1.14      joris     262:
1.22      joris     263:        dirlen = strlen(cvs_req_currentdir);
1.14      joris     264:
1.22      joris     265:        /*
                    266:         * Lets make sure we always start at the correct
                    267:         * directory.
                    268:         */
1.38      xsa       269:        cvs_chdir(cvs_server_tmpdir, 1);
1.14      joris     270:
1.22      joris     271:        /*
                    272:         * Set repository path.
                    273:         */
1.30      joris     274:        if (strlen(cvs_req_rootpath) < dirlen) {
                    275:                s = cvs_req_currentdir + strlen(cvs_req_rootpath) + 1;
1.36      xsa       276:                if (s >= (cvs_req_currentdir + dirlen))
                    277:                        fatal("you're bad, go away");
1.30      joris     278:        } else
                    279:                s = cvs_req_currentdir;
1.14      joris     280:
1.36      xsa       281:        repo = xstrdup(s);
1.22      joris     282:
                    283:        /*
                    284:         * Skip back "foo/bar" part, so we can feed the repo
                    285:         * as a startpoint for cvs_create_dir().
                    286:         */
                    287:        if (!pwd) {
                    288:                s = repo + strlen(repo) - strlen(line) - 1;
1.36      xsa       289:                if (*s != '/')
                    290:                        fatal("cvs_req_directory: malformed directory");
1.22      joris     291:
                    292:                *s = '\0';
                    293:        }
                    294:
                    295:        /*
                    296:         * Obtain the modulename, we only need to do this at
                    297:         * the very first time we get a Directory request.
                    298:         */
                    299:        if (cvs_req_modulename == NULL) {
                    300:                if ((p = strchr(repo, '/')) != NULL)
                    301:                        *p = '\0';
                    302:
1.36      xsa       303:                cvs_req_modulename = xstrdup(repo);
1.22      joris     304:
                    305:                if (p != NULL)
                    306:                        *p = '/';
                    307:
                    308:                /*
                    309:                 * Now, create the admin files in the top-level
                    310:                 * directory for the temp repo.
                    311:                 */
1.42      xsa       312:                cvs_mkadmin(cvs_server_tmpdir, cvs_rootstr, repo, NULL,
                    313:                    NULL, 0);
1.22      joris     314:        }
                    315:
                    316:        /*
                    317:         * create the directory plus the administrative files.
                    318:         */
                    319:        if (cvs_create_dir(line, 1, cvs_rootstr, repo) < 0) {
1.34      joris     320:                xfree(repo);
1.22      joris     321:                return (-1);
                    322:        }
1.14      joris     323:
1.22      joris     324:        /*
                    325:         * cvs_create_dir() has already put us in the correct directory
                    326:         * so now open it's Entry file for incoming files.
                    327:         */
                    328:        if (cvs_req_entf != NULL)
                    329:                cvs_ent_close(cvs_req_entf);
                    330:        cvs_req_entf = cvs_ent_open(".", O_RDWR);
1.36      xsa       331:        if (cvs_req_entf == NULL)
                    332:                fatal("failed to open Entry file for %s", line);
1.22      joris     333:
1.34      joris     334:        xfree(repo);
1.11      jfb       335:        return (0);
                    336: }
                    337:
1.13      jfb       338: static int
                    339: cvs_req_entry(int reqid, char *line)
                    340: {
                    341:        struct cvs_ent *ent;
                    342:
1.22      joris     343:        /* parse received entry */
1.13      jfb       344:        if ((ent = cvs_ent_parse(line)) == NULL)
                    345:                return (-1);
                    346:
1.22      joris     347:        /* add it to the entry file and done */
1.36      xsa       348:        if (cvs_ent_add(cvs_req_entf, ent) < 0)
                    349:                fatal("cvs_req_entry: cvs_ent_add: `%s'", ent->ce_name);
1.22      joris     350:
                    351:        /* XXX */
                    352:        cvs_ent_write(cvs_req_entf);
1.15      jfb       353:
1.13      jfb       354:        return (0);
                    355: }
                    356:
                    357: /*
                    358:  * cvs_req_filestate()
                    359:  *
                    360:  * Handler for the `Modified', `Is-Modified', `Unchanged' and `Questionable'
                    361:  * requests, which are all used to report the assumed state of a file from the
                    362:  * client.
                    363:  */
                    364: static int
                    365: cvs_req_filestate(int reqid, char *line)
                    366: {
1.22      joris     367:        int ret;
1.13      jfb       368:        mode_t fmode;
                    369:        BUF *fdata;
1.22      joris     370:        struct cvs_ent *ent;
1.13      jfb       371:
1.22      joris     372:        ret = 0;
                    373:        switch (reqid) {
                    374:        case CVS_REQ_MODIFIED:
1.13      jfb       375:                fdata = cvs_recvfile(NULL, &fmode);
                    376:                if (fdata == NULL)
                    377:                        return (-1);
1.14      joris     378:
                    379:                /* write the file */
1.35      xsa       380:                cvs_buf_write(fdata, line, fmode);
1.14      joris     381:                cvs_buf_free(fdata);
1.22      joris     382:                break;
                    383:        case CVS_REQ_ISMODIFIED:
                    384:                break;
                    385:        case CVS_REQ_UNCHANGED:
                    386:                ent = cvs_ent_get(cvs_req_entf, line);
                    387:                if (ent == NULL) {
                    388:                        cvs_log(LP_ERR,
1.32      reyk      389:                            "received Unchanged request "
                    390:                            "for a non-existing file");
1.22      joris     391:                        ret = -1;
                    392:                } else {
                    393:                        ent->ce_status = CVS_ENT_UPTODATE;
                    394:                }
                    395:                break;
                    396:        case CVS_REQ_QUESTIONABLE:
1.26      joris     397:                cvs_printf("? %s\n", line);
1.22      joris     398:                break;
                    399:        default:
                    400:                cvs_log(LP_ERR, "wrong request id type");
                    401:                ret = -1;
                    402:                break;
1.13      jfb       403:        }
                    404:
1.22      joris     405:        /* XXX */
                    406:        cvs_req_entf->cef_flags &= ~CVS_ENTF_SYNC;
                    407:        cvs_ent_write(cvs_req_entf);
                    408:
                    409:        return (ret);
1.13      jfb       410: }
1.12      jfb       411:
                    412: /*
                    413:  * cvs_req_expandmod()
                    414:  *
                    415:  */
                    416: static int
                    417: cvs_req_expandmod(int reqid, char *line)
                    418: {
1.41      xsa       419:        cvs_sendresp(CVS_RESP_OK, NULL);
1.12      jfb       420:        return (0);
                    421: }
                    422:
                    423:
1.11      jfb       424: /*
                    425:  * cvs_req_useunchanged()
                    426:  *
                    427:  * Handler for the `UseUnchanged' requests.  The protocol documentation
                    428:  * specifies that this request must be supported by the server and must be
                    429:  * sent by the client, though it gives no clue regarding its use.
                    430:  */
                    431: static int
                    432: cvs_req_useunchanged(int reqid, char *line)
                    433: {
1.5       jfb       434:        return (0);
                    435: }
                    436:
1.11      jfb       437:
1.5       jfb       438: /*
                    439:  * cvs_req_case()
                    440:  *
                    441:  * Handler for the `Case' requests, which toggles case sensitivity ON or OFF
                    442:  */
                    443: static int
                    444: cvs_req_case(int reqid, char *line)
                    445: {
                    446:        cvs_nocase = 1;
1.2       jfb       447:        return (0);
                    448: }
                    449:
                    450:
                    451: static int
1.11      jfb       452: cvs_req_set(int reqid, char *line)
                    453: {
                    454:        char *cp, *lp;
                    455:
1.36      xsa       456:        lp = xstrdup(line);
                    457:        if ((cp = strchr(lp, '=')) == NULL)
                    458:                fatal("error in Set request (no = in variable assignment)");
1.11      jfb       459:
                    460:        *(cp++) = '\0';
                    461:
                    462:        if (cvs_var_set(lp, cp) < 0) {
1.34      joris     463:                xfree(lp);
1.11      jfb       464:                return (-1);
                    465:        }
                    466:
1.34      joris     467:        xfree(lp);
1.11      jfb       468:
                    469:        return (0);
                    470: }
                    471:
                    472:
                    473: static int
1.2       jfb       474: cvs_req_argument(int reqid, char *line)
                    475: {
                    476:        char *nap;
                    477:
1.36      xsa       478:        if (cvs_req_nargs == CVS_PROTO_MAXARG)
                    479:                fatal("too many arguments");
1.2       jfb       480:
                    481:        if (reqid == CVS_REQ_ARGUMENT) {
1.34      joris     482:                cvs_req_args[cvs_req_nargs] = xstrdup(line);
1.2       jfb       483:                cvs_req_nargs++;
1.6       deraadt   484:        } else if (reqid == CVS_REQ_ARGUMENTX) {
1.2       jfb       485:                if (cvs_req_nargs == 0)
                    486:                        cvs_log(LP_WARN, "no argument to append to");
                    487:                else {
1.43      ray       488:                        xasprintf(&nap, "%s%s", cvs_req_args[cvs_req_nargs - 1],
1.2       jfb       489:                            line);
1.36      xsa       490:
1.34      joris     491:                        xfree(cvs_req_args[cvs_req_nargs - 1]);
1.2       jfb       492:                        cvs_req_args[cvs_req_nargs - 1] = nap;
                    493:                }
1.3       jfb       494:        }
                    495:
                    496:        return (0);
                    497: }
                    498:
                    499:
                    500: static int
                    501: cvs_req_globalopt(int reqid, char *line)
                    502: {
1.44    ! deraadt   503:        if (*line != '-' || *(line + 2) != '\0')
1.36      xsa       504:                fatal("invalid `Global_option' request format");
1.3       jfb       505:
                    506:        switch (*(line + 1)) {
                    507:        case 'l':
                    508:                cvs_nolog = 1;
                    509:                break;
                    510:        case 'n':
1.24      xsa       511:                cvs_noexec = 1;
1.3       jfb       512:                break;
                    513:        case 'Q':
                    514:                verbosity = 0;
                    515:                break;
                    516:        case 'q':
                    517:                if (verbosity > 1)
                    518:                        verbosity = 1;
                    519:                break;
                    520:        case 'r':
                    521:                cvs_readonly = 1;
                    522:                break;
                    523:        case 't':
                    524:                cvs_trace = 1;
                    525:                break;
                    526:        default:
                    527:                cvs_log(LP_ERR, "unknown global option `%s'", line);
                    528:                return (-1);
1.2       jfb       529:        }
1.8       jfb       530:
                    531:        return (0);
                    532: }
                    533:
                    534:
                    535: /*
                    536:  * cvs_req_gzipstream()
                    537:  *
                    538:  * Handler for the `Gzip-stream' request, which enables compression at the
                    539:  * level given along with the request.  After this request has been processed,
                    540:  * all further connection data should be compressed.
                    541:  */
                    542: static int
                    543: cvs_req_gzipstream(int reqid, char *line)
                    544: {
                    545:        char *ep;
                    546:        long val;
                    547:
                    548:        val = strtol(line, &ep, 10);
1.44    ! deraadt   549:        if (line[0] == '\0' || *ep != '\0')
1.36      xsa       550:                fatal("invalid Gzip-stream level `%s'", line);
1.44    ! deraadt   551:        else if (errno == ERANGE && (val < 0 || val > 9))
1.36      xsa       552:                fatal("Gzip-stream level %ld out of range", val);
1.8       jfb       553:
                    554:        cvs_compress = (int)val;
1.1       jfb       555:
                    556:        return (0);
                    557: }
                    558:
                    559:
1.11      jfb       560: /*
                    561:  * cvs_req_command()
                    562:  *
                    563:  * Generic request handler for CVS command requests (i.e. diff, update, tag).
                    564:  */
1.1       jfb       565: static int
1.11      jfb       566: cvs_req_command(int reqid, char *line)
1.1       jfb       567: {
1.15      jfb       568:        struct cvs_cmd *cmdp;
1.11      jfb       569:
1.15      jfb       570:        cmdp = cvs_findcmdbyreq(reqid);
                    571:        if (cmdp == NULL) {
1.11      jfb       572:                cvs_sendresp(CVS_RESP_ERROR, NULL);
1.15      jfb       573:                return (-1);
1.11      jfb       574:        }
1.15      jfb       575:
1.22      joris     576:        /* close the Entry file if it's still open */
                    577:        if (cvs_req_entf != NULL)
                    578:                cvs_ent_close(cvs_req_entf);
                    579:
                    580:        /* fill in the command name */
                    581:        cvs_req_args[0] = cmdp->cmd_name;
                    582:
                    583:        /* switch to the correct directory */
1.37      xsa       584:        if (cmdp->cmd_op != CVS_OP_VERSION)
1.38      xsa       585:                cvs_chdir(cvs_server_tmpdir, 1);
1.22      joris     586:
1.41      xsa       587:        if (cvs_startcmd(cmdp, cvs_req_nargs, cvs_req_args) == 0)
                    588:                cvs_sendresp(CVS_RESP_OK, NULL);
1.22      joris     589:
1.41      xsa       590:        return (0);
1.1       jfb       591: }