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: }