also add -fwrapv to CFLAGS (addresses #698908)
[alioth/cvs.git] / src / server.c
1 /* This program is free software; you can redistribute it and/or modify
2    it under the terms of the GNU General Public License as published by
3    the Free Software Foundation; either version 2, or (at your option)
4    any later version.
5
6    This program is distributed in the hope that it will be useful,
7    but WITHOUT ANY WARRANTY; without even the implied warranty of
8    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9    GNU General Public License for more details.  */
10
11 #include "cvs.h"
12
13 /* CVS */
14 #include "edit.h"
15 #include "fileattr.h"
16 #include "watch.h"
17
18 /* GNULIB */
19 #include "buffer.h"
20 #include "getline.h"
21 #include "getnline.h"
22
23 int server_active = 0;
24
25 #if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
26
27 # include "log-buffer.h"
28 # include "ms-buffer.h"
29 #endif  /* defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) */
30
31 #if defined (HAVE_GSSAPI) && defined (SERVER_SUPPORT)
32 # include "canon-host.h"
33 # include "gssapi-client.h"
34
35 /* This stuff isn't included solely with SERVER_SUPPORT since some of these
36  * functions (encryption & the like) get compiled with or without server
37  * support.
38  *
39  * FIXME - They should be in a different file.
40  */
41 /* We use Kerberos 5 routines to map the GSSAPI credential to a user
42    name.  */
43 # include <krb5.h>
44
45 static void gserver_authenticate_connection (void);
46
47 /* Whether we are already wrapping GSSAPI communication.  */
48 static int cvs_gssapi_wrapping;
49
50 #endif  /* defined (HAVE_GSSAPI) && defined (SERVER_SUPPORT) */
51
52 #ifdef SERVER_SUPPORT
53
54 extern char *server_hostname;
55
56 # if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI)
57 #   include <sys/socket.h>
58 # endif
59
60 # ifdef HAVE_SYSLOG_H
61 #   include <syslog.h>
62 #   ifndef LOG_DAEMON   /* for ancient syslogs */
63 #     define LOG_DAEMON 0
64 #   endif
65 # endif /* HAVE_SYSLOG_H */
66
67 # ifdef HAVE_KERBEROS
68 #   include <netinet/in.h>
69 #   include <krb.h>
70 #   ifndef HAVE_KRB_GET_ERR_TEXT
71 #     define krb_get_err_text(status) krb_err_txt[status]
72 #   endif
73
74 /* Information we need if we are going to use Kerberos encryption.  */
75 static C_Block kblock;
76 static Key_schedule sched;
77
78 # endif /* HAVE_KERBEROS */
79
80 /* for select */
81 # include "xselect.h"
82
83 # ifndef O_NONBLOCK
84 #   define O_NONBLOCK O_NDELAY
85 # endif
86
87 /* For initgroups().  */
88 # if HAVE_INITGROUPS
89 #   include <grp.h>
90 # endif /* HAVE_INITGROUPS */
91
92 # ifdef AUTH_SERVER_SUPPORT
93
94 #   ifdef HAVE_GETSPNAM
95 #     include <shadow.h>
96 #   endif
97
98 /* The cvs username sent by the client, which might or might not be
99    the same as the system username the server eventually switches to
100    run as.  CVS_Username gets set iff password authentication is
101    successful. */
102 char *CVS_Username = NULL;
103
104 /* Used to check that same repos is transmitted in pserver auth and in
105    later CVS protocol.  Exported because root.c also uses. */
106 static char *Pserver_Repos = NULL;
107
108 # endif /* AUTH_SERVER_SUPPORT */
109
110 # ifdef HAVE_PAM
111 #   if defined(HAVE_SECURITY_PAM_APPL_H)
112 #     include <security/pam_appl.h>
113 #   elif defined(HAVE_PAM_PAM_APPL_H)
114 #     include <pam/pam_appl.h>
115 #   endif
116
117 static pam_handle_t *pamh = NULL;
118
119 static char *pam_username;
120 static char *pam_password;
121 # endif /* HAVE_PAM */
122
123
124
125 /* While processing requests, this buffer accumulates data to be sent to
126    the client, and then once we are in do_cvs_command, we use it
127    for all the data to be sent.  */
128 static struct buffer *buf_to_net;
129
130 /* This buffer is used to read input from the client.  */
131 static struct buffer *buf_from_net;
132
133
134
135 # ifdef PROXY_SUPPORT
136 /* These are the secondary log buffers so that we can disable them after
137  * creation, when it is determined that they are unneeded, regardless of what
138  * other filters have been prepended to the buffer chain.
139  */
140 static struct buffer *proxy_log;
141 static struct buffer *proxy_log_out;
142
143 /* Set while we are reprocessing a log so that we can avoid sending responses
144  * to some requests twice.
145  */
146 static bool reprocessing;
147 # endif /* PROXY_SUPPORT */
148
149
150
151 /* Arguments storage for `Argument' & `Argumentx' requests.  */
152 static int argument_count;
153 static char **argument_vector;
154 static int argument_vector_size;
155
156 /*
157  * This is where we stash stuff we are going to use.  Format string
158  * which expects a single directory within it, starting with a slash.
159  */
160 static char *server_temp_dir;
161
162 /* This is the original value of server_temp_dir, before any possible
163    changes inserted by serve_max_dotdot.  */
164 static char *orig_server_temp_dir;
165
166 /* Nonzero if we should keep the temp directory around after we exit.  */
167 static int dont_delete_temp;
168
169 static void server_write_entries (void);
170
171 cvsroot_t *referrer;
172
173
174
175 /* Populate all of the directories between BASE_DIR and its relative
176    subdirectory DIR with CVSADM directories.  Return 0 for success or
177    errno value.  */
178 static int
179 create_adm_p (char *base_dir, char *dir)
180 {
181     char *dir_where_cvsadm_lives, *dir_to_register, *p, *tmp;
182     int retval, done;
183     FILE *f;
184
185     if (strcmp (dir, ".") == 0)
186         return 0;                       /* nothing to do */
187
188     /* Allocate some space for our directory-munging string. */
189     p = xmalloc (strlen (dir) + 1);
190     if (p == NULL)
191         return ENOMEM;
192
193     dir_where_cvsadm_lives = xmalloc (strlen (base_dir) + strlen (dir) + 100);
194     if (dir_where_cvsadm_lives == NULL)
195     {
196         free (p);
197         return ENOMEM;
198     }
199
200     /* Allocate some space for the temporary string in which we will
201        construct filenames. */
202     tmp = xmalloc (strlen (base_dir) + strlen (dir) + 100);
203     if (tmp == NULL)
204     {
205         free (p);
206         free (dir_where_cvsadm_lives);
207         return ENOMEM;
208     }
209
210
211     /* We make several passes through this loop.  On the first pass,
212        we simply create the CVSADM directory in the deepest directory.
213        For each subsequent pass, we try to remove the last path
214        element from DIR, create the CVSADM directory in the remaining
215        pathname, and register the subdirectory in the newly created
216        CVSADM directory. */
217
218     retval = done = 0;
219
220     strcpy (p, dir);
221     strcpy (dir_where_cvsadm_lives, base_dir);
222     strcat (dir_where_cvsadm_lives, "/");
223     strcat (dir_where_cvsadm_lives, p);
224     dir_to_register = NULL;
225
226     while (1)
227     {
228         /* Create CVSADM. */
229         (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM);
230         if ((CVS_MKDIR (tmp, 0777) < 0) && (errno != EEXIST))
231         {
232             retval = errno;
233             goto finish;
234         }
235
236         /* Create CVSADM_REP. */
237         (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM_REP);
238         if (! isfile (tmp))
239         {
240             /* Use Emptydir as the placeholder until the client sends
241                us the real value.  This code is similar to checkout.c
242                (emptydir_name), but the code below returns errors
243                differently.  */
244
245             char *empty;
246             empty = xmalloc (strlen (current_parsed_root->directory)
247                             + sizeof (CVSROOTADM)
248                             + sizeof (CVSNULLREPOS)
249                             + 3);
250             if (! empty)
251             {
252                 retval = ENOMEM;
253                 goto finish;
254             }
255
256             /* Create the directory name. */
257             (void) sprintf (empty, "%s/%s/%s", current_parsed_root->directory,
258                             CVSROOTADM, CVSNULLREPOS);
259
260             /* Create the directory if it doesn't exist. */
261             if (! isfile (empty))
262             {
263                 mode_t omask;
264                 omask = umask (cvsumask);
265                 if (CVS_MKDIR (empty, 0777) < 0)
266                 {
267                     retval = errno;
268                     free (empty);
269                     goto finish;
270                 }
271                 (void) umask (omask);
272             }
273
274             f = CVS_FOPEN (tmp, "w");
275             if (f == NULL)
276             {
277                 retval = errno;
278                 free (empty);
279                 goto finish;
280             }
281             /* Write the directory name to CVSADM_REP. */
282             if (fprintf (f, "%s\n", empty) < 0)
283             {
284                 retval = errno;
285                 fclose (f);
286                 free (empty);
287                 goto finish;
288             }
289             if (fclose (f) == EOF)
290             {
291                 retval = errno;
292                 free (empty);
293                 goto finish;
294             }
295
296             /* Clean up after ourselves. */
297             free (empty);
298         }
299
300         /* Create CVSADM_ENT.  We open in append mode because we
301            don't want to clobber an existing Entries file.  */
302         (void) sprintf (tmp, "%s/%s", dir_where_cvsadm_lives, CVSADM_ENT);
303         f = CVS_FOPEN (tmp, "a");
304         if (f == NULL)
305         {
306             retval = errno;
307             goto finish;
308         }
309         if (fclose (f) == EOF)
310         {
311             retval = errno;
312             goto finish;
313         }
314
315         if (dir_to_register != NULL)
316         {
317             /* FIXME: Yes, this results in duplicate entries in the
318                Entries.Log file, but it doesn't currently matter.  We
319                might need to change this later on to make sure that we
320                only write one entry.  */
321
322             Subdir_Register (NULL, dir_where_cvsadm_lives, dir_to_register);
323         }
324
325         if (done)
326             break;
327
328         dir_to_register = strrchr (p, '/');
329         if (dir_to_register == NULL)
330         {
331             dir_to_register = p;
332             strcpy (dir_where_cvsadm_lives, base_dir);
333             done = 1;
334         }
335         else
336         {
337             *dir_to_register = '\0';
338             dir_to_register++;
339             strcpy (dir_where_cvsadm_lives, base_dir);
340             strcat (dir_where_cvsadm_lives, "/");
341             strcat (dir_where_cvsadm_lives, p);
342         }
343     }
344
345   finish:
346     free (tmp);
347     free (dir_where_cvsadm_lives);
348     free (p);
349     return retval;
350 }
351
352
353
354 /*
355  * Make directory DIR, including all intermediate directories if necessary.
356  * Returns 0 for success or errno code.
357  */
358 static int
359 mkdir_p (char *dir)
360 {
361     char *p;
362     char *q = xmalloc (strlen (dir) + 1);
363     int retval;
364
365     if (q == NULL)
366         return ENOMEM;
367
368     retval = 0;
369
370     /*
371      * Skip over leading slash if present.  We won't bother to try to
372      * make '/'.
373      */
374     p = dir + 1;
375     while (1)
376     {
377         while (*p != '/' && *p != '\0')
378             ++p;
379         if (*p == '/')
380         {
381             strncpy (q, dir, p - dir);
382             q[p - dir] = '\0';
383             if (q[p - dir - 1] != '/'  &&  CVS_MKDIR (q, 0777) < 0)
384             {
385                 int saved_errno = errno;
386
387                 if (saved_errno != EEXIST
388                     && ((saved_errno != EACCES && saved_errno != EROFS)
389                         || !isdir (q)))
390                 {
391                     retval = saved_errno;
392                     goto done;
393                 }
394             }
395             ++p;
396         }
397         else
398         {
399             if (CVS_MKDIR (dir, 0777) < 0)
400                 retval = errno;
401             goto done;
402         }
403     }
404   done:
405     free (q);
406     return retval;
407 }
408
409
410
411 /*
412  * Print the error response for error code STATUS.  The caller is
413  * reponsible for making sure we get back to the command loop without
414  * any further output occuring.
415  * Must be called only in contexts where it is OK to send output.
416  */
417 static void
418 print_error (int status)
419 {
420     char *msg;
421     char tmpstr[80];
422
423     buf_output0 (buf_to_net, "error  ");
424     msg = strerror (status);
425     if (msg == NULL)
426     {
427        sprintf (tmpstr, "unknown error %d", status);
428        msg = tmpstr;
429     }
430     buf_output0 (buf_to_net, msg);
431     buf_append_char (buf_to_net, '\n');
432
433     buf_flush (buf_to_net, 0);
434 }
435
436
437
438 static int pending_error;
439 /*
440  * Malloc'd text for pending error.  Each line must start with "E ".  The
441  * last line should not end with a newline.
442  */
443 static char *pending_error_text;
444 static char *pending_warning_text;
445
446 /* If an error is pending, print it and return 1.  If not, return 0.
447    Also prints pending warnings, but this does not affect the return value.
448    Must be called only in contexts where it is OK to send output.  */
449 static int
450 print_pending_error (void)
451 {
452     /* Check this case first since it usually means we are out of memory and
453      * the buffer output routines might try and allocate memory.
454      */
455     if (!pending_error_text && pending_error)
456     {
457         print_error (pending_error);
458         pending_error = 0;
459         return 1;
460     }
461
462     if (pending_warning_text)
463     {
464         buf_output0 (buf_to_net, pending_warning_text);
465         buf_append_char (buf_to_net, '\n');
466         buf_flush (buf_to_net, 0);
467
468         free (pending_warning_text);
469         pending_warning_text = NULL;
470     }
471
472     if (pending_error_text)
473     {
474         buf_output0 (buf_to_net, pending_error_text);
475         buf_append_char (buf_to_net, '\n');
476         if (pending_error)
477             print_error (pending_error);
478         else
479             buf_output0 (buf_to_net, "error  \n");
480
481         buf_flush (buf_to_net, 0);
482
483         pending_error = 0;
484         free (pending_error_text);
485         pending_error_text = NULL;
486         return 1;
487     }
488
489     return 0;
490 }
491
492
493
494 /* Is an error pending?  */
495 # define error_pending() (pending_error || pending_error_text)
496 # define warning_pending() (pending_warning_text)
497
498 /* Allocate SIZE bytes for pending_error_text and return nonzero
499    if we could do it.  */
500 static inline int
501 alloc_pending_internal (char **dest, size_t size)
502 {
503     *dest = malloc (size);
504     if (!*dest)
505     {
506         pending_error = ENOMEM;
507         return 0;
508     }
509     return 1;
510 }
511
512
513
514 /* Allocate SIZE bytes for pending_error_text and return nonzero
515    if we could do it.  */
516 static int
517 alloc_pending (size_t size)
518 {
519     if (error_pending ())
520         /* Probably alloc_pending callers will have already checked for
521            this case.  But we might as well handle it if they don't, I
522            guess.  */
523         return 0;
524     return alloc_pending_internal (&pending_error_text, size);
525 }
526
527
528
529 /* Allocate SIZE bytes for pending_error_text and return nonzero
530    if we could do it.  */
531 static int
532 alloc_pending_warning (size_t size)
533 {
534     if (warning_pending ())
535         /* Warnings can be lost here.  */
536         return 0;
537     return alloc_pending_internal (&pending_warning_text, size);
538 }
539
540
541
542 static int
543 supported_response (char *name)
544 {
545     struct response *rs;
546
547     for (rs = responses; rs->name != NULL; ++rs)
548         if (strcmp (rs->name, name) == 0)
549             return rs->status == rs_supported;
550     error (1, 0, "internal error: testing support for unknown response?");
551     /* NOTREACHED */
552     return 0;
553 }
554
555
556
557 /*
558  * Return true if we need to relay write requests to a primary server
559  * and false otherwise.
560  *
561  * NOTES
562  *
563  *   - primarily handles :ext: method as this seems most likely to be used in
564  *     practice.
565  *
566  *   - :fork: method is handled for testing.
567  *
568  *   - Could handle pserver too, but would have to store the password
569  *     the client sent us.
570  *
571  *
572  * GLOBALS
573  *   config->PrimaryServer
574  *                        The parsed setting from CVSROOT/config, if any, or
575  *                        NULL, otherwise.
576  *   current_parsed_root  The current repository.
577  *
578  * RETURNS
579  *   true                 If this server is configured as a secondary server.
580  *   false                Otherwise.
581  */
582 static inline bool
583 isProxyServer (void)
584 {
585     assert (current_parsed_root);
586
587     /***
588      *** The following is done as a series of if/return combinations an an
589      *** optimization.
590      ***/
591
592     /* If there is no primary server defined in CVSROOT/config, then we can't
593      * be a secondary.
594      */
595     if (!config || !config->PrimaryServer) return false;
596
597     /* The directory must not match for all methods.  */
598     if (!isSamePath (config->PrimaryServer->directory,
599                      current_parsed_root->directory))
600         return true;
601
602     /* Only the directory is important for fork.  */
603     if (config->PrimaryServer->method == fork_method)
604         return false;
605
606     /* Must be :ext: method, then.  This is enforced when CVSROOT/config is
607      * parsed.
608      */
609     assert (config->PrimaryServer->isremote);
610
611     if (isThisHost (config->PrimaryServer->hostname))
612         return false;
613
614     return true;
615 }
616
617
618
619 static void
620 serve_valid_responses (char *arg)
621 {
622     char *p = arg;
623     char *q;
624     struct response *rs;
625
626 # ifdef PROXY_SUPPORT
627     /* Process this in the first pass since the data it gathers can be used
628      * prior to a `Root' request.
629      */
630     if (reprocessing) return;
631 # endif /* PROXY_SUPPORT */
632
633     do
634     {
635         q = strchr (p, ' ');
636         if (q != NULL)
637             *q++ = '\0';
638         for (rs = responses; rs->name != NULL; ++rs)
639         {
640             if (strcmp (rs->name, p) == 0)
641                 break;
642         }
643         if (rs->name == NULL)
644             /*
645              * It is a response we have never heard of (and thus never
646              * will want to use).  So don't worry about it.
647              */
648             ;
649         else
650             rs->status = rs_supported;
651         p = q;
652     } while (q != NULL);
653     for (rs = responses; rs->name != NULL; ++rs)
654     {
655         if (rs->status == rs_essential)
656         {
657             buf_output0 (buf_to_net, "E response `");
658             buf_output0 (buf_to_net, rs->name);
659             buf_output0 (buf_to_net, "' not supported by client\nerror  \n");
660
661             /* FIXME: This call to buf_flush could conceivably
662                cause deadlock, as noted in server_cleanup.  */
663             buf_flush (buf_to_net, 1);
664
665             exit (EXIT_FAILURE);
666         }
667         else if (rs->status == rs_optional)
668             rs->status = rs_not_supported;
669     }
670 }
671
672
673
674 /*
675  * Process IDs of the subprocess, or negative if that subprocess
676  * does not exist.
677  */
678 static pid_t command_pid;
679
680 static void
681 outbuf_memory_error (struct buffer *buf)
682 {
683     static const char msg[] = "E Fatal server error\n\
684 error ENOMEM Virtual memory exhausted.\n";
685     if (command_pid > 0)
686         kill (command_pid, SIGTERM);
687
688     /*
689      * We have arranged things so that printing this now either will
690      * be valid, or the "E fatal error" line will get glommed onto the
691      * end of an existing "E" or "M" response.
692      */
693
694     /* If this gives an error, not much we could do.  syslog() it?  */
695     write (STDOUT_FILENO, msg, sizeof (msg) - 1);
696 # ifdef HAVE_SYSLOG_H
697     syslog (LOG_DAEMON | LOG_ERR, "virtual memory exhausted");
698 # endif /* HAVE_SYSLOG_H */
699     exit (EXIT_FAILURE);
700 }
701
702
703
704 static void
705 input_memory_error (struct buffer *buf)
706 {
707     outbuf_memory_error (buf);
708 }
709
710
711
712 # ifdef PROXY_SUPPORT
713 /* This function rewinds the net connection using the write proxy log file.
714  *
715  * GLOBALS
716  *   proxy_log  The buffer object containing the write proxy log.
717  *
718  * RETURNS
719  *   Nothing.
720  */
721 static void
722 rewind_buf_from_net (void)
723 {
724     struct buffer *log;
725
726     assert (proxy_log);
727
728     /* Free the arguments since we processed some of them in the first pass.
729      */
730     {
731         /* argument_vector[0] is a dummy argument, we don't mess with
732          * it.
733          */
734         char **cp;
735         for (cp = argument_vector + 1;
736              cp < argument_vector + argument_count;
737              ++cp)
738             free (*cp);
739
740         argument_count = 1;
741     }
742
743     log = log_buffer_rewind (proxy_log);
744     proxy_log = NULL;
745     /* Dispose of any read but unused data in the net buffer since it will
746      * already be in the log.
747      */
748     buf_free_data (buf_from_net);
749     buf_from_net = ms_buffer_initialize (outbuf_memory_error, log,
750                                          buf_from_net);
751     reprocessing = true;
752 }
753 # endif /* PROXY_SUPPORT */
754
755
756
757 char *gConfigPath;
758
759
760
761 /*
762  * This request cannot be ignored by a potential secondary since it is used to
763  * determine if we _are_ a secondary.
764  */
765 static void
766 serve_root (char *arg)
767 {
768     char *path;
769
770     TRACE (TRACE_FUNCTION, "serve_root (%s)", arg ? arg : "(null)");
771
772     /* Don't process this twice or when errors are pending.  */
773     if (error_pending()
774 # ifdef PROXY_SUPPORT
775         || reprocessing
776 # endif /* PROXY_SUPPORT */
777        ) return;
778
779     if (!ISABSOLUTE (arg))
780     {
781         if (alloc_pending (80 + strlen (arg)))
782             sprintf (pending_error_text,
783                      "E Root %s must be an absolute pathname", arg);
784         return;
785     }
786
787     /* Sending "Root" twice is invalid.
788
789        The other way to handle a duplicate Root requests would be as a
790        request to clear out all state and start over as if it was a
791        new connection.  Doing this would cause interoperability
792        headaches, so it should be a different request, if there is
793        any reason why such a feature is needed.  */
794     if (current_parsed_root != NULL)
795     {
796         if (alloc_pending (80 + strlen (arg)))
797             sprintf (pending_error_text,
798                      "E Protocol error: Duplicate Root request, for %s", arg);
799         return;
800     }
801
802     /* Set original_parsed_root here, not because it can be changed in the
803      * client Redirect sense, but so we don't have to switch in code that
804      * runs in both modes to decide which to print.
805      */
806     original_parsed_root = current_parsed_root = local_cvsroot (arg);
807
808 # ifdef AUTH_SERVER_SUPPORT
809     if (Pserver_Repos != NULL)
810     {
811         if (strcmp (Pserver_Repos, current_parsed_root->directory) != 0)
812         {
813             if (alloc_pending (80 + strlen (Pserver_Repos)
814                                + strlen (current_parsed_root->directory)))
815                 /* The explicitness is to aid people who are writing clients.
816                    I don't see how this information could help an
817                    attacker.  */
818                 sprintf (pending_error_text, "\
819 E Protocol error: Root says \"%s\" but pserver says \"%s\"",
820                          current_parsed_root->directory, Pserver_Repos);
821             return;
822         }
823     }
824 # endif
825
826     if (root_allow_used() && !root_allow_ok(arg))
827     {
828         if (alloc_pending (80 + strlen (arg)))
829             sprintf (pending_error_text,
830                      "E Bad root %s", arg);
831         return;
832     }
833
834     /* For pserver, this will already have happened, and the call will do
835        nothing.  But for rsh, we need to do it now.  */
836     config = get_root_allow_config (current_parsed_root->directory,
837                                     gConfigPath);
838
839 # ifdef PROXY_SUPPORT
840     /* At this point we have enough information to determine if we are a
841      * secondary server or not.
842      */
843     if (proxy_log && !isProxyServer ())
844     {
845         /* Else we are not a secondary server.  There is no point in
846          * reprocessing since we handle all the requests we can receive
847          * before `Root' as we receive them.  But close the logs.
848          */
849         log_buffer_closelog (proxy_log);
850         log_buffer_closelog (proxy_log_out);
851         proxy_log = NULL;
852         /*
853          * Don't need this.  We assume it when proxy_log == NULL.
854          *
855          *   proxy_log_out = NULL;
856          */
857     }
858 # endif /* PROXY_SUPPORT */
859
860     /* Now set the TMPDIR environment variable.  If it was set in the config
861      * file, we now know it.
862      */
863     push_env_temp_dir ();
864
865     /* OK, now figure out where we stash our temporary files.  */
866     {
867         char *p;
868
869         /* The code which wants to chdir into server_temp_dir is not set
870          * up to deal with it being a relative path.  So give an error
871          * for that case.
872          */
873         if (!ISABSOLUTE (get_cvs_tmp_dir ()))
874         {
875             if (alloc_pending (80 + strlen (get_cvs_tmp_dir ())))
876                 sprintf (pending_error_text,
877                          "E Value of %s for TMPDIR is not absolute",
878                          get_cvs_tmp_dir ());
879
880             /* FIXME: we would like this error to be persistent, that
881              * is, not cleared by print_pending_error.  The current client
882              * will exit as soon as it gets an error, but the protocol spec
883              * does not require a client to do so.
884              */
885         }
886         else
887         {
888             int status;
889             int i = 0;
890
891             server_temp_dir = xmalloc (strlen (get_cvs_tmp_dir ()) + 80);
892             if (!server_temp_dir)
893             {
894                 /* Strictly speaking, we're not supposed to output anything
895                  * now.  But we're about to exit(), give it a try.
896                  */
897                 printf ("E Fatal server error, aborting.\n\
898 error ENOMEM Virtual memory exhausted.\n");
899
900                 exit (EXIT_FAILURE);
901             }
902             strcpy (server_temp_dir, get_cvs_tmp_dir ());
903
904             /* Remove a trailing slash from TMPDIR if present.  */
905             p = server_temp_dir + strlen (server_temp_dir) - 1;
906             if (*p == '/')
907                 *p = '\0';
908
909             /* I wanted to use cvs-serv/PID, but then you have to worry about
910              * the permissions on the cvs-serv directory being right.  So
911              * use cvs-servPID.
912              */
913             strcat (server_temp_dir, "/cvs-serv");
914
915             p = server_temp_dir + strlen (server_temp_dir);
916             sprintf (p, "%ld", (long) getpid ());
917
918             orig_server_temp_dir = server_temp_dir;
919
920             /* Create the temporary directory, and set the mode to
921              * 700, to discourage random people from tampering with
922              * it.
923              */
924             while ((status = mkdir_p (server_temp_dir)) == EEXIST)
925             {
926                 static const char suffix[] = "abcdefghijklmnopqrstuvwxyz";
927
928                 if (i >= sizeof suffix - 1) break;
929                 if (i == 0) p = server_temp_dir + strlen (server_temp_dir);
930                 p[0] = suffix[i++];
931                 p[1] = '\0';
932             }
933             if (status)
934             {
935                 if (alloc_pending (80 + strlen (server_temp_dir)))
936                     sprintf (pending_error_text,
937                             "E can't create temporary directory %s",
938                             server_temp_dir);
939                 pending_error = status;
940             }
941 #ifndef CHMOD_BROKEN
942             else if (chmod (server_temp_dir, S_IRWXU) < 0)
943             {
944                 int save_errno = errno;
945                 if (alloc_pending (80 + strlen (server_temp_dir)))
946                     sprintf (pending_error_text,
947 "E cannot change permissions on temporary directory %s",
948                              server_temp_dir);
949                 pending_error = save_errno;
950             }
951 #endif
952             else if (CVS_CHDIR (server_temp_dir) < 0)
953             {
954                 int save_errno = errno;
955                 if (alloc_pending (80 + strlen (server_temp_dir)))
956                     sprintf (pending_error_text,
957 "E cannot change to temporary directory %s",
958                              server_temp_dir);
959                 pending_error = save_errno;
960             }
961         }
962     }
963
964     /* Now that we have a config, verify our compression level.  Since 
965      * most clients do not send Gzip-stream requests until after the root
966      * request, wait until the first request following Root to verify that
967      * compression is being used when level 0 is not allowed.
968      */
969     if (gzip_level)
970     {
971         bool forced = false;
972
973         if (gzip_level < config->MinCompressionLevel)
974         {
975             gzip_level = config->MinCompressionLevel;
976             forced = true;
977         }
978
979         if (gzip_level > config->MaxCompressionLevel)
980         {
981             gzip_level = config->MaxCompressionLevel;
982             forced = true;
983         }
984
985         if (forced && !quiet
986             && alloc_pending_warning (120 + strlen (program_name)))
987             sprintf (pending_warning_text,
988 "E %s server: Forcing compression level %d (allowed: %d <= z <= %d).",
989                      program_name, gzip_level, config->MinCompressionLevel,
990                      config->MaxCompressionLevel);
991     }
992
993     path = xmalloc (strlen (current_parsed_root->directory)
994                    + sizeof (CVSROOTADM)
995                    + 2);
996     if (path == NULL)
997     {
998         pending_error = ENOMEM;
999         return;
1000     }
1001     (void) sprintf (path, "%s/%s", current_parsed_root->directory, CVSROOTADM);
1002     if (!isaccessible (path, R_OK | X_OK))
1003     {
1004         int save_errno = errno;
1005         if (alloc_pending (80 + strlen (path)))
1006             sprintf (pending_error_text, "E Cannot access %s", path);
1007         pending_error = save_errno;
1008     }
1009     free (path);
1010
1011     setenv (CVSROOT_ENV, current_parsed_root->directory, 1);
1012 }
1013
1014
1015
1016 static int max_dotdot_limit = 0;
1017
1018 /* Is this pathname OK to recurse into when we are running as the server?
1019    If not, call error() with a fatal error.  */
1020 void
1021 server_pathname_check (char *path)
1022 {
1023     TRACE (TRACE_FUNCTION, "server_pathname_check (%s)",
1024            path ? path : "(null)");
1025
1026     /* An absolute pathname is almost surely a path on the *client* machine,
1027        and is unlikely to do us any good here.  It also is probably capable
1028        of being a security hole in the anonymous readonly case.  */
1029     if (ISABSOLUTE (path))
1030         /* Giving an error is actually kind of a cop-out, in the sense
1031            that it would be nice for "cvs co -d /foo/bar/baz" to work.
1032            A quick fix in the server would be requiring Max-dotdot of
1033            at least one if pathnames are absolute, and then putting
1034            /abs/foo/bar/baz in the temp dir beside the /d/d/d stuff.
1035            A cleaner fix in the server might be to decouple the
1036            pathnames we pass back to the client from pathnames in our
1037            temp directory (this would also probably remove the need
1038            for Max-dotdot).  A fix in the client would have the client
1039            turn it into "cd /foo/bar; cvs co -d baz" (more or less).
1040            This probably has some problems with pathnames which appear
1041            in messages.  */
1042         error ( 1, 0,
1043                 "absolute pathnames invalid for server (specified `%s')",
1044                 path );
1045     if (pathname_levels (path) > max_dotdot_limit)
1046     {
1047         /* Similar to the ISABSOLUTE case in security implications.  */
1048         error (0, 0, "protocol error: `%s' contains more leading ..", path);
1049         error (1, 0, "than the %d which Max-dotdot specified",
1050                max_dotdot_limit);
1051     }
1052 }
1053
1054
1055
1056 /* Is file or directory REPOS an absolute pathname within the
1057    current_parsed_root->directory?  If yes, return 0.  If no, set pending_error
1058    and return 1.  */
1059 static int
1060 outside_root (char *repos)
1061 {
1062     size_t repos_len = strlen (repos);
1063     size_t root_len = strlen (current_parsed_root->directory);
1064
1065     /* ISABSOLUTE (repos) should always be true, but
1066        this is a good security precaution regardless. -DRP
1067      */
1068     if (!ISABSOLUTE (repos))
1069     {
1070         if (alloc_pending (repos_len + 80))
1071             sprintf (pending_error_text, "\
1072 E protocol error: %s is not absolute", repos);
1073         return 1;
1074     }
1075
1076     if (repos_len < root_len
1077         || strncmp (current_parsed_root->directory, repos, root_len) != 0)
1078     {
1079     not_within:
1080         if (alloc_pending (strlen (current_parsed_root->directory)
1081                            + strlen (repos)
1082                            + 80))
1083             sprintf (pending_error_text, "\
1084 E protocol error: directory '%s' not within root '%s'",
1085                      repos, current_parsed_root->directory);
1086         return 1;
1087     }
1088     if (repos_len > root_len)
1089     {
1090         if (repos[root_len] != '/')
1091             goto not_within;
1092         if (pathname_levels (repos + root_len + 1) > 0)
1093             goto not_within;
1094     }
1095     return 0;
1096 }
1097
1098
1099
1100 /* Is file or directory FILE outside the current directory (that is, does
1101    it contain '/')?  If no, return 0.  If yes, set pending_error
1102    and return 1.  */
1103 static int
1104 outside_dir (char *file)
1105 {
1106     if (strchr (file, '/') != NULL)
1107     {
1108         if (alloc_pending (strlen (file)
1109                            + 80))
1110             sprintf (pending_error_text, "\
1111 E protocol error: directory '%s' not within current directory",
1112                      file);
1113         return 1;
1114     }
1115     return 0;
1116 }
1117
1118
1119
1120 /*
1121  * Add as many directories to the temp directory as the client tells us it
1122  * will use "..", so we never try to access something outside the temp
1123  * directory via "..".
1124  */
1125 static void
1126 serve_max_dotdot (char *arg)
1127 {
1128     int lim = atoi (arg);
1129     int i;
1130     char *p;
1131
1132 #ifdef PROXY_SUPPORT
1133     if (proxy_log) return;
1134 #endif /* PROXY_SUPPORT */
1135
1136     if (lim < 0 || lim > 10000)
1137         return;
1138     p = xmalloc (strlen (server_temp_dir) + 2 * lim + 10);
1139     if (p == NULL)
1140     {
1141         pending_error = ENOMEM;
1142         return;
1143     }
1144     strcpy (p, server_temp_dir);
1145     for (i = 0; i < lim; ++i)
1146         strcat (p, "/d");
1147     if (server_temp_dir != orig_server_temp_dir)
1148         free (server_temp_dir);
1149     server_temp_dir = p;
1150     max_dotdot_limit = lim;
1151 }
1152
1153
1154
1155 static char *gDirname;
1156 static char *gupdate_dir;
1157
1158 static void
1159 dirswitch (char *dir, char *repos)
1160 {
1161     int status;
1162     FILE *f;
1163     size_t dir_len;
1164
1165     TRACE (TRACE_FUNCTION, "dirswitch (%s, %s)", dir ? dir : "(null)",
1166            repos ? repos : "(null)");
1167
1168     server_write_entries ();
1169
1170     if (error_pending()) return;
1171
1172     /* Check for bad directory name.
1173
1174        FIXME: could/should unify these checks with server_pathname_check
1175        except they need to report errors differently.  */
1176     if (ISABSOLUTE (dir))
1177     {
1178         if (alloc_pending (80 + strlen (dir)))
1179             sprintf ( pending_error_text,
1180                       "E absolute pathnames invalid for server (specified `%s')",
1181                       dir);
1182         return;
1183     }
1184     if (pathname_levels (dir) > max_dotdot_limit)
1185     {
1186         if (alloc_pending (80 + strlen (dir)))
1187             sprintf (pending_error_text,
1188                      "E protocol error: `%s' has too many ..", dir);
1189         return;
1190     }
1191
1192     dir_len = strlen (dir);
1193
1194     /* Check for a trailing '/'.  This is not ISSLASH because \ in the
1195        protocol is an ordinary character, not a directory separator (of
1196        course, it is perhaps unwise to use it in directory names, but that
1197        is another issue).  */
1198     if (dir_len > 0
1199         && dir[dir_len - 1] == '/')
1200     {
1201         if (alloc_pending (80 + dir_len))
1202             sprintf (pending_error_text,
1203                      "E protocol error: invalid directory syntax in %s", dir);
1204         return;
1205     }
1206
1207     if (gDirname != NULL)
1208         free (gDirname);
1209     if (gupdate_dir != NULL)
1210         free (gupdate_dir);
1211
1212     if (!strcmp (dir, "."))
1213         gupdate_dir = xstrdup ("");
1214     else
1215         gupdate_dir = xstrdup (dir);
1216
1217     gDirname = xmalloc (strlen (server_temp_dir) + dir_len + 40);
1218     if (gDirname == NULL)
1219     {
1220         pending_error = ENOMEM;
1221         return;
1222     }
1223
1224     strcpy (gDirname, server_temp_dir);
1225     strcat (gDirname, "/");
1226     strcat (gDirname, dir);
1227
1228     status = mkdir_p (gDirname);
1229     if (status != 0
1230         && status != EEXIST)
1231     {
1232         if (alloc_pending (80 + strlen (gDirname)))
1233             sprintf (pending_error_text, "E cannot mkdir %s", gDirname);
1234         pending_error = status;
1235         return;
1236     }
1237
1238     /* We need to create adm directories in all path elements because
1239        we want the server to descend them, even if the client hasn't
1240        sent the appropriate "Argument xxx" command to match the
1241        already-sent "Directory xxx" command.  See recurse.c
1242        (start_recursion) for a big discussion of this.  */
1243
1244     status = create_adm_p (server_temp_dir, dir);
1245     if (status != 0)
1246     {
1247         if (alloc_pending (80 + strlen (gDirname)))
1248             sprintf (pending_error_text, "E cannot create_adm_p %s", gDirname);
1249         pending_error = status;
1250         return;
1251     }
1252
1253     if ( CVS_CHDIR (gDirname) < 0)
1254     {
1255         int save_errno = errno;
1256         if (alloc_pending (80 + strlen (gDirname)))
1257             sprintf (pending_error_text, "E cannot change to %s", gDirname);
1258         pending_error = save_errno;
1259         return;
1260     }
1261     /*
1262      * This is pretty much like calling Create_Admin, but Create_Admin doesn't
1263      * report errors in the right way for us.
1264      */
1265     if ((CVS_MKDIR (CVSADM, 0777) < 0) && (errno != EEXIST))
1266     {
1267         int save_errno = errno;
1268         if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM)))
1269             sprintf (pending_error_text,
1270                      "E cannot mkdir %s/%s", gDirname, CVSADM);
1271         pending_error = save_errno;
1272         return;
1273     }
1274
1275     /* The following will overwrite the contents of CVSADM_REP.  This
1276        is the correct behavior -- mkdir_p may have written a
1277        placeholder value to this file and we need to insert the
1278        correct value. */
1279
1280     f = CVS_FOPEN (CVSADM_REP, "w");
1281     if (f == NULL)
1282     {
1283         int save_errno = errno;
1284         if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1285             sprintf (pending_error_text,
1286                      "E cannot open %s/%s", gDirname, CVSADM_REP);
1287         pending_error = save_errno;
1288         return;
1289     }
1290     if (fprintf (f, "%s", repos) < 0)
1291     {
1292         int save_errno = errno;
1293         if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1294             sprintf (pending_error_text,
1295                      "E error writing %s/%s", gDirname, CVSADM_REP);
1296         pending_error = save_errno;
1297         fclose (f);
1298         return;
1299     }
1300     /* Non-remote CVS handles a module representing the entire tree
1301        (e.g., an entry like ``world -a .'') by putting /. at the end
1302        of the Repository file, so we do the same.  */
1303     if (strcmp (dir, ".") == 0
1304         && current_parsed_root != NULL
1305         && current_parsed_root->directory != NULL
1306         && strcmp (current_parsed_root->directory, repos) == 0)
1307     {
1308         if (fprintf (f, "/.") < 0)
1309         {
1310             int save_errno = errno;
1311             if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1312                 sprintf (pending_error_text,
1313                          "E error writing %s/%s", gDirname, CVSADM_REP);
1314             pending_error = save_errno;
1315             fclose (f);
1316             return;
1317         }
1318     }
1319     if (fprintf (f, "\n") < 0)
1320     {
1321         int save_errno = errno;
1322         if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1323             sprintf (pending_error_text,
1324                      "E error writing %s/%s", gDirname, CVSADM_REP);
1325         pending_error = save_errno;
1326         fclose (f);
1327         return;
1328     }
1329     if (fclose (f) == EOF)
1330     {
1331         int save_errno = errno;
1332         if (alloc_pending (80 + strlen (gDirname) + strlen (CVSADM_REP)))
1333             sprintf (pending_error_text,
1334                      "E error closing %s/%s", gDirname, CVSADM_REP);
1335         pending_error = save_errno;
1336         return;
1337     }
1338     /* We open in append mode because we don't want to clobber an
1339        existing Entries file.  */
1340     f = CVS_FOPEN (CVSADM_ENT, "a");
1341     if (f == NULL)
1342     {
1343         int save_errno = errno;
1344         if (alloc_pending (80 + strlen (CVSADM_ENT)))
1345             sprintf (pending_error_text, "E cannot open %s", CVSADM_ENT);
1346         pending_error = save_errno;
1347         return;
1348     }
1349     if (fclose (f) == EOF)
1350     {
1351         int save_errno = errno;
1352         if (alloc_pending (80 + strlen (CVSADM_ENT)))
1353             sprintf (pending_error_text, "E cannot close %s", CVSADM_ENT);
1354         pending_error = save_errno;
1355         return;
1356     }
1357 }
1358
1359
1360
1361 static void
1362 serve_repository (char *arg)
1363 {
1364 # ifdef PROXY_SUPPORT
1365     assert (!proxy_log);
1366 # endif /* PROXY_SUPPORT */
1367
1368     if (alloc_pending (80))
1369         strcpy (pending_error_text,
1370                 "E Repository request is obsolete; aborted");
1371     return;
1372 }
1373
1374
1375
1376 static void
1377 serve_directory (char *arg)
1378 {
1379     int status;
1380     char *repos;
1381
1382     TRACE (TRACE_FUNCTION, "serve_directory (%s)", arg ? arg : "(null)");
1383
1384
1385     /* The data needs to be read into the secondary log regardless, but
1386      * processing of anything other than errors is skipped until later.
1387      */
1388     status = buf_read_line (buf_from_net, &repos, NULL);
1389     if (status == 0)
1390     {
1391         if (!ISABSOLUTE (repos))
1392         {
1393             /* Make absolute.
1394              *
1395              * FIXME: This is kinda hacky - we should probably only ever store
1396              * and pass SHORT_REPOS (perhaps with the occassional exception
1397              * for optimizations, but many, many functions end up
1398              * deconstructing REPOS to gain SHORT_REPOS anyhow) - the
1399              * CVSROOT portion of REPOS is redundant with
1400              * current_parsed_root->directory - but since this is the way
1401              * things have always been done, changing this will likely involve
1402              * a major overhaul.
1403              */
1404             char *short_repos;
1405
1406             short_repos = repos;
1407             repos = Xasprintf ("%s/%s",
1408                               current_parsed_root->directory, short_repos);
1409             free (short_repos);
1410         }
1411         else
1412             repos = xstrdup (primary_root_translate (repos));
1413
1414         if (
1415 # ifdef PROXY_SUPPORT
1416             !proxy_log &&
1417 # endif /* PROXY_SUPPORT */
1418             !outside_root (repos))
1419             dirswitch (arg, repos);
1420         free (repos);
1421     }
1422     else if (status == -2)
1423     {
1424         pending_error = ENOMEM;
1425     }
1426     else if (status != 0)
1427     {
1428         pending_error_text = xmalloc (80 + strlen (arg));
1429         if (pending_error_text == NULL)
1430         {
1431             pending_error = ENOMEM;
1432         }
1433         else if (status == -1)
1434         {
1435             sprintf (pending_error_text,
1436                      "E end of file reading mode for %s", arg);
1437         }
1438         else
1439         {
1440             sprintf (pending_error_text,
1441                      "E error reading mode for %s", arg);
1442             pending_error = status;
1443         }
1444     }
1445 }
1446
1447
1448
1449 static void
1450 serve_static_directory (char *arg)
1451 {
1452     FILE *f;
1453
1454     if (error_pending ()
1455 # ifdef PROXY_SUPPORT
1456         || proxy_log
1457 # endif /* PROXY_SUPPORT */
1458        ) return;
1459
1460     f = CVS_FOPEN (CVSADM_ENTSTAT, "w+");
1461     if (f == NULL)
1462     {
1463         int save_errno = errno;
1464         if (alloc_pending (80 + strlen (CVSADM_ENTSTAT)))
1465             sprintf (pending_error_text, "E cannot open %s", CVSADM_ENTSTAT);
1466         pending_error = save_errno;
1467         return;
1468     }
1469     if (fclose (f) == EOF)
1470     {
1471         int save_errno = errno;
1472         if (alloc_pending (80 + strlen (CVSADM_ENTSTAT)))
1473             sprintf (pending_error_text, "E cannot close %s", CVSADM_ENTSTAT);
1474         pending_error = save_errno;
1475         return;
1476     }
1477 }
1478
1479
1480
1481 static void
1482 serve_sticky (char *arg)
1483 {
1484     FILE *f;
1485
1486     if (error_pending ()
1487 # ifdef PROXY_SUPPORT
1488         || proxy_log
1489 # endif /* PROXY_SUPPORT */
1490        ) return;
1491
1492     f = CVS_FOPEN (CVSADM_TAG, "w+");
1493     if (f == NULL)
1494     {
1495         int save_errno = errno;
1496         if (alloc_pending (80 + strlen (CVSADM_TAG)))
1497             sprintf (pending_error_text, "E cannot open %s", CVSADM_TAG);
1498         pending_error = save_errno;
1499         return;
1500     }
1501     if (fprintf (f, "%s\n", arg) < 0)
1502     {
1503         int save_errno = errno;
1504         if (alloc_pending (80 + strlen (CVSADM_TAG)))
1505             sprintf (pending_error_text, "E cannot write to %s", CVSADM_TAG);
1506         pending_error = save_errno;
1507         return;
1508     }
1509     if (fclose (f) == EOF)
1510     {
1511         int save_errno = errno;
1512         if (alloc_pending (80 + strlen (CVSADM_TAG)))
1513             sprintf (pending_error_text, "E cannot close %s", CVSADM_TAG);
1514         pending_error = save_errno;
1515         return;
1516     }
1517 }
1518
1519
1520
1521 /*
1522  * Read SIZE bytes from buf_from_net, write them to FILE.
1523  *
1524  * Currently this isn't really used for receiving parts of a file --
1525  * the file is still sent over in one chunk.  But if/when we get
1526  * spiffy in-process gzip support working, perhaps the compressed
1527  * pieces could be sent over as they're ready, if the network is fast
1528  * enough.  Or something.
1529  */
1530 static void
1531 receive_partial_file (size_t size, int file)
1532 {
1533     while (size > 0)
1534     {
1535         int status;
1536         size_t nread;
1537         char *data;
1538
1539         status = buf_read_data (buf_from_net, size, &data, &nread);
1540         if (status != 0)
1541         {
1542             if (status == -2)
1543                 pending_error = ENOMEM;
1544             else
1545             {
1546                 pending_error_text = xmalloc (80);
1547                 if (pending_error_text == NULL)
1548                     pending_error = ENOMEM;
1549                 else if (status == -1)
1550                 {
1551                     sprintf (pending_error_text,
1552                              "E premature end of file from client");
1553                     pending_error = 0;
1554                 }
1555                 else
1556                 {
1557                     sprintf (pending_error_text,
1558                              "E error reading from client");
1559                     pending_error = status;
1560                 }
1561             }
1562             return;
1563         }
1564
1565         size -= nread;
1566
1567         while (nread > 0)
1568         {
1569             ssize_t nwrote;
1570
1571             nwrote = write (file, data, nread);
1572             if (nwrote < 0)
1573             {
1574                 int save_errno = errno;
1575                 if (alloc_pending (40))
1576                     strcpy (pending_error_text, "E unable to write");
1577                 pending_error = save_errno;
1578
1579                 /* Read and discard the file data.  */
1580                 while (size > 0)
1581                 {
1582                     int status;
1583                     size_t nread;
1584                     char *data;
1585
1586                     status = buf_read_data (buf_from_net, size, &data, &nread);
1587                     if (status != 0)
1588                         return;
1589                     size -= nread;
1590                 }
1591
1592                 return;
1593             }
1594             nread -= nwrote;
1595             data += nwrote;
1596         }
1597     }
1598 }
1599
1600
1601
1602 /* Receive SIZE bytes, write to filename FILE.  */
1603 static void
1604 receive_file (size_t size, char *file, int gzipped)
1605 {
1606     int fd;
1607     char *arg = file;
1608
1609     /* Write the file.  */
1610     fd = CVS_OPEN (arg, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1611     if (fd < 0)
1612     {
1613         int save_errno = errno;
1614         if (alloc_pending (40 + strlen (arg)))
1615             sprintf (pending_error_text, "E cannot open %s", arg);
1616         pending_error = save_errno;
1617         return;
1618     }
1619
1620     if (gzipped)
1621     {
1622         /* Using gunzip_and_write isn't really a high-performance
1623            approach, because it keeps the whole thing in memory
1624            (contiguous memory, worse yet).  But it seems easier to
1625            code than the alternative (and less vulnerable to subtle
1626            bugs).  Given that this feature is mainly for
1627            compatibility, that is the better tradeoff.  */
1628
1629         size_t toread = size;
1630         char *filebuf;
1631         char *p;
1632
1633         filebuf = xmalloc (size);
1634         p = filebuf;
1635         /* If NULL, we still want to read the data and discard it.  */
1636
1637         while (toread > 0)
1638         {
1639             int status;
1640             size_t nread;
1641             char *data;
1642
1643             status = buf_read_data (buf_from_net, toread, &data, &nread);
1644             if (status != 0)
1645             {
1646                 if (status == -2)
1647                     pending_error = ENOMEM;
1648                 else
1649                 {
1650                     pending_error_text = xmalloc (80);
1651                     if (pending_error_text == NULL)
1652                         pending_error = ENOMEM;
1653                     else if (status == -1)
1654                     {
1655                         sprintf (pending_error_text,
1656                                  "E premature end of file from client");
1657                         pending_error = 0;
1658                     }
1659                     else
1660                     {
1661                         sprintf (pending_error_text,
1662                                  "E error reading from client");
1663                         pending_error = status;
1664                     }
1665                 }
1666                 return;
1667             }
1668
1669             toread -= nread;
1670
1671             if (filebuf != NULL)
1672             {
1673                 memcpy (p, data, nread);
1674                 p += nread;
1675             }
1676         }
1677         if (filebuf == NULL)
1678         {
1679             pending_error = ENOMEM;
1680             goto out;
1681         }
1682
1683         if (gunzip_and_write (fd, file, (unsigned char *) filebuf, size))
1684         {
1685             if (alloc_pending (80))
1686                 sprintf (pending_error_text,
1687                          "E aborting due to compression error");
1688         }
1689         free (filebuf);
1690     }
1691     else
1692         receive_partial_file (size, fd);
1693
1694     if (pending_error_text)
1695     {
1696         char *p = xrealloc (pending_error_text,
1697                            strlen (pending_error_text) + strlen (arg) + 30);
1698         if (p)
1699         {
1700             pending_error_text = p;
1701             sprintf (p + strlen (p), ", file %s", arg);
1702         }
1703         /* else original string is supposed to be unchanged */
1704     }
1705
1706  out:
1707     if (close (fd) < 0 && !error_pending ())
1708     {
1709         int save_errno = errno;
1710         if (alloc_pending (40 + strlen (arg)))
1711             sprintf (pending_error_text, "E cannot close %s", arg);
1712         pending_error = save_errno;
1713         return;
1714     }
1715 }
1716
1717
1718
1719 /* Kopt for the next file sent in Modified or Is-modified.  */
1720 static char *kopt;
1721
1722 /* Timestamp (Checkin-time) for next file sent in Modified or
1723    Is-modified.  */
1724 static int checkin_time_valid;
1725 static time_t checkin_time;
1726
1727
1728
1729 /*
1730  * Used to keep track of Entry requests.
1731  */
1732 struct an_entry {
1733     struct an_entry *next;
1734     char *entry;
1735 };
1736
1737 static struct an_entry *entries;
1738
1739 static void
1740 serve_is_modified (char *arg)
1741 {
1742     struct an_entry *p;
1743     char *name;
1744     char *cp;
1745     char *timefield;
1746     /* Have we found this file in "entries" yet.  */
1747     int found;
1748
1749     if (error_pending ()
1750 # ifdef PROXY_SUPPORT
1751         || proxy_log
1752 # endif /* PROXY_SUPPORT */
1753        ) return;
1754
1755     if (outside_dir (arg))
1756         return;
1757
1758     /* Rewrite entries file to have `M' in timestamp field.  */
1759     found = 0;
1760     for (p = entries; p != NULL; p = p->next)
1761     {
1762         name = p->entry + 1;
1763         cp = strchr (name, '/');
1764         if (cp != NULL
1765             && strlen (arg) == cp - name
1766             && strncmp (arg, name, cp - name) == 0)
1767         {
1768             if (!(timefield = strchr (cp + 1, '/')) || *++timefield == '\0')
1769             {
1770                 /* We didn't find the record separator or it is followed by
1771                  * the end of the string, so just exit.
1772                  */
1773                 if (alloc_pending (80))
1774                     sprintf (pending_error_text,
1775                              "E Malformed Entry encountered.");
1776                 return;
1777             }
1778             /* If the time field is not currently empty, then one of
1779              * serve_modified, serve_is_modified, & serve_unchanged were
1780              * already called for this file.  We would like to ignore the
1781              * reinvocation silently or, better yet, exit with an error
1782              * message, but we just avoid the copy-forward and overwrite the
1783              * value from the last invocation instead.  See the comment below
1784              * for more.
1785              */
1786             if (*timefield == '/')
1787             {
1788                 /* Copy forward one character.  Space was allocated for this
1789                  * already in serve_entry().  */
1790                 cp = timefield + strlen (timefield);
1791                 cp[1] = '\0';
1792                 while (cp > timefield)
1793                 {
1794                     *cp = cp[-1];
1795                     --cp;
1796                 }
1797
1798                 /* *timefield == '/';  */
1799             }
1800             /* If *TIMEFIELD wasn't '/' and wasn't '+', we assume that it was
1801              * because of multiple calls to Is-modified & Unchanged by the
1802              * client and just overwrite the value from the last call.
1803              * Technically, we should probably either ignore calls after the
1804              * first or send the client an error, since the client/server
1805              * protocol specification specifies that only one call to either
1806              * Is-Modified or Unchanged is allowed, but broken versions of
1807              * CVSNT (at least 2.0.34 - 2.0.41, reported fixed in 2.0.41a) and
1808              * the WinCVS & TortoiseCVS clients which depend on those broken
1809              * versions of CVSNT (WinCVS 1.3 & at least one TortoiseCVS
1810              * release) rely on this behavior.
1811              */
1812             if (*timefield != '+')
1813                 *timefield = 'M';
1814
1815             if (kopt != NULL)
1816             {
1817                 if (alloc_pending (strlen (name) + 80))
1818                     sprintf (pending_error_text,
1819                              "E protocol error: both Kopt and Entry for %s",
1820                              arg);
1821                 free (kopt);
1822                 kopt = NULL;
1823                 return;
1824             }
1825             found = 1;
1826             break;
1827         }
1828     }
1829     if (!found)
1830     {
1831         /* We got Is-modified but no Entry.  Add a dummy entry.
1832            The "D" timestamp is what makes it a dummy.  */
1833         p = xmalloc (sizeof (struct an_entry));
1834         if (p == NULL)
1835         {
1836             pending_error = ENOMEM;
1837             return;
1838         }
1839         p->entry = xmalloc (strlen (arg) + 80);
1840         if (p->entry == NULL)
1841         {
1842             pending_error = ENOMEM;
1843             free (p);
1844             return;
1845         }
1846         strcpy (p->entry, "/");
1847         strcat (p->entry, arg);
1848         strcat (p->entry, "//D/");
1849         if (kopt != NULL)
1850         {
1851             strcat (p->entry, kopt);
1852             free (kopt);
1853             kopt = NULL;
1854         }
1855         strcat (p->entry, "/");
1856         p->next = entries;
1857         entries = p;
1858     }
1859 }
1860
1861
1862
1863 static void
1864 serve_modified (char *arg)
1865 {
1866     size_t size;
1867     int read_size;
1868     int status;
1869     char *size_text;
1870     char *mode_text;
1871
1872     int gzipped = 0;
1873
1874     /*
1875      * This used to return immediately if error_pending () was true.
1876      * However, that fails, because it causes each line of the file to
1877      * be echoed back to the client as an unrecognized command.  The
1878      * client isn't reading from the socket, so eventually both
1879      * processes block trying to write to the other.  Now, we try to
1880      * read the file if we can.
1881      */
1882
1883     status = buf_read_line (buf_from_net, &mode_text, NULL);
1884     if (status != 0)
1885     {
1886         if (status == -2)
1887             pending_error = ENOMEM;
1888         else
1889         {
1890             pending_error_text = xmalloc (80 + strlen (arg));
1891             if (pending_error_text == NULL)
1892                 pending_error = ENOMEM;
1893             else
1894             {
1895                 if (status == -1)
1896                     sprintf (pending_error_text,
1897                              "E end of file reading mode for %s", arg);
1898                 else
1899                 {
1900                     sprintf (pending_error_text,
1901                              "E error reading mode for %s", arg);
1902                     pending_error = status;
1903                 }
1904             }
1905         }
1906         return;
1907     }
1908
1909     status = buf_read_line (buf_from_net, &size_text, NULL);
1910     if (status != 0)
1911     {
1912         if (status == -2)
1913             pending_error = ENOMEM;
1914         else
1915         {
1916             pending_error_text = xmalloc (80 + strlen (arg));
1917             if (pending_error_text == NULL)
1918                 pending_error = ENOMEM;
1919             else
1920             {
1921                 if (status == -1)
1922                     sprintf (pending_error_text,
1923                              "E end of file reading size for %s", arg);
1924                 else
1925                 {
1926                     sprintf (pending_error_text,
1927                              "E error reading size for %s", arg);
1928                     pending_error = status;
1929                 }
1930             }
1931         }
1932         free (mode_text);
1933         return;
1934     }
1935     if (size_text[0] == 'z')
1936     {
1937         gzipped = 1;
1938         read_size = atoi (size_text + 1);
1939     }
1940     else
1941         read_size = atoi (size_text);
1942     free (size_text);
1943
1944     if (read_size < 0 && alloc_pending (80))
1945     {
1946         sprintf (pending_error_text,
1947                  "E client sent invalid (negative) file size");
1948         return;
1949     }
1950     else
1951         size = read_size;
1952
1953     if (error_pending ())
1954     {
1955         /* Now that we know the size, read and discard the file data.  */
1956         while (size > 0)
1957         {
1958             int status;
1959             size_t nread;
1960             char *data;
1961
1962             status = buf_read_data (buf_from_net, size, &data, &nread);
1963             if (status != 0)
1964                 return;
1965             size -= nread;
1966         }
1967         free (mode_text);
1968         return;
1969     }
1970
1971     if (
1972 # ifdef PROXY_SUPPORT
1973         !proxy_log &&
1974 # endif /* PROXY_SUPPORT */
1975         outside_dir (arg))
1976     {
1977         free (mode_text);
1978         return;
1979     }
1980
1981     receive_file (size,
1982 # ifdef PROXY_SUPPORT
1983                   proxy_log ? DEVNULL :
1984 # endif /* PROXY_SUPPORT */
1985                               arg,
1986                   gzipped);
1987     if (error_pending ())
1988     {
1989         free (mode_text);
1990         return;
1991     }
1992
1993 # ifdef PROXY_SUPPORT
1994     /* We've read all the data that needed to be read if we're still logging
1995      * for a secondary.  Return.
1996      */
1997     if (proxy_log) return;
1998 # endif /* PROXY_SUPPORT */
1999
2000     if (checkin_time_valid)
2001     {
2002         struct utimbuf t;
2003
2004         memset (&t, 0, sizeof (t));
2005         t.modtime = t.actime = checkin_time;
2006         if (utime (arg, &t) < 0)
2007         {
2008             int save_errno = errno;
2009             if (alloc_pending (80 + strlen (arg)))
2010                 sprintf (pending_error_text, "E cannot utime %s", arg);
2011             pending_error = save_errno;
2012             free (mode_text);
2013             return;
2014         }
2015         checkin_time_valid = 0;
2016     }
2017
2018     {
2019         int status = change_mode (arg, mode_text, 0);
2020         free (mode_text);
2021         if (status)
2022         {
2023             if (alloc_pending (40 + strlen (arg)))
2024                 sprintf (pending_error_text,
2025                          "E cannot change mode for %s", arg);
2026             pending_error = status;
2027             return;
2028         }
2029     }
2030
2031     /* Make sure that the Entries indicate the right kopt.  We probably
2032        could do this even in the non-kopt case and, I think, save a stat()
2033        call in time_stamp_server.  But for conservatism I'm leaving the
2034        non-kopt case alone.  */
2035     if (kopt != NULL)
2036         serve_is_modified (arg);
2037 }
2038
2039
2040
2041 static void
2042 serve_enable_unchanged (char *arg)
2043 {
2044 # ifdef PROXY_SUPPORT
2045     /* Might as well skip this since this function does nothing anyhow.  If
2046      * it did do anything and could generate errors, then the line below would
2047      * be necessary since this can be processed before a `Root' request.
2048      *
2049      *     if (reprocessing) return;
2050      */
2051 # endif /* PROXY_SUPPORT */
2052 }
2053
2054
2055
2056 static void
2057 serve_unchanged (char *arg)
2058 {
2059     struct an_entry *p;
2060     char *name;
2061     char *cp;
2062     char *timefield;
2063
2064     if (error_pending ()
2065 # ifdef PROXY_SUPPORT
2066         || proxy_log
2067 # endif /* PROXY_SUPPORT */
2068        ) return;
2069
2070     if (outside_dir (arg))
2071         return;
2072
2073     /* Rewrite entries file to have `=' in timestamp field.  */
2074     for (p = entries; p != NULL; p = p->next)
2075     {
2076         name = p->entry + 1;
2077         cp = strchr (name, '/');
2078         if (cp != NULL
2079             && strlen (arg) == cp - name
2080             && strncmp (arg, name, cp - name) == 0)
2081         {
2082             if (!(timefield = strchr (cp + 1, '/')) || *++timefield == '\0')
2083             {
2084                 /* We didn't find the record separator or it is followed by
2085                  * the end of the string, so just exit.
2086                  */
2087                 if (alloc_pending (80))
2088                     sprintf (pending_error_text,
2089                              "E Malformed Entry encountered.");
2090                 return;
2091             }
2092             /* If the time field is not currently empty, then one of
2093              * serve_modified, serve_is_modified, & serve_unchanged were
2094              * already called for this file.  We would like to ignore the
2095              * reinvocation silently or, better yet, exit with an error
2096              * message, but we just avoid the copy-forward and overwrite the
2097              * value from the last invocation instead.  See the comment below
2098              * for more.
2099              */
2100             if (*timefield == '/')
2101             {
2102                 /* Copy forward one character.  Space was allocated for this
2103                  * already in serve_entry().  */
2104                 cp = timefield + strlen (timefield);
2105                 cp[1] = '\0';
2106                 while (cp > timefield)
2107                 {
2108                     *cp = cp[-1];
2109                     --cp;
2110                 }
2111
2112                 /* *timefield == '/';  */
2113             }
2114             if (*timefield != '+')
2115             {
2116                 /* '+' is a conflict marker and we don't want to mess with it
2117                  * until Version_TS catches it.
2118                  */
2119                 if (timefield[1] != '/')
2120                 {
2121                     /* Obliterate anything else in TIMEFIELD.  This is again to
2122                      * support the broken CVSNT clients mentioned below, in
2123                      * conjunction with strict timestamp string boundry
2124                      * checking in time_stamp_server() from vers_ts.c &
2125                      * file_has_conflict() from subr.c, since the broken
2126                      * clients used to send malformed timestamp fields in the
2127                      * Entry request that they then depended on the subsequent
2128                      * Unchanged request to overwrite.
2129                      */
2130                     char *d = timefield + 1;
2131                     if ((cp = strchr (d, '/')))
2132                     {
2133                         while (*cp)
2134                         {
2135                             *d++ = *cp++;
2136                         }
2137                         *d = '\0';
2138                     }
2139                 }
2140                 /* If *TIMEFIELD wasn't '/', we assume that it was because of
2141                  * multiple calls to Is-modified & Unchanged by the client and
2142                  * just overwrite the value from the last call.  Technically,
2143                  * we should probably either ignore calls after the first or
2144                  * send the client an error, since the client/server protocol
2145                  * specification specifies that only one call to either
2146                  * Is-Modified or Unchanged is allowed, but broken versions of
2147                  * CVSNT (at least 2.0.34 - 2.0.41, reported fixed in 2.0.41a)
2148                  * and the WinCVS & TortoiseCVS clients which depend on those
2149                  * broken versions of CVSNT (WinCVS 1.3 & at least one
2150                  * TortoiseCVS release) rely on this behavior.
2151                  */
2152                 *timefield = '=';
2153             }
2154             break;
2155         }
2156     }
2157 }
2158
2159
2160
2161 static void
2162 serve_entry (char *arg)
2163 {
2164     struct an_entry *p;
2165     char *cp;
2166     int i = 0;
2167
2168     if (error_pending()
2169 # ifdef PROXY_SUPPORT
2170         || proxy_log
2171 # endif /* PROXY_SUPPORT */
2172        ) return;
2173
2174     /* Verify that the entry is well-formed.  This can avoid problems later.
2175      * At the moment we only check that the Entry contains five slashes in
2176      * approximately the correct locations since some of the code makes
2177      * assumptions about this.
2178      */
2179     cp = arg;
2180     if (*cp == 'D') cp++;
2181     while (i++ < 5)
2182     {
2183         if (!cp || *cp != '/')
2184         {
2185             if (alloc_pending (80))
2186                 sprintf (pending_error_text,
2187                          "E protocol error: Malformed Entry");
2188             return;
2189         }
2190         cp = strchr (cp + 1, '/');
2191     }
2192
2193     p = xmalloc (sizeof (struct an_entry));
2194     if (p == NULL)
2195     {
2196         pending_error = ENOMEM;
2197         return;
2198     }
2199     /* Leave space for serve_unchanged to write '=' if it wants.  */
2200     cp = xmalloc (strlen (arg) + 2);
2201     if (cp == NULL)
2202     {
2203         free (p);
2204         pending_error = ENOMEM;
2205         return;
2206     }
2207     strcpy (cp, arg);
2208     p->next = entries;
2209     p->entry = cp;
2210     entries = p;
2211 }
2212
2213
2214
2215 static void
2216 serve_kopt (char *arg)
2217 {
2218     if (error_pending ()
2219 # ifdef PROXY_SUPPORT
2220         || proxy_log
2221 # endif /* PROXY_SUPPORT */
2222        )
2223         return;
2224
2225     if (kopt != NULL)
2226     {
2227         if (alloc_pending (80 + strlen (arg)))
2228             sprintf (pending_error_text,
2229                      "E protocol error: duplicate Kopt request: %s", arg);
2230         return;
2231     }
2232
2233     /* Do some sanity checks.  In particular, that it is not too long.
2234        This lets the rest of the code not worry so much about buffer
2235        overrun attacks.  Probably should call RCS_check_kflag here,
2236        but that would mean changing RCS_check_kflag to handle errors
2237        other than via exit(), fprintf(), and such.  */
2238     if (strlen (arg) > 10)
2239     {
2240         if (alloc_pending (80 + strlen (arg)))
2241             sprintf (pending_error_text,
2242                      "E protocol error: invalid Kopt request: %s", arg);
2243         return;
2244     }
2245
2246     kopt = xmalloc (strlen (arg) + 1);
2247     if (kopt == NULL)
2248     {
2249         pending_error = ENOMEM;
2250         return;
2251     }
2252     strcpy (kopt, arg);
2253 }
2254
2255
2256
2257 static void
2258 serve_checkin_time (char *arg)
2259 {
2260     struct timespec t;
2261
2262     if (error_pending ()
2263 # ifdef PROXY_SUPPORT
2264         || proxy_log
2265 # endif /* PROXY_SUPPORT */
2266        )
2267         return;
2268
2269     if (checkin_time_valid)
2270     {
2271         if (alloc_pending (80 + strlen (arg)))
2272             sprintf (pending_error_text,
2273                      "E protocol error: duplicate Checkin-time request: %s",
2274                      arg);
2275         return;
2276     }
2277
2278     if (!get_date (&t, arg, NULL))
2279     {
2280         if (alloc_pending (80 + strlen (arg)))
2281             sprintf (pending_error_text, "E cannot parse date %s", arg);
2282         return;
2283     }
2284
2285     /* Truncate any nanoseconds returned by get_date().  */
2286     checkin_time = t.tv_sec;
2287     checkin_time_valid = 1;
2288 }
2289
2290
2291
2292 static void
2293 server_write_entries (void)
2294 {
2295     FILE *f;
2296     struct an_entry *p;
2297     struct an_entry *q;
2298
2299     if (entries == NULL)
2300         return;
2301
2302     f = NULL;
2303     /* Note that we free all the entries regardless of errors.  */
2304     if (!error_pending ())
2305     {
2306         /* We open in append mode because we don't want to clobber an
2307            existing Entries file.  If we are checking out a module
2308            which explicitly lists more than one file in a particular
2309            directory, then we will wind up calling
2310            server_write_entries for each such file.  */
2311         f = CVS_FOPEN (CVSADM_ENT, "a");
2312         if (f == NULL)
2313         {
2314             int save_errno = errno;
2315             if (alloc_pending (80 + strlen (CVSADM_ENT)))
2316                 sprintf (pending_error_text, "E cannot open %s", CVSADM_ENT);
2317             pending_error = save_errno;
2318         }
2319     }
2320     for (p = entries; p != NULL;)
2321     {
2322         if (!error_pending ())
2323         {
2324             if (fprintf (f, "%s\n", p->entry) < 0)
2325             {
2326                 int save_errno = errno;
2327                 if (alloc_pending (80 + strlen(CVSADM_ENT)))
2328                     sprintf (pending_error_text,
2329                              "E cannot write to %s", CVSADM_ENT);
2330                 pending_error = save_errno;
2331             }
2332         }
2333         free (p->entry);
2334         q = p->next;
2335         free (p);
2336         p = q;
2337     }
2338     entries = NULL;
2339     if (f != NULL && fclose (f) == EOF && !error_pending ())
2340     {
2341         int save_errno = errno;
2342         if (alloc_pending (80 + strlen (CVSADM_ENT)))
2343             sprintf (pending_error_text, "E cannot close %s", CVSADM_ENT);
2344         pending_error = save_errno;
2345     }
2346 }
2347
2348
2349
2350 # ifdef PROXY_SUPPORT
2351 /*
2352  * callback proc to run a script when admin finishes.
2353  */
2354 static int
2355 prepost_proxy_proc (const char *repository, const char *filter, void *closure)
2356 {
2357     char *cmdline;
2358     bool *pre = closure;
2359
2360     /* %c = cvs_cmd_name
2361      * %I = commit ID
2362      * %p = shortrepos
2363      * %r = repository
2364      */
2365     TRACE (TRACE_FUNCTION, "prepost_proxy_proc (%s, %s, %s)", repository,
2366            filter, *pre ? "pre" : "post");
2367
2368     /*
2369      * Cast any NULL arguments as appropriate pointers as this is an
2370      * stdarg function and we need to be certain the caller gets what
2371      * is expected.
2372      */
2373     cmdline = format_cmdline (
2374 # ifdef SUPPORT_OLD_INFO_FMT_STRINGS
2375                               0, ".",
2376 # endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
2377                               filter,
2378                               "c", "s", cvs_cmd_name,
2379                               "I", "s", global_session_id,
2380                               "R", "s", referrer ? referrer->original : "NONE",
2381                               "p", "s", ".",
2382                               "r", "s", current_parsed_root->directory,
2383                               "P", "s", config->PrimaryServer->original,
2384                               (char *) NULL);
2385
2386     if (!cmdline || !strlen (cmdline))
2387     {
2388         if (cmdline) free (cmdline);
2389         if (*pre)
2390             error (0, 0, "preadmin proc resolved to the empty string!");
2391         else
2392             error (0, 0, "postadmin proc resolved to the empty string!");
2393         return 1;
2394     }
2395
2396     run_setup (cmdline);
2397
2398     free (cmdline);
2399
2400     /* FIXME - read the comment in verifymsg_proc() about why we use abs()
2401      * below() and shouldn't.
2402      */
2403     return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
2404                           RUN_NORMAL | RUN_SIGIGNORE));
2405 }
2406
2407
2408
2409 /* Become a secondary write proxy to a master server.
2410  *
2411  * This function opens the connection to the primary, dumps the secondary log
2412  * to the primary, then reads data from any available connection and writes it
2413  * to its partner:
2414  *
2415  *   buf_from_net -> buf_to_primary
2416  *   buf_from_primary -> buf_to_net
2417  *
2418  * When all "from" connections have sent EOF and all data has been sent to
2419  * "to" connections, this function closes the "to" pipes and returns.
2420  */
2421 static void
2422 become_proxy (void)
2423 {
2424     struct buffer *buf_to_primary;
2425     struct buffer *buf_from_primary;
2426
2427     /* Close the client log and open it for read.  */
2428     struct buffer *buf_clientlog = log_buffer_rewind (proxy_log_out);
2429     int status, to_primary_fd, from_primary_fd, to_net_fd, from_net_fd;
2430
2431     /* Call presecondary script.  */
2432     bool pre = true;
2433
2434             char *data;
2435             size_t thispass, got;
2436             int s;
2437             char *newdata;
2438
2439     Parse_Info (CVSROOTADM_PREPROXY, current_parsed_root->directory,
2440                 prepost_proxy_proc, PIOPT_ALL, &pre);
2441
2442     /* Open connection to primary server.  */
2443     open_connection_to_server (config->PrimaryServer, &buf_to_primary,
2444                                &buf_from_primary);
2445     setup_logfiles ("CVS_SECONDARY_LOG", &buf_to_primary, &buf_from_primary);
2446     if ((status = set_nonblock (buf_from_primary)))
2447         error (1, status, "failed to set nonblocking io from primary");
2448     if ((status = set_nonblock (buf_from_net)))
2449         error (1, status, "failed to set nonblocking io from client");
2450     if ((status = set_nonblock (buf_to_primary)))
2451         error (1, status, "failed to set nonblocking io to primary");
2452     if ((status = set_nonblock (buf_to_net)))
2453         error (1, status, "failed to set nonblocking io to client");
2454
2455     to_primary_fd = buf_get_fd (buf_to_primary);
2456     from_primary_fd = buf_get_fd (buf_from_primary);
2457     to_net_fd = buf_get_fd (buf_to_net);
2458     assert (to_primary_fd >= 0 && from_primary_fd >= 0 && to_net_fd >= 0);
2459
2460     /* Close the client log and open it for read.  */
2461     rewind_buf_from_net ();
2462
2463     while (from_primary_fd >= 0 || to_primary_fd >= 0)
2464     {
2465         fd_set readfds, writefds;
2466         int status, numfds = -1;
2467         struct timeval *timeout_ptr;
2468         struct timeval timeout;
2469         size_t toread;
2470
2471         FD_ZERO (&readfds);
2472         FD_ZERO (&writefds);
2473
2474         /* The fd for a multi-source buffer can change with any read.  */
2475         from_net_fd = buf_from_net ? buf_get_fd (buf_from_net) : -1;
2476
2477         if ((buf_from_net && !buf_empty_p (buf_from_net))
2478             || (buf_from_primary && !buf_empty_p (buf_from_primary)))
2479         {
2480             /* There is data pending so don't block if we don't find any new
2481              * data on the fds.
2482              */
2483             timeout.tv_sec = 0;
2484             timeout.tv_usec = 0;
2485             timeout_ptr = &timeout;
2486         }
2487         else
2488             /* block indefinately */
2489             timeout_ptr = NULL;
2490
2491         /* Set writefds if data is pending.  */
2492         if (to_net_fd >= 0 && !buf_empty_p (buf_to_net))
2493         {
2494             FD_SET (to_net_fd, &writefds);
2495             numfds = MAX (numfds, to_net_fd);
2496         }
2497         if (to_primary_fd >= 0 && !buf_empty_p (buf_to_primary))
2498         {
2499             FD_SET (to_primary_fd, &writefds);
2500             numfds = MAX (numfds, to_primary_fd);
2501         }
2502
2503         /* Set readfds if descriptors are still open.  */
2504         if (from_net_fd >= 0)
2505         {
2506             FD_SET (from_net_fd, &readfds);
2507             numfds = MAX (numfds, from_net_fd);
2508         }
2509         if (from_primary_fd >= 0)
2510         {
2511             FD_SET (from_primary_fd, &readfds);
2512             numfds = MAX (numfds, from_primary_fd);
2513         }
2514
2515         /* NUMFDS needs to be the highest descriptor + 1 according to the
2516          * select spec.
2517          */
2518         numfds++;
2519
2520         do {
2521             /* This used to select on exceptions too, but as far
2522                as I know there was never any reason to do that and
2523                SCO doesn't let you select on exceptions on pipes.  */
2524             numfds = select (numfds, &readfds, &writefds,
2525                              NULL, timeout_ptr);
2526             if (numfds < 0 && errno != EINTR)
2527             {
2528                 /* Sending an error to the client, possibly in the middle of a
2529                  * separate protocol message, will likely not mean much to the
2530                  * client, but it's better than nothing, I guess.
2531                  */
2532                 buf_output0 (buf_to_net, "E select failed\n");
2533                 print_error (errno);
2534                 exit (EXIT_FAILURE);
2535             }
2536         } while (numfds < 0);
2537
2538         if (numfds == 0)
2539         {
2540             FD_ZERO (&readfds);
2541             FD_ZERO (&writefds);
2542         }
2543
2544         if (to_net_fd >= 0 && FD_ISSET (to_net_fd, &writefds))
2545         {
2546             /* What should we do with errors?  syslog() them?  */
2547             buf_send_output (buf_to_net);
2548             buf_flush (buf_to_net, false);
2549         }
2550
2551         status = 0;
2552         if (from_net_fd >= 0 && (FD_ISSET (from_net_fd, &readfds)))
2553             status = buf_input_data (buf_from_net, NULL);
2554
2555         if (buf_from_net && !buf_empty_p (buf_from_net))
2556         {
2557             if (buf_to_primary)
2558                 buf_append_buffer (buf_to_primary, buf_from_net);
2559             else
2560                 /* (Sys?)log this?  */;
2561                 
2562         }
2563
2564         if (status == -1 /* EOF */)
2565         {
2566             SIG_beginCrSect();
2567             /* Need only to shut this down and set to NULL, really, in
2568              * crit sec, to ensure no double-dispose and to make sure
2569              * network pipes are closed as properly as possible, but I
2570              * don't see much optimization potential in saving values and
2571              * postponing the free.
2572              */
2573             buf_shutdown (buf_from_net);
2574             buf_free (buf_from_net);
2575             buf_from_net = NULL;
2576             /* So buf_to_primary will be closed at the end of this loop.  */
2577             from_net_fd = -1;
2578             SIG_endCrSect();
2579         }
2580         else if (status > 0 /* ERRNO */)
2581         {
2582             buf_output0 (buf_to_net,
2583                          "E buf_input_data failed reading from client\n");
2584             print_error (status);
2585             exit (EXIT_FAILURE);
2586         }
2587
2588         if (to_primary_fd >= 0 && FD_ISSET (to_primary_fd, &writefds))
2589         {
2590             /* What should we do with errors?  syslog() them?  */
2591             buf_send_output (buf_to_primary);
2592             buf_flush (buf_to_primary, false);
2593         }
2594
2595         status = 0;
2596         if (from_primary_fd >= 0 && FD_ISSET (from_primary_fd, &readfds))
2597             status = buf_input_data (buf_from_primary, &toread);
2598
2599         /* Avoid resending data from the server which we already sent to the
2600          * client.  Otherwise clients get really confused.
2601          */
2602         if (buf_clientlog
2603             && buf_from_primary && !buf_empty_p (buf_from_primary))
2604         {
2605             /* Dispose of data we already sent to the client.  */
2606             while (buf_clientlog && toread > 0)
2607             {
2608                 s = buf_read_data (buf_clientlog, toread, &data, &got);
2609                 if (s == -2)
2610                     error (1, ENOMEM, "Failed to read data.");
2611                 if (s == -1)
2612                 {
2613                     buf_shutdown (buf_clientlog);
2614                     buf_clientlog = NULL;
2615                 }
2616                 else if (s)
2617                     error (1, s, "Error reading writeproxy log.");
2618                 else
2619                 {
2620                     thispass = got;
2621                     while (thispass > 0)
2622                     {
2623                         /* No need to check for errors here since we know we
2624                          * won't read more than buf_input read into
2625                          * BUF_FROM_PRIMARY (see how TOREAD is set above).
2626                          */
2627                         buf_read_data (buf_from_primary, thispass, &newdata,
2628                                        &got);
2629                         /* Verify that we are throwing away what we think we
2630                          * are.
2631                          *
2632                          * It is valid to assume that the secondary and primary
2633                          * are closely enough in sync that this portion of the
2634                          * communication will be in sync beacuse if they were
2635                          * not, then the secondary might provide a
2636                          * valid-request string to the client which contained a
2637                          * request that the primary didn't support.  If the
2638                          * client later used the request, the primary server
2639                          * would exit anyhow.
2640                          *
2641                          * FIXME?
2642                          * An alternative approach might be to make sure that
2643                          * the secondary provides the same string as the
2644                          * primary regardless, for purposes like pointing a
2645                          * secondary at an unwitting primary, in which case it
2646                          * might be useful to have some way to override the
2647                          * valid-requests string on a secondary, but it seems
2648                          * much easier to simply sync the versions, at the
2649                          * moment.
2650                          */
2651                         if (memcmp (data, newdata, got))
2652                             error (1, 0, "Secondary out of sync with primary!");
2653                         data += got;
2654                         thispass -= got;
2655                     }
2656                     toread -= got;
2657                 }
2658             }
2659         }
2660
2661         if (buf_from_primary && !buf_empty_p (buf_from_primary))
2662         {
2663             if (buf_to_net)
2664                 buf_append_buffer (buf_to_net, buf_from_primary);
2665             else
2666                 /* (Sys?)log this?  */;
2667                 
2668         }
2669
2670         if (status == -1 /* EOF */)
2671         {
2672             buf_shutdown (buf_from_primary);
2673             buf_from_primary = NULL;
2674             from_primary_fd = -1;
2675         }
2676         else if (status > 0 /* ERRNO */)
2677         {
2678             buf_output0 (buf_to_net,
2679                          "E buf_input_data failed reading from primary\n");
2680             print_error (status);
2681             exit (EXIT_FAILURE);
2682         }
2683
2684         /* If our "source pipe" is closed and all data has been sent, avoid
2685          * selecting it for writability, but don't actually close the buffer in
2686          * case other routines want to use it later.  The buffer will be closed
2687          * in server_cleanup ().
2688          */
2689         if (from_primary_fd < 0
2690             && buf_to_net && buf_empty_p (buf_to_net))
2691             to_net_fd = -1;
2692
2693         if (buf_to_primary
2694             && (/* Assume that there is no further reason to keep the buffer to
2695                  * the primary open if we can no longer read its responses.
2696                  */
2697                 (from_primary_fd < 0 && buf_to_primary)
2698                 /* Also close buf_to_primary when it becomes impossible to find
2699                  * more data to send to it.  We don't close buf_from_primary
2700                  * yet since there may be data pending or the primary may react
2701                  * to the EOF on its input pipe.
2702                  */
2703                 || (from_net_fd < 0 && buf_empty_p (buf_to_primary))))
2704         {
2705             buf_shutdown (buf_to_primary);
2706             buf_free (buf_to_primary);
2707             buf_to_primary = NULL;
2708
2709             /* Setting the fd < 0 with from_primary_fd already < 0 will cause
2710              * an escape from this while loop.
2711              */
2712             to_primary_fd = -1;
2713         }
2714     }
2715
2716     /* Call postsecondary script.  */
2717     pre = false;
2718     Parse_Info (CVSROOTADM_POSTPROXY, current_parsed_root->directory,
2719                 prepost_proxy_proc, PIOPT_ALL, &pre);
2720 }
2721 # endif /* PROXY_SUPPORT */
2722
2723
2724
2725 struct notify_note {
2726     /* Directory in which this notification happens.  xmalloc'd*/
2727     char *dir;
2728
2729     /* xmalloc'd.  */
2730     char *update_dir;
2731
2732     /* xmalloc'd.  */
2733     char *filename;
2734
2735     /* The following three all in one xmalloc'd block, pointed to by TYPE.
2736        Each '\0' terminated.  */
2737     /* "E" or "U".  */
2738     char *type;
2739     /* time+host+dir */
2740     char *val;
2741     char *watches;
2742
2743     struct notify_note *next;
2744 };
2745
2746 static struct notify_note *notify_list;
2747 /* Used while building list, to point to the last node that already exists.  */
2748 static struct notify_note *last_node;
2749
2750 static void
2751 serve_notify (char *arg)
2752 {
2753     struct notify_note *new = NULL;
2754     char *data = NULL;
2755     int status;
2756
2757     if (error_pending ()) return;
2758
2759     if (isProxyServer())
2760     {
2761 # ifdef PROXY_SUPPORT
2762         if (!proxy_log)
2763         {
2764 # endif /* PROXY_SUPPORT */
2765             if (alloc_pending (160) + strlen (program_name))
2766                 sprintf (pending_error_text, 
2767 "E This CVS server does not support disconnected `%s edit'.  For now, remove all `%s' files in your workspace and try your command again.",
2768                          program_name, CVSADM_NOTIFY);
2769         return;
2770 # ifdef PROXY_SUPPORT
2771         }
2772         else
2773         {
2774             /* This is effectively a write command, so run it on the primary.  */
2775             become_proxy ();
2776             exit (EXIT_SUCCESS);
2777         }
2778 # endif /* PROXY_SUPPORT */
2779     }
2780
2781     if (outside_dir (arg))
2782         return;
2783
2784     if (gDirname == NULL)
2785         goto error;
2786
2787     new = xmalloc (sizeof (struct notify_note));
2788     if (new == NULL)
2789     {
2790         pending_error = ENOMEM;
2791         return;
2792     }
2793     new->dir = xmalloc (strlen (gDirname) + 1);
2794     new->update_dir = xmalloc (strlen (gupdate_dir) + 1);
2795     new->filename = xmalloc (strlen (arg) + 1);
2796     if (new->dir == NULL || new->update_dir == NULL || new->filename == NULL)
2797     {
2798         pending_error = ENOMEM;
2799         if (new->dir != NULL)
2800             free (new->dir);
2801         free (new);
2802         return;
2803     }
2804     strcpy (new->dir, gDirname);
2805     strcpy (new->update_dir, gupdate_dir);
2806     strcpy (new->filename, arg);
2807
2808     status = buf_read_line (buf_from_net, &data, NULL);
2809     if (status != 0)
2810     {
2811         if (status == -2)
2812             pending_error = ENOMEM;
2813         else
2814         {
2815             pending_error_text = xmalloc (80 + strlen (arg));
2816             if (pending_error_text == NULL)
2817                 pending_error = ENOMEM;
2818             else
2819             {
2820                 if (status == -1)
2821                     sprintf (pending_error_text,
2822                              "E end of file reading notification for %s", arg);
2823                 else
2824                 {
2825                     sprintf (pending_error_text,
2826                              "E error reading notification for %s", arg);
2827                     pending_error = status;
2828                 }
2829             }
2830         }
2831         free (new->filename);
2832         free (new->dir);
2833         free (new);
2834     }
2835     else
2836     {
2837         char *cp;
2838
2839         if (!data[0])
2840             goto error;
2841
2842         if (strchr (data, '+'))
2843             goto error;
2844
2845         new->type = data;
2846         if (data[1] != '\t')
2847             goto error;
2848         data[1] = '\0';
2849         cp = data + 2;
2850         new->val = cp;
2851         cp = strchr (cp, '\t');
2852         if (cp == NULL)
2853             goto error;
2854         *cp++ = '+';
2855         cp = strchr (cp, '\t');
2856         if (cp == NULL)
2857             goto error;
2858         *cp++ = '+';
2859         cp = strchr (cp, '\t');
2860         if (cp == NULL)
2861             goto error;
2862         *cp++ = '\0';
2863         new->watches = cp;
2864         /* If there is another tab, ignore everything after it,
2865            for future expansion.  */
2866         cp = strchr (cp, '\t');
2867         if (cp != NULL)
2868             *cp = '\0';
2869
2870         new->next = NULL;
2871
2872         if (last_node == NULL)
2873             notify_list = new;
2874         else
2875             last_node->next = new;
2876         last_node = new;
2877     }
2878     return;
2879   error:
2880     pending_error = 0;
2881     if (alloc_pending (80))
2882         strcpy (pending_error_text,
2883                 "E Protocol error; misformed Notify request");
2884     if (data != NULL)
2885         free (data);
2886     if (new != NULL)
2887     {
2888         free (new->filename);
2889         free (new->update_dir);
2890         free (new->dir);
2891         free (new);
2892     }
2893     return;
2894 }
2895
2896
2897
2898 static void
2899 serve_hostname (char *arg)
2900 {
2901     free (hostname);
2902     hostname = xstrdup (arg);
2903     return;
2904 }
2905
2906
2907
2908 static void
2909 serve_localdir (char *arg)
2910 {
2911     if (CurDir) free (CurDir);
2912     CurDir = xstrdup (arg);
2913 }
2914
2915
2916
2917 /* Process all the Notify requests that we have stored up.  Returns 0
2918    if successful, if not prints error message (via error()) and
2919    returns negative value.  */
2920 static int
2921 server_notify (void)
2922 {
2923     struct notify_note *p;
2924     char *repos;
2925
2926     TRACE (TRACE_FUNCTION, "server_notify()");
2927
2928     while (notify_list != NULL)
2929     {
2930         if (CVS_CHDIR (notify_list->dir) < 0)
2931         {
2932             error (0, errno, "cannot change to %s", notify_list->dir);
2933             return -1;
2934         }
2935         repos = Name_Repository (NULL, NULL);
2936
2937         lock_dir_for_write (repos);
2938
2939         fileattr_startdir (repos);
2940
2941         notify_do (*notify_list->type, notify_list->filename,
2942                    notify_list->update_dir, getcaller(), notify_list->val,
2943                    notify_list->watches, repos);
2944
2945         buf_output0 (buf_to_net, "Notified ");
2946         {
2947             char *dir = notify_list->dir + strlen (server_temp_dir) + 1;
2948             if (dir[0] == '\0')
2949                 buf_append_char (buf_to_net, '.');
2950             else
2951                 buf_output0 (buf_to_net, dir);
2952             buf_append_char (buf_to_net, '/');
2953             buf_append_char (buf_to_net, '\n');
2954         }
2955         buf_output0 (buf_to_net, repos);
2956         buf_append_char (buf_to_net, '/');
2957         buf_output0 (buf_to_net, notify_list->filename);
2958         buf_append_char (buf_to_net, '\n');
2959         free (repos);
2960
2961         p = notify_list->next;
2962         free (notify_list->filename);
2963         free (notify_list->dir);
2964         free (notify_list->type);
2965         free (notify_list);
2966         notify_list = p;
2967
2968         fileattr_write ();
2969         fileattr_free ();
2970
2971         Lock_Cleanup ();
2972     }
2973
2974     last_node = NULL;
2975
2976     /* The code used to call fflush (stdout) here, but that is no
2977        longer necessary.  The data is now buffered in buf_to_net,
2978        which will be flushed by the caller, do_cvs_command.  */
2979
2980     return 0;
2981 }
2982
2983
2984
2985 /* This request is processed in all passes since requests which must
2986  * sometimes be processed before it is known whether we are running as a
2987  * secondary or not, for instance the `expand-modules' request, sometimes use
2988  * the `Arguments'.
2989  */
2990 static void
2991 serve_argument (char *arg)
2992 {
2993     char *p;
2994
2995     if (error_pending()) return;
2996
2997     if (argument_count >= 10000)
2998     {
2999         if (alloc_pending (80))
3000             sprintf (pending_error_text, 
3001                      "E Protocol error: too many arguments");
3002         return;
3003     }
3004
3005     if (argument_vector_size <= argument_count)
3006     {
3007         argument_vector_size *= 2;
3008         argument_vector = xnrealloc (argument_vector,
3009                                      argument_vector_size, sizeof (char *));
3010         if (argument_vector == NULL)
3011         {
3012             pending_error = ENOMEM;
3013             return;
3014         }
3015     }
3016     p = xmalloc (strlen (arg) + 1);
3017     if (p == NULL)
3018     {
3019         pending_error = ENOMEM;
3020         return;
3021     }
3022     strcpy (p, arg);
3023     argument_vector[argument_count++] = p;
3024 }
3025
3026
3027
3028 /* For secondary servers, this is handled in all passes, as is the `Argument'
3029  * request, and for the same reasons.
3030  */
3031 static void
3032 serve_argumentx (char *arg)
3033 {
3034     char *p;
3035
3036     if (error_pending()) return;
3037     
3038     if (argument_count <= 1) 
3039     {
3040         if (alloc_pending (80))
3041             sprintf (pending_error_text,
3042 "E Protocol error: called argumentx without prior call to argument");
3043         return;
3044     }
3045
3046     p = argument_vector[argument_count - 1];
3047     p = xrealloc (p, strlen (p) + 1 + strlen (arg) + 1);
3048     if (p == NULL)
3049     {
3050         pending_error = ENOMEM;
3051         return;
3052     }
3053     strcat (p, "\n");
3054     strcat (p, arg);
3055     argument_vector[argument_count - 1] = p;
3056 }
3057
3058
3059
3060 static void
3061 serve_global_option (char *arg)
3062 {
3063 # ifdef PROXY_SUPPORT
3064     /* This can generate error messages and termination before `Root' requests,
3065      * so it must be dealt with in the first pass.
3066      */ 
3067     if (reprocessing) return;
3068 # endif /* PROXY_SUPPORT */
3069
3070     if (arg[0] != '-' || arg[1] == '\0' || arg[2] != '\0')
3071     {
3072     error_return:
3073         if (alloc_pending (strlen (arg) + 80))
3074             sprintf (pending_error_text,
3075                      "E Protocol error: bad global option %s",
3076                      arg);
3077         return;
3078     }
3079     switch (arg[1])
3080     {
3081         case 'l':
3082             error(0, 0, "WARNING: global `-l' option ignored.");
3083             break;
3084         case 'n':
3085             noexec = 1;
3086             logoff = 1;
3087             break;
3088         case 'q':
3089             quiet = 1;
3090             break;
3091         case 'r':
3092             cvswrite = 0;
3093             break;
3094         case 'Q':
3095             really_quiet = 1;
3096             break;
3097         case 't':
3098             trace++;
3099             break;
3100         default:
3101             goto error_return;
3102     }
3103 }
3104
3105
3106
3107 /* This needs to be processed before Root requests, so we allow it to be
3108  * be processed before knowing whether we are running as a secondary server
3109  * to allow `noop' and `Root' requests to generate errors as before.
3110  */
3111 static void
3112 serve_set (char *arg)
3113 {
3114 # ifdef PROXY_SUPPORT
3115     if (reprocessing) return;
3116 # endif /* PROXY_SUPPORT */
3117
3118     /* FIXME: This sends errors immediately (I think); they should be
3119        put into pending_error.  */
3120     variable_set (arg);
3121 }
3122
3123 # ifdef ENCRYPTION
3124
3125 #   ifdef HAVE_KERBEROS
3126
3127 static void
3128 serve_kerberos_encrypt( char *arg )
3129 {
3130 #     ifdef PROXY_SUPPORT
3131     assert (!proxy_log);
3132 #     endif /* PROXY_SUPPORT */
3133
3134     /* All future communication with the client will be encrypted.  */
3135
3136     buf_to_net = krb_encrypt_buffer_initialize (buf_to_net, 0, sched,
3137                                                 kblock,
3138                                                 buf_to_net->memory_error);
3139     buf_from_net = krb_encrypt_buffer_initialize (buf_from_net, 1, sched,
3140                                                   kblock,
3141                                                   buf_from_net->memory_error);
3142 }
3143
3144 #   endif /* HAVE_KERBEROS */
3145
3146 #   ifdef HAVE_GSSAPI
3147
3148 static void
3149 serve_gssapi_encrypt( char *arg )
3150 {
3151 #     ifdef PROXY_SUPPORT
3152     assert (!proxy_log);
3153 #     endif /* PROXY_SUPPORT */
3154
3155     if (cvs_gssapi_wrapping)
3156     {
3157         /* We're already using a gssapi_wrap buffer for stream
3158            authentication.  Flush everything we've output so far, and
3159            turn on encryption for future data.  On the input side, we
3160            should only have unwrapped as far as the Gssapi-encrypt
3161            command, so future unwrapping will become encrypted.  */
3162         buf_flush (buf_to_net, 1);
3163         cvs_gssapi_encrypt = 1;
3164         return;
3165     }
3166
3167     /* All future communication with the client will be encrypted.  */
3168
3169     cvs_gssapi_encrypt = 1;
3170
3171     buf_to_net = cvs_gssapi_wrap_buffer_initialize (buf_to_net, 0,
3172                                                     gcontext,
3173                                                     buf_to_net->memory_error);
3174     buf_from_net = cvs_gssapi_wrap_buffer_initialize (buf_from_net, 1,
3175                                                       gcontext,
3176                                                       buf_from_net->memory_error);
3177
3178     cvs_gssapi_wrapping = 1;
3179 }
3180
3181 #   endif /* HAVE_GSSAPI */
3182
3183 # endif /* ENCRYPTION */
3184
3185 # ifdef HAVE_GSSAPI
3186
3187 static void
3188 serve_gssapi_authenticate (char *arg)
3189 {
3190 #   ifdef PROXY_SUPPORT
3191     assert (!proxy_log);
3192 #   endif /* PROXY_SUPPORT */
3193
3194     if (cvs_gssapi_wrapping)
3195     {
3196         /* We're already using a gssapi_wrap buffer for encryption.
3197            That includes authentication, so we don't have to do
3198            anything further.  */
3199         return;
3200     }
3201
3202     buf_to_net = cvs_gssapi_wrap_buffer_initialize (buf_to_net, 0,
3203                                                     gcontext,
3204                                                     buf_to_net->memory_error);
3205     buf_from_net = cvs_gssapi_wrap_buffer_initialize (buf_from_net, 1,
3206                                                       gcontext,
3207                                                       buf_from_net->memory_error);
3208
3209     cvs_gssapi_wrapping = 1;
3210 }
3211
3212 # endif /* HAVE_GSSAPI */
3213
3214
3215
3216 # ifdef SERVER_FLOWCONTROL
3217 /* The maximum we'll queue to the remote client before blocking.  */
3218 #   ifndef SERVER_HI_WATER
3219 #     define SERVER_HI_WATER (2 * 1024 * 1024)
3220 #   endif /* SERVER_HI_WATER */
3221 /* When the buffer drops to this, we restart the child */
3222 #   ifndef SERVER_LO_WATER
3223 #     define SERVER_LO_WATER (1 * 1024 * 1024)
3224 #   endif /* SERVER_LO_WATER */
3225 # endif /* SERVER_FLOWCONTROL */
3226
3227
3228
3229 static void
3230 serve_questionable (char *arg)
3231 {
3232     static int initted;
3233
3234 # ifdef PROXY_SUPPORT
3235     if (proxy_log) return;
3236 # endif /* PROXY_SUPPORT */
3237
3238     if (error_pending ()) return;
3239
3240     if (!initted)
3241     {
3242         /* Pick up ignores from CVSROOTADM_IGNORE, $HOME/.cvsignore on server,
3243            and CVSIGNORE on server.  */
3244         ign_setup ();
3245         initted = 1;
3246     }
3247
3248     if (gDirname == NULL)
3249     {
3250         if (alloc_pending (80))
3251             sprintf (pending_error_text,
3252 "E Protocol error: `Directory' missing");
3253         return;
3254     }
3255
3256     if (outside_dir (arg))
3257         return;
3258
3259     if (!ign_name (arg))
3260     {
3261         char *update_dir;
3262
3263         buf_output (buf_to_net, "M ? ", 4);
3264         update_dir = gDirname + strlen (server_temp_dir) + 1;
3265         if (!(update_dir[0] == '.' && update_dir[1] == '\0'))
3266         {
3267             buf_output0 (buf_to_net, update_dir);
3268             buf_output (buf_to_net, "/", 1);
3269         }
3270         buf_output0 (buf_to_net, arg);
3271         buf_output (buf_to_net, "\n", 1);
3272     }
3273 }
3274
3275
3276
3277 static struct buffer *protocol = NULL;
3278
3279 /* This is the output which we are saving up to send to the server, in the
3280    child process.  We will push it through, via the `protocol' buffer, when
3281    we have a complete line.  */
3282 static struct buffer *saved_output;
3283
3284 /* Likewise, but stuff which will go to stderr.  */
3285 static struct buffer *saved_outerr;
3286
3287
3288
3289 static void
3290 protocol_memory_error (struct buffer *buf)
3291 {
3292     error (1, ENOMEM, "Virtual memory exhausted");
3293 }
3294
3295
3296
3297 /* If command is valid, return 1.
3298  * Else if command is invalid and croak_on_invalid is set, then die.
3299  * Else just return 0 to indicate that command is invalid.
3300  */
3301 static bool
3302 check_command_valid_p (char *cmd_name)
3303 {
3304     /* Right now, only pserver notices invalid commands -- namely,
3305      * write attempts by a read-only user.  Therefore, if CVS_Username
3306      * is not set, this just returns 1, because CVS_Username unset
3307      * means pserver is not active.
3308      */
3309 # ifdef AUTH_SERVER_SUPPORT
3310     if (CVS_Username == NULL)
3311         return true;
3312
3313     if (lookup_command_attribute (cmd_name) & CVS_CMD_MODIFIES_REPOSITORY)
3314     {
3315         /* This command has the potential to modify the repository, so
3316          * we check if the user have permission to do that.
3317          *
3318          * (Only relevant for remote users -- local users can do
3319          * whatever normal Unix file permissions allow them to do.)
3320          *
3321          * The decision method:
3322          *
3323          *    If $CVSROOT/CVSADMROOT_READERS exists and user is listed
3324          *    in it, then read-only access for user.
3325          *
3326          *    Or if $CVSROOT/CVSADMROOT_WRITERS exists and user NOT
3327          *    listed in it, then also read-only access for user.
3328          *
3329          *    Else read-write access for user.
3330          */
3331
3332          char *linebuf = NULL;
3333          int num_red = 0;
3334          size_t linebuf_len = 0;
3335          char *fname;
3336          size_t flen;
3337          FILE *fp;
3338          int found_it = 0;
3339
3340          /* else */
3341          flen = strlen (current_parsed_root->directory)
3342                 + strlen (CVSROOTADM)