xref: /openbsd-src/usr.bin/cvs/client.c (revision 431378d1a7a36c77c1b34ffa43b6933d2ecc970a)
1*431378d1Snaddy /*	$OpenBSD: client.c,v 1.129 2020/10/19 19:51:20 naddy Exp $	*/
29fac60a5Sjoris /*
39fac60a5Sjoris  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
49fac60a5Sjoris  *
59fac60a5Sjoris  * Permission to use, copy, modify, and distribute this software for any
69fac60a5Sjoris  * purpose with or without fee is hereby granted, provided that the above
79fac60a5Sjoris  * copyright notice and this permission notice appear in all copies.
89fac60a5Sjoris  *
99fac60a5Sjoris  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
109fac60a5Sjoris  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119fac60a5Sjoris  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
129fac60a5Sjoris  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139fac60a5Sjoris  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149fac60a5Sjoris  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159fac60a5Sjoris  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169fac60a5Sjoris  */
179fac60a5Sjoris 
18b9fc9a72Sderaadt #include <sys/types.h>
191f8531bdSotto #include <sys/dirent.h>
201f8531bdSotto #include <sys/stat.h>
21d3362ad9Stobias #include <sys/time.h>
221f8531bdSotto 
231f8531bdSotto #include <errno.h>
241f8531bdSotto #include <fcntl.h>
251f8531bdSotto #include <libgen.h>
261f8531bdSotto #include <limits.h>
271f8531bdSotto #include <pwd.h>
281f8531bdSotto #include <stdlib.h>
291f8531bdSotto #include <string.h>
306534056aStobias #include <time.h>
311f8531bdSotto #include <unistd.h>
329fac60a5Sjoris 
339fac60a5Sjoris #include "cvs.h"
349fac60a5Sjoris #include "remote.h"
359fac60a5Sjoris 
369fac60a5Sjoris struct cvs_req cvs_requests[] = {
379fac60a5Sjoris 	/* this is what our client will use, the server should support it */
389fac60a5Sjoris 	{ "Root",		1,	cvs_server_root, REQ_NEEDED },
399fac60a5Sjoris 	{ "Valid-responses",	1,	cvs_server_validresp, REQ_NEEDED },
409fac60a5Sjoris 	{ "valid-requests",	1,	cvs_server_validreq, REQ_NEEDED },
419fac60a5Sjoris 	{ "Directory",		0,	cvs_server_directory, REQ_NEEDED },
427917ff23Sjoris 	{ "Static-directory",	0,	cvs_server_static_directory,
437917ff23Sjoris 	    REQ_NEEDED | REQ_NEEDDIR },
447917ff23Sjoris 	{ "Sticky",		0,	cvs_server_sticky,
457917ff23Sjoris 	    REQ_NEEDED | REQ_NEEDDIR },
467917ff23Sjoris 	{ "Entry",		0,	cvs_server_entry,
477917ff23Sjoris 	    REQ_NEEDED | REQ_NEEDDIR },
487917ff23Sjoris 	{ "Modified",		0,	cvs_server_modified,
497917ff23Sjoris 	    REQ_NEEDED | REQ_NEEDDIR },
509fac60a5Sjoris 	{ "UseUnchanged",	0,	cvs_server_useunchanged, REQ_NEEDED },
517917ff23Sjoris 	{ "Unchanged",		0,	cvs_server_unchanged,
527917ff23Sjoris 	    REQ_NEEDED | REQ_NEEDDIR },
539fac60a5Sjoris 	{ "Questionable",	0,	cvs_server_questionable, REQ_NEEDED },
549fac60a5Sjoris 	{ "Argument",		0,	cvs_server_argument, REQ_NEEDED },
5590f82a7aSxsa 	{ "Argumentx",		0,	cvs_server_argumentx, REQ_NEEDED },
5690f82a7aSxsa 	{ "Global_option",	0,	cvs_server_globalopt, REQ_NEEDED },
57b4a9add8Sxsa 	{ "Set",		0,	cvs_server_set, REQ_NEEDED },
585ede44f5Sjoris 	{ "expand-modules",	0,	cvs_server_exp_modules, 0 },
599fac60a5Sjoris 
609fac60a5Sjoris 	/*
619fac60a5Sjoris 	 * used to tell the server what is going on in our
629fac60a5Sjoris 	 * working copy, unsupported until we are told otherwise
639fac60a5Sjoris 	 */
649fac60a5Sjoris 	{ "Max-dotdot",			0,	NULL, 0 },
659fac60a5Sjoris 	{ "Checkin-prog",		0,	NULL, 0 },
669fac60a5Sjoris 	{ "Update-prog",		0,	NULL, 0 },
679fac60a5Sjoris 	{ "Kopt",			0,	NULL, 0 },
689fac60a5Sjoris 	{ "Checkin-time",		0,	NULL, 0 },
699fac60a5Sjoris 	{ "Is-modified",		0,	NULL, 0 },
709fac60a5Sjoris 	{ "Notify",			0,	NULL, 0 },
719fac60a5Sjoris 	{ "Case",			0,	NULL, 0 },
729fac60a5Sjoris 	{ "Gzip-stream",		0,	NULL, 0 },
7393bcb4acSxsa 	{ "wrapper-sendme-rcsOptions",	0,	NULL, 0 },
749fac60a5Sjoris 	{ "Kerberos-encrypt",		0,	NULL, 0 },
759fac60a5Sjoris 	{ "Gssapi-encrypt",		0,	NULL, 0 },
769fac60a5Sjoris 	{ "Gssapi-authenticate",	0,	NULL, 0 },
779fac60a5Sjoris 
789fac60a5Sjoris 	/* commands that might be supported */
79a45a22ceSxsa 	{ "ci",			0,	cvs_server_commit,	REQ_NEEDDIR },
80a45a22ceSxsa 	{ "co",			0,	cvs_server_checkout,	REQ_NEEDDIR },
81a45a22ceSxsa 	{ "update",		0,	cvs_server_update,	REQ_NEEDDIR },
827917ff23Sjoris 	{ "diff",		0,	cvs_server_diff,	REQ_NEEDDIR },
837917ff23Sjoris 	{ "log",		0,	cvs_server_log,		REQ_NEEDDIR },
84045a5a09Sxsa 	{ "rlog",		0,	cvs_server_rlog, 0 },
857917ff23Sjoris 	{ "add",		0,	cvs_server_add,		REQ_NEEDDIR },
86a45a22ceSxsa 	{ "remove",		0,	cvs_server_remove,	REQ_NEEDDIR },
87cadd0e2eSxsa 	{ "update-patches",	0,	cvs_server_update_patches, 0 },
8893bcb4acSxsa 	{ "gzip-file-contents",	0,	NULL, 0 },
89a45a22ceSxsa 	{ "status",		0,	cvs_server_status,	REQ_NEEDDIR },
90fd660bf2Stobias 	{ "rdiff",		0,	cvs_server_rdiff, 0 },
917917ff23Sjoris 	{ "tag",		0,	cvs_server_tag,		REQ_NEEDDIR },
924b15c6ebStobias 	{ "rtag",		0,	cvs_server_rtag, 0 },
93a45a22ceSxsa 	{ "import",		0,	cvs_server_import,	REQ_NEEDDIR },
947917ff23Sjoris 	{ "admin",		0,	cvs_server_admin,	REQ_NEEDDIR },
95a45a22ceSxsa 	{ "export",		0,	cvs_server_export,	REQ_NEEDDIR },
9693bcb4acSxsa 	{ "history",		0,	NULL, 0 },
97a45a22ceSxsa 	{ "release",		0,	cvs_server_release,	REQ_NEEDDIR },
989fac60a5Sjoris 	{ "watch-on",		0,	NULL, 0 },
999fac60a5Sjoris 	{ "watch-off",		0,	NULL, 0 },
1009fac60a5Sjoris 	{ "watch-add",		0,	NULL, 0 },
1019fac60a5Sjoris 	{ "watch-remove",	0,	NULL, 0 },
10293bcb4acSxsa 	{ "watchers",		0,	NULL, 0 },
10393bcb4acSxsa 	{ "editors",		0,	NULL, 0 },
104545fb2e2Stobias 	{ "init",		0,	cvs_server_init, 0 },
105a45a22ceSxsa 	{ "annotate",		0,	cvs_server_annotate,	REQ_NEEDDIR },
106e9658789Stobias 	{ "rannotate",		0,	cvs_server_rannotate, 0 },
1079fac60a5Sjoris 	{ "noop",		0,	NULL, 0 },
10893bcb4acSxsa 	{ "version",		0,	cvs_server_version, 0 },
1099fac60a5Sjoris 	{ "",			-1,	NULL, 0 }
1109fac60a5Sjoris };
1119fac60a5Sjoris 
112bf6291b7Sjoris static void	 client_check_directory(char *, char *);
1139fac60a5Sjoris static char	*client_get_supported_responses(void);
1149fac60a5Sjoris static char	*lastdir = NULL;
1159fac60a5Sjoris static int	 end_of_response = 0;
1169fac60a5Sjoris 
11779cb225aSxsa static void	cvs_client_initlog(void);
11879cb225aSxsa 
11979cb225aSxsa /*
12079cb225aSxsa  * File descriptors for protocol logging when the CVS_CLIENT_LOG environment
12179cb225aSxsa  * variable is set.
12279cb225aSxsa  */
12379cb225aSxsa static int	cvs_client_logon = 0;
12444001a5bSjoris int	cvs_client_inlog_fd = -1;
12544001a5bSjoris int	cvs_client_outlog_fd = -1;
12679cb225aSxsa 
12779cb225aSxsa 
128e453c9e9Sjoris int server_response = SERVER_OK;
129e453c9e9Sjoris 
1309fac60a5Sjoris static char *
client_get_supported_responses(void)1319fac60a5Sjoris client_get_supported_responses(void)
1329fac60a5Sjoris {
1339fac60a5Sjoris 	BUF *bp;
1349fac60a5Sjoris 	char *d;
1359fac60a5Sjoris 	int i, first;
1369fac60a5Sjoris 
1379fac60a5Sjoris 	first = 0;
1387bb3ddb0Sray 	bp = buf_alloc(512);
1399fac60a5Sjoris 	for (i = 0; cvs_responses[i].supported != -1; i++) {
1409fac60a5Sjoris 		if (cvs_responses[i].hdlr == NULL)
1419fac60a5Sjoris 			continue;
1429fac60a5Sjoris 
1439fac60a5Sjoris 		if (first != 0)
1447bb3ddb0Sray 			buf_putc(bp, ' ');
1459fac60a5Sjoris 		else
1469fac60a5Sjoris 			first++;
1477bb3ddb0Sray 		buf_puts(bp, cvs_responses[i].name);
1489fac60a5Sjoris 	}
1499fac60a5Sjoris 
1507bb3ddb0Sray 	buf_putc(bp, '\0');
1517bb3ddb0Sray 	d = buf_release(bp);
1529fac60a5Sjoris 	return (d);
1539fac60a5Sjoris }
1549fac60a5Sjoris 
155627b103eSjoris static void
client_check_directory(char * data,char * repository)156bf6291b7Sjoris client_check_directory(char *data, char *repository)
157627b103eSjoris {
158627b103eSjoris 	CVSENTRIES *entlist;
159ae83823aSxsa 	char *entry, *parent, *base, *p;
160*431378d1Snaddy 	char basebuf[PATH_MAX], parentbuf[PATH_MAX];
161627b103eSjoris 
162627b103eSjoris 	STRIP_SLASH(data);
163627b103eSjoris 
164bf6291b7Sjoris 	/* first directory we get is our module root */
16502d9e5c9Sjoris 	if (module_repo_root == NULL && checkout_target_dir != NULL) {
166bf6291b7Sjoris 		p = repository + strlen(current_cvsroot->cr_dir) + 1;
167bf6291b7Sjoris 		module_repo_root = xstrdup(p);
168bf6291b7Sjoris 		p = strrchr(module_repo_root, '/');
169bf6291b7Sjoris 		if (p != NULL)
170bf6291b7Sjoris 			*p = '\0';
171bf6291b7Sjoris 	}
172bf6291b7Sjoris 
1735dd120b0Sjoris 	cvs_mkpath(data, NULL);
174627b103eSjoris 
1755e1effbaStobias 	if (cvs_cmdop == CVS_OP_EXPORT)
1765e1effbaStobias 		return;
1775e1effbaStobias 
178*431378d1Snaddy 	if (strlcpy(basebuf, data, sizeof(basebuf)) >= sizeof(basebuf))
179*431378d1Snaddy 		fatal("client_check_directory: truncation");
180*431378d1Snaddy 	if ((base = basename(basebuf)) == NULL)
181627b103eSjoris 		fatal("client_check_directory: overflow");
182627b103eSjoris 
183*431378d1Snaddy 	if (strlcpy(parentbuf, data, sizeof(parentbuf)) >= sizeof(parentbuf))
184*431378d1Snaddy 		fatal("client_check_directory: truncation");
185*431378d1Snaddy 	if ((parent = dirname(parentbuf)) == NULL)
186627b103eSjoris 		fatal("client_check_directory: overflow");
187627b103eSjoris 
18815e08f08Sjoris 	if (!strcmp(parent, "."))
18915e08f08Sjoris 		return;
19015e08f08Sjoris 
191ae83823aSxsa 	entry = xmalloc(CVS_ENT_MAXLINELEN);
192ae83823aSxsa 	cvs_ent_line_str(base, NULL, NULL, NULL, NULL, 1, 0, entry,
193ae83823aSxsa 	    CVS_ENT_MAXLINELEN);
194627b103eSjoris 
195627b103eSjoris 	entlist = cvs_ent_open(parent);
196627b103eSjoris 	cvs_ent_add(entlist, entry);
197ae83823aSxsa 
198397ddb8aSnicm 	free(entry);
199627b103eSjoris }
200627b103eSjoris 
2019fac60a5Sjoris void
cvs_client_connect_to_server(void)2029fac60a5Sjoris cvs_client_connect_to_server(void)
2039fac60a5Sjoris {
204b4a9add8Sxsa 	struct cvs_var *vp;
205e5dd404eSmillert 	char *cmd, *argv[10], *resp;
2069fac60a5Sjoris 	int ifd[2], ofd[2], argc;
2079fac60a5Sjoris 
20880f6ca9bSjoris 	if (cvs_server_active == 1)
20980f6ca9bSjoris 		fatal("cvs_client_connect: I was already connected to server");
21080f6ca9bSjoris 
2119fac60a5Sjoris 	switch (current_cvsroot->cr_method) {
2129fac60a5Sjoris 	case CVS_METHOD_PSERVER:
2139fac60a5Sjoris 	case CVS_METHOD_KSERVER:
2149fac60a5Sjoris 	case CVS_METHOD_GSERVER:
2159fac60a5Sjoris 	case CVS_METHOD_FORK:
216a1ec0796Sjoris 		fatal("the specified connection method is not supported");
2179fac60a5Sjoris 	default:
2189fac60a5Sjoris 		break;
2199fac60a5Sjoris 	}
2209fac60a5Sjoris 
2219fac60a5Sjoris 	if (pipe(ifd) == -1)
2229fac60a5Sjoris 		fatal("cvs_client_connect: %s", strerror(errno));
2239fac60a5Sjoris 	if (pipe(ofd) == -1)
2249fac60a5Sjoris 		fatal("cvs_client_connect: %s", strerror(errno));
2259fac60a5Sjoris 
2269fac60a5Sjoris 	switch (fork()) {
2279fac60a5Sjoris 	case -1:
2289fac60a5Sjoris 		fatal("cvs_client_connect: fork failed: %s", strerror(errno));
2299fac60a5Sjoris 	case 0:
2309fac60a5Sjoris 		if (dup2(ifd[0], STDIN_FILENO) == -1)
2319fac60a5Sjoris 			fatal("cvs_client_connect: %s", strerror(errno));
2329fac60a5Sjoris 		if (dup2(ofd[1], STDOUT_FILENO) == -1)
2339fac60a5Sjoris 			fatal("cvs_client_connect: %s", strerror(errno));
2349fac60a5Sjoris 
2359fac60a5Sjoris 		close(ifd[1]);
2369fac60a5Sjoris 		close(ofd[0]);
2379fac60a5Sjoris 
2389fac60a5Sjoris 		if ((cmd = getenv("CVS_SERVER")) == NULL)
2399fac60a5Sjoris 			cmd = CVS_SERVER_DEFAULT;
2409fac60a5Sjoris 
2419fac60a5Sjoris 		argc = 0;
2429fac60a5Sjoris 		argv[argc++] = cvs_rsh;
2439fac60a5Sjoris 
2449fac60a5Sjoris 		if (current_cvsroot->cr_user != NULL) {
2459fac60a5Sjoris 			argv[argc++] = "-l";
2469fac60a5Sjoris 			argv[argc++] = current_cvsroot->cr_user;
2479fac60a5Sjoris 		}
2489fac60a5Sjoris 
249e5dd404eSmillert 		argv[argc++] = "--";
2509fac60a5Sjoris 		argv[argc++] = current_cvsroot->cr_host;
2519fac60a5Sjoris 		argv[argc++] = cmd;
2529fac60a5Sjoris 		argv[argc++] = "server";
2539fac60a5Sjoris 		argv[argc] = NULL;
2549fac60a5Sjoris 
2559fac60a5Sjoris 		cvs_log(LP_TRACE, "connecting to server %s",
2569fac60a5Sjoris 		    current_cvsroot->cr_host);
2579fac60a5Sjoris 
2589fac60a5Sjoris 		execvp(argv[0], argv);
2599fac60a5Sjoris 		fatal("cvs_client_connect: failed to execute cvs server");
2609fac60a5Sjoris 	default:
2619fac60a5Sjoris 		break;
2629fac60a5Sjoris 	}
2639fac60a5Sjoris 
2649fac60a5Sjoris 	close(ifd[0]);
2659fac60a5Sjoris 	close(ofd[1]);
2669fac60a5Sjoris 
2679fac60a5Sjoris 	if ((current_cvsroot->cr_srvin = fdopen(ifd[1], "w")) == NULL)
2689fac60a5Sjoris 		fatal("cvs_client_connect: %s", strerror(errno));
2699fac60a5Sjoris 	if ((current_cvsroot->cr_srvout = fdopen(ofd[0], "r")) == NULL)
2709fac60a5Sjoris 		fatal("cvs_client_connect: %s", strerror(errno));
2719fac60a5Sjoris 
2729fac60a5Sjoris 	setvbuf(current_cvsroot->cr_srvin, NULL,_IOLBF, 0);
2739fac60a5Sjoris 	setvbuf(current_cvsroot->cr_srvout, NULL, _IOLBF, 0);
2749fac60a5Sjoris 
27579cb225aSxsa 	cvs_client_initlog();
27679cb225aSxsa 
277fd590a3cSxsa 	if (cvs_cmdop != CVS_OP_INIT)
2789fac60a5Sjoris 		cvs_client_send_request("Root %s", current_cvsroot->cr_dir);
2799fac60a5Sjoris 
2809fac60a5Sjoris 	resp = client_get_supported_responses();
2819fac60a5Sjoris 	cvs_client_send_request("Valid-responses %s", resp);
282397ddb8aSnicm 	free(resp);
2839fac60a5Sjoris 
2849fac60a5Sjoris 	cvs_client_send_request("valid-requests");
2859fac60a5Sjoris 	cvs_client_get_responses();
2869fac60a5Sjoris 
2875c2137d6Sxsa 	cvs_client_send_request("UseUnchanged");
2885c2137d6Sxsa 
289aec5fbd0Sxsa 	if (cvs_nolog == 1)
290aec5fbd0Sxsa 		cvs_client_send_request("Global_option -l");
291aec5fbd0Sxsa 
292d2aa4319Sxsa 	if (cvs_noexec == 1)
293d2aa4319Sxsa 		cvs_client_send_request("Global_option -n");
294d2aa4319Sxsa 
295a694c629Stobias 	switch (verbosity) {
296a694c629Stobias 	case 0:
297d2aa4319Sxsa 		cvs_client_send_request("Global_option -Q");
298a694c629Stobias 		break;
299a694c629Stobias 	case 1:
300aec5fbd0Sxsa 		/* Be quiet. This is the default in OpenCVS. */
301aec5fbd0Sxsa 		cvs_client_send_request("Global_option -q");
302a694c629Stobias 		break;
303a694c629Stobias 	default:
304a694c629Stobias 		break;
305a694c629Stobias 	}
306aec5fbd0Sxsa 
307d2aa4319Sxsa 	if (cvs_readonly == 1)
308d2aa4319Sxsa 		cvs_client_send_request("Global_option -r");
309d2aa4319Sxsa 
310d2aa4319Sxsa 	if (cvs_trace == 1)
3119fac60a5Sjoris 		cvs_client_send_request("Global_option -t");
3129fac60a5Sjoris 
313b4a9add8Sxsa 	/* XXX: If 'Set' is supported? */
314b4a9add8Sxsa 	TAILQ_FOREACH(vp, &cvs_variables, cv_link)
315b4a9add8Sxsa 		cvs_client_send_request("Set %s=%s", vp->cv_name, vp->cv_val);
3169fac60a5Sjoris }
3179fac60a5Sjoris 
3189fac60a5Sjoris void
cvs_client_send_request(char * fmt,...)3199fac60a5Sjoris cvs_client_send_request(char *fmt, ...)
3209fac60a5Sjoris {
321be3337c1Stobias 	int i;
3229fac60a5Sjoris 	va_list ap;
3239fac60a5Sjoris 	char *data, *s;
3249fac60a5Sjoris 	struct cvs_req *req;
3259fac60a5Sjoris 
3269fac60a5Sjoris 	va_start(ap, fmt);
327be3337c1Stobias 	i = vasprintf(&data, fmt, ap);
3289fac60a5Sjoris 	va_end(ap);
329be3337c1Stobias 	if (i == -1)
3309a0ecc80Stobias 		fatal("cvs_client_send_request: could not allocate memory");
3319fac60a5Sjoris 
3329fac60a5Sjoris 	if ((s = strchr(data, ' ')) != NULL)
3339fac60a5Sjoris 		*s = '\0';
3349fac60a5Sjoris 
3359fac60a5Sjoris 	req = cvs_remote_get_request_info(data);
3369fac60a5Sjoris 	if (req == NULL)
3379fac60a5Sjoris 		fatal("'%s' is an unknown request", data);
3389fac60a5Sjoris 
3399fac60a5Sjoris 	if (req->supported != 1)
3409fac60a5Sjoris 		fatal("remote cvs server does not support '%s'", data);
3419fac60a5Sjoris 
3429fac60a5Sjoris 	if (s != NULL)
3439fac60a5Sjoris 		*s = ' ';
3449fac60a5Sjoris 
345d0a063c4Sjoris 	cvs_log(LP_TRACE, "%s", data);
34679cb225aSxsa 
3479fac60a5Sjoris 	cvs_remote_output(data);
348397ddb8aSnicm 	free(data);
3499fac60a5Sjoris }
3509fac60a5Sjoris 
3519fac60a5Sjoris void
cvs_client_read_response(void)3529fac60a5Sjoris cvs_client_read_response(void)
3539fac60a5Sjoris {
3549fac60a5Sjoris 	char *cmd, *data;
3559fac60a5Sjoris 	struct cvs_resp *resp;
3569fac60a5Sjoris 
3579fac60a5Sjoris 	cmd = cvs_remote_input();
3589fac60a5Sjoris 	if ((data = strchr(cmd, ' ')) != NULL)
3599fac60a5Sjoris 		(*data++) = '\0';
3609fac60a5Sjoris 
3619fac60a5Sjoris 	resp = cvs_remote_get_response_info(cmd);
3629fac60a5Sjoris 	if (resp == NULL)
3639fac60a5Sjoris 		fatal("response '%s' is not supported by our client", cmd);
3649fac60a5Sjoris 
3659fac60a5Sjoris 	if (resp->hdlr == NULL)
3669fac60a5Sjoris 		fatal("opencvs client does not support '%s'", cmd);
3679fac60a5Sjoris 
3689fac60a5Sjoris 	(*resp->hdlr)(data);
36979cb225aSxsa 
370397ddb8aSnicm 	free(cmd);
3719fac60a5Sjoris }
3729fac60a5Sjoris 
3739fac60a5Sjoris void
cvs_client_get_responses(void)3749fac60a5Sjoris cvs_client_get_responses(void)
3759fac60a5Sjoris {
3769fac60a5Sjoris 	while (end_of_response != 1)
3779fac60a5Sjoris 		cvs_client_read_response();
3789fac60a5Sjoris 
3799fac60a5Sjoris 	end_of_response = 0;
3809fac60a5Sjoris }
3819fac60a5Sjoris 
3829fac60a5Sjoris void
cvs_client_send_logmsg(char * msg)383f76ce13cSjoris cvs_client_send_logmsg(char *msg)
384f76ce13cSjoris {
385f76ce13cSjoris 	char *buf, *p, *q;
386f76ce13cSjoris 
387f76ce13cSjoris 	(void)xasprintf(&buf, "%s\n", msg);
388f76ce13cSjoris 
389f76ce13cSjoris 	cvs_client_send_request("Argument -m");
390f76ce13cSjoris 	if ((p = strchr(buf, '\n')) != NULL)
391f76ce13cSjoris 		*p++ = '\0';
392f76ce13cSjoris 	cvs_client_send_request("Argument %s", buf);
393f76ce13cSjoris 	for (q = p; p != NULL; q = p) {
394f76ce13cSjoris 		if ((p = strchr(q, '\n')) != NULL) {
395f76ce13cSjoris 			*p++ = '\0';
396f76ce13cSjoris 			cvs_client_send_request("Argumentx %s", q);
397f76ce13cSjoris 		}
398f76ce13cSjoris 	}
399f76ce13cSjoris 
400397ddb8aSnicm 	free(buf);
401f76ce13cSjoris }
402f76ce13cSjoris 
403f76ce13cSjoris void
cvs_client_senddir(const char * dir)4049fac60a5Sjoris cvs_client_senddir(const char *dir)
4059fac60a5Sjoris {
40604465c28Sxsa 	struct stat st;
4074b21b192Sxsa 	int nb;
408b9fc9a72Sderaadt 	char *d, *date, fpath[PATH_MAX], repo[PATH_MAX], *tag;
4094b21b192Sxsa 
4104b21b192Sxsa 	d = NULL;
4119fac60a5Sjoris 
4129fac60a5Sjoris 	if (lastdir != NULL && !strcmp(dir, lastdir))
4139fac60a5Sjoris 		return;
4149fac60a5Sjoris 
415b9fc9a72Sderaadt 	cvs_get_repository_path(dir, repo, PATH_MAX);
41623c15b64Sxsa 
417c634bf10Stobias 	if (cvs_cmdop != CVS_OP_RLOG)
41823c15b64Sxsa 		cvs_client_send_request("Directory %s\n%s", dir, repo);
41923c15b64Sxsa 
420b9fc9a72Sderaadt 	(void)xsnprintf(fpath, PATH_MAX, "%s/%s",
421e40de241Sxsa 	    dir, CVS_PATH_STATICENTRIES);
42204465c28Sxsa 
42304465c28Sxsa 	if (stat(fpath, &st) == 0 && (st.st_mode & (S_IRUSR|S_IRGRP|S_IROTH)))
42404465c28Sxsa 		cvs_client_send_request("Static-directory");
42504465c28Sxsa 
4264b21b192Sxsa 	d = xstrdup(dir);
4274b21b192Sxsa 	cvs_parse_tagfile(d, &tag, &date, &nb);
4284b21b192Sxsa 
4294b21b192Sxsa 	if (tag != NULL || date != NULL) {
4304b21b192Sxsa 		char buf[128];
4314b21b192Sxsa 
432b3904d3cSchl 		if (tag != NULL && nb != 0) {
4334b21b192Sxsa 			if (strlcpy(buf, "N", sizeof(buf)) >= sizeof(buf))
4344b21b192Sxsa 				fatal("cvs_client_senddir: truncation");
4354b21b192Sxsa 		} else if (tag != NULL) {
4364b21b192Sxsa 			if (strlcpy(buf, "T", sizeof(buf)) >= sizeof(buf))
4374b21b192Sxsa 				fatal("cvs_client_senddir: truncation");
4384b21b192Sxsa 		} else {
4394b21b192Sxsa 			if (strlcpy(buf, "D", sizeof(buf)) >= sizeof(buf))
4404b21b192Sxsa 				fatal("cvs_client_senddir: truncation");
4414b21b192Sxsa 		}
4424b21b192Sxsa 
4434b21b192Sxsa 		if (strlcat(buf, tag ? tag : date, sizeof(buf)) >= sizeof(buf))
4444b21b192Sxsa 			fatal("cvs_client_senddir: truncation");
4454b21b192Sxsa 
4464b21b192Sxsa 		cvs_client_send_request("Sticky %s", buf);
4474b21b192Sxsa 
448397ddb8aSnicm 		free(tag);
449397ddb8aSnicm 		free(date);
4504b21b192Sxsa 	}
451397ddb8aSnicm 	free(d);
452397ddb8aSnicm 	free(lastdir);
4539fac60a5Sjoris 	lastdir = xstrdup(dir);
4549fac60a5Sjoris }
4559fac60a5Sjoris 
4569fac60a5Sjoris void
cvs_client_sendfile(struct cvs_file * cf)4579fac60a5Sjoris cvs_client_sendfile(struct cvs_file *cf)
4589fac60a5Sjoris {
4599fac60a5Sjoris 	size_t len;
4606534056aStobias 	struct tm datetm;
4614967382bSxsa 	char rev[CVS_REV_BUFSZ], timebuf[CVS_TIME_BUFSZ], sticky[CVS_REV_BUFSZ];
4629fac60a5Sjoris 
4639fac60a5Sjoris 	cvs_client_senddir(cf->file_wd);
4649fac60a5Sjoris 	cvs_remote_classify_file(cf);
4659fac60a5Sjoris 
4662314050cSjoris 	if (cf->file_type == CVS_DIR)
467627b103eSjoris 		return;
468627b103eSjoris 
469361b89f3Stobias 	if (cf->file_ent != NULL && cvs_cmdop != CVS_OP_IMPORT) {
4709fac60a5Sjoris 		if (cf->file_status == FILE_ADDED) {
4719fac60a5Sjoris 			len = strlcpy(rev, "0", sizeof(rev));
4729fac60a5Sjoris 			if (len >= sizeof(rev))
4739fac60a5Sjoris 				fatal("cvs_client_sendfile: truncation");
4749fac60a5Sjoris 
4759fac60a5Sjoris 			len = strlcpy(timebuf, "Initial ", sizeof(timebuf));
4769fac60a5Sjoris 			if (len >= sizeof(timebuf))
4779fac60a5Sjoris 				fatal("cvs_client_sendfile: truncation");
4789fac60a5Sjoris 
4799fac60a5Sjoris 			len = strlcat(timebuf, cf->file_name, sizeof(timebuf));
4809fac60a5Sjoris 			if (len >= sizeof(timebuf))
4819fac60a5Sjoris 				fatal("cvs_client_sendfile: truncation");
4829fac60a5Sjoris 		} else {
4839fac60a5Sjoris 			rcsnum_tostr(cf->file_ent->ce_rev, rev, sizeof(rev));
4849fac60a5Sjoris 			ctime_r(&cf->file_ent->ce_mtime, timebuf);
4859fac60a5Sjoris 		}
4869fac60a5Sjoris 
4879fac60a5Sjoris 		if (cf->file_ent->ce_conflict == NULL) {
4882820b891Stobias 			timebuf[strcspn(timebuf, "\n")] = '\0';
4899fac60a5Sjoris 		} else {
4909fac60a5Sjoris 			len = strlcpy(timebuf, cf->file_ent->ce_conflict,
4919fac60a5Sjoris 			    sizeof(timebuf));
4929fac60a5Sjoris 			if (len >= sizeof(timebuf))
4939fac60a5Sjoris 				fatal("cvs_client_sendfile: truncation");
494a955ea3dSjoris 			len = strlcat(timebuf, "+=", sizeof(timebuf));
495828954c0Sjoris 			if (len >= sizeof(timebuf))
496828954c0Sjoris 				fatal("cvs_client_sendfile: truncation");
4979fac60a5Sjoris 		}
4989fac60a5Sjoris 
4999fac60a5Sjoris 		sticky[0] = '\0';
5009fac60a5Sjoris 		if (cf->file_ent->ce_tag != NULL) {
501c486465dSxsa 			(void)xsnprintf(sticky, sizeof(sticky), "T%s",
5029fac60a5Sjoris 			    cf->file_ent->ce_tag);
503a03c507eSjoris 		} else if (cf->file_ent->ce_date != -1) {
5046534056aStobias 			gmtime_r(&(cf->file_ent->ce_date), &datetm);
50525d9b074Sxsa 			(void)strftime(sticky, sizeof(sticky),
5066534056aStobias 			    "D"CVS_DATE_FMT, &datetm);
5079fac60a5Sjoris 		}
5089fac60a5Sjoris 
509a41f006dSotto 		cvs_client_send_request("Entry /%s/%s%s/%s/%s/%s",
5109fac60a5Sjoris 		    cf->file_name, (cf->file_status == FILE_REMOVED) ? "-" : "",
51156f996a2Sxsa 		    rev, timebuf, cf->file_ent->ce_opts ?
51256f996a2Sxsa 		    cf->file_ent->ce_opts : "", sticky);
5139fac60a5Sjoris 	}
5149fac60a5Sjoris 
5157bd344d4Stobias 	if (cvs_cmdop == CVS_OP_ADD)
5167bd344d4Stobias 		cf->file_status = FILE_MODIFIED;
5177bd344d4Stobias 
5189fac60a5Sjoris 	switch (cf->file_status) {
5199fac60a5Sjoris 	case FILE_UNKNOWN:
5205cf15c45Sjoris 		if (cf->file_flags & FILE_ON_DISK)
5219fac60a5Sjoris 			cvs_client_send_request("Questionable %s",
5229fac60a5Sjoris 			    cf->file_name);
5239fac60a5Sjoris 		break;
5249fac60a5Sjoris 	case FILE_ADDED:
525daa36f23Snicm 		if (backup_local_changes)	/* for update -C */
526daa36f23Snicm 			cvs_backup_file(cf);
527daa36f23Snicm 
528daa36f23Snicm 		cvs_client_send_request("Modified %s", cf->file_name);
529daa36f23Snicm 		cvs_remote_send_file(cf->file_path, cf->fd);
530daa36f23Snicm 		break;
5319fac60a5Sjoris 	case FILE_MODIFIED:
532daa36f23Snicm 		if (backup_local_changes) {	/* for update -C */
533daa36f23Snicm 			cvs_backup_file(cf);
534daa36f23Snicm 			cvs_client_send_request("Entry /%s/%s%s/%s/%s/%s",
535daa36f23Snicm 			    cf->file_name, "", rev, timebuf,
536daa36f23Snicm 			    cf->file_ent->ce_opts ? cf->file_ent->ce_opts : "",
537daa36f23Snicm 			    sticky);
538daa36f23Snicm 			break;
539daa36f23Snicm 		}
540daa36f23Snicm 
5419fac60a5Sjoris 		cvs_client_send_request("Modified %s", cf->file_name);
5422e0d696aSjoris 		cvs_remote_send_file(cf->file_path, cf->fd);
5439fac60a5Sjoris 		break;
5449fac60a5Sjoris 	case FILE_UPTODATE:
5459fac60a5Sjoris 		cvs_client_send_request("Unchanged %s", cf->file_name);
5469fac60a5Sjoris 		break;
5479fac60a5Sjoris 	}
5489fac60a5Sjoris }
5499fac60a5Sjoris 
5509fac60a5Sjoris void
cvs_client_send_files(char ** argv,int argc)5519fac60a5Sjoris cvs_client_send_files(char **argv, int argc)
5529fac60a5Sjoris {
5539fac60a5Sjoris 	int i;
5549fac60a5Sjoris 
5559fac60a5Sjoris 	for (i = 0; i < argc; i++)
5569fac60a5Sjoris 		cvs_client_send_request("Argument %s", argv[i]);
5579fac60a5Sjoris }
5589fac60a5Sjoris 
5599fac60a5Sjoris void
cvs_client_ok(char * data)5609fac60a5Sjoris cvs_client_ok(char *data)
5619fac60a5Sjoris {
5629fac60a5Sjoris 	end_of_response = 1;
563e453c9e9Sjoris 	server_response = SERVER_OK;
5649fac60a5Sjoris }
5659fac60a5Sjoris 
5669fac60a5Sjoris void
cvs_client_error(char * data)5679fac60a5Sjoris cvs_client_error(char *data)
5689fac60a5Sjoris {
5699fac60a5Sjoris 	end_of_response = 1;
570e453c9e9Sjoris 	server_response = SERVER_ERROR;
5719fac60a5Sjoris }
5729fac60a5Sjoris 
5739fac60a5Sjoris void
cvs_client_validreq(char * data)5749fac60a5Sjoris cvs_client_validreq(char *data)
5759fac60a5Sjoris {
5769fac60a5Sjoris 	int i;
5779fac60a5Sjoris 	char *sp, *ep;
5789fac60a5Sjoris 	struct cvs_req *req;
5799fac60a5Sjoris 
58049b4f6e3Sray 	if ((sp = data) == NULL)
58149b4f6e3Sray 		fatal("Missing argument for Valid-requests");
58249b4f6e3Sray 
5839fac60a5Sjoris 	do {
5849fac60a5Sjoris 		if ((ep = strchr(sp, ' ')) != NULL)
5859fac60a5Sjoris 			*ep = '\0';
5869fac60a5Sjoris 
5879fac60a5Sjoris 		req = cvs_remote_get_request_info(sp);
5889fac60a5Sjoris 		if (req != NULL)
5899fac60a5Sjoris 			req->supported = 1;
5909fac60a5Sjoris 
5919fac60a5Sjoris 		if (ep != NULL)
5929fac60a5Sjoris 			sp = ep + 1;
5939fac60a5Sjoris 	} while (ep != NULL);
5949fac60a5Sjoris 
5959fac60a5Sjoris 	for (i = 0; cvs_requests[i].supported != -1; i++) {
5969fac60a5Sjoris 		req = &cvs_requests[i];
5979fac60a5Sjoris 		if ((req->flags & REQ_NEEDED) &&
5989fac60a5Sjoris 		    req->supported != 1) {
5999fac60a5Sjoris 			fatal("server does not support required '%s'",
6009fac60a5Sjoris 			    req->name);
6019fac60a5Sjoris 		}
6029fac60a5Sjoris 	}
6039fac60a5Sjoris }
6049fac60a5Sjoris 
6059fac60a5Sjoris void
cvs_client_e(char * data)6069fac60a5Sjoris cvs_client_e(char *data)
6079fac60a5Sjoris {
60849b4f6e3Sray 	if (data == NULL)
60949b4f6e3Sray 		fatal("Missing argument for E");
61049b4f6e3Sray 
611a5bdf81aStobias 	fprintf(stderr, "%s\n", data);
6129fac60a5Sjoris }
6139fac60a5Sjoris 
6149fac60a5Sjoris void
cvs_client_m(char * data)6159fac60a5Sjoris cvs_client_m(char *data)
6169fac60a5Sjoris {
61749b4f6e3Sray 	if (data == NULL)
61849b4f6e3Sray 		fatal("Missing argument for M");
61949b4f6e3Sray 
620a5bdf81aStobias 	puts(data);
6219fac60a5Sjoris }
6229fac60a5Sjoris 
6239fac60a5Sjoris void
cvs_client_checkedin(char * data)6249fac60a5Sjoris cvs_client_checkedin(char *data)
6259fac60a5Sjoris {
6269fac60a5Sjoris 	CVSENTRIES *entlist;
6279fac60a5Sjoris 	struct cvs_ent *ent, *newent;
6287bd344d4Stobias 	size_t len;
6296534056aStobias 	struct tm datetm;
630ae83823aSxsa 	char *dir, *e, *entry, rev[CVS_REV_BUFSZ];
6310a7da307Sxsa 	char sticky[CVS_ENT_MAXLINELEN], timebuf[CVS_TIME_BUFSZ];
6329fac60a5Sjoris 
63349b4f6e3Sray 	if (data == NULL)
63449b4f6e3Sray 		fatal("Missing argument for Checked-in");
63549b4f6e3Sray 
6369fac60a5Sjoris 	dir = cvs_remote_input();
637448eb359Sotto 	e = cvs_remote_input();
638397ddb8aSnicm 	free(dir);
6399fac60a5Sjoris 
6409fac60a5Sjoris 	entlist = cvs_ent_open(data);
641448eb359Sotto 	newent = cvs_ent_parse(e);
6429fac60a5Sjoris 	ent = cvs_ent_get(entlist, newent->ce_name);
643397ddb8aSnicm 	free(e);
6449fac60a5Sjoris 
6459fac60a5Sjoris 	rcsnum_tostr(newent->ce_rev, rev, sizeof(rev));
6469fac60a5Sjoris 
6479fac60a5Sjoris 	sticky[0] = '\0';
6487bd344d4Stobias 	if (ent == NULL) {
6497bd344d4Stobias 		len = strlcpy(rev, "0", sizeof(rev));
6507bd344d4Stobias 		if (len >= sizeof(rev))
6517bd344d4Stobias 			fatal("cvs_client_sendfile: truncation");
6527bd344d4Stobias 
6537bd344d4Stobias 		len = strlcpy(timebuf, "Initial ", sizeof(timebuf));
6547bd344d4Stobias 		if (len >= sizeof(timebuf))
6557bd344d4Stobias 			fatal("cvs_client_sendfile: truncation");
6567bd344d4Stobias 
6577bd344d4Stobias 		len = strlcat(timebuf, newent->ce_name, sizeof(timebuf));
6587bd344d4Stobias 		if (len >= sizeof(timebuf))
6597bd344d4Stobias 			fatal("cvs_client_sendfile: truncation");
6607bd344d4Stobias 	} else {
661c61000bdStobias 		gmtime_r(&ent->ce_mtime, &datetm);
662c61000bdStobias 		asctime_r(&datetm, timebuf);
6637bd344d4Stobias 		timebuf[strcspn(timebuf, "\n")] = '\0';
6647bd344d4Stobias 
665a03c507eSjoris 		if (newent->ce_tag != NULL) {
6667bd344d4Stobias 			(void)xsnprintf(sticky, sizeof(sticky), "T%s",
667cef0f137Stobias 			    newent->ce_tag);
668a03c507eSjoris 		} else if (newent->ce_date != -1) {
6696534056aStobias 			gmtime_r(&(newent->ce_date), &datetm);
67025d9b074Sxsa 			(void)strftime(sticky, sizeof(sticky),
6716534056aStobias 			    "D"CVS_DATE_FMT, &datetm);
672a03c507eSjoris 		}
6737bd344d4Stobias 
6747bd344d4Stobias 		cvs_ent_free(ent);
6757bd344d4Stobias 	}
6769fac60a5Sjoris 
677ae83823aSxsa 	entry = xmalloc(CVS_ENT_MAXLINELEN);
678ae83823aSxsa 	cvs_ent_line_str(newent->ce_name, rev, timebuf,
679ae83823aSxsa 	    newent->ce_opts ? newent->ce_opts : "", sticky, 0,
680ae83823aSxsa 	    newent->ce_status == CVS_ENT_REMOVED ? 1 : 0,
681ae83823aSxsa 	    entry, CVS_ENT_MAXLINELEN);
6829fac60a5Sjoris 
6839fac60a5Sjoris 	cvs_ent_free(newent);
6849fac60a5Sjoris 	cvs_ent_add(entlist, entry);
685ae83823aSxsa 
686397ddb8aSnicm 	free(entry);
6879fac60a5Sjoris }
6889fac60a5Sjoris 
6899fac60a5Sjoris void
cvs_client_updated(char * data)6909fac60a5Sjoris cvs_client_updated(char *data)
6919fac60a5Sjoris {
692c486465dSxsa 	int fd;
6939fac60a5Sjoris 	time_t now;
69488b7ad1cStobias 	mode_t fmode;
6959fac60a5Sjoris 	size_t flen;
6969fac60a5Sjoris 	CVSENTRIES *ent;
6979fac60a5Sjoris 	struct cvs_ent *e;
6989fac60a5Sjoris 	const char *errstr;
6996534056aStobias 	struct tm datetm;
7009fac60a5Sjoris 	struct timeval tv[2];
701b9fc9a72Sderaadt 	char repo[PATH_MAX], *entry;
7020a7da307Sxsa 	char timebuf[CVS_TIME_BUFSZ], revbuf[CVS_REV_BUFSZ];
7034d16ba43Sjoris 	char *en, *mode, *len, *rpath, *p;
704b9fc9a72Sderaadt 	char sticky[CVS_ENT_MAXLINELEN], fpath[PATH_MAX];
7059fac60a5Sjoris 
70649b4f6e3Sray 	if (data == NULL)
70749b4f6e3Sray 		fatal("Missing argument for Updated");
70849b4f6e3Sray 
7099fac60a5Sjoris 	rpath = cvs_remote_input();
710448eb359Sotto 	en = cvs_remote_input();
7119fac60a5Sjoris 	mode = cvs_remote_input();
7129fac60a5Sjoris 	len = cvs_remote_input();
7139fac60a5Sjoris 
714bf6291b7Sjoris 	client_check_directory(data, rpath);
715b9fc9a72Sderaadt 	cvs_get_repository_path(".", repo, PATH_MAX);
7169fac60a5Sjoris 
7175219eee5Sjoris 	STRIP_SLASH(repo);
7185219eee5Sjoris 
7199fac60a5Sjoris 	if (strlen(repo) + 1 > strlen(rpath))
7209fac60a5Sjoris 		fatal("received a repository path that is too short");
7219fac60a5Sjoris 
7224d16ba43Sjoris 	p = strrchr(rpath, '/');
7234d16ba43Sjoris 	if (p == NULL)
7244d16ba43Sjoris 		fatal("malicious repository path from server");
7254d16ba43Sjoris 
7264d16ba43Sjoris 	(void)xsnprintf(fpath, sizeof(fpath), "%s/%s", data, p);
7279fac60a5Sjoris 
7289fac60a5Sjoris 	flen = strtonum(len, 0, INT_MAX, &errstr);
7299fac60a5Sjoris 	if (errstr != NULL)
7309fac60a5Sjoris 		fatal("cvs_client_updated: %s: %s", len, errstr);
731397ddb8aSnicm 	free(len);
7329fac60a5Sjoris 
7339fac60a5Sjoris 	cvs_strtomode(mode, &fmode);
734397ddb8aSnicm 	free(mode);
73588b7ad1cStobias 	fmode &= ~cvs_umask;
7369fac60a5Sjoris 
7379fac60a5Sjoris 	time(&now);
7386534056aStobias 	gmtime_r(&now, &datetm);
7396534056aStobias 	asctime_r(&datetm, timebuf);
7402820b891Stobias 	timebuf[strcspn(timebuf, "\n")] = '\0';
7419fac60a5Sjoris 
742448eb359Sotto 	e = cvs_ent_parse(en);
743397ddb8aSnicm 	free(en);
7445dd120b0Sjoris 
7455dd120b0Sjoris 	sticky[0] = '\0';
746a03c507eSjoris 	if (e->ce_tag != NULL) {
7475dd120b0Sjoris 		(void)xsnprintf(sticky, sizeof(sticky), "T%s", e->ce_tag);
748a03c507eSjoris 	} else if (e->ce_date != -1) {
7496534056aStobias 		gmtime_r(&(e->ce_date), &datetm);
75025d9b074Sxsa 		(void)strftime(sticky, sizeof(sticky),
7516534056aStobias 		    "D"CVS_DATE_FMT, &datetm);
752a03c507eSjoris 	}
7535dd120b0Sjoris 
7549fac60a5Sjoris 	rcsnum_tostr(e->ce_rev, revbuf, sizeof(revbuf));
755ae83823aSxsa 
756ae83823aSxsa 	entry = xmalloc(CVS_ENT_MAXLINELEN);
757ae83823aSxsa 	cvs_ent_line_str(e->ce_name, revbuf, timebuf,
758ae83823aSxsa 	    e->ce_opts ? e->ce_opts : "", sticky, 0, 0,
759ae83823aSxsa 	    entry, CVS_ENT_MAXLINELEN);
7609fac60a5Sjoris 
7619fac60a5Sjoris 	cvs_ent_free(e);
7625e1effbaStobias 
7635e1effbaStobias 	if (cvs_cmdop != CVS_OP_EXPORT) {
764bf6291b7Sjoris 		ent = cvs_ent_open(data);
7659fac60a5Sjoris 		cvs_ent_add(ent, entry);
7665e1effbaStobias 	}
7679fac60a5Sjoris 
768397ddb8aSnicm 	free(entry);
769ae83823aSxsa 
770801c91ecSjoris 	(void)unlink(fpath);
7719fac60a5Sjoris 	if ((fd = open(fpath, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
7729fac60a5Sjoris 		fatal("cvs_client_updated: open: %s: %s",
7739fac60a5Sjoris 		    fpath, strerror(errno));
7749fac60a5Sjoris 
775bb510330Sjoris 	cvs_remote_receive_file(fd, flen);
7769fac60a5Sjoris 
7779fac60a5Sjoris 	tv[0].tv_sec = now;
7789fac60a5Sjoris 	tv[0].tv_usec = 0;
7799fac60a5Sjoris 	tv[1] = tv[0];
7809fac60a5Sjoris 
7819fac60a5Sjoris 	if (futimes(fd, tv) == -1)
7829fac60a5Sjoris 		fatal("cvs_client_updated: futimes: %s", strerror(errno));
7839fac60a5Sjoris 
7849fac60a5Sjoris 	if (fchmod(fd, fmode) == -1)
7859fac60a5Sjoris 		fatal("cvs_client_updated: fchmod: %s", strerror(errno));
7869fac60a5Sjoris 
7879fac60a5Sjoris 	(void)close(fd);
7889fac60a5Sjoris 
789397ddb8aSnicm 	free(rpath);
7909fac60a5Sjoris }
7919fac60a5Sjoris 
7929fac60a5Sjoris void
cvs_client_merged(char * data)7939fac60a5Sjoris cvs_client_merged(char *data)
7949fac60a5Sjoris {
795828954c0Sjoris 	int fd;
796828954c0Sjoris 	time_t now;
79788b7ad1cStobias 	mode_t fmode;
798828954c0Sjoris 	size_t flen;
799828954c0Sjoris 	CVSENTRIES *ent;
800828954c0Sjoris 	const char *errstr;
801828954c0Sjoris 	struct timeval tv[2];
8026534056aStobias 	struct tm datetm;
8030a7da307Sxsa 	char timebuf[CVS_TIME_BUFSZ], *repo, *rpath, *entry, *mode;
804*431378d1Snaddy 	char *len, *fpath, *wdir, wdirbuf[PATH_MAX];
805828954c0Sjoris 
80649b4f6e3Sray 	if (data == NULL)
80749b4f6e3Sray 		fatal("Missing argument for Merged");
80849b4f6e3Sray 
809828954c0Sjoris 	rpath = cvs_remote_input();
810828954c0Sjoris 	entry = cvs_remote_input();
811828954c0Sjoris 	mode = cvs_remote_input();
812828954c0Sjoris 	len = cvs_remote_input();
813828954c0Sjoris 
814bf6291b7Sjoris 	client_check_directory(data, rpath);
815bf6291b7Sjoris 
816b9fc9a72Sderaadt 	repo = xmalloc(PATH_MAX);
817b9fc9a72Sderaadt 	cvs_get_repository_path(".", repo, PATH_MAX);
818828954c0Sjoris 
819828954c0Sjoris 	STRIP_SLASH(repo);
820828954c0Sjoris 
821828954c0Sjoris 	if (strlen(repo) + 1 > strlen(rpath))
822828954c0Sjoris 		fatal("received a repository path that is too short");
823828954c0Sjoris 
824828954c0Sjoris 	fpath = rpath + strlen(repo) + 1;
825*431378d1Snaddy 	if (strlcpy(wdirbuf, fpath, sizeof(wdirbuf)) >= sizeof(wdirbuf))
826*431378d1Snaddy 		fatal("cvs_client_merged: truncation");
827*431378d1Snaddy 	if ((wdir = dirname(wdirbuf)) == NULL)
828828954c0Sjoris 		fatal("cvs_client_merged: dirname: %s", strerror(errno));
829397ddb8aSnicm 	free(repo);
830828954c0Sjoris 
831828954c0Sjoris 	flen = strtonum(len, 0, INT_MAX, &errstr);
832828954c0Sjoris 	if (errstr != NULL)
833828954c0Sjoris 		fatal("cvs_client_merged: %s: %s", len, errstr);
834397ddb8aSnicm 	free(len);
835828954c0Sjoris 
836828954c0Sjoris 	cvs_strtomode(mode, &fmode);
837397ddb8aSnicm 	free(mode);
83888b7ad1cStobias 	fmode &= ~cvs_umask;
839828954c0Sjoris 
840828954c0Sjoris 	time(&now);
8416534056aStobias 	gmtime_r(&now, &datetm);
8426534056aStobias 	asctime_r(&datetm, timebuf);
8432820b891Stobias 	timebuf[strcspn(timebuf, "\n")] = '\0';
844828954c0Sjoris 
845828954c0Sjoris 	ent = cvs_ent_open(wdir);
846828954c0Sjoris 	cvs_ent_add(ent, entry);
847397ddb8aSnicm 	free(entry);
848828954c0Sjoris 
849801c91ecSjoris 	(void)unlink(fpath);
850828954c0Sjoris 	if ((fd = open(fpath, O_CREAT | O_WRONLY | O_TRUNC)) == -1)
851828954c0Sjoris 		fatal("cvs_client_merged: open: %s: %s",
852828954c0Sjoris 		    fpath, strerror(errno));
853828954c0Sjoris 
854828954c0Sjoris 	cvs_remote_receive_file(fd, flen);
855828954c0Sjoris 
856828954c0Sjoris 	tv[0].tv_sec = now;
857828954c0Sjoris 	tv[0].tv_usec = 0;
858828954c0Sjoris 	tv[1] = tv[0];
859828954c0Sjoris 
860828954c0Sjoris 	if (futimes(fd, tv) == -1)
861828954c0Sjoris 		fatal("cvs_client_merged: futimes: %s", strerror(errno));
862828954c0Sjoris 
863828954c0Sjoris 	if (fchmod(fd, fmode) == -1)
864828954c0Sjoris 		fatal("cvs_client_merged: fchmod: %s", strerror(errno));
865828954c0Sjoris 
866828954c0Sjoris 	(void)close(fd);
867828954c0Sjoris 
868397ddb8aSnicm 	free(rpath);
8699fac60a5Sjoris }
8709fac60a5Sjoris 
8719fac60a5Sjoris void
cvs_client_removed(char * data)8729fac60a5Sjoris cvs_client_removed(char *data)
8739fac60a5Sjoris {
87452979273Sjoris 	CVSENTRIES *entlist;
875b9fc9a72Sderaadt 	char *rpath, *filename, fpath[PATH_MAX];
876e0a36014Sjoris 
8771786582cStobias 	if (data == NULL)
8781786582cStobias 		fatal("Missing argument for Removed");
8791786582cStobias 
88052979273Sjoris 	rpath = cvs_remote_input();
88152979273Sjoris 	if ((filename = strrchr(rpath, '/')) == NULL)
88252979273Sjoris 		fatal("bad rpath in cvs_client_removed: %s", rpath);
88352979273Sjoris 	filename++;
88452979273Sjoris 
88552979273Sjoris 	entlist = cvs_ent_open(data);
88652979273Sjoris 	cvs_ent_remove(entlist, filename);
88752979273Sjoris 
888b9fc9a72Sderaadt 	(void)xsnprintf(fpath, PATH_MAX, "%s/%s", data, filename);
88952979273Sjoris 	(void)unlink(fpath);
89052979273Sjoris 
891397ddb8aSnicm 	free(rpath);
8929fac60a5Sjoris }
8939fac60a5Sjoris 
8949fac60a5Sjoris void
cvs_client_remove_entry(char * data)8959fac60a5Sjoris cvs_client_remove_entry(char *data)
8969fac60a5Sjoris {
897a341aa38Sxsa 	CVSENTRIES *entlist;
898408908afSjoris 	char *filename, *rpath;
8999fac60a5Sjoris 
90049b4f6e3Sray 	if (data == NULL)
90149b4f6e3Sray 		fatal("Missing argument for Remove-entry");
90249b4f6e3Sray 
9030a6fb2eeSjoris 	rpath = cvs_remote_input();
9040a6fb2eeSjoris 	if ((filename = strrchr(rpath, '/')) == NULL)
9050a6fb2eeSjoris 		fatal("bad rpath in cvs_client_remove_entry: %s", rpath);
9064e757633Sray 	filename++;
907a341aa38Sxsa 
908a341aa38Sxsa 	entlist = cvs_ent_open(data);
909408908afSjoris 	cvs_ent_remove(entlist, filename);
910a341aa38Sxsa 
911397ddb8aSnicm 	free(rpath);
9129fac60a5Sjoris }
913239a5408Sxsa 
914239a5408Sxsa void
cvs_client_set_static_directory(char * data)915a16f5d47Sxsa cvs_client_set_static_directory(char *data)
916a16f5d47Sxsa {
917a16f5d47Sxsa 	FILE *fp;
918b9fc9a72Sderaadt 	char *dir, fpath[PATH_MAX];
919a16f5d47Sxsa 
92049b4f6e3Sray 	if (data == NULL)
92149b4f6e3Sray 		fatal("Missing argument for Set-static-directory");
92249b4f6e3Sray 
923a16f5d47Sxsa 	STRIP_SLASH(data);
924a16f5d47Sxsa 
925a16f5d47Sxsa 	dir = cvs_remote_input();
926397ddb8aSnicm 	free(dir);
927a16f5d47Sxsa 
9285e1effbaStobias 	if (cvs_cmdop == CVS_OP_EXPORT)
9295e1effbaStobias 		return;
9305e1effbaStobias 
931b9fc9a72Sderaadt 	(void)xsnprintf(fpath, PATH_MAX, "%s/%s",
932e40de241Sxsa 	    data, CVS_PATH_STATICENTRIES);
933a16f5d47Sxsa 
934a16f5d47Sxsa 	if ((fp = fopen(fpath, "w+")) == NULL) {
935a16f5d47Sxsa 		cvs_log(LP_ERRNO, "%s", fpath);
936ba7b4b60Sotto 		return;
937a16f5d47Sxsa 	}
938a16f5d47Sxsa 	(void)fclose(fp);
939a16f5d47Sxsa }
940a16f5d47Sxsa 
941a16f5d47Sxsa void
cvs_client_clear_static_directory(char * data)942239a5408Sxsa cvs_client_clear_static_directory(char *data)
943239a5408Sxsa {
944b9fc9a72Sderaadt 	char *dir, fpath[PATH_MAX];
945239a5408Sxsa 
94649b4f6e3Sray 	if (data == NULL)
94749b4f6e3Sray 		fatal("Missing argument for Clear-static-directory");
94849b4f6e3Sray 
949239a5408Sxsa 	STRIP_SLASH(data);
950239a5408Sxsa 
951239a5408Sxsa 	dir = cvs_remote_input();
952397ddb8aSnicm 	free(dir);
953239a5408Sxsa 
9545e1effbaStobias 	if (cvs_cmdop == CVS_OP_EXPORT)
9555e1effbaStobias 		return;
9565e1effbaStobias 
957b9fc9a72Sderaadt 	(void)xsnprintf(fpath, PATH_MAX, "%s/%s",
958e40de241Sxsa 	    data, CVS_PATH_STATICENTRIES);
959239a5408Sxsa 
960239a5408Sxsa 	(void)cvs_unlink(fpath);
961239a5408Sxsa }
962239a5408Sxsa 
963239a5408Sxsa void
cvs_client_set_sticky(char * data)964239a5408Sxsa cvs_client_set_sticky(char *data)
965239a5408Sxsa {
966239a5408Sxsa 	FILE *fp;
967b9fc9a72Sderaadt 	char *dir, *tag, tagpath[PATH_MAX];
968239a5408Sxsa 
96949b4f6e3Sray 	if (data == NULL)
97049b4f6e3Sray 		fatal("Missing argument for Set-sticky");
97149b4f6e3Sray 
972239a5408Sxsa 	STRIP_SLASH(data);
973239a5408Sxsa 
974239a5408Sxsa 	dir = cvs_remote_input();
975239a5408Sxsa 	tag = cvs_remote_input();
976239a5408Sxsa 
9775e1effbaStobias 	if (cvs_cmdop == CVS_OP_EXPORT)
9785e1effbaStobias 		goto out;
9795e1effbaStobias 
980bf6291b7Sjoris 	client_check_directory(data, dir);
9815dd120b0Sjoris 
982b9fc9a72Sderaadt 	(void)xsnprintf(tagpath, PATH_MAX, "%s/%s", data, CVS_PATH_TAG);
983239a5408Sxsa 
984239a5408Sxsa 	if ((fp = fopen(tagpath, "w+")) == NULL) {
985239a5408Sxsa 		cvs_log(LP_ERRNO, "%s", tagpath);
986239a5408Sxsa 		goto out;
987239a5408Sxsa 	}
988239a5408Sxsa 
989239a5408Sxsa 	(void)fprintf(fp, "%s\n", tag);
990239a5408Sxsa 	(void)fclose(fp);
991239a5408Sxsa out:
992397ddb8aSnicm 	free(tag);
993397ddb8aSnicm 	free(dir);
994239a5408Sxsa }
995239a5408Sxsa 
996239a5408Sxsa void
cvs_client_clear_sticky(char * data)997239a5408Sxsa cvs_client_clear_sticky(char *data)
998239a5408Sxsa {
999b9fc9a72Sderaadt 	char *dir, tagpath[PATH_MAX];
1000239a5408Sxsa 
100149b4f6e3Sray 	if (data == NULL)
100249b4f6e3Sray 		fatal("Missing argument for Clear-sticky");
100349b4f6e3Sray 
1004239a5408Sxsa 	STRIP_SLASH(data);
1005239a5408Sxsa 
1006239a5408Sxsa 	dir = cvs_remote_input();
1007bf6291b7Sjoris 
1008bf6291b7Sjoris 	if (cvs_cmdop == CVS_OP_EXPORT) {
1009397ddb8aSnicm 		free(dir);
10105e1effbaStobias 		return;
1011bf6291b7Sjoris 	}
10125e1effbaStobias 
1013bf6291b7Sjoris 	client_check_directory(data, dir);
10145dd120b0Sjoris 
1015b9fc9a72Sderaadt 	(void)xsnprintf(tagpath, PATH_MAX, "%s/%s", data, CVS_PATH_TAG);
10165dd120b0Sjoris 	(void)unlink(tagpath);
1017bf6291b7Sjoris 
1018397ddb8aSnicm 	free(dir);
1019239a5408Sxsa }
1020239a5408Sxsa 
1021239a5408Sxsa 
102279cb225aSxsa /*
102379cb225aSxsa  * cvs_client_initlog()
102479cb225aSxsa  *
102579cb225aSxsa  * Initialize protocol logging if the CVS_CLIENT_LOG environment variable is
102679cb225aSxsa  * set.  In this case, the variable's value is used as a path to which the
102779cb225aSxsa  * appropriate suffix is added (".in" for client input and ".out" for server
102879cb225aSxsa  * output).
102979cb225aSxsa  */
103079cb225aSxsa static void
cvs_client_initlog(void)103179cb225aSxsa cvs_client_initlog(void)
103279cb225aSxsa {
103379cb225aSxsa 	u_int i;
1034b9fc9a72Sderaadt 	char *env, *envdup, buf[PATH_MAX], fpath[PATH_MAX];
1035b9fc9a72Sderaadt 	char rpath[PATH_MAX], timebuf[CVS_TIME_BUFSZ], *s;
103679cb225aSxsa 	struct stat st;
103779cb225aSxsa 	time_t now;
103879cb225aSxsa 	struct passwd *pwd;
103979cb225aSxsa 
104079cb225aSxsa 	/* avoid doing it more than once */
104179cb225aSxsa 	if (cvs_client_logon)
104279cb225aSxsa 		return;
104379cb225aSxsa 
104479cb225aSxsa 	if ((env = getenv("CVS_CLIENT_LOG")) == NULL)
104579cb225aSxsa 		return;
104679cb225aSxsa 
104779cb225aSxsa 	envdup = xstrdup(env);
104879cb225aSxsa 	if ((s = strchr(envdup, '%')) != NULL)
104979cb225aSxsa 		*s = '\0';
105079cb225aSxsa 
105179cb225aSxsa 	if (strlcpy(buf, env, sizeof(buf)) >= sizeof(buf))
105279cb225aSxsa 		fatal("cvs_client_initlog: truncation");
105379cb225aSxsa 
105479cb225aSxsa 	if (strlcpy(rpath, envdup, sizeof(rpath)) >= sizeof(rpath))
105579cb225aSxsa 		fatal("cvs_client_initlog: truncation");
105679cb225aSxsa 
1057397ddb8aSnicm 	free(envdup);
105879cb225aSxsa 
105979cb225aSxsa 	s = buf;
106079cb225aSxsa 	while ((s = strchr(s, '%')) != NULL) {
106179cb225aSxsa 		s++;
106279cb225aSxsa 		switch (*s) {
106379cb225aSxsa 		case 'c':
10649d091990Stobias 			if (strlcpy(fpath, cmdp->cmd_name, sizeof(fpath)) >=
106579cb225aSxsa 			    sizeof(fpath))
106679cb225aSxsa 				fatal("cvs_client_initlog: truncation");
106779cb225aSxsa 			break;
106879cb225aSxsa 		case 'd':
106979cb225aSxsa 			time(&now);
10706534056aStobias 			ctime_r(&now, timebuf);
10716534056aStobias 			timebuf[strcspn(timebuf, "\n")] = '\0';
10726534056aStobias 			if (strlcpy(fpath, timebuf, sizeof(fpath)) >=
107379cb225aSxsa 			    sizeof(fpath))
107479cb225aSxsa 				fatal("cvs_client_initlog: truncation");
107579cb225aSxsa 			break;
107679cb225aSxsa 		case 'p':
1077c486465dSxsa 			(void)xsnprintf(fpath, sizeof(fpath), "%d", getpid());
107879cb225aSxsa 			break;
107979cb225aSxsa 		case 'u':
108079cb225aSxsa 			if ((pwd = getpwuid(getuid())) != NULL) {
108179cb225aSxsa 				if (strlcpy(fpath, pwd->pw_name,
108279cb225aSxsa 				    sizeof(fpath)) >= sizeof(fpath))
108379cb225aSxsa 					fatal("cvs_client_initlog: truncation");
108479cb225aSxsa 			} else {
108579cb225aSxsa 				fpath[0] = '\0';
108679cb225aSxsa 			}
108779cb225aSxsa 			endpwent();
108879cb225aSxsa 			break;
108979cb225aSxsa 		default:
109079cb225aSxsa 			fpath[0] = '\0';
109179cb225aSxsa 			break;
109279cb225aSxsa 		}
109379cb225aSxsa 
109479cb225aSxsa 		if (fpath[0] != '\0') {
109579cb225aSxsa 			if (strlcat(rpath, "-", sizeof(rpath)) >= sizeof(rpath))
109679cb225aSxsa 				fatal("cvs_client_initlog: truncation");
109779cb225aSxsa 
109879cb225aSxsa 			if (strlcat(rpath, fpath, sizeof(rpath))
109979cb225aSxsa 			    >= sizeof(rpath))
110079cb225aSxsa 				fatal("cvs_client_initlog: truncation");
110179cb225aSxsa 		}
110279cb225aSxsa 	}
110379cb225aSxsa 
110479cb225aSxsa 	for (i = 0; i < UINT_MAX; i++) {
1105c486465dSxsa 		(void)xsnprintf(fpath, sizeof(fpath), "%s-%d.in", rpath, i);
110679cb225aSxsa 
110779cb225aSxsa 		if (stat(fpath, &st) != -1)
110879cb225aSxsa 			continue;
110979cb225aSxsa 
111079cb225aSxsa 		if (errno != ENOENT)
111179cb225aSxsa 			fatal("cvs_client_initlog() stat failed '%s'",
111279cb225aSxsa 			    strerror(errno));
111379cb225aSxsa 
111479cb225aSxsa 		break;
111579cb225aSxsa 	}
111679cb225aSxsa 
111779cb225aSxsa 	if ((cvs_client_inlog_fd = open(fpath,
1118b203ab89Sxsa 	    O_RDWR | O_CREAT | O_TRUNC, 0644)) == -1) {
111979cb225aSxsa 		fatal("cvs_client_initlog: open `%s': %s",
112079cb225aSxsa 		    fpath, strerror(errno));
112179cb225aSxsa 	}
112279cb225aSxsa 
112379cb225aSxsa 	for (i = 0; i < UINT_MAX; i++) {
1124c486465dSxsa 		(void)xsnprintf(fpath, sizeof(fpath), "%s-%d.out", rpath, i);
112579cb225aSxsa 
112679cb225aSxsa 		if (stat(fpath, &st) != -1)
112779cb225aSxsa 			continue;
112879cb225aSxsa 
112979cb225aSxsa 		if (errno != ENOENT)
113079cb225aSxsa 			fatal("cvs_client_initlog() stat failed '%s'",
113179cb225aSxsa 			    strerror(errno));
113279cb225aSxsa 
113379cb225aSxsa 		break;
113479cb225aSxsa 	}
113579cb225aSxsa 
113679cb225aSxsa 	if ((cvs_client_outlog_fd = open(fpath,
1137b203ab89Sxsa 	    O_RDWR | O_CREAT | O_TRUNC, 0644)) == -1) {
113879cb225aSxsa 		fatal("cvs_client_initlog: open `%s': %s",
113979cb225aSxsa 		    fpath, strerror(errno));
114079cb225aSxsa 	}
114179cb225aSxsa 
114279cb225aSxsa 	cvs_client_logon = 1;
114379cb225aSxsa }
1144