Annotation of src/usr.bin/cvs/proto.c, Revision 1.12
1.1 jfb 1: /* $OpenBSD$ */
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: * CVS client/server protocol
28: * ==========================
29: *
30: * The following code implements the CVS client/server protocol, which is
31: * documented at the following URL:
32: * http://www.loria.fr/~molli/cvs/doc/cvsclient_toc.html
33: *
34: * The protocol is split up into two parts; the first part is the client side
35: * of things and is composed of all the response handlers, which are all named
36: * with a prefix of "cvs_resp_". The second part is the set of request
37: * handlers used by the server. These handlers process the request and
38: * generate the appropriate response to send back. The prefix for request
39: * handlers is "cvs_req_".
40: *
41: */
42:
43: #include <sys/types.h>
44: #include <sys/stat.h>
45:
46: #include <fcntl.h>
47: #include <stdio.h>
48: #include <errno.h>
49: #include <stdlib.h>
50: #include <unistd.h>
51: #include <signal.h>
52: #include <string.h>
53: #include <sysexits.h>
54: #ifdef CVS_ZLIB
55: #include <zlib.h>
56: #endif
57:
58: #include "buf.h"
59: #include "cvs.h"
60: #include "log.h"
61:
62:
1.5 jfb 63: #define CVS_MTSTK_MAXDEPTH 16
64:
1.11 jfb 65: /* request flags */
66: #define CVS_REQF_RESP 0x01
67:
68:
1.5 jfb 69:
1.1 jfb 70:
71: extern int verbosity;
72: extern int cvs_compress;
73: extern char *cvs_rsh;
74: extern int cvs_trace;
75: extern int cvs_nolog;
76: extern int cvs_readonly;
77:
78:
79:
80: static int cvs_resp_validreq (int, char *);
81: static int cvs_resp_cksum (int, char *);
1.4 jfb 82: static int cvs_resp_modtime (int, char *);
1.1 jfb 83: static int cvs_resp_m (int, char *);
84: static int cvs_resp_ok (int, char *);
85: static int cvs_resp_error (int, char *);
86: static int cvs_resp_statdir (int, char *);
1.5 jfb 87: static int cvs_resp_sticky (int, char *);
1.1 jfb 88: static int cvs_resp_newentry (int, char *);
89: static int cvs_resp_updated (int, char *);
90: static int cvs_resp_removed (int, char *);
91: static int cvs_resp_mode (int, char *);
1.5 jfb 92: static int cvs_resp_modxpand (int, char *);
1.1 jfb 93:
94:
1.7 jfb 95: static const char *cvs_months[] = {
1.9 jfb 96: "Jan", "Feb", "Mar", "Apr", "May", "Jun",
97: "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1.7 jfb 98: };
99:
100:
101:
1.1 jfb 102: struct cvs_req {
103: int req_id;
104: char req_str[32];
105: u_int req_flags;
106: int (*req_hdlr)(int, char *);
107: } cvs_requests[] = {
108: { CVS_REQ_DIRECTORY, "Directory", 0, NULL },
109: { CVS_REQ_MAXDOTDOT, "Max-dotdot", 0, NULL },
110: { CVS_REQ_STATICDIR, "Static-directory", 0, NULL },
111: { CVS_REQ_STICKY, "Sticky", 0, NULL },
112: { CVS_REQ_ENTRY, "Entry", 0, NULL },
113: { CVS_REQ_ENTRYEXTRA, "EntryExtra", 0, NULL },
114: { CVS_REQ_CHECKINTIME, "Checkin-time", 0, NULL },
115: { CVS_REQ_MODIFIED, "Modified", 0, NULL },
116: { CVS_REQ_ISMODIFIED, "Is-modified", 0, NULL },
117: { CVS_REQ_UNCHANGED, "Unchanged", 0, NULL },
118: { CVS_REQ_USEUNCHANGED, "UseUnchanged", 0, NULL },
119: { CVS_REQ_NOTIFY, "Notify", 0, NULL },
120: { CVS_REQ_NOTIFYUSER, "NotifyUser", 0, NULL },
121: { CVS_REQ_QUESTIONABLE, "Questionable", 0, NULL },
122: { CVS_REQ_CASE, "Case", 0, NULL },
123: { CVS_REQ_UTF8, "Utf8", 0, NULL },
124: { CVS_REQ_ARGUMENT, "Argument", 0, NULL },
125: { CVS_REQ_ARGUMENTX, "Argumentx", 0, NULL },
126: { CVS_REQ_GLOBALOPT, "Global_option", 0, NULL },
127: { CVS_REQ_GZIPSTREAM, "Gzip-stream", 0, NULL },
128: { CVS_REQ_READCVSRC2, "read-cvsrc2", 0, NULL },
129: { CVS_REQ_READWRAP, "read-cvswrappers", 0, NULL },
130: { CVS_REQ_READIGNORE, "read-cvsignore", 0, NULL },
131: { CVS_REQ_ERRIFREADER, "Error-If-Reader", 0, NULL },
132: { CVS_REQ_VALIDRCSOPT, "Valid-RcsOptions", 0, NULL },
133: { CVS_REQ_SET, "Set", 0, NULL },
1.11 jfb 134: { CVS_REQ_XPANDMOD, "expand-modules", CVS_REQF_RESP, NULL },
1.1 jfb 135: { CVS_REQ_LOG, "log", 0, NULL },
1.11 jfb 136: { CVS_REQ_CO, "co", CVS_REQF_RESP, NULL },
137: { CVS_REQ_EXPORT, "export", CVS_REQF_RESP, NULL },
1.1 jfb 138: { CVS_REQ_RANNOTATE, "rannotate", 0, NULL },
139: { CVS_REQ_RDIFF, "rdiff", 0, NULL },
140: { CVS_REQ_RLOG, "rlog", 0, NULL },
1.11 jfb 141: { CVS_REQ_RTAG, "rtag", CVS_REQF_RESP, NULL },
142: { CVS_REQ_INIT, "init", CVS_REQF_RESP, NULL },
143: { CVS_REQ_STATUS, "status", CVS_REQF_RESP, NULL },
144: { CVS_REQ_UPDATE, "update", CVS_REQF_RESP, NULL },
1.1 jfb 145: { CVS_REQ_HISTORY, "history", 0, NULL },
1.11 jfb 146: { CVS_REQ_IMPORT, "import", CVS_REQF_RESP, NULL },
147: { CVS_REQ_ADD, "add", CVS_REQF_RESP, NULL },
148: { CVS_REQ_REMOVE, "remove", CVS_REQF_RESP, NULL },
149: { CVS_REQ_RELEASE, "release", CVS_REQF_RESP, NULL },
1.1 jfb 150: { CVS_REQ_ROOT, "Root", 0, NULL },
1.11 jfb 151: { CVS_REQ_VALIDRESP, "Valid-responses", CVS_REQF_RESP, NULL },
152: { CVS_REQ_VALIDREQ, "valid-requests", CVS_REQF_RESP, NULL },
153: { CVS_REQ_VERSION, "version", CVS_REQF_RESP, NULL },
154: { CVS_REQ_NOOP, "noop", CVS_REQF_RESP, NULL },
155: { CVS_REQ_DIFF, "diff", CVS_REQF_RESP, NULL },
1.1 jfb 156: };
157:
158:
159: struct cvs_resp {
160: u_int resp_id;
161: char resp_str[32];
162: int (*resp_hdlr)(int, char *);
163: } cvs_responses[] = {
164: { CVS_RESP_OK, "ok", cvs_resp_ok },
165: { CVS_RESP_ERROR, "error", cvs_resp_error },
166: { CVS_RESP_VALIDREQ, "Valid-requests", cvs_resp_validreq },
167: { CVS_RESP_M, "M", cvs_resp_m },
168: { CVS_RESP_MBINARY, "Mbinary", cvs_resp_m },
169: { CVS_RESP_MT, "MT", cvs_resp_m },
170: { CVS_RESP_E, "E", cvs_resp_m },
171: { CVS_RESP_F, "F", cvs_resp_m },
172: { CVS_RESP_CREATED, "Created", cvs_resp_updated },
173: { CVS_RESP_UPDATED, "Updated", cvs_resp_updated },
174: { CVS_RESP_UPDEXIST, "Update-existing", cvs_resp_updated },
175: { CVS_RESP_REMOVED, "Removed", cvs_resp_removed },
176: { CVS_RESP_MERGED, "Merged", NULL },
177: { CVS_RESP_CKSUM, "Checksum", cvs_resp_cksum },
178: { CVS_RESP_CLRSTATDIR, "Clear-static-directory", cvs_resp_statdir },
179: { CVS_RESP_SETSTATDIR, "Set-static-directory", cvs_resp_statdir },
180: { CVS_RESP_NEWENTRY, "New-entry", cvs_resp_newentry },
181: { CVS_RESP_CHECKEDIN, "Checked-in", cvs_resp_newentry },
182: { CVS_RESP_MODE, "Mode", cvs_resp_mode },
1.4 jfb 183: { CVS_RESP_MODTIME, "Mod-time", cvs_resp_modtime },
1.5 jfb 184: { CVS_RESP_MODXPAND, "Module-expansion", cvs_resp_modxpand },
185: { CVS_RESP_SETSTICKY, "Set-sticky", cvs_resp_sticky },
186: { CVS_RESP_CLRSTICKY, "Clear-sticky", cvs_resp_sticky },
1.1 jfb 187: };
188:
189:
1.5 jfb 190: static char *cvs_mt_stack[CVS_MTSTK_MAXDEPTH];
191: static u_int cvs_mtstk_depth = 0;
192:
1.8 jfb 193: static time_t cvs_modtime = 0;
1.7 jfb 194:
1.5 jfb 195:
1.1 jfb 196: #define CVS_NBREQ (sizeof(cvs_requests)/sizeof(cvs_requests[0]))
197: #define CVS_NBRESP (sizeof(cvs_responses)/sizeof(cvs_responses[0]))
198:
199: /* mask of requets supported by server */
200: static u_char cvs_server_validreq[CVS_REQ_MAX + 1];
201:
1.5 jfb 202: /*
203: * Local and remote directory used by the `Directory' request.
204: */
205: char cvs_ldir[MAXPATHLEN];
206: char cvs_rdir[MAXPATHLEN];
207:
208: char *cvs_fcksum = NULL;
209:
210: mode_t cvs_lastmode = 0;
211:
1.1 jfb 212:
213: /*
214: * cvs_req_getbyid()
215: *
216: */
217:
218: const char*
219: cvs_req_getbyid(int reqid)
220: {
221: u_int i;
222:
223: for (i = 0; i < CVS_NBREQ; i++)
224: if (cvs_requests[i].req_id == reqid)
225: return (cvs_requests[i].req_str);
226: return (NULL);
227: }
228:
229:
230: /*
231: * cvs_req_getbyname()
232: */
233:
234: int
235: cvs_req_getbyname(const char *rname)
236: {
237: u_int i;
238:
239: for (i = 0; i < CVS_NBREQ; i++)
240: if (strcmp(cvs_requests[i].req_str, rname) == 0)
241: return (cvs_requests[i].req_id);
242:
243: return (-1);
244: }
245:
246:
247: /*
248: * cvs_req_getvalid()
249: *
250: * Build a space-separated list of all the requests that this protocol
251: * implementation supports.
252: */
253:
254: char*
255: cvs_req_getvalid(void)
256: {
257: u_int i;
258: size_t len;
259: char *vrstr;
260: BUF *buf;
261:
262: buf = cvs_buf_alloc(512, BUF_AUTOEXT);
263: if (buf == NULL)
264: return (NULL);
265:
266: cvs_buf_set(buf, cvs_requests[0].req_str,
267: strlen(cvs_requests[0].req_str), 0);
268:
269: for (i = 1; i < CVS_NBREQ; i++) {
270: if ((cvs_buf_putc(buf, ' ') < 0) ||
271: (cvs_buf_append(buf, cvs_requests[i].req_str,
272: strlen(cvs_requests[i].req_str)) < 0)) {
273: cvs_buf_free(buf);
274: return (NULL);
275: }
276: }
277:
278: /* NUL-terminate */
279: if (cvs_buf_putc(buf, '\0') < 0) {
280: cvs_buf_free(buf);
281: return (NULL);
282: }
283:
284: len = cvs_buf_size(buf);
285: vrstr = (char *)malloc(len);
286:
287: cvs_buf_copy(buf, 0, vrstr, len);
288:
289: cvs_buf_free(buf);
290:
291: return (vrstr);
292: }
293:
294:
295: /*
296: * cvs_req_handle()
297: *
298: * Generic request handler dispatcher.
299: */
300:
301: int
302: cvs_req_handle(char *line)
303: {
304: return (0);
305: }
306:
307:
308: /*
309: * cvs_resp_getbyid()
310: *
311: */
312:
313: const char*
314: cvs_resp_getbyid(int respid)
315: {
316: u_int i;
317:
318: for (i = 0; i < CVS_NBREQ; i++)
319: if (cvs_responses[i].resp_id == respid)
320: return (cvs_responses[i].resp_str);
321: return (NULL);
322: }
323:
324:
325: /*
326: * cvs_resp_getbyname()
327: */
328:
329: int
330: cvs_resp_getbyname(const char *rname)
331: {
332: u_int i;
333:
334: for (i = 0; i < CVS_NBREQ; i++)
335: if (strcmp(cvs_responses[i].resp_str, rname) == 0)
336: return (cvs_responses[i].resp_id);
337:
338: return (-1);
339: }
340:
341:
342: /*
343: * cvs_resp_getvalid()
344: *
345: * Build a space-separated list of all the responses that this protocol
346: * implementation supports.
347: */
348:
349: char*
350: cvs_resp_getvalid(void)
351: {
352: u_int i;
353: size_t len;
354: char *vrstr;
355: BUF *buf;
356:
357: buf = cvs_buf_alloc(512, BUF_AUTOEXT);
358: if (buf == NULL)
359: return (NULL);
360:
361: cvs_buf_set(buf, cvs_responses[0].resp_str,
362: strlen(cvs_responses[0].resp_str), 0);
363:
364: for (i = 1; i < CVS_NBRESP; i++) {
365: if ((cvs_buf_putc(buf, ' ') < 0) ||
366: (cvs_buf_append(buf, cvs_responses[i].resp_str,
367: strlen(cvs_responses[i].resp_str)) < 0)) {
368: cvs_buf_free(buf);
369: return (NULL);
370: }
371: }
372:
373: /* NUL-terminate */
374: if (cvs_buf_putc(buf, '\0') < 0) {
375: cvs_buf_free(buf);
376: return (NULL);
377: }
378:
379: len = cvs_buf_size(buf);
380: vrstr = (char *)malloc(len);
381:
382: cvs_buf_copy(buf, 0, vrstr, len);
383: cvs_buf_free(buf);
384:
385: return (vrstr);
386: }
387:
388:
389: /*
390: * cvs_resp_handle()
391: *
392: * Generic response handler dispatcher. The handler expects the first line
393: * of the command as single argument.
394: * Returns the return value of the command on success, or -1 on failure.
395: */
396:
397: int
398: cvs_resp_handle(char *line)
399: {
400: u_int i;
401: size_t len;
402: char *cp, *cmd;
403:
404: cmd = line;
405:
406: cp = strchr(cmd, ' ');
407: if (cp != NULL)
408: *(cp++) = '\0';
409:
410: for (i = 0; i < CVS_NBRESP; i++) {
411: if (strcmp(cvs_responses[i].resp_str, cmd) == 0)
412: return (*cvs_responses[i].resp_hdlr)
413: (cvs_responses[i].resp_id, cp);
414: }
415:
416: /* unhandled */
417: return (-1);
418: }
419:
420:
421: /*
422: * cvs_resp_validreq()
423: *
424: * Handler for the `Valid-requests' response. The list of valid requests is
425: * split on spaces and each request's entry in the valid request array is set
426: * to 1 to indicate the validity.
427: * Returns 0 on success, or -1 on failure.
428: */
429:
430: static int
431: cvs_resp_validreq(int type, char *line)
432: {
433: int i;
434: char *sp, *ep;
435:
436: /* parse the requests */
437: sp = line;
438: do {
439: ep = strchr(sp, ' ');
440: if (ep != NULL)
441: *ep = '\0';
442:
443: i = cvs_req_getbyname(sp);
444: if (i != -1)
445: cvs_server_validreq[i] = 1;
446:
447: if (ep != NULL)
448: sp = ep + 1;
449: } while (ep != NULL);
450:
451: return (0);
452: }
453:
454:
455: /*
456: * cvs_resp_m()
457: *
1.4 jfb 458: * Handler for the `M', 'MT', `F' and `E' responses.
1.1 jfb 459: */
460:
461: static int
462: cvs_resp_m(int type, char *line)
463: {
1.5 jfb 464: char *cp;
1.1 jfb 465: FILE *stream;
466:
467: stream = NULL;
468:
469: switch (type) {
470: case CVS_RESP_F:
471: fflush(stderr);
472: return (0);
473: case CVS_RESP_M:
474: stream = stdout;
475: break;
476: case CVS_RESP_E:
477: stream = stderr;
478: break;
479: case CVS_RESP_MT:
1.5 jfb 480: if (*line == '+') {
481: if (cvs_mtstk_depth == CVS_MTSTK_MAXDEPTH) {
482: cvs_log(LP_ERR,
483: "MT scope stack has reached max depth");
484: return (-1);
485: }
1.6 jfb 486: cvs_mt_stack[cvs_mtstk_depth] = strdup(line + 1);
487: if (cvs_mt_stack[cvs_mtstk_depth] == NULL)
1.5 jfb 488: return (-1);
1.6 jfb 489: cvs_mtstk_depth++;
1.5 jfb 490: }
491: else if (*line == '-') {
492: if (cvs_mtstk_depth == 0) {
493: cvs_log(LP_ERR, "MT scope stack underflow");
494: return (-1);
495: }
1.6 jfb 496: else if (strcmp(line + 1,
497: cvs_mt_stack[cvs_mtstk_depth - 1]) != 0) {
1.5 jfb 498: cvs_log(LP_ERR, "mismatch in MT scope stack");
499: return (-1);
500: }
501: free(cvs_mt_stack[cvs_mtstk_depth--]);
502: }
503: else {
504: if (strcmp(line, "newline") == 0)
505: putc('\n', stdout);
506: else if (strncmp(line, "fname ", 6) == 0)
507: printf("%s", line + 6);
508: else {
509: /* assume text */
510: cp = strchr(line, ' ');
511: if (cp != NULL)
512: printf("%s", cp + 1);
513: }
514: }
515:
1.6 jfb 516: return (0);
1.1 jfb 517: case CVS_RESP_MBINARY:
1.5 jfb 518: cvs_log(LP_WARN, "Mbinary not supported in client yet");
1.1 jfb 519: break;
520: }
521:
522: fputs(line, stream);
523: fputc('\n', stream);
524:
525: return (0);
526: }
527:
528:
529: /*
530: * cvs_resp_ok()
531: *
532: * Handler for the `ok' response. This handler's job is to
533: */
534:
535: static int
536: cvs_resp_ok(int type, char *line)
537: {
538: return (1);
539: }
540:
541:
542: /*
543: * cvs_resp_error()
544: *
545: * Handler for the `error' response. This handler's job is to
546: */
547:
548: static int
549: cvs_resp_error(int type, char *line)
550: {
551: return (1);
552: }
553:
554:
555: /*
556: * cvs_resp_statdir()
557: *
558: * Handler for the `Clear-static-directory' and `Set-static-directory'
559: * responses.
560: */
561:
562: static int
563: cvs_resp_statdir(int type, char *line)
564: {
565: int fd;
1.6 jfb 566: char rpath[MAXPATHLEN], statpath[MAXPATHLEN];
567:
568: cvs_client_getln(rpath, sizeof(rpath));
1.1 jfb 569:
570: snprintf(statpath, sizeof(statpath), "%s/%s", line,
571: CVS_PATH_STATICENTRIES);
572:
573: if ((type == CVS_RESP_CLRSTATDIR) &&
1.5 jfb 574: (unlink(statpath) == -1) && (errno != ENOENT)) {
1.1 jfb 575: cvs_log(LP_ERRNO, "failed to unlink %s file",
576: CVS_PATH_STATICENTRIES);
577: return (-1);
578: }
579: else if (type == CVS_RESP_SETSTATDIR) {
1.5 jfb 580: fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0400);
1.1 jfb 581: if (fd == -1) {
582: cvs_log(LP_ERRNO, "failed to create %s file",
583: CVS_PATH_STATICENTRIES);
584: return (-1);
585: }
586: (void)close(fd);
1.5 jfb 587:
588: }
589:
590: return (0);
591: }
592:
593: /*
594: * cvs_resp_sticky()
595: *
596: * Handler for the `Clear-sticky' and `Set-sticky' responses.
597: */
598:
599: static int
600: cvs_resp_sticky(int type, char *line)
601: {
1.9 jfb 602: size_t len;
1.6 jfb 603: char rpath[MAXPATHLEN];
604: struct stat st;
1.7 jfb 605: CVSFILE *cf;
1.6 jfb 606:
1.9 jfb 607: /* remove trailing slash */
608: len = strlen(line);
609: if ((len > 0) && (line[len - 1] == '/'))
610: line[--len] = '\0';
611:
1.6 jfb 612: /* get the remote path */
613: cvs_client_getln(rpath, sizeof(rpath));
614:
615: /* if the directory doesn't exist, create it */
616: if (stat(line, &st) == -1) {
1.7 jfb 617: /* attempt to create it */
1.6 jfb 618: if (errno != ENOENT) {
619: cvs_log(LP_ERRNO, "failed to stat %s", line);
620: }
1.7 jfb 621: else {
622: cf = cvs_file_create(line, DT_DIR, 0755);
623: if (cf == NULL)
624: return (-1);
625: cf->cf_ddat->cd_repo = strdup(line);
626: cf->cf_ddat->cd_root = cvs_root;
627: cvs_mkadmin(cf, 0755);
628:
629: cf->cf_ddat->cd_root = NULL;
630: cvs_file_free(cf);
1.6 jfb 631: }
632: }
633:
1.5 jfb 634: if (type == CVS_RESP_CLRSTICKY) {
635: }
636: else if (type == CVS_RESP_SETSTICKY) {
1.1 jfb 637: }
638:
639: return (0);
640: }
641:
642:
643: /*
644: * cvs_resp_newentry()
645: *
646: * Handler for the `New-entry' response and `Checked-in' responses.
647: */
648:
649: static int
650: cvs_resp_newentry(int type, char *line)
651: {
652: char entbuf[128], path[MAXPATHLEN];
653: CVSENTRIES *entfile;
654:
1.2 jfb 655: snprintf(path, sizeof(path), "%s/" CVS_PATH_ENTRIES, line);
1.1 jfb 656:
657: /* get the remote path */
658: cvs_client_getln(entbuf, sizeof(entbuf));
659:
660: /* get the new Entries line */
661: if (cvs_client_getln(entbuf, sizeof(entbuf)) < 0)
662: return (-1);
663:
1.2 jfb 664: entfile = cvs_ent_open(path, O_WRONLY);
1.1 jfb 665: if (entfile == NULL)
666: return (-1);
1.3 jfb 667: cvs_ent_addln(entfile, entbuf);
1.1 jfb 668: cvs_ent_close(entfile);
669:
670: return (0);
671: }
672:
673:
674: /*
675: * cvs_resp_cksum()
676: *
677: * Handler for the `Checksum' response. We store the checksum received for
678: * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>.
679: * Upon next file reception, the handler checks to see if there is a stored
680: * checksum.
681: * The file handler must make sure that the checksums match and free the
682: * checksum buffer once it's done to indicate there is no further checksum.
683: */
684:
685: static int
686: cvs_resp_cksum(int type, char *line)
687: {
688: if (cvs_fcksum != NULL) {
689: cvs_log(LP_WARN, "unused checksum");
690: free(cvs_fcksum);
691: }
692:
693: cvs_fcksum = strdup(line);
694: if (cvs_fcksum == NULL) {
695: cvs_log(LP_ERRNO, "failed to copy checksum string");
696: return (-1);
697: }
698:
1.4 jfb 699: return (0);
700: }
701:
702:
703: /*
704: * cvs_resp_modtime()
705: *
706: * Handler for the `Mod-time' file update modifying response. The timestamp
707: * given is used to set the last modification time on the next file that
708: * will be received.
709: */
710:
711: static int
712: cvs_resp_modtime(int type, char *line)
713: {
1.8 jfb 714: int i;
715: long off;
716: char sign, mon[8], gmt[8], hr[4], min[4], *ep;
717: struct tm cvs_tm;
718:
719: memset(&cvs_tm, 0, sizeof(cvs_tm));
1.9 jfb 720: sscanf(line, "%d %3s %d %2d:%2d:%2d %5s", &cvs_tm.tm_mday, mon,
1.8 jfb 721: &cvs_tm.tm_year, &cvs_tm.tm_hour, &cvs_tm.tm_min,
722: &cvs_tm.tm_sec, gmt);
723: cvs_tm.tm_year -= 1900;
1.9 jfb 724: cvs_tm.tm_isdst = -1;
1.8 jfb 725:
726: if (*gmt == '-') {
1.9 jfb 727: sscanf(gmt, "%c%2s%2s", &sign, hr, min);
1.8 jfb 728: cvs_tm.tm_gmtoff = strtol(hr, &ep, 10);
729: if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
730: (cvs_tm.tm_gmtoff == LONG_MAX) ||
731: (*ep != '\0')) {
732: cvs_log(LP_ERR,
733: "parse error in GMT hours specification `%s'", hr);
734: cvs_tm.tm_gmtoff = 0;
735: }
736: else {
737: /* get seconds */
738: cvs_tm.tm_gmtoff *= 3600;
1.7 jfb 739:
1.8 jfb 740: /* add the minutes */
741: off = strtol(min, &ep, 10);
742: if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
743: (cvs_tm.tm_gmtoff == LONG_MAX) ||
744: (*ep != '\0')) {
745: cvs_log(LP_ERR,
746: "parse error in GMT minutes "
747: "specification `%s'", min);
748: }
749: else
750: cvs_tm.tm_gmtoff += off * 60;
751: }
752: }
753: if (sign == '-')
754: cvs_tm.tm_gmtoff = -cvs_tm.tm_gmtoff;
1.7 jfb 755:
1.8 jfb 756: for (i = 0; i < (int)(sizeof(cvs_months)/sizeof(cvs_months[0])); i++) {
757: if (strcmp(cvs_months[i], mon) == 0) {
758: cvs_tm.tm_mon = i;
759: break;
760: }
1.7 jfb 761: }
762:
1.8 jfb 763: cvs_modtime = mktime(&cvs_tm);
1.1 jfb 764: return (0);
765: }
766:
767:
768: /*
769: * cvs_resp_updated()
770: *
1.6 jfb 771: * Handler for the `Updated' and `Created' responses.
1.1 jfb 772: */
773:
774: static int
775: cvs_resp_updated(int type, char *line)
776: {
1.7 jfb 777: size_t len;
778: char tbuf[32], path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN];
779: CVSENTRIES *ef;
1.6 jfb 780: struct cvs_ent *ep;
1.1 jfb 781:
782: if (type == CVS_RESP_CREATED) {
1.6 jfb 783: /* read the remote path of the file */
784: cvs_client_getln(path, sizeof(path));
785:
786: /* read the new entry */
787: cvs_client_getln(path, sizeof(path));
788: ep = cvs_ent_parse(path);
789: if (ep == NULL)
790: return (-1);
1.7 jfb 791:
792: /* set the timestamp as the last one received from Mod-time */
1.8 jfb 793: ep->ce_timestamp = ctime_r(&cvs_modtime, tbuf);
1.7 jfb 794: len = strlen(tbuf);
795: if ((len > 0) && (tbuf[len - 1] == '\n'))
796: tbuf[--len] = '\0';
797:
798: ef = cvs_ent_open(line, O_WRONLY);
799: if (ef == NULL)
800: return (-1);
801:
802: cvs_ent_add(ef, ep);
803: cvs_ent_close(ef);
1.1 jfb 804: }
805: else if (type == CVS_RESP_UPDEXIST) {
806: }
807: else if (type == CVS_RESP_UPDATED) {
808: }
809:
1.6 jfb 810: snprintf(path, sizeof(path), "%s%s", line, ep->ce_name);
811: if (cvs_recvfile(path) < 0) {
1.1 jfb 812: return (-1);
813: }
814:
815: /* now see if there is a checksum */
1.6 jfb 816: if (cvs_fcksum != NULL) {
817: if (cvs_cksum(line, cksum_buf, sizeof(cksum_buf)) < 0) {
818: }
819:
820: if (strcmp(cksum_buf, cvs_fcksum) != 0) {
821: cvs_log(LP_ERR, "checksum error on received file");
822: (void)unlink(line);
823: }
1.1 jfb 824:
1.6 jfb 825: free(cvs_fcksum);
826: cvs_fcksum = NULL;
1.1 jfb 827: }
828:
829: return (0);
830: }
831:
832:
833: /*
834: * cvs_resp_removed()
835: *
836: * Handler for the `Updated' response.
837: */
838:
839: static int
840: cvs_resp_removed(int type, char *line)
841: {
842: return (0);
843: }
844:
845:
846: /*
847: * cvs_resp_mode()
848: *
849: * Handler for the `Mode' response.
850: */
851:
852: static int
853: cvs_resp_mode(int type, char *line)
854: {
855: if (cvs_strtomode(line, &cvs_lastmode) < 0) {
856: return (-1);
857: }
1.5 jfb 858: return (0);
859: }
860:
861:
862: /*
863: * cvs_resp_modxpand()
864: *
865: * Handler for the `Module-expansion' response.
866: */
867:
868: static int
869: cvs_resp_modxpand(int type, char *line)
870: {
1.1 jfb 871: return (0);
872: }
873:
874:
875: /*
876: * cvs_sendfile()
877: *
878: * Send the mode and size of a file followed by the file's contents.
879: * Returns 0 on success, or -1 on failure.
880: */
881:
882: int
883: cvs_sendfile(const char *path)
884: {
885: int fd;
886: ssize_t ret;
887: char buf[4096];
888: struct stat st;
889:
890: if (stat(path, &st) == -1) {
891: cvs_log(LP_ERRNO, "failed to stat `%s'", path);
892: return (-1);
893: }
894:
895: fd = open(path, O_RDONLY, 0);
896: if (fd == -1) {
897: return (-1);
898: }
899:
900: if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
901: return (-1);
902:
903: cvs_client_sendln(buf);
904: snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
905: cvs_client_sendln(buf);
906:
907: while ((ret = read(fd, buf, sizeof(buf))) != 0) {
908: if (ret == -1) {
909: cvs_log(LP_ERRNO, "failed to read file `%s'", path);
910: return (-1);
911: }
912:
913: cvs_client_sendraw(buf, (size_t)ret);
914:
915: }
916:
917: (void)close(fd);
918:
919: return (0);
920: }
921:
922:
923: /*
924: * cvs_recvfile()
925: *
926: * Receive the mode and size of a file followed the file's contents and
927: * create or update the file whose path is <path> with the received
928: * information.
929: */
930:
931: int
932: cvs_recvfile(const char *path)
933: {
934: int fd;
935: mode_t mode;
936: size_t len;
937: ssize_t ret;
938: off_t fsz, cnt;
939: char buf[4096], *ep;
940:
941: if ((cvs_client_getln(buf, sizeof(buf)) < 0) ||
942: (cvs_strtomode(buf, &mode) < 0)) {
943: return (-1);
944: }
945:
946: cvs_client_getln(buf, sizeof(buf));
947:
948: fsz = (off_t)strtol(buf, &ep, 10);
949: if (*ep != '\0') {
950: cvs_log(LP_ERR, "parse error in file size transmission");
951: return (-1);
952: }
953:
1.6 jfb 954: fd = open(path, O_WRONLY|O_CREAT, mode);
1.1 jfb 955: if (fd == -1) {
956: cvs_log(LP_ERRNO, "failed to open `%s'", path);
957: return (-1);
958: }
959:
960: cnt = 0;
961: do {
962: len = MIN(sizeof(buf), (size_t)(fsz - cnt));
1.6 jfb 963: if (len == 0)
964: break;
1.1 jfb 965: ret = cvs_client_recvraw(buf, len);
966: if (ret == -1) {
967: (void)close(fd);
968: (void)unlink(path);
969: return (-1);
970: }
971:
972: if (write(fd, buf, (size_t)ret) == -1) {
973: cvs_log(LP_ERRNO,
974: "failed to write contents to file `%s'", path);
975: (void)close(fd);
976: (void)unlink(path);
977: return (-1);
978: }
979:
980: cnt += (off_t)ret;
981: } while (cnt < fsz);
982:
983: (void)close(fd);
984:
985: return (0);
986: }
1.12 ! jfb 987:
! 988:
! 989: #ifdef notyet
! 990: /*
! 991: * cvs_sendreq()
! 992: *
! 993: * Send a request to the server of type <rid>, with optional arguments
! 994: * contained in <arg>, which should not be terminated by a newline.
! 995: * Returns 0 on success, or -1 on failure.
! 996: */
! 997:
! 998: int
! 999: cvs_sendreq(struct cvs_root *root, u_int rid, const char *arg)
! 1000: {
! 1001: int ret;
! 1002: size_t len;
! 1003: struct cvs_req *reqp;
! 1004:
! 1005: if (root->cr_srvin == NULL) {
! 1006: cvs_log(LP_ERR, "cannot send request: Not connected");
! 1007: return (-1);
! 1008: }
! 1009:
! 1010: reqp = cvs_req_getbyid(rid);
! 1011: if (reqp == NULL) {
! 1012: cvs_log(LP_ERR, "unsupported request type %u", rid);
! 1013: return (-1);
! 1014: }
! 1015:
! 1016: snprintf(cvs_client_buf, sizeof(cvs_client_buf), "%s %s\n",
! 1017: reqp->req_str, (arg == NULL) ? "" : " ", (arg == NULL) ? "" : arg);
! 1018:
! 1019: if (cvs_server_inlog != NULL)
! 1020: fputs(cvs_client_buf, cvs_server_inlog);
! 1021:
! 1022: ret = fputs(cvs_client_buf, root->cr_srvin);
! 1023: if (ret == EOF) {
! 1024: cvs_log(LP_ERRNO, "failed to send request to server");
! 1025: return (-1);
! 1026: }
! 1027:
! 1028: if (reqp->req_flags & CVS_REQF_RESP)
! 1029: ret = cvs_getresp(root);
! 1030:
! 1031: return (0);
! 1032: }
! 1033:
! 1034:
! 1035: /*
! 1036: * cvs_getresp()
! 1037: *
! 1038: * Get a response from the server. This call will actually read and handle
! 1039: * responses from the server until one of the response handlers returns
! 1040: * non-zero (either an error occured or the end of the response was reached).
! 1041: * Returns the number of handled commands on success, or -1 on failure.
! 1042: */
! 1043:
! 1044: int
! 1045: cvs_getresp(struct cvs_root *root)
! 1046: {
! 1047: int nbcmd;
! 1048:
! 1049: nbcmd = 0;
! 1050:
! 1051: do {
! 1052: /* wait for incoming data */
! 1053: if (fgets(cvs_client_buf, sizeof(cvs_client_buf),
! 1054: root->cr_srvout) == NULL) {
! 1055: if (feof(root->cr_srvout))
! 1056: return (0);
! 1057: cvs_log(LP_ERRNO,
! 1058: "failed to read response from server");
! 1059: return (-1);
! 1060: }
! 1061:
! 1062: if (cvs_server_outlog != NULL)
! 1063: fputs(cvs_client_buf, cvs_server_outlog);
! 1064:
! 1065: if ((len = strlen(cvs_client_buf)) != 0) {
! 1066: if (cvs_client_buf[len - 1] != '\n') {
! 1067: /* truncated line */
! 1068: }
! 1069: else
! 1070: cvs_client_buf[--len] = '\0';
! 1071: }
! 1072:
! 1073: ret = cvs_resp_handle(cvs_client_buf);
! 1074: nbcmd++;
! 1075: } while (ret == 0);
! 1076:
! 1077: if (ret > 0)
! 1078: ret = nbcmd;
! 1079: return (ret);
! 1080: }
! 1081:
! 1082:
! 1083: /*
! 1084: * cvs_sendresp()
! 1085: *
! 1086: * Send a response to the client of type <rid>, with optional arguments
! 1087: * contained in <arg>, which should not be terminated by a newline.
! 1088: * Returns 0 on success, or -1 on failure.
! 1089: */
! 1090:
! 1091: int
! 1092: cvs_sendresp(u_int rid, const char *arg)
! 1093: {
! 1094: int ret;
! 1095: size_t len;
! 1096: const char *resp;
! 1097:
! 1098: resp = cvs_resp_getbyid(rid);
! 1099: if (reqp == NULL) {
! 1100: cvs_log(LP_ERR, "unsupported response type %u", rid);
! 1101: return (-1);
! 1102: }
! 1103:
! 1104: snprintf(cvs_client_buf, sizeof(cvs_client_buf), "%s %s\n", resp,
! 1105: (arg == NULL) ? "" : arg);
! 1106:
! 1107: ret = fputs(resp, stdout);
! 1108: if (ret == EOF) {
! 1109: cvs_log(LP_ERRNO, "failed to send response to client");
! 1110: }
! 1111: else {
! 1112: if (arg != NULL)
! 1113: ret = fprintf(stdout, " %s", arg);
! 1114: putc('\n', stdout);
! 1115: }
! 1116: return (0);
! 1117: }
! 1118:
! 1119:
! 1120: /*
! 1121: * cvs_getreq()
! 1122: *
! 1123: * Get a request from the client.
! 1124: */
! 1125:
! 1126: int
! 1127: cvs_getreq(void)
! 1128: {
! 1129: int nbcmd;
! 1130:
! 1131: nbcmd = 0;
! 1132:
! 1133: do {
! 1134: /* wait for incoming data */
! 1135: if (fgets(cvs_client_buf, sizeof(cvs_client_buf),
! 1136: stdin) == NULL) {
! 1137: if (feof(stdin))
! 1138: return (0);
! 1139: cvs_log(LP_ERRNO,
! 1140: "failed to read request from client");
! 1141: return (-1);
! 1142: }
! 1143:
! 1144: if ((len = strlen(cvs_client_buf)) != 0) {
! 1145: if (cvs_client_buf[len - 1] != '\n') {
! 1146: /* truncated line */
! 1147: }
! 1148: else
! 1149: cvs_client_buf[--len] = '\0';
! 1150: }
! 1151:
! 1152: ret = cvs_resp_handle(cvs_client_buf);
! 1153: } while (ret == 0);
! 1154: }
! 1155:
! 1156:
! 1157: /*
! 1158: * cvs_connect()
! 1159: *
! 1160: * Open a client connection to the cvs server whose address is given in
! 1161: * the <root> variable. The method used to connect depends on the
! 1162: * setting of the CVS_RSH variable.
! 1163: * Returns 0 on success, or -1 on failure.
! 1164: */
! 1165:
! 1166: int
! 1167: cvs_connect(struct cvsroot *root)
! 1168: {
! 1169: int i, argc, infd[2], outfd[2];
! 1170: pid_t pid;
! 1171: char *argv[16], *cvs_server_cmd;
! 1172:
! 1173: if (pipe(infd) == -1) {
! 1174: cvs_log(LP_ERRNO,
! 1175: "failed to create input pipe for client connection");
! 1176: return (-1);
! 1177: }
! 1178:
! 1179: if (pipe(outfd) == -1) {
! 1180: cvs_log(LP_ERRNO,
! 1181: "failed to create output pipe for client connection");
! 1182: (void)close(infd[0]);
! 1183: (void)close(infd[1]);
! 1184: return (-1);
! 1185: }
! 1186:
! 1187: pid = fork();
! 1188: if (pid == -1) {
! 1189: cvs_log(LP_ERRNO, "failed to fork for cvs server connection");
! 1190: return (-1);
! 1191: }
! 1192: if (pid == 0) {
! 1193: if ((dup2(infd[0], STDIN_FILENO) == -1) ||
! 1194: (dup2(outfd[1], STDOUT_FILENO) == -1)) {
! 1195: cvs_log(LP_ERRNO,
! 1196: "failed to setup standard streams for cvs server");
! 1197: return (-1);
! 1198: }
! 1199: (void)close(infd[1]);
! 1200: (void)close(outfd[0]);
! 1201:
! 1202: argc = 0;
! 1203: argv[argc++] = cvs_rsh;
! 1204:
! 1205: if (root->cr_user != NULL) {
! 1206: argv[argc++] = "-l";
! 1207: argv[argc++] = root->cr_user;
! 1208: }
! 1209:
! 1210:
! 1211: cvs_server_cmd = getenv("CVS_SERVER");
! 1212: if (cvs_server_cmd == NULL)
! 1213: cvs_server_cmd = "cvs";
! 1214:
! 1215: argv[argc++] = root->cr_host;
! 1216: argv[argc++] = cvs_server_cmd;
! 1217: argv[argc++] = "server";
! 1218: argv[argc] = NULL;
! 1219:
! 1220: for (i = 0; i < argc; i++)
! 1221: printf("argv[%d] = `%s'\n", i, argv[i]);
! 1222:
! 1223: execvp(argv[0], argv);
! 1224: cvs_log(LP_ERRNO, "failed to exec");
! 1225: exit(EX_OSERR);
! 1226: }
! 1227:
! 1228: /* we are the parent */
! 1229: cvs_server_infd = infd[1];
! 1230: cvs_server_outfd = outfd[0];
! 1231:
! 1232: cvs_server_in = fdopen(cvs_server_infd, "w");
! 1233: if (cvs_server_in == NULL) {
! 1234: cvs_log(LP_ERRNO, "failed to create pipe stream");
! 1235: return (-1);
! 1236: }
! 1237:
! 1238: cvs_server_out = fdopen(cvs_server_outfd, "r");
! 1239: if (cvs_server_out == NULL) {
! 1240: cvs_log(LP_ERRNO, "failed to create pipe stream");
! 1241: return (-1);
! 1242: }
! 1243: root->cr_srvin = cvs_server_in;
! 1244: root->cr_srvout = cvs_server_out;
! 1245:
! 1246: /* make the streams line-buffered */
! 1247: setvbuf(cvs_server_in, NULL, _IOLBF, 0);
! 1248: setvbuf(cvs_server_out, NULL, _IOLBF, 0);
! 1249:
! 1250: (void)close(infd[0]);
! 1251: (void)close(outfd[1]);
! 1252:
! 1253: cvs_client_initlog();
! 1254:
! 1255: cvs_client_sendinfo();
! 1256:
! 1257: #ifdef CVS_ZLIB
! 1258: /* if compression was requested, initialize it */
! 1259: #endif
! 1260:
! 1261: return (0);
! 1262: }
! 1263:
! 1264:
! 1265: /*
! 1266: * cvs_disconnect()
! 1267: *
! 1268: * Disconnect from the cvs server.
! 1269: */
! 1270:
! 1271: void
! 1272: cvs_disconnect(struct cvsroot *root)
! 1273: {
! 1274: cvs_log(LP_DEBUG, "closing client connection");
! 1275: if (root->cr_srvin != NULL) {
! 1276: (void)fclose(root->cr_srvin);
! 1277: root->cr_srvin = NULL;
! 1278: }
! 1279: if (root->cr_srvout != NULL) {
! 1280: (void)fclose(root->cr_srvout);
! 1281: root->cr_srvout = NULL;
! 1282: }
! 1283:
! 1284: if (cvs_server_inlog != NULL)
! 1285: fclose(cvs_server_inlog);
! 1286: if (cvs_server_outlog != NULL)
! 1287: fclose(cvs_server_outlog);
! 1288: }
! 1289:
! 1290:
! 1291: /*
! 1292: * cvs_sendln()
! 1293: *
! 1294: * Send a single line <line> string to the server. The line is sent as is,
! 1295: * without any modifications.
! 1296: * Returns 0 on success, or -1 on failure.
! 1297: */
! 1298:
! 1299: int
! 1300: cvs_sendln(struct cvsroot *root, const char *line)
! 1301: {
! 1302: int nl;
! 1303: size_t len;
! 1304:
! 1305: nl = 0;
! 1306: len = strlen(line);
! 1307:
! 1308: if ((len > 0) && (line[len - 1] != '\n'))
! 1309: nl = 1;
! 1310:
! 1311: if (cvs_server_inlog != NULL) {
! 1312: fputs(line, cvs_server_inlog);
! 1313: if (nl)
! 1314: fputc('\n', cvs_server_inlog);
! 1315: }
! 1316: fputs(line, root->cr_srvin);
! 1317: if (nl)
! 1318: fputc('\n', root->cr_srvin);
! 1319:
! 1320: return (0);
! 1321: }
! 1322:
! 1323:
! 1324: /*
! 1325: * cvs_client_sendraw()
! 1326: *
! 1327: * Send the first <len> bytes from the buffer <src> to the server.
! 1328: */
! 1329:
! 1330: int
! 1331: cvs_client_sendraw(const void *src, size_t len)
! 1332: {
! 1333: if (cvs_server_inlog != NULL)
! 1334: fwrite(src, sizeof(char), len, cvs_server_inlog);
! 1335: if (fwrite(src, sizeof(char), len, cvs_server_in) < len) {
! 1336: return (-1);
! 1337: }
! 1338:
! 1339: return (0);
! 1340: }
! 1341:
! 1342:
! 1343: /*
! 1344: * cvs_client_recvraw()
! 1345: *
! 1346: * Receive the first <len> bytes from the buffer <src> to the server.
! 1347: */
! 1348:
! 1349: ssize_t
! 1350: cvs_client_recvraw(void *dst, size_t len)
! 1351: {
! 1352: size_t ret;
! 1353:
! 1354: ret = fread(dst, sizeof(char), len, cvs_server_out);
! 1355: if (ret == 0)
! 1356: return (-1);
! 1357: if (cvs_server_outlog != NULL)
! 1358: fwrite(dst, sizeof(char), len, cvs_server_outlog);
! 1359: return (ssize_t)ret;
! 1360: }
! 1361:
! 1362:
! 1363: /*
! 1364: * cvs_client_getln()
! 1365: *
! 1366: * Get a line from the server's output and store it in <lbuf>. The terminating
! 1367: * newline character is stripped from the result.
! 1368: */
! 1369:
! 1370: int
! 1371: cvs_client_getln(char *lbuf, size_t len)
! 1372: {
! 1373: size_t rlen;
! 1374:
! 1375: if (fgets(lbuf, len, cvs_server_out) == NULL) {
! 1376: if (ferror(cvs_server_out)) {
! 1377: cvs_log(LP_ERRNO, "failed to read line from server");
! 1378: return (-1);
! 1379: }
! 1380:
! 1381: if (feof(cvs_server_out))
! 1382: *lbuf = '\0';
! 1383: }
! 1384:
! 1385: if (cvs_server_outlog != NULL)
! 1386: fputs(lbuf, cvs_server_outlog);
! 1387:
! 1388: rlen = strlen(lbuf);
! 1389: if ((rlen > 0) && (lbuf[rlen - 1] == '\n'))
! 1390: lbuf[--rlen] = '\0';
! 1391:
! 1392: return (0);
! 1393: }
! 1394:
! 1395:
! 1396: /*
! 1397: * cvs_client_sendinfo()
! 1398: *
! 1399: * Initialize the connection status by first requesting the list of
! 1400: * supported requests from the server. Then, we send the CVSROOT variable
! 1401: * with the `Root' request.
! 1402: * Returns 0 on success, or -1 on failure.
! 1403: */
! 1404:
! 1405: static int
! 1406: cvs_client_sendinfo(void)
! 1407: {
! 1408: char *vresp;
! 1409: /*
! 1410: * First, send the server the list of valid responses, then ask
! 1411: * for valid requests
! 1412: */
! 1413:
! 1414: vresp = cvs_resp_getvalid();
! 1415: if (vresp == NULL) {
! 1416: cvs_log(LP_ERR, "can't generate list of valid responses");
! 1417: return (-1);
! 1418: }
! 1419:
! 1420: if (cvs_client_sendreq(CVS_REQ_VALIDRESP, vresp, 0) < 0) {
! 1421: }
! 1422: free(vresp);
! 1423:
! 1424: if (cvs_client_sendreq(CVS_REQ_VALIDREQ, NULL, 1) < 0) {
! 1425: cvs_log(LP_ERR, "failed to get valid requests from server");
! 1426: return (-1);
! 1427: }
! 1428:
! 1429: /* now share our global options with the server */
! 1430: if (verbosity == 1)
! 1431: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-q", 0);
! 1432: else if (verbosity == 0)
! 1433: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-Q", 0);
! 1434:
! 1435: if (cvs_nolog)
! 1436: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-l", 0);
! 1437: if (cvs_readonly)
! 1438: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-r", 0);
! 1439: if (cvs_trace)
! 1440: cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-t", 0);
! 1441:
! 1442: /* now send the CVSROOT to the server */
! 1443: if (cvs_client_sendreq(CVS_REQ_ROOT, cvs_root->cr_dir, 0) < 0)
! 1444: return (-1);
! 1445:
! 1446: /* not sure why, but we have to send this */
! 1447: if (cvs_client_sendreq(CVS_REQ_USEUNCHANGED, NULL, 0) < 0)
! 1448: return (-1);
! 1449:
! 1450: return (0);
! 1451: }
! 1452:
! 1453:
! 1454: /*
! 1455: * cvs_client_senddir()
! 1456: *
! 1457: * Send a `Directory' request along with the 2 paths that follow it.
! 1458: */
! 1459:
! 1460: int
! 1461: cvs_client_senddir(const char *dir)
! 1462: {
! 1463: char repo[MAXPATHLEN], buf[MAXPATHLEN];
! 1464:
! 1465: if (cvs_readrepo(dir, repo, sizeof(repo)) < 0) {
! 1466: repo[0] = '\0';
! 1467: strlcpy(buf, cvs_root->cr_dir, sizeof(buf));
! 1468: }
! 1469: else {
! 1470: snprintf(buf, sizeof(buf), "%s/%s", cvs_root->cr_dir, repo);
! 1471: }
! 1472:
! 1473: if ((cvs_client_sendreq(CVS_REQ_DIRECTORY, dir, 0) < 0) ||
! 1474: (cvs_client_sendln(buf) < 0))
! 1475: return (-1);
! 1476:
! 1477: return (0);
! 1478: }
! 1479:
! 1480:
! 1481: /*
! 1482: * cvs_client_sendarg()
! 1483: *
! 1484: * Send the argument <arg> to the server. The argument <append> is used to
! 1485: * determine if the argument should be simply appended to the last argument
! 1486: * sent or if it should be created as a new argument (0).
! 1487: */
! 1488:
! 1489: int
! 1490: cvs_client_sendarg(const char *arg, int append)
! 1491: {
! 1492: return cvs_client_sendreq(((append == 0) ?
! 1493: CVS_REQ_ARGUMENT : CVS_REQ_ARGUMENTX), arg, 0);
! 1494: }
! 1495:
! 1496:
! 1497: /*
! 1498: * cvs_client_sendentry()
! 1499: *
! 1500: * Send an `Entry' request to the server along with the mandatory fields from
! 1501: * the CVS entry <ent> (which are the name and revision).
! 1502: */
! 1503:
! 1504: int
! 1505: cvs_client_sendentry(const struct cvs_ent *ent)
! 1506: {
! 1507: char ebuf[128], numbuf[64];
! 1508:
! 1509: snprintf(ebuf, sizeof(ebuf), "/%s/%s///", ent->ce_name,
! 1510: rcsnum_tostr(ent->ce_rev, numbuf, sizeof(numbuf)));
! 1511:
! 1512: return cvs_client_sendreq(CVS_REQ_ENTRY, ebuf, 0);
! 1513: }
! 1514:
! 1515:
! 1516: /*
! 1517: * cvs_client_initlog()
! 1518: *
! 1519: * Initialize protocol logging if the CVS_CLIENT_LOG environment variable is
! 1520: * set. In this case, the variable's value is used as a path to which the
! 1521: * appropriate suffix is added (".in" for server input and ".out" for server
! 1522: * output.
! 1523: * Returns 0 on success, or -1 on failure.
! 1524: */
! 1525:
! 1526: static int
! 1527: cvs_client_initlog(void)
! 1528: {
! 1529: char *env, fpath[MAXPATHLEN];
! 1530:
! 1531: env = getenv("CVS_CLIENT_LOG");
! 1532: if (env == NULL)
! 1533: return (0);
! 1534:
! 1535: strlcpy(fpath, env, sizeof(fpath));
! 1536: strlcat(fpath, ".in", sizeof(fpath));
! 1537: cvs_server_inlog = fopen(fpath, "w");
! 1538: if (cvs_server_inlog == NULL) {
! 1539: cvs_log(LP_ERRNO, "failed to open server input log `%s'",
! 1540: fpath);
! 1541: return (-1);
! 1542: }
! 1543:
! 1544: strlcpy(fpath, env, sizeof(fpath));
! 1545: strlcat(fpath, ".out", sizeof(fpath));
! 1546: cvs_server_outlog = fopen(fpath, "w");
! 1547: if (cvs_server_outlog == NULL) {
! 1548: cvs_log(LP_ERRNO, "failed to open server output log `%s'",
! 1549: fpath);
! 1550: return (-1);
! 1551: }
! 1552:
! 1553: /* make the streams line-buffered */
! 1554: setvbuf(cvs_server_inlog, NULL, _IOLBF, 0);
! 1555: setvbuf(cvs_server_outlog, NULL, _IOLBF, 0);
! 1556:
! 1557: return (0);
! 1558: }
! 1559:
! 1560:
! 1561: /*
! 1562: * cvs_client_sendfile()
! 1563: *
! 1564: */
! 1565:
! 1566: int
! 1567: cvs_client_sendfile(const char *path)
! 1568: {
! 1569: int fd;
! 1570: ssize_t ret;
! 1571: char buf[4096];
! 1572: struct stat st;
! 1573:
! 1574: if (stat(path, &st) == -1) {
! 1575: cvs_log(LP_ERRNO, "failed to stat `%s'", path);
! 1576: return (-1);
! 1577: }
! 1578:
! 1579: fd = open(path, O_RDONLY, 0);
! 1580: if (fd == -1) {
! 1581: return (-1);
! 1582: }
! 1583:
! 1584: if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
! 1585: return (-1);
! 1586:
! 1587: cvs_client_sendln(buf);
! 1588: snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
! 1589: cvs_client_sendln(buf);
! 1590:
! 1591: while ((ret = read(fd, buf, sizeof(buf))) != 0) {
! 1592: if (ret == -1) {
! 1593: cvs_log(LP_ERRNO, "failed to read file `%s'", path);
! 1594: return (-1);
! 1595: }
! 1596:
! 1597: cvs_client_sendraw(buf, (size_t)ret);
! 1598:
! 1599: }
! 1600:
! 1601: (void)close(fd);
! 1602:
! 1603: return (0);
! 1604: }
! 1605: #endif