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