diff options
author | dscho <dscho> | 2001-09-21 23:33:47 +0000 |
---|---|---|
committer | dscho <dscho> | 2001-09-21 23:33:47 +0000 |
commit | febced55362321b1015fe23ca7d7b831c28acfcc (patch) | |
tree | 2b2d95725059e4d1ea0c38a68c095e685d338b3f /httpd.c | |
parent | c880fd33ff71ca0b53eaf6bd9942f74be67fe4e4 (diff) | |
download | libvncserver-febced55362321b1015fe23ca7d7b831c28acfcc.zip libvncserver-febced55362321b1015fe23ca7d7b831c28acfcc.tar.gz |
http added, prepare for cursor
Diffstat (limited to 'httpd.c')
-rw-r--r-- | httpd.c | 383 |
1 files changed, 383 insertions, 0 deletions
@@ -0,0 +1,383 @@ +/* + * httpd.c - a simple HTTP server + */ + +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <fcntl.h> +#include <errno.h> +#include <pwd.h> + +#include "rfb.h" + +#define NOT_FOUND_STR "HTTP/1.0 404 Not found\n\n" \ + "<HEAD><TITLE>File Not Found</TITLE></HEAD>\n" \ + "<BODY><H1>File Not Found</H1></BODY>\n" + +#define OK_STR "HTTP/1.0 200 OK\n\n" + +static void httpProcessInput(); +static Bool compareAndSkip(char **ptr, const char *str); + +/* +int httpPort = 0; +char *httpDir = NULL; + +int httpListenSock = -1; +int httpSock = -1; +FILE* httpFP = NULL; +*/ + +#define BUF_SIZE 32768 + +static char buf[BUF_SIZE]; + + +/* + * httpInitSockets sets up the TCP socket to listen for HTTP connections. + */ + +void +httpInitSockets(rfbScreenInfoPtr rfbScreen) +{ + static Bool done = FALSE; + + if (done) + return; + + done = TRUE; + + if (!rfbScreen->httpDir) + return; + + if (rfbScreen->httpPort == 0) { + rfbScreen->httpPort = rfbScreen->rfbPort-100; + } + + rfbLog("Listening for HTTP connections on TCP port %d\n", rfbScreen->httpPort); + + rfbLog(" URL http://%s:%d\n",rfbScreen->rfbThisHost,rfbScreen->httpPort); + + if ((rfbScreen->httpListenSock = ListenOnTCPPort(rfbScreen->httpPort)) < 0) { + rfbLogPerror("ListenOnTCPPort"); + exit(1); + } + + //AddEnabledDevice(httpListenSock); +} + + +/* + * httpCheckFds is called from ProcessInputEvents to check for input on the + * HTTP socket(s). If there is input to process, httpProcessInput is called. + */ + +void +httpCheckFds(rfbScreenInfoPtr rfbScreen) +{ + int nfds, n; + fd_set fds; + struct timeval tv; + struct sockaddr_in addr; + int addrlen = sizeof(addr); + + if (!rfbScreen->httpDir) + return; + + FD_ZERO(&fds); + FD_SET(rfbScreen->httpListenSock, &fds); + if (rfbScreen->httpSock >= 0) { + FD_SET(rfbScreen->httpSock, &fds); + } + tv.tv_sec = 0; + tv.tv_usec = 0; + nfds = select(max(rfbScreen->httpSock,rfbScreen->httpListenSock) + 1, &fds, NULL, NULL, &tv); + if (nfds == 0) { + return; + } + if (nfds < 0) { + rfbLogPerror("httpCheckFds: select"); + return; + } + + if ((rfbScreen->httpSock >= 0) && FD_ISSET(rfbScreen->httpSock, &fds)) { + httpProcessInput(rfbScreen); + } + + if (FD_ISSET(rfbScreen->httpListenSock, &fds)) { + if (rfbScreen->httpSock >= 0) close(rfbScreen->httpSock); + + if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock, + (struct sockaddr *)&addr, &addrlen)) < 0) { + rfbLogPerror("httpCheckFds: accept"); + return; + } + if ((rfbScreen->httpFP = fdopen(rfbScreen->httpSock, "r+")) == NULL) { + rfbLogPerror("httpCheckFds: fdopen"); + close(rfbScreen->httpSock); + rfbScreen->httpSock = -1; + return; + } + + //AddEnabledDevice(httpSock); + } +} + + +static void +httpCloseSock(rfbScreenInfoPtr rfbScreen) +{ + fclose(rfbScreen->httpFP); + rfbScreen->httpFP = NULL; + //RemoveEnabledDevice(httpSock); + rfbScreen->httpSock = -1; +} + +static rfbClientRec cl; + +/* + * httpProcessInput is called when input is received on the HTTP socket. + */ + +static void +httpProcessInput(rfbScreenInfoPtr rfbScreen) +{ + struct sockaddr_in addr; + int addrlen = sizeof(addr); + char fullFname[256]; + char *fname; + int maxFnameLen; + int fd; + Bool gotGet = FALSE; + Bool performSubstitutions = FALSE; + char str[256]; + struct passwd *user = getpwuid(getuid());; + + cl.sock=rfbScreen->httpSock; + + if (strlen(rfbScreen->httpDir) > 200) { + rfbLog("-httpd directory too long\n"); + httpCloseSock(rfbScreen); + return; + } + strcpy(fullFname, rfbScreen->httpDir); + fname = &fullFname[strlen(fullFname)]; + maxFnameLen = 255 - strlen(fullFname); + + buf[0] = '\0'; + + while (1) { + + /* Read lines from the HTTP client until a blank line. The only + line we need to parse is the line "GET <filename> ..." */ + + if (!fgets(buf, BUF_SIZE, rfbScreen->httpFP)) { + rfbLogPerror("httpProcessInput: fgets"); + httpCloseSock(rfbScreen); + return; + } + + if ((strcmp(buf,"\n") == 0) || (strcmp(buf,"\r\n") == 0) + || (strcmp(buf,"\r") == 0) || (strcmp(buf,"\n\r") == 0)) + /* end of client request */ + break; + + if (strncmp(buf, "GET ", 4) == 0) { + gotGet = TRUE; + + if (strlen(buf) > maxFnameLen) { + rfbLog("GET line too long\n"); + httpCloseSock(rfbScreen); + return; + } + + if (sscanf(buf, "GET %s HTTP/1.0", fname) != 1) { + rfbLog("couldn't parse GET line\n"); + httpCloseSock(rfbScreen); + return; + } + + if (fname[0] != '/') { + rfbLog("filename didn't begin with '/'\n"); + WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); + httpCloseSock(rfbScreen); + return; + } + + if (strchr(fname+1, '/') != NULL) { + rfbLog("asking for file in other directory\n"); + WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); + httpCloseSock(rfbScreen); + return; + } + + getpeername(rfbScreen->httpSock, (struct sockaddr *)&addr, &addrlen); + rfbLog("httpd: get '%s' for %s\n", fname+1, + inet_ntoa(addr.sin_addr)); + continue; + } + } + + if (!gotGet) { + rfbLog("no GET line\n"); + httpCloseSock(rfbScreen); + return; + } + + /* If we were asked for '/', actually read the file index.vnc */ + + if (strcmp(fname, "/") == 0) { + strcpy(fname, "/index.vnc"); + rfbLog("httpd: defaulting to '%s'\n", fname+1); + } + + /* Substitutions are performed on files ending .vnc */ + + if (strlen(fname) >= 4 && strcmp(&fname[strlen(fname)-4], ".vnc") == 0) { + performSubstitutions = TRUE; + } + + /* Open the file */ + + if ((fd = open(fullFname, O_RDONLY)) < 0) { + rfbLogPerror("httpProcessInput: open"); + WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); + httpCloseSock(rfbScreen); + return; + } + + WriteExact(&cl, OK_STR, strlen(OK_STR)); + + while (1) { + int n = read(fd, buf, BUF_SIZE-1); + if (n < 0) { + rfbLogPerror("httpProcessInput: read"); + close(fd); + httpCloseSock(rfbScreen); + return; + } + + if (n == 0) + break; + + if (performSubstitutions) { + + /* Substitute $WIDTH, $HEIGHT, etc with the appropriate values. + This won't quite work properly if the .vnc file is longer than + BUF_SIZE, but it's reasonable to assume that .vnc files will + always be short. */ + + char *ptr = buf; + char *dollar; + buf[n] = 0; /* make sure it's null-terminated */ + + while ((dollar = strchr(ptr, '$'))!=NULL) { + WriteExact(&cl, ptr, (dollar - ptr)); + + ptr = dollar; + + if (compareAndSkip(&ptr, "$WIDTH")) { + + sprintf(str, "%d", rfbScreen->width); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$HEIGHT")) { + + sprintf(str, "%d", rfbScreen->height); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$APPLETWIDTH")) { + + sprintf(str, "%d", rfbScreen->width); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$APPLETHEIGHT")) { + + sprintf(str, "%d", rfbScreen->height + 32); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$PORT")) { + + sprintf(str, "%d", rfbScreen->rfbPort); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$DESKTOP")) { + + WriteExact(&cl, rfbScreen->desktopName, strlen(rfbScreen->desktopName)); + + } else if (compareAndSkip(&ptr, "$DISPLAY")) { + + sprintf(str, "%s:%d", rfbScreen->rfbThisHost, rfbScreen->rfbPort-5900); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$USER")) { + + if (user) { + WriteExact(&cl, user->pw_name, + strlen(user->pw_name)); + } else { + WriteExact(&cl, "?", 1); + } + + } else { + if (!compareAndSkip(&ptr, "$$")) + ptr++; + + if (WriteExact(&cl, "$", 1) < 0) { + close(fd); + httpCloseSock(rfbScreen); + return; + } + } + } + if (WriteExact(&cl, ptr, (&buf[n] - ptr)) < 0) + break; + + } else { + + /* For files not ending .vnc, just write out the buffer */ + + if (WriteExact(&cl, buf, n) < 0) + break; + } + } + + close(fd); + httpCloseSock(rfbScreen); +} + + +static Bool +compareAndSkip(char **ptr, const char *str) +{ + if (strncmp(*ptr, str, strlen(str)) == 0) { + *ptr += strlen(str); + return TRUE; + } + + return FALSE; +} |