Merge from vendor branch GCC:
[dragonfly.git] / crypto / heimdal-0.6.3 / appl / popper / pop_dropcopy.c
1 /*
2  * Copyright (c) 1989 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6
7 #include <popper.h>
8 RCSID("$Id: pop_dropcopy.c,v 1.26 2002/07/04 14:10:11 joda Exp $");
9
10 /*
11  * Run as the user in `pwd'
12  */
13
14 int
15 changeuser(POP *p, struct passwd *pwd)
16 {
17     if(setgid(pwd->pw_gid) < 0) {
18         pop_log (p, POP_PRIORITY,
19                  "Unable to change to gid %u: %s",
20                  (unsigned)pwd->pw_gid,
21                  strerror(errno));
22         return pop_msg (p, POP_FAILURE,
23                         "Unable to change gid");
24     }
25     if(setuid(pwd->pw_uid) < 0) {
26         pop_log (p, POP_PRIORITY,
27                  "Unable to change to uid %u: %s",
28                  (unsigned)pwd->pw_uid,
29                  strerror(errno));
30         return pop_msg (p, POP_FAILURE,
31                         "Unable to change uid");
32     }
33 #ifdef DEBUG
34     if(p->debug)
35         pop_log(p, POP_DEBUG,"uid = %u, gid = %u",
36                (unsigned)getuid(),
37                (unsigned)getgid());
38 #endif /* DEBUG */
39     return POP_SUCCESS;
40 }
41
42 /* 
43  *  dropcopy:   Make a temporary copy of the user's mail drop and 
44  *  save a stream pointer for it.
45  */
46
47 int
48 pop_dropcopy(POP *p, struct passwd *pwp)
49 {
50     int                     mfd;                    /*  File descriptor for 
51                                                         the user's maildrop */
52     int                     dfd;                    /*  File descriptor for 
53                                                         the SERVER maildrop */
54     FILE                    *tf;                    /*  The temp file */
55     char                    template[POP_TMPSIZE];  /*  Temp name holder */
56     char                    buffer[BUFSIZ];         /*  Read buffer */
57     long                    offset;                 /*  Old/New boundary */
58     int                     nchar;                  /*  Bytes written/read */
59     int                     tf_fd;                  /*  fd for temp file */
60     int                     ret;
61
62     /*  Create a temporary maildrop into which to copy the updated maildrop */
63     snprintf(p->temp_drop, sizeof(p->temp_drop), POP_DROP,p->user);
64
65 #ifdef DEBUG
66     if(p->debug)
67         pop_log(p,POP_DEBUG,"Creating temporary maildrop '%s'",
68             p->temp_drop);
69 #endif /* DEBUG */
70
71     /* Here we work to make sure the user doesn't cause us to remove or
72      * write over existing files by limiting how much work we do while
73      * running as root.
74      */
75
76     strlcpy(template, POP_TMPDROP, sizeof(template));
77     if ((tf_fd = mkstemp(template)) < 0 ||
78         (tf = fdopen(tf_fd, "w+")) == NULL) {
79         pop_log(p,POP_PRIORITY,
80             "Unable to create temporary temporary maildrop '%s': %s",template,
81                 strerror(errno));
82         return pop_msg(p,POP_FAILURE,
83                 "System error, can't create temporary file.");
84     }
85
86     /* Now give this file to the user   */
87     chown(template, pwp->pw_uid, pwp->pw_gid);
88     chmod(template, 0600);
89
90     /* Now link this file to the temporary maildrop.  If this fails it
91      * is probably because the temporary maildrop already exists.  If so,
92      * this is ok.  We can just go on our way, because by the time we try
93      * to write into the file we will be running as the user.
94      */
95     link(template,p->temp_drop);
96     fclose(tf);
97     unlink(template);
98
99     ret = changeuser(p, pwp);
100     if (ret != POP_SUCCESS)
101         return ret;
102
103     /* Open for append,  this solves the crash recovery problem */
104     if ((dfd = open(p->temp_drop,O_RDWR|O_APPEND|O_CREAT,0600)) == -1){
105         pop_log(p,POP_PRIORITY,
106             "Unable to open temporary maildrop '%s': %s",p->temp_drop,
107                 strerror(errno));
108         return pop_msg(p,POP_FAILURE,
109                 "System error, can't open temporary file, do you own it?");
110     }
111
112     /*  Lock the temporary maildrop */
113     if ( flock (dfd, (LOCK_EX | LOCK_NB)) == -1 ) 
114     switch(errno) {
115         case EWOULDBLOCK:
116             return pop_msg(p,POP_FAILURE,
117                  "%sMaildrop lock busy!  Is another session active?", 
118                            (p->flags & POP_FLAG_CAPA) ? "[IN-USE] " : "");
119             /* NOTREACHED */
120         default:
121             return pop_msg(p,POP_FAILURE,"flock: '%s': %s", p->temp_drop,
122                 strerror(errno));
123             /* NOTREACHED */
124         }
125     
126     /* May have grown or shrunk between open and lock! */
127     offset = lseek(dfd,0, SEEK_END);
128
129     /*  Open the user's maildrop, If this fails,  no harm in assuming empty */
130     if ((mfd = open(p->drop_name,O_RDWR)) > 0) {
131
132         /*  Lock the maildrop */
133         if (flock (mfd, LOCK_EX) == -1) {
134             close(mfd) ;
135             return pop_msg(p,POP_FAILURE, "flock: '%s': %s", p->temp_drop,
136                 strerror(errno));
137         }
138
139         /*  Copy the actual mail drop into the temporary mail drop */
140         while ( (nchar=read(mfd,buffer,BUFSIZ)) > 0 )
141             if ( nchar != write(dfd,buffer,nchar) ) {
142                 nchar = -1 ;
143                 break ;
144             }
145
146         if ( nchar != 0 ) {
147             /* Error adding new mail.  Truncate to original size,
148                and leave the maildrop as is.  The user will not 
149                see the new mail until the error goes away.
150                Should let them process the current backlog,  in case
151                the error is a quota problem requiring deletions! */
152             ftruncate(dfd,(int)offset) ;
153         } else {
154             /* Mail transferred!  Zero the mail drop NOW,  that we
155                do not have to do gymnastics to figure out what's new
156                and what is old later */
157             ftruncate(mfd,0) ;
158         }
159
160         /*  Close the actual mail drop */
161         close (mfd);
162     }
163
164     /*  Acquire a stream pointer for the temporary maildrop */
165     if ( (p->drop = fdopen(dfd,"a+")) == NULL ) {
166         close(dfd) ;
167         return pop_msg(p,POP_FAILURE,"Cannot assign stream for %s",
168             p->temp_drop);
169     }
170
171     rewind (p->drop);
172
173     return(POP_SUCCESS);
174 }