--- /dev/null
+#include "system.h"
+
+#if !defined(HAVE_CONFIG_H)
+#define HAVE_MACHINE_TYPES_H 1
+#define HAVE_ALLOCA_H 1
+#define HAVE_NETINET_IN_SYSTM_H 1
+#define HAVE_SYS_SOCKET_H 1
+#endif
+
+#ifndef __LCLINT__
+#if HAVE_MACHINE_TYPES_H
+# include <machine/types.h>
+#endif
+#endif
+
+#if HAVE_NETINET_IN_SYSTM_H
+# include <sys/types.h>
+# include <netinet/in_systm.h>
+#endif
+
+#if ! HAVE_HERRNO
+extern int h_errno;
+#endif
+
+#include <stdarg.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+
+#include "rpmlib.h"
+#include "rpmio.h"
+
+#if !defined(HAVE_INET_ATON)
+int inet_aton(const char *cp, struct in_addr *inp);
+#endif
+
+#define TIMEOUT_SECS 60
+#define BUFFER_SIZE 4096
+
+#if defined(USE_ALT_DNS) && USE_ALT_DNS
+#include "dns.h"
+#endif
+
+#include "rpmurl.h"
+
+#ifdef __MINT__
+# ifndef EAGAIN
+# define EAGAIN EWOULDBLOCK
+# endif
+# ifndef O_NONBLOCK
+# define O_NONBLOCK O_NDELAY
+# endif
+#endif
+
+static int ftpDebug = 0;
+static int ftpTimeoutSecs = TIMEOUT_SECS;
+static int httpTimeoutSecs = TIMEOUT_SECS;
+
+static rpmCallbackFunction urlNotify = NULL;
+static void * urlNotifyData = NULL;
+static int urlNotifyCount = -1;
+
+void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
+ urlNotify = notify;
+ urlNotifyData = notifyData;
+ urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
+}
+
+static int checkResponse(int fd, int secs, int *ecp, char ** str) {
+ static char buf[BUFFER_SIZE + 1];
+ int bufLength = 0;
+ fd_set emptySet, readSet;
+ char *se, *s;
+ struct timeval timeout;
+ int bytesRead, rc = 0;
+ int doesContinue = 1;
+ char errorCode[4];
+
+ errorCode[0] = '\0';
+
+ do {
+ /*
+ * XXX In order to preserve both getFile and getFd methods with
+ * XXX HTTP, the response is read 1 char at a time with breaks on
+ * XXX newlines.
+ */
+ do {
+ FD_ZERO(&emptySet);
+ FD_ZERO(&readSet);
+ FD_SET(fd, &readSet);
+
+ timeout.tv_sec = secs;
+ timeout.tv_usec = 0;
+
+ rc = select(fd + 1, &readSet, &emptySet, &emptySet, &timeout);
+ if (rc < 1) {
+ if (rc == 0)
+ return FTPERR_BAD_SERVER_RESPONSE;
+ else
+ rc = FTPERR_UNKNOWN;
+ } else
+ rc = 0;
+
+ s = buf + bufLength;
+ bytesRead = read(fd, s, 1);
+ bufLength += bytesRead;
+ buf[bufLength] = '\0';
+ } while (bufLength < sizeof(buf) && *s != '\n');
+
+ /*
+ * Divide the response into lines. Skip continuation lines.
+ */
+ s = se = buf;
+ while (*se != '\0') {
+ while (*se && *se != '\n') se++;
+
+ if (se > s && se[-1] == '\r')
+ se[-1] = '\0';
+ if (*se == '\0')
+ break;
+
+ /* HTTP header termination on empty line */
+ if (*s == '\0') {
+ doesContinue = 0;
+ break;
+ }
+ *se++ = '\0';
+
+ /* HTTP: look for "HTTP/1.1 123 ..." */
+ if (!strncmp(s, "HTTP", 4)) {
+ char *e;
+ if ((e = strchr(s, ' ')) != NULL) {
+ e++;
+ if (strchr("0123456789", *e))
+ strncpy(errorCode, e, 3);
+ errorCode[3] = '\0';
+ }
+ s = se;
+ continue;
+ }
+
+ /* FTP: look for "123-" and/or "123 " */
+ if (strchr("0123456789", *s)) {
+ if (errorCode[0]) {
+ if (!strncmp(s, errorCode, 3) && s[3] == ' ')
+ doesContinue = 0;
+ } else {
+ strncpy(errorCode, s, 3);
+ errorCode[3] = '\0';
+ if (s[3] != '-') {
+ doesContinue = 0;
+ }
+ }
+ }
+ s = se;
+ }
+
+ if (doesContinue && se > s) {
+ bufLength = se - s - 1;
+ if (s != buf)
+ memcpy(buf, s, bufLength);
+ } else {
+ bufLength = 0;
+ }
+ } while (doesContinue && !rc);
+
+if (ftpDebug)
+fprintf(stderr, "<- %s\n", buf);
+
+ if (str) *str = buf;
+ if (ecp) *ecp = atoi(errorCode);
+
+ return rc;
+}
+
+static int ftpCheckResponse(urlinfo *u, char ** str) {
+ int ec = 0;
+ int rc = checkResponse(u->ftpControl, ftpTimeoutSecs, &ec, str);
+
+ switch (ec) {
+ case 550:
+ return FTPERR_FILE_NOT_FOUND;
+ break;
+ case 552:
+ return FTPERR_NIC_ABORT_IN_PROGRESS;
+ break;
+ default:
+ if (ec >= 400 && ec <= 599)
+ return FTPERR_BAD_SERVER_RESPONSE;
+ break;
+ }
+ return rc;
+}
+
+static int ftpCommand(urlinfo *u, char * command, ...) {
+ va_list ap;
+ int len;
+ char * s;
+ char * buf;
+
+ va_start(ap, command);
+ len = strlen(command) + 2;
+ s = va_arg(ap, char *);
+ while (s) {
+ len += strlen(s) + 1;
+ s = va_arg(ap, char *);
+ }
+ va_end(ap);
+
+ buf = alloca(len + 1);
+
+ va_start(ap, command);
+ strcpy(buf, command);
+ strcat(buf, " ");
+ s = va_arg(ap, char *);
+ while (s) {
+ strcat(buf, s);
+ strcat(buf, " ");
+ s = va_arg(ap, char *);
+ }
+ va_end(ap);
+
+ buf[len - 2] = '\r';
+ buf[len - 1] = '\n';
+ buf[len] = '\0';
+
+if (ftpDebug)
+fprintf(stderr, "-> %s", buf);
+ if (write(u->ftpControl, buf, len) != len) {
+ return FTPERR_SERVER_IO_ERROR;
+ }
+
+ return ftpCheckResponse(u, NULL);
+}
+
+#if !defined(USE_ALT_DNS) || !USE_ALT_DNS
+static int mygethostbyname(const char * host, struct in_addr * address) {
+ struct hostent * hostinfo;
+
+ hostinfo = gethostbyname(host);
+ if (!hostinfo) return 1;
+
+ memcpy(address, hostinfo->h_addr_list[0], hostinfo->h_length);
+ return 0;
+}
+#endif
+
+static int getHostAddress(const char * host, struct in_addr * address) {
+ if (isdigit(host[0])) {
+ if (!inet_aton(host, address)) {
+ return FTPERR_BAD_HOST_ADDR;
+ }
+ } else {
+ if (mygethostbyname(host, address)) {
+ errno = h_errno;
+ return FTPERR_BAD_HOSTNAME;
+ }
+ }
+
+ return 0;
+}
+
+static int tcpConnect(const char *host, int port)
+{
+ struct sockaddr_in sin;
+ int sock = -1;
+ int rc;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ do {
+ if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
+ break;
+
+ if ((sock = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
+ rc = FTPERR_FAILED_CONNECT;
+ break;
+ }
+
+ if (connect(sock, (struct sockaddr *) &sin, sizeof(sin))) {
+ rc = FTPERR_FAILED_CONNECT;
+ break;
+ }
+ } while (0);
+
+ if (rc < 0 && sock >= 0) {
+ close(sock);
+ return rc;
+ }
+
+if (ftpDebug)
+fprintf(stderr,"++ connect %s:%d on fd %d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), sock);
+
+ return sock;
+}
+
+int httpOpen(urlinfo *u)
+{
+ int sock;
+ const char *host;
+ const char *path;
+ int port;
+ char *buf;
+ size_t len;
+
+ if (u == NULL || ((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
+ return FTPERR_BAD_HOSTNAME;
+
+ if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
+
+ path = (u->proxyh || u->proxyp > 0) ? u->url : u->path;
+ if ((sock = tcpConnect(host, port)) < 0)
+ return sock;
+
+ len = strlen(path) + sizeof("GET HTTP/1.0\r\n\r\n");
+ buf = alloca(len);
+ strcpy(buf, "GET ");
+ strcat(buf, path);
+ strcat(buf, " HTTP/1.0\r\n");
+ strcat(buf, "\r\n");
+
+ if (write(sock, buf, len) != len) {
+ close(sock);
+ return FTPERR_SERVER_IO_ERROR;
+ }
+
+if (ftpDebug)
+fprintf(stderr, "-> %s", buf);
+
+ { int ec = 0;
+ int rc;
+ rc = checkResponse(sock, httpTimeoutSecs, &ec, NULL);
+
+ switch (ec) {
+ default:
+ if (rc == 0 && ec != 200) /* not HTTP_OK */
+ rc = FTPERR_FILE_NOT_FOUND;
+ break;
+ }
+
+ if (rc < 0) {
+ close(sock);
+ return rc;
+ }
+ }
+
+ return sock;
+}
+
+int ftpOpen(urlinfo *u)
+{
+ const char * host;
+ const char * user;
+ const char * password;
+ int port;
+ int rc;
+
+ if (u == NULL || ((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
+ return FTPERR_BAD_HOSTNAME;
+
+ if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
+
+ if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
+ user = "anonymous";
+
+ if ((password = u->password) == NULL) {
+ if (getuid()) {
+ struct passwd * pw = getpwuid(getuid());
+ char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
+ strcpy(myp, pw->pw_name);
+ strcat(myp, "@");
+ password = myp;
+ } else {
+ password = "root@";
+ }
+ }
+
+ if ((u->ftpControl = tcpConnect(host, port)) < 0)
+ return u->ftpControl;
+
+ /* ftpCheckResponse() assumes the socket is nonblocking */
+ if (fcntl(u->ftpControl, F_SETFL, O_NONBLOCK)) {
+ rc = FTPERR_FAILED_CONNECT;
+ goto errxit;
+ }
+
+ if ((rc = ftpCheckResponse(u, NULL))) {
+ return rc;
+ }
+
+ if ((rc = ftpCommand(u, "USER", user, NULL)))
+ goto errxit;
+
+ if ((rc = ftpCommand(u, "PASS", password, NULL)))
+ goto errxit;
+
+ if ((rc = ftpCommand(u, "TYPE", "I", NULL)))
+ goto errxit;
+
+ return u->ftpControl;
+
+errxit:
+ close(u->ftpControl);
+ u->ftpControl = -1;
+ return rc;
+}
+
+static int copyData(FD_t sfd, FD_t tfd) {
+ char buf[BUFFER_SIZE];
+ fd_set emptySet, readSet;
+ struct timeval timeout;
+ int bytesRead;
+ int bytesCopied = 0;
+ int rc;
+ int notifier = -1;
+
+ if (urlNotify) {
+ (*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
+ 0, 0, NULL, urlNotifyData);
+ }
+
+ while (1) {
+ FD_ZERO(&emptySet);
+ FD_ZERO(&readSet);
+ FD_SET(fdFileno(sfd), &readSet);
+
+ timeout.tv_sec = ftpTimeoutSecs;
+ timeout.tv_usec = 0;
+
+ rc = select(fdFileno(sfd) + 1, &readSet, &emptySet, &emptySet, &timeout);
+ if (rc == 0) {
+ rc = FTPERR_SERVER_TIMEOUT;
+ break;
+ } else if (rc < 0) {
+ rc = FTPERR_UNKNOWN;
+ break;
+ }
+
+ bytesRead = fdRead(sfd, buf, sizeof(buf));
+ if (bytesRead == 0) {
+ rc = 0;
+ break;
+ }
+
+ if (fdWrite(tfd, buf, bytesRead) != bytesRead) {
+ rc = FTPERR_FILE_IO_ERROR;
+ break;
+ }
+ bytesCopied += bytesRead;
+ if (urlNotify && urlNotifyCount > 0) {
+ int n = bytesCopied/urlNotifyCount;
+ if (n != notifier) {
+ (*urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
+ bytesCopied, 0, NULL, urlNotifyData);
+ notifier = n;
+ }
+ }
+ }
+
+if (ftpDebug)
+fprintf(stderr, "++ copied %d bytes: %s\n", bytesCopied, ftpStrerror(rc));
+
+ if (urlNotify) {
+ (*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
+ bytesCopied, bytesCopied, NULL, urlNotifyData);
+ }
+
+ fdClose(sfd);
+ return rc;
+}
+
+int ftpAbort(FD_t fd) {
+ urlinfo *u = (urlinfo *)fd->fd_url;
+ char buf[BUFFER_SIZE];
+ int rc;
+ int tosecs = ftpTimeoutSecs;
+
+if (ftpDebug)
+fprintf(stderr, "-> ABOR\n");
+
+ sprintf(buf, "%c%c%c", IAC, IP, IAC);
+ send(u->ftpControl, buf, 3, MSG_OOB);
+ sprintf(buf, "%cABOR\r\n", DM);
+ if (write(u->ftpControl, buf, 7) != 7) {
+ close(u->ftpControl);
+ u->ftpControl = -1;
+ return FTPERR_SERVER_IO_ERROR;
+ }
+ if (fdFileno(fd) >= 0) {
+ while(read(fdFileno(fd), buf, sizeof(buf)) > 0)
+ ;
+ }
+
+ ftpTimeoutSecs = 10;
+ if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
+ rc = ftpCheckResponse(u, NULL);
+ }
+ rc = ftpCheckResponse(u, NULL);
+ ftpTimeoutSecs = tosecs;
+
+ if (fdFileno(fd) >= 0)
+ fdClose(fd);
+ return 0;
+}
+
+static int ftpGetFileDone(urlinfo *u) {
+ if (u->ftpGetFileDoneNeeded) {
+ u->ftpGetFileDoneNeeded = 0;
+ if (ftpCheckResponse(u, NULL))
+ return FTPERR_BAD_SERVER_RESPONSE;
+ }
+ return 0;
+}
+
+int ftpGetFileDesc(FD_t fd)
+{
+ urlinfo *u;
+ const char *remotename;
+ struct sockaddr_in dataAddress;
+ int i, j;
+ char * passReply;
+ char * chptr;
+ char * retrCommand;
+ int rc;
+
+ u = (urlinfo *)fd->fd_url;
+ remotename = u->path;
+
+/*
+ * XXX When ftpGetFileDesc() is called, there may be a lurking
+ * XXX transfer complete message (if ftpGetFileDone() was not
+ * XXX called to clear that message). Clear that message now.
+ */
+
+ if (u->ftpGetFileDoneNeeded)
+ rc = ftpGetFileDone(u);
+
+if (ftpDebug)
+fprintf(stderr, "-> PASV\n");
+ if (write(u->ftpControl, "PASV\r\n", 6) != 6)
+ return FTPERR_SERVER_IO_ERROR;
+
+ if ((rc = ftpCheckResponse(u, &passReply)))
+ return FTPERR_PASSIVE_ERROR;
+
+ chptr = passReply;
+ while (*chptr && *chptr != '(') chptr++;
+ if (*chptr != '(') return FTPERR_PASSIVE_ERROR;
+ chptr++;
+ passReply = chptr;
+ while (*chptr && *chptr != ')') chptr++;
+ if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
+ *chptr-- = '\0';
+
+ while (*chptr && *chptr != ',') chptr--;
+ if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
+ chptr--;
+ while (*chptr && *chptr != ',') chptr--;
+ if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
+ *chptr++ = '\0';
+
+ /* now passReply points to the IP portion, and chptr points to the
+ port number portion */
+
+ dataAddress.sin_family = AF_INET;
+ if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
+ return FTPERR_PASSIVE_ERROR;
+ }
+ dataAddress.sin_port = htons((i << 8) + j);
+
+ chptr = passReply;
+ while (*chptr++) {
+ if (*chptr == ',') *chptr = '.';
+ }
+
+ if (!inet_aton(passReply, &dataAddress.sin_addr))
+ return FTPERR_PASSIVE_ERROR;
+
+ fd->fd_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+ if (fdFileno(fd) < 0) {
+ return FTPERR_FAILED_CONNECT;
+ }
+
+ retrCommand = alloca(strlen(remotename) + 20);
+ sprintf(retrCommand, "RETR %s\r\n", remotename);
+ i = strlen(retrCommand);
+
+ while (connect(fdFileno(fd), (struct sockaddr *) &dataAddress,
+ sizeof(dataAddress)) < 0) {
+ if (errno == EINTR)
+ continue;
+ fdClose(fd);
+ return FTPERR_FAILED_DATA_CONNECT;
+ }
+
+if (ftpDebug)
+fprintf(stderr, "-> %s", retrCommand);
+ if (write(u->ftpControl, retrCommand, i) != i) {
+ return FTPERR_SERVER_IO_ERROR;
+ }
+
+ if ((rc = ftpCheckResponse(u, NULL))) {
+ fdClose(fd);
+ return rc;
+ }
+
+ u->ftpGetFileDoneNeeded = 1;
+ return 0;
+}
+
+int httpGetFile(FD_t sfd, FD_t tfd) {
+ return copyData(sfd, tfd);
+}
+
+int ftpGetFile(FD_t sfd, FD_t tfd)
+{
+ urlinfo *u;
+ int rc;
+
+ /* XXX sfd will be freed by copyData -- grab sfd->fd_url now */
+ u = (urlinfo *)sfd->fd_url;
+
+ /* XXX normally sfd = ufdOpen(...) and this code does not execute */
+ if (fdFileno(sfd) < 0 && (rc = ftpGetFileDesc(sfd)) < 0) {
+ fdClose(sfd);
+ return rc;
+ }
+
+ rc = copyData(sfd, tfd);
+ if (rc < 0)
+ return rc;
+
+ return ftpGetFileDone(u);
+}
+
+int ftpClose(FD_t fd) {
+ int fdno = ((urlinfo *)fd->fd_url)->ftpControl;
+ if (fdno >= 0)
+ close(fdno);
+ return 0;
+}
+
+const char *ftpStrerror(int errorNumber) {
+ switch (errorNumber) {
+ case 0:
+ return _("Success");
+
+ case FTPERR_BAD_SERVER_RESPONSE:
+ return _("Bad server response");
+
+ case FTPERR_SERVER_IO_ERROR:
+ return _("Server IO error");
+
+ case FTPERR_SERVER_TIMEOUT:
+ return _("Server timeout");
+
+ case FTPERR_BAD_HOST_ADDR:
+ return _("Unable to lookup server host address");
+
+ case FTPERR_BAD_HOSTNAME:
+ return _("Unable to lookup server host name");
+
+ case FTPERR_FAILED_CONNECT:
+ return _("Failed to connect to server");
+
+ case FTPERR_FAILED_DATA_CONNECT:
+ return _("Failed to establish data connection to server");
+
+ case FTPERR_FILE_IO_ERROR:
+ return _("IO error to local file");
+
+ case FTPERR_PASSIVE_ERROR:
+ return _("Error setting remote server to passive mode");
+
+ case FTPERR_FILE_NOT_FOUND:
+ return _("File not found on server");
+
+ case FTPERR_NIC_ABORT_IN_PROGRESS:
+ return _("Abort in progress");
+
+ case FTPERR_UNKNOWN:
+ default:
+ return _("Unknown or unexpected error");
+ }
+}
+
--- /dev/null
+#include "system.h"
+
+#include <netinet/in.h>
+
+#include "build/rpmbuild.h"
+
+#include "rpmurl.h"
+
+static struct urlstring {
+ const char *leadin;
+ urltype ret;
+} urlstrings[] = {
+ { "file://", URL_IS_PATH },
+ { "ftp://", URL_IS_FTP },
+ { "http://", URL_IS_HTTP },
+ { "-", URL_IS_DASH },
+ { NULL, URL_IS_UNKNOWN }
+};
+
+void freeUrlinfo(urlinfo *u)
+{
+ if (u->ftpControl >= 0)
+ close(u->ftpControl);
+ FREE(u->url);
+ FREE(u->service);
+ FREE(u->user);
+ FREE(u->password);
+ FREE(u->host);
+ FREE(u->portstr);
+ FREE(u->path);
+ FREE(u->proxyu);
+ FREE(u->proxyh);
+
+ FREE(u);
+}
+
+urlinfo *newUrlinfo(void)
+{
+ urlinfo *u;
+ if ((u = malloc(sizeof(*u))) == NULL)
+ return NULL;
+ memset(u, 0, sizeof(*u));
+ u->proxyp = -1;
+ u->port = -1;
+ u->ftpControl = -1;
+ u->ftpGetFileDoneNeeded = 0;
+ return u;
+}
+
+static int urlStrcmp(const char *str1, const char *str2)
+{
+ if (str1 && str2) {
+ return (strcmp(str1, str2));
+ } else
+ if (str1 != str2)
+ return -1;
+ return 0;
+}
+
+static void findUrlinfo(urlinfo **uret, int mustAsk)
+{
+ urlinfo *u;
+ urlinfo **empty;
+ static urlinfo **uCache = NULL;
+ static int uCount = 0;
+ int i;
+
+ if (uret == NULL)
+ return;
+
+ u = *uret;
+
+ empty = NULL;
+ for (i = 0; i < uCount; i++) {
+ urlinfo *ou;
+ if ((ou = uCache[i]) == NULL) {
+ if (empty == NULL)
+ empty = &uCache[i];
+ continue;
+ }
+ /* Check for cache-miss condition. A cache miss is
+ * a) both items are not NULL and don't compare.
+ * b) either of the items is not NULL.
+ */
+ if (urlStrcmp(u->service, ou->service))
+ continue;
+ if (urlStrcmp(u->service, ou->service))
+ continue;
+ if (urlStrcmp(u->host, ou->host))
+ continue;
+ if (urlStrcmp(u->user, ou->user))
+ continue;
+ if (urlStrcmp(u->password, ou->password))
+ continue;
+ if (urlStrcmp(u->portstr, ou->portstr))
+ continue;
+ break; /* Found item in cache */
+ }
+
+ if (i == uCount) {
+ if (empty == NULL) {
+ uCount++;
+ if (uCache)
+ uCache = realloc(uCache, sizeof(*uCache) * uCount);
+ else
+ uCache = malloc(sizeof(*uCache));
+ empty = &uCache[i];
+ }
+ *empty = u;
+ } else {
+ /* Swap original url and path into the cached structure */
+ const char *up = uCache[i]->path;
+ uCache[i]->path = u->path;
+ u->path = up;
+ up = uCache[i]->url;
+ uCache[i]->url = u->url;
+ u->url = up;
+ freeUrlinfo(u);
+ }
+
+ /* This URL is now cached. */
+ *uret = u = uCache[i];
+
+ /* Zap proxy host and port in case they have been reset */
+ u->proxyp = -1;
+ FREE(u->proxyh);
+
+ /* Perform one-time FTP initialization */
+ if (!strcmp(u->service, "ftp")) {
+
+ if (mustAsk || (u->user != NULL && u->password == NULL)) {
+ char * prompt;
+ FREE(u->password);
+ prompt = alloca(strlen(u->host) + strlen(u->user) + 40);
+ sprintf(prompt, _("Password for %s@%s: "), u->user, u->host);
+ u->password = strdup(getpass(prompt));
+ }
+
+ if (u->proxyh == NULL) {
+ const char *proxy = rpmExpand("%{_ftpproxy}", NULL);
+ if (proxy && *proxy != '%') {
+ const char *uu = (u->user ? u->user : "anonymous");
+ char *nu = malloc(strlen(uu) + sizeof("@") + strlen(u->host));
+ strcpy(nu, uu);
+ strcat(nu, "@");
+ strcat(nu, u->host);
+ u->proxyu = nu;
+ u->proxyh = strdup(proxy);
+ }
+ xfree(proxy);
+ }
+
+ if (u->proxyp < 0) {
+ const char *proxy = rpmExpand("%{_ftpport}", NULL);
+ if (proxy && *proxy != '%') {
+ char *end;
+ int port = strtol(proxy, &end, 0);
+ if (!(end && *end == '\0')) {
+ fprintf(stderr, _("error: %sport must be a number\n"),
+ u->service);
+ return;
+ }
+ u->proxyp = port;
+ }
+ xfree(proxy);
+ }
+ }
+
+ /* Perform one-time HTTP initialization */
+ if (!strcmp(u->service, "http")) {
+
+ if (u->proxyh == NULL) {
+ const char *proxy = rpmExpand("%{_httpproxy}", NULL);
+ if (proxy && *proxy != '%')
+ u->proxyh = strdup(proxy);
+ xfree(proxy);
+ }
+
+ if (u->proxyp < 0) {
+ const char *proxy = rpmExpand("%{_httpport}", NULL);
+ if (proxy && *proxy != '%') {
+ char *end;
+ int port = strtol(proxy, &end, 0);
+ if (!(end && *end == '\0')) {
+ fprintf(stderr, _("error: %sport must be a number\n"),
+ u->service);
+ return;
+ }
+ u->proxyp = port;
+ }
+ xfree(proxy);
+ }
+
+ }
+
+ return;
+}
+
+/*
+ * Split URL into components. The URL can look like
+ * service://user:password@host:port/path
+ */
+int urlSplit(const char * url, urlinfo **uret)
+{
+ urlinfo *u;
+ char *myurl;
+ char *s, *se, *f, *fe;
+
+ if (uret == NULL)
+ return -1;
+ if ((u = newUrlinfo()) == NULL)
+ return -1;
+
+ if ((se = s = myurl = strdup(url)) == NULL) {
+ freeUrlinfo(u);
+ return -1;
+ }
+
+ u->url = strdup(url);
+
+ do {
+ /* Point to end of next item */
+ while (*se && *se != '/') se++;
+ if (*se == '\0') {
+ /* XXX can't find path */
+ if (myurl) free(myurl);
+ freeUrlinfo(u);
+ return -1;
+ }
+ /* Item was service. Save service and go for the rest ...*/
+ if ((se != s) && se[-1] == ':' && se[0] == '/' && se[1] == '/') {
+ se[-1] = '\0';
+ u->service = strdup(s);
+ se += 2; /* skip over "//" */
+ s = se++;
+ continue;
+ }
+
+ /* Item was everything-but-path. Save path and continue parse on rest */
+ u->path = strdup(se);
+ *se = '\0';
+ break;
+ } while (1);
+
+ /* Look for ...@host... */
+ fe = f = s;
+ while (*fe && *fe != '@') fe++;
+ if (*fe == '@') {
+ s = fe + 1;
+ *fe = '\0';
+ /* Look for user:password@host... */
+ while (fe > f && *fe != ':') fe--;
+ if (*fe == ':') {
+ *fe++ = '\0';
+ u->password = strdup(fe);
+ }
+ u->user = strdup(f);
+ }
+
+ /* Look for ...host:port */
+ fe = f = s;
+ while (*fe && *fe != ':') fe++;
+ if (*fe == ':') {
+ *fe++ = '\0';
+ u->portstr = strdup(fe);
+ if (u->portstr != NULL && u->portstr[0] != '\0') {
+ char *end;
+ u->port = strtol(u->portstr, &end, 0);
+ if (!(end && *end == '\0')) {
+ rpmMessage(RPMMESS_ERROR, _("url port must be a number\n"));
+ if (myurl) free(myurl);
+ freeUrlinfo(u);
+ return -1;
+ }
+ }
+ }
+ u->host = strdup(f);
+
+ if (u->port < 0 && u->service != NULL) {
+ struct servent *se;
+ if ((se = getservbyname(u->service, "tcp")) != NULL)
+ u->port = ntohs(se->s_port);
+ else if (!strcasecmp(u->service, "ftp"))
+ u->port = IPPORT_FTP;
+ else if (!strcasecmp(u->service, "http"))
+ u->port = IPPORT_HTTP;
+ }
+
+ if (myurl) free(myurl);
+ if (uret) {
+ *uret = u;
+ findUrlinfo(uret, 0);
+ }
+ return 0;
+}
+
+static int urlConnect(const char * url, urlinfo ** uret)
+{
+ urlinfo *u;
+
+ if (urlSplit(url, &u) < 0)
+ return -1;
+
+ if (!strcmp(u->service, "ftp") && u->ftpControl < 0) {
+
+ u->ftpGetFileDoneNeeded = 0; /* XXX PARANOIA */
+ rpmMessage(RPMMESS_DEBUG, _("logging into %s as %s, pw %s\n"),
+ u->host,
+ u->user ? u->user : "ftp",
+ u->password ? u->password : "(username)");
+
+ u->ftpControl = ftpOpen(u);
+
+ if (u->ftpControl < 0) { /* XXX save ftpOpen error */
+ u->openError = u->ftpControl;
+ return u->ftpControl;
+ }
+
+ }
+
+ if (uret != NULL)
+ *uret = u;
+
+ return 0;
+}
+
+urltype urlIsURL(const char * url)
+{
+ struct urlstring *us;
+
+ for (us = urlstrings; us->leadin != NULL; us++) {
+ if (strncmp(url, us->leadin, strlen(us->leadin)))
+ continue;
+ return us->ret;
+ }
+
+ return URL_IS_UNKNOWN;
+}
+
+#ifdef NOTYET
+int urlAbort(FD_t fd)
+{
+ if (fd != NULL && fd->fd_url) {
+ urlinfo *u = (urlinfo *)fd->fd_url;
+ if (u->ftpControl >= 0)
+ ftpAbort(fd);
+ }
+}
+#endif
+
+int ufdClose(FD_t fd)
+{
+ if (fd != NULL && fd->fd_url) {
+ urlinfo *u = (urlinfo *)fd->fd_url;
+ /* Close the ftp control channel (not strictly necessary, but ... */
+ if (u->ftpControl >= 0) {
+ ftpAbort(fd);
+ fd = NULL; /* XXX ftpAbort does fdClose(fd) */
+ close(u->ftpControl);
+ u->ftpControl = -1;
+ }
+ }
+ return fdClose(fd);
+}
+
+FD_t ufdOpen(const char *url, int flags, mode_t mode)
+{
+ FD_t fd = NULL;
+ urlinfo *u;
+
+ switch (urlIsURL(url)) {
+ case URL_IS_FTP:
+ if (urlConnect(url, &u) < 0)
+ break;
+ if ((fd = fdNew()) == NULL)
+ break;
+ fd->fd_url = u;
+ if ((u->openError = ftpGetFileDesc(fd)) < 0) {
+ u->ftpControl = -1;
+ fd = NULL; /* XXX fd already closed */
+ }
+ break;
+ case URL_IS_HTTP:
+ if (urlSplit(url, &u))
+ break;
+ if ((fd = fdNew()) == NULL)
+ break;
+ fd->fd_url = u;
+ fd->fd_fd = httpOpen(u);
+ if (fd->fd_fd < 0) /* XXX Save httpOpen error */
+ u->openError = fd->fd_fd;
+ break;
+ case URL_IS_PATH:
+ if (urlSplit(url, &u))
+ break;
+ fd = fdOpen(u->path, flags, mode);
+ break;
+ case URL_IS_DASH:
+ fd = fdDup(STDIN_FILENO);
+ break;
+ case URL_IS_UNKNOWN:
+ default:
+ fd = fdOpen(url, flags, mode);
+ break;
+ }
+
+ if (fd == NULL || fdFileno(fd) < 0) {
+ ufdClose(fd);
+ return NULL;
+ }
+ return fd;
+}
+
+int urlGetFile(const char * url, const char * dest) {
+ int rc;
+ FD_t sfd = NULL;
+ FD_t tfd = NULL;
+
+ sfd = ufdOpen(url, O_RDONLY, 0);
+ if (sfd == NULL || fdFileno(sfd) < 0) {
+ rpmMessage(RPMMESS_DEBUG, _("failed to open %s\n"), url);
+ ufdClose(sfd);
+ return FTPERR_UNKNOWN;
+ }
+
+ if (sfd->fd_url != NULL && dest == NULL) {
+ const char *fileName = ((urlinfo *)sfd->fd_url)->path;
+ if ((dest = strrchr(fileName, '/')) != NULL)
+ dest++;
+ else
+ dest = fileName;
+ }
+
+ tfd = fdOpen(dest, O_CREAT|O_WRONLY|O_TRUNC, 0600);
+ if (fdFileno(tfd) < 0) {
+ rpmMessage(RPMMESS_DEBUG, _("failed to create %s\n"), dest);
+ fdClose(tfd);
+ ufdClose(sfd);
+ return FTPERR_UNKNOWN;
+ }
+
+ switch (urlIsURL(url)) {
+ case URL_IS_FTP:
+ if ((rc = ftpGetFile(sfd, tfd))) {
+ unlink(dest);
+ ufdClose(sfd);
+ }
+ /* XXX fdClose(sfd) done by copyData */
+ break;
+ case URL_IS_HTTP:
+ case URL_IS_PATH:
+ case URL_IS_DASH:
+ if ((rc = httpGetFile(sfd, tfd))) {
+ unlink(dest);
+ ufdClose(sfd);
+ }
+ /* XXX fdClose(sfd) done by copyData */
+ break;
+ case URL_IS_UNKNOWN:
+ default:
+ rc = FTPERR_UNKNOWN;
+ break;
+ }
+
+ fdClose(tfd);
+
+ return rc;
+}
+
+/* XXX This only works for httpOpen/ftpOpen/ftpGetFileDesc failures */
+const char *urlStrerror(const char *url)
+{
+ urlinfo *u;
+ if (urlSplit(url, &u))
+ return "Malformed URL";
+ return ftpStrerror(u->openError);
+}