--- ./httpfs.c.orig 2006-07-12 00:26:41.000000000 +0400 +++ ./httpfs.c 2012-11-18 21:33:35.000000000 +0400 @@ -1,91 +1,74 @@ + /* - * HTTPFS: import a file from a web server to local file system - * the main use is, to mount an iso on a web server with loop device - * - * depends on: - * FUSE: Filesystem in Userspace - * Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu> - * + * HTTPFS: http filesystem using FUSE * This program can be distributed under the terms of the GNU GPL. - * - */ -/* - * This 'beam me up, Scotty'-branch of httpfs tries to achieve, - * that the mount-point-folder behaves as before. - * But how can you access the original folder after the mount? - * Answer comes from FuseCompress: - * Open the folder before the mount, - * keep it open all the time, - * make a chdir to it - * and always use a relative path. - * It suffices not to chdir in main() and it's unnecessary to - * do it in every function. httpfs_init is the right place. - */ + Authors: + Marion Raven (httpfs code) + Tomas M (all the further updates) + + Anton Goroshkin <http://magos-linux.ru> + */ #include <fuse.h> +#include <errno.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/dir.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> #include <netdb.h> -#include <time.h> - -#ifdef USE_SSL -#include <openssl/ssl.h> -#include <openssl/err.h> -#endif #define TIMEOUT 30 -#define VERSION "2.06.07.10" - -static char* argv0; -static char* argv1; -static char* argv2; -static long long int file_size; -static int protocol; -static char host[1000]; -static unsigned short port; -static char* file_name; - - -static char httpfs_path[256]; -static int targetFd; +#define BUFSIZE 1024*8 +#define BIGBUFSIZE 64*BUFSIZE +#define RESP_STATUS_LEN 12 // sizeof "HTTP/1.1 200" +#define VERSION "for MagOS Linux" + +#define MIN(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + +static char *myself; +static char *arg_url; +static char *arg_mnt; +FILE * fou; + +static char http_auth[BUFSIZE]; +static char http_host[BUFSIZE]; +static char http_base[BUFSIZE]; +static char http_path[BUFSIZE]; +static char http_file[BUFSIZE]; +static unsigned short int http_port; + +//static char host[BUFSIZE]; +//static unsigned short port; +//static char *file_name; +//static char httpfs_path[BUFSIZE]; +//static char http_base[BUFSIZE]; static int sockfd; - -/* Protocol symbols. */ -#define PROTO_HTTP 0 -#ifdef USE_SSL -#define PROTO_HTTPS 1 -#endif +static char *http = "http://"; #ifdef USE_AUTH -static char auth_buf[1000]; - +/* static char b64_encode_table[64] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', /* 0-7 */ - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 8-15 */ - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', /* 16-23 */ - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', /* 24-31 */ - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 32-39 */ - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', /* 40-47 */ - 'w', 'x', 'y', 'z', '0', '1', '2', '3', /* 48-55 */ - '4', '5', '6', '7', '8', '9', '+', '/' /* 56-63 */ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0-7 + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8-15 + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16-23 + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24-31 + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32-39 + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40-47 + 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48-55 + '4', '5', '6', '7', '8', '9', '+', '/' // 56-63 }; - -/* Do base-64 encoding on a hunk of bytes. Return the actual number of -** bytes generated. Base-64 encoding takes up 4/3 the space of the original, -** plus a bit for end-padding. 3/2+5 gives a safe margin. -*/ -static int b64_encode(unsigned char* ptr, int len, char* space, int size) { + +// Do base-64 encoding on a hunk of bytes. Return the actual number of +// bytes generated. Base-64 encoding takes up 4/3 the space of the original, +// plus a bit for end-padding. 3/2+5 gives a safe margin. +// + +static int b64_encode(char* ptr, int len, char* space, int size) { int ptr_idx; unsigned char c = 0; unsigned char d = 0; @@ -117,7 +100,7 @@ if (phase != 0) { space[space_idx++] = b64_encode_table[d]; if (space_idx == size) return space_idx; - /* Pad with ='s. */ + // Pad with ='s. while (phase++ > 0) { space[space_idx++] = '='; if (space_idx == size) return space_idx; @@ -126,79 +109,60 @@ } return space_idx; } - +*/ #endif /* USE_AUTH */ /* - * Function yields host, file, port and auth_buf - * (auth_buf is global) - * Besides protocol is returned - * (-1 in case of error) + * Function sets global variables http_host, http_path, http_file, http_port, and http_auth + * url = http://user:password@domain.com:80/path/file.dat + * If parsing fails, returns -1 */ +static int parse_url(char *url) { + char *pos, *auth, *port, *path, *file; -static int parseURL(char* url, char* host, char** ptrfile, unsigned short* port) { - int host_len, protocol, proto_len; - char* file; - - char* http = "http://"; -#ifdef USE_SSL - char* https = "https://"; -#endif /* USE_SSL */ - - - if (url == NULL) { - (void) fprintf(stderr, "%s: null URL\n", argv0); - return -1; - } - if (strncmp(http, url, strlen(http)) == 0) { - proto_len = strlen(http); - protocol = PROTO_HTTP; - *port = 80; -#ifdef USE_SSL - } else if (strncmp(https, url, strlen(https)) == 0) { - proto_len = strlen(https); - protocol = PROTO_HTTPS; - *port = 443; -#endif /* USE_SSL */ + memset(http_auth,0,BUFSIZE); + memset(http_host,0,BUFSIZE); + memset(http_base,0,BUFSIZE); + memset(http_path,0,BUFSIZE); + memset(http_file,0,BUFSIZE); + + // http part is optional + pos = url + (strcasestr(url, http) ? strlen(http) : 0); + + auth = strchr(pos, '@'); + if (auth != NULL) { + fprintf(stderr,"user auth not supported yet, ignored"); + // http_auth = "user:pass"; // fix this + pos = auth + 1; + } + + path = strchr(pos, '/'); + port = strchr(pos, ':'); + + if (port < path && port != NULL) { // a semicolon is sooner than slash for path - that specifies port number + http_port = (unsigned short) atoi(port + 1); + strncpy(http_host, pos, port - pos); } else { - (void) fprintf(stderr, "%s: non-http URL\n", argv0); - return -1; + http_port = 80; + strncpy(http_host, pos, path - pos); } -#ifdef USE_AUTH - /* Get user and password */ - auth_buf[0] = '\0'; - for (file = url + proto_len; *file != '\0' && *file != '/'; ++file) { - if (*file == '@') { - auth_buf[b64_encode(url + proto_len, file - url - proto_len, auth_buf, sizeof(auth_buf))] = '\0'; - proto_len = file - url + 1; - break; - } - } -#endif /* USE_AUTH */ - - /* Get the host name. */ - for (file = url + proto_len; *file != '\0' && *file != ':' && *file != '/'; ++file) - ; - host_len = file - url - proto_len; - strncpy(host, url + proto_len, host_len); - host[host_len] = '\0'; - - /* Get port number. */ - if (*file == ':') { - *port = (unsigned short) atoi(++file); - for (; *file != '\0' && *file != '/'; ++file) - ; + if (path != NULL) { +// strncpy(http_host, pos, path - pos); + file = strrchr(path, '/'); + strncpy(http_path, path, file == path ? 1 : file - path); + strncpy(http_file, file + 1, strlen(file) - 1); + } else { + strncpy(http_host, pos, strlen(pos)); + http_path[0]='/'; } - - /* Get the file name. */ - if (*file == '\0') - file = "/"; - - *ptrfile = file; - return protocol; -} + strncpy(http_base, http, strlen(http)); + strncpy(http_base + strlen(http), http_host, strlen(http_host)); + if (strcmp(http_path, "/") != 0) strncpy(http_base + strlen(http_base), http_path, strlen(http_path)); + + return 1; +} /* * Function yields either a 'connected' socket for @@ -207,758 +171,412 @@ * hostname is something like 'www.tmtd.de' or 192.168.0.86 * port is expected in machine order (not net order) * - * ((Flonix defines USE_IPV6)) - * */ -#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) -#define USE_IPV6 -#endif - -static int open_client_socket(char* hostname, unsigned short port) { -#ifdef USE_IPV6 - struct addrinfo hints; - char portstr[10]; - int gaierr; - struct addrinfo* ai; - struct addrinfo* aiv4; - struct addrinfo* aiv6 = 0; - struct sockaddr_in6 sa; -#else /* USE_IPV6 */ +static int open_client_socket() { + int fd; struct hostent *he; struct sockaddr_in sa; -#endif /* USE_IPV6 */ - int sa_len, sock_family, sock_type, sock_protocol; - int sockfd; - - (void) memset((void*) &sa, 0, sizeof(sa)); - -#ifdef USE_IPV6 - (void) memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - (void) snprintf(portstr, sizeof(portstr), "%d", (int) port); - if ((gaierr = getaddrinfo(hostname, portstr, &hints, &ai)) != 0) { - (void) fprintf(stderr, "%s: getaddrinfo %s - %s\n", argv0, hostname, gai_strerror(gaierr)); - return -1; - } - - /* Find the first IPv4 and IPv6 entries. */ - for (aiv4 = ai; aiv4 != NULL; aiv4 = aiv4->ai_next) { - if (aiv4->ai_family == AF_INET) - break; - if ((aiv4->ai_family == AF_INET6) && (aiv6 == NULL)) - aiv6 = aiv4; - } + struct timeval timeout; - /* If there's an IPv4 address, use that, otherwise try IPv6. */ - if (aiv4 == NULL) - aiv4 = aiv6; - if (aiv4 == NULL) { - (void) fprintf(stderr, "%s: no valid address found for host %s\n", argv0, hostname); - return -1; - } - if (sizeof(sa) < aiv4->ai_addrlen) { - (void) fprintf(stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname, (unsigned long) sizeof(sa), (unsigned long) aiv4->ai_addrlen); + he = gethostbyname(http_host); + if (he == NULL) { + fprintf(stderr, "%s: unknown host - %s port=%d\n", myself, http_host,http_port); return -1; } - sock_family = aiv4->ai_family; - sock_type = aiv4->ai_socktype; - sock_protocol = aiv4->ai_protocol; - sa_len = aiv4->ai_addrlen; - (void) memmove(&sa, aiv4->ai_addr, sa_len); - freeaddrinfo(ai); -#else /* USE_IPV6 */ + memset((void*) &sa, 0, sizeof(sa)); + sa.sin_family = he->h_addrtype; + memmove(&sa.sin_addr, he->h_addr, he->h_length); + sa.sin_port = htons(http_port); - he = gethostbyname(hostname); - if (he == NULL) { - (void) fprintf(stderr, "%s: unknown host - %s\n", argv0, hostname); - return -1; - } - sock_family = sa.sin_family = he->h_addrtype; - sock_type = SOCK_STREAM; - sock_protocol = 0; - sa_len = sizeof(sa); - (void) memmove(&sa.sin_addr, he->h_addr, he->h_length); - sa.sin_port = htons(port); - -#endif /* USE_IPV6 */ - - sockfd = socket(sock_family, sock_type, sock_protocol); - if (sockfd < 0) { - (void) fprintf(stderr, "%s: couldn't get socket, got errno: %u\n", argv0, errno); + fd = socket(sa.sin_family, SOCK_STREAM, 0); + if (fd < 0) { + fprintf(stderr, "%s: couldn't get socket\n", myself); return -1; } - if (connect(sockfd, (struct sockaddr*) &sa, sa_len) < 0) { - (void) fprintf(stderr, "%s: couldn't connect socket, got errno: %u\n", argv0, errno); + if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { + fprintf(stderr, "%s: couldn't connect socket\n", myself); return -1; } - return sockfd; -} - -/* - * Function uses HEAD-HTTP-Request - * to determine the file size - * This is done only once - */ - -int getSize() { -#ifdef USE_SSL - SSL_CTX* ssl_ctx; - SSL* ssl; -#endif - char buf[1000]; - char* b; - struct timeval timeout; - int bytes, status; - -#ifdef USE_SSL - if (protocol == PROTO_HTTPS) { - /* Make SSL connection. */ - int r; - SSL_load_error_strings(); - SSLeay_add_ssl_algorithms(); - ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - ssl = SSL_new(ssl_ctx); - SSL_set_fd(ssl, sockfd); - r = SSL_connect(ssl); - if (r <= 0) { - (void) fprintf(stderr, "%s: %s:%d - SSL connection failed - %d\n", argv0, host, port, r); - ERR_print_errors_fp(stderr); - exit(1); - } - } -#endif - - /* Build request buffer, starting with the HEAD. */ - - bytes = snprintf(buf, sizeof(buf), "HEAD %s HTTP/1.1\r\nHost: %s\r\n", file_name, host); -#ifdef USE_AUTH - if ( *auth_buf != '\0' ) - bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Authorization: Basic %s\r\n", auth_buf); -#endif - bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "\r\n"); - - /* Now actually send it. */ -#ifdef USE_SSL - if (protocol == PROTO_HTTPS) - (void) SSL_write(ssl, buf, bytes); - else -#endif - (void) write(sockfd, buf, bytes); + // set socket timeout timeout.tv_sec = TIMEOUT; timeout.tv_usec = 0; - setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); -#ifdef USE_SSL - if (protocol == PROTO_HTTPS) - bytes = SSL_read(ssl, buf, sizeof(buf)); - else -#else - bytes = read(sockfd, buf, sizeof(buf)); -#endif - if (bytes <= 0) { - fprintf(stderr, "%s: HEAD (read) failed\n", argv0); - return -1; - } -#ifdef USE_SSL - if (protocol == PROTO_HTTPS) { - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); - } -#endif - - (void) sscanf(buf, "HTTP/1.1 %d ", &status); - if (status != 200) { - fprintf(stderr, "%s: HEAD (read) failed with Status %d\n", argv0, status); - return -1; - } - b = strstr(buf,"Content-Length:"); - if (b == NULL) { - fprintf(stderr, "%s: HEAD-Reply didn't contain Content-Length\n", argv0); - return -1; - } - file_size = atoll(b+16); - return 0; + return fd; } -/* - * HttpGet does all the magic - * a GET-Request with Range-Header - * allows to read arbitrary bytes - */ - -int HttpGet(off_t start, size_t size, char * destination) { -#ifdef USE_SSL - SSL_CTX* ssl_ctx; - SSL* ssl; -#endif - char buf[1000]; - char* b; - size_t end = start + size - 1; - struct timeval timeout; - int bytes, status; +// return relative URL against the http_base +// compare case insensitive +char *relative_url(char *url) { + if (strncasecmp(url, http_base, strlen(http_base)) == 0) url += strlen(http_base); + if (strncasecmp(url, http, strlen(http)) == 0) url += strlen(url); + return url; +} -#ifdef USE_SSL - if (protocol == PROTO_HTTPS) { - /* Make SSL connection. */ - int r; - SSL_load_error_strings(); - SSLeay_add_ssl_algorithms(); - ssl_ctx = SSL_CTX_new(SSLv23_client_method()); - ssl = SSL_new(ssl_ctx); - SSL_set_fd(ssl, sockfd); - r = SSL_connect(ssl); - if (r <= 0) { - (void) fprintf(stderr, "%s: %s:%d - SSL connection failed - %d\n", argv0, host, port, r); - ERR_print_errors_fp(stderr); - exit(1); - } - } -#endif +/* + * A generic sendRequest function + * which handles all cases when connection no more exists + */ - /* Build request buffer, starting with the GET. */ +static int sendRequest(const char *method, const char *path, off_t start, off_t end) +{ + char buf[BUFSIZE]; + char tmp[RESP_STATUS_LEN]; + char *pos; + int bytes; + int status; + + // build the request buffer + memset(buf, 0, sizeof(buf)); + bytes = snprintf(buf, sizeof(buf), "%s %s%s HTTP/1.1\r\nHost: %s\r\n", method, strcmp(http_path,"/") == 0 ? "" : http_path, path, http_host); + + // keep the connection alive if requested + bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Connection: Keep-Alive\r\n"); + + // handle HTTP_RANGE if needed + if (start > 0 || end > 0) { + bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Range: bytes=%llu-%llu\r\n", (unsigned long long) start, (unsigned long long) end); + } + + // add authentication string if needed + #ifdef USE_AUTH + if ( *http_auth != '\0' ) + bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Authorization: Basic %s\r\n", http_auth); + #endif - bytes = snprintf(buf, sizeof(buf), "GET %s HTTP/1.1\r\nHost: %s\r\n", file_name, host); - bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Range: bytes=%llu-%llu\r\n", (unsigned long long) start, (unsigned long long) end); -#ifdef USE_AUTH - if ( *auth_buf != '\0' ) - bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Authorization: Basic %s\r\n", auth_buf); -#endif + // finally send empty line so server can understnad it's all bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "\r\n"); + fprintf(stderr,"------------- request -------------\n%s\n",buf); + for (;;) + { + // write request to socket and read first 12 bytes of response + write(sockfd, buf, bytes); + memset(tmp, 0, sizeof(tmp)); + status = read(sockfd, tmp, sizeof(tmp)); + + if (status > 0) { + // parse real status code + sscanf(tmp, "HTTP/1.1 %d", &status); + + // handle HTTP redirect (status code 3xx with Location: header) + if (status > 300 && status < 400) { + read(sockfd, buf, sizeof(buf)); // hopefully we get enough data here + pos = strstr(buf,"Location:"); + if (pos != NULL) { + pos[strcspn(pos, "\n\r")] = '\0'; + pos = relative_url(pos+10); // 10 = strlen("Location: ") + if (strlen(pos) == 0) return 404; + close(sockfd); + return sendRequest(method, pos, start, end); + } + } - /* Now actually send it. */ -#ifdef USE_SSL - if (protocol == PROTO_HTTPS) - (void) SSL_write(ssl, buf, bytes); - else -#endif - (void) write(sockfd, buf, bytes); - - timeout.tv_sec = TIMEOUT; - timeout.tv_usec = 0; - setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + if (status >= 100 && status <= 999) { + return status; + } + } + + // if we got so far, we were unable to read from sockfd. Reconnect. + close(sockfd); + sockfd = open_client_socket(); + } +} +static off_t getSize(const char * path) { + char buf[BUFSIZE]; + char* pos; + int status; + + memset(buf, 0, sizeof(buf)); + status = sendRequest("HEAD",path,0,0); + read(sockfd, buf, sizeof(buf)); +// fprintf(fou,"=============HEAD start=========\n%s\n=============HEAD end=========\n",buf); + + pos = strstr(buf,"Accept-Ranges:"); + if (pos == NULL || status != 200) return -1; + pos = strstr(buf,"Content-Length:"); + if (pos == NULL || status != 200) return -1; + return atoll(pos+16); +} + +static int httpGet(const char * path, off_t start, size_t size, char * destination, off_t maxRange) { + char buf[BUFSIZE]; + char *pos; +// char *posa; + int b, bytes, status, c; + off_t end; + + end = start + size - 1; + if (end > maxRange) end = maxRange; + if (start > end) start = end; + + memset(buf, 0, sizeof(buf)); + status = sendRequest("GET", path, start, end); + if (status >= 300) return -1; + bytes = read(sockfd, buf, sizeof(buf)); -#ifdef USE_SSL - if (protocol == PROTO_HTTPS) - bytes = SSL_read(ssl, buf, sizeof(buf)); - else -#else - bytes = read(sockfd, buf, sizeof(buf)); -#endif - if (bytes <= 0) { - fprintf(stderr, "%s: GET (read) failed with bytes= %d\n", argv0, bytes); - return bytes; - } - (void) sscanf(buf, "HTTP/1.1 %d ", &status); - if ((status != 200) && (status != 206)) { - fprintf(stderr, "%s: GET (read) failed with Status %d\n", argv0, status); - return -1; - } - b = strstr(buf,"Content-Length:"); - if (b == NULL) { - fprintf(stderr, "%s: GET-Reply didn't contain Content-Length\n", argv0); - return -1; - } - if (atoll(b+16) != size) { - fprintf(stderr, "%s: GET didn't yield whole piece\n", argv0); - size = (size_t) atoll(b+16); + // If the response doesn't contain all headers ended by \r\n\r\n, + // read more data. This should be rather handled by select() syscall, + // but I do not know how to use it yet. + b = 1; + while (strstr(buf,"\r\n\r\n") == NULL && b > 0) { + b = read(sockfd, buf + bytes, sizeof(buf) - bytes); + bytes += b; + } + +// posa = strstr(buf,"Accept-Ranges:"); + pos = strstr(buf,"Content-Length:"); + if ((pos == NULL)) { + /* + * HTTP 1.1 specification states that if the response doesn't + * specify Content-Length, Transfer-Encoding is always Chunked. + * For that reason, we assume chunks here. We will read only + * ONE chunk of data here, it should be enough for us. + * + * This part of httpGet is called by httpfs_readdir(), it should + * never happen to jump here in httpfs_read() + */ + size_t chunksize = 0; + pos = strstr(buf,"\r\n\r\n")+4; + b = 1; // make sure we have at least one line of data in buffer + while ( strstr(pos,"\r\n") == NULL && b > 0 ) { + b = read(sockfd, buf + bytes, sizeof(buf) - bytes); + bytes += b; + } + sscanf(pos, "%x", &chunksize); + if (size > chunksize) size = chunksize; + pos = strstr(pos,"\r\n")+2; + c = 1; // close connection at the end - to drop other chunks + } + else { + size = (size_t) atoll(pos+16); + pos = strstr(buf,"\r\n\r\n")+4; + c = 0; // keep connection alive } - b = strstr(buf,"\r\n\r\n")+4; - - bytes -= (b - buf); - memcpy(destination, b, bytes); + // copy bytes from the current response to destination + bytes -= (pos - buf); + if (bytes < 0) return 0; + memcpy(destination, pos, bytes); size -= bytes; + + // read the rest of the response directly to address destination+bytes for (; size > 0; size -= bytes) { destination += bytes; - timeout.tv_sec = TIMEOUT; - timeout.tv_usec = 0; - setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); -#ifdef USE_SSL - if (protocol == PROTO_HTTPS) - bytes = SSL_read(ssl, destination, size); - else -#else - bytes = read(sockfd, destination, size); -#endif - - if (bytes < 0) { - fprintf(stderr, "%s: GET (read) failed\n", argv0); - return bytes; - } - if (bytes == 0) { - break; - } + bytes = read(sockfd, destination, size); + if (bytes == 0) break; } -#ifdef USE_SSL - if (protocol == PROTO_HTTPS) { - SSL_free(ssl); - SSL_CTX_free(ssl_ctx); - } -#endif - + if (c) close(sockfd); return end - start + 1 - size; } + static int httpfs_getattr(const char *path, struct stat *stbuf) { + off_t size; + int res=0; + time_t mtime=time(NULL); + memset(stbuf, 0, sizeof(struct stat)); + if (strcmp(path, "/") == 0) { - stbuf->st_mode = S_IFDIR | 0755; /* read, write and search by owner, read and write by all other */ + stbuf->st_mode = S_IFDIR | 0555; stbuf->st_nlink = 2; - } else if (strcmp(path, httpfs_path) == 0) { - stbuf->st_mode = S_IFREG | 0555; /* read and execute for everybody */ - stbuf->st_nlink = 1; - stbuf->st_size = file_size; + stbuf->st_mtime = mtime; } else { - if (lstat(path+1, stbuf) == -1) - return -ENOENT; - } - return 0; -} - -static int httpfs_access(const char *path, int mask) { - int res; - - if (strcmp(path, "/") == 0) { - return 0; - } else if (strcmp(path, httpfs_path) == 0) { - return ((mask & W_OK) == W_OK) ? -1 : 0; - } else { - res = access(path+1, mask); - if (res == -1) - return -errno; - } - return 0; -} - -static int httpfs_readlink(const char *path, char *buf, size_t size) { - int res; - - res = readlink(path+1, buf, size - 1); - if (res == -1) - return -errno; - - buf[res] = '\0'; - return 0; -} +// fprintf(fou,"getattr -------------> path: %s\n", path); -static int httpfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) { - char items[300]; - struct dirent * item; - off_t Pointer; - size_t i; - (void) offset; - (void) fi; - - if(strcmp(path, "/") != 0) { - DIR *dp; - struct dirent *de; - - dp = opendir(path + 1); - if (dp == NULL) - return -errno; - - while ((de = readdir(dp)) != NULL) { - struct stat st; - memset(&st, 0, sizeof(st)); - st.st_ino = de->d_ino; - st.st_mode = de->d_type << 12; - if (filler(buf, de->d_name, &st, 0)) - break; + size = getSize(path); + if (size < 0) { + stbuf->st_mode = S_IFDIR | 0555; + stbuf->st_nlink = 2; + stbuf->st_mtime = mtime; } - - closedir(dp); - return 0; - - } - - lseek(targetFd, 0, 0); - for (Pointer = 0; (i = getdirentries(targetFd, items, 299, &Pointer)) > 0;) { - for (item = (struct dirent *) items; i > 0; item = (struct dirent *) ((char *) item + item->d_reclen)) { - struct stat st; - memset(&st, 0, sizeof(st)); - st.st_ino = item->d_ino; - st.st_mode = item->d_type << 12; - filler(buf, item->d_name, &st, 0); - i -= item->d_reclen; + else { + stbuf->st_mode = S_IFREG | 0444; + stbuf->st_nlink = 1; + stbuf->st_size = size; + stbuf->st_mtime = mtime; } - } - - filler(buf, httpfs_path + 1, NULL, 0); - - return 0; -} - -static int httpfs_mknod(const char *path, mode_t mode, dev_t rdev) { - int res; - - /* On Linux this could just be 'mknod(path, mode, rdev)' but this is more portable */ - if (S_ISREG(mode)) { - res = open(path+1, O_CREAT | O_EXCL | O_WRONLY, mode); - if (res >= 0) - res = close(res); - } else if (S_ISFIFO(mode)) - res = mkfifo(path, mode); - else - res = mknod(path, mode, rdev); - if (res == -1) - return -errno; - return 0; -} - -static int httpfs_mkdir(const char *path, mode_t mode) { - - if (mkdir(path+1, mode) == -1) - return -errno; - - return 0; -} - -static int httpfs_symlink(const char *from, const char *to) { - -// This function is only called, if "to" happens to reside in mount-point-folder. -// "from" is always an ordinary path + } +// else +// res=-ENOENT; - if (symlink(from, to+1) == -1) - return -errno; - - return 0; -} - -static int httpfs_unlink(const char *path) { - int res; - - res = unlink(path+1); - if (res == -1) - return -errno; - - return 0; -} - -static int httpfs_rmdir(const char *path) { - int res; - - res = rmdir(path+1); - if (res == -1) - return -errno; - - return 0; -} - -static int httpfs_rename(const char *from, const char *to) { - -// seems to be never called (cross-device link) -// replaced by copy and delete ? - - if (rename(from, to+1) == -1) - return -errno; - - return 0; + return res; } -static int httpfs_link(const char *from, const char *to) { - int res; - - res = link(from+1, to+1); - if (res == -1) - return -errno; - - return 0; +static int httpfs_access(const char *path, int mask) { + return 0; } -static int httpfs_chmod(const char *path, mode_t mode) { +static int httpfs_readdir(const char *path, void *buffer, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) { - if (strcmp(path, httpfs_path) == 0) { - return -EACCES; - } else { - if (chmod(path+1, mode) == -1) - return -errno; - } - return 0; -} + char *w, *i; + size_t len,min_len, bytes; + char file[BUFSIZE]; + char parent; + -static int httpfs_chown(const char *path, uid_t uid, gid_t gid) { +//if (strcmp(path,"/") !=0) +// return -ENOENT; - if (strcmp(path, httpfs_path) == 0) { - return -EACCES; - } else { - if (lchown(path+1, uid, gid) == -1) - return -errno; - } - return 0; -} +// fprintf(fou,"-------------start-----------\n pathuri=%s\n",path); -static int httpfs_truncate(const char *path, off_t size) { + (*filler)(buffer, ".", NULL, 0); + (*filler)(buffer, "..", NULL, 0); + + // download the given URL + w = calloc(BIGBUFSIZE, 1); + bytes = httpGet(path, 0, BIGBUFSIZE, w, 0); + if (bytes < 0) goto end; + + // Now actually parse the files + i = w; + while ( (i = strcasestr(i, "href=")) != NULL ) { + parent=1; + i += 5; + i += strspn(i, "\t \n\r"); + + if (*i == '"' || *i == '\'') i++; + i = relative_url(i); + + len = strcspn(i, "\"'\t \n\r>?"); + while (*i == '/') { + i++; + len--; + parent=0; + } + min_len = MIN(len, strcspn(i, "/")); - if (strcmp(path, httpfs_path) == 0) { - return -EACCES; - } else { - if (truncate(path+1, size) == -1) - return -errno; + if ((len > 0)&&(parent==1)) + { + strncpy(file, i, min_len); + file[min_len] = '\0'; +// fprintf(fou,"\nfile: %s\n", file); +// fprintf(fou,"http_file: %s\n", http_file); +// fprintf(fou,"path: %s\n", path); +// fprintf(fou,"http_path: %s\n\n", http_path); + + if (! (strncmp(file, http_file, strlen(http_file)) == 0 + && strlen(http_file)>0 + && strncmp(path, http_path, strlen(http_path)) == 0) ) { + (*filler)(buffer, file, NULL, 0); + } + } } - return 0; -} -static int httpfs_utime(const char *path, struct utimbuf *buf) { + if (strcmp(path, "/") == 0 && strlen(http_file) > 0) + { +// (*filler)(buffer, http_file, NULL, 0); + } - if (strcmp(path, httpfs_path) == 0) { - return -EACCES; - } else { - if (utime(path+1, buf) == -1) - return -errno; - } + i = w; + end: + free(w); +// fprintf(fou,"-------------end-----------\n"); return 0; } static int httpfs_open(const char *path, struct fuse_file_info *fi) { - int res; + off_t size; - if (strcmp(path, httpfs_path) == 0) { - if((fi->flags & 3) != O_RDONLY) { - return -EACCES; - } else { - return 0; - } - } - res = open(path+1, fi->flags); - if (res == -1) - return -errno; + if((fi->flags & 3) != O_RDONLY ) + return -EACCES; + + size = getSize(path); - close(res); +// fprintf(fou,"httpfs_open ----- path: %s\n", path); + + if (size > 0) fi->fh = size-1; return 0; - } static int httpfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { - int fd; - int res; size_t got; - (void) fi; - if (strcmp(path, httpfs_path) == 0) { - if (offset < file_size) { - if (offset + size > file_size) - size = file_size - offset; - got = HttpGet(offset, size, buf); - if (got < 0) - return -EIO; - if (got != size) { - close(sockfd); - sockfd = open_client_socket(host, port); - got = HttpGet(offset, size, buf); - if (got != size) - return -EIO; - } - } else - got = 0; - return got; - } else { - fd = open(path+1, O_RDONLY); - if (fd == -1) - return -errno; - res = pread(fd, buf, size, offset); - if (res == -1) - res = -errno; - close(fd); - return res; - } -} -static int httpfs_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) { - int fd; - int res; - - (void) fi; - if (strcmp(path, httpfs_path) == 0) { - return -EACCES; - } - fd = open(path+1, O_WRONLY); - if (fd == -1) - return -errno; +// fprintf(fou,"httpfs_read ----- path: %s\n", path); - res = pwrite(fd, buf, size, offset); - if (res == -1) - res = -errno; - close(fd); - return res; -} - -static int httpfs_statfs(const char *path, struct statvfs *stbuf) { - int res; - (void) path; - - res = statvfs(".", stbuf); - if (res == -1) - return -errno; + got = httpGet(path, offset, size, buf, fi->fh); + if (got < 0) + return -1; - return 0; + return got; } -static int httpfs_release(const char *path, struct fuse_file_info *fi) { - - /* Just a stub. This method is optional and can safely be left unimplemented */ - (void) path; - (void) fi; - return 0; -} - -static int httpfs_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) { - - /* Just a stub. This method is optional and can safely be left unimplemented */ - (void) path; - (void) isdatasync; - (void) fi; +static int httpfs_flush(const char *path, struct fuse_file_info *fi) { return 0; } static void *httpfs_init(void) { - fchdir(targetFd); /* that's the catch */ return NULL; } static void httpfs_destroy(void *arg) { - close(targetFd); -} - -#ifdef HAVE_SETXATTR -/* xattr operations are optional and can safely be left unimplemented */ -static int httpfs_setxattr(const char *path, const char *name, const char *value, - size_t size, int flags) { - - if (strcmp(path, httpfs_path) == 0) - return -EACCES; - if (lsetxattr(path+1, name, value, size, flags) == -1) - return -errno; - return 0; -} - -static int httpfs_getxattr(const char *path, const char *name, char *value, - size_t size) { - int res; - - if (strcmp(path, httpfs_path) == 0) - return -EACCES; - res = lgetxattr(path+1, name, value, size); - if (res == -1) - return -errno; - return res; + return; } -static int httpfs_listxattr(const char *path, char *list, size_t size) { - int res; - - if (strcmp(path, httpfs_path) == 0) - return -EACCES; - res = llistxattr(path+1, list, size); - if (res == -1) - return -errno; - return res; -} - -static int httpfs_removexattr(const char *path, const char *name) { - - if (strcmp(path, httpfs_path) == 0) - return -EACCES; - if (lremovexattr(path+1, name) == -1) - return -errno; - return 0; -} -#endif /* HAVE_SETXATTR */ - static struct fuse_operations httpfs_oper = { + .flush = httpfs_flush, .getattr = httpfs_getattr, .access = httpfs_access, - .readlink = httpfs_readlink, .readdir = httpfs_readdir, - .mknod = httpfs_mknod, - .mkdir = httpfs_mkdir, - .symlink = httpfs_symlink, - .unlink = httpfs_unlink, - .rmdir = httpfs_rmdir, - .rename = httpfs_rename, - .link = httpfs_link, - .chmod = httpfs_chmod, - .chown = httpfs_chown, - .truncate = httpfs_truncate, - .utime = httpfs_utime, .open = httpfs_open, .read = httpfs_read, - .write = httpfs_write, - .statfs = httpfs_statfs, - .release = httpfs_release, - .fsync = httpfs_fsync, .init = httpfs_init, .destroy = httpfs_destroy, -#ifdef HAVE_SETXATTR - .setxattr = httpfs_setxattr, - .getxattr = httpfs_getxattr, - .listxattr = httpfs_listxattr, - .removexattr= httpfs_removexattr, -#endif }; int main(int argc, char *argv[]) { struct stat mpstat; - int sr; - char* ri; + int sr,ok; char* fusev[4]; - - argv0 = argv[0]; + + + + myself = argv[0]; if (argc != 3) { - (void) fprintf(stderr, "usage: %s url mount-point\n", argv0); - (void) fprintf(stderr, ">>> Version: %s <<<\n", VERSION); + fprintf(stderr, "HTTP Filesystem version %s\n", VERSION); + fprintf(stderr, "usage: %s url mount-point\n", myself); return 1; } - argv1 = argv[1]; - argv2 = argv[2]; - protocol = parseURL(argv1, host, &file_name, &port); - if (protocol == -1) - return 1; +// fou=fopen("out","w+"); +// fprintf(fou,"debug:\n"); + + arg_url = argv[1]; + arg_mnt = argv[2]; - sockfd = open_client_socket(host, port); - if (sockfd < 0) + ok = parse_url(arg_url); + if (ok == -1) return 1; - if (getSize() != 0) + sockfd = open_client_socket(); + if (sockfd < 0) return 1; - sr = stat(argv2, &mpstat); + sr = stat(arg_mnt, &mpstat); if (sr < 0) { - (void) fprintf(stderr, "%s: bad mount-point %s\n", argv0, argv2); + fprintf(stderr, "%s: bad mount-point %s\n", myself, arg_mnt); return 1; } if ((mpstat.st_mode & S_IFDIR) == 0) { - (void) fprintf(stderr, "%s: %s is not a directory\n", argv0, argv2); - return 1; - } - - if ((targetFd = open(argv2, 0)) == -1) { - (void) fprintf(stderr, "%s: open %s failed\n", argv0, argv2); + fprintf(stderr, "%s: %s is not a directory\n", myself, arg_mnt); return 1; } - - ri = rindex(file_name, '/'); - if (ri == (char *) 0) { - httpfs_path[0] = '/'; - strcpy(&httpfs_path[1], file_name); - } else - strcpy(httpfs_path, ri); - - fusev[0] = argv0; - fusev[1] = "-o"; - fusev[2] = "nonempty"; - fusev[3] = argv2; + + fusev[0] = myself; + fusev[1] = "-s"; // disable multi threaded support (to make HTTP work) + fusev[2] = "-ononempty,attr_timeout=300,entry_timeout=300,negative_timeout=300,kernel_cache,allow_other"; + fusev[3] = arg_mnt; return fuse_main(argc+1, fusev, &httpfs_oper); /* close(sockfd); */ } +