gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r26703 - in libmicrohttpd: . src/examples


From: gnunet
Subject: [GNUnet-SVN] r26703 - in libmicrohttpd: . src/examples
Date: Sun, 31 Mar 2013 22:22:02 +0200

Author: grothoff
Date: 2013-03-31 22:22:02 +0200 (Sun, 31 Mar 2013)
New Revision: 26703

Modified:
   libmicrohttpd/configure.ac
   libmicrohttpd/src/examples/Makefile.am
   libmicrohttpd/src/examples/demo.c
Log:
add mime types to demo

Modified: libmicrohttpd/configure.ac
===================================================================
--- libmicrohttpd/configure.ac  2013-03-31 20:03:26 UTC (rev 26702)
+++ libmicrohttpd/configure.ac  2013-03-31 20:22:02 UTC (rev 26703)
@@ -255,6 +255,14 @@
   AC_DEFINE_UNQUOTED([MHD_REQ_CURL_GNUTLS_VERSION], 
"$MHD_REQ_CURL_GNUTLS_VERSION", [gnuTLS lib version - used in conjunction with 
cURL])
   AC_DEFINE_UNQUOTED([MHD_REQ_CURL_NSS_VERSION], "$MHD_REQ_CURL_NSS_VERSION", 
[NSS lib version - used in conjunction with cURL])
 fi
+
+AC_MSG_CHECKING(for magic_open -lmagic)
+AC_CHECK_LIB(magic, magic_open,
+  [AC_CHECK_HEADERS([magic.h],
+   AM_CONDITIONAL(HAVE_MAGIC, true),
+   AM_CONDITIONAL(HAVE_MAGIC, false))],
+  AM_CONDITIONAL(HAVE_MAGIC, false))
+
 LIBS=$SAVE_LIBS
 AM_CONDITIONAL(HAVE_CURL, test x$curl = x1)
 

Modified: libmicrohttpd/src/examples/Makefile.am
===================================================================
--- libmicrohttpd/src/examples/Makefile.am      2013-03-31 20:03:26 UTC (rev 
26702)
+++ libmicrohttpd/src/examples/Makefile.am      2013-03-31 20:22:02 UTC (rev 
26703)
@@ -29,9 +29,12 @@
 endif
 if HAVE_POSTPROCESSOR
 noinst_PROGRAMS += \
-  demo \
   post_example 
+if HAVE_MAGIC
+noinst_PROGRAMS += \
+  demo 
 endif
+endif
 
 if ENABLE_DAUTH
 noinst_PROGRAMS += \
@@ -55,7 +58,8 @@
 demo_SOURCES = \
  demo.c 
 demo_LDADD = \
- $(top_builddir)/src/daemon/libmicrohttpd.la 
+ $(top_builddir)/src/daemon/libmicrohttpd.la  \
+ -lmagic
 
 dual_stack_example_SOURCES = \
  dual_stack_example.c 

Modified: libmicrohttpd/src/examples/demo.c
===================================================================
--- libmicrohttpd/src/examples/demo.c   2013-03-31 20:03:26 UTC (rev 26702)
+++ libmicrohttpd/src/examples/demo.c   2013-03-31 20:22:02 UTC (rev 26703)
@@ -23,17 +23,23 @@
  * @author Christian Grothoff
  *
  * TODO:
- * - even with LARGE memory pool & buffers, uploads proceed at 5000 bytes/call 
(ugh)
  * - should have a slightly more ambitious upload form & file listing 
(structure!)
- * - may want to add MIME-types to replies
  */
 #include "platform.h"
 #include <microhttpd.h>
 #include <unistd.h>
 #include <pthread.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <dirent.h>
+#include <magic.h>
 
+/**
+ * How many bytes of a file do we give to libmagic to determine the mime type?
+ * 16k might be a bit excessive, but ought not hurt performance much anyway,
+ * and should definitively be on the safe side.
+ */
+#define MAGIC_HEADER_SIZE (16 * 1024)
 
 /**
  * Page returned for file-not-found.
@@ -48,6 +54,12 @@
 
 
 /**
+ * Page returned for refused requests.
+ */
+#define REQUEST_REFUSED_PAGE "<html><head><title>Request 
refused</title></head><body>Request refused (file exists?)</body></html>"
+
+
+/**
  * Head of index page.
  */
 #define INDEX_PAGE_HEADER 
"<html>\n<head><title>Welcome</title></head>\n<body>\n"\
@@ -63,6 +75,7 @@
 #define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>"
 
 
+
 /**
  * Response returned if the requested file does not exist (or is not 
accessible).
  */
@@ -79,12 +92,36 @@
 static struct MHD_Response *cached_directory_response;
 
 /**
+ * Response returned for refused uploads.
+ */
+static struct MHD_Response *request_refused_response;
+
+/**
  * Mutex used when we update the cached directory response object.
  */
 static pthread_mutex_t mutex;
 
+/**
+ * Global handle to MAGIC data.
+ */
+static magic_t magic;
 
+
 /**
+ * Mark the given response as HTML for the brower.
+ *
+ * @param response response to mark
+ */
+static void
+mark_as_html (struct MHD_Response *response)
+{
+  (void) MHD_add_response_header (response,
+                                 MHD_HTTP_HEADER_CONTENT_TYPE,
+                                 "text/html");
+}
+
+
+/**
  * Replace the existing 'cached_directory_response' with the
  * given response.
  *
@@ -102,68 +139,119 @@
 
 
 /**
- * Re-scan our local directory and re-build the index.
+ * Context keeping the data for the response we're building.
  */
-static void
-update_directory ()
+struct ResponseDataContext
 {
-  static size_t initial_allocation = 32 * 1024; /* initial size for response 
buffer */
-  DIR *dir;
-  struct dirent *de;
-  struct MHD_Response *response;
+  /**
+   * Response data string.
+   */
   char *buf;
+  
+  /**
+   * Number of bytes allocated for 'buf'.
+   */
   size_t buf_len;
+
+  /**
+   * Current position where we append to 'buf'. Must be smaller or equal to 
'buf_len'.
+   */
   size_t off;
 
-  dir = opendir (".");
-  if (NULL == dir)
-    goto err;
-  buf_len = initial_allocation; 
-  buf = malloc (buf_len);
-  if (NULL == buf)
-    {
-      closedir (dir);
-      goto err;
-    }
-  off = snprintf (buf, buf_len,
-                 "%s",
-                 INDEX_PAGE_HEADER);
+};
+
+
+/**
+ * Create a listing of the files in 'dirname' in HTML.
+ *
+ * @param rdc where to store the list of files
+ * @param dirname name of the directory to list
+ * @return MHD_YES on success, MHD_NO on error
+ */
+static int
+list_directory (struct ResponseDataContext *rdc,
+               const char *dirname)
+{
+  char fullname[PATH_MAX];
+  struct stat sbuf;
+  DIR *dir;
+  struct dirent *de;
+
+  if (NULL == (dir = opendir (dirname)))
+    return MHD_NO;      
   while (NULL != (de = readdir (dir)))
     {
       if ('.' == de->d_name[0])
        continue;
-      if (off + 1024 > buf_len)
+      if (sizeof (fullname) <= 
+         snprintf (fullname, sizeof (fullname),
+                   "%s/%s",
+                   dirname, de->d_name))
+       continue; /* ugh, file too long? how can this be!? */
+      if (0 != stat (fullname, &sbuf))
+       continue; /* ugh, failed to 'stat' */
+      if (! S_ISREG (sbuf.st_mode))
+       continue; /* not a regular file, skip */
+      if (rdc->off + 1024 > rdc->buf_len)
        {
          void *r;
 
-         if ( (2 * buf_len + 1024) < buf_len)
+         if ( (2 * rdc->buf_len + 1024) < rdc->buf_len)
            break; /* more than SIZE_T _index_ size? Too big for us */
-         buf_len = 2 * buf_len + 1024;
-         if (NULL == (r = realloc (buf, buf_len)))
+         rdc->buf_len = 2 * rdc->buf_len + 1024;
+         if (NULL == (r = realloc (rdc->buf, rdc->buf_len)))
            break; /* out of memory */
-         buf = r;
+         rdc->buf = r;
        }
-      off += snprintf (&buf[off], buf_len - off,
-                      "<li><a href=\"/%s\">%s</a></li>\n",
-                      de->d_name,
-                      de->d_name);
+      rdc->off += snprintf (&rdc->buf[rdc->off], 
+                           rdc->buf_len - rdc->off,
+                           "<li><a href=\"/%s\">%s</a></li>\n",
+                           de->d_name,
+                           de->d_name);
     }
+  (void) closedir (dir);
+  return MHD_YES;
+}
+
+
+/**
+ * Re-scan our local directory and re-build the index.
+ */
+static void
+update_directory ()
+{
+  static size_t initial_allocation = 32 * 1024; /* initial size for response 
buffer */
+  struct MHD_Response *response;
+  struct ResponseDataContext rdc;
+
+  rdc.buf_len = initial_allocation; 
+  if (NULL == (rdc.buf = malloc (rdc.buf_len)))
+    {
+      update_cached_response (NULL);
+      return; 
+    }
+  rdc.off = snprintf (rdc.buf, rdc.buf_len,
+                     "%s",
+                     INDEX_PAGE_HEADER);
+
+  if (MHD_NO == list_directory (&rdc, "."))
+    {
+      free (rdc.buf);
+      update_cached_response (NULL);
+      return;
+    }
   /* we ensured always +1k room, filenames are ~256 bytes,
      so there is always still enough space for the footer 
      without need for a final reallocation check. */
-  off += snprintf (&buf[off], buf_len - off,
-                  "%s",
-                  INDEX_PAGE_FOOTER);
-  closedir (dir);
-  initial_allocation = buf_len; /* remember for next time */
-  response = MHD_create_response_from_buffer (off,
-                                             buf,
+  rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
+                      "%s",
+                      INDEX_PAGE_FOOTER);
+  initial_allocation = rdc.buf_len; /* remember for next time */
+  response = MHD_create_response_from_buffer (rdc.off,
+                                             rdc.buf,
                                              MHD_RESPMEM_MUST_FREE);
+  mark_as_html (response);
   update_cached_response (response);
-  return;
- err:
-  /* failed to list directory, use error page */
-  update_cached_response (NULL);
 }
 
 
@@ -178,6 +266,11 @@
   int fd;
 
   /**
+   * Name of the file on disk (used to remove on errors).
+   */
+  char *filename;
+
+  /**
    * Post processor we're using to process the upload.
    */
   struct MHD_PostProcessor *pp;
@@ -186,6 +279,11 @@
    * Handle to connection that we're processing the upload for.
    */
   struct MHD_Connection *connection;
+
+  /**
+   * Response to generate, NULL to use directory.
+   */
+  struct MHD_Response *response;
 };
 
 
@@ -228,32 +326,48 @@
     }
   if (-1 == uc->fd)
     {
+      if ( (NULL != strstr (filename, "..")) ||
+          (NULL != strchr (filename, '/')) ||
+          (NULL != strchr (filename, '\\')) )
+       {
+         uc->response = request_refused_response;
+         return MHD_NO;
+       }
       uc->fd = open (filename, 
                     O_CREAT | O_EXCL 
 #if O_LARGEFILE
                     | O_LARGEFILE
 #endif
-#if O_NONBLOCK
-                    | O_NONBLOCK
-#endif
                     | O_WRONLY,
                     S_IRUSR | S_IWUSR);
       if (-1 == uc->fd)
        {
-         // FIXME: generate error page NICELY
-         fprintf (stderr, "Error opening file to write!\n");
+         fprintf (stderr, 
+                  "Error opening file `%s' for upload: %s\n",
+                  filename,
+                  strerror (errno));
+         uc->response = request_refused_response;
          return MHD_NO;
        }      
     }
-  else if (0 == size)
-    sleep (1);
-
-
+  uc->filename = strdup (filename);
   if ( (0 != size) &&
        (size != write (uc->fd, data, size)) )    
     {
-      // FIXME: generate error page NICELY
-      fprintf (stderr, "Error writing to disk!\n");
+      /* write failed; likely: disk full */
+      fprintf (stderr, 
+              "Error writing to file `%s': %s\n",
+              filename,
+              strerror (errno));
+      uc->response = internal_error_response;
+      close (uc->fd);
+      uc->fd = -1;
+      if (NULL != uc->filename)
+       {
+         unlink (uc->filename);
+         free (uc->filename);
+         uc->filename = NULL;
+       }
       return MHD_NO; 
     }
   return MHD_YES;
@@ -288,10 +402,17 @@
     }
   if (-1 != uc->fd)
   {
-    close (uc->fd);
-    fprintf (stderr, "Possible upload failure, need to remove file?!\n");
-    /* FIXME: unlink here on error!? */
+    (void) close (uc->fd);
+    if (NULL != uc->filename)
+      {
+       fprintf (stderr, 
+                "Upload of file `%s' failed (incomplete or aborted), removing 
file.\n",
+                uc->filename);
+       (void) unlink (uc->filename);
+      }
   }
+  if (NULL != uc->filename)    
+    free (uc->filename);
   free (uc);
 }
 
@@ -303,7 +424,7 @@
  * @return MHD_YES on success, MHD_NO on error
  */
 static int
-list_directory (struct MHD_Connection *connection)
+return_directory_response (struct MHD_Connection *connection)
 {
   int ret;
 
@@ -321,7 +442,6 @@
 }
 
 
-
 /**
  * Main callback from MHD, used to generate the page.
  *
@@ -352,6 +472,10 @@
   if (0 != strcmp (url, "/"))
     {
       /* should be file download */
+      char file_data[MAGIC_HEADER_SIZE];
+      ssize_t got;
+      const char *mime;
+
       if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
        return MHD_NO;  /* unexpected method (we're not polite...) */
       if ( (0 == stat (&url[1], &buf)) &&
@@ -364,6 +488,14 @@
        return MHD_queue_response (connection, 
                                   MHD_HTTP_NOT_FOUND, 
                                   file_not_found_response);
+      /* read beginning of the file to determine mime type  */
+      got = read (fd, file_data, sizeof (file_data));
+      if (-1 != got)
+       mime = magic_buffer (magic, file_data, got);
+      else
+       mime = NULL;
+      (void) lseek (fd, 0, SEEK_SET);
+
       if (NULL == (response = MHD_create_response_from_fd (buf.st_size, 
                                                           fd)))
        {
@@ -371,6 +503,12 @@
          (void) close (fd);
          return MHD_NO; 
        }
+
+      /* add mime type if we had one */
+      if (NULL != mime)
+       (void) MHD_add_response_header (response,
+                                       MHD_HTTP_HEADER_CONTENT_TYPE,
+                                       mime);
       ret = MHD_queue_response (connection, 
                                MHD_HTTP_OK, 
                                response);
@@ -387,6 +525,8 @@
        {
          if (NULL == (uc = malloc (sizeof (struct UploadContext))))
            return MHD_NO; /* out of memory, close connection */
+         uc->response = NULL;
+         uc->filename = NULL;
           uc->fd = -1;
          uc->connection = connection;
          uc->pp = MHD_create_post_processor (connection,
@@ -400,14 +540,15 @@
            }
          *ptr = uc;
          return MHD_YES;
-       }
+       }     
       if (0 != *upload_data_size)
        {
-         ret = MHD_post_process (uc->pp, 
-                                 upload_data,
-                                 *upload_data_size);
+         if (NULL != uc->response)
+           (void) MHD_post_process (uc->pp, 
+                                    upload_data,
+                                    *upload_data_size);
          *upload_data_size = 0;
-         return ret;
+         return MHD_YES;
        }
       /* end of upload, finish it! */
       MHD_destroy_post_processor (uc->pp);
@@ -417,22 +558,32 @@
          close (uc->fd);
          uc->fd = -1;
        }
-      update_directory ();
-      return list_directory (connection);
+      if (NULL != uc->response)
+       {
+         return MHD_queue_response (connection, 
+                                    MHD_HTTP_FORBIDDEN, 
+                                    uc->response);
+       }
+      else
+       {
+         update_directory ();
+         return return_directory_response (connection);
+       }
     }
   if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
-    return list_directory (connection);
+    return return_directory_response (connection);
 
   /* unexpected request, refuse */
-  fprintf (stderr, "Unexpected request, refusing\n");
-  return MHD_NO;
+  return MHD_queue_response (connection, 
+                            MHD_HTTP_FORBIDDEN, 
+                            request_refused_response);
 }
 
 
 /**
  * Entry point to demo.  Note: this HTTP server will make all
  * files in the current directory and its subdirectories available
- * to anyone.
+ * to anyone.  Press ENTER to stop the server once it has started.
  *
  * @param argc number of arguments in argv
  * @param argv first and only argument should be the port number
@@ -452,13 +603,22 @@
               "%s PORT\n", argv[0]);
       return 1;
     }
+  magic = magic_open (MAGIC_MIME_TYPE);
+  (void) magic_load (magic, NULL);
+
   (void) pthread_mutex_init (&mutex, NULL);
   file_not_found_response = MHD_create_response_from_buffer (strlen 
(FILE_NOT_FOUND_PAGE),
                                                             (void *) 
FILE_NOT_FOUND_PAGE,
                                                             
MHD_RESPMEM_PERSISTENT);
+  mark_as_html (file_not_found_response);
+  request_refused_response = MHD_create_response_from_buffer (strlen 
(REQUEST_REFUSED_PAGE),
+                                                            (void *) 
REQUEST_REFUSED_PAGE,
+                                                            
MHD_RESPMEM_PERSISTENT);
+  mark_as_html (request_refused_response);
   internal_error_response = MHD_create_response_from_buffer (strlen 
(INTERNAL_ERROR_PAGE),
                                                             (void *) 
INTERNAL_ERROR_PAGE,
                                                             
MHD_RESPMEM_PERSISTENT);
+  mark_as_html (internal_error_response);
   update_directory ();
   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
                         port,
@@ -470,12 +630,15 @@
                        MHD_OPTION_END);
   if (NULL == d)
     return 1;
+  fprintf (stderr, "HTTP server running. Press ENTER to stop the server\n");
   (void) getc (stdin);
   MHD_stop_daemon (d);
   MHD_destroy_response (file_not_found_response);
+  MHD_destroy_response (request_refused_response);
   MHD_destroy_response (internal_error_response);
   update_cached_response (NULL);
   (void) pthread_mutex_destroy (&mutex);
+  magic_close (magic);
   return 0;
 }
 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]