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