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