Annotation of src/usr.bin/cvs/req.c, Revision 1.40
1.40 ! xsa 1: /* $OpenBSD: req.c,v 1.39 2005/12/30 16:47:36 joris 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: {
185: int ret;
186:
187: ret = cvs_sendresp(CVS_RESP_OK, NULL);
188: if (ret < 0)
189: return (-1);
190: return (0);
191: }
192:
1.1 jfb 193:
194: static int
195: cvs_req_root(int reqid, char *line)
196: {
1.11 jfb 197: if (cvs_req_rootpath != NULL) {
198: cvs_log(LP_ERR, "duplicate Root request received");
1.13 jfb 199: cvs_printf("Protocol error: Duplicate Root request");
1.11 jfb 200: return (-1);
201: }
1.9 jfb 202:
1.34 joris 203: cvs_req_rootpath = xstrdup(line);
1.22 joris 204: cvs_rootstr = cvs_req_rootpath;
205:
1.4 jfb 206: return (0);
207: }
208:
209:
210: static int
211: cvs_req_validreq(int reqid, char *line)
212: {
213: char *vreq;
214:
215: vreq = cvs_req_getvalid();
216: if (vreq == NULL)
217: return (-1);
218:
1.11 jfb 219: if ((cvs_sendresp(CVS_RESP_VALIDREQ, vreq) < 0) ||
220: (cvs_sendresp(CVS_RESP_OK, NULL) < 0))
221: return (-1);
1.4 jfb 222:
223: return (0);
224: }
225:
226: static int
227: cvs_req_validresp(int reqid, char *line)
228: {
229: char *sp, *ep;
230: struct cvs_resp *resp;
1.1 jfb 231:
1.4 jfb 232: sp = line;
233: do {
234: ep = strchr(sp, ' ');
235: if (ep != NULL)
236: *(ep++) = '\0';
237:
238: resp = cvs_resp_getbyname(sp);
239: if (resp != NULL)
240: ;
241:
242: if (ep != NULL)
243: sp = ep + 1;
244: } while (ep != NULL);
1.1 jfb 245:
246: return (0);
247: }
248:
249: static int
250: cvs_req_directory(int reqid, char *line)
251: {
1.22 joris 252: int pwd;
253: size_t dirlen;
1.11 jfb 254: char rdir[MAXPATHLEN];
1.22 joris 255: char *repo, *s, *p;
256:
257: pwd = (!strcmp(line, "."));
1.1 jfb 258:
1.39 joris 259: cvs_getln(NULL, rdir, sizeof(rdir));
1.33 joris 260:
261: STRIP_SLASH(rdir);
1.11 jfb 262:
1.14 joris 263: if (cvs_req_currentdir != NULL)
1.34 joris 264: xfree(cvs_req_currentdir);
1.14 joris 265:
1.34 joris 266: cvs_req_currentdir = xstrdup(rdir);
1.14 joris 267:
1.22 joris 268: dirlen = strlen(cvs_req_currentdir);
1.14 joris 269:
1.22 joris 270: /*
271: * Lets make sure we always start at the correct
272: * directory.
273: */
1.38 xsa 274: cvs_chdir(cvs_server_tmpdir, 1);
1.14 joris 275:
1.22 joris 276: /*
277: * Set repository path.
278: */
1.30 joris 279: if (strlen(cvs_req_rootpath) < dirlen) {
280: s = cvs_req_currentdir + strlen(cvs_req_rootpath) + 1;
1.36 xsa 281: if (s >= (cvs_req_currentdir + dirlen))
282: fatal("you're bad, go away");
1.30 joris 283: } else
284: s = cvs_req_currentdir;
1.14 joris 285:
1.36 xsa 286: repo = xstrdup(s);
1.22 joris 287:
288: /*
289: * Skip back "foo/bar" part, so we can feed the repo
290: * as a startpoint for cvs_create_dir().
291: */
292: if (!pwd) {
293: s = repo + strlen(repo) - strlen(line) - 1;
1.36 xsa 294: if (*s != '/')
295: fatal("cvs_req_directory: malformed directory");
1.22 joris 296:
297: *s = '\0';
298: }
299:
300: /*
301: * Obtain the modulename, we only need to do this at
302: * the very first time we get a Directory request.
303: */
304: if (cvs_req_modulename == NULL) {
305: if ((p = strchr(repo, '/')) != NULL)
306: *p = '\0';
307:
1.36 xsa 308: cvs_req_modulename = xstrdup(repo);
1.22 joris 309:
310: if (p != NULL)
311: *p = '/';
312:
313: /*
314: * Now, create the admin files in the top-level
315: * directory for the temp repo.
316: */
1.31 xsa 317: if (cvs_mkadmin(cvs_server_tmpdir, cvs_rootstr, repo,
318: NULL, NULL, 0) < 0) {
1.22 joris 319: cvs_log(LP_ERR, "failed to create admin files");
1.34 joris 320: xfree(repo);
1.22 joris 321: return (-1);
322: }
323: }
324:
325: /*
326: * create the directory plus the administrative files.
327: */
328: if (cvs_create_dir(line, 1, cvs_rootstr, repo) < 0) {
1.34 joris 329: xfree(repo);
1.22 joris 330: return (-1);
331: }
1.14 joris 332:
1.22 joris 333: /*
334: * cvs_create_dir() has already put us in the correct directory
335: * so now open it's Entry file for incoming files.
336: */
337: if (cvs_req_entf != NULL)
338: cvs_ent_close(cvs_req_entf);
339: cvs_req_entf = cvs_ent_open(".", O_RDWR);
1.36 xsa 340: if (cvs_req_entf == NULL)
341: fatal("failed to open Entry file for %s", line);
1.22 joris 342:
1.34 joris 343: xfree(repo);
1.11 jfb 344: return (0);
345: }
346:
1.13 jfb 347: static int
348: cvs_req_entry(int reqid, char *line)
349: {
350: struct cvs_ent *ent;
351:
1.22 joris 352: /* parse received entry */
1.13 jfb 353: if ((ent = cvs_ent_parse(line)) == NULL)
354: return (-1);
355:
1.22 joris 356: /* add it to the entry file and done */
1.36 xsa 357: if (cvs_ent_add(cvs_req_entf, ent) < 0)
358: fatal("cvs_req_entry: cvs_ent_add: `%s'", ent->ce_name);
1.22 joris 359:
360: /* XXX */
361: cvs_ent_write(cvs_req_entf);
1.15 jfb 362:
1.13 jfb 363: return (0);
364: }
365:
366: /*
367: * cvs_req_filestate()
368: *
369: * Handler for the `Modified', `Is-Modified', `Unchanged' and `Questionable'
370: * requests, which are all used to report the assumed state of a file from the
371: * client.
372: */
373: static int
374: cvs_req_filestate(int reqid, char *line)
375: {
1.22 joris 376: int ret;
1.13 jfb 377: mode_t fmode;
378: BUF *fdata;
1.22 joris 379: struct cvs_ent *ent;
1.13 jfb 380:
1.22 joris 381: ret = 0;
382: switch (reqid) {
383: case CVS_REQ_MODIFIED:
1.13 jfb 384: fdata = cvs_recvfile(NULL, &fmode);
385: if (fdata == NULL)
386: return (-1);
1.14 joris 387:
388: /* write the file */
1.35 xsa 389: cvs_buf_write(fdata, line, fmode);
1.14 joris 390: cvs_buf_free(fdata);
1.22 joris 391: break;
392: case CVS_REQ_ISMODIFIED:
393: break;
394: case CVS_REQ_UNCHANGED:
395: ent = cvs_ent_get(cvs_req_entf, line);
396: if (ent == NULL) {
397: cvs_log(LP_ERR,
1.32 reyk 398: "received Unchanged request "
399: "for a non-existing file");
1.22 joris 400: ret = -1;
401: } else {
402: ent->ce_status = CVS_ENT_UPTODATE;
403: }
404: break;
405: case CVS_REQ_QUESTIONABLE:
1.26 joris 406: cvs_printf("? %s\n", line);
1.22 joris 407: break;
408: default:
409: cvs_log(LP_ERR, "wrong request id type");
410: ret = -1;
411: break;
1.13 jfb 412: }
413:
1.22 joris 414: /* XXX */
415: cvs_req_entf->cef_flags &= ~CVS_ENTF_SYNC;
416: cvs_ent_write(cvs_req_entf);
417:
418: return (ret);
1.13 jfb 419: }
1.12 jfb 420:
421: /*
422: * cvs_req_expandmod()
423: *
424: */
425: static int
426: cvs_req_expandmod(int reqid, char *line)
427: {
428: int ret;
429:
430: ret = cvs_sendresp(CVS_RESP_OK, NULL);
431: if (ret < 0)
432: return (-1);
433: return (0);
434: }
435:
436:
1.11 jfb 437: /*
438: * cvs_req_useunchanged()
439: *
440: * Handler for the `UseUnchanged' requests. The protocol documentation
441: * specifies that this request must be supported by the server and must be
442: * sent by the client, though it gives no clue regarding its use.
443: */
444: static int
445: cvs_req_useunchanged(int reqid, char *line)
446: {
1.5 jfb 447: return (0);
448: }
449:
1.11 jfb 450:
1.5 jfb 451: /*
452: * cvs_req_case()
453: *
454: * Handler for the `Case' requests, which toggles case sensitivity ON or OFF
455: */
456: static int
457: cvs_req_case(int reqid, char *line)
458: {
459: cvs_nocase = 1;
1.2 jfb 460: return (0);
461: }
462:
463:
464: static int
1.11 jfb 465: cvs_req_set(int reqid, char *line)
466: {
467: char *cp, *lp;
468:
1.36 xsa 469: lp = xstrdup(line);
470: if ((cp = strchr(lp, '=')) == NULL)
471: fatal("error in Set request (no = in variable assignment)");
1.11 jfb 472:
473: *(cp++) = '\0';
474:
475: if (cvs_var_set(lp, cp) < 0) {
1.34 joris 476: xfree(lp);
1.11 jfb 477: return (-1);
478: }
479:
1.34 joris 480: xfree(lp);
1.11 jfb 481:
482: return (0);
483: }
484:
485:
486: static int
1.2 jfb 487: cvs_req_argument(int reqid, char *line)
488: {
489: char *nap;
490:
1.36 xsa 491: if (cvs_req_nargs == CVS_PROTO_MAXARG)
492: fatal("too many arguments");
1.2 jfb 493:
494: if (reqid == CVS_REQ_ARGUMENT) {
1.34 joris 495: cvs_req_args[cvs_req_nargs] = xstrdup(line);
1.2 jfb 496: cvs_req_nargs++;
1.6 deraadt 497: } else if (reqid == CVS_REQ_ARGUMENTX) {
1.2 jfb 498: if (cvs_req_nargs == 0)
499: cvs_log(LP_WARN, "no argument to append to");
500: else {
501: asprintf(&nap, "%s%s", cvs_req_args[cvs_req_nargs - 1],
502: line);
1.36 xsa 503: if (nap == NULL)
504: fatal("cvs_req_argument: asprintf failed");
505:
1.34 joris 506: xfree(cvs_req_args[cvs_req_nargs - 1]);
1.2 jfb 507: cvs_req_args[cvs_req_nargs - 1] = nap;
508: }
1.3 jfb 509: }
510:
511: return (0);
512: }
513:
514:
515: static int
516: cvs_req_globalopt(int reqid, char *line)
517: {
1.36 xsa 518: if ((*line != '-') || (*(line + 2) != '\0'))
519: fatal("invalid `Global_option' request format");
1.3 jfb 520:
521: switch (*(line + 1)) {
522: case 'l':
523: cvs_nolog = 1;
524: break;
525: case 'n':
1.24 xsa 526: cvs_noexec = 1;
1.3 jfb 527: break;
528: case 'Q':
529: verbosity = 0;
530: break;
531: case 'q':
532: if (verbosity > 1)
533: verbosity = 1;
534: break;
535: case 'r':
536: cvs_readonly = 1;
537: break;
538: case 't':
539: cvs_trace = 1;
540: break;
541: default:
542: cvs_log(LP_ERR, "unknown global option `%s'", line);
543: return (-1);
1.2 jfb 544: }
1.8 jfb 545:
546: return (0);
547: }
548:
549:
550: /*
551: * cvs_req_gzipstream()
552: *
553: * Handler for the `Gzip-stream' request, which enables compression at the
554: * level given along with the request. After this request has been processed,
555: * all further connection data should be compressed.
556: */
557: static int
558: cvs_req_gzipstream(int reqid, char *line)
559: {
560: char *ep;
561: long val;
562:
563: val = strtol(line, &ep, 10);
1.36 xsa 564: if ((line[0] == '\0') || (*ep != '\0'))
565: fatal("invalid Gzip-stream level `%s'", line);
566: else if ((errno == ERANGE) && ((val < 0) || (val > 9)))
567: fatal("Gzip-stream level %ld out of range", val);
1.8 jfb 568:
569: cvs_compress = (int)val;
1.1 jfb 570:
571: return (0);
572: }
573:
574:
1.11 jfb 575: /*
576: * cvs_req_command()
577: *
578: * Generic request handler for CVS command requests (i.e. diff, update, tag).
579: */
1.1 jfb 580: static int
1.11 jfb 581: cvs_req_command(int reqid, char *line)
1.1 jfb 582: {
1.16 jfb 583: int ret = 0;
1.15 jfb 584: struct cvs_cmd *cmdp;
1.11 jfb 585:
1.15 jfb 586: cmdp = cvs_findcmdbyreq(reqid);
587: if (cmdp == NULL) {
1.11 jfb 588: cvs_sendresp(CVS_RESP_ERROR, NULL);
1.15 jfb 589: return (-1);
1.11 jfb 590: }
1.15 jfb 591:
1.22 joris 592: /* close the Entry file if it's still open */
593: if (cvs_req_entf != NULL)
594: cvs_ent_close(cvs_req_entf);
595:
596: /* fill in the command name */
597: cvs_req_args[0] = cmdp->cmd_name;
598:
599: /* switch to the correct directory */
1.37 xsa 600: if (cmdp->cmd_op != CVS_OP_VERSION)
1.38 xsa 601: cvs_chdir(cvs_server_tmpdir, 1);
1.22 joris 602:
1.16 jfb 603: ret = cvs_startcmd(cmdp, cvs_req_nargs, cvs_req_args);
1.22 joris 604:
1.11 jfb 605: if (ret == 0)
606: ret = cvs_sendresp(CVS_RESP_OK, NULL);
607:
608: return (ret);
1.1 jfb 609: }