/**************************************************************************** * netutils.c * * * * LocalGate Local Filesystem -> WWW Gateway * * * * LocalGate allows a user to access HTML files on the local filesystem * * as if they had a web server to access the files. * * * * netutils.c: network access routines for LocalGate. * * * * Copyright (C) 1997 Michael Chu * * This file is part of the LocalGate local filesystem -> WWW gateway. * * * * This program 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 program 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 program; if not, write to the Free Software * * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * * Contact information: Michael Chu * * mmchu@pobox.com * ****************************************************************************/ /*********************************** INCLUDES *******************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "htmlfilter.h" #include "fileutils.h" #include "netutils.h" /********************************** FUNCTIONS *******************************/ /**************************************************************************** * socketInit: * * * * Summary: Initializes a socket for the server listening on the given * * port. * * * * Input: serverPort: server port to listen to. * * * * Returns: Resulting socket id (non-negative integer). * * -1 if socket creation error. * * * * Global State Affected: * * Socket is bound to given port. * ****************************************************************************/ int socketInit(int serverPort) { int newSocketId = 0; /* new socket id. */ struct sockaddr_in socketSettings; /* socket settings. */ /* guarantee that the server port is non-negative. */ assert(serverPort >= 0); /* try to create a TCP socket. */ newSocketId = socket(AF_INET, SOCK_STREAM, 0); /* if creation of socket failed, say so. */ if (newSocketId < 0) { /* show error. */ fprintf(stderr, "Socket creation error! (errno = %d)\n", errno); /* return error. */ return(-1); } /* set up the socket settings. */ memset(&socketSettings, 0, sizeof(socketSettings)); socketSettings.sin_family = AF_INET; socketSettings.sin_addr.s_addr = htonl(INADDR_ANY); socketSettings.sin_port = htons(serverPort); /* bind the socket to the given port. */ /* if there is a problem binding, then say so. */ if ((bind(newSocketId, (struct sockaddr *) &socketSettings, sizeof(socketSettings))) < 0) { /* show error. */ fprintf(stderr, "Socket binding error! (errno = %d)\n", errno); /* return error. */ return(-1); } /* listen to the socket. */ listen(newSocketId, 5); /* print socket listening settings. */ fprintf(stderr, "Server listening on server port [%d]\n", serverPort); /* return the new socket id. */ return(newSocketId); } /**************************************************************************** * getIncomingConnection: * * * * Summary: Blocks until a connection is received on the given socket id. * * * * Input: socketId: socket id to connect from. * * * * Returns: Returns the socket id (non-negative integer). * * -1 if socket connection error. * * * * Global State Affected: * * New socket is created to the new incoming connection. * ****************************************************************************/ int getIncomingConnection(int socketId) { int newSocketId = 0; /* new socket id. */ struct sockaddr_in incomingSettings; /* incoming socket settings. */ int incomingSettingsLength = 0; /* incoming settings length. */ /* make sure that socket id is non-negative. */ assert(socketId >= 0); /* get the length of the settings length. */ incomingSettingsLength = sizeof(incomingSettings); /* accept an incoming connection. */ newSocketId = accept(socketId, (struct sockaddr *) &incomingSettings, &incomingSettingsLength); /* if incoming connection had an error, say so. */ if (newSocketId < 0) { /* show error. */ fprintf(stderr, "Incoming socket connection error! (errno = %d)\n", errno); /* return error. */ return(-1); } /* return new socket id. */ return(newSocketId); } /**************************************************************************** * getLocalFileRequest: * * * * Summary: Gets a string from the socket and try to extract a local file * * request. * * NOTE: if a directory is the target, then we will append * * /index.html automatically. * * * * Input: socketId: socket id to write to. * * * * Returns: String of filename requested. * * NULL if no filename requested or invalid request. * * * * Global State Affected: * * String is read from the socket. * ****************************************************************************/ char *getLocalFileRequest(int socketId) { char incomingLine[DEFAULT_BUFFER_SIZE] = "\0"; /* line buffer. */ int lineLength = 0; /* length of line read in. */ char *stringToken = NULL; /* used to tokenize the input. */ char *fileRedirection = NULL; /* stores the redirection string. */ char *fileRequest = NULL; /* stores copy of file request. */ struct stat fileStat; /* used to get file status. */ int nameLength = 0; /* length of name request. */ int hasConformityTag = 0; /* flag for whether or not request has conformity tag. */ /* make sure that socket id is non-negative. */ assert(socketId >= 0); /* try to read in a line from the socket. */ lineLength = readLineFromSocket(socketId, incomingLine, DEFAULT_BUFFER_SIZE); /* if we got a negative number, then we must have an error. */ /* if we got zero length back, then remote side must have closed. */ if (lineLength <= 0) { /* return NULL. */ return(NULL); } /* check to see if request has conformity tag. */ hasConformityTag = hasHTTPConformityTag(incomingLine); /* try to tokenize the string for action word "GET". */ stringToken = strtok(incomingLine, " "); /* make sure we received a word. */ if (stringToken == NULL) { /* show error. */ fprintf(stderr, "Error tokenizing \"%s\" for action word!\n", incomingLine); /* try to send error message to client about unknown request. */ sendUnknownQueryError(socketId, hasConformityTag); /* return NULL. */ return(NULL); } /* check to make sure first word is "GET". */ if ((strcmp(stringToken, "GET")) != 0) { /* show error. */ fprintf(stderr, "Did not receive \"GET\" as action word! "); fprintf(stderr, "(received = \"%s\")\n", stringToken); /* try to send error message to client about unknown request. */ sendUnknownQueryError(socketId, hasConformityTag); /* return NULL. */ return(NULL); } /* try to tokenize the string for the file request. */ stringToken = strtok(NULL, " "); /* make sure we received a word. */ if (stringToken == NULL) { /* show error. */ fprintf(stderr, "Error tokenizing \"%s\" for file request!\n", incomingLine); /* try to send error message to client about unknown request. */ sendUnknownQueryError(socketId, hasConformityTag); /* return NULL. */ return(NULL); } /* make sure we received a path. */ if ((stringToken[0]) != '/') { /* show error. */ fprintf(stderr, "Did not receive full path for file request! "); fprintf(stderr, "(received = \"%s\")\n", stringToken); /* try to send error message to client about unknown request. */ sendUnknownQueryError(socketId, hasConformityTag); /* return NULL. */ return(NULL); } /* get the length of the name request. */ nameLength = strlen(stringToken); /* get status of selected file. */ if (stat(stringToken, &fileStat) < 0) { /* either we could not stat file or it does not exist. */ if (errno == ENOENT) { /* show error. */ fprintf(stderr, "Requested file does not exist! "); fprintf(stderr, "(requested = \"%s\")\n", stringToken); /* try to send error message to client about file not existing. */ sendFileNotExistError(socketId, stringToken, hasConformityTag); /* return NULL. */ return(NULL); } else { /* show error. */ fprintf(stderr, "Error stating file %s! (errno = %d)\n", stringToken, errno); /* return NULL. */ return(NULL); } } /* if the request is a directory, then append index.html, otherwise, just copy over the request. */ if (S_ISDIR(fileStat.st_mode)) { /* if the name does not end in a '/', then send redirection message back. */ if (stringToken[nameLength-1] != '/') { /* do not print out to stderr an error since this may occur frequently. */ /* allocation memory for the redirection string. */ fileRedirection = (char *) malloc(nameLength+1+1); /* make sure malloc worked. */ assert(fileRedirection != NULL); /* make a copy of the file request. */ strcpy(fileRedirection, stringToken); /* append "/" to the end. */ strcat(fileRedirection, "/"); /* try to send a redirection message to force browser to append a '/'. */ sendDocumentHasMovedMessage(socketId, fileRedirection, hasConformityTag); /* free the redirection string. */ free(fileRedirection); fileRedirection = NULL; /* return NULL. */ return(NULL); } /* allocate memory to make a copy of the file request. */ fileRequest = (char *) malloc(nameLength+strlen("index.html")+1); /* make sure malloc worked. */ assert(fileRequest != NULL); /* make a copy of the file request. */ strcpy(fileRequest, stringToken); /* append "index.html". */ strcat(fileRequest, "index.html"); /* get status of new selected file. */ if (stat(fileRequest, &fileStat) < 0) { /* either we could not stat file or it does not exist. */ if (errno == ENOENT) { /* try to send error message to client about file not existing. */ sendFileNotExistError(socketId, fileRequest, hasConformityTag); /* free fileRequest. */ free(fileRequest); fileRequest = NULL; /* return NULL. */ return(NULL); } else { /* show error. */ fprintf(stderr, "Error stating file %s! (errno = %d)\n", fileRequest, errno); /* free fileRequest. */ free(fileRequest); fileRequest = NULL; /* return NULL. */ return(NULL); } } } else { /* allocate memory to make a copy of the file request. */ fileRequest = (char *) malloc(nameLength+1); /* make sure malloc worked. */ assert(fileRequest != NULL); /* make a copy of the file request. */ strcpy(fileRequest, stringToken); } /* return the file name found. */ return(fileRequest); } /**************************************************************************** * readFromSocket: * * * * Summary: Reads the given number of bytes from the given socket into the * * given buffer. * * * * Input: socketId: socket id to read from. * * buffer: buffer to which to write to. * * maxBytes: maximum number of bytes to read from socket. * * * * Returns: Actual number of bytes read from the socket. * * * * Global State Affected: * * Given number of bytes is read from the given socket. * ****************************************************************************/ int readFromSocket(int socketId, char *buffer, int maxBytes) { int numBytesRead = 0; /* number of bytes read. */ /* make sure that socket id is non-negative. */ assert(socketId >= 0); /* make sure that buffer is not NULL. */ assert(buffer != NULL); /* make sure that number of bytes to read is non-negative. */ assert(maxBytes >= 0); /* try to read bytes. */ numBytesRead = read(socketId, buffer, maxBytes); /* if we had an error reading from the socket, show it. */ if (numBytesRead < 0) { /* show error. */ fprintf(stderr, "Error reading from socket! (errno = %d)\n", errno); /* return error. */ return(-1); } /* return the number of bytes read. */ return(numBytesRead); } /**************************************************************************** * readLineFromSocket: * * * * Summary: Reads a line from the given socket into the given buffer. * * Read at most the given maximum byte length. * * NOTE: the newline is stripped automatically. * * NOTE: one character used automatically for NULL-termination. * * NOTE: we check for a non-printable character instead of * * actually newline since newline is not always just one * * character. * * * * Input: socketId: socket id to read from. * * buffer: buffer to which to write to. * * maxBytes: maximum number of bytes to read from socket. * * * * Returns: Actual number of bytes read from the socket. * * * * Global State Affected: * * One line is read in from the given socket. * ****************************************************************************/ int readLineFromSocket(int socketId, char *buffer, int maxBytes) { int numBytesRead = 0; /* number of bytes read. */ int numBytesCurrentlyRead = 0; /* number of bytes read currently. */ char byteRead = '\0'; /* byte that is read. */ /* make sure that socket id is non-negative. */ assert(socketId >= 0); /* make sure that buffer is not NULL. */ assert(buffer != NULL); /* make sure that number of bytes to read is non-negative. */ assert(maxBytes >= 0); /* iterate until read one line or maximum bytes are read. */ for (numBytesRead = 1; numBytesRead < maxBytes; numBytesRead++) { /* try to read a byte. */ numBytesCurrentlyRead = read(socketId, &byteRead, 1); /* make sure byte was read. */ if (numBytesCurrentlyRead == 1) { /* insert byte into buffer. */ buffer[numBytesRead-1] = byteRead; /* if we read a non-printable character then stop. */ /* this is most likely the newline. */ if (!(isprint(byteRead))) { break; } } else if (numBytesCurrentlyRead == 0) { /* return total read. */ return(numBytesRead-1); } else { /* show error. */ fprintf(stderr, "Error reading line from socket!\n"); /* return error. */ return(-1); } } /* set the last written byte to NULL. this will also override the last newline. */ buffer[numBytesRead-1] = '\0'; /* return the string length. */ return(strlen(buffer)); } /**************************************************************************** * writeToSocket: * * * * Summary: Writes the given number of bytes from the given buffer to the * * socket given by the socket id. * * * * Input: socketId: socket id to write to. * * buffer: buffer from which to write from. * * numBytes: number of bytes to write to the socket. * * * * Returns: Actual number of bytes written to the socket. * * * * Global State Affected: * * Given number of bytes is written out to the given socket. * ****************************************************************************/ int writeToSocket(int socketId, char *buffer, int numBytes) { int numBytesLeft = 0; /* number of bytes left to write. */ int numBytesWritten = 0; /* number of bytes written. */ /* make sure that socket id is non-negative. */ assert(socketId >= 0); /* make sure that buffer is not NULL. */ assert(buffer != NULL); /* make sure that number of bytes to write is non-negative. */ assert(numBytes >= 0); /* set the number of bytes left to write. */ numBytesLeft = numBytes; /* iterate until all bytes have been written. */ while (numBytesLeft > 0) { /* try to write out bytes to the socket. */ numBytesWritten = write(socketId, buffer, numBytesLeft); /* if we have written out no bytes, then stop. */ if (numBytesWritten <= 0) { /* show error. */ fprintf(stderr, "Error writing to socket! (errno = %d)\n", errno); /* return error. */ return(numBytesWritten); } /* decrement number of bytes left to send. */ numBytesLeft = numBytesLeft - numBytesWritten; /* increment pointer of buffer to unwritten part. */ buffer = buffer + numBytesWritten; } /* return bytes written. */ return(numBytes - numBytesLeft); } /**************************************************************************** * writeFileToSocket: * * * * Summary: Writes the given file to the socket given by the socket id. * * * * Input: socketId: socket id to write to. * * fileName: file to read from. * * * * Returns: 0 if there was an error sending file to socket. * * 1 if the file was sent correctly. * * * * Global State Affected: * * Given file is written out to the given socket. * ****************************************************************************/ int writeFileToSocket(int socketId, char *fileName) { char inputBuffer[DEFAULT_BUFFER_SIZE] = "\0"; /* buffer for input file. */ int fileId = 0; /* file id of the file to read from. */ int numBytesFromFile = 0; /* number of bytes read from the files. */ /* make sure that socket id is non-negative. */ assert(socketId >= 0); /* make sure that fileName is not NULL. */ assert(fileName != NULL); /* try to open the file for reading. */ fileId = open(fileName, O_RDONLY); /* if we could not open the file, show error. */ if (fileId < 0) { /* show error. */ fprintf(stderr, "Error opening %s for reading! (errno = %d)\n", fileName, errno); /* return error. */ return(0); } /* if this is an HTML file, then we will filter it. otherwise, just dump it out. */ if (isHTMLFileName(fileName)) { /* it is an HTML file so filter it before dumping to socket. */ if (!(filterHTMLFileToSocket(fileId, socketId))) { /* show error. */ fprintf(stderr, "Error filtering %s to client!\n", fileName); /* close the file id. */ close(fileId); /* return error. */ return(0); } } else { /* not an HTML file so just dump to socket. */ /* continue iterating to get all bytes from the file. */ for (;;) { /* try to read in a bufferful of bytes from the file. */ numBytesFromFile = readFromFile(fileId, inputBuffer, DEFAULT_BUFFER_SIZE); /* if we read some bytes, output them to the socket. */ /* if we got an error, return an error. */ /* if we have reached the end of the file, then stop. */ if (numBytesFromFile > 0) { /* try to send out file bytes. */ /* if we do not send out a bytes completely, stop and return error. */ if ((writeToSocket(socketId, inputBuffer, numBytesFromFile)) != numBytesFromFile) { /* show error. */ fprintf(stderr, "Error sending %s to client!\n", fileName); /* close the file id. */ close(fileId); /* return error. */ return(0); } } else if (numBytesFromFile < 0) { /* show error. */ fprintf(stderr, "Error reading from %s!\n", fileName); /* close the file id. */ close(fileId); /* return error. */ return(0); } else { /* exit loop. */ break; } } } /* close the file id. */ close(fileId); /* return success. */ return(1); } /**************************************************************************** * hasHTTPConformityTag: * * * * Summary: Checks the given string to see if it has the HTTP conformity * * tag in it. * * NOTE: Tag is "HTTP*" starting anywhere but the first * * position. * * * * Input: requestString: the request string received. * * * * Returns: 0 if there was an HTTP conformity tag in it. * * 1 if the error message was sent correctly. * * * * Global State Affected: * * Error message is sent to client. * ****************************************************************************/ int hasHTTPConformityTag(char *requestString) { char *requestStringCopy = NULL; /* makes a copy of the request string. */ char *stringToken = NULL; /* holds the string tokens. */ int httpTagFound = 0; /* flag for if we found "HTTP". */ int tokenLength = 0; /* length of the token. */ int tokenIndex = 0; /* index used to traverse token. */ int tokenCount = 0; /* counts number of tokens received. */ /* make sure requestString is non NULL. */ assert(requestString != NULL); /* allocate memory to make a copy of the request string. */ requestStringCopy = (char *) malloc(strlen(requestString)+1); /* make sure malloc worked. */ assert(requestStringCopy != NULL); /* make a copy of the request string. */ strcpy(requestStringCopy, requestString); /* start to tokenize string. */ stringToken = strtok(requestStringCopy, " "); /* if we got a token, then up the count. */ if (stringToken != NULL) { /* increment count. */ tokenCount++; } /* get second token. */ stringToken = strtok(NULL, " "); /* if we got a token, then up the count. */ if (stringToken != NULL) { /* increment count. */ tokenCount++; } /* while we have not found the tag, we will keep looking for the tag. */ /* we will actually, count all tags for tag count. */ while (stringToken != NULL) { /* get the length of the token. */ tokenLength = strlen(stringToken); /* traverse over the token and make it upper-cased. */ for (tokenIndex = 0; tokenIndex < tokenLength; tokenIndex++) { /* in-line replace the character with the upper-case version. */ stringToken[tokenIndex] = toupper(stringToken[tokenIndex]); } /* if we find the "HTTP" tag, then flag that. */ if ((strncmp(stringToken, "HTTP", 4)) == 0) { /* set "HTTP" found flag. */ httpTagFound = 1; /* stop looking! */ break; } /* get next token. */ stringToken = strtok(NULL, " "); /* if we got a token, then up the count. */ if (stringToken != NULL) { /* increment count. */ tokenCount++; } } /* free the copy of the request string. */ free(requestStringCopy); requestStringCopy = NULL; /* return the status of the search for the conformity tag. */ /* we must have at least 3 tokens for conformity. */ return(httpTagFound && (tokenCount >= 3)); } /**************************************************************************** * sendUnknownQueryError: * * * * Summary: Tries to send an error page to client saying they sent a query * * that server could not understand. * * * * Input: socketId: socket id to write to. * * conformsHTML: flag for whether or not browser conforms HTML. * * * * Returns: 0 if there was an error sending error message. * * 1 if the error message was sent correctly. * * * * Global State Affected: * * Error message is sent to client. * ****************************************************************************/ int sendUnknownQueryError(int socketId, int conformsHTML) { char sendBuffer[DEFAULT_BUFFER_SIZE] = "\0"; /* buffer for sending message. */ int sendLengthSize = 0; /* length of sending message. */ time_t timeNumber = 0; /* stores the current time. */ char timeBuffer[DEFAULT_BUFFER_SIZE] = "\0"; /* buffer for time string. */ /* make sure that socket id is non-negative. */ assert(socketId >= 0); /* if this conforms to HTML, then generate the header message. */ if (conformsHTML) { /* get the current time. */ timeNumber = time(NULL); /* generate the time string. */ strftime(timeBuffer, DEFAULT_BUFFER_SIZE, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&timeNumber)); /* compose header message. */ strcat(sendBuffer, "HTTP/1.0 400 Bad Request\n"); strcat(sendBuffer, "Date: "); strcat(sendBuffer, timeBuffer); strcat(sendBuffer, "\n"); strcat(sendBuffer, "Server: LocalGate/1.00\n"); strcat(sendBuffer, "Content-type: text/html\n"); strcat(sendBuffer, "\n"); } /* compose error message and calculate length. */ strcat(sendBuffer, "Bad Request\n"); strcat(sendBuffer, "

Bad Request

\n"); strcat(sendBuffer, "Your browser sent a query that\n"); strcat(sendBuffer, "this server could not understand.

\n"); strcat(sendBuffer, "\n"); sendLengthSize = strlen(sendBuffer); /* try to send out message. */ /* if we do not send out a message completely, stop and return error. */ if ((writeToSocket(socketId, sendBuffer, sendLengthSize)) != sendLengthSize) { /* show error. */ fprintf(stderr, "Error sending error message back to client!\n"); /* return error. */ return(0); } /* return success on error message. */ return(1); } /**************************************************************************** * sendFileNotExistError: * * * * Summary: Tries to send an error page to client saying they sent a query * * for a file that does not exist. * * * * Input: socketId: socket id to write to. * * fileName: file name of file that does not exist. * * conformsHTML: flag for whether or not browser conforms HTML. * * * * Returns: 0 if there was an error sending error message. * * 1 if the error message was sent correctly. * * * * Global State Affected: * * Error message is sent to client. * ****************************************************************************/ int sendFileNotExistError(int socketId, char *fileName, int conformsHTML) { char sendBuffer[DEFAULT_BUFFER_SIZE] = "\0"; /* buffer for sending message. */ int sendLengthSize = 0; /* length of sending message. */ time_t timeNumber = 0; /* stores the current time. */ char timeBuffer[DEFAULT_BUFFER_SIZE] = "\0"; /* buffer for time string. */ /* make sure that socket id is non-negative. */ assert(socketId >= 0); /* if this conforms to HTML, then generate the header message. */ if (conformsHTML) { /* get the current time. */ timeNumber = time(NULL); /* generate the time string. */ strftime(timeBuffer, DEFAULT_BUFFER_SIZE, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&timeNumber)); /* compose header message. */ strcat(sendBuffer, "HTTP/1.0 404 Not found\n"); strcat(sendBuffer, "Date: "); strcat(sendBuffer, timeBuffer); strcat(sendBuffer, "\n"); strcat(sendBuffer, "Server: LocalGate/1.00\n"); strcat(sendBuffer, "Content-type: text/html\n"); strcat(sendBuffer, "\n"); } /* compose error message and calculate length. */ strcat(sendBuffer, "File Not found\n"); strcat(sendBuffer, "

File Not found

\n"); strcat(sendBuffer, "The requested URL "); strcat(sendBuffer, fileName); strcat(sendBuffer, " was not found on this server.

\n"); strcat(sendBuffer, "\n"); sendLengthSize = strlen(sendBuffer); /* try to send out message. */ /* if we do not send out a message completely, stop and return error. */ if ((writeToSocket(socketId, sendBuffer, sendLengthSize)) != sendLengthSize) { /* show error. */ fprintf(stderr, "Error sending error message back to client!\n"); /* return error. */ return(0); } /* return success on error message. */ return(1); } /**************************************************************************** * sendDocumentHasMovedMessage: * * * * Summary: Tries to send a redirection page to client saying that the * * document is at a new location. * * * * Input: socketId: socket id to write to. * * newLocation: the new location where the document can be found. * * conformsHTML: flag for whether or not browser conforms HTML. * * * * Returns: 0 if there was an error sending redirection message. * * 1 if the redirection message was sent correctly. * * * * Global State Affected: * * Redirection message is sent to client. * ****************************************************************************/ int sendDocumentHasMovedMessage(int socketId, char *newLocation, int conformsHTML) { char sendBuffer[DEFAULT_BUFFER_SIZE] = "\0"; /* buffer for sending message. */ int sendLengthSize = 0; /* length of sending message. */ time_t timeNumber = 0; /* stores the current time. */ char timeBuffer[DEFAULT_BUFFER_SIZE] = "\0"; /* buffer for time string. */ /* make sure that socket id is non-negative. */ assert(socketId >= 0); /* if this conforms to HTML, then generate the header message. */ if (conformsHTML) { /* get the current time. */ timeNumber = time(NULL); /* generate the time string. */ strftime(timeBuffer, DEFAULT_BUFFER_SIZE, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&timeNumber)); /* compose header message. */ strcat(sendBuffer, "HTTP/1.0 302 Found\n"); strcat(sendBuffer, "Date: "); strcat(sendBuffer, timeBuffer); strcat(sendBuffer, "\n"); strcat(sendBuffer, "Server: LocalGate/1.00\n"); strcat(sendBuffer, "Location: "); strcat(sendBuffer, newLocation); strcat(sendBuffer, "\n"); strcat(sendBuffer, "Content-type: text/html\n"); strcat(sendBuffer, "\n"); } /* compose error message and calculate length. */ strcat(sendBuffer, "Document moved\n"); strcat(sendBuffer, "

Document moved

\n"); strcat(sendBuffer, "The document has moved here.

\n"); strcat(sendBuffer, "\n"); sendLengthSize = strlen(sendBuffer); /* try to send out message. */ /* if we do not send out a message completely, stop and return error. */ if ((writeToSocket(socketId, sendBuffer, sendLengthSize)) != sendLengthSize) { /* show error. */ fprintf(stderr, "Error sending redirection message back to client!\n"); /* return error. */ return(0); } /* return success on redirection message. */ return(1); }