diff options
Diffstat (limited to 'pkgtools/pkg_install/files/lib/ftpio.c')
-rw-r--r-- | pkgtools/pkg_install/files/lib/ftpio.c | 1183 |
1 files changed, 766 insertions, 417 deletions
diff --git a/pkgtools/pkg_install/files/lib/ftpio.c b/pkgtools/pkg_install/files/lib/ftpio.c index b298185bdd7..f8544801eb6 100644 --- a/pkgtools/pkg_install/files/lib/ftpio.c +++ b/pkgtools/pkg_install/files/lib/ftpio.c @@ -1,4 +1,4 @@ -/* $NetBSD: ftpio.c,v 1.6 2003/09/23 13:22:42 grant Exp $ */ +/* $NetBSD: ftpio.c,v 1.7 2003/10/29 23:00:28 jlam Exp $ */ #if HAVE_CONFIG_H #include "config.h" @@ -8,9 +8,45 @@ #include <sys/cdefs.h> #endif #ifndef lint -__RCSID("$NetBSD: ftpio.c,v 1.6 2003/09/23 13:22:42 grant Exp $"); +__RCSID("$NetBSD: ftpio.c,v 1.7 2003/10/29 23:00:28 jlam Exp $"); #endif +/*- + * Copyright (c) 2003 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + /* * Copyright (c) 1999 Hubert Feyrer. All rights reserved. * @@ -50,6 +86,9 @@ __RCSID("$NetBSD: ftpio.c,v 1.6 2003/09/23 13:22:42 grant Exp $"); #if HAVE_SYS_POLL_H #include <sys/poll.h> #endif +#if HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif #if HAVE_SIGNAL_H #include <signal.h> #endif @@ -116,9 +155,6 @@ typedef struct { #if EXPECT_DEBUG static int expect_debug = 1; #endif /* EXPECT_DEBUG */ -#ifdef STANDALONE -int Verbose=1; -#endif static int needclose=0; static int ftp_started=0; static fds ftpio; @@ -127,6 +163,12 @@ static char term[1024]; static char bold_on[1024]; static char bold_off[1024]; +static char *ftp_expand_URL(const char *, char *); +static int hexvalue(char); +static char *http_expand_URL(const char *, char *); +static int http_extract_fn(char *, char *, size_t); +static void URL_decode(char *); + /* * expect "str" (a regular expression) on file descriptor "fd", storing * the FTP return code of the command in the integer "ftprc". The "str" @@ -136,126 +178,126 @@ static char bold_off[1024]; static int expect(int fd, const char *str, int *ftprc) { - int rc; - char buf[90]; + int rc; + char buf[90]; #if EXPECT_DEBUG - char *vstr; + char *vstr; #endif /* EXPECT_DEBUG */ - regex_t rstr; - int done; - struct pollfd set[1]; - int retval; - regmatch_t match; - int verbose_expect=0; + regex_t rstr; + int done; + struct pollfd set[1]; + int retval; + regmatch_t match; + int verbose_expect=0; #if EXPECT_DEBUG - vstr=malloc(2*sizeof(buf)); - if (vstr == NULL) - err(EXIT_FAILURE, "expect: malloc() failed"); - strvis(vstr, str, VIS_NL|VIS_SAFE|VIS_CSTYLE); + vstr=malloc(2*sizeof(buf)); + if (vstr == NULL) + err(EXIT_FAILURE, "expect: malloc() failed"); + strvis(vstr, str, VIS_NL|VIS_SAFE|VIS_CSTYLE); #endif /* EXPECT_DEBUG */ - if (regcomp(&rstr, str, REG_EXTENDED) != 0) - err(EXIT_FAILURE, "expect: regcomp() failed"); + if (regcomp(&rstr, str, REG_EXTENDED) != 0) + err(EXIT_FAILURE, "expect: regcomp() failed"); #if EXPECT_DEBUG - if (expect_debug) - printf("expecting \"%s\" on fd %d ...\n", vstr, fd); + if (expect_debug) + printf("expecting \"%s\" on fd %d ...\n", vstr, fd); #endif /* EXPECT_DEBUG */ - if(0) setbuf(stdout, NULL); - - memset(buf, '\n', sizeof(buf)); - - done=0; - retval=0; - set[0].fd = fd; - set[0].events = POLLIN; - while(!done) { - rc = poll(set, 1, 10*60*1000); /* seconds until next message from tar */ - switch (rc) { - case -1: - if (errno == EINTR) - break; - warn("expect: poll() failed (probably ftp died because of bad args)"); - done = 1; - retval = -1; - break; - case 0: - warnx("expect: poll() timeout"); - /* need to send ftp coprocess SIGINT to make it stop - * downloading into dir that we'll blow away in a second */ - kill(ftp_pid, SIGINT); - - /* Wait until ftp coprocess is responsive again - * XXX Entering recursion here! - */ - rc = ftp_cmd("cd .\n", "\n(550|250).*\n"); - if (rc != 250) { - /* now we have a really good reason to bail out ;) */ - } - /* ftp is at command prompt again, and will wait for our - * next command. If we were downloading, we can now safely - * continue and remove the dir that the tar command was - * expanding to */ + if(0) setbuf(stdout, NULL); + + memset(buf, '\n', sizeof(buf)); + + done=0; + retval=0; + set[0].fd = fd; + set[0].events = POLLIN; + while(!done) { + rc = poll(set, 1, 10*60*1000); /* seconds until next message from tar */ + switch (rc) { + case -1: + if (errno == EINTR) + break; + warn("expect: poll() failed (probably ftp died because of bad args)"); + done = 1; + retval = -1; + break; + case 0: + warnx("expect: poll() timeout"); + /* need to send ftp coprocess SIGINT to make it stop + * downloading into dir that we'll blow away in a second */ + kill(ftp_pid, SIGINT); + + /* Wait until ftp coprocess is responsive again + * XXX Entering recursion here! + */ + rc = ftp_cmd("cd .\n", "\n(550|250).*\n"); + if (rc != 250) { + /* now we have a really good reason to bail out ;) */ + } + /* ftp is at command prompt again, and will wait for our + * next command. If we were downloading, we can now safely + * continue and remove the dir that the tar command was + * expanding to */ - done = 1; /* hope that's ok */ - retval = -1; - break; - default: - if (set[0].revents & POLLHUP) { - done = 1; - retval = -1; - break; - } + done = 1; /* hope that's ok */ + retval = -1; + break; + default: + if (set[0].revents & POLLHUP) { + done = 1; + retval = -1; + break; + } - rc = read(fd, &buf[sizeof(buf) - 1], 1); - if (rc <= 0) { - done = 1; - retval = -1; - break; - } + rc = read(fd, &buf[sizeof(buf) - 1], 1); + if (rc <= 0) { + done = 1; + retval = -1; + break; + } - if (verbose_expect) - putchar(buf[sizeof(buf)-1]); + if (verbose_expect) + putchar(buf[sizeof(buf)-1]); #if EXPECT_DEBUG - { - char *v=malloc(2*sizeof(buf)); - strvis(v, buf, VIS_NL|VIS_SAFE|VIS_CSTYLE); - if (expect_debug) - printf("expect=<%s>, buf=<%*s>\n", vstr, strlen(v), v); - free(v); - } + { + char *v=malloc(2*sizeof(buf)); + strvis(v, buf, VIS_NL|VIS_SAFE|VIS_CSTYLE); + if (expect_debug) + printf("expect=<%s>, buf=<%*s>\n", vstr, strlen(v), v); + free(v); + } #endif /* EXPECT_DEBUG */ - if (regexec(&rstr, buf, 1, &match, 0) == 0) { + if (regexec(&rstr, buf, 1, &match, 0) == 0) { #if EXPECT_DEBUG - if (expect_debug) - printf("Gotcha -> %s!\n", buf+match.rm_so+1); - fflush(stdout); + if (expect_debug) + printf("Gotcha -> %s!\n", buf+match.rm_so+1); + fflush(stdout); #endif /* EXPECT_DEBUG */ - if (ftprc && isdigit(buf[match.rm_so+1])) - *ftprc = atoi(buf+match.rm_so+1); + if (ftprc && isdigit(buf[match.rm_so+1])) + *ftprc = atoi(buf+match.rm_so+1); - done=1; - retval=0; - } + done=1; + retval=0; + } - memmove(buf, buf+1, sizeof(buf)-1); /* yes, this is non-performant */ - break; + memmove(buf, buf+1, sizeof(buf)-1); /* yes, this is non-performant */ + break; + } } - } #if EXPECT_DEBUG - printf("done.\n"); + printf("done.\n"); - if (str) - free(vstr); + if (str) + free(vstr); #endif /* EXPECT_DEBUG */ - return retval; + return retval; } /* @@ -266,29 +308,29 @@ expect(int fd, const char *str, int *ftprc) int ftp_cmd(const char *cmd, const char *expectstr) { - int rc=0, verbose_ftp=0; - int len; + int rc=0, verbose_ftp=0; + int len; - if (Verbose) - verbose_ftp=1; + if (Verbose) + verbose_ftp=1; - if (verbose_ftp) - fprintf(stderr, "\n%sftp> %s%s", bold_on, cmd, bold_off); + if (verbose_ftp) + fprintf(stderr, "\n%sftp> %s%s", bold_on, cmd, bold_off); - fflush(stdout); - len = write(ftpio.command, cmd, strlen(cmd)); - if (len == strlen(cmd)) { - if (expectstr) { - /* set "rc" to the FTP error code: */ - if (expect(ftpio.answer, expectstr, &rc) == -1) - rc = -1; /* some error occurred */ - } - } else { - if (Verbose) - warn("short write"); - } - - return rc; + fflush(stdout); + len = write(ftpio.command, cmd, strlen(cmd)); + if (len == strlen(cmd)) { + if (expectstr) { + /* set "rc" to the FTP error code: */ + if (expect(ftpio.answer, expectstr, &rc) == -1) + rc = -1; /* some error occurred */ + } + } else { + if (Verbose) + warn("short write"); + } + + return rc; } @@ -298,85 +340,85 @@ ftp_cmd(const char *cmd, const char *expectstr) static int setupCoproc(const char *base) { - int command_pipe[2]; - int answer_pipe[2]; - int rc1, rc2; - char buf[20]; - char *argv0 = strrchr(FTP_CMD, '/'); - if (argv0 == NULL) - argv0 = FTP_CMD; - else - argv0++; - - rc1 = pipe(command_pipe); - rc2 = pipe(answer_pipe); - - if(rc1==-1 || rc2==-1) { - warn("setupCoproc: pipe() failed"); - return -1; - } - - if (command_pipe[0] == -1 || command_pipe[1] == -1 || - answer_pipe[0] == -1 || answer_pipe[1] == -1 ) { - warn("setupCoproc: pipe() returned bogus descriptor"); - return -1; - } - - rc1 = fork(); - switch (rc1) { - case -1: - /* Error */ + int command_pipe[2]; + int answer_pipe[2]; + int rc1, rc2; + char buf[20]; + char *argv0 = strrchr(FTP_CMD, '/'); + if (argv0 == NULL) + argv0 = FTP_CMD; + else + argv0++; + + rc1 = pipe(command_pipe); + rc2 = pipe(answer_pipe); + + if(rc1==-1 || rc2==-1) { + warn("setupCoproc: pipe() failed"); + return -1; + } + + if (command_pipe[0] == -1 || command_pipe[1] == -1 || + answer_pipe[0] == -1 || answer_pipe[1] == -1 ) { + warn("setupCoproc: pipe() returned bogus descriptor"); + return -1; + } + + rc1 = fork(); + switch (rc1) { + case -1: + /* Error */ - warn("setupCoproc: fork() failed"); - return -1; - break; + warn("setupCoproc: fork() failed"); + return -1; + break; - case 0: - /* Child */ + case 0: + /* Child */ - (void) close(command_pipe[1]); - rc1 = dup2(command_pipe[0], 0); - if (rc1 == -1) { - err(EXIT_FAILURE, "setupCoproc: dup2 failed (command_pipe[0])"); - } - (void) close(command_pipe[0]); + (void) close(command_pipe[1]); + rc1 = dup2(command_pipe[0], 0); + if (rc1 == -1) { + err(EXIT_FAILURE, "setupCoproc: dup2 failed (command_pipe[0])"); + } + (void) close(command_pipe[0]); - (void) close(answer_pipe[0]); - rc1 = dup2(answer_pipe[1], 1); - if (rc1 == -1) { - err(EXIT_FAILURE, "setupCoproc: dup2 failed (answer_pipe[1])"); - } - (void) close(answer_pipe[1]); + (void) close(answer_pipe[0]); + rc1 = dup2(answer_pipe[1], 1); + if (rc1 == -1) { + err(EXIT_FAILURE, "setupCoproc: dup2 failed (answer_pipe[1])"); + } + (void) close(answer_pipe[1]); - setbuf(stdout, NULL); + setbuf(stdout, NULL); - if (Verbose) - fprintf(stderr, "%sftp -detv %s%s\n", bold_on, base, bold_off); - rc1 = execlp(FTP_CMD, argv0, "-detv", base, NULL); - warn("setupCoproc: execlp() failed"); - exit(1); - break; - default: - /* Parent */ - (void) close(command_pipe[0]); - (void) close(answer_pipe[1]); + if (Verbose) + fprintf(stderr, "%sftp -detv %s%s\n", bold_on, base, bold_off); + rc1 = execlp(FTP_CMD, argv0, "-detv", base, NULL); + warn("setupCoproc: execlp() failed"); + exit(1); + break; + default: + /* Parent */ + (void) close(command_pipe[0]); + (void) close(answer_pipe[1]); - (void) snprintf(buf, sizeof(buf), "%d", command_pipe[1]); - setenv(PKG_FTPIO_COMMAND, buf, 1); - (void) snprintf(buf, sizeof(buf), "%d", answer_pipe[0]); - setenv(PKG_FTPIO_ANSWER, buf, 1); + (void) snprintf(buf, sizeof(buf), "%d", command_pipe[1]); + setenv(PKG_FTPIO_COMMAND, buf, 1); + (void) snprintf(buf, sizeof(buf), "%d", answer_pipe[0]); + setenv(PKG_FTPIO_ANSWER, buf, 1); - ftpio.command = command_pipe[1]; - ftpio.answer = answer_pipe[0]; - ftp_pid = rc1; /* to ^C transfers */ + ftpio.command = command_pipe[1]; + ftpio.answer = answer_pipe[0]; + ftp_pid = rc1; /* to ^C transfers */ - fcntl(ftpio.command, F_SETFL, O_NONBLOCK); - fcntl(ftpio.answer , F_SETFL, O_NONBLOCK); + fcntl(ftpio.command, F_SETFL, O_NONBLOCK); + fcntl(ftpio.answer , F_SETFL, O_NONBLOCK); - break; - } + break; + } - return 0; + return 0; } @@ -399,10 +441,10 @@ sigchld_handler (int n) static void sigpipe_handler(int n) { - /* aparently our ftp companion died */ - if (Verbose) - fprintf(stderr, "SIGPIPE!\n"); - needclose = 0; + /* aparently our ftp companion died */ + if (Verbose) + fprintf(stderr, "SIGPIPE!\n"); + needclose = 0; } @@ -451,7 +493,7 @@ ftp_stop(void) * coprocess is currently at, close first. */ int -ftp_start(char *base) +ftp_start(const char *base) { const char *tmp1, *tmp2; char *p; @@ -462,7 +504,6 @@ ftp_start(char *base) const char *currentDir=getenv(PKG_FTPIO_CURRENTDIR); int urllen; -#if HAVE_TGETENT /* talk to termcap for bold on/off escape sequences */ if (getenv("TERM") != NULL && tgetent(term, getenv("TERM")) > 0) { p = bold_on; tgetstr("md", &p); @@ -471,10 +512,6 @@ ftp_start(char *base) bold_on[0] = '\0'; bold_off[0] = '\0'; } -#else - bold_on[0] = '\0'; - bold_off[0] = '\0'; -#endif fileURLHost(base, newHost, sizeof(newHost)); urllen = URLlength(base); @@ -523,7 +560,7 @@ ftp_start(char *base) return -1; } - /* lukemftp now issues a CWD for each part of the path + /* nbftp now issues a CWD for each part of the path * and will return a code for each of them. No idea how to * deal with that other than to issue a 'prompt off' to * get something that we can wait for and that does NOT @@ -570,71 +607,98 @@ ftp_start(char *base) int expandURL(char *expandedurl, const char *wildcardurl) { - char *pkg; - int rc; - char base[FILENAME_MAX]; - - pkg=strrchr(wildcardurl, '/'); - if (pkg == NULL){ - warnx("expandURL: no '/' in URL %s?!", wildcardurl); - return -1; - } - (void) snprintf(base, sizeof(base), "%*.*s/", (int)(pkg-wildcardurl), - (int)(pkg-wildcardurl), wildcardurl); - pkg++; - - rc = ftp_start(base); - if (rc == -1) { - warnx("ftp_start() failed"); - return -1; /* error */ - } - - /* for a given wildcard URL, find the best matching pkg */ - { + char *pattern; + char *bestmatch; + char base[FILENAME_MAX]; + + pattern=strrchr(wildcardurl, '/'); + if (pattern == NULL){ + warnx("expandURL: no '/' in URL %s?!", wildcardurl); + return -1; + } + if (pattern-strchr(wildcardurl, '/') < 2) { + /* only one or two slashes in total */ + warnx("expandURL: not enough '/' in URL %s", wildcardurl); + return -1; + } + (void) snprintf(base, sizeof(base), "%.*s/", + (int)(pattern-wildcardurl), wildcardurl); + pattern++; + + if (strncmp(wildcardurl, "ftp://", 6) == 0) + bestmatch=ftp_expand_URL(base, pattern); + else if (strncmp(wildcardurl, "http://", 7) == 0) + bestmatch=http_expand_URL(base, pattern); + else { + warnx("expandURL: unknown protocol in URL `%s'", wildcardurl); + return -1; + } + + /* no match found */ + if (bestmatch == NULL) + return -1; + + snprintf(expandedurl, FILENAME_MAX, "%s%s", base, bestmatch); + if (Verbose) + printf("best match: '%s'\n", expandedurl); + + return 0; +} + +/* for a given wildcard ftp:// URL, find the best matching pkg */ +static char * +ftp_expand_URL(const char *base, char *pattern) +{ char *s, buf[FILENAME_MAX]; char tmpname[FILENAME_MAX]; char best[FILENAME_MAX]; - int tfd; + int rc, tfd; + + rc = ftp_start(base); + if (rc == -1) { + warnx("ftp_start() failed"); + return NULL; + } strlcpy(tmpname, "/var/tmp/pkg.XXXXXX", sizeof(tmpname)); tfd=mkstemp(tmpname); if (tfd == -1) { warnx("Cannot generate temp file for ftp(1)'s nlist output"); - return -1; /* error */ + return NULL; } close(tfd); /* We don't need the file descriptor, but will use the file in a second */ - s=strpbrk(pkg, "<>[]?*{"); /* Could leave out "[]?*" here; - * ftp(1) is not that stupid */ + s=strpbrk(pattern, "<>[]?*{"); /* Could leave out "[]?*" here; + * ftp(1) is not that stupid */ if (!s) { /* This should only happen when getting here with (only) a package * name specified to pkg_add, and PKG_PATH containing some URL. */ - (void) snprintf(buf, sizeof(buf), "nlist %s %s\n", pkg, tmpname); + (void) snprintf(buf, sizeof(buf), "nlist %s %s\n", pattern, tmpname); } else { /* replace possible version(wildcard) given with "-*". * we can't use the pkg wildcards here as dewey compare * and alternates won't be handled by ftp(1); sort * out later, using pmatch() */ - (void) snprintf(buf, sizeof(buf), "nlist %*.*s*.t[bg]z %s\n", - (int)(s-pkg), (int)(s-pkg), pkg, tmpname); + (void) snprintf(buf, sizeof(buf), "nlist %.*s*.t[bg]z %s\n", + (int)(s-pattern), pattern, tmpname); } - + rc = ftp_cmd(buf, "\n(550|226).*\n"); /* catch errors */ if (rc != 226) { - if (Verbose) - warnx("nlist failed!"); - unlink(tmpname); /* remove clutter */ - return -1; + if (Verbose) + warnx("nlist failed!"); + unlink(tmpname); /* remove clutter */ + return NULL; } /* Sync - don't remove */ rc = ftp_cmd("cd .\n", "\n(550|250).*\n"); if (rc != 250) { - warnx("chdir failed!"); - unlink(tmpname); /* remove clutter */ - return -1; + warnx("chdir failed!"); + unlink(tmpname); /* remove clutter */ + return NULL; } best[0]='\0'; @@ -642,12 +706,12 @@ expandURL(char *expandedurl, const char *wildcardurl) int matches; FILE *f; char filename[FILENAME_MAX]; - + f=fopen(tmpname, "r"); if (f == NULL) { - warn("fopen"); - unlink(tmpname); /* remove clutter */ - return -1; + warn("fopen"); + unlink(tmpname); /* remove clutter */ + return NULL; } matches=0; /* The following loop is basically the same as the readdir() loop @@ -655,19 +719,19 @@ expandURL(char *expandedurl, const char *wildcardurl) while (fgets(filename, sizeof(filename), f)) { /* - * We need to stripp of any .t[bg]z etc. + * We need to strip off any .t[bg]z etc. * suffix here */ char s_filename[FILENAME_MAX]; - char s_pkg[FILENAME_MAX]; - + char s_pattern[FILENAME_MAX]; + filename[strlen(filename)-1] = '\0'; strip_txz(s_filename, NULL, filename); - strip_txz(s_pkg, NULL, pkg); - - if (pmatch(s_pkg, s_filename)) { + strip_txz(s_pattern, NULL, pattern); + + if (pmatch(s_pattern, s_filename)) { matches++; /* compare findbestmatchingname() */ @@ -675,26 +739,426 @@ expandURL(char *expandedurl, const char *wildcardurl) } } (void) fclose(f); - + if (matches == 0 && Verbose) warnx("nothing appropriate found"); } unlink(tmpname); - if (best[0] != '\0') { + if (best[0] == '\0') + return NULL; + + return strdup(best); +} + +/* for a given wildcard http:// URL, find the best matching pkg */ +static char * +http_expand_URL(const char *base, char *pattern) +{ + char best[FILENAME_MAX]; + char line[BUFSIZ]; + char filename[FILENAME_MAX]; + FILE *fp; + int pipefds[2]; + int state; + pid_t pid; + + *best = '\0'; + + /* Set up a pipe for getting the file list */ + if (pipe(pipefds) == -1) { + warnx("cannot create pipe"); + return NULL; + } + if ((pid = fork()) == -1) { + warnx("cannot fork ftp process"); + return NULL; + } + if (pid == 0) { /* The child */ + if (dup2(pipefds[1], STDOUT_FILENO) == -1) { + warnx("dup2 failed before starting ftp"); + _exit(2); + } + close(pipefds[0]); + close(pipefds[1]); + /* get URL contents to stdout and thus to parent, + * silently */ + execlp("ftp", "ftp", "-V", "-o", "-", base, NULL); + warnx("failed to execute ftp"); + _exit(2); + } + + /* parent */ + close(pipefds[1]); + + if ((fp=fdopen(pipefds[0], "r")) == NULL) + warn("can't fdopen pipe end"); + else { + char s_pattern[FILENAME_MAX]; + int len, offset; + + /* strip of .t[bg]z for comparison */ + strip_txz(s_pattern, NULL, pattern); + + /* initialize http_extract_fn internal state */ + http_extract_fn(NULL, NULL, 0); + + /* read line from HTTP output and extract filenames */ + while (fgets(line, sizeof(line), fp) != NULL) { + len = offset = 0; + while ((len=http_extract_fn(line+offset, filename, + sizeof(filename))) > 0) { + char s_filename[FILENAME_MAX]; + + offset += len; + strip_txz(s_filename, NULL, filename); + + if (pmatch(s_pattern, s_filename)) { + /* compare findbestmatchingname() */ + findbestmatchingname_fn(filename, + best); + } + } + } + + } + + /* wait for child to exit */ + if (waitpid(pid, &state, 0) < 0) { + /* error has been reported by child */ + return NULL; + } + + if (best[0] == '\0') { if (Verbose) - printf("best match: '%s%s'\n", base, best); - snprintf(expandedurl, FILENAME_MAX, "%s%s", base, best); + warnx("nothing appropriate found"); + return NULL; + } + + return strdup(best); + +} + +enum http_states { + ST_NONE, + ST_LT, ST_LTA, ST_TAGA, ST_H, ST_R, ST_E, ST_F, ST_HREF, + ST_TAG, ST_TAGAX +}; + +/* return any hrefs found */ +static int +http_extract_fn(char *input, char *outbuf, size_t outbuflen) +{ + /* partial copied hrefs from previous calls are saved here */ + static char tempbuf[FILENAME_MAX]; + /* fill state of tempbuf */ + static int tempbuffill = 0; + /* parsing state information */ + static enum http_states state; + /* currently in double quotes (in parsing) */ + static int dqflag; + char p; + int offset, found; + + if (outbuf == NULL) { + /* init */ + dqflag = tempbuffill = 0; + state = ST_NONE; + return 0; + } + + offset = 0; + found = 0; + while ((p=input[offset++]) != '\0') { + /* handle anything that's inside double quotes */ + if (dqflag) { + /* incomplete href */ + if (state == ST_HREF) { + /* check if space left in output + * buffer */ + if (tempbuffill >= sizeof(tempbuf)) { + warnx("href starting with `%.*s'" + " too long", 60, tempbuf); + /* ignore remainder */ + tempbuffill = 0; + /* need space before "href" + * can start again (invalidly, + * of course, but we don't + * care) */ + state = ST_TAGAX; + } + + /* href complete */ + if (p == '\"') { + /* complete */ + dqflag = 0; + tempbuf[tempbuffill++] = '\0'; + /* need space before "href" + * can start again (invalidly, + * of course, but we don't + * care) */ + state = ST_TAGAX; + found = 1; + break; + } else { + /* copy one more char */ + tempbuf[tempbuffill++] = p; + } + } else { + /* leaving double quotes */ + if (p == '\"') + dqflag = 0; + } + continue; + } + + /* + * entering double quotes? (only relevant inside a tag) + */ + if (state != ST_NONE && p == '\"') { + dqflag = 1; + continue; + } + + /* other cases */ + switch (state) { + case ST_NONE: + /* plain text, not in markup */ + if (p == '<') + state = ST_LT; + break; + case ST_LT: + /* in tag -- "<" already found */ + if (p == '>') + state = ST_NONE; + else if (p == 'a' || p == 'A') + state = ST_LTA; + else if (!isspace(p)) + state = ST_TAG; + break; + case ST_LTA: + /* in tag -- "<a" already found */ + if (p == '>') + state = ST_NONE; + else if (isspace(p)) + state = ST_TAGA; + else + state = ST_TAG; + break; + case ST_TAG: + /* in tag, but not "<a" -- disregard */ + if (p == '>') + state = ST_NONE; + break; + case ST_TAGA: + /* in a-tag -- "<a " already found */ + if (p == '>') + state = ST_NONE; + else if (p == 'h' || p == 'H') + state = ST_H; + else if (!isspace(p)) + state = ST_TAGAX; + break; + case ST_TAGAX: + /* in unknown keyword in a-tag */ + if (p == '>') + state = ST_NONE; + else if (isspace(p)) + state = ST_TAGA; + break; + case ST_H: + /* in a-tag -- "<a h" already found */ + if (p == '>') + state = ST_NONE; + else if (p == 'r' || p == 'R') + state = ST_R; + else if (isspace(p)) + state = ST_TAGA; + else + state = ST_TAGAX; + break; + case ST_R: + /* in a-tag -- "<a hr" already found */ + if (p == '>') + state = ST_NONE; + else if (p == 'e' || p == 'E') + state = ST_E; + else if (isspace(p)) + state = ST_TAGA; + else + state = ST_TAGAX; + break; + case ST_E: + /* in a-tag -- "<a hre" already found */ + if (p == '>') + state = ST_NONE; + else if (p == 'f' || p == 'F') + state = ST_F; + else if (isspace(p)) + state = ST_TAGA; + else + state = ST_TAGAX; + break; + case ST_F: + /* in a-tag -- "<a href" already found */ + if (p == '>') + state = ST_NONE; + else if (p == '=') + state = ST_HREF; + else if (!isspace(p)) + state = ST_TAGAX; + break; + case ST_HREF: + /* in a-tag -- "<a href=" already found */ + /* XXX: handle missing double quotes? */ + if (p == '>') + state = ST_NONE; + /* skip spaces before URL */ + else if (!isspace(p)) + state = ST_TAGA; + break; + /* no default case by purpose */ + } + } + + if (p == '\0') + return -1; + + if (found) { + char *q; + + URL_decode(tempbuf); + + /* strip path (XXX) */ + if ((q=strrchr(tempbuf, '/')) == NULL) + q = tempbuf; + + (void)strlcpy(outbuf, q, outbuflen); + tempbuffill = 0; } + + return offset; +} + + +static int +hexvalue(char p) +{ + if (p >= '0' && p <= '9') + return (p-'0'); + else if (p >= 'a' && p <= 'f') + return (p-'a'+10); + else if (p >= 'A' && p <= 'F') + return (p-'A'+10); else return -1; - } +} + +/* fetch and extract URL url into directory path */ +static int +http_fetch(const char *url, const char *path) +{ + int pipefds[2]; + int stateftp, state; + pid_t pidftp, pid; + + /* Set up a pipe for passing the fetched contents. */ + if (pipe(pipefds) == -1) { + warn("cannot create pipe"); + return -1; + } + /* fork ftp child */ + if ((pidftp = fork()) == -1) { + warn("cannot fork process for ftp"); + return -1; + } + if (pidftp == 0) { + /* child */ + if (dup2(pipefds[1], STDOUT_FILENO) == -1) { + warn("dup2 failed before executing ftp"); + _exit(2); + } + close(pipefds[0]); + close(pipefds[1]); + execlp(FTP_CMD, FTP_CMD, "-o", "-", url, NULL); + warnx("failed to execute ftp"); + _exit(2); + } + + /* fork unpack child */ + if ((pid = fork()) == -1) { + warn("cannot fork unpack process"); + return -1; + } + if (pid == 0) { + /* child */ + if (dup2(pipefds[0], STDIN_FILENO) == -1) { + warn("dup2 failed before unpack"); + _exit(2); + } + close(pipefds[0]); + close(pipefds[1]); + if ((path != NULL) && (chdir(path) < 0)) + _exit(127); + + if (unpack("-", Verbose ? "-vv" : NULL, NULL) != 0) { + warnx("unpack failed"); + _exit(2); + } + + _exit(0); + } + + close(pipefds[0]); + close(pipefds[1]); + + /* wait for unpack to exit */ + while (waitpid(pid, &state, 0) < 0) { + if (errno != EINTR) { + (void)waitpid(pidftp, &stateftp, 0); + return -1; + } + } + while (waitpid(pidftp, &stateftp, 0) < 0) { + if (errno != EINTR) { + return -1; + } + } + + if (!WIFEXITED(state) || !WIFEXITED(stateftp)) + return -1; - return 0; + if (WEXITSTATUS(state) != 0 || WEXITSTATUS(stateftp) != 0) + return -1; + + return 0; } +static void +URL_decode(char *URL) +{ + char *in, *out; + + in = out = URL; + + while (*in != '\0') { + if (in[0] == '%' && in[1] != '\0' && in[2] != '\0') { + /* URL-decode character */ + if (hexvalue(in[1]) != -1 && hexvalue(in[2]) != -1) { + *out++ = hexvalue(in[1])*16+hexvalue(in[2]); + } + /* skip invalid encoded signs too */ + in += 3; + } + else + *out++ = *in++; + } + + *out = '\0'; + return; +} /* * extract the given (expanded) URL "url" to the given directory "dir" * return -1 on error, 0 else; @@ -728,9 +1192,8 @@ unpackURL(const char *url, const char *dir) warnx("unpackURL: no '/' in URL %s?!", url); return -1; } - (void) snprintf(base, sizeof(base), "%*.*s/", (int)(pkg-url), - (int)(pkg-url), url); - (void) snprintf(pkg_path, sizeof(pkg_path), "%*.*s", (int)(pkg-url), + (void) snprintf(base, sizeof(base), "%.*s/", (int)(pkg-url), url); + (void) snprintf(pkg_path, sizeof(pkg_path), "%.*s", (int)(pkg-url), url); /* no trailing '/' */ pkg++; @@ -742,7 +1205,10 @@ unpackURL(const char *url, const char *dir) #endif printf("setenv PKG_PATH='%s'\n",pkg_path); } - + + if (strncmp(url, "http://", 7) == 0) + return http_fetch(url, dir); + rc = ftp_start(base); if (rc == -1) { warnx("ftp_start() failed"); @@ -768,186 +1234,69 @@ unpackURL(const char *url, const char *dir) } -#if 0 -/* - * Some misc stuff not needed yet, but maybe later - */ -int -miscstuff(const char *url) -{ - char *pkg; - int rc; - char base[FILENAME_MAX]; - - pkg=strrchr(url, '/'); - if (pkg == NULL){ - warnx("miscstuff: no '/' in URL %s?!", url); - return -1; - } - (void) snprintf(base, sizeof(base), "%*.*s/", (int)(pkg-url), (int)(pkg-url), - url); - pkg++; - - rc = ftp_start(base); - if (rc == -1) { - warnx("ftp_start() failed"); - return -1; /* error */ - } - - /* basic operation */ - if (0) { - rc = ftp_cmd("cd ../All\n", "\n(550|250).*\n"); - if (rc != 250) { - warnx("chdir failed!"); - return -1; - } - } - - /* get and extract a file to tmpdir */ - if (0) { - char cmd[256]; - char tmpdir[256]; - - (void) snprintf(tmpdir, sizeof(tmpdir), "/var/tmp/dir%s", - (getenv(PKG_FTPIO_CNT))?getenv(PKG_FTPIO_CNT):""); - - mkdir(tmpdir, 0755); - - /* yes, this is gross, but needed for borken ftp(1) */ - (void) snprintf(cmd, sizeof(cmd), "get xpmroot-1.01.tgz \"| ( cd %s ; gunzip 2>/dev/null | tar -vvx -f - )\"\n", tmpdir); - rc = ftp_cmd(cmd, "\n(226|550).*\n"); - if (rc != 226) { - warnx("Cannot fetch file (%d != 226)!", rc); - return -1; - } - } - - /* check if one more file(s) exist */ - if (0) { - char buf[FILENAME_MAX]; - (void) snprintf(buf, sizeof(buf), "nlist %s /var/tmp/xxx\n", pkg); - rc = ftp_cmd(buf, "\n(226|550).*\n"); /* catch errors */ - if (rc != 226) { - if (Verbose) - warnx("nlist failed!"); - return -1; - } - - /* Sync - don't remove */ - rc = ftp_cmd("cd .\n", "\n(550|250).*\n"); - if (rc != 250) { - warnx("chdir failed!"); - return -1; - } - - if (access("/var/tmp/xxx", R_OK)==0) { - fexec("cat", "/var/tmp/xxx", NULL); - - { - /* count lines - >0 -> fexists() == true */ - int len, count; - FILE *f; - - f=fopen("/var/tmp/xxx", "r"); - if (f == NULL) { - warn("fopen"); - return -1; - } - count=0; - while (fgetln(f, &len)) - count++; - (void) fclose(f); - - printf("#lines = %d\n", count); - } - } else - printf("NO MATCH\n"); - - unlink("/var/tmp/xxx"); - } - - /* for a given wildcard URL, find the best matching pkg */ - /* spawn child - like pkg_add'ing another package */ - /* Here's where the connection caching kicks in */ -#if 0 - if (0) { - char *s, buf[FILENAME_MAX]; - - if ((s=getenv(PKG_FTPIO_CNT)) && atoi(s)>0){ - (void) snprintf(buf, sizeof(buf),"%d", atoi(s)-1); - setenv(PKG_FTPIO_CNT, buf, 1); - - (void) snprintf(buf, sizeof(buf), "%s/%s", url, pkg); - printf("%s>>> %s %s\n", s, argv0, buf); - fexec(argv0, buf, NULL); - } - } -#endif - - return 0; - -} -#endif - - #ifdef STANDALONE static void usage(void) { - errx(EXIT_FAILURE, "Usage: foo [-v] ftp://-pattern"); + errx(EXIT_FAILURE, "Usage: foo [-v] ftp://-pattern"); } int main(int argc, char *argv[]) { - int rc, ch; - char *argv0 = argv[0]; - - while ((ch = getopt(argc, argv, "v")) != -1) { - switch (ch) { - case 'v': - Verbose=1; - break; - default: - usage(); - } - } - argc -= optind; - argv += optind; - - if (argc<1) - usage(); - - while(argv[0] != NULL) { - char newurl[FILENAME_MAX]; + int rc, ch; + char *argv0 = argv[0]; + + while ((ch = getopt(argc, argv, "v")) != -1) { + switch (ch) { + case 'v': + Verbose=1; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc<1) + usage(); + + while(argv[0] != NULL) { + char newurl[FILENAME_MAX]; - printf("Expand %s:\n", argv[0]); - rc = expandURL(newurl, argv[0]); - if (rc==-1) - warnx("Cannot expand %s", argv[0]); - else - printf("Expanded URL: %s\n", newurl); - - /* test out connection caching */ - if (1) { - char *s, buf[FILENAME_MAX]; + printf("Expand %s:\n", argv[0]); + rc = expandURL(newurl, argv[0]); + if (rc==-1) + warnx("Cannot expand %s", argv[0]); + else + printf("Expanded URL: %s\n", newurl); + + /* test out connection caching */ + if (1) { + char *s, buf[FILENAME_MAX]; - if ((s=getenv(PKG_FTPIO_CNT)) && atoi(s)>0){ - (void) snprintf(buf, sizeof(buf),"%d", atoi(s)-1); - setenv(PKG_FTPIO_CNT, buf, 1); + if ((s=getenv(PKG_FTPIO_CNT)) && atoi(s)>0){ + (void) snprintf(buf, sizeof(buf),"%d", atoi(s)-1); + setenv(PKG_FTPIO_CNT, buf, 1); - printf("%s>>> %s -v %s\n", s, argv0, argv[0]); - fexec(argv0, "-v", argv[0], NULL); - } - } + printf("%s>>> %s -v %s\n", s, argv0, argv[0]); + fexec(argv0, "-v", argv[0], NULL); + } + } - printf("\n\n\n"); - argv++; - } + printf("\n\n\n"); + argv++; + } - ftp_stop(); + ftp_stop(); - return 0; + return 0; +} + +void +cleanup(int i) +{ } #endif /* STANDALONE */ |