summaryrefslogtreecommitdiff
path: root/snarf-ftp.c
diff options
context:
space:
mode:
authorAaron Plattner <aplattner@nvidia.com>2008-02-13 10:20:36 -0800
committerAaron Plattner <aplattner@nvidia.com>2008-02-13 10:20:36 -0800
commit6d2a0069d419975ba01c2c423b18ef7cd2e76a6f (patch)
tree130e177fc9fa77d4c7d0788ba07c3b5af42dd84f /snarf-ftp.c
1.0-61061.0-6106
Diffstat (limited to 'snarf-ftp.c')
-rw-r--r--snarf-ftp.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/snarf-ftp.c b/snarf-ftp.c
new file mode 100644
index 0000000..23e35db
--- /dev/null
+++ b/snarf-ftp.c
@@ -0,0 +1,415 @@
+/*
+ * nvidia-installer: A tool for installing NVIDIA software packages on
+ * Unix and Linux systems.
+ *
+ * Copyright (C) 2003 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the:
+ *
+ * Free Software Foundation, Inc.
+ * 59 Temple Place - Suite 330
+ * Boston, MA 02111-1307, USA
+ *
+ *
+ * snarf-ftp.c - this source file contains functions for downloading
+ * files via the ftp protocol. Based on snarf
+ * (http://www.xach.com/snarf/) by Zachary Beane <xach@xach.com>,
+ * released under the GPL.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <stdarg.h>
+
+#include <netdb.h>
+
+#ifdef USE_SOCKS5
+#define SOCKS
+#include <socks.h>
+#endif
+
+#include <errno.h>
+
+#include "snarf-internal.h"
+#include "user-interface.h"
+#include "misc.h"
+
+
+
+static void close_quit(int sock)
+{
+ if (sock) {
+ write(sock, "QUIT\r\n", 6);
+ close(sock);
+ }
+} /* close_quit() */
+
+
+
+static void ftp_set_defaults(UrlResource *rsrc, Url *u)
+{
+ if (!u->port) u->port = 21;
+ if (!u->username) u->username = strdup("anonymous");
+ if (!u->password) u->password = strdup("snarf@");
+
+} /* ftp_set_defaults() */
+
+
+
+static void send_control(int sock, char *string, ...)
+{
+ va_list args;
+ char *line = NULL;
+ char *newline;
+ char *s = NULL;
+
+ line = nvstrdup(string);
+
+ va_start(args, string);
+ s = va_arg(args, char *);
+ while (s) {
+ newline = nvstrcat(line, s, NULL);
+ nvfree(line);
+ line = newline;
+ s = va_arg(args, char *);
+ }
+ va_end(args);
+
+ write(sock, line, strlen(line));
+ nvfree(line);
+
+} /* send_control() */
+
+
+
+static char *get_line(UrlResource *rsrc, int control)
+{
+ int bytes_read = 0;
+ char *end;
+ char buf[SNARF_BUFSIZE+1];
+
+ while ((bytes_read = read(control, buf, SNARF_BUFSIZE))) {
+
+ if (buf[0] == '4' || buf[0] == '5') return NULL;
+
+ /* in case there's a partial read */
+ buf[bytes_read] = '\0';
+
+ if (buf[bytes_read - 1] == '\n') buf[bytes_read - 1] = '\0';
+
+ if (buf[bytes_read - 2] == '\r') buf[bytes_read - 2] = '\0';
+
+ ui_expert(rsrc->op, buf);
+
+ if (isdigit(buf[0]) && buf[3] == ' ') {
+ return strdup(buf);
+ }
+
+ /* skip to last line of possibly multiple line input */
+
+ if ((end = strrchr(buf, '\n'))) {
+ end++;
+ if (isdigit(end[0]) && end[3] == ' ')
+ return strdup(end);
+ }
+ }
+
+ return NULL;
+
+} /* get_line() */
+
+
+
+static int check_numeric(const char *numeric, const char *buf)
+{
+ return ((buf[0] == numeric[0]) &&
+ (buf[1] == numeric[1]) &&
+ (buf[2] == numeric[2]));
+
+} /* check_numeric() */
+
+
+
+static int sock_init(Options *op, struct sockaddr_in *sa, int control)
+{
+ socklen_t i;
+ int sock;
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ ui_error(op, "unable to open socket (%s)", strerror(errno));
+ return(0);
+ }
+
+ i = sizeof(*sa);
+
+ getsockname(control, (struct sockaddr *)sa, &i);
+ sa->sin_port = 0 ; /* let system choose a port */
+ if (bind (sock, (struct sockaddr *)sa, sizeof (*sa)) < 0) {
+ ui_error(op, "unable to bind socket (%s)", strerror(errno));
+ return 0;
+ }
+
+ return sock;
+
+} /* sock_init() */
+
+
+
+static int get_passive_sock(UrlResource *rsrc, int control)
+{
+ unsigned char *addr;
+ struct sockaddr_in sa;
+ int sock;
+ int x;
+ char *line, *orig_line;
+
+ send_control(control, "PASV\r\n", NULL);
+
+ if( !((line = get_line(rsrc, control)) &&
+ check_numeric("227", line)) ) {
+ nvfree(line);
+ return 0;
+ }
+
+ orig_line = line;
+
+ if (strlen(line) < 4) {
+ nvfree(line);
+ return 0;
+ }
+
+ if (!(sock = sock_init(rsrc->op, &sa, control))) return -1;
+
+ /* skip the numeric response */
+ line += 4;
+
+ /* then find the digits */
+
+ while (!(isdigit(*line))) line++;
+
+ /* ugliness from snarf 1.x */
+
+ sa.sin_family = AF_INET;
+ addr = (unsigned char *)&sa.sin_addr;
+
+ for(x = 0; x < 4; x++) {
+ addr[x] = atoi(line);
+ line = strchr(line,',') + 1;
+ }
+
+ addr = (unsigned char *)&sa.sin_port ;
+ addr[0] = atoi(line);
+ line = strchr(line,',') + 1;
+ addr[1] = atoi(line);
+
+ if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
+ nvfree(orig_line);
+ ui_error(rsrc->op, "unable to connect (%s)", strerror(errno));
+ return -1;
+ }
+
+ nvfree(orig_line);
+ return sock;
+
+} /* get_passive_sock() */
+
+
+
+static int get_sock(UrlResource *rsrc, int control)
+{
+ struct sockaddr_in sa;
+ unsigned char *addr;
+ unsigned char *port;
+ char *line;
+ char port_string[SNARF_BUFSIZE];
+ unsigned int sock;
+ socklen_t i;
+
+ if (!(sock = sock_init(rsrc->op, &sa, control))) return 0;
+
+
+ if (listen(sock, 0) < 0) {
+ ui_error(rsrc->op, "unable to listen (%s)", strerror(errno));
+ return 0;
+ }
+
+ i = sizeof(sa);
+
+ getsockname(sock, (struct sockaddr *)&sa, &i);
+
+ addr = (unsigned char *)(&sa.sin_addr.s_addr);
+ port = (unsigned char *)(&sa.sin_port);
+
+ sprintf(port_string, "PORT %d,%d,%d,%d,%d,%d\r\n",
+ addr[0], addr[1], addr[2], addr[3],
+ port[0],(unsigned char)port[1]);
+
+ send_control(control, port_string, NULL);
+
+ if (!((line = get_line(rsrc, control)) && check_numeric("200", line))) {
+ nvfree(line);
+ return 0;
+ }
+ nvfree(line);
+
+ return sock;
+
+} /* get_sock() */
+
+
+
+/* I'm going to go to hell for not doing proper cleanup. */
+
+int ftp_transfer(UrlResource *rsrc)
+{
+ Url *u = NULL;
+ char *line = NULL;
+ int sock = 0;
+ int data_sock = 0;
+ int passive = 1;
+ int retval = 0;
+
+ u = rsrc->url;
+
+ /*
+ * first of all, if this is proxied, just pass it off to the
+ * http module, since that's how we support proxying.
+ */
+
+ rsrc->proxy = get_proxy("FTP_PROXY");
+
+ if (rsrc->proxy) {
+ return http_transfer(rsrc);
+ }
+
+ ftp_set_defaults(rsrc, u);
+
+ if (!(sock = tcp_connect(rsrc->op, u->host, u->port)))
+ return FALSE;
+
+ if (!(line = get_line(rsrc, sock)))
+ return FALSE;
+
+ if (!check_numeric("220", line)) {
+ ui_error(rsrc->op, "bad ftp server greeting: %s", line);
+ nvfree(line);
+ return FALSE;
+ }
+
+ send_control(sock, "USER ", u->username, "\r\n", NULL);
+
+ if (!(line = get_line(rsrc, sock))) return FALSE;
+
+ /* do the password dance */
+ if (!check_numeric("230", line)) {
+ if (!check_numeric("331", line)) {
+ nvfree(line);
+ ui_error(rsrc->op, "bad/unexpected response: %s", line);
+ return FALSE;
+ } else {
+ nvfree(line);
+
+ send_control(sock, "PASS ", u->password, "\r\n", NULL);
+
+ if (!((line = get_line(rsrc, sock)) &&
+ check_numeric("230", line)) ) {
+ nvfree(line);
+ ui_error(rsrc->op, "login failed");
+ return FALSE;
+ }
+ nvfree(line);
+ }
+ }
+
+ /* set binmode */
+ send_control(sock, "TYPE I\r\n", NULL);
+
+ if (!(line = get_line(rsrc, sock))) return 0;
+ nvfree(line);
+
+ if (u->path) {
+ send_control(sock, "CWD ", u->path, "\r\n", NULL);
+
+ if (!((line = get_line(rsrc, sock)) &&
+ check_numeric("250", line))) {
+ nvfree(line);
+ close_quit(sock);
+ return 0;
+ }
+ nvfree(line);
+ }
+
+ /* finally, the good stuff */
+
+ /* get a socket for reading. try passive first. */
+
+ if ((data_sock = get_passive_sock(rsrc, sock)) == -1) {
+ return FALSE;
+ }
+
+ if (!data_sock) {
+ if ((data_sock = get_sock(rsrc, sock)) < 1)
+ return 0;
+ else
+ passive = 0;
+ }
+
+ if (u->file) {
+ send_control(sock, "SIZE ", u->file, "\r\n", NULL);
+ line = get_line(rsrc, sock);
+ if (line && check_numeric("213", line)) {
+ rsrc->outfile_size = atoi(line + 3);
+ } else {
+ rsrc->outfile_size = 0;
+ }
+ }
+
+ if (u->file)
+ send_control(sock, "RETR ", u->file, "\r\n", NULL);
+ else
+ send_control(sock, "NLST\r\n", NULL);
+
+ if (!((line = get_line(rsrc, sock)) &&
+ (check_numeric("150", line) || check_numeric("125", line)))) {
+ nvfree(line);
+ close_quit(sock);
+ return 0;
+ }
+
+ if (!passive)
+ data_sock = accept(data_sock, NULL, NULL);
+
+ nvfree(line);
+
+ retval = dump_data(rsrc, data_sock);
+
+ line = get_line(rsrc, sock); /* 226 Transfer complete */
+ nvfree(line);
+ send_control(sock, "QUIT\r\n", NULL);
+ line = get_line(rsrc, sock); /* 221 Goodbye */
+ nvfree(line);
+
+ close(sock);
+ close(data_sock);
+ return retval;
+
+} /* ftp_transfer() */