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