Goto sanos source index

//
// hfile.c
//
// HTTP file handler
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <httpd.h>

char *get_extension(char *path) {
  char *ext;

  ext = NULL;
  while (*path) {
    if (*path == PS1 || *path == PS2) {
      ext = NULL;
    } else if (*path == '.') {
      ext = path + 1;
    }
    path++;
  }

  return ext;
}

int httpd_return_file_error(struct httpd_connection *conn, int err) {
  switch (err) {
    case EACCES: 
      return httpd_send_error(conn->rsp, 403, "Forbidden", NULL);

    case ENOENT:
      return httpd_send_error(conn->rsp, 404, "Not Found", NULL);

    default:
      return httpd_send_error(conn->rsp, 500, "Internal Server Error", strerror(err));
  }
}

int ls(struct httpd_connection *conn) {
  int dir;
  int rc;
  int urllen;
  struct stat64 statbuf;
  struct direntry dirp;
  char path[MAXPATH];
  char buf[32];
  struct tm *tm;

  dir = _opendir(conn->req->path_translated);
  if (dir < 0) return httpd_return_file_error(conn, errno);

  urllen = strlen(conn->req->decoded_url);
  if (urllen > 0 && conn->req->decoded_url[urllen - 1] == '/') urllen--;
  httpd_send(conn->rsp, "<HTML><HEAD><TITLE>Index of ", -1);
  httpd_send(conn->rsp, conn->req->decoded_url, urllen);
  httpd_send(conn->rsp, "</TITLE></HEAD>\r\n<BODY>\r\n<H1>Index of ", -1);
  
  if (urllen == 0) {
    httpd_send(conn->rsp, "/", -1);
  } else {
    httpd_send(conn->rsp, conn->req->decoded_url, urllen);
  }

  httpd_send(conn->rsp, "</H1><PRE>\r\n", -1);
  httpd_send(conn->rsp, "   Name                              Last Modified           Size\r\n", -1);
  httpd_send(conn->rsp, "<HR>\r\n", -1);

  if (urllen > 1) httpd_send(conn->rsp, "<IMG SRC=\"/icons/folder.gif\"> <A HREF=\"..\">..</A>\r\n", -1);

  while (_readdir(dir, &dirp, 1) > 0) {
    strcpy(path, conn->req->path_translated);
    strcat(path, "/");
    strcat(path, dirp.name);

    rc = stat64(path, &statbuf);
    if (rc < 0) return -1;

    tm = gmtime(&statbuf.st_mtime);
    if (!tm) return -1;

    if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
      httpd_send(conn->rsp, "<IMG SRC=\"/icons/folder.gif\"> ", -1);
    } else {
      httpd_send(conn->rsp, "<IMG SRC=\"/icons/file.gif\"> ", -1);
    }

    httpd_send(conn->rsp, "<A HREF=\"", -1);
    httpd_send(conn->rsp, dirp.name, dirp.namelen);
    if ((statbuf.st_mode & S_IFMT) == S_IFDIR) httpd_send(conn->rsp, "/", -1);
    httpd_send(conn->rsp, "\">", -1);

    httpd_send(conn->rsp, dirp.name, dirp.namelen);
    if ((statbuf.st_mode & S_IFMT) == S_IFDIR) httpd_send(conn->rsp, "/", -1);
    httpd_send(conn->rsp, "</A>", -1);
    if ((statbuf.st_mode & S_IFMT) != S_IFDIR) httpd_send(conn->rsp, " ", -1);

    if (dirp.namelen < 32) httpd_send(conn->rsp, "                                        ", 32 - dirp.namelen);

    strftime(buf, 32, "%d-%b-%Y %H:%M:%S", tm);
    httpd_send(conn->rsp, buf, -1);

    if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
      if (statbuf.st_size < 1024) {
        sprintf(buf, "%8d B", (int) statbuf.st_size);
      } else if (statbuf.st_size < 1024 * 1024) {
        sprintf(buf, "%8d KB", (int) (statbuf.st_size / 1024));
      } else if (statbuf.st_size < 1073741824i64) {
        sprintf(buf, "%8d MB", (int) (statbuf.st_size / (1024 * 1024)));
      } else {
        sprintf(buf, "%8d GB", (int) (statbuf.st_size / 1073741824i64));
      }

      httpd_send(conn->rsp, buf, -1);
    }

    httpd_send(conn->rsp, "\r\n", -1);
  }

  httpd_send(conn->rsp, "</PRE><HR>\r\n", -1);
  httpd_send(conn->rsp, "<ADDRESS>", -1);
  httpd_send(conn->rsp, conn->server->swname, -1);
  httpd_send(conn->rsp, "</ADDRESS>\r\n", -1);
  httpd_send(conn->rsp, "</BODY></HTML>\r\n", -1);
  
  close(dir);
  return 0;
}

int httpd_file_handler(struct httpd_connection *conn) {
  int rc;
  int fd;
  struct stat64 statbuf;
  char *filename;
  char buf[MAXPATH];

  if (strcmp(conn->req->method, "GET") != 0 && strcmp(conn->req->method, "HEAD") != 0) {
    return httpd_send_error(conn->rsp, 405, "Method Not Allowed", NULL);
  }

  filename = conn->req->path_translated;
  rc = stat64(filename, &statbuf);
  if (rc < 0) return httpd_return_file_error(conn, errno);

  if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
    int urllen = strlen(conn->req->decoded_url);

    if (urllen < 1 || urllen >= MAXPATH - 1) return 400;

    if (conn->req->decoded_url[urllen - 1] != '/') {
      strcpy(buf, conn->req->decoded_url);
      strcat(buf, "/");

      rc = httpd_redirect(conn->rsp, buf);
      if (rc < 0) return -1;

      return 0;
    }

    if (conn->server->indexname && *conn->server->indexname) {
      if (strlen(conn->req->path_translated) + strlen(conn->server->indexname) + 2 > MAXPATH) return 400;
      strcpy(buf, conn->req->path_translated);
      strcat(buf, "/");
      strcat(buf, conn->server->indexname);

      rc = stat64(buf, &statbuf);
      if (rc < 0) {
        if (conn->server->allowdirbrowse) {
          return ls(conn);
        } else {
          return httpd_send_error(conn->rsp, 403, "Forbidden", "Directory browsing now allowed");
        }
      } else {
        if ((statbuf.st_mode & S_IFMT) == S_IFDIR) return 500;
        filename = buf;
      }
    }
  }

  if ((statbuf.st_mode & S_IFMT) == S_IFREG) {
    conn->rsp->content_length = (int) statbuf.st_size;
    conn->rsp->last_modified = statbuf.st_mtime;
    conn->rsp->content_type = httpd_get_mimetype(conn->server, get_extension(filename));

    if (conn->rsp->last_modified <= conn->req->if_modified_since) {
      return httpd_send_header(conn->rsp, 304, "Not Modified", NULL);
    }
  } else {
    conn->rsp->content_type = "text/plain";
  }


  if (strcmp(conn->req->method, "HEAD") == 0) return 0;

  fd = open(filename, O_RDONLY | O_BINARY);
  if (fd < 0) return httpd_return_file_error(conn, errno);

  rc = httpd_send_file(conn->rsp, fd);
  if (rc < 0) return -1;

  return 0;
}

int httpd_resource_handler(struct httpd_connection *conn) {
  char *resdata;
  int reslen;
  int rc;

  if (strcmp(conn->req->method, "GET") != 0 && strcmp(conn->req->method, "HEAD") != 0) {
    return httpd_send_error(conn->rsp, 405, "Method Not Allowed", NULL);
  }

  resdata = getresdata(conn->req->context->hmod, 10, conn->req->pathinfo, 0, &reslen);
  if (!resdata) {
    return httpd_send_error(conn->rsp, 404, "Not Found", NULL);
  }

  conn->rsp->content_length = reslen;
  conn->rsp->last_modified = conn->req->context->mtime;
  conn->rsp->content_type = httpd_get_mimetype(conn->server, get_extension(conn->req->pathinfo));

  if (conn->rsp->last_modified <= conn->req->if_modified_since) {
    return httpd_send_header(conn->rsp, 304, "Not Modified", NULL);
  }

  if (strcmp(conn->req->method, "HEAD") == 0) return 0;

  rc = httpd_send_fixed_data(conn->rsp, resdata, reslen);
  if (rc < 0) return rc;

  return 0;
}