1 /* makeweb.c - let a user create a web subdirectory
2 **
3 ** Copyright � 1995 by Jef Poskanzer <jef@mail.acme.com>.
4 ** All rights reserved.
5 **
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
8 ** are met:
9 ** 1. Redistributions of source code must retain the above copyright
10 ** notice, this list of conditions and the following disclaimer.
11 ** 2. Redistributions in binary form must reproduce the above copyright
12 ** notice, this list of conditions and the following disclaimer in the
13 ** documentation and/or other materials provided with the distribution.
14 **
15 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 */
27
28 /* This is intended to be installed setgid to a group that has
29 ** write access to the system web directory. It allows any user
30 ** to create a subdirectory there. It also makes a symbolic link
31 ** in the user's home directory pointing at the new web subdir.
32 */
33
34
35 #include "../config.h"
36
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <pwd.h>
42 #include <errno.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45
46
47 #define LINK "public_html"
48
49 static char* argv0;
50
51 static void
usage(void)52 usage( void )
53 {
54 fprintf( stderr, "usage: %s [-d webdir]\n", argv0 );
55 }
56
57 static void
check_room(int size,int len)58 check_room( int size, int len )
59 {
60 if ( len > size )
61 {
62 (void) fprintf( stderr, "%s: internal error, out of room\n", argv0 );
63 exit( 1 );
64 }
65 }
66
67
68 static void
end_with_slash(char * str)69 end_with_slash( char* str )
70 {
71 if ( str[strlen( str ) - 1] != '/' )
72 (void) strcat( str, "/" );
73 }
74
75
76 static void
check_dir(char * dirname,uid_t uid,gid_t gid)77 check_dir( char* dirname, uid_t uid, gid_t gid )
78 {
79 struct stat sb;
80
81 /* Check the directory. */
82 if ( stat( dirname, &sb ) < 0 )
83 {
84 if ( errno != ENOENT )
85 {
86 perror( dirname );
87 exit( 1 );
88 }
89 /* Doesn't exist. Try to make it. */
90 if ( mkdir( dirname, 0755 ) < 0 )
91 {
92 if ( errno == ENOENT )
93 (void) printf( "\
94 Some part of the path %s does not exist.\n\
95 This is probably a configuration error.\n", dirname );
96 else
97 perror( dirname );
98 exit( 1 );
99 }
100 (void) printf( "Created web directory %s\n", dirname );
101 /* Try to change the group of the new dir to the user's group. */
102 (void) chown( dirname, -1, gid );
103 }
104 else
105 {
106 /* The directory already exists. Well, check that it is in
107 ** fact a directory.
108 */
109 if ( ! S_ISDIR( sb.st_mode ) )
110 {
111 (void) printf(
112 "%s already exists but is not a directory!\n", dirname );
113 exit( 1 );
114 }
115 if ( sb.st_uid != uid )
116 {
117 (void) printf(
118 "%s already exists but you don't own it!\n", dirname );
119 exit( 1 );
120 }
121 (void) printf( "Web directory %s already existed.\n", dirname );
122 }
123 }
124
125
126 int
main(int argc,char ** argv)127 main( int argc, char** argv )
128 {
129 char* webdir;
130 char* prefix;
131 struct passwd* pwd;
132 char* username;
133 char* homedir;
134 int opts;
135 char dirname[5000];
136 char linkname[5000];
137 char linkbuf[5000];
138 struct stat sb;
139
140 argv0 = argv[0];
141
142 #ifndef TILDE_MAP_2
143 webdir = WEBDIR;
144 #endif /* TILDE_MAP_2 */
145
146 if ( (opts = getopt(argc, argv, "d:h")) != -1 )
147 {
148 switch (opts)
149 {
150 case 'd': webdir = strdup(optarg); break;
151 case 'h':
152 default: usage(); return 1; break;
153 }
154 }
155
156 pwd = getpwuid( getuid() );
157 if ( pwd == (struct passwd*) 0 )
158 {
159 (void) fprintf( stderr, "%s: can't find your username\n", argv0 );
160 exit( 1 );
161 }
162 username = pwd->pw_name;
163 homedir = pwd->pw_dir;
164
165 #ifdef TILDE_MAP_2
166
167 /* All we have to do for the TILDE_MAP_2 case is make sure there's
168 ** a public_html subdirectory.
169 */
170 check_room(
171 sizeof(dirname), strlen( homedir ) + strlen( TILDE_MAP_2 ) + 2 );
172 (void) strcpy( dirname, homedir );
173 end_with_slash( dirname );
174 (void) strcat( dirname, TILDE_MAP_2 );
175
176 check_dir( dirname, pwd->pw_uid, pwd->pw_gid );
177
178 #else /* TILDE_MAP_2 */
179
180 #ifdef TILDE_MAP_1
181 prefix = TILDE_MAP_1;
182 #else /* TILDE_MAP_1 */
183 prefix = "";
184 #endif /* TILDE_MAP_1 */
185
186 /* Assemble the directory name. Be paranoid cause we're sgid. */
187 check_room(
188 sizeof(dirname),
189 strlen( webdir ) + strlen( prefix ) + strlen( username ) + 3 );
190 (void) strcpy( dirname, webdir );
191 end_with_slash( dirname );
192 if ( strlen( prefix ) != 0 )
193 {
194 (void) strcat( dirname, prefix );
195 end_with_slash( dirname );
196 }
197 (void) strcat( dirname, username );
198
199 /* Assemble the link name. */
200 check_room( sizeof(linkname), strlen( homedir ) + strlen( LINK ) + 2 );
201 (void) strcpy( linkname, homedir );
202 end_with_slash( linkname );
203 (void) strcat( linkname, LINK );
204
205 check_dir( dirname, pwd->pw_uid, pwd->pw_gid );
206
207 /* Check the symlink. */
208 try_link_again: ;
209 if ( lstat( linkname, &sb ) < 0 )
210 {
211 if ( errno != ENOENT )
212 {
213 perror( linkname );
214 exit( 1 );
215 }
216 /* Doesn't exist. Try to make it. */
217 if ( symlink( dirname, linkname ) < 0 )
218 {
219 if ( errno == ENOENT )
220 (void) printf( "\
221 Some part of the path %s does not exist.\n\
222 This is probably a configuration error.\n", linkname );
223 else
224 perror( linkname );
225 exit( 1 );
226 }
227 (void) printf( "Created symbolic link %s\n", linkname );
228 }
229 else
230 {
231 /* The link already exists. Well, check that it is in
232 ** fact a link.
233 */
234 if ( ! S_ISLNK( sb.st_mode ) )
235 {
236 (void) printf( "\
237 %s already exists but is not a\n\
238 symbolic link! Perhaps you have a real web subdirectory in your\n\
239 home dir from a previous web server configuration? You may have\n\
240 to rename it, run %s again, and then copy in the old\n\
241 contents.\n", linkname, argv0 );
242 exit( 1 );
243 }
244 /* Check the existing link's contents. */
245 if ( readlink( linkname, linkbuf, sizeof(linkbuf) ) < 0 )
246 {
247 perror( linkname );
248 exit( 1 );
249 }
250 if ( strcmp( dirname, linkbuf ) == 0 )
251 (void) printf( "Symbolic link %s already existed.\n", linkname );
252 else
253 {
254 (void) printf( "\
255 Symbolic link %s already existed\n\
256 but it points to the wrong place! Attempting to remove and\n\
257 recreate it.\n", linkname );
258 if ( unlink( linkname ) < 0 )
259 {
260 perror( linkname );
261 exit( 1 );
262 }
263 goto try_link_again;
264 }
265 }
266 #endif /* TILDE_MAP_2 */
267
268 exit( 0 );
269 }
270