1a7c91847Schristos /* CVS client-related stuff.
2a7c91847Schristos
3a7c91847Schristos This program is free software; you can redistribute it and/or modify
4a7c91847Schristos it under the terms of the GNU General Public License as published by
5a7c91847Schristos the Free Software Foundation; either version 2, or (at your option)
6a7c91847Schristos any later version.
7a7c91847Schristos
8a7c91847Schristos This program is distributed in the hope that it will be useful,
9a7c91847Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
10a7c91847Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11a7c91847Schristos GNU General Public License for more details. */
12*3cd63638Schristos #include <sys/cdefs.h>
13*3cd63638Schristos __RCSID("$NetBSD: client.c,v 1.5 2016/05/17 14:00:09 christos Exp $");
14a7c91847Schristos
15a7c91847Schristos #ifdef HAVE_CONFIG_H
16a7c91847Schristos # include "config.h"
17a7c91847Schristos #endif /* HAVE_CONFIG_H */
18a7c91847Schristos
19a7c91847Schristos #include "cvs.h"
20a7c91847Schristos #include "getline.h"
21a7c91847Schristos #include "edit.h"
22a7c91847Schristos #include "buffer.h"
23a7c91847Schristos #include "save-cwd.h"
24a7c91847Schristos
25a7c91847Schristos #ifdef CLIENT_SUPPORT
26a7c91847Schristos
27a7c91847Schristos # include "log-buffer.h"
28a7c91847Schristos # include "md5.h"
29a7c91847Schristos
30a7c91847Schristos #include "socket-client.h"
31a7c91847Schristos #include "rsh-client.h"
32a7c91847Schristos
33a7c91847Schristos # ifdef HAVE_GSSAPI
34a7c91847Schristos # include "gssapi-client.h"
35a7c91847Schristos # endif
36a7c91847Schristos
37a7c91847Schristos # ifdef HAVE_KERBEROS
38a7c91847Schristos # include "kerberos4-client.h"
39a7c91847Schristos # endif
40a7c91847Schristos
41a7c91847Schristos
42a7c91847Schristos
43a7c91847Schristos /* Keep track of any paths we are sending for Max-dotdot so that we can verify
44a7c91847Schristos * that uplevel paths coming back form the server are valid.
45a7c91847Schristos *
46a7c91847Schristos * FIXME: The correct way to do this is probably provide some sort of virtual
47a7c91847Schristos * path map on the client side. This would be generic enough to be applied to
48a7c91847Schristos * absolute paths supplied by the user too.
49a7c91847Schristos */
50a7c91847Schristos static List *uppaths;
51a7c91847Schristos
52a7c91847Schristos
53a7c91847Schristos
54a7c91847Schristos static void add_prune_candidate (const char *);
55a7c91847Schristos
56a7c91847Schristos /* All the commands. */
57a7c91847Schristos int add (int argc, char **argv);
58a7c91847Schristos int admin (int argc, char **argv);
59a7c91847Schristos int checkout (int argc, char **argv);
60a7c91847Schristos int commit (int argc, char **argv);
61a7c91847Schristos int diff (int argc, char **argv);
62a7c91847Schristos int history (int argc, char **argv);
63a7c91847Schristos int import (int argc, char **argv);
64a7c91847Schristos int cvslog (int argc, char **argv);
65a7c91847Schristos int patch (int argc, char **argv);
66a7c91847Schristos int release (int argc, char **argv);
67a7c91847Schristos int cvsremove (int argc, char **argv);
68a7c91847Schristos int rtag (int argc, char **argv);
69a7c91847Schristos int status (int argc, char **argv);
70a7c91847Schristos int tag (int argc, char **argv);
71a7c91847Schristos int update (int argc, char **argv);
72a7c91847Schristos
739f2c58f4Schristos #if defined AUTH_CLIENT_SUPPORT || defined HAVE_KERBEROS || defined HAVE_GSSAPI
749f2c58f4Schristos static int connect_to(char *, unsigned int);
759f2c58f4Schristos #endif
769f2c58f4Schristos
77a7c91847Schristos static size_t try_read_from_server (char *, size_t);
78a7c91847Schristos
79a7c91847Schristos static void auth_server (cvsroot_t *, struct buffer *, struct buffer *,
80274254cdSchristos int, int);
81a7c91847Schristos
82a7c91847Schristos
83a7c91847Schristos
84a7c91847Schristos /* This is the referrer who referred us to a primary, or write server, using
85a7c91847Schristos * the "Redirect" request.
86a7c91847Schristos */
87a7c91847Schristos static cvsroot_t *client_referrer;
88a7c91847Schristos
89a7c91847Schristos /* We need to keep track of the list of directories we've sent to the
90a7c91847Schristos server. This list, along with the current CVSROOT, will help us
91a7c91847Schristos decide which command-line arguments to send. */
92a7c91847Schristos List *dirs_sent_to_server;
93a7c91847Schristos static int
is_arg_a_parent_or_listed_dir(Node * n,void * d)94a7c91847Schristos is_arg_a_parent_or_listed_dir (Node *n, void *d)
95a7c91847Schristos {
96a7c91847Schristos char *directory = n->key; /* name of the dir sent to server */
97a7c91847Schristos char *this_argv_elem = d; /* this argv element */
98a7c91847Schristos
99a7c91847Schristos /* Say we should send this argument if the argument matches the
100a7c91847Schristos beginning of a directory name sent to the server. This way,
101a7c91847Schristos the server will know to start at the top of that directory
102a7c91847Schristos hierarchy and descend. */
103a7c91847Schristos
104a7c91847Schristos if (!strncmp (directory, this_argv_elem, strlen (this_argv_elem)))
105a7c91847Schristos return 1;
106a7c91847Schristos
107a7c91847Schristos return 0;
108a7c91847Schristos }
109a7c91847Schristos
110a7c91847Schristos
111a7c91847Schristos
112a7c91847Schristos /* Return nonzero if this argument should not be sent to the
113a7c91847Schristos server. */
114a7c91847Schristos static int
arg_should_not_be_sent_to_server(char * arg)115a7c91847Schristos arg_should_not_be_sent_to_server (char *arg)
116a7c91847Schristos {
117a7c91847Schristos /* Decide if we should send this directory name to the server. We
118a7c91847Schristos should always send argv[i] if:
119a7c91847Schristos
120a7c91847Schristos 1) the list of directories sent to the server is empty (as it
121a7c91847Schristos will be for checkout, etc.).
122a7c91847Schristos
123a7c91847Schristos 2) the argument is "."
124a7c91847Schristos
125a7c91847Schristos 3) the argument is a file in the cwd and the cwd is checked out
126a7c91847Schristos from the current root
127a7c91847Schristos
128a7c91847Schristos 4) the argument lies within one of the paths in
129a7c91847Schristos dirs_sent_to_server.
130a7c91847Schristos
131a7c91847Schristos */
132a7c91847Schristos
133a7c91847Schristos if (list_isempty (dirs_sent_to_server))
134a7c91847Schristos return 0; /* always send it */
135a7c91847Schristos
136a7c91847Schristos if (!strcmp (arg, "."))
137a7c91847Schristos return 0; /* always send it */
138a7c91847Schristos
139a7c91847Schristos /* We should send arg if it is one of the directories sent to the
140a7c91847Schristos server or the parent of one; this tells the server to descend
141a7c91847Schristos the hierarchy starting at this level. */
142a7c91847Schristos if (isdir (arg))
143a7c91847Schristos {
144a7c91847Schristos if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir, arg))
145a7c91847Schristos return 0;
146a7c91847Schristos
147a7c91847Schristos /* If arg wasn't a parent, we don't know anything about it (we
148a7c91847Schristos would have seen something related to it during the
149a7c91847Schristos send_files phase). Don't send it. */
150a7c91847Schristos return 1;
151a7c91847Schristos }
152a7c91847Schristos
153a7c91847Schristos /* Try to decide whether we should send arg to the server by
154a7c91847Schristos checking the contents of the corresponding CVSADM directory. */
155a7c91847Schristos {
156a7c91847Schristos char *t, *root_string;
157a7c91847Schristos cvsroot_t *this_root = NULL;
158a7c91847Schristos
159a7c91847Schristos /* Calculate "dirname arg" */
160a7c91847Schristos for (t = arg + strlen (arg) - 1; t >= arg; t--)
161a7c91847Schristos {
162a7c91847Schristos if (ISSLASH (*t))
163a7c91847Schristos break;
164a7c91847Schristos }
165a7c91847Schristos
166a7c91847Schristos /* Now we're either poiting to the beginning of the
167a7c91847Schristos string, or we found a path separator. */
168a7c91847Schristos if (t >= arg)
169a7c91847Schristos {
170a7c91847Schristos /* Found a path separator. */
171a7c91847Schristos char c = *t;
172a7c91847Schristos *t = '\0';
173a7c91847Schristos
174a7c91847Schristos /* First, check to see if we sent this directory to the
175a7c91847Schristos server, because it takes less time than actually
176a7c91847Schristos opening the stuff in the CVSADM directory. */
177a7c91847Schristos if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir,
178a7c91847Schristos arg))
179a7c91847Schristos {
180a7c91847Schristos *t = c; /* make sure to un-truncate the arg */
181a7c91847Schristos return 0;
182a7c91847Schristos }
183a7c91847Schristos
184a7c91847Schristos /* Since we didn't find it in the list, check the CVSADM
185a7c91847Schristos files on disk. */
186a7c91847Schristos this_root = Name_Root (arg, NULL);
187a7c91847Schristos root_string = this_root->original;
188a7c91847Schristos *t = c;
189a7c91847Schristos }
190a7c91847Schristos else
191a7c91847Schristos {
192a7c91847Schristos /* We're at the beginning of the string. Look at the
193a7c91847Schristos CVSADM files in cwd. */
194a7c91847Schristos if (CVSroot_cmdline)
195a7c91847Schristos root_string = CVSroot_cmdline;
196a7c91847Schristos else
197a7c91847Schristos {
198a7c91847Schristos this_root = Name_Root (NULL, NULL);
199a7c91847Schristos root_string = this_root->original;
200a7c91847Schristos }
201a7c91847Schristos }
202a7c91847Schristos
203a7c91847Schristos /* Now check the value for root. */
204a7c91847Schristos if (root_string && current_parsed_root
205a7c91847Schristos && strcmp (root_string, original_parsed_root->original))
206a7c91847Schristos {
207a7c91847Schristos /* Don't send this, since the CVSROOTs don't match. */
208a7c91847Schristos return 1;
209a7c91847Schristos }
210a7c91847Schristos }
211a7c91847Schristos
212a7c91847Schristos /* OK, let's send it. */
213a7c91847Schristos return 0;
214a7c91847Schristos }
215a7c91847Schristos #endif /* CLIENT_SUPPORT */
216a7c91847Schristos
217a7c91847Schristos
218a7c91847Schristos
219a7c91847Schristos #if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
220a7c91847Schristos
221a7c91847Schristos /* Shared with server. */
222a7c91847Schristos
223a7c91847Schristos /*
224a7c91847Schristos * Return a malloc'd, '\0'-terminated string
225a7c91847Schristos * corresponding to the mode in SB.
226a7c91847Schristos */
227a7c91847Schristos char *
mode_to_string(mode_t mode)228a7c91847Schristos mode_to_string (mode_t mode)
229a7c91847Schristos {
230a7c91847Schristos char u[4], g[4], o[4];
231a7c91847Schristos int i;
232a7c91847Schristos
233a7c91847Schristos i = 0;
234a7c91847Schristos if (mode & S_IRUSR) u[i++] = 'r';
235a7c91847Schristos if (mode & S_IWUSR) u[i++] = 'w';
236a7c91847Schristos if (mode & S_IXUSR) u[i++] = 'x';
237a7c91847Schristos u[i] = '\0';
238a7c91847Schristos
239a7c91847Schristos i = 0;
240a7c91847Schristos if (mode & S_IRGRP) g[i++] = 'r';
241a7c91847Schristos if (mode & S_IWGRP) g[i++] = 'w';
242a7c91847Schristos if (mode & S_IXGRP) g[i++] = 'x';
243a7c91847Schristos g[i] = '\0';
244a7c91847Schristos
245a7c91847Schristos i = 0;
246a7c91847Schristos if (mode & S_IROTH) o[i++] = 'r';
247a7c91847Schristos if (mode & S_IWOTH) o[i++] = 'w';
248a7c91847Schristos if (mode & S_IXOTH) o[i++] = 'x';
249a7c91847Schristos o[i] = '\0';
250a7c91847Schristos
251a7c91847Schristos return Xasprintf ("u=%s,g=%s,o=%s", u, g, o);
252a7c91847Schristos }
253a7c91847Schristos
254a7c91847Schristos
255a7c91847Schristos
256a7c91847Schristos /*
257a7c91847Schristos * Change mode of FILENAME to MODE_STRING.
258a7c91847Schristos * Returns 0 for success or errno code.
259a7c91847Schristos * If RESPECT_UMASK is set, then honor the umask.
260a7c91847Schristos */
261a7c91847Schristos int
change_mode(const char * filename,const char * mode_string,int respect_umask)262a7c91847Schristos change_mode (const char *filename, const char *mode_string, int respect_umask)
263a7c91847Schristos {
264a7c91847Schristos #ifdef CHMOD_BROKEN
265a7c91847Schristos char *p;
266a7c91847Schristos int writeable = 0;
267a7c91847Schristos
268a7c91847Schristos /* We can only distinguish between
269a7c91847Schristos 1) readable
270a7c91847Schristos 2) writeable
271a7c91847Schristos 3) Picasso's "Blue Period"
272a7c91847Schristos We handle the first two. */
273a7c91847Schristos p = mode_string;
274a7c91847Schristos while (*p != '\0')
275a7c91847Schristos {
276a7c91847Schristos if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
277a7c91847Schristos {
278a7c91847Schristos char *q = p + 2;
279a7c91847Schristos while (*q != ',' && *q != '\0')
280a7c91847Schristos {
281a7c91847Schristos if (*q == 'w')
282a7c91847Schristos writeable = 1;
283a7c91847Schristos ++q;
284a7c91847Schristos }
285a7c91847Schristos }
286a7c91847Schristos /* Skip to the next field. */
287a7c91847Schristos while (*p != ',' && *p != '\0')
288a7c91847Schristos ++p;
289a7c91847Schristos if (*p == ',')
290a7c91847Schristos ++p;
291a7c91847Schristos }
292a7c91847Schristos
293a7c91847Schristos /* xchmod honors the umask for us. In the !respect_umask case, we
294a7c91847Schristos don't try to cope with it (probably to handle that well, the server
295a7c91847Schristos needs to deal with modes in data structures, rather than via the
296a7c91847Schristos modes in temporary files). */
297a7c91847Schristos xchmod (filename, writeable);
298a7c91847Schristos return 0;
299a7c91847Schristos
300a7c91847Schristos #else /* ! CHMOD_BROKEN */
301a7c91847Schristos
302a7c91847Schristos const char *p;
303a7c91847Schristos mode_t mode = 0;
304a7c91847Schristos mode_t oumask;
305a7c91847Schristos
306a7c91847Schristos p = mode_string;
307a7c91847Schristos while (*p != '\0')
308a7c91847Schristos {
309a7c91847Schristos if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
310a7c91847Schristos {
311a7c91847Schristos int can_read = 0, can_write = 0, can_execute = 0;
312a7c91847Schristos const char *q = p + 2;
313a7c91847Schristos while (*q != ',' && *q != '\0')
314a7c91847Schristos {
315a7c91847Schristos if (*q == 'r')
316a7c91847Schristos can_read = 1;
317a7c91847Schristos else if (*q == 'w')
318a7c91847Schristos can_write = 1;
319a7c91847Schristos else if (*q == 'x')
320a7c91847Schristos can_execute = 1;
321a7c91847Schristos ++q;
322a7c91847Schristos }
323a7c91847Schristos if (p[0] == 'u')
324a7c91847Schristos {
325a7c91847Schristos if (can_read)
326a7c91847Schristos mode |= S_IRUSR;
327a7c91847Schristos if (can_write)
328a7c91847Schristos mode |= S_IWUSR;
329a7c91847Schristos if (can_execute)
330a7c91847Schristos mode |= S_IXUSR;
331a7c91847Schristos }
332a7c91847Schristos else if (p[0] == 'g')
333a7c91847Schristos {
334a7c91847Schristos if (can_read)
335a7c91847Schristos mode |= S_IRGRP;
336a7c91847Schristos if (can_write)
337a7c91847Schristos mode |= S_IWGRP;
338a7c91847Schristos if (can_execute)
339a7c91847Schristos mode |= S_IXGRP;
340a7c91847Schristos }
341a7c91847Schristos else if (p[0] == 'o')
342a7c91847Schristos {
343a7c91847Schristos if (can_read)
344a7c91847Schristos mode |= S_IROTH;
345a7c91847Schristos if (can_write)
346a7c91847Schristos mode |= S_IWOTH;
347a7c91847Schristos if (can_execute)
348a7c91847Schristos mode |= S_IXOTH;
349a7c91847Schristos }
350a7c91847Schristos }
351a7c91847Schristos /* Skip to the next field. */
352a7c91847Schristos while (*p != ',' && *p != '\0')
353a7c91847Schristos ++p;
354a7c91847Schristos if (*p == ',')
355a7c91847Schristos ++p;
356a7c91847Schristos }
357a7c91847Schristos
358a7c91847Schristos if (respect_umask)
359a7c91847Schristos {
360a7c91847Schristos oumask = umask (0);
361a7c91847Schristos (void) umask (oumask);
362a7c91847Schristos mode &= ~oumask;
363a7c91847Schristos }
364a7c91847Schristos
365a7c91847Schristos if (chmod (filename, mode) < 0)
366a7c91847Schristos return errno;
367a7c91847Schristos return 0;
368a7c91847Schristos #endif /* ! CHMOD_BROKEN */
369a7c91847Schristos }
370a7c91847Schristos #endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
371a7c91847Schristos
372a7c91847Schristos
373a7c91847Schristos
374a7c91847Schristos #ifdef CLIENT_SUPPORT
375a7c91847Schristos int client_prune_dirs;
376a7c91847Schristos
377a7c91847Schristos static List *ignlist = NULL;
378a7c91847Schristos
379a7c91847Schristos /* Buffer to write to the server. */
380a7c91847Schristos static struct buffer *global_to_server;
381a7c91847Schristos
382a7c91847Schristos /* Buffer used to read from the server. */
383a7c91847Schristos static struct buffer *global_from_server;
384a7c91847Schristos
385a7c91847Schristos
386a7c91847Schristos
387a7c91847Schristos /*
388a7c91847Schristos * Read a line from the server. Result does not include the terminating \n.
389a7c91847Schristos *
390a7c91847Schristos * Space for the result is malloc'd and should be freed by the caller.
391a7c91847Schristos *
392a7c91847Schristos * Returns number of bytes read.
393a7c91847Schristos */
394a7c91847Schristos static size_t
read_line_via(struct buffer * via_from_buffer,struct buffer * via_to_buffer,char ** resultp)395a7c91847Schristos read_line_via (struct buffer *via_from_buffer, struct buffer *via_to_buffer,
396a7c91847Schristos char **resultp)
397a7c91847Schristos {
398a7c91847Schristos int status;
399a7c91847Schristos char *result;
400a7c91847Schristos size_t len;
401a7c91847Schristos
402a7c91847Schristos status = buf_flush (via_to_buffer, 1);
403a7c91847Schristos if (status != 0)
404a7c91847Schristos error (1, status, "writing to server");
405a7c91847Schristos
406a7c91847Schristos status = buf_read_line (via_from_buffer, &result, &len);
407a7c91847Schristos if (status != 0)
408a7c91847Schristos {
409a7c91847Schristos if (status == -1)
410a7c91847Schristos error (1, 0,
411a7c91847Schristos "end of file from server (consult above messages if any)");
412a7c91847Schristos else if (status == -2)
413a7c91847Schristos error (1, 0, "out of memory");
414a7c91847Schristos else
415a7c91847Schristos error (1, status, "reading from server");
416a7c91847Schristos }
417a7c91847Schristos
418a7c91847Schristos if (resultp)
419a7c91847Schristos *resultp = result;
420a7c91847Schristos else
421a7c91847Schristos free (result);
422a7c91847Schristos
423a7c91847Schristos return len;
424a7c91847Schristos }
425a7c91847Schristos
426a7c91847Schristos
427a7c91847Schristos
428a7c91847Schristos static size_t
read_line(char ** resultp)429a7c91847Schristos read_line (char **resultp)
430a7c91847Schristos {
431a7c91847Schristos return read_line_via (global_from_server, global_to_server, resultp);
432a7c91847Schristos }
433a7c91847Schristos #endif /* CLIENT_SUPPORT */
434a7c91847Schristos
435a7c91847Schristos
436a7c91847Schristos
437a7c91847Schristos #if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
438a7c91847Schristos /*
439a7c91847Schristos * Zero if compression isn't supported or requested; non-zero to indicate
440a7c91847Schristos * a compression level to request from gzip.
441a7c91847Schristos */
442a7c91847Schristos int gzip_level;
443a7c91847Schristos
444a7c91847Schristos /*
445a7c91847Schristos * Level of compression to use when running gzip on a single file.
446a7c91847Schristos */
447a7c91847Schristos int file_gzip_level;
448a7c91847Schristos
449a7c91847Schristos #endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
450a7c91847Schristos
451a7c91847Schristos #ifdef CLIENT_SUPPORT
452a7c91847Schristos
453a7c91847Schristos /* Whether the server asked us to force compression. */
454a7c91847Schristos static bool force_gzip;
455a7c91847Schristos
456a7c91847Schristos /*
457a7c91847Schristos * The Repository for the top level of this command (not necessarily
458a7c91847Schristos * the CVSROOT, just the current directory at the time we do it).
459a7c91847Schristos */
460a7c91847Schristos static char *toplevel_repos;
461a7c91847Schristos
462a7c91847Schristos /* Working directory when we first started. Note: we could speed things
463a7c91847Schristos up on some systems by using savecwd.h here instead of just always
464a7c91847Schristos storing a name. */
465a7c91847Schristos char *toplevel_wd;
466a7c91847Schristos
467a7c91847Schristos
468a7c91847Schristos
469a7c91847Schristos static void
handle_ok(char * args,size_t len)470a7c91847Schristos handle_ok (char *args, size_t len)
471a7c91847Schristos {
472a7c91847Schristos return;
473a7c91847Schristos }
474a7c91847Schristos
475a7c91847Schristos
476a7c91847Schristos
477a7c91847Schristos static void
handle_error(char * args,size_t len)478a7c91847Schristos handle_error (char *args, size_t len)
479a7c91847Schristos {
480a7c91847Schristos int something_printed;
481a7c91847Schristos
482a7c91847Schristos /*
483a7c91847Schristos * First there is a symbolic error code followed by a space, which
484a7c91847Schristos * we ignore.
485a7c91847Schristos */
486a7c91847Schristos char *p = strchr (args, ' ');
487a7c91847Schristos if (!p)
488a7c91847Schristos {
489a7c91847Schristos error (0, 0, "invalid data from cvs server");
490a7c91847Schristos return;
491a7c91847Schristos }
492a7c91847Schristos ++p;
493a7c91847Schristos
494a7c91847Schristos /* Next we print the text of the message from the server. We
495a7c91847Schristos probably should be prefixing it with "server error" or some
496a7c91847Schristos such, because if it is something like "Out of memory", the
497a7c91847Schristos current behavior doesn't say which machine is out of
498a7c91847Schristos memory. */
499a7c91847Schristos
500a7c91847Schristos len -= p - args;
501a7c91847Schristos something_printed = 0;
502a7c91847Schristos for (; len > 0; --len)
503a7c91847Schristos {
504a7c91847Schristos something_printed = 1;
505a7c91847Schristos putc (*p++, stderr);
506a7c91847Schristos }
507a7c91847Schristos if (something_printed)
508a7c91847Schristos putc ('\n', stderr);
509a7c91847Schristos }
510a7c91847Schristos
511a7c91847Schristos
512a7c91847Schristos
513a7c91847Schristos static void
handle_valid_requests(char * args,size_t len)514a7c91847Schristos handle_valid_requests (char *args, size_t len)
515a7c91847Schristos {
516a7c91847Schristos char *p = args;
517a7c91847Schristos char *q;
518a7c91847Schristos struct request *rq;
519a7c91847Schristos do
520a7c91847Schristos {
521a7c91847Schristos q = strchr (p, ' ');
522a7c91847Schristos if (q)
523a7c91847Schristos *q++ = '\0';
524a7c91847Schristos for (rq = requests; rq->name; ++rq)
525a7c91847Schristos {
526a7c91847Schristos if (!strcmp (rq->name, p))
527a7c91847Schristos break;
528a7c91847Schristos }
529a7c91847Schristos if (!rq->name)
530a7c91847Schristos /*
531a7c91847Schristos * It is a request we have never heard of (and thus never
532a7c91847Schristos * will want to use). So don't worry about it.
533a7c91847Schristos */
534a7c91847Schristos ;
535a7c91847Schristos else
536a7c91847Schristos {
537a7c91847Schristos if (rq->flags & RQ_ENABLEME)
538a7c91847Schristos {
539a7c91847Schristos /*
540a7c91847Schristos * Server wants to know if we have this, to enable the
541a7c91847Schristos * feature.
542a7c91847Schristos */
543a7c91847Schristos send_to_server (rq->name, 0);
544a7c91847Schristos send_to_server ("\012", 0);
545a7c91847Schristos }
546a7c91847Schristos else
547a7c91847Schristos rq->flags |= RQ_SUPPORTED;
548a7c91847Schristos }
549a7c91847Schristos p = q;
550a7c91847Schristos } while (q);
551a7c91847Schristos for (rq = requests; rq->name; ++rq)
552a7c91847Schristos {
553a7c91847Schristos if ((rq->flags & RQ_SUPPORTED)
554a7c91847Schristos || (rq->flags & RQ_ENABLEME))
555a7c91847Schristos continue;
556a7c91847Schristos if (rq->flags & RQ_ESSENTIAL)
557a7c91847Schristos error (1, 0, "request `%s' not supported by server", rq->name);
558a7c91847Schristos }
559a7c91847Schristos }
560a7c91847Schristos
561a7c91847Schristos static void
handle_force_gzip(char * args,size_t len)562a7c91847Schristos handle_force_gzip (char *args, size_t len)
563a7c91847Schristos {
564a7c91847Schristos force_gzip = true;
565a7c91847Schristos }
566a7c91847Schristos
567a7c91847Schristos
568a7c91847Schristos
569a7c91847Schristos /* Has the server told us its name since the last redirect?
570a7c91847Schristos */
571a7c91847Schristos static bool referred_since_last_redirect = false;
572a7c91847Schristos static bool free_client_referrer = false;
573a7c91847Schristos
574a7c91847Schristos
575a7c91847Schristos
576a7c91847Schristos static void
handle_referrer(char * args,size_t len)577a7c91847Schristos handle_referrer (char *args, size_t len)
578a7c91847Schristos {
579a7c91847Schristos TRACE (TRACE_FUNCTION, "handle_referrer (%s)", args);
580a7c91847Schristos client_referrer = parse_cvsroot (args);
581a7c91847Schristos referred_since_last_redirect = true;
582a7c91847Schristos free_client_referrer = true;
583a7c91847Schristos }
584a7c91847Schristos
585a7c91847Schristos
586a7c91847Schristos
587a7c91847Schristos /* Redirect our connection to a different server and start over.
588a7c91847Schristos *
589a7c91847Schristos * GLOBALS
590a7c91847Schristos * current_parsed_root The CVSROOT being accessed.
591a7c91847Schristos * client_referrer Used to track the server which referred us to a
592a7c91847Schristos * new server. Can be supplied by the referring
593a7c91847Schristos * server.
594a7c91847Schristos * free_client_referrer Used to track whether the client_referrer needs
595a7c91847Schristos * to be freed before changing it.
596a7c91847Schristos * referred_since_last_redirect
597a7c91847Schristos * Tracks whether the currect server told us how
598a7c91847Schristos * to refer to it.
599a7c91847Schristos *
600a7c91847Schristos * OUTPUTS
601a7c91847Schristos * current_parsed_root Updated to point to the new CVSROOT.
602a7c91847Schristos * referred_since_last_redirect
603a7c91847Schristos * Always cleared.
604a7c91847Schristos * client_referrer Set automatically to current_parsed_root if
605a7c91847Schristos * the current server did not give us a name to
606a7c91847Schristos * refer to it by.
607a7c91847Schristos * free_client_referrer Reset when necessary.
608a7c91847Schristos */
609a7c91847Schristos static void
handle_redirect(char * args,size_t len)610a7c91847Schristos handle_redirect (char *args, size_t len)
611a7c91847Schristos {
612a7c91847Schristos static List *redirects = NULL;
613a7c91847Schristos
614a7c91847Schristos TRACE (TRACE_FUNCTION, "handle_redirect (%s)", args);
615a7c91847Schristos
616a7c91847Schristos if (redirects && findnode (redirects, args))
617a7c91847Schristos error (1, 0, "`Redirect' loop detected. Server misconfiguration?");
618a7c91847Schristos else
619a7c91847Schristos {
620a7c91847Schristos if (!redirects) redirects = getlist();
621a7c91847Schristos push_string (redirects, args);
622a7c91847Schristos }
623a7c91847Schristos
624a7c91847Schristos if (referred_since_last_redirect)
625a7c91847Schristos referred_since_last_redirect = false;
626a7c91847Schristos else
627a7c91847Schristos {
628a7c91847Schristos if (free_client_referrer) free (client_referrer);
629a7c91847Schristos client_referrer = current_parsed_root;
630a7c91847Schristos free_client_referrer = false;
631a7c91847Schristos }
632a7c91847Schristos
633a7c91847Schristos current_parsed_root = parse_cvsroot (args);
634a7c91847Schristos
635a7c91847Schristos /* We deliberately do not set ORIGINAL_PARSED_ROOT here.
636a7c91847Schristos * ORIGINAL_PARSED_ROOT is used by the client to determine the current root
637a7c91847Schristos * being processed for the purpose of looking it up in lists and such, even
638a7c91847Schristos * after a redirect.
639a7c91847Schristos *
640a7c91847Schristos * FIXME
641a7c91847Schristos * CURRENT_PARSED_ROOT should not be reset by this function. Redirects
642a7c91847Schristos * should be "added" to it. The REDIRECTS list should also be replaced
643a7c91847Schristos * by this new CURRENT_PARSED_ROOT element. This way, if, for instance,
644a7c91847Schristos * a multi-root workspace had two secondaries pointing to the same
645a7c91847Schristos * primary, then the client would not report a looping error.
646a7c91847Schristos *
647a7c91847Schristos * There is also a potential memory leak above and storing new roots as
648a7c91847Schristos * part of the original could help avoid it fairly elegantly.
649a7c91847Schristos */
650a7c91847Schristos if (!current_parsed_root)
651a7c91847Schristos error (1, 0, "Server requested redirect to invalid root: `%s'",
652a7c91847Schristos args);
653a7c91847Schristos }
654a7c91847Schristos
655a7c91847Schristos
656a7c91847Schristos
657a7c91847Schristos /*
658a7c91847Schristos * This is a proc for walklist(). It inverts the error return premise of
659a7c91847Schristos * walklist.
660a7c91847Schristos *
661a7c91847Schristos * RETURNS
662a7c91847Schristos * True If this path is prefixed by one of the paths in walklist and
663a7c91847Schristos * does not step above the prefix path.
664a7c91847Schristos * False Otherwise.
665a7c91847Schristos */
666a7c91847Schristos static
path_list_prefixed(Node * p,void * closure)667a7c91847Schristos int path_list_prefixed (Node *p, void *closure)
668a7c91847Schristos {
669a7c91847Schristos const char *questionable = closure;
670a7c91847Schristos const char *prefix = p->key;
671a7c91847Schristos if (strncmp (prefix, questionable, strlen (prefix))) return 0;
672a7c91847Schristos questionable += strlen (prefix);
673a7c91847Schristos while (ISSLASH (*questionable)) questionable++;
674a7c91847Schristos if (*questionable == '\0') return 1;
675a7c91847Schristos return pathname_levels (questionable);
676a7c91847Schristos }
677a7c91847Schristos
678a7c91847Schristos
679a7c91847Schristos
680a7c91847Schristos /*
681a7c91847Schristos * Need to validate the client pathname. Disallowed paths include:
682a7c91847Schristos *
683a7c91847Schristos * 1. Absolute paths.
684a7c91847Schristos * 2. Pathnames that do not reference a specifically requested update
685a7c91847Schristos * directory.
686a7c91847Schristos *
687a7c91847Schristos * In case 2, we actually only check that the directory is under the uppermost
688a7c91847Schristos * directories mentioned on the command line.
689a7c91847Schristos *
690a7c91847Schristos * RETURNS
691a7c91847Schristos * True If the path is valid.
692a7c91847Schristos * False Otherwise.
693a7c91847Schristos */
694a7c91847Schristos static
is_valid_client_path(const char * pathname)695a7c91847Schristos int is_valid_client_path (const char *pathname)
696a7c91847Schristos {
697a7c91847Schristos /* 1. Absolute paths. */
698a7c91847Schristos if (ISABSOLUTE (pathname)) return 0;
699a7c91847Schristos /* 2. No up-references in path. */
700a7c91847Schristos if (pathname_levels (pathname) == 0) return 1;
701a7c91847Schristos /* 2. No Max-dotdot paths registered. */
702a7c91847Schristos if (!uppaths) return 0;
703a7c91847Schristos
704a7c91847Schristos return walklist (uppaths, path_list_prefixed, (void *)pathname);
705a7c91847Schristos }
706a7c91847Schristos
707a7c91847Schristos
708a7c91847Schristos
709a7c91847Schristos /*
710a7c91847Schristos * Do all the processing for PATHNAME, where pathname consists of the
711a7c91847Schristos * repository and the filename. The parameters we pass to FUNC are:
712a7c91847Schristos * DATA is just the DATA parameter which was passed to
713a7c91847Schristos * call_in_directory; ENT_LIST is a pointer to an entries list (which
714a7c91847Schristos * we manage the storage for); SHORT_PATHNAME is the pathname of the
715a7c91847Schristos * file relative to the (overall) directory in which the command is
716a7c91847Schristos * taking place; and FILENAME is the filename portion only of
717a7c91847Schristos * SHORT_PATHNAME. When we call FUNC, the curent directory points to
718a7c91847Schristos * the directory portion of SHORT_PATHNAME. */
719a7c91847Schristos static void
call_in_directory(const char * pathname,void (* func)(void *,List *,const char *,const char *),void * data)720a7c91847Schristos call_in_directory (const char *pathname,
721a7c91847Schristos void (*func) (void *, List *, const char *, const char *),
722a7c91847Schristos void *data)
723a7c91847Schristos {
724a7c91847Schristos /* This variable holds the result of Entries_Open. */
725a7c91847Schristos List *last_entries = NULL;
726a7c91847Schristos char *dir_name;
727a7c91847Schristos char *filename;
728a7c91847Schristos /* This is what we get when we hook up the directory (working directory
729a7c91847Schristos name) from PATHNAME with the filename from REPOSNAME. For example:
730a7c91847Schristos pathname: ccvs/src/
731a7c91847Schristos reposname: /u/src/master/ccvs/foo/ChangeLog
732a7c91847Schristos short_pathname: ccvs/src/ChangeLog
733a7c91847Schristos */
734a7c91847Schristos char *short_pathname;
735a7c91847Schristos char *p;
736a7c91847Schristos
737a7c91847Schristos /*
738a7c91847Schristos * Do the whole descent in parallel for the repositories, so we
739a7c91847Schristos * know what to put in CVS/Repository files. I'm not sure the
740a7c91847Schristos * full hair is necessary since the server does a similar
741a7c91847Schristos * computation; I suspect that we only end up creating one
742a7c91847Schristos * directory at a time anyway.
743a7c91847Schristos *
744a7c91847Schristos * Also note that we must *only* worry about this stuff when we
745a7c91847Schristos * are creating directories; `cvs co foo/bar; cd foo/bar; cvs co
746a7c91847Schristos * CVSROOT; cvs update' is legitimate, but in this case
747a7c91847Schristos * foo/bar/CVSROOT/CVS/Repository is not a subdirectory of
748a7c91847Schristos * foo/bar/CVS/Repository.
749a7c91847Schristos */
750a7c91847Schristos char *reposname;
751a7c91847Schristos char *short_repos;
752a7c91847Schristos char *reposdirname;
753a7c91847Schristos char *rdirp;
754a7c91847Schristos int reposdirname_absolute;
755a7c91847Schristos int newdir = 0;
756a7c91847Schristos
757a7c91847Schristos assert (pathname);
758a7c91847Schristos
759a7c91847Schristos reposname = NULL;
760a7c91847Schristos read_line (&reposname);
761a7c91847Schristos assert (reposname);
762a7c91847Schristos
763a7c91847Schristos reposdirname_absolute = 0;
764a7c91847Schristos if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)))
765a7c91847Schristos {
766a7c91847Schristos reposdirname_absolute = 1;
767a7c91847Schristos short_repos = reposname;
768a7c91847Schristos }
769a7c91847Schristos else
770a7c91847Schristos {
771a7c91847Schristos short_repos = reposname + strlen (toplevel_repos) + 1;
772a7c91847Schristos if (short_repos[-1] != '/')
773a7c91847Schristos {
774a7c91847Schristos reposdirname_absolute = 1;
775a7c91847Schristos short_repos = reposname;
776a7c91847Schristos }
777a7c91847Schristos }
778a7c91847Schristos
779a7c91847Schristos /* Now that we have SHORT_REPOS, we can calculate the path to the file we
780a7c91847Schristos * are being requested to operate on.
781a7c91847Schristos */
782a7c91847Schristos filename = strrchr (short_repos, '/');
783a7c91847Schristos if (!filename)
784a7c91847Schristos filename = short_repos;
785a7c91847Schristos else
786a7c91847Schristos ++filename;
787a7c91847Schristos
788a7c91847Schristos short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5);
789a7c91847Schristos strcpy (short_pathname, pathname);
790a7c91847Schristos strcat (short_pathname, filename);
791a7c91847Schristos
792a7c91847Schristos /* Now that we know the path to the file we were requested to operate on,
793a7c91847Schristos * we can verify that it is valid.
794a7c91847Schristos *
795a7c91847Schristos * For security reasons, if SHORT_PATHNAME is absolute or attempts to
796a7c91847Schristos * ascend outside of the current sanbbox, we abort. The server should not
797a7c91847Schristos * send us anything but relative paths which remain inside the sandbox
798a7c91847Schristos * here. Anything less means a trojan CVS server could create and edit
799a7c91847Schristos * arbitrary files on the client.
800a7c91847Schristos */
801a7c91847Schristos if (!is_valid_client_path (short_pathname))
802a7c91847Schristos {
803a7c91847Schristos error (0, 0,
804a7c91847Schristos "Server attempted to update a file via an invalid pathname:");
805a7c91847Schristos error (1, 0, "`%s'.", short_pathname);
806a7c91847Schristos }
807a7c91847Schristos
808a7c91847Schristos reposdirname = xstrdup (short_repos);
809a7c91847Schristos p = strrchr (reposdirname, '/');
810a7c91847Schristos if (!p)
811a7c91847Schristos {
812a7c91847Schristos reposdirname = xrealloc (reposdirname, 2);
813a7c91847Schristos reposdirname[0] = '.'; reposdirname[1] = '\0';
814a7c91847Schristos }
815a7c91847Schristos else
816a7c91847Schristos *p = '\0';
817a7c91847Schristos
818a7c91847Schristos dir_name = xstrdup (pathname);
819a7c91847Schristos p = strrchr (dir_name, '/');
820a7c91847Schristos if (!p)
821a7c91847Schristos {
822a7c91847Schristos dir_name = xrealloc (dir_name, 2);
823a7c91847Schristos dir_name[0] = '.'; dir_name[1] = '\0';
824a7c91847Schristos }
825a7c91847Schristos else
826a7c91847Schristos *p = '\0';
827a7c91847Schristos if (client_prune_dirs)
828a7c91847Schristos add_prune_candidate (dir_name);
829a7c91847Schristos
830a7c91847Schristos if (!toplevel_wd)
831a7c91847Schristos {
832a7c91847Schristos toplevel_wd = xgetcwd ();
833a7c91847Schristos if (!toplevel_wd)
834a7c91847Schristos error (1, errno, "could not get working directory");
835a7c91847Schristos }
836a7c91847Schristos
837a7c91847Schristos if (CVS_CHDIR (toplevel_wd) < 0)
838a7c91847Schristos error (1, errno, "could not chdir to %s", toplevel_wd);
839a7c91847Schristos
840a7c91847Schristos /* Create the CVS directory at the top level if needed. The
841a7c91847Schristos isdir seems like an unneeded system call, but it *does*
842a7c91847Schristos need to be called both if the CVS_CHDIR below succeeds
843a7c91847Schristos (e.g. "cvs co .") or if it fails (e.g. basicb-1a in
844a7c91847Schristos testsuite). We only need to do this for the "." case,
845a7c91847Schristos since the server takes care of forcing this directory to be
846a7c91847Schristos created in all other cases. If we don't create CVSADM
847a7c91847Schristos here, the call to Entries_Open below will fail. FIXME:
848a7c91847Schristos perhaps this means that we should change our algorithm
849a7c91847Schristos below that calls Create_Admin instead of having this code
850a7c91847Schristos here? */
851a7c91847Schristos if (/* I think the reposdirname_absolute case has to do with
852a7c91847Schristos things like "cvs update /foo/bar". In any event, the
853a7c91847Schristos code below which tries to put toplevel_repos into
854a7c91847Schristos CVS/Repository is almost surely unsuited to
855a7c91847Schristos the reposdirname_absolute case. */
856a7c91847Schristos !reposdirname_absolute
857a7c91847Schristos && !strcmp (dir_name, ".")
858a7c91847Schristos && ! isdir (CVSADM))
859a7c91847Schristos {
860a7c91847Schristos char *repo;
861a7c91847Schristos char *r;
862a7c91847Schristos
863a7c91847Schristos newdir = 1;
864a7c91847Schristos
865a7c91847Schristos /* If toplevel_repos doesn't have at least one character, then the
866a7c91847Schristos * reference to r[-1] below could be out of bounds.
867a7c91847Schristos */
868a7c91847Schristos assert (*toplevel_repos);
869a7c91847Schristos
870a7c91847Schristos repo = xmalloc (strlen (toplevel_repos)
871a7c91847Schristos + 10);
872a7c91847Schristos strcpy (repo, toplevel_repos);
873a7c91847Schristos r = repo + strlen (repo);
874a7c91847Schristos if (r[-1] != '.' || r[-2] != '/')
875a7c91847Schristos strcpy (r, "/.");
876a7c91847Schristos
877a7c91847Schristos Create_Admin (".", ".", repo, NULL, NULL, 0, 1, 1);
878a7c91847Schristos
879a7c91847Schristos free (repo);
880a7c91847Schristos }
881a7c91847Schristos
882a7c91847Schristos if (CVS_CHDIR (dir_name) < 0)
883a7c91847Schristos {
884a7c91847Schristos char *dir;
885a7c91847Schristos char *dirp;
886a7c91847Schristos
887a7c91847Schristos if (! existence_error (errno))
888a7c91847Schristos error (1, errno, "could not chdir to %s", dir_name);
889a7c91847Schristos
890a7c91847Schristos /* Directory does not exist, we need to create it. */
891a7c91847Schristos newdir = 1;
892a7c91847Schristos
893a7c91847Schristos /* Provided we are willing to assume that directories get
894a7c91847Schristos created one at a time, we could simplify this a lot.
895a7c91847Schristos Do note that one aspect still would need to walk the
896a7c91847Schristos dir_name path: the checking for "fncmp (dir, CVSADM)". */
897a7c91847Schristos
898a7c91847Schristos dir = xmalloc (strlen (dir_name) + 1);
899a7c91847Schristos dirp = dir_name;
900a7c91847Schristos rdirp = reposdirname;
901a7c91847Schristos
902a7c91847Schristos /* This algorithm makes nested directories one at a time
903a7c91847Schristos and create CVS administration files in them. For
904a7c91847Schristos example, we're checking out foo/bar/baz from the
905a7c91847Schristos repository:
906a7c91847Schristos
907a7c91847Schristos 1) create foo, point CVS/Repository to <root>/foo
908a7c91847Schristos 2) .. foo/bar .. <root>/foo/bar
909a7c91847Schristos 3) .. foo/bar/baz .. <root>/foo/bar/baz
910a7c91847Schristos
911a7c91847Schristos As you can see, we're just stepping along DIR_NAME (with
912a7c91847Schristos DIRP) and REPOSDIRNAME (with RDIRP) respectively.
913a7c91847Schristos
914a7c91847Schristos We need to be careful when we are checking out a
915a7c91847Schristos module, however, since DIR_NAME and REPOSDIRNAME are not
916a7c91847Schristos going to be the same. Since modules will not have any
917a7c91847Schristos slashes in their names, we should watch the output of
918a7c91847Schristos STRCHR to decide whether or not we should use STRCHR on
919a7c91847Schristos the RDIRP. That is, if we're down to a module name,
920a7c91847Schristos don't keep picking apart the repository directory name. */
921a7c91847Schristos
922a7c91847Schristos do
923a7c91847Schristos {
924a7c91847Schristos dirp = strchr (dirp, '/');
925a7c91847Schristos if (dirp)
926a7c91847Schristos {
927a7c91847Schristos strncpy (dir, dir_name, dirp - dir_name);
928a7c91847Schristos dir[dirp - dir_name] = '\0';
929a7c91847Schristos /* Skip the slash. */
930a7c91847Schristos ++dirp;
931a7c91847Schristos if (!rdirp)
932a7c91847Schristos /* This just means that the repository string has
933a7c91847Schristos fewer components than the dir_name string. But
934a7c91847Schristos that is OK (e.g. see modules3-8 in testsuite). */
935a7c91847Schristos ;
936a7c91847Schristos else
937a7c91847Schristos rdirp = strchr (rdirp, '/');
938a7c91847Schristos }
939a7c91847Schristos else
940a7c91847Schristos {
941a7c91847Schristos /* If there are no more slashes in the dir name,
942a7c91847Schristos we're down to the most nested directory -OR- to
943a7c91847Schristos the name of a module. In the first case, we
944a7c91847Schristos should be down to a DIRP that has no slashes,
945a7c91847Schristos so it won't help/hurt to do another STRCHR call
946a7c91847Schristos on DIRP. It will definitely hurt, however, if
947a7c91847Schristos we're down to a module name, since a module
948a7c91847Schristos name can point to a nested directory (that is,
949a7c91847Schristos DIRP will still have slashes in it. Therefore,
950a7c91847Schristos we should set it to NULL so the routine below
951a7c91847Schristos copies the contents of REMOTEDIRNAME onto the
952a7c91847Schristos root repository directory (does this if rdirp
953a7c91847Schristos is set to NULL, because we used to do an extra
954a7c91847Schristos STRCHR call here). */
955a7c91847Schristos
956a7c91847Schristos rdirp = NULL;
957a7c91847Schristos strcpy (dir, dir_name);
958a7c91847Schristos }
959a7c91847Schristos
960a7c91847Schristos if (fncmp (dir, CVSADM) == 0)
961a7c91847Schristos {
962a7c91847Schristos error (0, 0, "cannot create a directory named %s", dir);
963a7c91847Schristos error (0, 0, "because CVS uses \"%s\" for its own uses",
964a7c91847Schristos CVSADM);
965a7c91847Schristos error (1, 0, "rename the directory and try again");
966a7c91847Schristos }
967a7c91847Schristos
968a7c91847Schristos if (mkdir_if_needed (dir))
969a7c91847Schristos {
970a7c91847Schristos /* It already existed, fine. Just keep going. */
971a7c91847Schristos }
972a7c91847Schristos else if (!strcmp (cvs_cmd_name, "export"))
973a7c91847Schristos /* Don't create CVSADM directories if this is export. */
974a7c91847Schristos ;
975a7c91847Schristos else
976a7c91847Schristos {
977a7c91847Schristos /*
978a7c91847Schristos * Put repository in CVS/Repository. For historical
979a7c91847Schristos * (pre-CVS/Root) reasons, this is an absolute pathname,
980a7c91847Schristos * but what really matters is the part of it which is
981a7c91847Schristos * relative to cvsroot.
982a7c91847Schristos */
983a7c91847Schristos char *repo;
984a7c91847Schristos char *r, *b;
985a7c91847Schristos
986a7c91847Schristos repo = xmalloc (strlen (reposdirname)
987a7c91847Schristos + strlen (toplevel_repos)
988a7c91847Schristos + 80);
989a7c91847Schristos if (reposdirname_absolute)
990a7c91847Schristos r = repo;
991a7c91847Schristos else
992a7c91847Schristos {
993a7c91847Schristos strcpy (repo, toplevel_repos);
994a7c91847Schristos strcat (repo, "/");
995a7c91847Schristos r = repo + strlen (repo);
996a7c91847Schristos }
997a7c91847Schristos
998a7c91847Schristos if (rdirp)
999a7c91847Schristos {
1000a7c91847Schristos /* See comment near start of function; the only
1001a7c91847Schristos way that the server can put the right thing
1002a7c91847Schristos in each CVS/Repository file is to create the
1003a7c91847Schristos directories one at a time. I think that the
1004a7c91847Schristos CVS server has been doing this all along. */
1005a7c91847Schristos error (0, 0, "\
1006a7c91847Schristos warning: server is not creating directories one at a time");
1007a7c91847Schristos strncpy (r, reposdirname, rdirp - reposdirname);
1008a7c91847Schristos r[rdirp - reposdirname] = '\0';
1009a7c91847Schristos }
1010a7c91847Schristos else
1011a7c91847Schristos strcpy (r, reposdirname);
1012a7c91847Schristos
1013a7c91847Schristos Create_Admin (dir, dir, repo, NULL, NULL, 0, 0, 1);
1014a7c91847Schristos free (repo);
1015a7c91847Schristos
1016a7c91847Schristos b = strrchr (dir, '/');
1017a7c91847Schristos if (!b)
1018a7c91847Schristos Subdir_Register (NULL, NULL, dir);
1019a7c91847Schristos else
1020a7c91847Schristos {
1021a7c91847Schristos *b = '\0';
1022a7c91847Schristos Subdir_Register (NULL, dir, b + 1);
1023a7c91847Schristos *b = '/';
1024a7c91847Schristos }
1025a7c91847Schristos }
1026a7c91847Schristos
1027a7c91847Schristos if (rdirp)
1028a7c91847Schristos {
1029a7c91847Schristos /* Skip the slash. */
1030a7c91847Schristos ++rdirp;
1031a7c91847Schristos }
1032a7c91847Schristos
1033a7c91847Schristos } while (dirp);
1034a7c91847Schristos free (dir);
1035a7c91847Schristos /* Now it better work. */
1036a7c91847Schristos if (CVS_CHDIR (dir_name) < 0)
1037a7c91847Schristos error (1, errno, "could not chdir to %s", dir_name);
1038a7c91847Schristos }
1039a7c91847Schristos else if (!strcmp (cvs_cmd_name, "export"))
1040a7c91847Schristos /* Don't create CVSADM directories if this is export. */
1041a7c91847Schristos ;
1042a7c91847Schristos else if (!isdir (CVSADM))
1043a7c91847Schristos {
1044a7c91847Schristos /*
1045a7c91847Schristos * Put repository in CVS/Repository. For historical
1046a7c91847Schristos * (pre-CVS/Root) reasons, this is an absolute pathname,
1047a7c91847Schristos * but what really matters is the part of it which is
1048a7c91847Schristos * relative to cvsroot.
1049a7c91847Schristos */
1050a7c91847Schristos char *repo;
1051a7c91847Schristos
1052a7c91847Schristos if (reposdirname_absolute)
1053a7c91847Schristos repo = reposdirname;
1054a7c91847Schristos else
1055a7c91847Schristos repo = Xasprintf ("%s/%s", toplevel_repos, reposdirname);
1056a7c91847Schristos
1057a7c91847Schristos Create_Admin (".", ".", repo, NULL, NULL, 0, 1, 1);
1058a7c91847Schristos if (repo != reposdirname)
1059a7c91847Schristos free (repo);
1060a7c91847Schristos }
1061a7c91847Schristos
1062a7c91847Schristos if (strcmp (cvs_cmd_name, "export"))
1063a7c91847Schristos {
1064a7c91847Schristos last_entries = Entries_Open (0, dir_name);
1065a7c91847Schristos
1066a7c91847Schristos /* If this is a newly created directory, we will record
1067a7c91847Schristos all subdirectory information, so call Subdirs_Known in
1068a7c91847Schristos case there are no subdirectories. If this is not a
1069a7c91847Schristos newly created directory, it may be an old working
1070a7c91847Schristos directory from before we recorded subdirectory
1071a7c91847Schristos information in the Entries file. We force a search for
1072a7c91847Schristos all subdirectories now, to make sure our subdirectory
1073a7c91847Schristos information is up to date. If the Entries file does
1074a7c91847Schristos record subdirectory information, then this call only
1075a7c91847Schristos does list manipulation. */
1076a7c91847Schristos if (newdir)
1077a7c91847Schristos Subdirs_Known (last_entries);
1078a7c91847Schristos else
1079a7c91847Schristos {
1080a7c91847Schristos List *dirlist;
1081a7c91847Schristos
1082a7c91847Schristos dirlist = Find_Directories (NULL, W_LOCAL, last_entries);
1083a7c91847Schristos dellist (&dirlist);
1084a7c91847Schristos }
1085a7c91847Schristos }
1086a7c91847Schristos free (reposdirname);
1087a7c91847Schristos (*func) (data, last_entries, short_pathname, filename);
1088a7c91847Schristos if (last_entries)
1089a7c91847Schristos Entries_Close (last_entries);
1090a7c91847Schristos free (dir_name);
1091a7c91847Schristos free (short_pathname);
1092a7c91847Schristos free (reposname);
1093a7c91847Schristos }
1094a7c91847Schristos
1095a7c91847Schristos
1096a7c91847Schristos
1097a7c91847Schristos static void
copy_a_file(void * data,List * ent_list,const char * short_pathname,const char * filename)1098a7c91847Schristos copy_a_file (void *data, List *ent_list, const char *short_pathname,
1099a7c91847Schristos const char *filename)
1100a7c91847Schristos {
1101a7c91847Schristos char *newname;
1102a7c91847Schristos
1103a7c91847Schristos read_line (&newname);
1104a7c91847Schristos
1105a7c91847Schristos #ifdef USE_VMS_FILENAMES
1106a7c91847Schristos {
1107a7c91847Schristos /* Mogrify the filename so VMS is happy with it. */
1108a7c91847Schristos char *p;
1109a7c91847Schristos for(p = newname; *p; p++)
1110a7c91847Schristos if(*p == '.' || *p == '#') *p = '_';
1111a7c91847Schristos }
1112a7c91847Schristos #endif
1113a7c91847Schristos /* cvsclient.texi has said for a long time that newname must be in the
1114a7c91847Schristos same directory. Wouldn't want a malicious or buggy server overwriting
1115a7c91847Schristos ~/.profile, /etc/passwd, or anything like that. */
1116a7c91847Schristos if (last_component (newname) != newname)
1117a7c91847Schristos error (1, 0, "protocol error: Copy-file tried to specify directory");
1118a7c91847Schristos
1119a7c91847Schristos if (unlink_file (newname) && !existence_error (errno))
1120a7c91847Schristos error (0, errno, "unable to remove %s", newname);
1121a7c91847Schristos copy_file (filename, newname);
1122a7c91847Schristos free (newname);
1123a7c91847Schristos }
1124a7c91847Schristos
1125a7c91847Schristos
1126a7c91847Schristos
1127a7c91847Schristos static void
handle_copy_file(char * args,size_t len)1128a7c91847Schristos handle_copy_file (char *args, size_t len)
1129a7c91847Schristos {
1130a7c91847Schristos call_in_directory (args, copy_a_file, NULL);
1131a7c91847Schristos }
1132a7c91847Schristos
1133a7c91847Schristos
1134a7c91847Schristos
1135a7c91847Schristos /* Read from the server the count for the length of a file, then read
1136a7c91847Schristos the contents of that file and write them to FILENAME. FULLNAME is
1137a7c91847Schristos the name of the file for use in error messages. FIXME-someday:
1138a7c91847Schristos extend this to deal with compressed files and make update_entries
1139a7c91847Schristos use it. On error, gives a fatal error. */
1140a7c91847Schristos static void
read_counted_file(const char * filename,const char * fullname)1141274254cdSchristos read_counted_file (const char *filename, const char *fullname)
1142a7c91847Schristos {
1143a7c91847Schristos char *size_string;
1144a7c91847Schristos size_t size;
1145a7c91847Schristos char *buf;
1146a7c91847Schristos
1147a7c91847Schristos /* Pointers in buf to the place to put data which will be read,
1148a7c91847Schristos and the data which needs to be written, respectively. */
1149a7c91847Schristos char *pread;
1150a7c91847Schristos char *pwrite;
1151a7c91847Schristos /* Number of bytes left to read and number of bytes in buf waiting to
1152a7c91847Schristos be written, respectively. */
1153a7c91847Schristos size_t nread;
1154a7c91847Schristos size_t nwrite;
1155a7c91847Schristos
1156a7c91847Schristos FILE *fp;
1157a7c91847Schristos
1158a7c91847Schristos read_line (&size_string);
1159a7c91847Schristos if (size_string[0] == 'z')
1160a7c91847Schristos error (1, 0, "\
1161a7c91847Schristos protocol error: compressed files not supported for that operation");
1162a7c91847Schristos /* FIXME: should be doing more error checking, probably. Like using
1163a7c91847Schristos strtoul and making sure we used up the whole line. */
1164a7c91847Schristos size = atoi (size_string);
1165a7c91847Schristos free (size_string);
1166a7c91847Schristos
1167a7c91847Schristos /* A more sophisticated implementation would use only a limited amount
1168a7c91847Schristos of buffer space (8K perhaps), and read that much at a time. We allocate
1169a7c91847Schristos a buffer for the whole file only to make it easy to keep track what
1170a7c91847Schristos needs to be read and written. */
1171a7c91847Schristos buf = xmalloc (size);
1172a7c91847Schristos
1173a7c91847Schristos /* FIXME-someday: caller should pass in a flag saying whether it
1174a7c91847Schristos is binary or not. I haven't carefully looked into whether
1175a7c91847Schristos CVS/Template files should use local text file conventions or
1176a7c91847Schristos not. */
1177a7c91847Schristos fp = CVS_FOPEN (filename, "wb");
1178a7c91847Schristos if (!fp)
1179a7c91847Schristos error (1, errno, "cannot write %s", fullname);
1180a7c91847Schristos nread = size;
1181a7c91847Schristos nwrite = 0;
1182a7c91847Schristos pread = buf;
1183a7c91847Schristos pwrite = buf;
1184a7c91847Schristos while (nread > 0 || nwrite > 0)
1185a7c91847Schristos {
1186a7c91847Schristos size_t n;
1187a7c91847Schristos
1188a7c91847Schristos if (nread > 0)
1189a7c91847Schristos {
1190a7c91847Schristos n = try_read_from_server (pread, nread);
1191a7c91847Schristos nread -= n;
1192a7c91847Schristos pread += n;
1193a7c91847Schristos nwrite += n;
1194a7c91847Schristos }
1195a7c91847Schristos
1196a7c91847Schristos if (nwrite > 0)
1197a7c91847Schristos {
1198a7c91847Schristos n = fwrite (pwrite, sizeof *pwrite, nwrite, fp);
1199a7c91847Schristos if (ferror (fp))
1200a7c91847Schristos error (1, errno, "cannot write %s", fullname);
1201a7c91847Schristos nwrite -= n;
1202a7c91847Schristos pwrite += n;
1203a7c91847Schristos }
1204a7c91847Schristos }
1205a7c91847Schristos free (buf);
1206a7c91847Schristos if (fclose (fp) < 0)
1207a7c91847Schristos error (1, errno, "cannot close %s", fullname);
1208a7c91847Schristos }
1209a7c91847Schristos
1210a7c91847Schristos
1211a7c91847Schristos
1212a7c91847Schristos /* OK, we want to swallow the "U foo.c" response and then output it only
1213a7c91847Schristos if we can update the file. In the future we probably want some more
1214a7c91847Schristos systematic approach to parsing tagged text, but for now we keep it
1215a7c91847Schristos ad hoc. "Why," I hear you cry, "do we not just look at the
1216a7c91847Schristos Update-existing and Created responses?" That is an excellent question,
1217a7c91847Schristos and the answer is roughly conservatism/laziness--I haven't read through
1218a7c91847Schristos update.c enough to figure out the exact correspondence or lack thereof
1219a7c91847Schristos between those responses and a "U foo.c" line (note that Merged, from
1220a7c91847Schristos join_file, can be either "C foo" or "U foo" depending on the context). */
1221a7c91847Schristos /* Nonzero if we have seen +updated and not -updated. */
1222a7c91847Schristos static int updated_seen;
1223a7c91847Schristos /* Filename from an "fname" tagged response within +updated/-updated. */
1224a7c91847Schristos static char *updated_fname;
1225a7c91847Schristos
1226a7c91847Schristos /* This struct is used to hold data when reading the +importmergecmd
1227a7c91847Schristos and -importmergecmd tags. We put the variables in a struct only
1228a7c91847Schristos for namespace issues. FIXME: As noted above, we need to develop a
1229a7c91847Schristos more systematic approach. */
1230a7c91847Schristos static struct
1231a7c91847Schristos {
1232a7c91847Schristos /* Nonzero if we have seen +importmergecmd and not -importmergecmd. */
1233a7c91847Schristos int seen;
1234a7c91847Schristos /* Number of conflicts, from a "conflicts" tagged response. */
1235a7c91847Schristos int conflicts;
1236a7c91847Schristos /* First merge tag, from a "mergetag1" tagged response. */
1237a7c91847Schristos char *mergetag1;
1238a7c91847Schristos /* Second merge tag, from a "mergetag2" tagged response. */
1239a7c91847Schristos char *mergetag2;
1240a7c91847Schristos /* Repository, from a "repository" tagged response. */
1241a7c91847Schristos char *repository;
1242a7c91847Schristos } importmergecmd;
1243a7c91847Schristos
1244a7c91847Schristos /* Nonzero if we should arrange to return with a failure exit status. */
1245a7c91847Schristos static bool failure_exit;
1246a7c91847Schristos
1247a7c91847Schristos
1248a7c91847Schristos /*
1249a7c91847Schristos * The time stamp of the last file we registered.
1250a7c91847Schristos */
1251a7c91847Schristos static time_t last_register_time;
1252a7c91847Schristos
1253a7c91847Schristos
1254a7c91847Schristos
1255a7c91847Schristos /*
1256a7c91847Schristos * The Checksum response gives the checksum for the file transferred
1257a7c91847Schristos * over by the next Updated, Merged or Patch response. We just store
1258a7c91847Schristos * it here, and then check it in update_entries.
1259a7c91847Schristos */
1260a7c91847Schristos static int stored_checksum_valid;
1261a7c91847Schristos static unsigned char stored_checksum[16];
1262a7c91847Schristos static void
handle_checksum(char * args,size_t len)1263a7c91847Schristos handle_checksum (char *args, size_t len)
1264a7c91847Schristos {
1265a7c91847Schristos char *s;
1266a7c91847Schristos char buf[3];
1267a7c91847Schristos int i;
1268a7c91847Schristos
1269a7c91847Schristos if (stored_checksum_valid)
1270a7c91847Schristos error (1, 0, "Checksum received before last one was used");
1271a7c91847Schristos
1272a7c91847Schristos s = args;
1273a7c91847Schristos buf[2] = '\0';
1274a7c91847Schristos for (i = 0; i < 16; i++)
1275a7c91847Schristos {
1276a7c91847Schristos char *bufend;
1277a7c91847Schristos
1278a7c91847Schristos buf[0] = *s++;
1279a7c91847Schristos buf[1] = *s++;
1280a7c91847Schristos stored_checksum[i] = (char) strtol (buf, &bufend, 16);
1281a7c91847Schristos if (bufend != buf + 2)
1282a7c91847Schristos break;
1283a7c91847Schristos }
1284a7c91847Schristos
1285a7c91847Schristos if (i < 16 || *s != '\0')
1286a7c91847Schristos error (1, 0, "Invalid Checksum response: `%s'", args);
1287a7c91847Schristos
1288a7c91847Schristos stored_checksum_valid = 1;
1289a7c91847Schristos }
1290a7c91847Schristos
1291a7c91847Schristos
1292a7c91847Schristos
1293a7c91847Schristos /* Mode that we got in a "Mode" response (malloc'd), or NULL if none. */
1294a7c91847Schristos static char *stored_mode;
1295a7c91847Schristos static void
handle_mode(char * args,size_t len)1296a7c91847Schristos handle_mode (char *args, size_t len)
1297a7c91847Schristos {
1298a7c91847Schristos if (stored_mode)
1299a7c91847Schristos error (1, 0, "protocol error: duplicate Mode");
1300a7c91847Schristos stored_mode = xstrdup (args);
1301a7c91847Schristos }
1302a7c91847Schristos
1303a7c91847Schristos
1304a7c91847Schristos
1305a7c91847Schristos /* Nonzero if time was specified in Mod-time. */
1306a7c91847Schristos static int stored_modtime_valid;
1307a7c91847Schristos /* Time specified in Mod-time. */
1308a7c91847Schristos static time_t stored_modtime;
1309a7c91847Schristos static void
handle_mod_time(char * args,size_t len)1310a7c91847Schristos handle_mod_time (char *args, size_t len)
1311a7c91847Schristos {
1312a7c91847Schristos struct timespec newtime;
1313a7c91847Schristos if (stored_modtime_valid)
1314a7c91847Schristos error (0, 0, "protocol error: duplicate Mod-time");
1315a7c91847Schristos if (get_date (&newtime, args, NULL))
1316a7c91847Schristos {
1317a7c91847Schristos /* Truncate nanoseconds. */
1318a7c91847Schristos stored_modtime = newtime.tv_sec;
1319a7c91847Schristos stored_modtime_valid = 1;
1320a7c91847Schristos }
1321a7c91847Schristos else
1322a7c91847Schristos error (0, 0, "protocol error: cannot parse date %s", args);
1323a7c91847Schristos }
1324a7c91847Schristos
1325a7c91847Schristos
1326a7c91847Schristos
1327a7c91847Schristos /*
1328a7c91847Schristos * If we receive a patch, but the patch program fails to apply it, we
1329a7c91847Schristos * want to request the original file. We keep a list of files whose
1330a7c91847Schristos * patches have failed.
1331a7c91847Schristos */
1332a7c91847Schristos
1333a7c91847Schristos char **failed_patches;
1334a7c91847Schristos int failed_patches_count;
1335a7c91847Schristos
1336a7c91847Schristos struct update_entries_data
1337a7c91847Schristos {
1338a7c91847Schristos enum {
1339a7c91847Schristos /*
1340a7c91847Schristos * We are just getting an Entries line; the local file is
1341a7c91847Schristos * correct.
1342a7c91847Schristos */
1343a7c91847Schristos UPDATE_ENTRIES_CHECKIN,
1344a7c91847Schristos /* We are getting the file contents as well. */
1345a7c91847Schristos UPDATE_ENTRIES_UPDATE,
1346a7c91847Schristos /*
1347a7c91847Schristos * We are getting a patch against the existing local file, not
1348a7c91847Schristos * an entire new file.
1349a7c91847Schristos */
1350a7c91847Schristos UPDATE_ENTRIES_PATCH,
1351a7c91847Schristos /*
1352a7c91847Schristos * We are getting an RCS change text (diff -n output) against
1353a7c91847Schristos * the existing local file, not an entire new file.
1354a7c91847Schristos */
1355a7c91847Schristos UPDATE_ENTRIES_RCS_DIFF
1356a7c91847Schristos } contents;
1357a7c91847Schristos
1358a7c91847Schristos enum {
1359a7c91847Schristos /* We are replacing an existing file. */
1360a7c91847Schristos UPDATE_ENTRIES_EXISTING,
1361a7c91847Schristos /* We are creating a new file. */
1362a7c91847Schristos UPDATE_ENTRIES_NEW,
1363a7c91847Schristos /* We don't know whether it is existing or new. */
1364a7c91847Schristos UPDATE_ENTRIES_EXISTING_OR_NEW
1365a7c91847Schristos } existp;
1366a7c91847Schristos
1367a7c91847Schristos /*
1368a7c91847Schristos * String to put in the timestamp field or NULL to use the timestamp
1369a7c91847Schristos * of the file.
1370a7c91847Schristos */
1371a7c91847Schristos char *timestamp;
1372a7c91847Schristos };
1373a7c91847Schristos
1374a7c91847Schristos
1375a7c91847Schristos
1376a7c91847Schristos /* Update the Entries line for this file. */
1377a7c91847Schristos static void
update_entries(void * data_arg,List * ent_list,const char * short_pathname,const char * filename)1378a7c91847Schristos update_entries (void *data_arg, List *ent_list, const char *short_pathname,
1379a7c91847Schristos const char *filename)
1380a7c91847Schristos {
1381a7c91847Schristos char *entries_line;
1382a7c91847Schristos struct update_entries_data *data = data_arg;
1383a7c91847Schristos
1384a7c91847Schristos char *cp;
1385a7c91847Schristos char *user;
1386a7c91847Schristos char *vn;
1387a7c91847Schristos /* Timestamp field. Always empty according to the protocol. */
1388a7c91847Schristos char *ts;
1389a7c91847Schristos char *options = NULL;
1390a7c91847Schristos char *tag = NULL;
1391a7c91847Schristos char *date = NULL;
1392a7c91847Schristos char *tag_or_date;
1393a7c91847Schristos char *scratch_entries = NULL;
1394a7c91847Schristos int bin;
1395a7c91847Schristos
1396a7c91847Schristos #ifdef UTIME_EXPECTS_WRITABLE
1397a7c91847Schristos int change_it_back = 0;
1398a7c91847Schristos #endif
1399a7c91847Schristos
1400a7c91847Schristos read_line (&entries_line);
1401a7c91847Schristos
1402a7c91847Schristos /*
1403a7c91847Schristos * Parse the entries line.
1404a7c91847Schristos */
1405a7c91847Schristos scratch_entries = xstrdup (entries_line);
1406a7c91847Schristos
1407a7c91847Schristos if (scratch_entries[0] != '/')
1408a7c91847Schristos error (1, 0, "bad entries line `%s' from server", entries_line);
1409a7c91847Schristos user = scratch_entries + 1;
1410a7c91847Schristos if (!(cp = strchr (user, '/')))
1411a7c91847Schristos error (1, 0, "bad entries line `%s' from server", entries_line);
1412a7c91847Schristos *cp++ = '\0';
1413a7c91847Schristos vn = cp;
1414a7c91847Schristos if (!(cp = strchr (vn, '/')))
1415a7c91847Schristos error (1, 0, "bad entries line `%s' from server", entries_line);
1416a7c91847Schristos *cp++ = '\0';
1417a7c91847Schristos
1418a7c91847Schristos ts = cp;
1419a7c91847Schristos if (!(cp = strchr (ts, '/')))
1420a7c91847Schristos error (1, 0, "bad entries line `%s' from server", entries_line);
1421a7c91847Schristos *cp++ = '\0';
1422a7c91847Schristos options = cp;
1423a7c91847Schristos if (!(cp = strchr (options, '/')))
1424a7c91847Schristos error (1, 0, "bad entries line `%s' from server", entries_line);
1425a7c91847Schristos *cp++ = '\0';
1426a7c91847Schristos tag_or_date = cp;
1427a7c91847Schristos
1428a7c91847Schristos /* If a slash ends the tag_or_date, ignore everything after it. */
1429a7c91847Schristos cp = strchr (tag_or_date, '/');
1430a7c91847Schristos if (cp)
1431a7c91847Schristos *cp = '\0';
1432a7c91847Schristos if (*tag_or_date == 'T')
1433a7c91847Schristos tag = tag_or_date + 1;
1434a7c91847Schristos else if (*tag_or_date == 'D')
1435a7c91847Schristos date = tag_or_date + 1;
1436a7c91847Schristos
1437a7c91847Schristos /* Done parsing the entries line. */
1438a7c91847Schristos
1439a7c91847Schristos if (data->contents == UPDATE_ENTRIES_UPDATE
1440a7c91847Schristos || data->contents == UPDATE_ENTRIES_PATCH
1441a7c91847Schristos || data->contents == UPDATE_ENTRIES_RCS_DIFF)
1442a7c91847Schristos {
1443a7c91847Schristos char *size_string;
1444a7c91847Schristos char *mode_string;
1445a7c91847Schristos int size;
1446a7c91847Schristos char *buf;
1447a7c91847Schristos char *temp_filename;
1448a7c91847Schristos int use_gzip;
1449a7c91847Schristos int patch_failed;
1450a7c91847Schristos
1451a7c91847Schristos read_line (&mode_string);
1452a7c91847Schristos
1453a7c91847Schristos read_line (&size_string);
1454a7c91847Schristos if (size_string[0] == 'z')
1455a7c91847Schristos {
1456a7c91847Schristos use_gzip = 1;
1457a7c91847Schristos size = atoi (size_string+1);
1458a7c91847Schristos }
1459a7c91847Schristos else
1460a7c91847Schristos {
1461a7c91847Schristos use_gzip = 0;
1462a7c91847Schristos size = atoi (size_string);
1463a7c91847Schristos }
1464a7c91847Schristos free (size_string);
1465a7c91847Schristos
1466a7c91847Schristos /* Note that checking this separately from writing the file is
1467a7c91847Schristos a race condition: if the existence or lack thereof of the
1468a7c91847Schristos file changes between now and the actual calls which
1469a7c91847Schristos operate on it, we lose. However (a) there are so many
1470a7c91847Schristos cases, I'm reluctant to try to fix them all, (b) in some
1471a7c91847Schristos cases the system might not even have a system call which
1472a7c91847Schristos does the right thing, and (c) it isn't clear this needs to
1473a7c91847Schristos work. */
1474a7c91847Schristos if (data->existp == UPDATE_ENTRIES_EXISTING
1475a7c91847Schristos && !isfile (filename))
1476a7c91847Schristos /* Emit a warning and update the file anyway. */
1477a7c91847Schristos error (0, 0, "warning: %s unexpectedly disappeared",
1478a7c91847Schristos short_pathname);
1479a7c91847Schristos
1480a7c91847Schristos if (data->existp == UPDATE_ENTRIES_NEW
1481a7c91847Schristos && isfile (filename))
1482a7c91847Schristos {
1483a7c91847Schristos /* Emit a warning and refuse to update the file; we don't want
1484a7c91847Schristos to clobber a user's file. */
1485a7c91847Schristos size_t nread;
1486a7c91847Schristos size_t toread;
1487a7c91847Schristos
1488a7c91847Schristos /* size should be unsigned, but until we get around to fixing
1489a7c91847Schristos that, work around it. */
1490a7c91847Schristos size_t usize;
1491a7c91847Schristos
1492a7c91847Schristos char buf[8192];
1493a7c91847Schristos
1494a7c91847Schristos /* This error might be confusing; it isn't really clear to
1495a7c91847Schristos the user what to do about it. Keep in mind that it has
1496a7c91847Schristos several causes: (1) something/someone creates the file
1497a7c91847Schristos during the time that CVS is running, (2) the repository
1498a7c91847Schristos has two files whose names clash for the client because
1499a7c91847Schristos of case-insensitivity or similar causes, See 3 for
1500a7c91847Schristos additional notes. (3) a special case of this is that a
1501a7c91847Schristos file gets renamed for example from a.c to A.C. A
1502a7c91847Schristos "cvs update" on a case-insensitive client will get this
1503a7c91847Schristos error. In this case and in case 2, the filename
1504a7c91847Schristos (short_pathname) printed in the error message will likely _not_
1505a7c91847Schristos have the same case as seen by the user in a directory listing.
1506a7c91847Schristos (4) the client has a file which the server doesn't know
1507a7c91847Schristos about (e.g. "? foo" file), and that name clashes with a file
1508a7c91847Schristos the server does know about, (5) classify.c will print the same
1509a7c91847Schristos message for other reasons.
1510a7c91847Schristos
1511a7c91847Schristos I hope the above paragraph makes it clear that making this
1512a7c91847Schristos clearer is not a one-line fix. */
1513a7c91847Schristos error (0, 0, "move away `%s'; it is in the way", short_pathname);
1514a7c91847Schristos if (updated_fname)
1515a7c91847Schristos {
1516a7c91847Schristos cvs_output ("C ", 0);
1517a7c91847Schristos cvs_output (updated_fname, 0);
1518a7c91847Schristos cvs_output ("\n", 1);
1519a7c91847Schristos }
1520a7c91847Schristos failure_exit = true;
1521a7c91847Schristos
1522a7c91847Schristos discard_file_and_return:
1523a7c91847Schristos /* Now read and discard the file contents. */
1524a7c91847Schristos usize = size;
1525a7c91847Schristos nread = 0;
1526a7c91847Schristos while (nread < usize)
1527a7c91847Schristos {
1528a7c91847Schristos toread = usize - nread;
1529a7c91847Schristos if (toread > sizeof buf)
1530a7c91847Schristos toread = sizeof buf;
1531a7c91847Schristos
1532a7c91847Schristos nread += try_read_from_server (buf, toread);
1533a7c91847Schristos if (nread == usize)
1534a7c91847Schristos break;
1535a7c91847Schristos }
1536a7c91847Schristos
1537a7c91847Schristos free (mode_string);
1538a7c91847Schristos free (scratch_entries);
1539a7c91847Schristos free (entries_line);
1540a7c91847Schristos
1541a7c91847Schristos /* The Mode, Mod-time, and Checksum responses should not carry
1542a7c91847Schristos over to a subsequent Created (or whatever) response, even
1543a7c91847Schristos in the error case. */
1544a7c91847Schristos if (stored_mode)
1545a7c91847Schristos {
1546a7c91847Schristos free (stored_mode);
1547a7c91847Schristos stored_mode = NULL;
1548a7c91847Schristos }
1549a7c91847Schristos stored_modtime_valid = 0;
1550a7c91847Schristos stored_checksum_valid = 0;
1551a7c91847Schristos
1552a7c91847Schristos if (updated_fname)
1553a7c91847Schristos {
1554a7c91847Schristos free (updated_fname);
1555a7c91847Schristos updated_fname = NULL;
1556a7c91847Schristos }
1557a7c91847Schristos return;
1558a7c91847Schristos }
1559a7c91847Schristos
1560a7c91847Schristos temp_filename = xmalloc (strlen (filename) + 80);
1561a7c91847Schristos #ifdef USE_VMS_FILENAMES
1562a7c91847Schristos /* A VMS rename of "blah.dat" to "foo" to implies a
1563a7c91847Schristos destination of "foo.dat" which is unfortinate for CVS */
1564a7c91847Schristos sprintf (temp_filename, "%s_new_", filename);
1565a7c91847Schristos #else
1566a7c91847Schristos #ifdef _POSIX_NO_TRUNC
1567a7c91847Schristos sprintf (temp_filename, ".new.%.9s", filename);
1568a7c91847Schristos #else /* _POSIX_NO_TRUNC */
1569a7c91847Schristos sprintf (temp_filename, ".new.%s", filename);
1570a7c91847Schristos #endif /* _POSIX_NO_TRUNC */
1571a7c91847Schristos #endif /* USE_VMS_FILENAMES */
1572a7c91847Schristos
1573a7c91847Schristos buf = xmalloc (size);
1574a7c91847Schristos
1575a7c91847Schristos /* Some systems, like OS/2 and Windows NT, end lines with CRLF
1576a7c91847Schristos instead of just LF. Format translation is done in the C
1577a7c91847Schristos library I/O funtions. Here we tell them whether or not to
1578a7c91847Schristos convert -- if this file is marked "binary" with the RCS -kb
1579a7c91847Schristos flag, then we don't want to convert, else we do (because
1580a7c91847Schristos CVS assumes text files by default). */
1581a7c91847Schristos
1582a7c91847Schristos if (options)
1583a7c91847Schristos bin = !strcmp (options, "-kb");
1584a7c91847Schristos else
1585a7c91847Schristos bin = 0;
1586a7c91847Schristos
1587a7c91847Schristos if (data->contents == UPDATE_ENTRIES_RCS_DIFF)
1588a7c91847Schristos {
1589a7c91847Schristos /* This is an RCS change text. We just hold the change
1590a7c91847Schristos text in memory. */
1591a7c91847Schristos
1592a7c91847Schristos if (use_gzip)
1593a7c91847Schristos error (1, 0,
1594a7c91847Schristos "server error: gzip invalid with RCS change text");
1595a7c91847Schristos
1596a7c91847Schristos read_from_server (buf, size);
1597a7c91847Schristos }
1598a7c91847Schristos else
1599a7c91847Schristos {
1600a7c91847Schristos int fd;
1601a7c91847Schristos
1602a7c91847Schristos fd = CVS_OPEN (temp_filename,
1603a7c91847Schristos (O_WRONLY | O_CREAT | O_TRUNC
1604a7c91847Schristos | (bin ? OPEN_BINARY : 0)),
1605a7c91847Schristos 0777);
1606a7c91847Schristos
1607a7c91847Schristos if (fd < 0)
1608a7c91847Schristos {
1609a7c91847Schristos /* I can see a case for making this a fatal error; for
1610a7c91847Schristos a condition like disk full or network unreachable
1611a7c91847Schristos (for a file server), carrying on and giving an
1612a7c91847Schristos error on each file seems unnecessary. But if it is
1613a7c91847Schristos a permission problem, or some such, then it is
1614a7c91847Schristos entirely possible that future files will not have
1615a7c91847Schristos the same problem. */
1616a7c91847Schristos error (0, errno, "cannot write %s", short_pathname);
1617a7c91847Schristos free (temp_filename);
1618a7c91847Schristos free (buf);
1619a7c91847Schristos goto discard_file_and_return;
1620a7c91847Schristos }
1621a7c91847Schristos
1622a7c91847Schristos if (size > 0)
1623a7c91847Schristos {
1624a7c91847Schristos read_from_server (buf, size);
1625a7c91847Schristos
1626a7c91847Schristos if (use_gzip)
1627a7c91847Schristos {
1628a7c91847Schristos if (gunzip_and_write (fd, short_pathname,
1629a7c91847Schristos (unsigned char *) buf, size))
1630a7c91847Schristos error (1, 0, "aborting due to compression error");
1631a7c91847Schristos }
1632a7c91847Schristos else if (write (fd, buf, size) != size)
1633a7c91847Schristos error (1, errno, "writing %s", short_pathname);
1634a7c91847Schristos }
1635a7c91847Schristos
1636a7c91847Schristos if (close (fd) < 0)
1637a7c91847Schristos error (1, errno, "writing %s", short_pathname);
1638a7c91847Schristos }
1639a7c91847Schristos
1640a7c91847Schristos /* This is after we have read the file from the net (a change
1641a7c91847Schristos from previous versions, where the server would send us
1642a7c91847Schristos "M U foo.c" before Update-existing or whatever), but before
1643a7c91847Schristos we finish writing the file (arguably a bug). The timing
1644a7c91847Schristos affects a user who wants status info about how far we have
1645a7c91847Schristos gotten, and also affects whether "U foo.c" appears in addition
1646a7c91847Schristos to various error messages. */
1647a7c91847Schristos if (updated_fname)
1648a7c91847Schristos {
1649a7c91847Schristos cvs_output ("U ", 0);
1650a7c91847Schristos cvs_output (updated_fname, 0);
1651a7c91847Schristos cvs_output ("\n", 1);
1652a7c91847Schristos free (updated_fname);
1653a7c91847Schristos updated_fname = 0;
1654a7c91847Schristos }
1655a7c91847Schristos
1656a7c91847Schristos patch_failed = 0;
1657a7c91847Schristos
1658a7c91847Schristos if (data->contents == UPDATE_ENTRIES_UPDATE)
1659a7c91847Schristos {
1660a7c91847Schristos rename_file (temp_filename, filename);
1661a7c91847Schristos }
1662a7c91847Schristos else if (data->contents == UPDATE_ENTRIES_PATCH)
1663a7c91847Schristos {
1664a7c91847Schristos /* You might think we could just leave Patched out of
1665a7c91847Schristos Valid-responses and not get this response. However, if
1666a7c91847Schristos memory serves, the CVS 1.9 server bases this on -u
1667a7c91847Schristos (update-patches), and there is no way for us to send -u
1668a7c91847Schristos or not based on whether the server supports "Rcs-diff".
1669a7c91847Schristos
1670a7c91847Schristos Fall back to transmitting entire files. */
1671a7c91847Schristos patch_failed = 1;
1672a7c91847Schristos }
1673a7c91847Schristos else
1674a7c91847Schristos {
1675a7c91847Schristos char *filebuf;
1676a7c91847Schristos size_t filebufsize;
1677a7c91847Schristos size_t nread;
1678a7c91847Schristos char *patchedbuf;
1679a7c91847Schristos size_t patchedlen;
1680a7c91847Schristos
1681a7c91847Schristos /* Handle UPDATE_ENTRIES_RCS_DIFF. */
1682a7c91847Schristos
1683a7c91847Schristos if (!isfile (filename))
1684a7c91847Schristos error (1, 0, "patch original file %s does not exist",
1685a7c91847Schristos short_pathname);
1686a7c91847Schristos filebuf = NULL;
1687a7c91847Schristos filebufsize = 0;
1688a7c91847Schristos nread = 0;
1689a7c91847Schristos
1690a7c91847Schristos get_file (filename, short_pathname, bin ? FOPEN_BINARY_READ : "r",
1691a7c91847Schristos &filebuf, &filebufsize, &nread);
1692a7c91847Schristos /* At this point the contents of the existing file are in
1693a7c91847Schristos FILEBUF, and the length of the contents is in NREAD.
1694a7c91847Schristos The contents of the patch from the network are in BUF,
1695a7c91847Schristos and the length of the patch is in SIZE. */
1696a7c91847Schristos
1697a7c91847Schristos if (! rcs_change_text (short_pathname, filebuf, nread, buf, size,
1698a7c91847Schristos &patchedbuf, &patchedlen))
1699a7c91847Schristos patch_failed = 1;
1700a7c91847Schristos else
1701a7c91847Schristos {
1702a7c91847Schristos if (stored_checksum_valid)
1703a7c91847Schristos {
1704a7c91847Schristos unsigned char checksum[16];
1705a7c91847Schristos
1706a7c91847Schristos /* We have a checksum. Check it before writing
1707a7c91847Schristos the file out, so that we don't have to read it
1708a7c91847Schristos back in again. */
1709a7c91847Schristos md5_buffer (patchedbuf, patchedlen, checksum);
1710a7c91847Schristos if (memcmp (checksum, stored_checksum, 16) != 0)
1711a7c91847Schristos {
1712a7c91847Schristos error (0, 0,
1713a7c91847Schristos "checksum failure after patch to %s; will refetch",
1714a7c91847Schristos short_pathname);
1715a7c91847Schristos
1716a7c91847Schristos patch_failed = 1;
1717a7c91847Schristos }
1718a7c91847Schristos
1719a7c91847Schristos stored_checksum_valid = 0;
1720a7c91847Schristos }
1721a7c91847Schristos
1722a7c91847Schristos if (! patch_failed)
1723a7c91847Schristos {
1724a7c91847Schristos FILE *e;
1725a7c91847Schristos
1726a7c91847Schristos e = xfopen (temp_filename,
1727a7c91847Schristos bin ? FOPEN_BINARY_WRITE : "w");
1728a7c91847Schristos if (fwrite (patchedbuf, sizeof *patchedbuf, patchedlen, e)
1729a7c91847Schristos != patchedlen)
1730a7c91847Schristos error (1, errno, "cannot write %s", temp_filename);
1731a7c91847Schristos if (fclose (e) == EOF)
1732a7c91847Schristos error (1, errno, "cannot close %s", temp_filename);
1733a7c91847Schristos rename_file (temp_filename, filename);
1734a7c91847Schristos }
1735a7c91847Schristos
1736a7c91847Schristos free (patchedbuf);
1737a7c91847Schristos }
1738a7c91847Schristos
1739a7c91847Schristos free (filebuf);
1740a7c91847Schristos }
1741a7c91847Schristos
1742a7c91847Schristos free (temp_filename);
1743a7c91847Schristos
1744a7c91847Schristos if (stored_checksum_valid && ! patch_failed)
1745a7c91847Schristos {
1746a7c91847Schristos FILE *e;
1747a7c91847Schristos struct md5_ctx context;
1748a7c91847Schristos unsigned char buf[8192];
1749a7c91847Schristos unsigned len;
1750a7c91847Schristos unsigned char checksum[16];
1751a7c91847Schristos
1752a7c91847Schristos /*
1753a7c91847Schristos * Compute the MD5 checksum. This will normally only be
1754a7c91847Schristos * used when receiving a patch, so we always compute it
1755a7c91847Schristos * here on the final file, rather than on the received
1756a7c91847Schristos * data.
1757a7c91847Schristos *
1758a7c91847Schristos * Note that if the file is a text file, we should read it
1759a7c91847Schristos * here using text mode, so its lines will be terminated the same
1760a7c91847Schristos * way they were transmitted.
1761a7c91847Schristos */
1762a7c91847Schristos e = CVS_FOPEN (filename, "r");
1763a7c91847Schristos if (!e)
1764a7c91847Schristos error (1, errno, "could not open %s", short_pathname);
1765a7c91847Schristos
1766a7c91847Schristos md5_init_ctx (&context);
1767a7c91847Schristos while ((len = fread (buf, 1, sizeof buf, e)) != 0)
1768a7c91847Schristos md5_process_bytes (buf, len, &context);
1769a7c91847Schristos if (ferror (e))
1770a7c91847Schristos error (1, errno, "could not read %s", short_pathname);
1771a7c91847Schristos md5_finish_ctx (&context, checksum);
1772a7c91847Schristos
1773a7c91847Schristos fclose (e);
1774a7c91847Schristos
1775a7c91847Schristos stored_checksum_valid = 0;
1776a7c91847Schristos
1777a7c91847Schristos if (memcmp (checksum, stored_checksum, 16) != 0)
1778a7c91847Schristos {
1779a7c91847Schristos if (data->contents != UPDATE_ENTRIES_PATCH)
1780a7c91847Schristos error (1, 0, "checksum failure on %s",
1781a7c91847Schristos short_pathname);
1782a7c91847Schristos
1783a7c91847Schristos error (0, 0,
1784a7c91847Schristos "checksum failure after patch to %s; will refetch",
1785a7c91847Schristos short_pathname);
1786a7c91847Schristos
1787a7c91847Schristos patch_failed = 1;
1788a7c91847Schristos }
1789a7c91847Schristos }
1790a7c91847Schristos
1791a7c91847Schristos if (patch_failed)
1792a7c91847Schristos {
1793a7c91847Schristos /* Save this file to retrieve later. */
1794a7c91847Schristos failed_patches = xnrealloc (failed_patches,
1795a7c91847Schristos failed_patches_count + 1,
1796a7c91847Schristos sizeof (char *));
1797a7c91847Schristos failed_patches[failed_patches_count] = xstrdup (short_pathname);
1798a7c91847Schristos ++failed_patches_count;
1799a7c91847Schristos
1800a7c91847Schristos stored_checksum_valid = 0;
1801a7c91847Schristos
1802a7c91847Schristos free (mode_string);
1803a7c91847Schristos free (buf);
1804a7c91847Schristos free (scratch_entries);
1805a7c91847Schristos free (entries_line);
1806a7c91847Schristos
1807a7c91847Schristos return;
1808a7c91847Schristos }
1809a7c91847Schristos
1810a7c91847Schristos {
1811a7c91847Schristos int status = change_mode (filename, mode_string, 1);
1812a7c91847Schristos if (status != 0)
1813a7c91847Schristos error (0, status, "cannot change mode of %s", short_pathname);
1814a7c91847Schristos }
1815a7c91847Schristos
1816a7c91847Schristos free (mode_string);
1817a7c91847Schristos free (buf);
1818a7c91847Schristos }
1819a7c91847Schristos
1820a7c91847Schristos if (stored_mode)
1821a7c91847Schristos {
1822a7c91847Schristos change_mode (filename, stored_mode, 1);
1823a7c91847Schristos free (stored_mode);
1824a7c91847Schristos stored_mode = NULL;
1825a7c91847Schristos }
1826a7c91847Schristos
1827a7c91847Schristos if (stored_modtime_valid)
1828a7c91847Schristos {
1829a7c91847Schristos struct utimbuf t;
1830a7c91847Schristos
1831a7c91847Schristos memset (&t, 0, sizeof (t));
1832a7c91847Schristos t.modtime = stored_modtime;
1833a7c91847Schristos (void) time (&t.actime);
1834a7c91847Schristos
1835a7c91847Schristos #ifdef UTIME_EXPECTS_WRITABLE
1836a7c91847Schristos if (!iswritable (filename))
1837a7c91847Schristos {
1838a7c91847Schristos xchmod (filename, 1);
1839a7c91847Schristos change_it_back = 1;
1840a7c91847Schristos }
1841a7c91847Schristos #endif /* UTIME_EXPECTS_WRITABLE */
1842a7c91847Schristos
1843a7c91847Schristos if (utime (filename, &t) < 0)
1844a7c91847Schristos error (0, errno, "cannot set time on %s", filename);
1845a7c91847Schristos
1846a7c91847Schristos #ifdef UTIME_EXPECTS_WRITABLE
1847a7c91847Schristos if (change_it_back)
1848a7c91847Schristos {
1849a7c91847Schristos xchmod (filename, 0);
1850a7c91847Schristos change_it_back = 0;
1851a7c91847Schristos }
1852a7c91847Schristos #endif /* UTIME_EXPECTS_WRITABLE */
1853a7c91847Schristos
1854a7c91847Schristos stored_modtime_valid = 0;
1855a7c91847Schristos }
1856a7c91847Schristos
1857a7c91847Schristos /*
1858a7c91847Schristos * Process the entries line. Do this after we've written the file,
1859a7c91847Schristos * since we need the timestamp.
1860a7c91847Schristos */
1861a7c91847Schristos if (strcmp (cvs_cmd_name, "export"))
1862a7c91847Schristos {
1863a7c91847Schristos char *local_timestamp;
1864a7c91847Schristos char *file_timestamp;
1865a7c91847Schristos
1866a7c91847Schristos (void) time (&last_register_time);
1867a7c91847Schristos
1868a7c91847Schristos local_timestamp = data->timestamp;
1869a7c91847Schristos if (!local_timestamp || ts[0] == '+')
1870a7c91847Schristos file_timestamp = time_stamp (filename);
1871a7c91847Schristos else
1872a7c91847Schristos file_timestamp = NULL;
1873a7c91847Schristos
1874a7c91847Schristos /*
1875a7c91847Schristos * These special version numbers signify that it is not up to
1876a7c91847Schristos * date. Create a dummy timestamp which will never compare
1877a7c91847Schristos * equal to the timestamp of the file.
1878a7c91847Schristos */
1879a7c91847Schristos if (vn[0] == '\0' || !strcmp (vn, "0") || vn[0] == '-')
1880a7c91847Schristos local_timestamp = "dummy timestamp";
1881a7c91847Schristos else if (!local_timestamp)
1882a7c91847Schristos {
1883a7c91847Schristos local_timestamp = file_timestamp;
1884a7c91847Schristos
1885a7c91847Schristos /* Checking for cvs_cmd_name of "commit" doesn't seem like
1886a7c91847Schristos the cleanest way to handle this, but it seem to roughly
1887a7c91847Schristos parallel what the :local: code which calls
1888a7c91847Schristos mark_up_to_date ends up amounting to. Some day, should
1889a7c91847Schristos think more about what the Checked-in response means
1890a7c91847Schristos vis-a-vis both Entries and Base and clarify
1891a7c91847Schristos cvsclient.texi accordingly. */
1892a7c91847Schristos
1893a7c91847Schristos if (!strcmp (cvs_cmd_name, "commit"))
1894a7c91847Schristos mark_up_to_date (filename);
1895a7c91847Schristos }
1896a7c91847Schristos
1897a7c91847Schristos Register (ent_list, filename, vn, local_timestamp,
1898a7c91847Schristos options, tag, date, ts[0] == '+' ? file_timestamp : NULL);
1899a7c91847Schristos
1900a7c91847Schristos if (file_timestamp)
1901a7c91847Schristos free (file_timestamp);
1902a7c91847Schristos
1903a7c91847Schristos }
1904a7c91847Schristos free (scratch_entries);
1905a7c91847Schristos free (entries_line);
1906a7c91847Schristos }
1907a7c91847Schristos
1908a7c91847Schristos
1909a7c91847Schristos
1910a7c91847Schristos static void
handle_checked_in(char * args,size_t len)1911a7c91847Schristos handle_checked_in (char *args, size_t len)
1912a7c91847Schristos {
1913a7c91847Schristos struct update_entries_data dat;
1914a7c91847Schristos dat.contents = UPDATE_ENTRIES_CHECKIN;
1915a7c91847Schristos dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1916a7c91847Schristos dat.timestamp = NULL;
1917a7c91847Schristos call_in_directory (args, update_entries, &dat);
1918a7c91847Schristos }
1919a7c91847Schristos
1920a7c91847Schristos
1921a7c91847Schristos
1922a7c91847Schristos static void
handle_new_entry(char * args,size_t len)1923a7c91847Schristos handle_new_entry (char *args, size_t len)
1924a7c91847Schristos {
1925a7c91847Schristos struct update_entries_data dat;
1926a7c91847Schristos dat.contents = UPDATE_ENTRIES_CHECKIN;
1927a7c91847Schristos dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1928a7c91847Schristos dat.timestamp = "dummy timestamp from new-entry";
1929a7c91847Schristos call_in_directory (args, update_entries, &dat);
1930a7c91847Schristos }
1931a7c91847Schristos
1932a7c91847Schristos
1933a7c91847Schristos
1934a7c91847Schristos static void
handle_updated(char * args,size_t len)1935a7c91847Schristos handle_updated (char *args, size_t len)
1936a7c91847Schristos {
1937a7c91847Schristos struct update_entries_data dat;
1938a7c91847Schristos dat.contents = UPDATE_ENTRIES_UPDATE;
1939a7c91847Schristos dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1940a7c91847Schristos dat.timestamp = NULL;
1941a7c91847Schristos call_in_directory (args, update_entries, &dat);
1942a7c91847Schristos }
1943a7c91847Schristos
1944a7c91847Schristos
1945a7c91847Schristos
1946a7c91847Schristos static void
handle_created(char * args,size_t len)1947a7c91847Schristos handle_created (char *args, size_t len)
1948a7c91847Schristos {
1949a7c91847Schristos struct update_entries_data dat;
1950a7c91847Schristos dat.contents = UPDATE_ENTRIES_UPDATE;
1951a7c91847Schristos dat.existp = UPDATE_ENTRIES_NEW;
1952a7c91847Schristos dat.timestamp = NULL;
1953a7c91847Schristos call_in_directory (args, update_entries, &dat);
1954a7c91847Schristos }
1955a7c91847Schristos
1956a7c91847Schristos
1957a7c91847Schristos
1958a7c91847Schristos static void
handle_update_existing(char * args,size_t len)1959a7c91847Schristos handle_update_existing (char *args, size_t len)
1960a7c91847Schristos {
1961a7c91847Schristos struct update_entries_data dat;
1962a7c91847Schristos dat.contents = UPDATE_ENTRIES_UPDATE;
1963a7c91847Schristos dat.existp = UPDATE_ENTRIES_EXISTING;
1964a7c91847Schristos dat.timestamp = NULL;
1965a7c91847Schristos call_in_directory (args, update_entries, &dat);
1966a7c91847Schristos }
1967a7c91847Schristos
1968a7c91847Schristos
1969a7c91847Schristos
1970a7c91847Schristos static void
handle_merged(char * args,size_t len)1971a7c91847Schristos handle_merged (char *args, size_t len)
1972a7c91847Schristos {
1973a7c91847Schristos struct update_entries_data dat;
1974a7c91847Schristos dat.contents = UPDATE_ENTRIES_UPDATE;
1975a7c91847Schristos /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */
1976a7c91847Schristos dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1977a7c91847Schristos dat.timestamp = "Result of merge";
1978a7c91847Schristos call_in_directory (args, update_entries, &dat);
1979a7c91847Schristos }
1980a7c91847Schristos
1981a7c91847Schristos
1982a7c91847Schristos
1983a7c91847Schristos static void
handle_patched(char * args,size_t len)1984a7c91847Schristos handle_patched (char *args, size_t len)
1985a7c91847Schristos {
1986a7c91847Schristos struct update_entries_data dat;
1987a7c91847Schristos dat.contents = UPDATE_ENTRIES_PATCH;
1988a7c91847Schristos /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */
1989a7c91847Schristos dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1990a7c91847Schristos dat.timestamp = NULL;
1991a7c91847Schristos call_in_directory (args, update_entries, &dat);
1992a7c91847Schristos }
1993a7c91847Schristos
1994a7c91847Schristos
1995a7c91847Schristos
1996a7c91847Schristos static void
handle_rcs_diff(char * args,size_t len)1997a7c91847Schristos handle_rcs_diff (char *args, size_t len)
1998a7c91847Schristos {
1999a7c91847Schristos struct update_entries_data dat;
2000a7c91847Schristos dat.contents = UPDATE_ENTRIES_RCS_DIFF;
2001a7c91847Schristos /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */
2002a7c91847Schristos dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
2003a7c91847Schristos dat.timestamp = NULL;
2004a7c91847Schristos call_in_directory (args, update_entries, &dat);
2005a7c91847Schristos }
2006a7c91847Schristos
2007a7c91847Schristos
2008a7c91847Schristos
2009a7c91847Schristos static void
remove_entry(void * data,List * ent_list,const char * short_pathname,const char * filename)2010a7c91847Schristos remove_entry (void *data, List *ent_list, const char *short_pathname,
2011a7c91847Schristos const char *filename)
2012a7c91847Schristos {
2013a7c91847Schristos Scratch_Entry (ent_list, filename);
2014a7c91847Schristos }
2015a7c91847Schristos
2016a7c91847Schristos
2017a7c91847Schristos
2018a7c91847Schristos static void
handle_remove_entry(char * args,size_t len)2019a7c91847Schristos handle_remove_entry (char *args, size_t len)
2020a7c91847Schristos {
2021a7c91847Schristos call_in_directory (args, remove_entry, NULL);
2022a7c91847Schristos }
2023a7c91847Schristos
2024a7c91847Schristos
2025a7c91847Schristos
2026a7c91847Schristos static void
remove_entry_and_file(void * data,List * ent_list,const char * short_pathname,const char * filename)2027a7c91847Schristos remove_entry_and_file (void *data, List *ent_list, const char *short_pathname,
2028a7c91847Schristos const char *filename)
2029a7c91847Schristos {
2030a7c91847Schristos Scratch_Entry (ent_list, filename);
2031a7c91847Schristos /* Note that we don't ignore existence_error's here. The server
2032a7c91847Schristos should be sending Remove-entry rather than Removed in cases
2033a7c91847Schristos where the file does not exist. And if the user removes the
2034a7c91847Schristos file halfway through a cvs command, we should be printing an
2035a7c91847Schristos error. */
2036a7c91847Schristos if (unlink_file (filename) < 0)
2037a7c91847Schristos error (0, errno, "unable to remove %s", short_pathname);
2038a7c91847Schristos }
2039a7c91847Schristos
2040a7c91847Schristos
2041a7c91847Schristos
2042a7c91847Schristos static void
handle_removed(char * args,size_t len)2043a7c91847Schristos handle_removed (char *args, size_t len)
2044a7c91847Schristos {
2045a7c91847Schristos call_in_directory (args, remove_entry_and_file, NULL);
2046a7c91847Schristos }
2047a7c91847Schristos
2048a7c91847Schristos
2049a7c91847Schristos
2050a7c91847Schristos /* Is this the top level (directory containing CVSROOT)? */
2051a7c91847Schristos static int
is_cvsroot_level(char * pathname)2052a7c91847Schristos is_cvsroot_level (char *pathname)
2053a7c91847Schristos {
2054a7c91847Schristos if (strcmp (toplevel_repos, current_parsed_root->directory))
2055a7c91847Schristos return 0;
2056a7c91847Schristos
2057a7c91847Schristos return !strchr (pathname, '/');
2058a7c91847Schristos }
2059a7c91847Schristos
2060a7c91847Schristos
2061a7c91847Schristos
2062a7c91847Schristos static void
set_static(void * data,List * ent_list,const char * short_pathname,const char * filename)2063a7c91847Schristos set_static (void *data, List *ent_list, const char *short_pathname,
2064a7c91847Schristos const char *filename)
2065a7c91847Schristos {
2066a7c91847Schristos FILE *fp;
2067a7c91847Schristos fp = xfopen (CVSADM_ENTSTAT, "w+");
2068a7c91847Schristos if (fclose (fp) == EOF)
2069a7c91847Schristos error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
2070a7c91847Schristos }
2071a7c91847Schristos
2072a7c91847Schristos
2073a7c91847Schristos
2074a7c91847Schristos static void
handle_set_static_directory(char * args,size_t len)2075a7c91847Schristos handle_set_static_directory (char *args, size_t len)
2076a7c91847Schristos {
2077a7c91847Schristos if (!strcmp (cvs_cmd_name, "export"))
2078a7c91847Schristos {
2079a7c91847Schristos /* Swallow the repository. */
2080a7c91847Schristos read_line (NULL);
2081a7c91847Schristos return;
2082a7c91847Schristos }
2083a7c91847Schristos call_in_directory (args, set_static, NULL);
2084a7c91847Schristos }
2085a7c91847Schristos
2086a7c91847Schristos
2087a7c91847Schristos
2088a7c91847Schristos static void
clear_static(void * data,List * ent_list,const char * short_pathname,const char * filename)2089a7c91847Schristos clear_static (void *data, List *ent_list, const char *short_pathname,
2090a7c91847Schristos const char *filename)
2091a7c91847Schristos {
2092a7c91847Schristos if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
2093a7c91847Schristos error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
2094a7c91847Schristos }
2095a7c91847Schristos
2096a7c91847Schristos
2097a7c91847Schristos
2098a7c91847Schristos static void
handle_clear_static_directory(char * pathname,size_t len)2099a7c91847Schristos handle_clear_static_directory (char *pathname, size_t len)
2100a7c91847Schristos {
2101a7c91847Schristos if (!strcmp (cvs_cmd_name, "export"))
2102a7c91847Schristos {
2103a7c91847Schristos /* Swallow the repository. */
2104a7c91847Schristos read_line (NULL);
2105a7c91847Schristos return;
2106a7c91847Schristos }
2107a7c91847Schristos
2108a7c91847Schristos if (is_cvsroot_level (pathname))
2109a7c91847Schristos {
2110a7c91847Schristos /*
2111a7c91847Schristos * Top level (directory containing CVSROOT). This seems to normally
2112a7c91847Schristos * lack a CVS directory, so don't try to create files in it.
2113a7c91847Schristos */
2114a7c91847Schristos return;
2115a7c91847Schristos }
2116a7c91847Schristos call_in_directory (pathname, clear_static, NULL);
2117a7c91847Schristos }
2118a7c91847Schristos
2119a7c91847Schristos
2120a7c91847Schristos
2121a7c91847Schristos static void
set_sticky(void * data,List * ent_list,const char * short_pathname,const char * filename)2122a7c91847Schristos set_sticky (void *data, List *ent_list, const char *short_pathname,
2123a7c91847Schristos const char *filename)
2124a7c91847Schristos {
2125a7c91847Schristos char *tagspec;
2126a7c91847Schristos FILE *f;
2127a7c91847Schristos
2128a7c91847Schristos read_line (&tagspec);
2129a7c91847Schristos
2130a7c91847Schristos /* FIXME-update-dir: error messages should include the directory. */
2131a7c91847Schristos f = CVS_FOPEN (CVSADM_TAG, "w+");
2132a7c91847Schristos if (!f)
2133a7c91847Schristos {
2134a7c91847Schristos /* Making this non-fatal is a bit of a kludge (see dirs2
2135a7c91847Schristos in testsuite). A better solution would be to avoid having
2136a7c91847Schristos the server tell us about a directory we shouldn't be doing
2137a7c91847Schristos anything with anyway (e.g. by handling directory
2138a7c91847Schristos addition/removal better). */
2139a7c91847Schristos error (0, errno, "cannot open %s", CVSADM_TAG);
2140a7c91847Schristos free (tagspec);
2141a7c91847Schristos return;
2142a7c91847Schristos }
2143a7c91847Schristos if (fprintf (f, "%s\n", tagspec) < 0)
2144a7c91847Schristos error (1, errno, "writing %s", CVSADM_TAG);
2145a7c91847Schristos if (fclose (f) == EOF)
2146a7c91847Schristos error (1, errno, "closing %s", CVSADM_TAG);
2147a7c91847Schristos free (tagspec);
2148a7c91847Schristos }
2149a7c91847Schristos
2150a7c91847Schristos
2151a7c91847Schristos
2152a7c91847Schristos static void
handle_set_sticky(char * pathname,size_t len)2153a7c91847Schristos handle_set_sticky (char *pathname, size_t len)
2154a7c91847Schristos {
2155a7c91847Schristos if (!strcmp (cvs_cmd_name, "export"))
2156a7c91847Schristos {
2157a7c91847Schristos /* Swallow the repository. */
2158a7c91847Schristos read_line (NULL);
2159a7c91847Schristos /* Swallow the tag line. */
2160a7c91847Schristos read_line (NULL);
2161a7c91847Schristos return;
2162a7c91847Schristos }
2163a7c91847Schristos if (is_cvsroot_level (pathname))
2164a7c91847Schristos {
2165a7c91847Schristos /*
2166a7c91847Schristos * Top level (directory containing CVSROOT). This seems to normally
2167a7c91847Schristos * lack a CVS directory, so don't try to create files in it.
2168a7c91847Schristos */
2169a7c91847Schristos
2170a7c91847Schristos /* Swallow the repository. */
2171a7c91847Schristos read_line (NULL);
2172a7c91847Schristos /* Swallow the tag line. */
2173a7c91847Schristos read_line (NULL);
2174a7c91847Schristos return;
2175a7c91847Schristos }
2176a7c91847Schristos
2177a7c91847Schristos call_in_directory (pathname, set_sticky, NULL);
2178a7c91847Schristos }
2179a7c91847Schristos
2180a7c91847Schristos
2181a7c91847Schristos
2182a7c91847Schristos static void
clear_sticky(void * data,List * ent_list,const char * short_pathname,const char * filename)2183a7c91847Schristos clear_sticky (void *data, List *ent_list, const char *short_pathname,
2184a7c91847Schristos const char *filename)
2185a7c91847Schristos {
2186a7c91847Schristos if (unlink_file (CVSADM_TAG) < 0 && ! existence_error (errno))
2187a7c91847Schristos error (1, errno, "cannot remove %s", CVSADM_TAG);
2188a7c91847Schristos }
2189a7c91847Schristos
2190a7c91847Schristos
2191a7c91847Schristos
2192a7c91847Schristos static void
handle_clear_sticky(char * pathname,size_t len)2193a7c91847Schristos handle_clear_sticky (char *pathname, size_t len)
2194a7c91847Schristos {
2195a7c91847Schristos if (!strcmp (cvs_cmd_name, "export"))
2196a7c91847Schristos {
2197a7c91847Schristos /* Swallow the repository. */
2198a7c91847Schristos read_line (NULL);
2199a7c91847Schristos return;
2200a7c91847Schristos }
2201a7c91847Schristos
2202a7c91847Schristos if (is_cvsroot_level (pathname))
2203a7c91847Schristos {
2204a7c91847Schristos /*
2205a7c91847Schristos * Top level (directory containing CVSROOT). This seems to normally
2206a7c91847Schristos * lack a CVS directory, so don't try to create files in it.
2207a7c91847Schristos */
2208a7c91847Schristos return;
2209a7c91847Schristos }
2210a7c91847Schristos
2211a7c91847Schristos call_in_directory (pathname, clear_sticky, NULL);
2212a7c91847Schristos }
2213a7c91847Schristos
2214a7c91847Schristos
2215a7c91847Schristos
2216a7c91847Schristos /* Handle the client-side support for a successful edit.
2217a7c91847Schristos */
2218a7c91847Schristos static void
handle_edit_file(char * pathname,size_t len)2219a7c91847Schristos handle_edit_file (char *pathname, size_t len)
2220a7c91847Schristos {
2221a7c91847Schristos call_in_directory (pathname, edit_file, NULL);
2222a7c91847Schristos }
2223a7c91847Schristos
2224a7c91847Schristos
2225a7c91847Schristos
2226a7c91847Schristos static void
template(void * data,List * ent_list,const char * short_pathname,const char * filename)2227a7c91847Schristos template (void *data, List *ent_list, const char *short_pathname,
2228a7c91847Schristos const char *filename)
2229a7c91847Schristos {
2230a7c91847Schristos char *buf = Xasprintf ("%s/%s", short_pathname, CVSADM_TEMPLATE);
2231a7c91847Schristos read_counted_file (CVSADM_TEMPLATE, buf);
2232a7c91847Schristos free (buf);
2233a7c91847Schristos }
2234a7c91847Schristos
2235a7c91847Schristos
2236a7c91847Schristos
2237a7c91847Schristos static void
handle_template(char * pathname,size_t len)2238a7c91847Schristos handle_template (char *pathname, size_t len)
2239a7c91847Schristos {
2240a7c91847Schristos call_in_directory (pathname, template, NULL);
2241a7c91847Schristos }
2242a7c91847Schristos
2243a7c91847Schristos
2244a7c91847Schristos
2245a7c91847Schristos static void
clear_template(void * data,List * ent_list,const char * short_pathname,const char * filename)2246a7c91847Schristos clear_template (void *data, List *ent_list, const char *short_pathname,
2247a7c91847Schristos const char *filename)
2248a7c91847Schristos {
2249a7c91847Schristos if (unlink_file (CVSADM_TEMPLATE) < 0 && ! existence_error (errno))
2250a7c91847Schristos error (1, errno, "cannot remove %s", CVSADM_TEMPLATE);
2251a7c91847Schristos }
2252a7c91847Schristos
2253a7c91847Schristos
2254a7c91847Schristos
2255a7c91847Schristos static void
handle_clear_template(char * pathname,size_t len)2256a7c91847Schristos handle_clear_template (char *pathname, size_t len)
2257a7c91847Schristos {
2258a7c91847Schristos call_in_directory (pathname, clear_template, NULL);
2259a7c91847Schristos }
2260a7c91847Schristos
2261a7c91847Schristos
2262a7c91847Schristos
2263a7c91847Schristos struct save_dir {
2264a7c91847Schristos char *dir;
2265a7c91847Schristos struct save_dir *next;
2266a7c91847Schristos };
2267a7c91847Schristos
2268a7c91847Schristos struct save_dir *prune_candidates;
2269a7c91847Schristos
2270a7c91847Schristos static void
add_prune_candidate(const char * dir)2271a7c91847Schristos add_prune_candidate (const char *dir)
2272a7c91847Schristos {
2273a7c91847Schristos struct save_dir *p;
2274a7c91847Schristos
2275a7c91847Schristos if ((dir[0] == '.' && dir[1] == '\0')
2276a7c91847Schristos || (prune_candidates && !strcmp (dir, prune_candidates->dir)))
2277a7c91847Schristos return;
2278a7c91847Schristos p = xmalloc (sizeof (struct save_dir));
2279a7c91847Schristos p->dir = xstrdup (dir);
2280a7c91847Schristos p->next = prune_candidates;
2281a7c91847Schristos prune_candidates = p;
2282a7c91847Schristos }
2283a7c91847Schristos
2284a7c91847Schristos
2285a7c91847Schristos
2286a7c91847Schristos static void
process_prune_candidates(void)2287a7c91847Schristos process_prune_candidates (void)
2288a7c91847Schristos {
2289a7c91847Schristos struct save_dir *p;
2290a7c91847Schristos struct save_dir *q;
2291a7c91847Schristos
2292a7c91847Schristos if (toplevel_wd)
2293a7c91847Schristos {
2294a7c91847Schristos if (CVS_CHDIR (toplevel_wd) < 0)
2295a7c91847Schristos error (1, errno, "could not chdir to %s", toplevel_wd);
2296a7c91847Schristos }
2297a7c91847Schristos for (p = prune_candidates; p; )
2298a7c91847Schristos {
2299a7c91847Schristos if (isemptydir (p->dir, 1))
2300a7c91847Schristos {
2301a7c91847Schristos char *b;
2302a7c91847Schristos
2303a7c91847Schristos if (unlink_file_dir (p->dir) < 0)
2304a7c91847Schristos error (0, errno, "cannot remove %s", p->dir);
2305a7c91847Schristos b = strrchr (p->dir, '/');
2306a7c91847Schristos if (!b)
2307a7c91847Schristos Subdir_Deregister (NULL, NULL, p->dir);
2308a7c91847Schristos else
2309a7c91847Schristos {
2310a7c91847Schristos *b = '\0';
2311a7c91847Schristos Subdir_Deregister (NULL, p->dir, b + 1);
2312a7c91847Schristos }
2313a7c91847Schristos }
2314a7c91847Schristos free (p->dir);
2315a7c91847Schristos q = p->next;
2316a7c91847Schristos free (p);
2317a7c91847Schristos p = q;
2318a7c91847Schristos }
2319a7c91847Schristos prune_candidates = NULL;
2320a7c91847Schristos }
2321a7c91847Schristos
2322a7c91847Schristos
2323a7c91847Schristos
2324a7c91847Schristos /* Send a Repository line. */
2325a7c91847Schristos static char *last_repos;
2326a7c91847Schristos static char *last_update_dir;
2327a7c91847Schristos static void
send_repository(const char * dir,const char * repos,const char * update_dir)2328a7c91847Schristos send_repository (const char *dir, const char *repos, const char *update_dir)
2329a7c91847Schristos {
2330a7c91847Schristos char *adm_name;
2331a7c91847Schristos
2332a7c91847Schristos /* FIXME: this is probably not the best place to check; I wish I
2333a7c91847Schristos * knew where in here's callers to really trap this bug. To
2334a7c91847Schristos * reproduce the bug, just do this:
2335a7c91847Schristos *
2336a7c91847Schristos * mkdir junk
2337a7c91847Schristos * cd junk
2338a7c91847Schristos * cvs -d some_repos update foo
2339a7c91847Schristos *
2340a7c91847Schristos * Poof, CVS seg faults and dies! It's because it's trying to
2341a7c91847Schristos * send a NULL string to the server but dies in send_to_server.
2342a7c91847Schristos * That string was supposed to be the repository, but it doesn't
2343a7c91847Schristos * get set because there's no CVSADM dir, and somehow it's not
2344a7c91847Schristos * getting set from the -d argument either... ?
2345a7c91847Schristos */
2346a7c91847Schristos if (!repos)
2347a7c91847Schristos {
2348a7c91847Schristos /* Lame error. I want a real fix but can't stay up to track
2349a7c91847Schristos this down right now. */
2350a7c91847Schristos error (1, 0, "no repository");
2351a7c91847Schristos }
2352a7c91847Schristos
2353a7c91847Schristos if (!update_dir || update_dir[0] == '\0')
2354a7c91847Schristos update_dir = ".";
2355a7c91847Schristos
2356a7c91847Schristos if (last_repos && !strcmp (repos, last_repos)
2357a7c91847Schristos && last_update_dir && !strcmp (update_dir, last_update_dir))
2358a7c91847Schristos /* We've already sent it. */
2359a7c91847Schristos return;
2360a7c91847Schristos
2361a7c91847Schristos if (client_prune_dirs)
2362a7c91847Schristos add_prune_candidate (update_dir);
2363a7c91847Schristos
2364a7c91847Schristos /* Add a directory name to the list of those sent to the
2365a7c91847Schristos server. */
2366a7c91847Schristos if (update_dir && *update_dir != '\0' && strcmp (update_dir, ".")
2367a7c91847Schristos && !findnode (dirs_sent_to_server, update_dir))
2368a7c91847Schristos {
2369a7c91847Schristos Node *n;
2370a7c91847Schristos n = getnode ();
2371a7c91847Schristos n->type = NT_UNKNOWN;
2372a7c91847Schristos n->key = xstrdup (update_dir);
2373a7c91847Schristos n->data = NULL;
2374a7c91847Schristos
2375a7c91847Schristos if (addnode (dirs_sent_to_server, n))
2376a7c91847Schristos error (1, 0, "cannot add directory %s to list", n->key);
2377a7c91847Schristos }
2378a7c91847Schristos
2379a7c91847Schristos /* 80 is large enough for any of CVSADM_*. */
2380a7c91847Schristos adm_name = xmalloc (strlen (dir) + 80);
2381a7c91847Schristos
2382a7c91847Schristos send_to_server ("Directory ", 0);
2383a7c91847Schristos {
2384a7c91847Schristos /* Send the directory name. I know that this
2385a7c91847Schristos sort of duplicates code elsewhere, but each
2386a7c91847Schristos case seems slightly different... */
2387a7c91847Schristos char buf[1];
2388a7c91847Schristos const char *p = update_dir;
2389a7c91847Schristos while (*p != '\0')
2390a7c91847Schristos {
2391a7c91847Schristos assert (*p != '\012');
2392a7c91847Schristos if (ISSLASH (*p))
2393a7c91847Schristos {
2394a7c91847Schristos buf[0] = '/';
2395a7c91847Schristos send_to_server (buf, 1);
2396a7c91847Schristos }
2397a7c91847Schristos else
2398a7c91847Schristos {
2399a7c91847Schristos buf[0] = *p;
2400a7c91847Schristos send_to_server (buf, 1);
2401a7c91847Schristos }
2402a7c91847Schristos ++p;
2403a7c91847Schristos }
2404a7c91847Schristos }
2405a7c91847Schristos send_to_server ("\012", 1);
2406a7c91847Schristos if (supported_request ("Relative-directory"))
2407a7c91847Schristos {
2408a7c91847Schristos const char *short_repos = Short_Repository (repos);
2409a7c91847Schristos send_to_server (short_repos, 0);
2410a7c91847Schristos }
2411a7c91847Schristos else
2412a7c91847Schristos send_to_server (repos, 0);
2413a7c91847Schristos send_to_server ("\012", 1);
2414a7c91847Schristos
2415a7c91847Schristos if (supported_request ("Static-directory"))
2416a7c91847Schristos {
2417a7c91847Schristos adm_name[0] = '\0';
2418a7c91847Schristos if (dir[0] != '\0')
2419a7c91847Schristos {
2420a7c91847Schristos strcat (adm_name, dir);
2421a7c91847Schristos strcat (adm_name, "/");
2422a7c91847Schristos }
2423a7c91847Schristos strcat (adm_name, CVSADM_ENTSTAT);
2424a7c91847Schristos if (isreadable (adm_name))
2425a7c91847Schristos {
2426a7c91847Schristos send_to_server ("Static-directory\012", 0);
2427a7c91847Schristos }
2428a7c91847Schristos }
2429a7c91847Schristos if (supported_request ("Sticky"))
2430a7c91847Schristos {
2431a7c91847Schristos FILE *f;
2432a7c91847Schristos if (dir[0] == '\0')
2433a7c91847Schristos strcpy (adm_name, CVSADM_TAG);
2434a7c91847Schristos else
2435a7c91847Schristos sprintf (adm_name, "%s/%s", dir, CVSADM_TAG);
2436a7c91847Schristos
2437a7c91847Schristos f = CVS_FOPEN (adm_name, "r");
2438a7c91847Schristos if (!f)
2439a7c91847Schristos {
2440a7c91847Schristos if (! existence_error (errno))
2441a7c91847Schristos error (1, errno, "reading %s", adm_name);
2442a7c91847Schristos }
2443a7c91847Schristos else
2444a7c91847Schristos {
2445a7c91847Schristos char line[80];
2446a7c91847Schristos char *nl = NULL;
2447a7c91847Schristos send_to_server ("Sticky ", 0);
2448a7c91847Schristos while (fgets (line, sizeof (line), f))
2449a7c91847Schristos {
2450a7c91847Schristos send_to_server (line, 0);
2451a7c91847Schristos nl = strchr (line, '\n');
2452a7c91847Schristos if (nl)
2453a7c91847Schristos break;
2454a7c91847Schristos }
2455a7c91847Schristos if (!nl)
2456a7c91847Schristos send_to_server ("\012", 1);
2457a7c91847Schristos if (fclose (f) == EOF)
2458a7c91847Schristos error (0, errno, "closing %s", adm_name);
2459a7c91847Schristos }
2460a7c91847Schristos }
2461a7c91847Schristos free (adm_name);
2462a7c91847Schristos if (last_repos) free (last_repos);
2463a7c91847Schristos if (last_update_dir) free (last_update_dir);
2464a7c91847Schristos last_repos = xstrdup (repos);
2465a7c91847Schristos last_update_dir = xstrdup (update_dir);
2466a7c91847Schristos }
2467a7c91847Schristos
2468a7c91847Schristos
2469a7c91847Schristos
2470a7c91847Schristos /* Send a Repository line and set toplevel_repos. */
2471a7c91847Schristos void
send_a_repository(const char * dir,const char * repository,const char * update_dir_in)2472a7c91847Schristos send_a_repository (const char *dir, const char *repository,
2473a7c91847Schristos const char *update_dir_in)
2474a7c91847Schristos {
2475a7c91847Schristos char *update_dir = xstrdup (update_dir_in);
2476a7c91847Schristos
2477a7c91847Schristos if (!toplevel_repos && repository)
2478a7c91847Schristos {
2479a7c91847Schristos if (update_dir[0] == '\0'
2480a7c91847Schristos || (update_dir[0] == '.' && update_dir[1] == '\0'))
2481a7c91847Schristos toplevel_repos = xstrdup (repository);
2482a7c91847Schristos else
2483a7c91847Schristos {
2484a7c91847Schristos /*
2485a7c91847Schristos * Get the repository from a CVS/Repository file if update_dir
2486a7c91847Schristos * is absolute. This is not correct in general, because
2487a7c91847Schristos * the CVS/Repository file might not be the top-level one.
2488a7c91847Schristos * This is for cases like "cvs update /foo/bar" (I'm not
2489a7c91847Schristos * sure it matters what toplevel_repos we get, but it does
2490a7c91847Schristos * matter that we don't hit the "internal error" code below).
2491a7c91847Schristos */
2492a7c91847Schristos if (update_dir[0] == '/')
2493a7c91847Schristos toplevel_repos = Name_Repository (update_dir, update_dir);
2494a7c91847Schristos else
2495a7c91847Schristos {
2496a7c91847Schristos /*
2497a7c91847Schristos * Guess the repository of that directory by looking at a
2498a7c91847Schristos * subdirectory and removing as many pathname components
2499a7c91847Schristos * as are in update_dir. I think that will always (or at
2500a7c91847Schristos * least almost always) be 1.
2501a7c91847Schristos *
2502a7c91847Schristos * So this deals with directories which have been
2503a7c91847Schristos * renamed, though it doesn't necessarily deal with
2504a7c91847Schristos * directories which have been put inside other
2505a7c91847Schristos * directories (and cvs invoked on the containing
2506a7c91847Schristos * directory). I'm not sure the latter case needs to
2507a7c91847Schristos * work.
2508a7c91847Schristos *
2509a7c91847Schristos * 21 Aug 1998: Well, Mr. Above-Comment-Writer, it
2510a7c91847Schristos * does need to work after all. When we are using the
2511a7c91847Schristos * client in a multi-cvsroot environment, it will be
2512a7c91847Schristos * fairly common that we have the above case (e.g.,
2513a7c91847Schristos * cwd checked out from one repository but
2514a7c91847Schristos * subdirectory checked out from another). We can't
2515a7c91847Schristos * assume that by walking up a directory in our wd we
2516a7c91847Schristos * necessarily walk up a directory in the repository.
2517a7c91847Schristos */
2518a7c91847Schristos /*
2519a7c91847Schristos * This gets toplevel_repos wrong for "cvs update ../foo"
2520a7c91847Schristos * but I'm not sure toplevel_repos matters in that case.
2521a7c91847Schristos */
2522a7c91847Schristos
2523a7c91847Schristos int repository_len, update_dir_len;
2524a7c91847Schristos
2525a7c91847Schristos strip_trailing_slashes (update_dir);
2526a7c91847Schristos
2527a7c91847Schristos repository_len = strlen (repository);
2528a7c91847Schristos update_dir_len = strlen (update_dir);
2529a7c91847Schristos
2530a7c91847Schristos /* Try to remove the path components in UPDATE_DIR
2531a7c91847Schristos from REPOSITORY. If the path elements don't exist
2532a7c91847Schristos in REPOSITORY, or the removal of those path
2533a7c91847Schristos elements mean that we "step above"
2534a7c91847Schristos current_parsed_root->directory, set toplevel_repos to
2535a7c91847Schristos current_parsed_root->directory. */
2536a7c91847Schristos if (repository_len > update_dir_len
2537a7c91847Schristos && !strcmp (repository + repository_len - update_dir_len,
2538a7c91847Schristos update_dir)
2539a7c91847Schristos /* TOPLEVEL_REPOS shouldn't be above current_parsed_root->directory */
2540a7c91847Schristos && ((size_t)(repository_len - update_dir_len)
2541a7c91847Schristos > strlen (current_parsed_root->directory)))
2542a7c91847Schristos {
2543a7c91847Schristos /* The repository name contains UPDATE_DIR. Set
2544a7c91847Schristos toplevel_repos to the repository name without
2545a7c91847Schristos UPDATE_DIR. */
2546a7c91847Schristos
2547a7c91847Schristos toplevel_repos = xmalloc (repository_len - update_dir_len);
2548a7c91847Schristos /* Note that we don't copy the trailing '/'. */
2549a7c91847Schristos strncpy (toplevel_repos, repository,
2550a7c91847Schristos repository_len - update_dir_len - 1);
2551a7c91847Schristos toplevel_repos[repository_len - update_dir_len - 1] = '\0';
2552a7c91847Schristos }
2553a7c91847Schristos else
2554a7c91847Schristos {
2555a7c91847Schristos toplevel_repos = xstrdup (current_parsed_root->directory);
2556a7c91847Schristos }
2557a7c91847Schristos }
2558a7c91847Schristos }
2559a7c91847Schristos }
2560a7c91847Schristos
2561a7c91847Schristos send_repository (dir, repository, update_dir);
2562a7c91847Schristos free (update_dir);
2563a7c91847Schristos }
2564a7c91847Schristos
2565a7c91847Schristos
2566a7c91847Schristos
2567a7c91847Schristos static void
notified_a_file(void * data,List * ent_list,const char * short_pathname,const char * filename)2568a7c91847Schristos notified_a_file (void *data, List *ent_list, const char *short_pathname,
2569a7c91847Schristos const char *filename)
2570a7c91847Schristos {
2571a7c91847Schristos FILE *fp;
2572a7c91847Schristos FILE *newf;
2573a7c91847Schristos size_t line_len = 8192;
2574a7c91847Schristos char *line = xmalloc (line_len);
2575a7c91847Schristos char *cp;
2576a7c91847Schristos int nread;
2577a7c91847Schristos int nwritten;
2578a7c91847Schristos char *p;
2579a7c91847Schristos
2580a7c91847Schristos fp = xfopen (CVSADM_NOTIFY, "r");
2581a7c91847Schristos if (getline (&line, &line_len, fp) < 0)
2582a7c91847Schristos {
2583a7c91847Schristos if (feof (fp))
2584a7c91847Schristos error (0, 0, "cannot read %s: end of file", CVSADM_NOTIFY);
2585a7c91847Schristos else
2586a7c91847Schristos error (0, errno, "cannot read %s", CVSADM_NOTIFY);
2587a7c91847Schristos goto error_exit;
2588a7c91847Schristos }
2589a7c91847Schristos cp = strchr (line, '\t');
2590a7c91847Schristos if (!cp)
2591a7c91847Schristos {
2592a7c91847Schristos error (0, 0, "malformed %s file", CVSADM_NOTIFY);
2593a7c91847Schristos goto error_exit;
2594a7c91847Schristos }
2595a7c91847Schristos *cp = '\0';
2596a7c91847Schristos if (strcmp (filename, line + 1))
2597a7c91847Schristos error (0, 0, "protocol error: notified %s, expected %s", filename,
2598a7c91847Schristos line + 1);
2599a7c91847Schristos
2600a7c91847Schristos if (getline (&line, &line_len, fp) < 0)
2601a7c91847Schristos {
2602a7c91847Schristos if (feof (fp))
2603a7c91847Schristos {
2604a7c91847Schristos free (line);
2605a7c91847Schristos if (fclose (fp) < 0)
2606a7c91847Schristos error (0, errno, "cannot close %s", CVSADM_NOTIFY);
2607a7c91847Schristos if ( CVS_UNLINK (CVSADM_NOTIFY) < 0)
2608a7c91847Schristos error (0, errno, "cannot remove %s", CVSADM_NOTIFY);
2609a7c91847Schristos return;
2610a7c91847Schristos }
2611a7c91847Schristos else
2612a7c91847Schristos {
2613a7c91847Schristos error (0, errno, "cannot read %s", CVSADM_NOTIFY);
2614a7c91847Schristos goto error_exit;
2615a7c91847Schristos }
2616a7c91847Schristos }
2617a7c91847Schristos newf = xfopen (CVSADM_NOTIFYTMP, "w");
2618a7c91847Schristos if (fputs (line, newf) < 0)
2619a7c91847Schristos {
2620a7c91847Schristos error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
2621a7c91847Schristos goto error2;
2622a7c91847Schristos }
2623a7c91847Schristos while ((nread = fread (line, 1, line_len, fp)) > 0)
2624a7c91847Schristos {
2625a7c91847Schristos p = line;
2626a7c91847Schristos while ((nwritten = fwrite (p, sizeof *p, nread, newf)) > 0)
2627a7c91847Schristos {
2628a7c91847Schristos nread -= nwritten;
2629a7c91847Schristos p += nwritten;
2630a7c91847Schristos }
2631a7c91847Schristos if (ferror (newf))
2632a7c91847Schristos {
2633a7c91847Schristos error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
2634a7c91847Schristos goto error2;
2635a7c91847Schristos }
2636a7c91847Schristos }
2637a7c91847Schristos if (ferror (fp))
2638a7c91847Schristos {
2639a7c91847Schristos error (0, errno, "cannot read %s", CVSADM_NOTIFY);
2640a7c91847Schristos goto error2;
2641a7c91847Schristos }
2642a7c91847Schristos if (fclose (newf) < 0)
2643a7c91847Schristos {
2644a7c91847Schristos error (0, errno, "cannot close %s", CVSADM_NOTIFYTMP);
2645a7c91847Schristos goto error_exit;
2646a7c91847Schristos }
2647a7c91847Schristos free (line);
2648a7c91847Schristos if (fclose (fp) < 0)
2649a7c91847Schristos {
2650a7c91847Schristos error (0, errno, "cannot close %s", CVSADM_NOTIFY);
2651a7c91847Schristos return;
2652a7c91847Schristos }
2653a7c91847Schristos
2654a7c91847Schristos {
2655a7c91847Schristos /* In this case, we want rename_file() to ignore noexec. */
2656a7c91847Schristos int saved_noexec = noexec;
2657a7c91847Schristos noexec = 0;
2658a7c91847Schristos rename_file (CVSADM_NOTIFYTMP, CVSADM_NOTIFY);
2659a7c91847Schristos noexec = saved_noexec;
2660a7c91847Schristos }
2661a7c91847Schristos
2662a7c91847Schristos return;
2663a7c91847Schristos error2:
2664a7c91847Schristos (void)fclose (newf);
2665a7c91847Schristos error_exit:
2666a7c91847Schristos free (line);
2667a7c91847Schristos (void)fclose (fp);
2668a7c91847Schristos }
2669a7c91847Schristos
2670a7c91847Schristos
2671a7c91847Schristos
2672a7c91847Schristos static void
handle_notified(char * args,size_t len)2673a7c91847Schristos handle_notified (char *args, size_t len)
2674a7c91847Schristos {
2675a7c91847Schristos call_in_directory (args, notified_a_file, NULL);
2676a7c91847Schristos }
2677a7c91847Schristos
2678a7c91847Schristos
2679a7c91847Schristos
2680a7c91847Schristos /* The "expanded" modules. */
2681a7c91847Schristos static int modules_count;
2682a7c91847Schristos static int modules_allocated;
2683a7c91847Schristos static char **modules_vector;
2684a7c91847Schristos
2685a7c91847Schristos static void
handle_module_expansion(char * args,size_t len)2686a7c91847Schristos handle_module_expansion (char *args, size_t len)
2687a7c91847Schristos {
2688a7c91847Schristos if (!modules_vector)
2689a7c91847Schristos {
2690a7c91847Schristos modules_allocated = 1; /* Small for testing */
2691a7c91847Schristos modules_vector = xnmalloc (modules_allocated,
2692a7c91847Schristos sizeof (modules_vector[0]));
2693a7c91847Schristos }
2694a7c91847Schristos else if (modules_count >= modules_allocated)
2695a7c91847Schristos {
2696a7c91847Schristos modules_allocated *= 2;
2697a7c91847Schristos modules_vector = xnrealloc (modules_vector,
2698a7c91847Schristos modules_allocated,
2699a7c91847Schristos sizeof (modules_vector[0]));
2700a7c91847Schristos }
2701a7c91847Schristos modules_vector[modules_count] = xstrdup (args);
2702a7c91847Schristos ++modules_count;
2703a7c91847Schristos }
2704a7c91847Schristos
2705a7c91847Schristos
2706a7c91847Schristos
2707a7c91847Schristos /* Original, not "expanded" modules. */
2708a7c91847Schristos static int module_argc;
2709a7c91847Schristos static char **module_argv;
2710a7c91847Schristos
2711a7c91847Schristos void
client_expand_modules(int argc,char ** argv,int local)2712a7c91847Schristos client_expand_modules (int argc, char **argv, int local)
2713a7c91847Schristos {
2714a7c91847Schristos int errs;
2715a7c91847Schristos int i;
2716a7c91847Schristos
2717a7c91847Schristos module_argc = argc;
2718a7c91847Schristos module_argv = xnmalloc (argc + 1, sizeof (module_argv[0]));
2719a7c91847Schristos for (i = 0; i < argc; ++i)
2720a7c91847Schristos module_argv[i] = xstrdup (argv[i]);
2721a7c91847Schristos module_argv[argc] = NULL;
2722a7c91847Schristos
2723a7c91847Schristos for (i = 0; i < argc; ++i)
2724a7c91847Schristos send_arg (argv[i]);
2725a7c91847Schristos send_a_repository ("", current_parsed_root->directory, "");
2726a7c91847Schristos
2727a7c91847Schristos send_to_server ("expand-modules\012", 0);
2728a7c91847Schristos
2729a7c91847Schristos errs = get_server_responses ();
2730a7c91847Schristos
2731a7c91847Schristos if (last_repos) free (last_repos);
2732a7c91847Schristos last_repos = NULL;
2733a7c91847Schristos
2734a7c91847Schristos if (last_update_dir) free (last_update_dir);
2735a7c91847Schristos last_update_dir = NULL;
2736a7c91847Schristos
2737a7c91847Schristos if (errs)
2738a7c91847Schristos error (errs, 0, "cannot expand modules");
2739a7c91847Schristos }
2740a7c91847Schristos
2741a7c91847Schristos
2742a7c91847Schristos
2743a7c91847Schristos void
client_send_expansions(int local,char * where,int build_dirs)2744a7c91847Schristos client_send_expansions (int local, char *where, int build_dirs)
2745a7c91847Schristos {
2746a7c91847Schristos int i;
2747a7c91847Schristos char *argv[1];
2748a7c91847Schristos
2749a7c91847Schristos /* Send the original module names. The "expanded" module name might
2750a7c91847Schristos not be suitable as an argument to a co request (e.g. it might be
2751a7c91847Schristos the result of a -d argument in the modules file). It might be
2752a7c91847Schristos cleaner if we genuinely expanded module names, all the way to a
2753a7c91847Schristos local directory and repository, but that isn't the way it works
2754a7c91847Schristos now. */
2755a7c91847Schristos send_file_names (module_argc, module_argv, 0);
2756a7c91847Schristos
2757a7c91847Schristos for (i = 0; i < modules_count; ++i)
2758a7c91847Schristos {
2759a7c91847Schristos argv[0] = where ? where : modules_vector[i];
2760a7c91847Schristos if (isfile (argv[0]))
2761a7c91847Schristos send_files (1, argv, local, 0, build_dirs ? SEND_BUILD_DIRS : 0);
2762a7c91847Schristos }
2763a7c91847Schristos send_a_repository ("", current_parsed_root->directory, "");
2764a7c91847Schristos }
2765a7c91847Schristos
2766a7c91847Schristos
2767a7c91847Schristos
2768a7c91847Schristos void
client_nonexpanded_setup(void)2769a7c91847Schristos client_nonexpanded_setup (void)
2770a7c91847Schristos {
2771a7c91847Schristos send_a_repository ("", current_parsed_root->directory, "");
2772a7c91847Schristos }
2773a7c91847Schristos
2774a7c91847Schristos
2775a7c91847Schristos
2776a7c91847Schristos /* Receive a cvswrappers line from the server; it must be a line
2777a7c91847Schristos containing an RCS option (e.g., "*.exe -k 'b'").
2778a7c91847Schristos
2779a7c91847Schristos Note that this doesn't try to handle -t/-f options (which are a
2780a7c91847Schristos whole separate issue which noone has thought much about, as far
2781a7c91847Schristos as I know).
2782a7c91847Schristos
2783a7c91847Schristos We need to know the keyword expansion mode so we know whether to
2784a7c91847Schristos read the file in text or binary mode. */
2785a7c91847Schristos static void
handle_wrapper_rcs_option(char * args,size_t len)2786a7c91847Schristos handle_wrapper_rcs_option (char *args, size_t len)
2787a7c91847Schristos {
2788a7c91847Schristos char *p;
2789a7c91847Schristos
2790a7c91847Schristos /* Enforce the notes in cvsclient.texi about how the response is not
2791a7c91847Schristos as free-form as it looks. */
2792a7c91847Schristos p = strchr (args, ' ');
2793a7c91847Schristos if (!p)
2794a7c91847Schristos goto handle_error;
2795a7c91847Schristos if (*++p != '-'
2796a7c91847Schristos || *++p != 'k'
2797a7c91847Schristos || *++p != ' '
2798a7c91847Schristos || *++p != '\'')
2799a7c91847Schristos goto handle_error;
2800a7c91847Schristos if (!strchr (p, '\''))
2801a7c91847Schristos goto handle_error;
2802a7c91847Schristos
2803a7c91847Schristos /* Add server-side cvswrappers line to our wrapper list. */
2804a7c91847Schristos wrap_add (args, 0);
2805a7c91847Schristos return;
2806a7c91847Schristos handle_error:
2807a7c91847Schristos error (0, errno, "protocol error: ignoring invalid wrappers %s", args);
2808a7c91847Schristos }
2809a7c91847Schristos
2810a7c91847Schristos
2811a7c91847Schristos
2812a7c91847Schristos
2813a7c91847Schristos static void
handle_m(char * args,size_t len)2814a7c91847Schristos handle_m (char *args, size_t len)
2815a7c91847Schristos {
2816a7c91847Schristos /* In the case where stdout and stderr point to the same place,
2817a7c91847Schristos fflushing stderr will make output happen in the correct order.
2818a7c91847Schristos Often stderr will be line-buffered and this won't be needed,
2819a7c91847Schristos but not always (is that true? I think the comment is probably
2820a7c91847Schristos based on being confused between default buffering between
2821a7c91847Schristos stdout and stderr. But I'm not sure). */
2822a7c91847Schristos fflush (stderr);
2823a7c91847Schristos fwrite (args, sizeof *args, len, stdout);
2824a7c91847Schristos putc ('\n', stdout);
2825a7c91847Schristos }
2826a7c91847Schristos
2827a7c91847Schristos
2828a7c91847Schristos
2829a7c91847Schristos static void
handle_mbinary(char * args,size_t len)2830a7c91847Schristos handle_mbinary (char *args, size_t len)
2831a7c91847Schristos {
2832a7c91847Schristos char *size_string;
2833a7c91847Schristos size_t size;
2834a7c91847Schristos size_t totalread;
2835a7c91847Schristos size_t nread;
2836a7c91847Schristos size_t toread;
2837a7c91847Schristos char buf[8192];
2838a7c91847Schristos
2839a7c91847Schristos /* See comment at handle_m about (non)flush of stderr. */
2840a7c91847Schristos
2841a7c91847Schristos /* Get the size. */
2842a7c91847Schristos read_line (&size_string);
2843a7c91847Schristos size = atoi (size_string);
2844a7c91847Schristos free (size_string);
2845a7c91847Schristos
2846a7c91847Schristos /* OK, now get all the data. The algorithm here is that we read
2847a7c91847Schristos as much as the network wants to give us in
2848a7c91847Schristos try_read_from_server, and then we output it all, and then
2849a7c91847Schristos repeat, until we get all the data. */
2850a7c91847Schristos totalread = 0;
2851a7c91847Schristos while (totalread < size)
2852a7c91847Schristos {
2853a7c91847Schristos toread = size - totalread;
2854a7c91847Schristos if (toread > sizeof buf)
2855a7c91847Schristos toread = sizeof buf;
2856a7c91847Schristos
2857a7c91847Schristos nread = try_read_from_server (buf, toread);
2858a7c91847Schristos cvs_output_binary (buf, nread);
2859a7c91847Schristos totalread += nread;
2860a7c91847Schristos }
2861a7c91847Schristos }
2862a7c91847Schristos
2863a7c91847Schristos
2864a7c91847Schristos
2865a7c91847Schristos static void
handle_e(char * args,size_t len)2866a7c91847Schristos handle_e (char *args, size_t len)
2867a7c91847Schristos {
2868a7c91847Schristos /* In the case where stdout and stderr point to the same place,
2869a7c91847Schristos fflushing stdout will make output happen in the correct order. */
2870a7c91847Schristos fflush (stdout);
2871a7c91847Schristos fwrite (args, sizeof *args, len, stderr);
2872a7c91847Schristos putc ('\n', stderr);
2873a7c91847Schristos }
2874a7c91847Schristos
2875a7c91847Schristos
2876a7c91847Schristos
2877a7c91847Schristos /*ARGSUSED*/
2878a7c91847Schristos static void
handle_f(char * args,size_t len)2879a7c91847Schristos handle_f (char *args, size_t len)
2880a7c91847Schristos {
2881a7c91847Schristos fflush (stderr);
2882a7c91847Schristos }
2883a7c91847Schristos
2884a7c91847Schristos
2885a7c91847Schristos
2886a7c91847Schristos static void
handle_mt(char * args,size_t len)2887a7c91847Schristos handle_mt (char *args, size_t len)
2888a7c91847Schristos {
2889a7c91847Schristos char *p;
2890a7c91847Schristos char *tag = args;
2891a7c91847Schristos char *text;
2892a7c91847Schristos
2893a7c91847Schristos /* See comment at handle_m for more details. */
2894a7c91847Schristos fflush (stderr);
2895a7c91847Schristos
2896a7c91847Schristos p = strchr (args, ' ');
2897a7c91847Schristos if (!p)
2898a7c91847Schristos text = NULL;
2899a7c91847Schristos else
2900a7c91847Schristos {
2901a7c91847Schristos *p++ = '\0';
2902a7c91847Schristos text = p;
2903a7c91847Schristos }
2904a7c91847Schristos
2905a7c91847Schristos switch (tag[0])
2906a7c91847Schristos {
2907a7c91847Schristos case '+':
2908a7c91847Schristos if (!strcmp (tag, "+updated"))
2909a7c91847Schristos updated_seen = 1;
2910a7c91847Schristos else if (!strcmp (tag, "+importmergecmd"))
2911a7c91847Schristos importmergecmd.seen = 1;
2912a7c91847Schristos break;
2913a7c91847Schristos case '-':
2914a7c91847Schristos if (!strcmp (tag, "-updated"))
2915a7c91847Schristos updated_seen = 0;
2916a7c91847Schristos else if (!strcmp (tag, "-importmergecmd"))
2917a7c91847Schristos {
2918a7c91847Schristos char buf[80];
2919a7c91847Schristos
2920a7c91847Schristos /* Now that we have gathered the information, we can
2921a7c91847Schristos output the suggested merge command. */
2922a7c91847Schristos
2923a7c91847Schristos if (importmergecmd.conflicts == 0
2924a7c91847Schristos || !importmergecmd.mergetag1
2925a7c91847Schristos || !importmergecmd.mergetag2
2926a7c91847Schristos || !importmergecmd.repository)
2927a7c91847Schristos {
2928a7c91847Schristos error (0, 0,
2929a7c91847Schristos "invalid server: incomplete importmergecmd tags");
2930a7c91847Schristos break;
2931a7c91847Schristos }
2932a7c91847Schristos
2933a7c91847Schristos if (importmergecmd.conflicts == -1)
2934a7c91847Schristos sprintf (buf, "\nNo conflicts created by this import.\n");
2935a7c91847Schristos else
2936a7c91847Schristos sprintf (buf, "\n%d conflicts created by this import.\n",
2937a7c91847Schristos importmergecmd.conflicts);
2938a7c91847Schristos cvs_output (buf, 0);
2939a7c91847Schristos cvs_output ("Use the following command to help the merge:\n\n",
2940a7c91847Schristos 0);
2941a7c91847Schristos cvs_output ("\t", 1);
2942a7c91847Schristos cvs_output (program_name, 0);
2943a7c91847Schristos if (CVSroot_cmdline)
2944a7c91847Schristos {
2945a7c91847Schristos cvs_output (" -d ", 0);
2946a7c91847Schristos cvs_output (CVSroot_cmdline, 0);
2947a7c91847Schristos }
2948a7c91847Schristos cvs_output (" checkout -j", 0);
2949a7c91847Schristos cvs_output (importmergecmd.mergetag1, 0);
2950a7c91847Schristos cvs_output (" -j", 0);
2951a7c91847Schristos cvs_output (importmergecmd.mergetag2, 0);
2952a7c91847Schristos cvs_output (" ", 1);
2953a7c91847Schristos cvs_output (importmergecmd.repository, 0);
2954a7c91847Schristos cvs_output ("\n\n", 0);
2955a7c91847Schristos
2956a7c91847Schristos /* Clear the static variables so that everything is
2957a7c91847Schristos ready for any subsequent importmergecmd tag. */
2958a7c91847Schristos importmergecmd.conflicts = 0;
2959a7c91847Schristos free (importmergecmd.mergetag1);
2960a7c91847Schristos importmergecmd.mergetag1 = NULL;
2961a7c91847Schristos free (importmergecmd.mergetag2);
2962a7c91847Schristos importmergecmd.mergetag2 = NULL;
2963a7c91847Schristos free (importmergecmd.repository);
2964a7c91847Schristos importmergecmd.repository = NULL;
2965a7c91847Schristos
2966a7c91847Schristos importmergecmd.seen = 0;
2967a7c91847Schristos }
2968a7c91847Schristos break;
2969a7c91847Schristos default:
2970a7c91847Schristos if (updated_seen)
2971a7c91847Schristos {
2972a7c91847Schristos if (!strcmp (tag, "fname"))
2973a7c91847Schristos {
2974a7c91847Schristos if (updated_fname)
2975a7c91847Schristos {
2976a7c91847Schristos /* Output the previous message now. This can happen
2977a7c91847Schristos if there was no Update-existing or other such
2978a7c91847Schristos response, due to the -n global option. */
2979a7c91847Schristos cvs_output ("U ", 0);
2980a7c91847Schristos cvs_output (updated_fname, 0);
2981a7c91847Schristos cvs_output ("\n", 1);
2982a7c91847Schristos free (updated_fname);
2983a7c91847Schristos }
2984a7c91847Schristos updated_fname = xstrdup (text);
2985a7c91847Schristos }
2986a7c91847Schristos /* Swallow all other tags. Either they are extraneous
2987a7c91847Schristos or they reflect future extensions that we can
2988a7c91847Schristos safely ignore. */
2989a7c91847Schristos }
2990a7c91847Schristos else if (importmergecmd.seen)
2991a7c91847Schristos {
2992a7c91847Schristos if (!strcmp (tag, "conflicts"))
2993a7c91847Schristos {
2994a7c91847Schristos if (!strcmp (text, "No"))
2995a7c91847Schristos importmergecmd.conflicts = -1;
2996a7c91847Schristos else
2997a7c91847Schristos importmergecmd.conflicts = atoi (text);
2998a7c91847Schristos }
2999a7c91847Schristos else if (!strcmp (tag, "mergetag1"))
3000a7c91847Schristos importmergecmd.mergetag1 = xstrdup (text);
3001a7c91847Schristos else if (!strcmp (tag, "mergetag2"))
3002a7c91847Schristos importmergecmd.mergetag2 = xstrdup (text);
3003a7c91847Schristos else if (!strcmp (tag, "repository"))
3004a7c91847Schristos importmergecmd.repository = xstrdup (text);
3005a7c91847Schristos /* Swallow all other tags. Either they are text for
3006a7c91847Schristos which we are going to print our own version when we
3007a7c91847Schristos see -importmergecmd, or they are future extensions
3008a7c91847Schristos we can safely ignore. */
3009a7c91847Schristos }
3010a7c91847Schristos else if (!strcmp (tag, "newline"))
3011a7c91847Schristos printf ("\n");
3012a7c91847Schristos else if (!strcmp (tag, "date"))
3013a7c91847Schristos {
3014a7c91847Schristos char *date = format_date_alloc (text);
3015a7c91847Schristos printf ("%s", date);
3016a7c91847Schristos free (date);
3017a7c91847Schristos }
3018a7c91847Schristos else if (text)
3019a7c91847Schristos printf ("%s", text);
3020a7c91847Schristos }
3021a7c91847Schristos }
3022a7c91847Schristos
3023a7c91847Schristos
3024a7c91847Schristos
3025a7c91847Schristos #endif /* CLIENT_SUPPORT */
3026a7c91847Schristos #if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
3027a7c91847Schristos
3028a7c91847Schristos /* This table must be writeable if the server code is included. */
3029a7c91847Schristos struct response responses[] =
3030a7c91847Schristos {
3031a7c91847Schristos #ifdef CLIENT_SUPPORT
3032a7c91847Schristos #define RSP_LINE(n, f, t, s) {n, f, t, s}
3033a7c91847Schristos #else /* ! CLIENT_SUPPORT */
3034a7c91847Schristos #define RSP_LINE(n, f, t, s) {n, s}
3035a7c91847Schristos #endif /* CLIENT_SUPPORT */
3036a7c91847Schristos
3037a7c91847Schristos RSP_LINE("ok", handle_ok, response_type_ok, rs_essential),
3038a7c91847Schristos RSP_LINE("error", handle_error, response_type_error, rs_essential),
3039a7c91847Schristos RSP_LINE("Valid-requests", handle_valid_requests, response_type_normal,
3040a7c91847Schristos rs_essential),
3041a7c91847Schristos RSP_LINE("Force-gzip", handle_force_gzip, response_type_normal,
3042a7c91847Schristos rs_optional),
3043a7c91847Schristos RSP_LINE("Referrer", handle_referrer, response_type_normal, rs_optional),
3044a7c91847Schristos RSP_LINE("Redirect", handle_redirect, response_type_redirect, rs_optional),
3045a7c91847Schristos RSP_LINE("Checked-in", handle_checked_in, response_type_normal,
3046a7c91847Schristos rs_essential),
3047a7c91847Schristos RSP_LINE("New-entry", handle_new_entry, response_type_normal, rs_optional),
3048a7c91847Schristos RSP_LINE("Checksum", handle_checksum, response_type_normal, rs_optional),
3049a7c91847Schristos RSP_LINE("Copy-file", handle_copy_file, response_type_normal, rs_optional),
3050a7c91847Schristos RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential),
3051a7c91847Schristos RSP_LINE("Created", handle_created, response_type_normal, rs_optional),
3052a7c91847Schristos RSP_LINE("Update-existing", handle_update_existing, response_type_normal,
3053a7c91847Schristos rs_optional),
3054a7c91847Schristos RSP_LINE("Merged", handle_merged, response_type_normal, rs_essential),
3055a7c91847Schristos RSP_LINE("Patched", handle_patched, response_type_normal, rs_optional),
3056a7c91847Schristos RSP_LINE("Rcs-diff", handle_rcs_diff, response_type_normal, rs_optional),
3057a7c91847Schristos RSP_LINE("Mode", handle_mode, response_type_normal, rs_optional),
3058a7c91847Schristos RSP_LINE("Mod-time", handle_mod_time, response_type_normal, rs_optional),
3059a7c91847Schristos RSP_LINE("Removed", handle_removed, response_type_normal, rs_essential),
3060a7c91847Schristos RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal,
3061a7c91847Schristos rs_optional),
3062a7c91847Schristos RSP_LINE("Set-static-directory", handle_set_static_directory,
3063a7c91847Schristos response_type_normal,
3064a7c91847Schristos rs_optional),
3065a7c91847Schristos RSP_LINE("Clear-static-directory", handle_clear_static_directory,
3066a7c91847Schristos response_type_normal,
3067a7c91847Schristos rs_optional),
3068a7c91847Schristos RSP_LINE("Set-sticky", handle_set_sticky, response_type_normal,
3069a7c91847Schristos rs_optional),
3070a7c91847Schristos RSP_LINE("Clear-sticky", handle_clear_sticky, response_type_normal,
3071a7c91847Schristos rs_optional),
3072a7c91847Schristos RSP_LINE("Edit-file", handle_edit_file, response_type_normal,
3073a7c91847Schristos rs_optional),
3074a7c91847Schristos RSP_LINE("Template", handle_template, response_type_normal,
3075a7c91847Schristos rs_optional),
3076a7c91847Schristos RSP_LINE("Clear-template", handle_clear_template, response_type_normal,
3077a7c91847Schristos rs_optional),
3078a7c91847Schristos RSP_LINE("Notified", handle_notified, response_type_normal, rs_optional),
3079a7c91847Schristos RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal,
3080a7c91847Schristos rs_optional),
3081a7c91847Schristos RSP_LINE("Wrapper-rcsOption", handle_wrapper_rcs_option,
3082a7c91847Schristos response_type_normal,
3083a7c91847Schristos rs_optional),
3084a7c91847Schristos RSP_LINE("M", handle_m, response_type_normal, rs_essential),
3085a7c91847Schristos RSP_LINE("Mbinary", handle_mbinary, response_type_normal, rs_optional),
3086a7c91847Schristos RSP_LINE("E", handle_e, response_type_normal, rs_essential),
3087a7c91847Schristos RSP_LINE("F", handle_f, response_type_normal, rs_optional),
3088a7c91847Schristos RSP_LINE("MT", handle_mt, response_type_normal, rs_optional),
3089a7c91847Schristos /* Possibly should be response_type_error. */
3090a7c91847Schristos RSP_LINE(NULL, NULL, response_type_normal, rs_essential)
3091a7c91847Schristos
3092a7c91847Schristos #undef RSP_LINE
3093a7c91847Schristos };
3094a7c91847Schristos
3095a7c91847Schristos #endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
3096a7c91847Schristos #ifdef CLIENT_SUPPORT
3097a7c91847Schristos
3098a7c91847Schristos
3099a7c91847Schristos
3100a7c91847Schristos /*
3101a7c91847Schristos * If LEN is 0, then send_to_server_via() computes string's length itself.
3102a7c91847Schristos *
3103a7c91847Schristos * Therefore, pass the real length when transmitting data that might
3104a7c91847Schristos * contain 0's.
3105a7c91847Schristos */
3106a7c91847Schristos void
send_to_server_via(struct buffer * via_buffer,const char * str,size_t len)3107a7c91847Schristos send_to_server_via (struct buffer *via_buffer, const char *str, size_t len)
3108a7c91847Schristos {
3109a7c91847Schristos static int nbytes;
3110a7c91847Schristos
3111a7c91847Schristos if (len == 0)
3112a7c91847Schristos len = strlen (str);
3113a7c91847Schristos
3114a7c91847Schristos buf_output (via_buffer, str, len);
3115a7c91847Schristos
3116a7c91847Schristos /* There is no reason not to send data to the server, so do it
3117a7c91847Schristos whenever we've accumulated enough information in the buffer to
3118a7c91847Schristos make it worth sending. */
3119a7c91847Schristos nbytes += len;
3120a7c91847Schristos if (nbytes >= 2 * BUFFER_DATA_SIZE)
3121a7c91847Schristos {
3122a7c91847Schristos int status;
3123a7c91847Schristos
3124a7c91847Schristos status = buf_send_output (via_buffer);
3125a7c91847Schristos if (status != 0)
3126a7c91847Schristos error (1, status, "error writing to server");
3127a7c91847Schristos nbytes = 0;
3128a7c91847Schristos }
3129a7c91847Schristos }
3130a7c91847Schristos
3131a7c91847Schristos
3132a7c91847Schristos
3133a7c91847Schristos void
send_to_server(const char * str,size_t len)3134a7c91847Schristos send_to_server (const char *str, size_t len)
3135a7c91847Schristos {
3136a7c91847Schristos send_to_server_via (global_to_server, str, len);
3137a7c91847Schristos }
3138a7c91847Schristos
3139a7c91847Schristos
3140a7c91847Schristos
3141a7c91847Schristos /* Read up to LEN bytes from the server. Returns actual number of
3142a7c91847Schristos bytes read, which will always be at least one; blocks if there is
3143a7c91847Schristos no data available at all. Gives a fatal error on EOF or error. */
3144a7c91847Schristos static size_t
try_read_from_server(char * buf,size_t len)3145a7c91847Schristos try_read_from_server( char *buf, size_t len )
3146a7c91847Schristos {
3147a7c91847Schristos int status;
3148a7c91847Schristos size_t nread;
3149a7c91847Schristos char *data;
3150a7c91847Schristos
3151a7c91847Schristos status = buf_read_data (global_from_server, len, &data, &nread);
3152a7c91847Schristos if (status != 0)
3153a7c91847Schristos {
3154a7c91847Schristos if (status == -1)
3155a7c91847Schristos error (1, 0,
3156a7c91847Schristos "end of file from server (consult above messages if any)");
3157a7c91847Schristos else if (status == -2)
3158a7c91847Schristos error (1, 0, "out of memory");
3159a7c91847Schristos else
3160a7c91847Schristos error (1, status, "reading from server");
3161a7c91847Schristos }
3162a7c91847Schristos
3163a7c91847Schristos memcpy (buf, data, nread);
3164a7c91847Schristos
3165a7c91847Schristos return nread;
3166a7c91847Schristos }
3167a7c91847Schristos
3168a7c91847Schristos
3169a7c91847Schristos
3170a7c91847Schristos /*
3171a7c91847Schristos * Read LEN bytes from the server or die trying.
3172a7c91847Schristos */
3173a7c91847Schristos void
read_from_server(char * buf,size_t len)3174a7c91847Schristos read_from_server (char *buf, size_t len)
3175a7c91847Schristos {
3176a7c91847Schristos size_t red = 0;
3177a7c91847Schristos while (red < len)
3178a7c91847Schristos {
3179a7c91847Schristos red += try_read_from_server (buf + red, len - red);
3180a7c91847Schristos if (red == len)
3181a7c91847Schristos break;
3182a7c91847Schristos }
3183a7c91847Schristos }
3184a7c91847Schristos
3185a7c91847Schristos
3186a7c91847Schristos
3187a7c91847Schristos /* Get some server responses and process them.
3188a7c91847Schristos *
3189a7c91847Schristos * RETURNS
3190a7c91847Schristos * 0 Success
3191a7c91847Schristos * 1 Error
3192a7c91847Schristos * 2 Redirect
3193a7c91847Schristos */
3194a7c91847Schristos int
get_server_responses(void)3195a7c91847Schristos get_server_responses (void)
3196a7c91847Schristos {
3197a7c91847Schristos struct response *rs;
3198a7c91847Schristos do
3199a7c91847Schristos {
3200a7c91847Schristos char *cmd;
3201a7c91847Schristos size_t len;
3202a7c91847Schristos
3203a7c91847Schristos len = read_line (&cmd);
3204a7c91847Schristos for (rs = responses; rs->name; ++rs)
3205a7c91847Schristos if (!strncmp (cmd, rs->name, strlen (rs->name)))
3206a7c91847Schristos {
3207a7c91847Schristos size_t cmdlen = strlen (rs->name);
3208a7c91847Schristos if (cmd[cmdlen] == '\0')
3209a7c91847Schristos ;
3210a7c91847Schristos else if (cmd[cmdlen] == ' ')
3211a7c91847Schristos ++cmdlen;
3212a7c91847Schristos else
3213a7c91847Schristos /*
3214a7c91847Schristos * The first len characters match, but it's a different
3215a7c91847Schristos * response. e.g. the response is "oklahoma" but we
3216a7c91847Schristos * matched "ok".
3217a7c91847Schristos */
3218a7c91847Schristos continue;
3219a7c91847Schristos (*rs->func) (cmd + cmdlen, len - cmdlen);
3220a7c91847Schristos break;
3221a7c91847Schristos }
3222a7c91847Schristos if (!rs->name)
3223a7c91847Schristos /* It's OK to print just to the first '\0'. */
3224a7c91847Schristos /* We might want to handle control characters and the like
3225a7c91847Schristos in some other way other than just sending them to stdout.
3226a7c91847Schristos One common reason for this error is if people use :ext:
3227a7c91847Schristos with a version of rsh which is doing CRLF translation or
3228a7c91847Schristos something, and so the client gets "ok^M" instead of "ok".
3229a7c91847Schristos Right now that will tend to print part of this error
3230a7c91847Schristos message over the other part of it. It seems like we could
3231a7c91847Schristos do better (either in general, by quoting or omitting all
3232a7c91847Schristos control characters, and/or specifically, by detecting the CRLF
3233a7c91847Schristos case and printing a specific error message). */
3234a7c91847Schristos error (0, 0,
3235a7c91847Schristos "warning: unrecognized response `%s' from cvs server",
3236a7c91847Schristos cmd);
3237a7c91847Schristos free (cmd);
3238a7c91847Schristos } while (rs->type == response_type_normal);
3239a7c91847Schristos
3240a7c91847Schristos if (updated_fname)
3241a7c91847Schristos {
3242a7c91847Schristos /* Output the previous message now. This can happen
3243a7c91847Schristos if there was no Update-existing or other such
3244a7c91847Schristos response, due to the -n global option. */
3245a7c91847Schristos cvs_output ("U ", 0);
3246a7c91847Schristos cvs_output (updated_fname, 0);
3247a7c91847Schristos cvs_output ("\n", 1);
3248a7c91847Schristos free (updated_fname);
3249a7c91847Schristos updated_fname = NULL;
3250a7c91847Schristos }
3251a7c91847Schristos
3252a7c91847Schristos if (rs->type == response_type_redirect) return 2;
3253a7c91847Schristos if (rs->type == response_type_error) return 1;
3254a7c91847Schristos if (failure_exit) return 1;
3255a7c91847Schristos return 0;
3256a7c91847Schristos }
3257a7c91847Schristos
3258a7c91847Schristos
3259a7c91847Schristos
3260a7c91847Schristos static inline void
close_connection_to_server(struct buffer ** to,struct buffer ** from)3261a7c91847Schristos close_connection_to_server (struct buffer **to, struct buffer **from)
3262a7c91847Schristos {
3263a7c91847Schristos int status;
3264a7c91847Schristos
3265a7c91847Schristos /* First we shut down GLOBAL_TO_SERVER. That tells the server that its
3266a7c91847Schristos * input is finished. It then shuts down the buffer it is sending to us,
3267a7c91847Schristos * at which point our shut down of GLOBAL_FROM_SERVER will complete.
3268a7c91847Schristos */
3269a7c91847Schristos
3270a7c91847Schristos TRACE (TRACE_FUNCTION, "close_connection_to_server ()");
3271a7c91847Schristos
3272a7c91847Schristos status = buf_shutdown (*to);
3273a7c91847Schristos if (status != 0)
3274a7c91847Schristos error (0, status, "shutting down buffer to server");
3275a7c91847Schristos buf_free (*to);
3276a7c91847Schristos *to = NULL;
3277a7c91847Schristos
3278a7c91847Schristos status = buf_shutdown (*from);
3279a7c91847Schristos if (status != 0)
3280a7c91847Schristos error (0, status, "shutting down buffer from server");
3281a7c91847Schristos buf_free (*from);
3282a7c91847Schristos *from = NULL;
3283a7c91847Schristos }
3284a7c91847Schristos
3285a7c91847Schristos
3286a7c91847Schristos
3287a7c91847Schristos /* Get the responses and then close the connection. */
3288a7c91847Schristos
3289a7c91847Schristos /*
3290a7c91847Schristos * Flag var; we'll set it in start_server() and not one of its
3291a7c91847Schristos * callees, such as start_rsh_server(). This means that there might
3292a7c91847Schristos * be a small window between the starting of the server and the
3293a7c91847Schristos * setting of this var, but all the code in that window shouldn't care
3294a7c91847Schristos * because it's busy checking return values to see if the server got
3295a7c91847Schristos * started successfully anyway.
3296a7c91847Schristos */
3297a7c91847Schristos int server_started = 0;
3298a7c91847Schristos
3299a7c91847Schristos int
get_responses_and_close(void)3300a7c91847Schristos get_responses_and_close (void)
3301a7c91847Schristos {
3302a7c91847Schristos int errs = get_server_responses ();
3303a7c91847Schristos
3304a7c91847Schristos /* The following is necessary when working with multiple cvsroots, at least
3305a7c91847Schristos * with commit. It used to be buried nicely in do_deferred_progs() before
3306a7c91847Schristos * that function was removed. I suspect it wouldn't be necessary if
3307a7c91847Schristos * call_in_directory() saved its working directory via save_cwd() before
3308a7c91847Schristos * changing its directory and restored the saved working directory via
3309a7c91847Schristos * restore_cwd() before exiting. Of course, calling CVS_CHDIR only once,
3310a7c91847Schristos * here, may be more efficient.
3311a7c91847Schristos */
3312a7c91847Schristos if (toplevel_wd)
3313a7c91847Schristos {
3314a7c91847Schristos if (CVS_CHDIR (toplevel_wd) < 0)
3315a7c91847Schristos error (1, errno, "could not chdir to %s", toplevel_wd);
3316a7c91847Schristos }
3317a7c91847Schristos
3318a7c91847Schristos if (client_prune_dirs)
3319a7c91847Schristos process_prune_candidates ();
3320a7c91847Schristos
3321a7c91847Schristos close_connection_to_server (&global_to_server, &global_from_server);
3322a7c91847Schristos server_started = 0;
3323a7c91847Schristos
3324a7c91847Schristos /* see if we need to sleep before returning to avoid time-stamp races */
3325a7c91847Schristos if (last_register_time)
3326a7c91847Schristos sleep_past (last_register_time);
3327a7c91847Schristos
3328a7c91847Schristos return errs;
3329a7c91847Schristos }
3330a7c91847Schristos
3331a7c91847Schristos
3332a7c91847Schristos
3333a7c91847Schristos bool
supported_request(const char * name)3334a7c91847Schristos supported_request (const char *name)
3335a7c91847Schristos {
3336a7c91847Schristos struct request *rq;
3337a7c91847Schristos
3338a7c91847Schristos for (rq = requests; rq->name; rq++)
3339a7c91847Schristos if (!strcmp (rq->name, name))
3340a7c91847Schristos return (rq->flags & RQ_SUPPORTED) != 0;
3341a7c91847Schristos error (1, 0, "internal error: testing support for unknown request?");
3342a7c91847Schristos /* NOTREACHED */
3343a7c91847Schristos return 0;
3344a7c91847Schristos }
3345a7c91847Schristos
3346a7c91847Schristos
3347a7c91847Schristos
3348a7c91847Schristos #if defined (AUTH_CLIENT_SUPPORT) || defined (SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI)
3349a7c91847Schristos
3350a7c91847Schristos
3351a7c91847Schristos /* Generic function to do port number lookup tasks.
3352a7c91847Schristos *
3353a7c91847Schristos * In order of precedence, will return:
3354a7c91847Schristos * getenv (envname), if defined
3355a7c91847Schristos * getservbyname (portname), if defined
3356a7c91847Schristos * defaultport
3357a7c91847Schristos */
3358a7c91847Schristos static int
get_port_number(const char * envname,const char * portname,int defaultport)3359a7c91847Schristos get_port_number (const char *envname, const char *portname, int defaultport)
3360a7c91847Schristos {
3361a7c91847Schristos struct servent *s;
3362a7c91847Schristos char *port_s;
3363a7c91847Schristos
3364a7c91847Schristos if (envname && (port_s = getenv (envname)))
3365a7c91847Schristos {
3366a7c91847Schristos int port = atoi (port_s);
3367a7c91847Schristos if (port <= 0)
3368a7c91847Schristos {
3369a7c91847Schristos error (0, 0, "%s must be a positive integer! If you", envname);
3370a7c91847Schristos error (0, 0, "are trying to force a connection via rsh, please");
3371a7c91847Schristos error (0, 0, "put \":server:\" at the beginning of your CVSROOT");
3372a7c91847Schristos error (1, 0, "variable.");
3373a7c91847Schristos }
3374a7c91847Schristos return port;
3375a7c91847Schristos }
3376a7c91847Schristos else if (portname && (s = getservbyname (portname, "tcp")))
3377a7c91847Schristos return ntohs (s->s_port);
3378a7c91847Schristos else
3379a7c91847Schristos return defaultport;
3380a7c91847Schristos }
3381a7c91847Schristos
3382a7c91847Schristos
3383a7c91847Schristos
3384a7c91847Schristos /* get the port number for a client to connect to based on the port
3385a7c91847Schristos * and method of a cvsroot_t.
3386a7c91847Schristos *
3387a7c91847Schristos * we do this here instead of in parse_cvsroot so that we can keep network
3388a7c91847Schristos * code confined to a localized area and also to delay the lookup until the
3389a7c91847Schristos * last possible moment so it remains possible to run cvs client commands that
3390a7c91847Schristos * skip opening connections to the server (i.e. skip network operations
3391a7c91847Schristos * entirely)
3392a7c91847Schristos *
3393a7c91847Schristos * and yes, I know none of the commands do that now, but here's to planning
3394a7c91847Schristos * for the future, eh? cheers.
3395a7c91847Schristos */
3396a7c91847Schristos int
get_cvs_port_number(const cvsroot_t * root)3397a7c91847Schristos get_cvs_port_number (const cvsroot_t *root)
3398a7c91847Schristos {
3399a7c91847Schristos
3400a7c91847Schristos if (root->port) return root->port;
3401a7c91847Schristos
3402a7c91847Schristos switch (root->method)
3403a7c91847Schristos {
3404a7c91847Schristos # ifdef HAVE_GSSAPI
3405a7c91847Schristos case gserver_method:
3406a7c91847Schristos # endif /* HAVE_GSSAPI */
3407a7c91847Schristos # ifdef AUTH_CLIENT_SUPPORT
3408a7c91847Schristos case pserver_method:
3409a7c91847Schristos # endif /* AUTH_CLIENT_SUPPORT */
3410a7c91847Schristos # if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI)
3411a7c91847Schristos return get_port_number ("CVS_CLIENT_PORT", "cvspserver",
3412a7c91847Schristos CVS_AUTH_PORT);
3413a7c91847Schristos # endif /* defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) */
3414a7c91847Schristos # ifdef HAVE_KERBEROS
3415a7c91847Schristos case kserver_method:
3416a7c91847Schristos return get_port_number ("CVS_CLIENT_PORT", "cvs", CVS_PORT);
3417a7c91847Schristos # endif /* HAVE_KERBEROS */
3418a7c91847Schristos default:
3419a7c91847Schristos error(1, EINVAL,
3420a7c91847Schristos "internal error: get_cvs_port_number called for invalid connection method (%s)",
3421a7c91847Schristos method_names[root->method]);
3422a7c91847Schristos break;
3423a7c91847Schristos }
3424a7c91847Schristos /* NOTREACHED */
3425a7c91847Schristos return -1;
3426a7c91847Schristos }
3427a7c91847Schristos
3428a7c91847Schristos
3429a7c91847Schristos
3430a7c91847Schristos /* get the port number for a client to connect to based on the proxy port
3431a7c91847Schristos * of a cvsroot_t.
3432a7c91847Schristos */
3433a7c91847Schristos static int
get_proxy_port_number(const cvsroot_t * root)3434a7c91847Schristos get_proxy_port_number (const cvsroot_t *root)
3435a7c91847Schristos {
3436a7c91847Schristos
3437a7c91847Schristos if (root->proxy_port) return root->proxy_port;
3438a7c91847Schristos
3439a7c91847Schristos return get_port_number ("CVS_PROXY_PORT", NULL, CVS_PROXY_PORT);
3440a7c91847Schristos }
3441a7c91847Schristos
3442a7c91847Schristos
3443a7c91847Schristos
3444a7c91847Schristos void
make_bufs_from_fds(int tofd,int fromfd,int child_pid,cvsroot_t * root,struct buffer ** to_server_p,struct buffer ** from_server_p,int is_sock)3445a7c91847Schristos make_bufs_from_fds(int tofd, int fromfd, int child_pid, cvsroot_t *root,
3446a7c91847Schristos struct buffer **to_server_p,
3447a7c91847Schristos struct buffer **from_server_p, int is_sock)
3448a7c91847Schristos {
3449a7c91847Schristos # ifdef NO_SOCKET_TO_FD
3450a7c91847Schristos if (is_sock)
3451a7c91847Schristos {
3452a7c91847Schristos assert (tofd == fromfd);
3453a7c91847Schristos *to_server_p = socket_buffer_initialize (tofd, 0, NULL);
3454a7c91847Schristos *from_server_p = socket_buffer_initialize (tofd, 1, NULL);
3455a7c91847Schristos }
3456a7c91847Schristos else
3457a7c91847Schristos # endif /* NO_SOCKET_TO_FD */
3458a7c91847Schristos {
3459a7c91847Schristos /* todo: some OS's don't need these calls... */
3460a7c91847Schristos close_on_exec (tofd);
3461a7c91847Schristos close_on_exec (fromfd);
3462a7c91847Schristos
3463a7c91847Schristos /* SCO 3 and AIX have a nasty bug in the I/O libraries which precludes
3464a7c91847Schristos fdopening the same file descriptor twice, so dup it if it is the
3465a7c91847Schristos same. */
3466a7c91847Schristos if (tofd == fromfd)
3467a7c91847Schristos {
3468a7c91847Schristos fromfd = dup (tofd);
3469a7c91847Schristos if (fromfd < 0)
3470a7c91847Schristos error (1, errno, "cannot dup net connection");
3471a7c91847Schristos }
3472a7c91847Schristos
3473a7c91847Schristos /* These will use binary mode on systems which have it. */
3474a7c91847Schristos /*
3475a7c91847Schristos * Also, we know that from_server is shut down second, so we pass
3476a7c91847Schristos * child_pid in there. In theory, it should be stored in both
3477a7c91847Schristos * buffers with a ref count...
3478a7c91847Schristos */
3479a7c91847Schristos *to_server_p = fd_buffer_initialize (tofd, 0, root, false, NULL);
3480a7c91847Schristos *from_server_p = fd_buffer_initialize (fromfd, child_pid, root,
3481a7c91847Schristos true, NULL);
3482a7c91847Schristos }
3483a7c91847Schristos }
3484a7c91847Schristos #endif /* defined (AUTH_CLIENT_SUPPORT) || defined (SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined(HAVE_GSSAPI) */
3485a7c91847Schristos
3486a7c91847Schristos
3487a7c91847Schristos
3488a7c91847Schristos #if defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI)
3489a7c91847Schristos /* Connect to the authenticating server.
3490a7c91847Schristos
3491a7c91847Schristos If VERIFY_ONLY is non-zero, then just verify that the password is
3492a7c91847Schristos correct and then shutdown the connection.
3493a7c91847Schristos
3494a7c91847Schristos If VERIFY_ONLY is 0, then really connect to the server.
3495a7c91847Schristos
3496a7c91847Schristos If DO_GSSAPI is non-zero, then we use GSSAPI authentication rather
3497a7c91847Schristos than the pserver password authentication.
3498a7c91847Schristos
3499a7c91847Schristos If we fail to connect or if access is denied, then die with fatal
3500a7c91847Schristos error. */
3501a7c91847Schristos void
connect_to_pserver(cvsroot_t * root,struct buffer ** to_server_p,struct buffer ** from_server_p,int verify_only,int do_gssapi)3502a7c91847Schristos connect_to_pserver (cvsroot_t *root, struct buffer **to_server_p,
3503a7c91847Schristos struct buffer **from_server_p, int verify_only,
3504a7c91847Schristos int do_gssapi)
3505a7c91847Schristos {
3506a7c91847Schristos int sock;
3507a7c91847Schristos int port_number,
3508a7c91847Schristos proxy_port_number = 0; /* Initialize to silence -Wall. Dumb. */
3509274254cdSchristos char no_passwd = 0; /* gets set if no password found */
3510a7c91847Schristos struct buffer *to_server, *from_server;
3511a7c91847Schristos
3512a7c91847Schristos port_number = get_cvs_port_number (root);
3513a7c91847Schristos
3514a7c91847Schristos /* if we have a proxy connect to that instead */
3515a7c91847Schristos if (root->proxy_hostname)
3516a7c91847Schristos {
3517274254cdSchristos TRACE (TRACE_FUNCTION, "Connecting to %s:%d via proxy %s:%d.",
3518a7c91847Schristos root->hostname, port_number, root->proxy_hostname,
3519274254cdSchristos proxy_port_number);
3520274254cdSchristos proxy_port_number = get_proxy_port_number (root);
3521274254cdSchristos sock = connect_to(root->proxy_hostname, proxy_port_number);
3522a7c91847Schristos }
3523a7c91847Schristos else
3524a7c91847Schristos {
3525274254cdSchristos TRACE (TRACE_FUNCTION, "Connecting to %s:%d.",
3526274254cdSchristos root->hostname, port_number);
3527274254cdSchristos sock = connect_to(root->hostname, port_number);
3528a7c91847Schristos }
3529a7c91847Schristos
3530274254cdSchristos if (sock == -1)
3531274254cdSchristos error (1, 0, "connect to %s:%d failed: %s",
3532a7c91847Schristos root->proxy_hostname ? root->proxy_hostname : root->hostname,
3533a7c91847Schristos root->proxy_hostname ? proxy_port_number : port_number,
3534a7c91847Schristos SOCK_STRERROR (SOCK_ERRNO));
3535a7c91847Schristos
3536a7c91847Schristos make_bufs_from_fds (sock, sock, 0, root, &to_server, &from_server, 1);
3537a7c91847Schristos
3538a7c91847Schristos /* if we have proxy then connect to the proxy first */
3539a7c91847Schristos if (root->proxy_hostname)
3540a7c91847Schristos {
3541a7c91847Schristos #define CONNECT_STRING "CONNECT %s:%d HTTP/1.0\r\n\r\n"
3542a7c91847Schristos /* Send a "CONNECT" command to proxy: */
3543a7c91847Schristos char* read_buf;
3544a7c91847Schristos int codenum;
3545a7c91847Schristos size_t count;
3546a7c91847Schristos /* 4 characters for port covered by the length of %s & %d */
3547a7c91847Schristos char* write_buf = Xasnprintf (NULL, &count, CONNECT_STRING,
3548a7c91847Schristos root->hostname, port_number);
3549a7c91847Schristos send_to_server_via (to_server, write_buf, count);
3550a7c91847Schristos
3551a7c91847Schristos /* Wait for HTTP status code, bail out if you don't get back a 2xx
3552a7c91847Schristos * code.
3553a7c91847Schristos */
3554a7c91847Schristos read_line_via (from_server, to_server, &read_buf);
35551def5535Sdrochner count = sscanf (read_buf, "%*s %d", &codenum);
3556a7c91847Schristos
35571def5535Sdrochner if (count != 1 || (codenum / 100) != 2)
3558a7c91847Schristos error (1, 0, "proxy server %s:%d does not support http tunnelling",
3559a7c91847Schristos root->proxy_hostname, proxy_port_number);
3560a7c91847Schristos free (read_buf);
3561a7c91847Schristos free (write_buf);
3562a7c91847Schristos
3563a7c91847Schristos /* Skip through remaining part of MIME header, recv_line
3564a7c91847Schristos consumes the trailing \n */
3565a7c91847Schristos while (read_line_via (from_server, to_server, &read_buf) > 0)
3566a7c91847Schristos {
3567a7c91847Schristos if (read_buf[0] == '\r' || read_buf[0] == 0)
3568a7c91847Schristos {
3569a7c91847Schristos free (read_buf);
3570a7c91847Schristos break;
3571a7c91847Schristos }
3572a7c91847Schristos free (read_buf);
3573a7c91847Schristos }
3574a7c91847Schristos }
3575a7c91847Schristos
3576274254cdSchristos auth_server (root, to_server, from_server, verify_only, do_gssapi);
3577a7c91847Schristos
3578a7c91847Schristos if (verify_only)
3579a7c91847Schristos {
3580a7c91847Schristos int status;
3581a7c91847Schristos
3582a7c91847Schristos status = buf_shutdown (to_server);
3583a7c91847Schristos if (status != 0)
3584a7c91847Schristos error (0, status, "shutting down buffer to server");
3585a7c91847Schristos buf_free (to_server);
3586a7c91847Schristos to_server = NULL;
3587a7c91847Schristos
3588a7c91847Schristos status = buf_shutdown (from_server);
3589a7c91847Schristos if (status != 0)
3590a7c91847Schristos error (0, status, "shutting down buffer from server");
3591a7c91847Schristos buf_free (from_server);
3592a7c91847Schristos from_server = NULL;
3593a7c91847Schristos
3594a7c91847Schristos /* Don't need to set server_started = 0 since we don't set it to 1
3595a7c91847Schristos * until returning from this call.
3596a7c91847Schristos */
3597a7c91847Schristos }
3598a7c91847Schristos else
3599a7c91847Schristos {
3600a7c91847Schristos *to_server_p = to_server;
3601a7c91847Schristos *from_server_p = from_server;
3602a7c91847Schristos }
3603a7c91847Schristos
3604a7c91847Schristos return;
3605a7c91847Schristos }
3606a7c91847Schristos
3607a7c91847Schristos
3608a7c91847Schristos
3609a7c91847Schristos static void
auth_server(cvsroot_t * root,struct buffer * to_server,struct buffer * from_server,int verify_only,int do_gssapi)3610a7c91847Schristos auth_server (cvsroot_t *root, struct buffer *to_server,
3611274254cdSchristos struct buffer *from_server, int verify_only, int do_gssapi)
3612a7c91847Schristos {
3613a7c91847Schristos char *username = NULL; /* the username we use to connect */
3614a7c91847Schristos char no_passwd = 0; /* gets set if no password found */
3615a7c91847Schristos
3616a7c91847Schristos /* Run the authorization mini-protocol before anything else. */
3617a7c91847Schristos if (do_gssapi)
3618a7c91847Schristos {
3619a7c91847Schristos # ifdef HAVE_GSSAPI
3620a7c91847Schristos int fd = buf_get_fd (to_server);
3621a7c91847Schristos struct stat s;
3622a7c91847Schristos
3623a7c91847Schristos if ((fd < 0) || (fstat (fd, &s) < 0) || !S_ISSOCK(s.st_mode))
3624a7c91847Schristos {
3625a7c91847Schristos error (1, 0,
3626a7c91847Schristos "gserver currently only enabled for socket connections");
3627a7c91847Schristos }
3628a7c91847Schristos
3629274254cdSchristos if (! connect_to_gserver (root, fd, root->hostname))
3630a7c91847Schristos {
3631a7c91847Schristos error (1, 0,
3632a7c91847Schristos "authorization failed: server %s rejected access to %s",
3633a7c91847Schristos root->hostname, root->directory);
3634a7c91847Schristos }
3635a7c91847Schristos # else /* ! HAVE_GSSAPI */
3636a7c91847Schristos error (1, 0,
3637a7c91847Schristos "INTERNAL ERROR: This client does not support GSSAPI authentication");
3638a7c91847Schristos # endif /* HAVE_GSSAPI */
3639a7c91847Schristos }
3640a7c91847Schristos else /* ! do_gssapi */
3641a7c91847Schristos {
3642a7c91847Schristos # ifdef AUTH_CLIENT_SUPPORT
3643a7c91847Schristos char *begin = NULL;
3644a7c91847Schristos char *password = NULL;
3645a7c91847Schristos char *end = NULL;
3646a7c91847Schristos
3647a7c91847Schristos if (verify_only)
3648a7c91847Schristos {
3649a7c91847Schristos begin = "BEGIN VERIFICATION REQUEST";
3650a7c91847Schristos end = "END VERIFICATION REQUEST";
3651a7c91847Schristos }
3652a7c91847Schristos else
3653a7c91847Schristos {
3654a7c91847Schristos begin = "BEGIN AUTH REQUEST";
3655a7c91847Schristos end = "END AUTH REQUEST";
3656a7c91847Schristos }
3657a7c91847Schristos
3658a7c91847Schristos /* Get the password, probably from ~/.cvspass. */
3659a7c91847Schristos password = get_cvs_password ();
3660a7c91847Schristos username = root->username ? root->username : getcaller();
3661a7c91847Schristos
3662a7c91847Schristos /* Send the empty string by default. This is so anonymous CVS
3663a7c91847Schristos access doesn't require client to have done "cvs login". */
3664a7c91847Schristos if (!password)
3665a7c91847Schristos {
3666a7c91847Schristos no_passwd = 1;
3667a7c91847Schristos password = scramble ("");
3668a7c91847Schristos }
3669a7c91847Schristos
3670a7c91847Schristos /* Announce that we're starting the authorization protocol. */
3671a7c91847Schristos send_to_server_via(to_server, begin, 0);
3672a7c91847Schristos send_to_server_via(to_server, "\012", 1);
3673a7c91847Schristos
3674a7c91847Schristos /* Send the data the server needs. */
3675a7c91847Schristos send_to_server_via(to_server, root->directory, 0);
3676a7c91847Schristos send_to_server_via(to_server, "\012", 1);
3677a7c91847Schristos send_to_server_via(to_server, username, 0);
3678a7c91847Schristos send_to_server_via(to_server, "\012", 1);
3679a7c91847Schristos send_to_server_via(to_server, password, 0);
3680a7c91847Schristos send_to_server_via(to_server, "\012", 1);
3681a7c91847Schristos
3682a7c91847Schristos /* Announce that we're ending the authorization protocol. */
3683a7c91847Schristos send_to_server_via(to_server, end, 0);
3684a7c91847Schristos send_to_server_via(to_server, "\012", 1);
3685a7c91847Schristos
3686a7c91847Schristos /* Paranoia. */
3687274254cdSchristos free_cvs_password (password);
3688274254cdSchristos password = NULL;
3689a7c91847Schristos # else /* ! AUTH_CLIENT_SUPPORT */
3690a7c91847Schristos error (1, 0, "INTERNAL ERROR: This client does not support pserver authentication");
3691a7c91847Schristos # endif /* AUTH_CLIENT_SUPPORT */
3692a7c91847Schristos } /* if (do_gssapi) */
3693a7c91847Schristos
3694a7c91847Schristos {
3695a7c91847Schristos char *read_buf;
3696a7c91847Schristos
3697a7c91847Schristos /* Loop, getting responses from the server. */
3698a7c91847Schristos while (1)
3699a7c91847Schristos {
3700a7c91847Schristos read_line_via (from_server, to_server, &read_buf);
3701a7c91847Schristos
3702a7c91847Schristos if (!strcmp (read_buf, "I HATE YOU"))
3703a7c91847Schristos {
3704a7c91847Schristos /* Authorization not granted.
3705a7c91847Schristos *
3706a7c91847Schristos * This is a little confusing since we can reach this while
3707a7c91847Schristos * loop in GSSAPI mode, but if GSSAPI authentication failed,
3708a7c91847Schristos * we already jumped to the rejected label (there is no case
3709a7c91847Schristos * where the connect_to_gserver function can return 1 and we
3710a7c91847Schristos * will not receive "I LOVE YOU" from the server, barring
3711a7c91847Schristos * broken connections and garbled messages, of course). The
3712a7c91847Schristos * GSSAPI case is also the case where username can be NULL
3713a7c91847Schristos * since username is initialized in the !gssapi section.
3714a7c91847Schristos *
3715a7c91847Schristos * i.e. This is a pserver specific error message and should be
3716a7c91847Schristos * since GSSAPI doesn't use username.
3717a7c91847Schristos */
3718a7c91847Schristos error (0, 0,
3719a7c91847Schristos "authorization failed: server %s rejected access to %s for user %s",
3720a7c91847Schristos root->hostname, root->directory,
3721a7c91847Schristos username ? username : "(null)");
3722a7c91847Schristos
3723a7c91847Schristos /* Output a special error message if authentication was attempted
3724a7c91847Schristos with no password -- the user should be made aware that they may
3725a7c91847Schristos have missed a step. */
3726a7c91847Schristos if (no_passwd)
3727a7c91847Schristos {
3728a7c91847Schristos error (0, 0,
3729a7c91847Schristos "used empty password; try \"cvs login\" with a real password");
3730a7c91847Schristos }
3731a7c91847Schristos exit (EXIT_FAILURE);
3732a7c91847Schristos }
3733a7c91847Schristos else if (!strncmp (read_buf, "E ", 2))
3734a7c91847Schristos {
3735a7c91847Schristos fprintf (stderr, "%s\n", read_buf + 2);
3736a7c91847Schristos
3737a7c91847Schristos /* Continue with the authentication protocol. */
3738a7c91847Schristos }
3739a7c91847Schristos else if (!strncmp (read_buf, "error ", 6))
3740a7c91847Schristos {
3741a7c91847Schristos char *p;
3742a7c91847Schristos
3743a7c91847Schristos /* First skip the code. */
3744a7c91847Schristos p = read_buf + 6;
3745a7c91847Schristos while (*p != ' ' && *p != '\0')
3746a7c91847Schristos ++p;
3747a7c91847Schristos
3748a7c91847Schristos /* Skip the space that follows the code. */
3749a7c91847Schristos if (*p == ' ')
3750a7c91847Schristos ++p;
3751a7c91847Schristos
3752a7c91847Schristos /* Now output the text. */
3753a7c91847Schristos fprintf (stderr, "%s\n", p);
3754a7c91847Schristos exit (EXIT_FAILURE);
3755a7c91847Schristos }
3756a7c91847Schristos else if (!strcmp (read_buf, "I LOVE YOU"))
3757a7c91847Schristos {
3758a7c91847Schristos free (read_buf);
3759a7c91847Schristos break;
3760a7c91847Schristos }
3761a7c91847Schristos else
3762a7c91847Schristos {
3763a7c91847Schristos error (1, 0,
3764a7c91847Schristos "unrecognized auth response from %s: %s",
3765a7c91847Schristos root->hostname, read_buf);
3766a7c91847Schristos }
3767a7c91847Schristos free (read_buf);
3768a7c91847Schristos }
3769a7c91847Schristos }
3770a7c91847Schristos }
3771a7c91847Schristos #endif /* defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI) */
3772a7c91847Schristos
3773a7c91847Schristos
3774a7c91847Schristos
3775a7c91847Schristos #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
3776a7c91847Schristos /*
3777a7c91847Schristos * Connect to a forked server process.
3778a7c91847Schristos */
3779a7c91847Schristos static void
connect_to_forked_server(cvsroot_t * root,struct buffer ** to_server_p,struct buffer ** from_server_p)3780a7c91847Schristos connect_to_forked_server (cvsroot_t *root, struct buffer **to_server_p,
3781a7c91847Schristos struct buffer **from_server_p)
3782a7c91847Schristos {
3783a7c91847Schristos int tofd, fromfd;
3784a7c91847Schristos int child_pid;
3785a7c91847Schristos
3786a7c91847Schristos /* This is pretty simple. All we need to do is choose the correct
3787a7c91847Schristos cvs binary and call piped_child. */
3788a7c91847Schristos
3789a7c91847Schristos char *command[3];
3790a7c91847Schristos
3791a7c91847Schristos command[0] = (root->cvs_server
3792a7c91847Schristos ? root->cvs_server : getenv ("CVS_SERVER"));
3793a7c91847Schristos if (!command[0])
3794a7c91847Schristos # ifdef SERVER_SUPPORT
3795a7c91847Schristos /* FIXME:
3796a7c91847Schristos * I'm casting out the const below because I know that piped_child, the
3797a7c91847Schristos * only function we pass COMMAND to, accepts COMMAND as a
3798a7c91847Schristos * (char *const *) and won't alter it, and we don't alter it in this
3799a7c91847Schristos * function. This is yucky, there should be a way to declare COMMAND
3800a7c91847Schristos * such that this casting isn't needed, but I don't know how. If I
3801a7c91847Schristos * declare it as (const char *command[]), the compiler complains about
3802a7c91847Schristos * an incompatible arg 1 being passed to piped_child and if I declare
3803a7c91847Schristos * it as (char *const command[3]), then the compiler complains when I
3804a7c91847Schristos * assign values to command[i].
3805a7c91847Schristos */
3806a7c91847Schristos command[0] = (char *)program_path;
3807a7c91847Schristos # else /* SERVER_SUPPORT */
3808a7c91847Schristos {
3809a7c91847Schristos error( 0, 0, "You must set the CVS_SERVER environment variable when" );
3810a7c91847Schristos error( 0, 0, "using the :fork: access method." );
3811a7c91847Schristos error( 1, 0, "This CVS was not compiled with server support." );
3812a7c91847Schristos }
3813a7c91847Schristos # endif /* SERVER_SUPPORT */
3814a7c91847Schristos
3815a7c91847Schristos command[1] = "server";
3816a7c91847Schristos command[2] = NULL;
3817a7c91847Schristos
3818a7c91847Schristos TRACE (TRACE_FUNCTION, "Forking server: %s %s",
3819a7c91847Schristos command[0] ? command[0] : "(null)", command[1]);
3820a7c91847Schristos
3821a7c91847Schristos child_pid = piped_child (command, &tofd, &fromfd, false);
3822a7c91847Schristos if (child_pid < 0)
3823a7c91847Schristos error (1, 0, "could not fork server process");
3824a7c91847Schristos
3825a7c91847Schristos make_bufs_from_fds (tofd, fromfd, child_pid, root, to_server_p,
3826a7c91847Schristos from_server_p, 0);
3827a7c91847Schristos }
3828a7c91847Schristos #endif /* CLIENT_SUPPORT || SERVER_SUPPORT */
3829a7c91847Schristos
3830a7c91847Schristos
3831a7c91847Schristos
3832a7c91847Schristos static int
send_variable_proc(Node * node,void * closure)3833a7c91847Schristos send_variable_proc (Node *node, void *closure)
3834a7c91847Schristos {
3835a7c91847Schristos send_to_server ("Set ", 0);
3836a7c91847Schristos send_to_server (node->key, 0);
3837a7c91847Schristos send_to_server ("=", 1);
3838a7c91847Schristos send_to_server (node->data, 0);
3839a7c91847Schristos send_to_server ("\012", 1);
3840a7c91847Schristos return 0;
3841a7c91847Schristos }
3842a7c91847Schristos
3843a7c91847Schristos
3844a7c91847Schristos
3845a7c91847Schristos /* Open up the connection to the server and perform any necessary
3846a7c91847Schristos * authentication.
3847a7c91847Schristos */
3848a7c91847Schristos void
open_connection_to_server(cvsroot_t * root,struct buffer ** to_server_p,struct buffer ** from_server_p)3849a7c91847Schristos open_connection_to_server (cvsroot_t *root, struct buffer **to_server_p,
3850a7c91847Schristos struct buffer **from_server_p)
3851a7c91847Schristos {
3852a7c91847Schristos /* Note that generally speaking we do *not* fall back to a different
3853a7c91847Schristos way of connecting if the first one does not work. This is slow
3854a7c91847Schristos (*really* slow on a 14.4kbps link); the clean way to have a CVS
3855a7c91847Schristos which supports several ways of connecting is with access methods. */
3856a7c91847Schristos
3857a7c91847Schristos TRACE (TRACE_FUNCTION, "open_connection_to_server (%s)", root->original);
3858a7c91847Schristos
3859a7c91847Schristos switch (root->method)
3860a7c91847Schristos {
3861a7c91847Schristos case pserver_method:
3862a7c91847Schristos #ifdef AUTH_CLIENT_SUPPORT
3863a7c91847Schristos /* Toss the return value. It will die with an error message if
3864a7c91847Schristos * anything goes wrong anyway.
3865a7c91847Schristos */
3866a7c91847Schristos connect_to_pserver (root, to_server_p, from_server_p, 0, 0);
3867a7c91847Schristos #else /* AUTH_CLIENT_SUPPORT */
3868a7c91847Schristos error (0, 0, "CVSROOT is set for a pserver access method but your");
3869a7c91847Schristos error (1, 0, "CVS executable doesn't support it.");
3870a7c91847Schristos #endif /* AUTH_CLIENT_SUPPORT */
3871a7c91847Schristos break;
3872a7c91847Schristos
3873a7c91847Schristos case kserver_method:
3874a7c91847Schristos #if HAVE_KERBEROS
3875a7c91847Schristos start_kerberos4_server (root, to_server_p,
3876a7c91847Schristos from_server_p);
3877a7c91847Schristos #else /* !HAVE_KERBEROS */
3878a7c91847Schristos error (0, 0,
3879a7c91847Schristos "CVSROOT is set for a kerberos access method but your");
3880a7c91847Schristos error (1, 0, "CVS executable doesn't support it.");
3881a7c91847Schristos #endif /* HAVE_KERBEROS */
3882a7c91847Schristos break;
3883a7c91847Schristos
3884a7c91847Schristos case gserver_method:
3885a7c91847Schristos #ifdef HAVE_GSSAPI
3886a7c91847Schristos /* GSSAPI authentication is handled by the pserver. */
3887a7c91847Schristos connect_to_pserver (root, to_server_p, from_server_p, 0, 1);
3888a7c91847Schristos #else /* !HAVE_GSSAPI */
3889a7c91847Schristos error (0, 0, "CVSROOT is set for a GSSAPI access method but your");
3890a7c91847Schristos error (1, 0, "CVS executable doesn't support it.");
3891a7c91847Schristos #endif /* HAVE_GSSAPI */
3892a7c91847Schristos break;
3893a7c91847Schristos
3894a7c91847Schristos case ext_method:
3895a7c91847Schristos #ifdef NO_EXT_METHOD
3896a7c91847Schristos error (0, 0, ":ext: method not supported by this port of CVS");
3897a7c91847Schristos error (1, 0, "try :server: instead");
3898a7c91847Schristos #else /* ! NO_EXT_METHOD */
3899a7c91847Schristos start_rsh_server (root, to_server_p,
3900a7c91847Schristos from_server_p);
3901a7c91847Schristos #endif /* NO_EXT_METHOD */
3902a7c91847Schristos break;
3903a7c91847Schristos
3904a7c91847Schristos case server_method:
3905a7c91847Schristos #ifdef START_SERVER
3906a7c91847Schristos {
3907a7c91847Schristos int tofd, fromfd;
3908a7c91847Schristos START_SERVER (&tofd, &fromfd, getcaller (),
3909a7c91847Schristos root->username,
3910a7c91847Schristos root->hostname,
3911a7c91847Schristos root->directory);
3912a7c91847Schristos # ifdef START_SERVER_RETURNS_SOCKET
3913a7c91847Schristos make_bufs_from_fds (tofd, fromfd, 0, root, to_server_p,
3914a7c91847Schristos from_server_p, 1);
3915a7c91847Schristos # else /* ! START_SERVER_RETURNS_SOCKET */
3916a7c91847Schristos make_bufs_from_fds (tofd, fromfd, 0, root, to_server_p,
3917a7c91847Schristos from_server_p, 0);
3918a7c91847Schristos # endif /* START_SERVER_RETURNS_SOCKET */
3919a7c91847Schristos }
3920a7c91847Schristos #else /* ! START_SERVER */
3921a7c91847Schristos /* FIXME: It should be possible to implement this portably,
3922a7c91847Schristos like pserver, which would get rid of the duplicated code
3923a7c91847Schristos in {vms,windows-NT,...}/startserver.c. */
3924a7c91847Schristos error (1, 0,
3925a7c91847Schristos "the :server: access method is not supported by this port of CVS");
3926a7c91847Schristos #endif /* START_SERVER */
3927a7c91847Schristos break;
3928a7c91847Schristos
3929a7c91847Schristos case fork_method:
3930a7c91847Schristos connect_to_forked_server (root, to_server_p, from_server_p);
3931a7c91847Schristos break;
3932a7c91847Schristos
3933a7c91847Schristos default:
3934a7c91847Schristos error (1, 0,
3935a7c91847Schristos "(start_server internal error): unknown access method");
3936a7c91847Schristos break;
3937a7c91847Schristos }
3938a7c91847Schristos
3939a7c91847Schristos /* "Hi, I'm Darlene and I'll be your server tonight..." */
3940a7c91847Schristos server_started = 1;
3941a7c91847Schristos }
3942a7c91847Schristos
3943a7c91847Schristos
3944a7c91847Schristos
3945a7c91847Schristos /* Contact the server. */
3946a7c91847Schristos void
start_server(void)3947a7c91847Schristos start_server (void)
3948a7c91847Schristos {
3949a7c91847Schristos bool rootless;
3950a7c91847Schristos int status;
3951a7c91847Schristos bool have_global;
3952a7c91847Schristos
3953a7c91847Schristos do
3954a7c91847Schristos {
3955a7c91847Schristos /* Clear our static variables for this invocation. */
3956a7c91847Schristos if (toplevel_repos)
3957a7c91847Schristos free (toplevel_repos);
3958a7c91847Schristos toplevel_repos = NULL;
3959a7c91847Schristos
3960a7c91847Schristos open_connection_to_server (current_parsed_root, &global_to_server,
3961a7c91847Schristos &global_from_server);
3962a7c91847Schristos setup_logfiles ("CVS_CLIENT_LOG", &global_to_server,
3963a7c91847Schristos &global_from_server);
3964a7c91847Schristos
3965a7c91847Schristos /* Clear static variables. */
3966a7c91847Schristos if (toplevel_repos)
3967a7c91847Schristos {
3968a7c91847Schristos free (toplevel_repos);
3969a7c91847Schristos toplevel_repos = NULL;
3970a7c91847Schristos }
3971a7c91847Schristos if (last_repos)
3972a7c91847Schristos {
3973a7c91847Schristos free (last_repos);
3974a7c91847Schristos last_repos = NULL;
3975a7c91847Schristos }
3976a7c91847Schristos if (last_update_dir)
3977a7c91847Schristos {
3978a7c91847Schristos free (last_update_dir);
3979a7c91847Schristos last_update_dir = NULL;
3980a7c91847Schristos }
3981a7c91847Schristos stored_checksum_valid = 0;
3982a7c91847Schristos if (stored_mode)
3983a7c91847Schristos {
3984a7c91847Schristos free (stored_mode);
3985a7c91847Schristos stored_mode = NULL;
3986a7c91847Schristos }
3987a7c91847Schristos
3988a7c91847Schristos rootless = !strcmp (cvs_cmd_name, "init");
3989a7c91847Schristos if (!rootless)
3990a7c91847Schristos {
3991a7c91847Schristos send_to_server ("Root ", 0);
3992a7c91847Schristos send_to_server (current_parsed_root->directory, 0);
3993a7c91847Schristos send_to_server ("\012", 1);
3994a7c91847Schristos }
3995a7c91847Schristos
3996a7c91847Schristos {
3997a7c91847Schristos struct response *rs;
3998a7c91847Schristos bool suppress_redirect = !current_parsed_root->redirect;
3999a7c91847Schristos
4000a7c91847Schristos send_to_server ("Valid-responses", 0);
4001a7c91847Schristos
4002a7c91847Schristos for (rs = responses; rs->name; ++rs)
4003a7c91847Schristos {
4004a7c91847Schristos if (suppress_redirect && !strcmp (rs->name, "Redirect"))
4005a7c91847Schristos continue;
4006a7c91847Schristos
4007a7c91847Schristos send_to_server (" ", 0);
4008a7c91847Schristos send_to_server (rs->name, 0);
4009a7c91847Schristos }
4010a7c91847Schristos send_to_server ("\012", 1);
4011a7c91847Schristos }
4012a7c91847Schristos send_to_server ("valid-requests\012", 0);
4013a7c91847Schristos
4014a7c91847Schristos if (get_server_responses ())
4015a7c91847Schristos exit (EXIT_FAILURE);
4016a7c91847Schristos
4017a7c91847Schristos have_global = supported_request ("Global_option");
4018a7c91847Schristos
4019a7c91847Schristos /* Encryption needs to come before compression. Good encryption can
4020a7c91847Schristos * render compression useless in the other direction.
4021a7c91847Schristos */
4022a7c91847Schristos if (cvsencrypt && !rootless)
4023a7c91847Schristos {
4024a7c91847Schristos #ifdef ENCRYPTION
4025a7c91847Schristos /* Turn on encryption before turning on compression. We do
4026a7c91847Schristos * not want to try to compress the encrypted stream. Instead,
4027a7c91847Schristos * we want to encrypt the compressed stream. If we can't turn
4028a7c91847Schristos * on encryption, bomb out; don't let the user think the data
4029a7c91847Schristos * is being encrypted when it is not.
4030a7c91847Schristos */
4031a7c91847Schristos # ifdef HAVE_KERBEROS
4032a7c91847Schristos if (current_parsed_root->method == kserver_method)
4033a7c91847Schristos {
4034a7c91847Schristos if (!supported_request ("Kerberos-encrypt"))
4035a7c91847Schristos error (1, 0, "This server does not support encryption");
4036a7c91847Schristos send_to_server ("Kerberos-encrypt\012", 0);
4037a7c91847Schristos initialize_kerberos4_encryption_buffers (&global_to_server,
4038a7c91847Schristos &global_from_server);
4039a7c91847Schristos }
4040a7c91847Schristos else
4041a7c91847Schristos # endif /* HAVE_KERBEROS */
4042a7c91847Schristos # ifdef HAVE_GSSAPI
4043a7c91847Schristos if (current_parsed_root->method == gserver_method)
4044a7c91847Schristos {
4045a7c91847Schristos if (!supported_request ("Gssapi-encrypt"))
4046a7c91847Schristos error (1, 0, "This server does not support encryption");
4047a7c91847Schristos send_to_server ("Gssapi-encrypt\012", 0);
4048a7c91847Schristos initialize_gssapi_buffers (&global_to_server,
4049a7c91847Schristos &global_from_server);
4050a7c91847Schristos cvs_gssapi_encrypt = 1;
4051a7c91847Schristos }
4052a7c91847Schristos else
4053a7c91847Schristos # endif /* HAVE_GSSAPI */
4054a7c91847Schristos error (1, 0,
4055a7c91847Schristos "Encryption is only supported when using GSSAPI or Kerberos");
4056a7c91847Schristos #else /* ! ENCRYPTION */
4057a7c91847Schristos error (1, 0, "This client does not support encryption");
4058a7c91847Schristos #endif /* ! ENCRYPTION */
4059a7c91847Schristos }
4060a7c91847Schristos
4061274254cdSchristos if (nolock && !noexec)
4062274254cdSchristos {
4063274254cdSchristos if (have_global)
4064274254cdSchristos {
4065274254cdSchristos send_to_server ("Global_option -u\012", 0);
4066274254cdSchristos }
4067274254cdSchristos else
4068274254cdSchristos error (1, 0,
4069274254cdSchristos "This server does not support the global -u option.");
4070274254cdSchristos }
4071a7c91847Schristos /* Send this before compression to enable supression of the
4072a7c91847Schristos * "Forcing compression level Z" messages.
4073a7c91847Schristos */
4074a7c91847Schristos if (quiet)
4075a7c91847Schristos {
4076a7c91847Schristos if (have_global)
4077a7c91847Schristos {
4078a7c91847Schristos send_to_server ("Global_option -q\012", 0);
4079a7c91847Schristos }
4080a7c91847Schristos else
4081a7c91847Schristos error (1, 0,
4082a7c91847Schristos "This server does not support the global -q option.");
4083a7c91847Schristos }
4084a7c91847Schristos if (really_quiet)
4085a7c91847Schristos {
4086a7c91847Schristos if (have_global)
4087a7c91847Schristos {
4088a7c91847Schristos send_to_server ("Global_option -Q\012", 0);
4089a7c91847Schristos }
4090a7c91847Schristos else
4091a7c91847Schristos error (1, 0,
4092a7c91847Schristos "This server does not support the global -Q option.");
4093a7c91847Schristos }
4094a7c91847Schristos
4095a7c91847Schristos /* Compression needs to come before any of the rooted requests to
4096a7c91847Schristos * work with compression limits.
4097a7c91847Schristos */
4098a7c91847Schristos if (!rootless && (gzip_level || force_gzip))
4099a7c91847Schristos {
4100a7c91847Schristos if (supported_request ("Gzip-stream"))
4101a7c91847Schristos {
4102a7c91847Schristos char *gzip_level_buf = Xasprintf ("%d", gzip_level);
4103a7c91847Schristos send_to_server ("Gzip-stream ", 0);
4104a7c91847Schristos send_to_server (gzip_level_buf, 0);
4105a7c91847Schristos free (gzip_level_buf);
4106a7c91847Schristos send_to_server ("\012", 1);
4107a7c91847Schristos
4108a7c91847Schristos /* All further communication with the server will be
4109a7c91847Schristos compressed. */
4110a7c91847Schristos
4111a7c91847Schristos global_to_server =
4112a7c91847Schristos compress_buffer_initialize (global_to_server, 0,
4113a7c91847Schristos gzip_level, NULL);
4114a7c91847Schristos global_from_server =
4115a7c91847Schristos compress_buffer_initialize (global_from_server, 1,
4116a7c91847Schristos gzip_level, NULL);
4117a7c91847Schristos }
4118a7c91847Schristos #ifndef NO_CLIENT_GZIP_PROCESS
4119a7c91847Schristos else if (supported_request ("gzip-file-contents"))
4120a7c91847Schristos {
4121a7c91847Schristos char *gzip_level_buf = Xasprintf ("%d", gzip_level);
4122a7c91847Schristos send_to_server ("gzip-file-contents ", 0);
4123a7c91847Schristos send_to_server (gzip_level_buf, 0);
4124a7c91847Schristos free (gzip_level_buf);
4125a7c91847Schristos send_to_server ("\012", 1);
4126a7c91847Schristos
4127a7c91847Schristos file_gzip_level = gzip_level;
4128a7c91847Schristos }
4129a7c91847Schristos #endif
4130a7c91847Schristos else
4131a7c91847Schristos {
4132a7c91847Schristos fprintf (stderr, "server doesn't support gzip-file-contents\n");
4133a7c91847Schristos /* Setting gzip_level to 0 prevents us from giving the
4134a7c91847Schristos error twice if update has to contact the server again
4135a7c91847Schristos to fetch unpatchable files. */
4136a7c91847Schristos gzip_level = 0;
4137a7c91847Schristos }
4138a7c91847Schristos }
4139a7c91847Schristos
4140a7c91847Schristos if (client_referrer && supported_request ("Referrer"))
4141a7c91847Schristos {
4142a7c91847Schristos send_to_server ("Referrer ", 0);
4143a7c91847Schristos send_to_server (client_referrer->original, 0);
4144a7c91847Schristos send_to_server ("\012", 0);
4145a7c91847Schristos }
4146a7c91847Schristos
4147a7c91847Schristos /* FIXME: I think we should still be sending this for init. */
4148a7c91847Schristos if (!rootless && supported_request ("Command-prep"))
4149a7c91847Schristos {
4150a7c91847Schristos send_to_server ("Command-prep ", 0);
4151a7c91847Schristos send_to_server (cvs_cmd_name, 0);
4152a7c91847Schristos send_to_server ("\012", 0);
4153a7c91847Schristos status = get_server_responses ();
4154a7c91847Schristos if (status == 1) exit (EXIT_FAILURE);
4155a7c91847Schristos if (status == 2) close_connection_to_server (&global_to_server,
4156a7c91847Schristos &global_from_server);
4157a7c91847Schristos }
4158a7c91847Schristos else status = 0;
4159a7c91847Schristos } while (status == 2);
4160a7c91847Schristos
4161a7c91847Schristos
4162a7c91847Schristos /*
4163a7c91847Schristos * Now handle global options.
4164a7c91847Schristos *
4165a7c91847Schristos * -H, -f, -d, -e should be handled OK locally.
4166a7c91847Schristos *
4167a7c91847Schristos * -b we ignore (treating it as a server installation issue).
4168a7c91847Schristos * FIXME: should be an error message.
4169a7c91847Schristos *
4170a7c91847Schristos * -v we print local version info; FIXME: Add a protocol request to get
4171a7c91847Schristos * the version from the server so we can print that too.
4172a7c91847Schristos *
4173a7c91847Schristos * -l -t -r -w -q -n and -Q need to go to the server.
4174a7c91847Schristos */
4175a7c91847Schristos if (noexec)
4176a7c91847Schristos {
4177a7c91847Schristos if (have_global)
4178a7c91847Schristos {
4179a7c91847Schristos send_to_server ("Global_option -n\012", 0);
4180a7c91847Schristos }
4181a7c91847Schristos else
4182a7c91847Schristos error (1, 0,
4183a7c91847Schristos "This server does not support the global -n option.");
4184a7c91847Schristos }
4185a7c91847Schristos if (!cvswrite)
4186a7c91847Schristos {
4187a7c91847Schristos if (have_global)
4188a7c91847Schristos {
4189a7c91847Schristos send_to_server ("Global_option -r\012", 0);
4190a7c91847Schristos }
4191a7c91847Schristos else
4192a7c91847Schristos error (1, 0,
4193a7c91847Schristos "This server does not support the global -r option.");
4194a7c91847Schristos }
4195a7c91847Schristos if (trace)
4196a7c91847Schristos {
4197a7c91847Schristos if (have_global)
4198a7c91847Schristos {
4199a7c91847Schristos int count = trace;
4200a7c91847Schristos while (count--) send_to_server ("Global_option -t\012", 0);
4201a7c91847Schristos }
4202a7c91847Schristos else
4203a7c91847Schristos error (1, 0,
4204a7c91847Schristos "This server does not support the global -t option.");
4205a7c91847Schristos }
4206a7c91847Schristos
4207a7c91847Schristos /* Find out about server-side cvswrappers. An extra network
4208a7c91847Schristos turnaround for cvs import seems to be unavoidable, unless we
4209a7c91847Schristos want to add some kind of client-side place to configure which
4210a7c91847Schristos filenames imply binary. For cvs add, we could avoid the
4211a7c91847Schristos problem by keeping a copy of the wrappers in CVSADM (the main
4212a7c91847Schristos reason to bother would be so we could make add work without
4213a7c91847Schristos contacting the server, I suspect). */
4214a7c91847Schristos
4215a7c91847Schristos if (!strcmp (cvs_cmd_name, "import") || !strcmp (cvs_cmd_name, "add"))
4216a7c91847Schristos {
4217a7c91847Schristos if (supported_request ("wrapper-sendme-rcsOptions"))
4218a7c91847Schristos {
4219a7c91847Schristos int err;
4220a7c91847Schristos send_to_server ("wrapper-sendme-rcsOptions\012", 0);
4221a7c91847Schristos err = get_server_responses ();
4222a7c91847Schristos if (err != 0)
4223a7c91847Schristos error (err, 0, "error reading from server");
4224a7c91847Schristos }
4225a7c91847Schristos }
4226a7c91847Schristos
4227a7c91847Schristos if (cvsauthenticate && ! cvsencrypt && !rootless)
4228a7c91847Schristos {
4229a7c91847Schristos /* Turn on authentication after turning on compression, so
4230a7c91847Schristos that we can compress the authentication information. We
4231a7c91847Schristos assume that encrypted data is always authenticated--the
4232a7c91847Schristos ability to decrypt the data stream is itself a form of
4233a7c91847Schristos authentication. */
4234a7c91847Schristos #ifdef HAVE_GSSAPI
4235a7c91847Schristos if (current_parsed_root->method == gserver_method)
4236a7c91847Schristos {
4237a7c91847Schristos if (! supported_request ("Gssapi-authenticate"))
4238a7c91847Schristos error (1, 0,
4239a7c91847Schristos "This server does not support stream authentication");
4240a7c91847Schristos send_to_server ("Gssapi-authenticate\012", 0);
4241a7c91847Schristos initialize_gssapi_buffers(&global_to_server, &global_from_server);
4242a7c91847Schristos
4243a7c91847Schristos }
4244a7c91847Schristos else
4245a7c91847Schristos error (1, 0, "Stream authentication is only supported when using GSSAPI");
4246a7c91847Schristos #else /* ! HAVE_GSSAPI */
4247a7c91847Schristos error (1, 0, "This client does not support stream authentication");
4248a7c91847Schristos #endif /* ! HAVE_GSSAPI */
4249a7c91847Schristos }
4250a7c91847Schristos
4251a7c91847Schristos /* If "Set" is not supported, just silently fail to send the variables.
4252a7c91847Schristos Users with an old server should get a useful error message when it
4253a7c91847Schristos fails to recognize the ${=foo} syntax. This way if someone uses
4254a7c91847Schristos several servers, some of which are new and some old, they can still
4255a7c91847Schristos set user variables in their .cvsrc without trouble. */
4256a7c91847Schristos if (supported_request ("Set"))
4257a7c91847Schristos walklist (variable_list, send_variable_proc, NULL);
4258a7c91847Schristos }
4259a7c91847Schristos
4260a7c91847Schristos
4261a7c91847Schristos
4262a7c91847Schristos /* Send an argument STRING. */
4263a7c91847Schristos void
send_arg(const char * string)4264a7c91847Schristos send_arg (const char *string)
4265a7c91847Schristos {
4266a7c91847Schristos const char *p = string;
4267a7c91847Schristos
4268a7c91847Schristos send_to_server ("Argument ", 0);
4269a7c91847Schristos
4270a7c91847Schristos while (*p)
4271a7c91847Schristos {
4272a7c91847Schristos if (*p == '\n')
4273a7c91847Schristos send_to_server ("\012Argumentx ", 0);
4274a7c91847Schristos else
4275a7c91847Schristos send_to_server (p, 1);
4276a7c91847Schristos ++p;
4277a7c91847Schristos }
4278a7c91847Schristos send_to_server ("\012", 1);
4279a7c91847Schristos }
4280a7c91847Schristos
4281a7c91847Schristos
4282a7c91847Schristos
4283a7c91847Schristos /* VERS->OPTIONS specifies whether the file is binary or not. NOTE: BEFORE
4284a7c91847Schristos using any other fields of the struct vers, we would need to fix
4285a7c91847Schristos client_process_import_file to set them up. */
4286a7c91847Schristos static void
send_modified(const char * file,const char * short_pathname,Vers_TS * vers)4287a7c91847Schristos send_modified (const char *file, const char *short_pathname, Vers_TS *vers)
4288a7c91847Schristos {
4289a7c91847Schristos /* File was modified, send it. */
4290a7c91847Schristos struct stat sb;
4291a7c91847Schristos int fd;
4292a7c91847Schristos unsigned char *buf;
4293a7c91847Schristos char *mode_string;
4294a7c91847Schristos size_t bufsize;
4295a7c91847Schristos int bin;
4296a7c91847Schristos
4297a7c91847Schristos TRACE (TRACE_FUNCTION, "Sending file `%s' to server", file);
4298a7c91847Schristos
4299a7c91847Schristos /* Don't think we can assume fstat exists. */
4300a7c91847Schristos if (stat (file, &sb) < 0)
4301a7c91847Schristos error (1, errno, "reading %s", short_pathname);
4302a7c91847Schristos
4303a7c91847Schristos mode_string = mode_to_string (sb.st_mode);
4304a7c91847Schristos
4305a7c91847Schristos /* Beware: on systems using CRLF line termination conventions,
4306a7c91847Schristos the read and write functions will convert CRLF to LF, so the
4307a7c91847Schristos number of characters read is not the same as sb.st_size. Text
4308a7c91847Schristos files should always be transmitted using the LF convention, so
4309a7c91847Schristos we don't want to disable this conversion. */
4310a7c91847Schristos bufsize = sb.st_size;
4311a7c91847Schristos buf = xmalloc (bufsize);
4312a7c91847Schristos
4313a7c91847Schristos /* Is the file marked as containing binary data by the "-kb" flag?
4314a7c91847Schristos If so, make sure to open it in binary mode: */
4315a7c91847Schristos
4316a7c91847Schristos if (vers && vers->options)
4317a7c91847Schristos bin = !strcmp (vers->options, "-kb");
4318a7c91847Schristos else
4319a7c91847Schristos bin = 0;
4320a7c91847Schristos
4321a7c91847Schristos #ifdef BROKEN_READWRITE_CONVERSION
4322a7c91847Schristos if (!bin)
4323a7c91847Schristos {
4324a7c91847Schristos /* If only stdio, not open/write/etc., do text/binary
4325a7c91847Schristos conversion, use convert_file which can compensate
4326a7c91847Schristos (FIXME: we could just use stdio instead which would
4327a7c91847Schristos avoid the whole problem). */
4328a7c91847Schristos char *tfile = Xasprintf ("%s.CVSBFCTMP", file);
4329a7c91847Schristos convert_file (file, O_RDONLY,
4330a7c91847Schristos tfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY);
4331a7c91847Schristos fd = CVS_OPEN (tfile, O_RDONLY | OPEN_BINARY);
4332a7c91847Schristos if (fd < 0)
4333a7c91847Schristos error (1, errno, "reading %s", short_pathname);
4334a7c91847Schristos free (tfile);
4335a7c91847Schristos }
4336a7c91847Schristos else
4337a7c91847Schristos fd = CVS_OPEN (file, O_RDONLY | OPEN_BINARY);
4338a7c91847Schristos #else
4339a7c91847Schristos fd = CVS_OPEN (file, O_RDONLY | (bin ? OPEN_BINARY : 0));
4340a7c91847Schristos #endif
4341a7c91847Schristos
4342a7c91847Schristos if (fd < 0)
4343a7c91847Schristos error (1, errno, "reading %s", short_pathname);
4344a7c91847Schristos
4345a7c91847Schristos if (file_gzip_level && sb.st_size > 100)
4346a7c91847Schristos {
4347a7c91847Schristos size_t newsize = 0;
4348a7c91847Schristos
4349a7c91847Schristos if (read_and_gzip (fd, short_pathname, &buf,
4350a7c91847Schristos &bufsize, &newsize,
4351a7c91847Schristos file_gzip_level))
4352a7c91847Schristos error (1, 0, "aborting due to compression error");
4353a7c91847Schristos
4354a7c91847Schristos if (close (fd) < 0)
4355a7c91847Schristos error (0, errno, "warning: can't close %s", short_pathname);
4356a7c91847Schristos
4357a7c91847Schristos {
4358a7c91847Schristos char tmp[80];
4359a7c91847Schristos
4360a7c91847Schristos send_to_server ("Modified ", 0);
4361a7c91847Schristos send_to_server (file, 0);
4362a7c91847Schristos send_to_server ("\012", 1);
4363a7c91847Schristos send_to_server (mode_string, 0);
4364a7c91847Schristos send_to_server ("\012z", 2);
4365a7c91847Schristos sprintf (tmp, "%lu\n", (unsigned long) newsize);
4366a7c91847Schristos send_to_server (tmp, 0);
4367a7c91847Schristos
4368a7c91847Schristos send_to_server (buf, newsize);
4369a7c91847Schristos }
4370a7c91847Schristos }
4371a7c91847Schristos else
4372a7c91847Schristos {
4373a7c91847Schristos int newsize;
4374a7c91847Schristos
4375a7c91847Schristos {
4376a7c91847Schristos unsigned char *bufp = buf;
4377a7c91847Schristos int len;
4378a7c91847Schristos
4379a7c91847Schristos /* FIXME: This is gross. It assumes that we might read
4380a7c91847Schristos less than st_size bytes (true on NT), but not more.
4381a7c91847Schristos Instead of this we should just be reading a block of
4382a7c91847Schristos data (e.g. 8192 bytes), writing it to the network, and
4383a7c91847Schristos so on until EOF. */
4384a7c91847Schristos while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0)
4385a7c91847Schristos bufp += len;
4386a7c91847Schristos
4387a7c91847Schristos if (len < 0)
4388a7c91847Schristos error (1, errno, "reading %s", short_pathname);
4389a7c91847Schristos
4390a7c91847Schristos newsize = bufp - buf;
4391a7c91847Schristos }
4392a7c91847Schristos if (close (fd) < 0)
4393a7c91847Schristos error (0, errno, "warning: can't close %s", short_pathname);
4394a7c91847Schristos
4395a7c91847Schristos {
4396a7c91847Schristos char tmp[80];
4397a7c91847Schristos
4398a7c91847Schristos send_to_server ("Modified ", 0);
4399a7c91847Schristos send_to_server (file, 0);
4400a7c91847Schristos send_to_server ("\012", 1);
4401a7c91847Schristos send_to_server (mode_string, 0);
4402a7c91847Schristos send_to_server ("\012", 1);
4403a7c91847Schristos sprintf (tmp, "%lu\012", (unsigned long) newsize);
4404a7c91847Schristos send_to_server (tmp, 0);
4405a7c91847Schristos }
4406a7c91847Schristos #ifdef BROKEN_READWRITE_CONVERSION
4407a7c91847Schristos if (!bin)
4408a7c91847Schristos {
4409a7c91847Schristos char *tfile = Xasprintf ("%s.CVSBFCTMP", file);
4410a7c91847Schristos if (CVS_UNLINK (tfile) < 0)
4411a7c91847Schristos error (0, errno, "warning: can't remove temp file %s", tfile);
4412a7c91847Schristos free (tfile);
4413a7c91847Schristos }
4414a7c91847Schristos #endif
4415a7c91847Schristos
4416a7c91847Schristos /*
4417a7c91847Schristos * Note that this only ends with a newline if the file ended with
4418a7c91847Schristos * one.
4419a7c91847Schristos */
4420a7c91847Schristos if (newsize > 0)
4421a7c91847Schristos send_to_server (buf, newsize);
4422a7c91847Schristos }
4423a7c91847Schristos free (buf);
4424a7c91847Schristos free (mode_string);
4425a7c91847Schristos }
4426a7c91847Schristos
4427a7c91847Schristos
4428a7c91847Schristos
4429a7c91847Schristos /* The address of an instance of this structure is passed to
4430a7c91847Schristos send_fileproc, send_filesdoneproc, and send_direntproc, as the
4431a7c91847Schristos callerdat parameter. */
4432a7c91847Schristos struct send_data
4433a7c91847Schristos {
4434a7c91847Schristos /* Each of the following flags are zero for clear or nonzero for set. */
4435a7c91847Schristos int build_dirs;
4436a7c91847Schristos int force;
4437a7c91847Schristos int no_contents;
4438a7c91847Schristos int backup_modified;
4439a7c91847Schristos };
4440a7c91847Schristos
4441a7c91847Schristos /* Deal with one file. */
4442a7c91847Schristos static int
send_fileproc(void * callerdat,struct file_info * finfo)4443a7c91847Schristos send_fileproc (void *callerdat, struct file_info *finfo)
4444a7c91847Schristos {
4445a7c91847Schristos struct send_data *args = callerdat;
4446a7c91847Schristos Vers_TS *vers;
4447a7c91847Schristos struct file_info xfinfo;
4448a7c91847Schristos /* File name to actually use. Might differ in case from
4449a7c91847Schristos finfo->file. */
4450a7c91847Schristos const char *filename;
4451a7c91847Schristos
4452a7c91847Schristos send_a_repository ("", finfo->repository, finfo->update_dir);
4453a7c91847Schristos
4454a7c91847Schristos xfinfo = *finfo;
4455a7c91847Schristos xfinfo.repository = NULL;
4456a7c91847Schristos xfinfo.rcs = NULL;
4457a7c91847Schristos vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0);
4458a7c91847Schristos
4459a7c91847Schristos if (vers->entdata)
4460a7c91847Schristos filename = vers->entdata->user;
4461a7c91847Schristos else
4462a7c91847Schristos filename = finfo->file;
4463a7c91847Schristos
4464a7c91847Schristos if (vers->vn_user)
4465a7c91847Schristos {
4466a7c91847Schristos /* The Entries request. */
4467a7c91847Schristos send_to_server ("Entry /", 0);
4468a7c91847Schristos send_to_server (filename, 0);
4469a7c91847Schristos send_to_server ("/", 0);
4470a7c91847Schristos send_to_server (vers->vn_user, 0);
4471a7c91847Schristos send_to_server ("/", 0);
4472a7c91847Schristos if (vers->ts_conflict)
4473a7c91847Schristos {
4474a7c91847Schristos if (vers->ts_user && !strcmp (vers->ts_conflict, vers->ts_user))
4475a7c91847Schristos send_to_server ("+=", 0);
4476a7c91847Schristos else
4477a7c91847Schristos send_to_server ("+modified", 0);
4478a7c91847Schristos }
4479a7c91847Schristos send_to_server ("/", 0);
4480a7c91847Schristos send_to_server (vers->entdata ? vers->entdata->options : vers->options,
4481a7c91847Schristos 0);
4482a7c91847Schristos send_to_server ("/", 0);
4483a7c91847Schristos if (vers->entdata && vers->entdata->tag)
4484a7c91847Schristos {
4485a7c91847Schristos send_to_server ("T", 0);
4486a7c91847Schristos send_to_server (vers->entdata->tag, 0);
4487a7c91847Schristos }
4488a7c91847Schristos else if (vers->entdata && vers->entdata->date)
4489a7c91847Schristos {
4490a7c91847Schristos send_to_server ("D", 0);
4491a7c91847Schristos send_to_server (vers->entdata->date, 0);
4492a7c91847Schristos }
4493a7c91847Schristos send_to_server ("\012", 1);
4494a7c91847Schristos }
4495a7c91847Schristos else
4496a7c91847Schristos {
4497a7c91847Schristos /* It seems a little silly to re-read this on each file, but
4498a7c91847Schristos send_dirent_proc doesn't get called if filenames are specified
4499a7c91847Schristos explicitly on the command line. */
4500a7c91847Schristos wrap_add_file (CVSDOTWRAPPER, 1);
4501a7c91847Schristos
4502a7c91847Schristos if (wrap_name_has (filename, WRAP_RCSOPTION))
4503a7c91847Schristos {
4504a7c91847Schristos /* No "Entry", but the wrappers did give us a kopt so we better
4505a7c91847Schristos send it with "Kopt". As far as I know this only happens
4506a7c91847Schristos for "cvs add". Question: is there any reason why checking
4507a7c91847Schristos for options from wrappers isn't done in Version_TS?
4508a7c91847Schristos
4509a7c91847Schristos Note: it might have been better to just remember all the
4510a7c91847Schristos kopts on the client side, rather than send them to the server,
4511a7c91847Schristos and have it send us back the same kopts. But that seemed like
4512a7c91847Schristos a bigger change than I had in mind making now. */
4513a7c91847Schristos
4514a7c91847Schristos if (supported_request ("Kopt"))
4515a7c91847Schristos {
4516a7c91847Schristos char *opt;
4517a7c91847Schristos
4518a7c91847Schristos send_to_server ("Kopt ", 0);
4519a7c91847Schristos opt = wrap_rcsoption (filename, 1);
4520a7c91847Schristos send_to_server (opt, 0);
4521a7c91847Schristos send_to_server ("\012", 1);
4522a7c91847Schristos free (opt);
4523a7c91847Schristos }
4524a7c91847Schristos else
4525a7c91847Schristos error (0, 0, "\
4526a7c91847Schristos warning: ignoring -k options due to server limitations");
4527a7c91847Schristos }
4528a7c91847Schristos }
4529a7c91847Schristos
4530a7c91847Schristos if (!vers->ts_user)
4531a7c91847Schristos {
4532a7c91847Schristos /*
4533a7c91847Schristos * Do we want to print "file was lost" like normal CVS?
4534a7c91847Schristos * Would it always be appropriate?
4535a7c91847Schristos */
4536a7c91847Schristos /* File no longer exists. Don't do anything, missing files
4537a7c91847Schristos just happen. */
4538a7c91847Schristos }
4539a7c91847Schristos else if (!vers->ts_rcs || args->force
4540a7c91847Schristos || strcmp (vers->ts_conflict
4541a7c91847Schristos ? vers->ts_conflict : vers->ts_rcs, vers->ts_user)
4542a7c91847Schristos || (vers->ts_conflict && !strcmp (cvs_cmd_name, "diff")))
4543a7c91847Schristos {
4544a7c91847Schristos if (args->no_contents
4545a7c91847Schristos && supported_request ("Is-modified"))
4546a7c91847Schristos {
4547a7c91847Schristos send_to_server ("Is-modified ", 0);
4548a7c91847Schristos send_to_server (filename, 0);
4549a7c91847Schristos send_to_server ("\012", 1);
4550a7c91847Schristos }
4551a7c91847Schristos else
4552a7c91847Schristos send_modified (filename, finfo->fullname, vers);
4553a7c91847Schristos
4554a7c91847Schristos if (args->backup_modified)
4555a7c91847Schristos {
4556a7c91847Schristos char *bakname;
4557a7c91847Schristos bakname = backup_file (filename, vers->vn_user);
4558a7c91847Schristos /* This behavior is sufficiently unexpected to
4559a7c91847Schristos justify overinformativeness, I think. */
4560a7c91847Schristos if (! really_quiet)
4561a7c91847Schristos printf ("(Locally modified %s moved to %s)\n",
4562a7c91847Schristos filename, bakname);
4563a7c91847Schristos free (bakname);
4564a7c91847Schristos }
4565a7c91847Schristos }
4566a7c91847Schristos else
4567a7c91847Schristos {
4568a7c91847Schristos send_to_server ("Unchanged ", 0);
4569a7c91847Schristos send_to_server (filename, 0);
4570a7c91847Schristos send_to_server ("\012", 1);
4571a7c91847Schristos }
4572a7c91847Schristos
4573a7c91847Schristos /* if this directory has an ignore list, add this file to it */
4574a7c91847Schristos if (ignlist)
4575a7c91847Schristos {
4576a7c91847Schristos Node *p;
4577a7c91847Schristos
4578a7c91847Schristos p = getnode ();
4579a7c91847Schristos p->type = FILES;
4580a7c91847Schristos p->key = xstrdup (finfo->file);
4581a7c91847Schristos (void) addnode (ignlist, p);
4582a7c91847Schristos }
4583a7c91847Schristos
4584a7c91847Schristos freevers_ts (&vers);
4585a7c91847Schristos return 0;
4586a7c91847Schristos }
4587a7c91847Schristos
4588a7c91847Schristos
4589a7c91847Schristos
4590a7c91847Schristos static void
send_ignproc(const char * file,const char * dir)4591a7c91847Schristos send_ignproc (const char *file, const char *dir)
4592a7c91847Schristos {
4593a7c91847Schristos if (ign_inhibit_server || !supported_request ("Questionable"))
4594a7c91847Schristos {
4595a7c91847Schristos if (dir[0] != '\0')
4596a7c91847Schristos (void) printf ("? %s/%s\n", dir, file);
4597a7c91847Schristos else
4598a7c91847Schristos (void) printf ("? %s\n", file);
4599a7c91847Schristos }
4600a7c91847Schristos else
4601a7c91847Schristos {
4602a7c91847Schristos send_to_server ("Questionable ", 0);
4603a7c91847Schristos send_to_server (file, 0);
4604a7c91847Schristos send_to_server ("\012", 1);
4605a7c91847Schristos }
4606a7c91847Schristos }
4607a7c91847Schristos
4608a7c91847Schristos
4609a7c91847Schristos
4610a7c91847Schristos static int
send_filesdoneproc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)4611a7c91847Schristos send_filesdoneproc (void *callerdat, int err, const char *repository,
4612a7c91847Schristos const char *update_dir, List *entries)
4613a7c91847Schristos {
4614a7c91847Schristos /* if this directory has an ignore list, process it then free it */
4615a7c91847Schristos if (ignlist)
4616a7c91847Schristos {
4617a7c91847Schristos ignore_files (ignlist, entries, update_dir, send_ignproc);
4618a7c91847Schristos dellist (&ignlist);
4619a7c91847Schristos }
4620a7c91847Schristos
4621a7c91847Schristos return err;
4622a7c91847Schristos }
4623a7c91847Schristos
4624a7c91847Schristos
4625a7c91847Schristos
4626a7c91847Schristos /*
4627a7c91847Schristos * send_dirent_proc () is called back by the recursion processor before a
4628a7c91847Schristos * sub-directory is processed for update.
4629a7c91847Schristos * A return code of 0 indicates the directory should be
4630a7c91847Schristos * processed by the recursion code. A return of non-zero indicates the
4631a7c91847Schristos * recursion code should skip this directory.
4632a7c91847Schristos *
4633a7c91847Schristos */
4634a7c91847Schristos static Dtype
send_dirent_proc(void * callerdat,const char * dir,const char * repository,const char * update_dir,List * entries)4635a7c91847Schristos send_dirent_proc (void *callerdat, const char *dir, const char *repository,
4636a7c91847Schristos const char *update_dir, List *entries)
4637a7c91847Schristos {
4638a7c91847Schristos struct send_data *args = callerdat;
4639a7c91847Schristos int dir_exists;
4640a7c91847Schristos char *cvsadm_name;
4641a7c91847Schristos
4642a7c91847Schristos if (ignore_directory (update_dir))
4643a7c91847Schristos {
4644a7c91847Schristos /* print the warm fuzzy message */
4645a7c91847Schristos if (!quiet)
4646a7c91847Schristos error (0, 0, "Ignoring %s", update_dir);
4647a7c91847Schristos return R_SKIP_ALL;
4648a7c91847Schristos }
4649a7c91847Schristos
4650a7c91847Schristos /*
4651a7c91847Schristos * If the directory does not exist yet (e.g. "cvs update -d foo"),
4652a7c91847Schristos * no need to send any files from it. If the directory does not
4653a7c91847Schristos * have a CVS directory, then we pretend that it does not exist.
4654a7c91847Schristos * Otherwise, we will fail when trying to open the Entries file.
4655a7c91847Schristos * This case will happen when checking out a module defined as
4656a7c91847Schristos * ``-a .''.
4657a7c91847Schristos */
4658a7c91847Schristos cvsadm_name = Xasprintf ("%s/%s", dir, CVSADM);
4659a7c91847Schristos dir_exists = isdir (cvsadm_name);
4660a7c91847Schristos free (cvsadm_name);
4661a7c91847Schristos
4662a7c91847Schristos /*
4663a7c91847Schristos * If there is an empty directory (e.g. we are doing `cvs add' on a
4664a7c91847Schristos * newly-created directory), the server still needs to know about it.
4665a7c91847Schristos */
4666a7c91847Schristos
4667a7c91847Schristos if (dir_exists)
4668a7c91847Schristos {
4669a7c91847Schristos /*
4670a7c91847Schristos * Get the repository from a CVS/Repository file whenever possible.
4671a7c91847Schristos * The repository variable is wrong if the names in the local
4672a7c91847Schristos * directory don't match the names in the repository.
4673a7c91847Schristos */
4674a7c91847Schristos char *repos = Name_Repository (dir, update_dir);
4675a7c91847Schristos send_a_repository (dir, repos, update_dir);
4676a7c91847Schristos free (repos);
4677a7c91847Schristos
4678a7c91847Schristos /* initialize the ignore list for this directory */
4679a7c91847Schristos ignlist = getlist ();
4680a7c91847Schristos }
4681a7c91847Schristos else
4682a7c91847Schristos {
4683a7c91847Schristos /* It doesn't make sense to send a non-existent directory,
4684a7c91847Schristos because there is no way to get the correct value for
4685a7c91847Schristos the repository (I suppose maybe via the expand-modules
4686a7c91847Schristos request). In the case where the "obvious" choice for
4687a7c91847Schristos repository is correct, the server can figure out whether
4688a7c91847Schristos to recreate the directory; in the case where it is wrong
4689a7c91847Schristos (that is, does not match what modules give us), we might as
4690a7c91847Schristos well just fail to recreate it.
4691a7c91847Schristos
4692a7c91847Schristos Checking for noexec is a kludge for "cvs -n add dir". */
4693a7c91847Schristos /* Don't send a non-existent directory unless we are building
4694a7c91847Schristos new directories (build_dirs is true). Otherwise, CVS may
4695a7c91847Schristos see a D line in an Entries file, and recreate a directory
4696a7c91847Schristos which the user removed by hand. */
4697a7c91847Schristos if (args->build_dirs && noexec)
4698a7c91847Schristos send_a_repository (dir, repository, update_dir);
4699a7c91847Schristos }
4700a7c91847Schristos
4701a7c91847Schristos return dir_exists ? R_PROCESS : R_SKIP_ALL;
4702a7c91847Schristos }
4703a7c91847Schristos
4704a7c91847Schristos
4705a7c91847Schristos
4706a7c91847Schristos /*
4707a7c91847Schristos * send_dirleave_proc () is called back by the recursion code upon leaving
4708a7c91847Schristos * a directory. All it does is delete the ignore list if it hasn't already
4709a7c91847Schristos * been done (by send_filesdone_proc).
4710a7c91847Schristos */
4711a7c91847Schristos /* ARGSUSED */
4712a7c91847Schristos static int
send_dirleave_proc(void * callerdat,const char * dir,int err,const char * update_dir,List * entries)4713a7c91847Schristos send_dirleave_proc (void *callerdat, const char *dir, int err,
4714a7c91847Schristos const char *update_dir, List *entries )
4715a7c91847Schristos {
4716a7c91847Schristos
4717a7c91847Schristos /* Delete the ignore list if it hasn't already been done. */
4718a7c91847Schristos if (ignlist)
4719a7c91847Schristos dellist (&ignlist);
4720a7c91847Schristos return err;
4721a7c91847Schristos }
4722a7c91847Schristos
4723a7c91847Schristos
4724a7c91847Schristos
4725a7c91847Schristos /*
4726a7c91847Schristos * Send each option in an array to the server, one by one.
4727a7c91847Schristos * argv might be "--foo=bar", "-C", "5", "-y".
4728a7c91847Schristos */
4729a7c91847Schristos
4730a7c91847Schristos void
send_options(int argc,char * const * argv)4731a7c91847Schristos send_options (int argc, char * const *argv)
4732a7c91847Schristos {
4733a7c91847Schristos int i;
4734a7c91847Schristos for (i = 0; i < argc; i++)
4735a7c91847Schristos send_arg (argv[i]);
4736a7c91847Schristos }
4737a7c91847Schristos
4738a7c91847Schristos
4739a7c91847Schristos
4740a7c91847Schristos /* Send the names of all the argument files to the server. */
4741a7c91847Schristos void
send_file_names(int argc,char ** argv,unsigned int flags)4742a7c91847Schristos send_file_names (int argc, char **argv, unsigned int flags)
4743a7c91847Schristos {
4744a7c91847Schristos int i;
4745a7c91847Schristos
4746a7c91847Schristos /* The fact that we do this here as well as start_recursion is a bit
4747a7c91847Schristos of a performance hit. Perhaps worth cleaning up someday. */
4748a7c91847Schristos if (flags & SEND_EXPAND_WILD)
4749a7c91847Schristos expand_wild (argc, argv, &argc, &argv);
4750a7c91847Schristos
4751a7c91847Schristos for (i = 0; i < argc; ++i)
4752a7c91847Schristos {
4753a7c91847Schristos char buf[1];
4754a7c91847Schristos char *p;
4755a7c91847Schristos #ifdef FILENAMES_CASE_INSENSITIVE
4756a7c91847Schristos char *line = NULL;
4757a7c91847Schristos #endif /* FILENAMES_CASE_INSENSITIVE */
4758a7c91847Schristos
4759a7c91847Schristos if (arg_should_not_be_sent_to_server (argv[i]))
4760a7c91847Schristos continue;
4761a7c91847Schristos
4762a7c91847Schristos #ifdef FILENAMES_CASE_INSENSITIVE
4763a7c91847Schristos /* We want to send the path as it appears in the
4764a7c91847Schristos CVS/Entries files. We put this inside an ifdef
4765a7c91847Schristos to avoid doing all these system calls in
4766a7c91847Schristos cases where fncmp is just strcmp anyway. */
4767a7c91847Schristos /* The isdir (CVSADM) check could more gracefully be replaced
4768a7c91847Schristos with a way of having Entries_Open report back the
4769a7c91847Schristos error to us and letting us ignore existence_error.
4770a7c91847Schristos Or some such. */
4771a7c91847Schristos {
4772a7c91847Schristos List *stack;
4773a7c91847Schristos size_t line_len = 0;
4774a7c91847Schristos char *q, *r;
4775a7c91847Schristos struct saved_cwd sdir;
4776a7c91847Schristos
4777a7c91847Schristos /* Split the argument onto the stack. */
4778a7c91847Schristos stack = getlist();
4779a7c91847Schristos r = xstrdup (argv[i]);
4780a7c91847Schristos /* It's okay to discard the const from the last_component return
4781a7c91847Schristos * below since we know we passed in an arg that was not const.
4782a7c91847Schristos */
4783a7c91847Schristos while ((q = (char *)last_component (r)) != r)
4784a7c91847Schristos {
4785a7c91847Schristos push (stack, xstrdup (q));
4786a7c91847Schristos *--q = '\0';
4787a7c91847Schristos }
4788a7c91847Schristos push (stack, r);
4789a7c91847Schristos
4790a7c91847Schristos /* Normalize the path into outstr. */
4791a7c91847Schristos save_cwd (&sdir);
4792a7c91847Schristos while (q = pop (stack))
4793a7c91847Schristos {
4794a7c91847Schristos Node *node = NULL;
4795a7c91847Schristos if (isdir (CVSADM))
4796a7c91847Schristos {
4797a7c91847Schristos List *entries;
4798a7c91847Schristos
4799a7c91847Schristos /* Note that if we are adding a directory,
4800a7c91847Schristos the following will read the entry
4801a7c91847Schristos that we just wrote there, that is, we
4802a7c91847Schristos will get the case specified on the
4803a7c91847Schristos command line, not the case of the
4804a7c91847Schristos directory in the filesystem. This
4805a7c91847Schristos is correct behavior. */
4806a7c91847Schristos entries = Entries_Open (0, NULL);
4807a7c91847Schristos node = findnode_fn (entries, q);
4808a7c91847Schristos if (node)
4809a7c91847Schristos {
4810a7c91847Schristos /* Add the slash unless this is our first element. */
4811a7c91847Schristos if (line_len)
4812a7c91847Schristos xrealloc_and_strcat (&line, &line_len, "/");
4813a7c91847Schristos xrealloc_and_strcat (&line, &line_len, node->key);
4814a7c91847Schristos delnode (node);
4815a7c91847Schristos }
4816a7c91847Schristos Entries_Close (entries);
4817a7c91847Schristos }
4818a7c91847Schristos
4819a7c91847Schristos /* If node is still NULL then we either didn't find CVSADM or
4820a7c91847Schristos * we didn't find an entry there.
4821a7c91847Schristos */
4822a7c91847Schristos if (!node)
4823a7c91847Schristos {
4824a7c91847Schristos /* Add the slash unless this is our first element. */
4825a7c91847Schristos if (line_len)
4826a7c91847Schristos xrealloc_and_strcat (&line, &line_len, "/");
4827a7c91847Schristos xrealloc_and_strcat (&line, &line_len, q);
4828a7c91847Schristos break;
4829a7c91847Schristos }
4830a7c91847Schristos
4831a7c91847Schristos /* And descend the tree. */
4832a7c91847Schristos if (isdir (q))
4833a7c91847Schristos CVS_CHDIR (q);
4834a7c91847Schristos free (q);
4835a7c91847Schristos }
4836a7c91847Schristos restore_cwd (&sdir);
4837a7c91847Schristos free_cwd (&sdir);
4838a7c91847Schristos
4839a7c91847Schristos /* Now put everything we didn't find entries for back on. */
4840a7c91847Schristos while (q = pop (stack))
4841a7c91847Schristos {
4842a7c91847Schristos if (line_len)
4843a7c91847Schristos xrealloc_and_strcat (&line, &line_len, "/");
4844a7c91847Schristos xrealloc_and_strcat (&line, &line_len, q);
4845a7c91847Schristos free (q);
4846a7c91847Schristos }
4847a7c91847Schristos
4848a7c91847Schristos p = line;
4849a7c91847Schristos
4850a7c91847Schristos dellist (&stack);
4851a7c91847Schristos }
4852a7c91847Schristos #else /* !FILENAMES_CASE_INSENSITIVE */
4853a7c91847Schristos p = argv[i];
4854a7c91847Schristos #endif /* FILENAMES_CASE_INSENSITIVE */
4855a7c91847Schristos
4856a7c91847Schristos send_to_server ("Argument ", 0);
4857a7c91847Schristos
4858a7c91847Schristos while (*p)
4859a7c91847Schristos {
4860a7c91847Schristos if (*p == '\n')
4861a7c91847Schristos {
4862a7c91847Schristos send_to_server ("\012Argumentx ", 0);
4863a7c91847Schristos }
4864a7c91847Schristos else if (ISSLASH (*p))
4865a7c91847Schristos {
4866a7c91847Schristos buf[0] = '/';
4867a7c91847Schristos send_to_server (buf, 1);
4868a7c91847Schristos }
4869a7c91847Schristos else
4870a7c91847Schristos {
4871a7c91847Schristos buf[0] = *p;
4872a7c91847Schristos send_to_server (buf, 1);
4873a7c91847Schristos }
4874a7c91847Schristos ++p;
4875a7c91847Schristos }
4876a7c91847Schristos send_to_server ("\012", 1);
4877a7c91847Schristos #ifdef FILENAMES_CASE_INSENSITIVE
4878a7c91847Schristos free (line);
4879a7c91847Schristos #endif /* FILENAMES_CASE_INSENSITIVE */
4880a7c91847Schristos }
4881a7c91847Schristos
4882a7c91847Schristos if (flags & SEND_EXPAND_WILD)
4883a7c91847Schristos {
4884a7c91847Schristos int i;
4885a7c91847Schristos for (i = 0; i < argc; ++i)
4886a7c91847Schristos free (argv[i]);
4887a7c91847Schristos free (argv);
4888a7c91847Schristos }
4889a7c91847Schristos }
4890a7c91847Schristos
4891a7c91847Schristos
4892a7c91847Schristos
4893a7c91847Schristos /* Calculate and send max-dotdot to the server */
4894a7c91847Schristos static void
send_max_dotdot(argc,argv)4895a7c91847Schristos send_max_dotdot (argc, argv)
4896a7c91847Schristos int argc;
4897a7c91847Schristos char **argv;
4898a7c91847Schristos {
4899a7c91847Schristos int i;
4900a7c91847Schristos int level = 0;
4901a7c91847Schristos int max_level = 0;
4902a7c91847Schristos
4903a7c91847Schristos /* Send Max-dotdot if needed. */
4904a7c91847Schristos for (i = 0; i < argc; ++i)
4905a7c91847Schristos {
4906a7c91847Schristos level = pathname_levels (argv[i]);
4907a7c91847Schristos if (level > 0)
4908a7c91847Schristos {
4909a7c91847Schristos if (!uppaths) uppaths = getlist();
4910a7c91847Schristos push_string (uppaths, xstrdup (argv[i]));
4911a7c91847Schristos }
4912a7c91847Schristos if (level > max_level)
4913a7c91847Schristos max_level = level;
4914a7c91847Schristos }
4915a7c91847Schristos
4916a7c91847Schristos if (max_level > 0)
4917a7c91847Schristos {
4918a7c91847Schristos if (supported_request ("Max-dotdot"))
4919a7c91847Schristos {
4920a7c91847Schristos char buf[10];
4921a7c91847Schristos sprintf (buf, "%d", max_level);
4922a7c91847Schristos
4923a7c91847Schristos send_to_server ("Max-dotdot ", 0);
4924a7c91847Schristos send_to_server (buf, 0);
4925a7c91847Schristos send_to_server ("\012", 1);
4926a7c91847Schristos }
4927a7c91847Schristos else
4928a7c91847Schristos {
4929a7c91847Schristos error (1, 0,
4930a7c91847Schristos "backreference in path (`..') not supported by old (pre-Max-dotdot) servers");
4931a7c91847Schristos }
4932a7c91847Schristos }
4933a7c91847Schristos }
4934a7c91847Schristos
4935a7c91847Schristos
4936a7c91847Schristos
4937a7c91847Schristos /* Send Repository, Modified and Entry. argc and argv contain only
4938a7c91847Schristos the files to operate on (or empty for everything), not options.
4939a7c91847Schristos local is nonzero if we should not recurse (-l option). flags &
4940a7c91847Schristos SEND_BUILD_DIRS is nonzero if nonexistent directories should be
4941a7c91847Schristos sent. flags & SEND_FORCE is nonzero if we should send unmodified
4942a7c91847Schristos files to the server as though they were modified. flags &
4943a7c91847Schristos SEND_NO_CONTENTS means that this command only needs to know
4944a7c91847Schristos _whether_ a file is modified, not the contents. Also sends Argument
4945a7c91847Schristos lines for argc and argv, so should be called after options are sent. */
4946a7c91847Schristos void
send_files(int argc,char ** argv,int local,int aflag,unsigned int flags)4947a7c91847Schristos send_files (int argc, char **argv, int local, int aflag, unsigned int flags)
4948a7c91847Schristos {
4949a7c91847Schristos struct send_data args;
4950a7c91847Schristos int err;
4951a7c91847Schristos
4952a7c91847Schristos send_max_dotdot (argc, argv);
4953a7c91847Schristos
4954a7c91847Schristos /*
4955a7c91847Schristos * aflag controls whether the tag/date is copied into the vers_ts.
4956a7c91847Schristos * But we don't actually use it, so I don't think it matters what we pass
4957a7c91847Schristos * for aflag here.
4958a7c91847Schristos */
4959a7c91847Schristos args.build_dirs = flags & SEND_BUILD_DIRS;
4960a7c91847Schristos args.force = flags & SEND_FORCE;
4961a7c91847Schristos args.no_contents = flags & SEND_NO_CONTENTS;
4962a7c91847Schristos args.backup_modified = flags & BACKUP_MODIFIED_FILES;
4963a7c91847Schristos err = start_recursion
4964a7c91847Schristos (send_fileproc, send_filesdoneproc, send_dirent_proc,
4965a7c91847Schristos send_dirleave_proc, &args, argc, argv, local, W_LOCAL, aflag,
4966a7c91847Schristos CVS_LOCK_NONE, NULL, 0, NULL);
4967a7c91847Schristos if (err)
4968a7c91847Schristos exit (EXIT_FAILURE);
4969a7c91847Schristos if (!toplevel_repos)
4970a7c91847Schristos /*
4971a7c91847Schristos * This happens if we are not processing any files,
4972a7c91847Schristos * or for checkouts in directories without any existing stuff
4973a7c91847Schristos * checked out. The following assignment is correct for the
4974a7c91847Schristos * latter case; I don't think toplevel_repos matters for the
4975a7c91847Schristos * former.
4976a7c91847Schristos */
4977a7c91847Schristos toplevel_repos = xstrdup (current_parsed_root->directory);
4978a7c91847Schristos send_repository ("", toplevel_repos, ".");
4979a7c91847Schristos }
4980a7c91847Schristos
4981a7c91847Schristos
4982a7c91847Schristos
4983a7c91847Schristos void
client_import_setup(char * repository)4984a7c91847Schristos client_import_setup (char *repository)
4985a7c91847Schristos {
4986a7c91847Schristos if (!toplevel_repos) /* should always be true */
4987a7c91847Schristos send_a_repository ("", repository, "");
4988a7c91847Schristos }
4989a7c91847Schristos
4990a7c91847Schristos
4991a7c91847Schristos
4992a7c91847Schristos /*
4993a7c91847Schristos * Process the argument import file.
4994a7c91847Schristos */
4995a7c91847Schristos int
client_process_import_file(char * message,char * vfile,char * vtag,int targc,char * targv[],char * repository,int all_files_binary,int modtime)4996a7c91847Schristos client_process_import_file (char *message, char *vfile, char *vtag, int targc,
4997a7c91847Schristos char *targv[], char *repository,
4998a7c91847Schristos int all_files_binary,
4999a7c91847Schristos int modtime /* Nonzero for "import -d". */ )
5000a7c91847Schristos {
5001a7c91847Schristos char *update_dir;
5002a7c91847Schristos char *fullname;
5003a7c91847Schristos Vers_TS vers;
5004a7c91847Schristos
5005a7c91847Schristos assert (toplevel_repos);
5006a7c91847Schristos
5007a7c91847Schristos if (strncmp (repository, toplevel_repos, strlen (toplevel_repos)))
5008a7c91847Schristos error (1, 0,
5009a7c91847Schristos "internal error: pathname `%s' doesn't specify file in `%s'",
5010a7c91847Schristos repository, toplevel_repos);
5011a7c91847Schristos
5012a7c91847Schristos if (!strcmp (repository, toplevel_repos))
5013a7c91847Schristos {
5014a7c91847Schristos update_dir = "";
5015a7c91847Schristos fullname = xstrdup (vfile);
5016a7c91847Schristos }
5017a7c91847Schristos else
5018a7c91847Schristos {
5019a7c91847Schristos update_dir = repository + strlen (toplevel_repos) + 1;
5020a7c91847Schristos
5021a7c91847Schristos fullname = Xasprintf ("%s/%s", update_dir, vfile);
5022a7c91847Schristos }
5023a7c91847Schristos
5024a7c91847Schristos send_a_repository ("", repository, update_dir);
5025a7c91847Schristos if (all_files_binary)
5026a7c91847Schristos vers.options = xstrdup ("-kb");
5027a7c91847Schristos else
5028a7c91847Schristos vers.options = wrap_rcsoption (vfile, 1);
5029a7c91847Schristos
5030a7c91847Schristos if (vers.options)
5031a7c91847Schristos {
5032a7c91847Schristos if (supported_request ("Kopt"))
5033a7c91847Schristos {
5034a7c91847Schristos send_to_server ("Kopt ", 0);
5035a7c91847Schristos send_to_server (vers.options, 0);
5036a7c91847Schristos send_to_server ("\012", 1);
5037a7c91847Schristos }
5038a7c91847Schristos else
5039a7c91847Schristos error (0, 0,
5040a7c91847Schristos "warning: ignoring -k options due to server limitations");
5041a7c91847Schristos }
5042a7c91847Schristos if (modtime)
5043a7c91847Schristos {
5044a7c91847Schristos if (supported_request ("Checkin-time"))
5045a7c91847Schristos {
5046a7c91847Schristos struct stat sb;
5047a7c91847Schristos char *rcsdate;
5048a7c91847Schristos char netdate[MAXDATELEN];
5049a7c91847Schristos
5050a7c91847Schristos if (stat (vfile, &sb) < 0)
5051a7c91847Schristos error (1, errno, "cannot stat %s", fullname);
5052a7c91847Schristos rcsdate = date_from_time_t (sb.st_mtime);
5053a7c91847Schristos date_to_internet (netdate, rcsdate);
5054a7c91847Schristos free (rcsdate);
5055a7c91847Schristos
5056a7c91847Schristos send_to_server ("Checkin-time ", 0);
5057a7c91847Schristos send_to_server (netdate, 0);
5058a7c91847Schristos send_to_server ("\012", 1);
5059a7c91847Schristos }
5060a7c91847Schristos else
5061a7c91847Schristos error (0, 0,
5062a7c91847Schristos "warning: ignoring -d option due to server limitations");
5063a7c91847Schristos }
5064a7c91847Schristos send_modified (vfile, fullname, &vers);
5065a7c91847Schristos if (vers.options)
5066a7c91847Schristos free (vers.options);
5067a7c91847Schristos free (fullname);
5068a7c91847Schristos return 0;
5069a7c91847Schristos }
5070a7c91847Schristos
5071a7c91847Schristos
5072a7c91847Schristos
5073a7c91847Schristos void
client_import_done(void)5074a7c91847Schristos client_import_done (void)
5075a7c91847Schristos {
5076a7c91847Schristos if (!toplevel_repos)
5077a7c91847Schristos /*
5078a7c91847Schristos * This happens if we are not processing any files,
5079a7c91847Schristos * or for checkouts in directories without any existing stuff
5080a7c91847Schristos * checked out. The following assignment is correct for the
5081a7c91847Schristos * latter case; I don't think toplevel_repos matters for the
5082a7c91847Schristos * former.
5083a7c91847Schristos */
5084a7c91847Schristos /* FIXME: "can't happen" now that we call client_import_setup
5085a7c91847Schristos at the beginning. */
5086a7c91847Schristos toplevel_repos = xstrdup (current_parsed_root->directory);
5087a7c91847Schristos send_repository ("", toplevel_repos, ".");
5088a7c91847Schristos }
5089a7c91847Schristos
5090a7c91847Schristos
5091a7c91847Schristos
5092a7c91847Schristos void
client_notify(const char * repository,const char * update_dir,const char * filename,int notif_type,const char * val)5093a7c91847Schristos client_notify (const char *repository, const char *update_dir,
5094a7c91847Schristos const char *filename, int notif_type, const char *val)
5095a7c91847Schristos {
5096a7c91847Schristos char buf[2];
5097a7c91847Schristos
5098a7c91847Schristos send_a_repository ("", repository, update_dir);
5099a7c91847Schristos send_to_server ("Notify ", 0);
5100a7c91847Schristos send_to_server (filename, 0);
5101a7c91847Schristos send_to_server ("\012", 1);
5102a7c91847Schristos buf[0] = notif_type;
5103a7c91847Schristos buf[1] = '\0';
5104a7c91847Schristos send_to_server (buf, 1);
5105a7c91847Schristos send_to_server ("\t", 1);
5106a7c91847Schristos send_to_server (val, 0);
5107a7c91847Schristos }
5108a7c91847Schristos
5109a7c91847Schristos
5110a7c91847Schristos
5111a7c91847Schristos /*
5112a7c91847Schristos * Send an option with an argument, dealing correctly with newlines in
5113a7c91847Schristos * the argument. If ARG is NULL, forget the whole thing.
5114a7c91847Schristos */
5115a7c91847Schristos void
option_with_arg(const char * option,const char * arg)5116a7c91847Schristos option_with_arg (const char *option, const char *arg)
5117a7c91847Schristos {
5118a7c91847Schristos if (!arg)
5119a7c91847Schristos return;
5120a7c91847Schristos
5121a7c91847Schristos send_to_server ("Argument ", 0);
5122a7c91847Schristos send_to_server (option, 0);
5123a7c91847Schristos send_to_server ("\012", 1);
5124a7c91847Schristos
5125a7c91847Schristos send_arg (arg);
5126a7c91847Schristos }
5127a7c91847Schristos
5128a7c91847Schristos
5129a7c91847Schristos
5130a7c91847Schristos /* Send a date to the server. The input DATE is in RCS format.
5131a7c91847Schristos The time will be GMT.
5132a7c91847Schristos
5133a7c91847Schristos We then convert that to the format required in the protocol
5134a7c91847Schristos (including the "-D" option) and send it. According to
5135a7c91847Schristos cvsclient.texi, RFC 822/1123 format is preferred. */
5136a7c91847Schristos void
client_senddate(const char * date)5137a7c91847Schristos client_senddate (const char *date)
5138a7c91847Schristos {
5139a7c91847Schristos char buf[MAXDATELEN];
5140a7c91847Schristos
5141a7c91847Schristos date_to_internet (buf, date);
5142a7c91847Schristos option_with_arg ("-D", buf);
5143a7c91847Schristos }
5144a7c91847Schristos
5145a7c91847Schristos
5146a7c91847Schristos
5147a7c91847Schristos void
send_init_command(void)5148a7c91847Schristos send_init_command (void)
5149a7c91847Schristos {
5150a7c91847Schristos /* This is here because we need the current_parsed_root->directory variable. */
5151a7c91847Schristos send_to_server ("init ", 0);
5152a7c91847Schristos send_to_server (current_parsed_root->directory, 0);
5153a7c91847Schristos send_to_server ("\012", 0);
5154a7c91847Schristos }
5155a7c91847Schristos
5156a7c91847Schristos
5157a7c91847Schristos
5158a7c91847Schristos #if defined AUTH_CLIENT_SUPPORT || defined HAVE_KERBEROS || defined HAVE_GSSAPI
5159a7c91847Schristos
51609f2c58f4Schristos static int
connect_to(char * hostname,unsigned int port)5161274254cdSchristos connect_to(char *hostname, unsigned int port)
5162a7c91847Schristos {
5163274254cdSchristos struct addrinfo hints, *res, *res0 = NULL;
5164274254cdSchristos char pbuf[10];
5165274254cdSchristos int e, sock;
5166a7c91847Schristos
5167274254cdSchristos memset(&hints, 0, sizeof(hints));
5168274254cdSchristos hints.ai_family = PF_UNSPEC;
5169274254cdSchristos hints.ai_socktype = SOCK_STREAM;
5170274254cdSchristos hints.ai_flags = AI_CANONNAME;
5171274254cdSchristos
5172274254cdSchristos snprintf(pbuf, sizeof(pbuf), "%d", port);
5173274254cdSchristos e = getaddrinfo(hostname, pbuf, &hints, &res0);
5174274254cdSchristos if (e)
5175a7c91847Schristos {
5176274254cdSchristos error (1, 0, "%s", gai_strerror(e));
5177a7c91847Schristos }
5178274254cdSchristos sock = -1;
5179274254cdSchristos for (res = res0; res; res = res->ai_next) {
5180274254cdSchristos sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
5181274254cdSchristos if (sock < 0)
5182274254cdSchristos continue;
5183274254cdSchristos
5184274254cdSchristos TRACE (TRACE_FUNCTION, " -> Connecting to %s\n", hostname);
5185274254cdSchristos if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
5186274254cdSchristos close(sock);
5187274254cdSchristos sock = -1;
5188274254cdSchristos continue;
5189274254cdSchristos }
5190274254cdSchristos break;
5191274254cdSchristos }
5192274254cdSchristos freeaddrinfo(res0);
5193274254cdSchristos return sock;
5194a7c91847Schristos }
5195a7c91847Schristos
5196a7c91847Schristos #endif /* defined AUTH_CLIENT_SUPPORT || defined HAVE_KERBEROS
5197a7c91847Schristos * || defined HAVE_GSSAPI
5198a7c91847Schristos */
5199a7c91847Schristos
5200a7c91847Schristos #endif /* CLIENT_SUPPORT */
5201