/* vi:ts=4:sw=4 * * url.c * open URL * * code contributions By: Atsushi Nakamura anaka@mrit.mei.co.jp */ #ifdef MDOMAIN #include "vim.h" #include "globals.h" #include "proto.h" #include "param.h" #include "ops.h" #ifdef JP # include "jp.h" #endif #include #if !defined(pyr) && !defined(NOT_BOTH_TIME) # include /* on some systems time.h should not be included together with sys/time.h */ #endif #ifndef M_XENIX # include #endif #ifdef SYSV_UNIX # if defined(__sgi) || defined(UTS2) || defined(UTS4) || defined(AUX3) # include # endif # if defined(M_XENIX) || defined(SCO) # include # endif # if defined(M_XENIX) || defined(SCO) || defined(UNICOS) || defined(AIX31) # include # else # if !defined(AUX3) # include # endif # endif # if defined(SCO) || defined(ISC) # include # include # endif # if defined(M_UNIX) && !defined(SCO) # include # endif /* M_UNIX */ #else /* SYSV_UNIX */ # include #endif /* !SYSV_UNIX */ #if defined(FD_ZERO) && defined(SYSV_UNIX) # undef FD_ZERO #endif #include #include #include #include #ifdef _POSIX_VERSION #include #endif #include #ifdef JP extern char *UAJVersion; #else extern char *UAVersion; #endif extern gettimeofday(); #define BUFSIZE 100 #ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 64 #endif #ifndef MAXPATHLEN # define MAXPATHLEN 1024 #endif #define MAXPROTO 20 #define MAXPORT 20 #define MAXURL (MAXHOSTNAMELEN + MAXPORT + MAXPATHLEN) static char *dow[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static char *moy[12]= { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; static char hostname[MAXHOSTNAMELEN + 1] = ""; static char loginname[MAXHOSTNAMELEN + 1] = ""; static int uid, pid, count = 0; static char domainname[MAXHOSTNAMELEN + 1] = MDOMAIN; static char curl[MAXURL + 1] = ""; static int parse_url(), read_line(), send_line(), fd_wait(); static void const_url(), pinfo_init(); int is_url(url) char *url; { char proto[MAXPROTO]; proto[0] = NUL; parse_url(url, proto, NULL, NULL, NULL); if ( !strcmp(proto, "http") || ( *p_proxy && !strcmp(proto, "ftp")) ) return 1; return 0; } void url_init(url) char *url; { p_curl = curl; if (! *curl) const_url(curl, "http", "localhost", "", ""); if (!url) return; if (is_url(url)) { strncpy(curl, url, MAXURL); curl[MAXURL] = NUL; } } enum e_proto { PROTO_HTTP, PROTO_FTP, PROTO_ERROR }; int url_open(url, mode, total) char *url; int mode, *total; { char proto[MAXPROTO + 1]; char host[MAXHOSTNAMELEN + 1]; char port[MAXPORT + 1]; char path[MAXPATHLEN + 1], lpath[MAXPATHLEN + 1]; char furl[MAXURL + 1]; char cbuf[MAXURL + 11]; char temp[20], status[20], type[20]; char *cp, *cp2; int size; int fd; struct sockaddr_in dest; struct hostent *hostinfo; struct servent *servinfo; struct tm *tm; time_t now; enum e_proto protocol; if (mode != O_RDONLY && mode != O_WRONLY) return -1; if (!*curl) url_init(NULL); if (!loginname[0]) pinfo_init(); parse_url(url, NULL, NULL, NULL, lpath); parse_url(curl, proto, host, port, path); if (parse_url(url, proto, host, port, path)) return -1; const_url(furl, proto, host, port, path); if (*p_proxy) { /* parse proxy server */ cp = p_proxy; cp2 = host; while(*cp && *cp != ':') *cp2 ++ = *cp ++; *cp2 = NUL; if (*cp == ':') strcpy(port, cp + 1); strcpy(proto, "http"); } if (! strcmp(proto, "http") ) protocol = PROTO_HTTP; else if (! strcmp(proto, "ftp") ) protocol = PROTO_FTP; else protocol = PROTO_ERROR; smsg("Resolving %s", host); if (! (hostinfo = gethostbyname(host))) { emsg2("Host %s unknown.", host); return -1; } msg(""); memset((void *)&dest, 0, sizeof(dest)); dest.sin_family = AF_INET; dest.sin_port = htons(atoi(port)); if (!dest.sin_port) { if (*port) { if (! (servinfo = getservbyname(port, NULL)) ) { emsg2("Service '%s' unknown.", port); return -1; } dest.sin_port = servinfo -> s_port; } else if ((servinfo = getservbyname(proto, NULL))) dest.sin_port = servinfo -> s_port; else switch(protocol) { case PROTO_HTTP: dest.sin_port = htons(80); break; case PROTO_FTP: dest.sin_port = htons(21); break; default: emsg2("Default Service for '%s' unknown.", proto); return -1; } } memcpy(&dest.sin_addr, hostinfo->h_addr, hostinfo->h_length); if((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { emsg("Socket allocation error."); return -1; } smsg("Connecting to %s", host); fflush(stderr); if(connect(fd, (struct sockaddr *)&dest, sizeof(dest)) == -1) { switch(errno) { case EACCES: emsg("Search permission denied."); break; case EADDRNOTAVAIL: emsg("Address not available."); break; case ECONNREFUSED: emsg("Connection rejected."); break; case ENETUNREACH: emsg("Network unreachable."); break; case ETIMEDOUT: emsg("Connection timed out."); break; } return -1; } switch(protocol) { case PROTO_HTTP: /* header request */ smsg("sending request to %s.", host); if (p_ntr) fprintf(stderr, "\n"); if (mode == O_WRONLY) sprintf(cbuf, "PUT %s HTTP/1.0", *p_proxy ? furl : path); else sprintf(cbuf, "GET %s HTTP/1.0", *p_proxy ? furl : path); if (send_line(fd, cbuf)) goto fail; /* general header*/ /* send date and time in RFC1123 format */ now = time(NULL); tm = gmtime(&now); sprintf(cbuf, "DATE: %s, %02d %s %4d %02d:%02d:%02d GMT", dow[tm->tm_wday], tm->tm_mday, moy[tm->tm_mon], 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); if (send_line(fd, cbuf)) goto fail; /* send message ID in RFC822 format */ sprintf(cbuf, "Message-ID: <%04X%x.%s@%s.%s>", pid, count++ , loginname, hostname, domainname); if (send_line(fd, cbuf)) goto fail; /* send MIME Version*/ if (send_line(fd, "MIME-Version: 1.0")) goto fail; /* request header*/ /* send Accept */ if (send_line(fd, "Accept: */*")) goto fail; /* send Accept Character set */ if (send_line(fd, "Accept-Charset: ISO-2022-JP")) goto fail; /* send Accept Encoding */ if (send_line(fd, "Accept-Encoding: *")) goto fail; /* send user agent */ #ifdef JP sprintf(cbuf, "User-Agent: %s", UAJVersion); #else sprintf(cbuf, "User-Agent: %s", UAVersion); #endif if (send_line(fd, cbuf)) goto fail; /* send from */ sprintf(cbuf, "From: %s@%s.%s", loginname, hostname, domainname); if (send_line(fd, cbuf)) goto fail; /* end of header */ if (send_line(fd, "")) goto fail; if(mode == O_WRONLY && fd_wait(fd, 15) <= 0) goto fail; /* read response line */ smsg("waiting response from %s", host); if (p_ntr) fprintf(stderr, "\n"); if ((size = read_line(fd, cbuf)) < 0) goto fail; sscanf(cbuf, "%s %s", temp, status); if (status[0] != '2') { /* show all error messages */ smsg("%s\n", cbuf); while((size = read_line(fd, cbuf))) if (size < 0) break; close(fd); goto fail; } /* read response header */ *total = -1; type[0] = NUL; while((size = read_line(fd, cbuf))) { char *cp; if (size < 0) goto fail; for(cp = cbuf; *cp && *cp != ':'; cp++) if ('A' <= *cp && *cp <= 'Z') *cp -= 'A' - 'a'; if (!strncmp("content-length", cbuf, 14)) sscanf(cbuf, "%[^:]: %d", temp, total); else if (!strncmp("content-type", cbuf, 12)) sscanf(cbuf, "%[^:]: %s", temp, type); /* not implemented yet. */ } if (p_ntr) { wait_return(); updateScreen(CLEAR); } smsg("%d bytes %s", *total, type); /* if (strncmp(type, "text", 4)) { emsg2("File format(%s) is not text.", type); close(fd); goto fail; } */ break; case PROTO_FTP: default: /* Not implemented yet. */ break; } /* strcpy(curl, furl); p_curl = curl; */ return fd; fail: if (p_ntr) { fprintf(stderr, "Error....\n"); wait_return(); } return -1; } static int fd_wait(fd, sec) int fd; int sec; { #ifdef UNIX #if defined(SYSV_UNIX) && !defined(M_XENIX) && !defined(UNICOS) && !defined(AUX3) struct pollfd fds[2]; fds[0].fd = 0; fds[1].fd = fd; fds[0].events = fds[1].events = POLLIN; if (!poll(fds, 2, sec * 1000)) return 0; if ((fds[0].revents & (POLLIN | POLLERR | POLLHUP)) ||(fds[1].revents & (POLLERR | POLLHUP))) return -1; # else extern int select __ARGS((int, fd_set *, fd_set *, fd_set *, struct timeval *)); fd_set fds; struct timeval timeout; FD_ZERO(&fds); FD_SET(fd, &fds); FD_SET(0, &fds); timeout.tv_sec = sec; timeout.tv_usec = 0; if (select(32, &fds, NULL, NULL, &timeout) <= 0) return 0; if (!FD_ISSET(fd, &fds)) return -1; # endif return 1; #else return 1; #endif } static void const_url(url, proto, host, port, path) char *url, *proto, *host, *port, *path; { sprintf(url, "%s://%s", proto, host); if (*port) sprintf(url + strlen(url), ":%s", port); strcat(url, path); } static int parse_url(url, proto, host, port, path) char *url, *proto, *host, *port, *path; { char *cp; char *orgpath; /* protocol */ for(cp = url; *cp && *cp != ':'; cp++); if (*cp) { if (cp - url > MAXPROTO) return -1; if (proto) { if (port && strncmp(proto, url, strlen(proto))) *port = NUL; while(url != cp) *proto++ = *url++; *proto = NUL; } url = cp = cp + 1; } else cp = url; /* host address & port number */ if (*cp == '/' && *(cp + 1) == '/') { url = cp = cp + 2; for(; *cp && *cp != ':' && *cp != '/' ; cp++); if (cp - url > MAXHOSTNAMELEN) return -1; if (host) { while(url != cp) *host++ = *url++; *host = NUL; } if (*cp == ':') { for(url = cp = cp + 1; *cp && *cp != '/'; cp++); if (cp - url > MAXPROTO) return -1; if (port) while(url != cp) *port++ = *url++; } if (port) *port = NUL; } /* path */ if (!path) return 0; if (*cp) { orgpath = path; if (*cp != '/') { for(;*path; path++); for(;*path != '/' && path != orgpath; path--); } if ((int)strlen(cp) + (path - orgpath) > MAXPATHLEN) return -1; if (*cp != '/') *path++ = '/'; while(*cp) *path++ = *cp++; } *path = NUL; return 0; } static int send_line(fd, line) int fd; char *line; { if (p_ntr) fprintf(stderr, "< %s\n", line); if (write(fd, line, strlen(line)) >= 0 && write(fd, "\r\n", 2) >= 0 ) return 0; close(fd); return -1; } static int read_line(fd, line) int fd; char *line; { char c, *top; int i, size; switch(fd_wait(fd, p_nto)) { case 0: emsg("Timed out."); close(fd); return -1; case -1: emsg("Interrupted."); close(fd); return -1; default: break; } top = line; for(size = 0; 1; size++, line++) { i = read(fd, &c, 1); if (i == 0) { close(fd); return -1; } if (c == '\r') { read(fd, &c, 1); break; } if (c == '\n') break; *line = c; } *line = NUL; if (p_ntr) fprintf(stderr, "> %s\n", top); return size; } static void pinfo_init() { char *cp, *cp2; #ifdef _POSIX_VERSION struct utsname uts; uname(&uts); cp = uts.nodename; cp2 = hostname; while(*cp && cp2 - hostname < MAXHOSTNAMELEN) *cp2 ++ = *cp ++; *cp2 = NUL; #else gethostname(hostname, MAXHOSTNAMELEN); #endif if ((cp2 = strchr(hostname, '.')) != NULL) *cp2 = NUL; uid = getuid(); cp = getpwuid(uid)->pw_name; cp2 = loginname; while(*cp && cp2 - loginname < MAXHOSTNAMELEN) *cp2 ++ = *cp ++; pid = getpid(); if ((cp = getenv("DOMAINNAME"))) { if ((int)strlen(cp) < MAXHOSTNAMELEN) strcpy(domainname, cp); else emsg("Environment variable DOMAINNAME too long."); } else if (!strcmp(domainname, "localhost")) emsg("Recompile this program setting MDOMAIN...."); } int do_nget(args) char *args; { char *url, *file; char *cp; int rfd, wfd; int i, size, total; /* int bps; */ url = args; file = ""; for(cp = args; *cp && *cp != ' ' && *cp != '\t'; cp++); if (args == cp) { emsg("URL and filename are required."); return -1; } else if (*cp) { /* 2 parameters are specified */ *cp++ = NUL; for(; *cp && (*cp == ' ' || *cp == '\t'); cp++); file = cp; for(; *cp && *cp != ' ' && *cp != '\t'; cp++); /* more than 2 parameters */ if (*cp) { emsg("Only URL and filename are required."); return -1; } } if (! *file) { /* if file is not specified */ for(cp = url; *cp; cp++); if (* (cp - 1) == '/') { emsg("Filename is required."); return -1; } for(;cp != url && *cp != '/' && *cp != ':'; cp--); if (*cp == '/' || *cp == ':') cp++; file = cp; } if (!is_url(url)) { emsg2("%s is not URL.", url); return -1; } if ((rfd = url_open(url, O_RDONLY, &total)) < 0) return -1; if ((wfd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0744)) < 0) { emsg2("file '%s' cannot open for writing.", file); return -1; } size = 0; while((i = read(rfd, IObuff, IOSIZE))) { write(wfd, IObuff, i); size += i; /* bps = speed(i); */ if (size > 1024) smsg("%d/%d Kbytes", size >> 10, total >> 10); else smsg("%d/%d bytes", size, total); } smsg("%d bytes transferred", size); close(rfd); close(wfd); return 0; } /* static int speed(size) int size; { static struct timeval tprev = { 0, 0 }; struct timeval now, diff; int rate; gettimeofday(&now, NULL); rate = 0; if (tprev.tv_sec || tprev.tv_usec) { diff.tv_sec = now.tv_sec - tprev.tv_sec; diff.tv_usec = now.tv_usec - tprev.tv_usec; if (diff.tv_usec < 0) { diff.tv_sec --; diff.tv_usec += 1000000; } size <<= 8; diff.tv_sec <<= 8; diff.tv_usec <<= 8; diff.tv_sec += (diff.tv_usec / 1000000); if (diff.tv_sec) rate = size / diff.tv_sec; else rate = -1; } tprev = now; return rate; } */ #endif /* MDOMAIN */