Initial import from FreeBSD RELENG_4:
[games.git] / release / sysinstall / ftp.c
1 /*
2  * The new sysinstall program.
3  *
4  * This is probably the last attempt in the `sysinstall' line, the next
5  * generation being slated to essentially a complete rewrite.
6  *
7  * $FreeBSD: src/release/sysinstall/ftp.c,v 1.37.2.8 2002/10/24 13:00:52 nyan Exp $
8  *
9  * Copyright (c) 1995
10  *      Jordan Hubbard.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer,
17  *    verbatim and that no modifications are made prior to this
18  *    point in the file.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36
37 #include "sysinstall.h"
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <sys/param.h>
42 #include <sys/wait.h>
43 #include <netdb.h>
44 #include <pwd.h>
45 #include <ftpio.h>
46
47 Boolean ftpInitted = FALSE;
48 static FILE *OpenConn;
49 int FtpPort;
50
51 /* List of sub directories to look for under a given FTP server. */
52 #ifdef PC98
53 const char *ftp_dirs[] = { ".", "releases/pc98", "snapshots/pc98",
54     "pub/FreeBSD", "pub/FreeBSD/releases/pc98",
55     "pub/FreeBSD/snapshots/pc98", NULL };
56 #else
57 const char *ftp_dirs[] = { ".", "releases/"MACHINE, "snapshots/"MACHINE,
58     "pub/FreeBSD", "pub/FreeBSD/releases/"MACHINE,
59     "pub/FreeBSD/snapshots/"MACHINE, NULL };
60 #endif
61
62 /* Brings up attached network device, if any - takes FTP device as arg */
63 static Boolean
64 netUp(Device *dev)
65 {
66     Device *netdev = (Device *)dev->private;
67
68     if (netdev)
69         return DEVICE_INIT(netdev);
70     else
71         return TRUE;    /* No net == happy net */
72 }
73
74 /* Brings down attached network device, if any - takes FTP device as arg */
75 static void
76 netDown(Device *dev)
77 {
78     Device *netdev = (Device *)dev->private;
79
80     if (netdev)
81         DEVICE_SHUTDOWN(netdev);
82 }
83
84 Boolean
85 mediaInitFTP(Device *dev)
86 {
87     int i, code, af, fdir;
88     char *cp, *rel, *hostname, *dir;
89     char *user, *login_name, password[80];
90
91     if (ftpInitted)
92         return TRUE;
93
94     if (OpenConn) {
95         fclose(OpenConn);
96         OpenConn = NULL;
97     }
98
99     /* If we can't initialize the network, bag it! */
100     if (!netUp(dev))
101         return FALSE;
102
103 try:
104     cp = variable_get(VAR_FTP_PATH);
105     if (!cp) {
106         if (DITEM_STATUS(mediaSetFTP(NULL)) == DITEM_FAILURE || (cp = variable_get(VAR_FTP_PATH)) == NULL) {
107             msgConfirm("Unable to get proper FTP path.  FTP media not initialized.");
108             netDown(dev);
109             return FALSE;
110         }
111     }
112
113     hostname = variable_get(VAR_FTP_HOST);
114     dir = variable_get(VAR_FTP_DIR);
115     if (!hostname || !dir) {
116         msgConfirm("Missing FTP host or directory specification.  FTP media not initialized,");
117         netDown(dev);
118         return FALSE;
119     }
120     user = variable_get(VAR_FTP_USER);
121     login_name = (!user || !*user) ? "anonymous" : user;
122
123     if (variable_get(VAR_FTP_PASS))
124         SAFE_STRCPY(password, variable_get(VAR_FTP_PASS));
125     else if (RunningAsInit)
126         sprintf(password, "installer@%s", variable_get(VAR_HOSTNAME));
127     else {
128         struct passwd *pw;
129         char *user;
130
131         pw = getpwuid(getuid());
132         user = pw ? pw->pw_name : "ftp";
133         sprintf(password, "%s@%s", user, variable_get(VAR_HOSTNAME));
134     }
135     af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
136     msgNotify("Logging in to %s@%s..", login_name, hostname);
137     if ((OpenConn = ftpLoginAf(hostname, af, login_name, password, FtpPort, isDebug(), &code)) == NULL) {
138         msgConfirm("Couldn't open FTP connection to %s:\n  %s.", hostname, ftpErrString(code));
139         goto punt;
140     }
141
142     ftpPassive(OpenConn, !strcmp(variable_get(VAR_FTP_STATE), "passive"));
143     ftpBinary(OpenConn);
144     if (dir && *dir != '\0') {
145         if ((i = ftpChdir(OpenConn, dir)) != 0) {
146             if (i == 550)
147                 msgConfirm("No such directory ftp://%s/%s\n"
148                            "please check your URL and try again.", hostname, dir);
149             else
150                 msgConfirm("FTP chdir to ftp://%s/%s returned error status:\n  %s.", hostname, dir, ftpErrString(i));
151             goto punt;
152         }
153     }
154
155     /*
156      * Now that we've verified that the path we're given is ok, let's try to
157      * be a bit intelligent in locating the release we are looking for.  First
158      * off, if the release is specified as "__RELEASE" or "any", then just
159      * assume that the current directory is the one we want and give up.
160      */
161     rel = variable_get(VAR_RELNAME);
162     if (strcmp(rel, "__RELEASE") && strcmp(rel, "any")) {
163         /*
164          * Ok, since we have a release variable, let's walk through the list
165          * of directories looking for a release directory.  The first one to
166          * match wins.  For each case, we chdir to ftp_dirs[fdir] first.  If
167          * that fails, we skip to the next one.  Otherwise, we try to chdir to
168          * rel.  If it succeeds we break out.  If it fails, then we go back to
169          * the base directory and try again.  Lots of chdirs, but oh well. :)
170          */
171         for (fdir = 0; ftp_dirs[fdir]; fdir++) {
172             /* Avoid sending CWD . commands which confuse some ftp servers */
173             if (strcmp(ftp_dirs[fdir], ".") &&
174                 (ftpChdir(OpenConn, (char *)ftp_dirs[fdir]) != 0))
175                 continue;
176             if (ftpChdir(OpenConn, rel) == 0) {
177                 ftpInitted = TRUE;
178                 return TRUE;
179             }
180             else        /* reset to "root" dir for a fresh try */
181                 ftpChdir(OpenConn, "/");
182         }
183
184         /*
185          * If we get here, then all of the directories we tried failed, so
186          * print out the error message and ask the user if they want to try
187          * again.
188          */
189         if (!msgYesNo("Warning:  Can't find the `%s' distribution on this\n"
190                       "FTP server.  You may need to visit a different server for\n"
191                       "the release you are trying to fetch or go to the Options\n"
192                       "menu and to set the release name to explicitly match what's\n"
193                       "available on %s (or set to \"any\").\n\n"
194                       "Would you like to select another FTP server?",
195                       rel, hostname)) {
196             variable_unset(VAR_FTP_PATH);
197             if (DITEM_STATUS(mediaSetFTP(NULL)) != DITEM_FAILURE)
198                 goto try;
199         }
200     } else {
201         ftpInitted = TRUE;
202         return TRUE;
203     }
204
205 punt:
206     ftpInitted = FALSE;
207     if (OpenConn != NULL) {
208         fclose(OpenConn);
209         OpenConn = NULL;
210     }
211     netDown(dev);
212     variable_unset(VAR_FTP_PATH);
213     return FALSE;
214 }
215
216 FILE *
217 mediaGetFTP(Device *dev, char *file, Boolean probe)
218 {
219     int nretries = 1;
220     FILE *fp;
221     char *try, buf[PATH_MAX];
222
223     if (!OpenConn) {
224         msgDebug("No FTP connection open, can't get file %s\n", file);
225         return NULL;
226     }
227
228     try = file;
229     while ((fp = ftpGet(OpenConn, try, 0)) == NULL) {
230         int ftperr = ftpErrno(OpenConn);
231
232         /* If a hard fail, try to "bounce" the ftp server to clear it */
233         if (ftperr != 550) {
234             if (ftperr != 421)  /* Timeout? */
235                 variable_unset(VAR_FTP_PATH);
236             /* If we can't re-initialize, just forget it */
237             DEVICE_SHUTDOWN(dev);
238             if (!DEVICE_INIT(dev)) {
239                 netDown(dev);
240                 if (OpenConn) {
241                     fclose(OpenConn);
242                     OpenConn = NULL;
243                 }
244                 variable_unset(VAR_FTP_PATH);
245                 return NULL;
246             }
247         }
248         else if (probe)
249             return NULL;
250         else {
251             /* Try some alternatives */
252             switch (nretries++) {
253             case 1:
254                 sprintf(buf, "releases/%s", file);
255                 try = buf;
256                 break;
257
258             case 2:
259                 sprintf(buf, "%s/%s", variable_get(VAR_RELNAME), file);
260                 try = buf;
261                 break;
262
263             case 3:
264                 sprintf(buf, "%s/releases/%s", variable_get(VAR_RELNAME), file);
265                 try = buf;
266                 break;
267
268             case 4:
269                 try = file;
270                 break;
271             }
272         }
273     }
274     return fp;
275 }
276
277 void
278 mediaShutdownFTP(Device *dev)
279 {
280     if (!ftpInitted)
281         return;
282
283     if (OpenConn != NULL) {
284         fclose(OpenConn);
285         OpenConn = NULL;
286     }
287     ftpInitted = FALSE;
288 }