Annotation of src/usr.bin/cvs/req.c, Revision 1.14
1.14 ! joris 1: /* $OpenBSD: req.c,v 1.13 2005/04/18 21:33:34 jfb 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 },
119: { NULL },
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:
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.4 jfb 222: return (0);
223: }
224:
225:
226: static int
227: cvs_req_validreq(int reqid, char *line)
228: {
229: char *vreq;
230:
231: vreq = cvs_req_getvalid();
232: if (vreq == NULL)
233: return (-1);
234:
1.11 jfb 235: if ((cvs_sendresp(CVS_RESP_VALIDREQ, vreq) < 0) ||
236: (cvs_sendresp(CVS_RESP_OK, NULL) < 0))
237: return (-1);
1.4 jfb 238:
239: return (0);
240: }
241:
242: static int
243: cvs_req_validresp(int reqid, char *line)
244: {
245: char *sp, *ep;
246: struct cvs_resp *resp;
1.1 jfb 247:
1.4 jfb 248: sp = line;
249: do {
250: ep = strchr(sp, ' ');
251: if (ep != NULL)
252: *(ep++) = '\0';
253:
254: resp = cvs_resp_getbyname(sp);
255: if (resp != NULL)
256: ;
257:
258: if (ep != NULL)
259: sp = ep + 1;
260: } while (ep != NULL);
1.1 jfb 261:
262: return (0);
263: }
264:
265: static int
266: cvs_req_directory(int reqid, char *line)
267: {
1.14 ! joris 268: int l;
1.11 jfb 269: char rdir[MAXPATHLEN];
1.1 jfb 270:
1.11 jfb 271: if (cvs_getln(NULL, rdir, sizeof(rdir)) < 0)
272: return (-1);
273:
1.14 ! joris 274: if (cvs_req_currentdir != NULL)
! 275: free(cvs_req_currentdir);
! 276:
! 277: cvs_req_currentdir = strdup(rdir);
! 278: if (cvs_req_currentdir == NULL) {
! 279: cvs_log(LP_ERROR, "failed to duplicate directory");
! 280: return (-1);
! 281: }
! 282:
! 283: /* now obtain the path relative to the Root directory */
! 284: cvs_req_repopath = cvs_req_currentdir + strlen(cvs_req_rootpath) + 1;
! 285:
! 286: /* create tmp path */
! 287: l = snprintf(cvs_req_tmppath, sizeof(cvs_req_tmppath), "%s/%s",
! 288: cvs_server_tmpdir, cvs_req_repopath);
! 289: if (l == -1 || l >= (int)sizeof(cvs_req_tmppath)) {
! 290: errno = ENAMETOOLONG;
! 291: cvs_log(LP_ERRNO, "%s", cvs_req_tmppath);
! 292: return (-1);
! 293: }
! 294:
! 295: if ((mkdir(cvs_req_tmppath, 0755) == -1) && (errno != EEXIST)) {
! 296: cvs_log(LP_ERRNO, "failed to create temporary directory '%s'",
! 297: cvs_req_tmppath);
! 298: return (-1);
! 299: }
! 300:
! 301: /* create the CVS/ administrative files */
! 302: /* XXX - TODO */
! 303:
1.11 jfb 304: return (0);
305: }
306:
1.13 jfb 307: static int
308: cvs_req_entry(int reqid, char *line)
309: {
310: struct cvs_ent *ent;
311:
312: if ((ent = cvs_ent_parse(line)) == NULL)
313: return (-1);
314:
315: return (0);
316: }
317:
318: /*
319: * cvs_req_filestate()
320: *
321: * Handler for the `Modified', `Is-Modified', `Unchanged' and `Questionable'
322: * requests, which are all used to report the assumed state of a file from the
323: * client.
324: */
325: static int
326: cvs_req_filestate(int reqid, char *line)
327: {
1.14 ! joris 328: int l;
1.13 jfb 329: mode_t fmode;
330: BUF *fdata;
1.14 ! joris 331: char fpath[MAXPATHLEN];
1.13 jfb 332:
333: if (reqid == CVS_REQ_MODIFIED) {
334: fdata = cvs_recvfile(NULL, &fmode);
335: if (fdata == NULL)
336: return (-1);
1.14 ! joris 337:
! 338: /* create full temporary path */
! 339: l = snprintf(fpath, sizeof(fpath), "%s/%s", cvs_req_tmppath,
! 340: line);
! 341: if (l == -1 || l >= (int)sizeof(fpath)) {
! 342: errno = ENAMETOOLONG;
! 343: cvs_log(LP_ERRNO, "%s", fpath);
! 344: cvs_buf_free(fdata);
! 345: return (-1);
! 346: }
! 347:
! 348: /* write the file */
! 349: if (cvs_buf_write(fdata, fpath, fmode) < 0) {
! 350: cvs_log(LP_ERROR, "failed to create file %s", fpath);
! 351: cvs_buf_free(fdata);
! 352: return (-1);
! 353: }
! 354:
! 355: cvs_buf_free(fdata);
1.13 jfb 356: }
357:
358: return (0);
359: }
1.12 jfb 360:
361: /*
362: * cvs_req_expandmod()
363: *
364: */
365: static int
366: cvs_req_expandmod(int reqid, char *line)
367: {
368: int ret;
369:
370: ret = cvs_sendresp(CVS_RESP_OK, NULL);
371: if (ret < 0)
372: return (-1);
373: return (0);
374: }
375:
376:
1.11 jfb 377: /*
378: * cvs_req_useunchanged()
379: *
380: * Handler for the `UseUnchanged' requests. The protocol documentation
381: * specifies that this request must be supported by the server and must be
382: * sent by the client, though it gives no clue regarding its use.
383: */
384: static int
385: cvs_req_useunchanged(int reqid, char *line)
386: {
1.5 jfb 387: return (0);
388: }
389:
1.11 jfb 390:
1.5 jfb 391: /*
392: * cvs_req_case()
393: *
394: * Handler for the `Case' requests, which toggles case sensitivity ON or OFF
395: */
396: static int
397: cvs_req_case(int reqid, char *line)
398: {
399: cvs_nocase = 1;
1.2 jfb 400: return (0);
401: }
402:
403:
404: static int
1.11 jfb 405: cvs_req_set(int reqid, char *line)
406: {
407: char *cp, *lp;
408:
409: if ((lp = strdup(line)) == NULL) {
410: cvs_log(LP_ERRNO, "failed to copy Set argument");
411: return (-1);
412: }
413:
414: if ((cp = strchr(lp, '=')) == NULL) {
415: cvs_log(LP_ERR, "error in Set request "
416: "(no = in variable assignment)");
417: free(lp);
418: return (-1);
419: }
420: *(cp++) = '\0';
421:
422: if (cvs_var_set(lp, cp) < 0) {
423: free(lp);
424: return (-1);
425: }
426:
427: free(lp);
428:
429: return (0);
430: }
431:
432:
433: static int
1.2 jfb 434: cvs_req_argument(int reqid, char *line)
435: {
436: char *nap;
437:
438: if (cvs_req_nargs == CVS_PROTO_MAXARG) {
439: cvs_log(LP_ERR, "too many arguments");
440: return (-1);
441: }
442:
443: if (reqid == CVS_REQ_ARGUMENT) {
444: cvs_req_args[cvs_req_nargs] = strdup(line);
445: if (cvs_req_args[cvs_req_nargs] == NULL) {
446: cvs_log(LP_ERRNO, "failed to copy argument");
447: return (-1);
448: }
449: cvs_req_nargs++;
1.6 deraadt 450: } else if (reqid == CVS_REQ_ARGUMENTX) {
1.2 jfb 451: if (cvs_req_nargs == 0)
452: cvs_log(LP_WARN, "no argument to append to");
453: else {
454: asprintf(&nap, "%s%s", cvs_req_args[cvs_req_nargs - 1],
455: line);
456: if (nap == NULL) {
457: cvs_log(LP_ERRNO,
458: "failed to append to argument");
459: return (-1);
460: }
461: free(cvs_req_args[cvs_req_nargs - 1]);
462: cvs_req_args[cvs_req_nargs - 1] = nap;
463: }
1.3 jfb 464: }
465:
466: return (0);
467: }
468:
469:
470: static int
471: cvs_req_globalopt(int reqid, char *line)
472: {
473: if ((*line != '-') || (*(line + 2) != '\0')) {
474: cvs_log(LP_ERR,
475: "invalid `Global_option' request format");
476: return (-1);
477: }
478:
479: switch (*(line + 1)) {
480: case 'l':
481: cvs_nolog = 1;
482: break;
483: case 'n':
484: break;
485: case 'Q':
486: verbosity = 0;
487: break;
488: case 'q':
489: if (verbosity > 1)
490: verbosity = 1;
491: break;
492: case 'r':
493: cvs_readonly = 1;
494: break;
495: case 't':
496: cvs_trace = 1;
497: break;
498: default:
499: cvs_log(LP_ERR, "unknown global option `%s'", line);
500: return (-1);
1.2 jfb 501: }
1.8 jfb 502:
503: return (0);
504: }
505:
506:
507: /*
508: * cvs_req_gzipstream()
509: *
510: * Handler for the `Gzip-stream' request, which enables compression at the
511: * level given along with the request. After this request has been processed,
512: * all further connection data should be compressed.
513: */
514: static int
515: cvs_req_gzipstream(int reqid, char *line)
516: {
517: char *ep;
518: long val;
519:
520: val = strtol(line, &ep, 10);
521: if ((line[0] == '\0') || (*ep != '\0')) {
522: cvs_log(LP_ERR, "invalid Gzip-stream level `%s'", line);
523: return (-1);
524: } else if ((errno == ERANGE) && ((val < 0) || (val > 9))) {
525: cvs_log(LP_ERR, "Gzip-stream level %ld out of range", val);
526: return (-1);
527: }
528:
529: cvs_compress = (int)val;
1.1 jfb 530:
531: return (0);
532: }
533:
534:
1.11 jfb 535: /*
536: * cvs_req_command()
537: *
538: * Generic request handler for CVS command requests (i.e. diff, update, tag).
539: */
1.1 jfb 540: static int
1.11 jfb 541: cvs_req_command(int reqid, char *line)
1.1 jfb 542: {
1.11 jfb 543: int ret;
544:
545: switch (reqid) {
546: case CVS_REQ_VERSION:
547: ret = cvs_sendresp(CVS_RESP_M, CVS_VERSION);
548: break;
549: case CVS_REQ_ADD:
550: case CVS_REQ_ANNOTATE:
1.12 jfb 551: case CVS_REQ_CO:
1.11 jfb 552: case CVS_REQ_CI:
553: case CVS_REQ_DIFF:
554: case CVS_REQ_LOG:
555: case CVS_REQ_REMOVE:
556: case CVS_REQ_STATUS:
557: case CVS_REQ_TAG:
558: default:
559: cvs_sendresp(CVS_RESP_E, "command not yet implemented");
560: cvs_sendresp(CVS_RESP_ERROR, NULL);
561: return (0);
562: }
563:
564: if (ret == 0)
565: ret = cvs_sendresp(CVS_RESP_OK, NULL);
566:
567: return (ret);
1.1 jfb 568: }