1/*
2 * Copyright (c) 1999
3 *	Philipp Mergenthaler <philipp.mergenthaler@stud.uni-karlsruhe.de>
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, LIFE 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 * $FreeBSD$
28 */
29
30#include "sysinstall.h"
31#include <sys/socket.h>
32#include <netinet/in.h>
33#include <arpa/inet.h>
34#include <sys/param.h>
35#include <netdb.h>
36
37extern const char *ftp_dirs[]; /* defined in ftp.c */
38
39Boolean
40checkAccess(Boolean connectCheckOnly, Boolean isProxy)
41{
42    int rv, s, af;
43    bool el, found=FALSE;		    /* end of header line */
44    char *cp, buf[PATH_MAX], req[BUFSIZ];
45    struct addrinfo hints, *res, *res0;
46
47    af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
48    memset(&hints, 0, sizeof(hints));
49    hints.ai_family = af;
50    hints.ai_socktype = SOCK_STREAM;
51    hints.ai_protocol = 0;
52    if ((rv = getaddrinfo(variable_get(VAR_HTTP_HOST),
53			  variable_get(VAR_HTTP_PORT), &hints, &res0)) != 0) {
54	msgConfirm("%s", gai_strerror(rv));
55	variable_unset(VAR_HTTP_HOST);
56	return FALSE;
57    }
58    s = -1;
59    for (res = res0; res; res = res->ai_next) {
60	if ((s = socket(res->ai_family, res->ai_socktype,
61			res->ai_protocol)) < 0)
62	    continue;
63	if (connect(s, res->ai_addr, res->ai_addrlen) >= 0)
64	    break;
65	close(s);
66	s = -1;
67    }
68    freeaddrinfo(res0);
69    if (s == -1) {
70	if (isProxy) {
71		msgConfirm("Couldn't connect to proxy %s:%s",
72			    variable_get(VAR_HTTP_HOST),variable_get(VAR_HTTP_PORT));
73	} else {
74		msgConfirm("Couldn't connect to server http://%s:%s/",
75			    variable_get(VAR_HTTP_HOST),variable_get(VAR_HTTP_PORT));
76	}
77	variable_unset(VAR_HTTP_HOST);
78	return FALSE;
79    }
80    if (connectCheckOnly) {
81       close(s);
82       return TRUE;
83    }
84
85    msgNotify("Checking access to\n %s", variable_get(VAR_HTTP_PATH));
86    if (isProxy)
87	sprintf(req,"GET %s/ HTTP/1.0\r\n\r\n", variable_get(VAR_HTTP_PATH));
88    else
89	sprintf(req,"GET /%s/ HTTP/1.0\r\n\r\n", variable_get(VAR_HTTP_PATH));
90    write(s,req,strlen(req));
91/*
92 *  scan the headers of the response
93 *  this is extremely quick'n dirty
94 *
95 */
96    bzero(buf, PATH_MAX);
97    cp=buf;
98    el=FALSE;
99    rv=read(s,cp,1);
100    variable_set2(VAR_HTTP_FTP_MODE,"",0);
101    while (rv>0) {
102	if ((*cp == '\012') && el) {
103	    /* reached end of a header line */
104	    if (!strncmp(buf,"HTTP",4)) {
105		if (strtol((char *)(buf+9),0,0) == 200) {
106		    found = TRUE;
107		}
108	    }
109
110	    /*
111	     * Some proxies fetch files with certain extensions in "ascii mode"
112	     * instead of "binary mode" for FTP. The FTP server then translates
113	     * all LF to CRLF.
114	     *
115	     * You can force Squid to use binary mode by appending ";type=i" to
116	     * the URL, which is what I do here. For other proxies, the
117	     * LF->CRLF substitution is reverted in distExtract().
118	     */
119	    if (isProxy && !strncmp(buf,"Server: ",8)) {
120		if (!strncmp(buf,"Server: Squid",13)) {
121		    variable_set2(VAR_HTTP_FTP_MODE,";type=i",0);
122		} else {
123		    variable_set2(VAR_HTTP_FTP_MODE,"",0);
124		}
125	    }
126	    /* ignore other headers */
127	    /* check for "\015\012" at beginning of line, i.e. end of headers */
128	    if ((cp-buf) == 1)
129		break;
130	    cp=buf;
131	    rv=read(s,cp,1);
132	} else {
133	    el=FALSE;
134	    if (*cp == '\015')
135		el=TRUE;
136	    cp++;
137	    rv=read(s,cp,1);
138	}
139    }
140    close(s);
141    return found;
142}
143
144Boolean
145mediaInitHTTP(Device *dev)
146{
147    bool found=FALSE;		    /* end of header line */
148    char *rel, req[BUFSIZ];
149    int fdir;
150
151    /*
152     * First verify the proxy access
153     */
154    checkAccess(TRUE, TRUE);
155    while (variable_get(VAR_HTTP_HOST) == NULL) {
156        if (DITEM_STATUS(mediaSetHTTP(NULL)) == DITEM_FAILURE)
157            return FALSE;
158        checkAccess(TRUE, TRUE);
159    }
160again:
161    /* If the release is specified as "__RELEASE" or "any", then just
162     * assume that the path the user gave is ok.
163     */
164    rel = variable_get(VAR_RELNAME);
165    /*
166    msgConfirm("rel: -%s-", rel);
167    */
168
169    if (strcmp(rel, "__RELEASE") && strcmp(rel, "any"))  {
170        for (fdir = 0; ftp_dirs[fdir]; fdir++) {
171            sprintf(req, "%s/%s/%s", variable_get(VAR_FTP_PATH),
172                ftp_dirs[fdir], rel);
173            variable_set2(VAR_HTTP_PATH, req, 0);
174            if (checkAccess(FALSE, TRUE)) {
175                found = TRUE;
176                break;
177            }
178        }
179    } else {
180        variable_set2(VAR_HTTP_PATH, variable_get(VAR_FTP_PATH), 0);
181        found = checkAccess(FALSE, TRUE);
182    }
183    if (!found) {
184    	msgConfirm("No such directory: %s\n"
185		   "please check the URL and try again.", variable_get(VAR_HTTP_PATH));
186        variable_unset(VAR_HTTP_PATH);
187        dialog_clear_norefresh();
188        clear();
189        if (DITEM_STATUS(mediaSetHTTP(NULL)) != DITEM_FAILURE) goto again;
190    }
191    return found;
192}
193
194FILE *
195mediaGetHTTP(Device *dev, char *file, Boolean probe)
196{
197    FILE *fp;
198    int rv, s, af;
199    bool el;			/* end of header line */
200    char *cp, buf[PATH_MAX], req[BUFSIZ];
201    struct addrinfo hints, *res, *res0;
202
203    af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
204    memset(&hints, 0, sizeof(hints));
205    hints.ai_family = af;
206    hints.ai_socktype = SOCK_STREAM;
207    hints.ai_protocol = 0;
208    if ((rv = getaddrinfo(variable_get(VAR_HTTP_HOST),
209			  variable_get(VAR_HTTP_PORT), &hints, &res0)) != 0) {
210	msgConfirm("%s", gai_strerror(rv));
211	return NULL;
212    }
213    s = -1;
214    for (res = res0; res; res = res->ai_next) {
215	if ((s = socket(res->ai_family, res->ai_socktype,
216			res->ai_protocol)) < 0)
217	    continue;
218	if (connect(s, res->ai_addr, res->ai_addrlen) >= 0)
219	    break;
220	close(s);
221	s = -1;
222    }
223    freeaddrinfo(res0);
224    if (s == -1) {
225	msgConfirm("Couldn't connect to proxy %s:%s",
226		    variable_get(VAR_HTTP_HOST),variable_get(VAR_HTTP_PORT));
227	return NULL;
228    }
229
230    sprintf(req,"GET %s/%s%s HTTP/1.0\r\n\r\n",
231	    variable_get(VAR_HTTP_PATH), file, variable_get(VAR_HTTP_FTP_MODE));
232
233    if (isDebug()) {
234	msgDebug("sending http request: %s\n",req);
235    }
236    write(s,req,strlen(req));
237
238/*
239 *  scan the headers of the response
240 *  this is extremely quick'n dirty
241 *
242 */
243    cp=buf;
244    el=FALSE;
245    rv=read(s,cp,1);
246    while (rv>0) {
247	if ((*cp == '\012') && el) {
248  	    /* reached end of a header line */
249  	    if (!strncmp(buf,"HTTP",4)) {
250		rv=strtol((char *)(buf+9),0,0);
251		*(cp-1)='\0';		/* chop the CRLF off */
252		if (probe && (rv != 200)) {
253		    return NULL;
254		} else if (rv >= 500) {
255		    msgConfirm("Server error %s when sending %s, you could try an other server",buf, req);
256		    return NULL;
257		} else if (rv == 404) {
258		    msgConfirm("%s was not found, maybe directory or release-version are wrong?",req);
259		    return NULL;
260		} else if (rv >= 400) {
261		    msgConfirm("Client error %s, you could try an other server",buf);
262		    return NULL;
263		} else if (rv >= 300) {
264		    msgConfirm("Error %s",buf);
265		    return NULL;
266		} else if (rv != 200) {
267		    msgConfirm("Error %s when sending %s, you could try an other server",buf, req);
268		    return NULL;
269		}
270	    }
271	    /* ignore other headers */
272	    /* check for "\015\012" at beginning of line, i.e. end of headers */
273	    if ((cp-buf) == 1)
274		break;
275	    cp=buf;
276	    rv=read(s,cp,1);
277	} else {
278	    el=FALSE;
279	    if (*cp == '\015')
280		el=TRUE;
281	    cp++;
282	    rv=read(s,cp,1);
283	}
284    }
285    fp=fdopen(s,"r");
286    return fp;
287}
288