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