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