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