1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007 Christian Grothoff (and other contributing authors) 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20 /** 21 * @file fileserver_example.c 22 * @brief example for how to use libmicrohttpd to serve files (with directory support) 23 * @author Christian Grothoff 24 */ 25 26 #include "platform.h" 27 #include <dirent.h> 28 #include <microhttpd.h> 29 #include <unistd.h> 30 31 #define PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>" 32 33 static ssize_t 34 file_reader (void *cls, uint64_t pos, char *buf, size_t max) 35 { 36 FILE *file = cls; 37 38 (void) fseek (file, pos, SEEK_SET); 39 return fread (buf, 1, max, file); 40 } 41 42 static void 43 file_free_callback (void *cls) 44 { 45 FILE *file = cls; 46 fclose (file); 47 } 48 49 static void 50 dir_free_callback (void *cls) 51 { 52 DIR *dir = cls; 53 if (dir != NULL) 54 closedir (dir); 55 } 56 57 static ssize_t 58 dir_reader (void *cls, uint64_t pos, char *buf, size_t max) 59 { 60 DIR *dir = cls; 61 struct dirent *e; 62 63 if (max < 512) 64 return 0; 65 do 66 { 67 e = readdir (dir); 68 if (e == NULL) 69 return MHD_CONTENT_READER_END_OF_STREAM; 70 } while (e->d_name[0] == '.'); 71 return snprintf (buf, max, 72 "<a href=\"/%s\">%s</a><br>", 73 e->d_name, 74 e->d_name); 75 } 76 77 78 static int 79 ahc_echo (void *cls, 80 struct MHD_Connection *connection, 81 const char *url, 82 const char *method, 83 const char *version, 84 const char *upload_data, 85 size_t *upload_data_size, void **ptr) 86 { 87 static int aptr; 88 struct MHD_Response *response; 89 int ret; 90 FILE *file; 91 DIR *dir; 92 struct stat buf; 93 char emsg[1024]; 94 95 if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) 96 return MHD_NO; /* unexpected method */ 97 if (&aptr != *ptr) 98 { 99 /* do never respond on first call */ 100 *ptr = &aptr; 101 return MHD_YES; 102 } 103 *ptr = NULL; /* reset when done */ 104 if ( (0 == stat (&url[1], &buf)) && 105 (S_ISREG (buf.st_mode)) ) 106 file = fopen (&url[1], "rb"); 107 else 108 file = NULL; 109 if (file == NULL) 110 { 111 dir = opendir ("."); 112 if (dir == NULL) 113 { 114 /* most likely cause: more concurrent requests than 115 available file descriptors / 2 */ 116 snprintf (emsg, 117 sizeof (emsg), 118 "Failed to open directory `.': %s\n", 119 strerror (errno)); 120 response = MHD_create_response_from_buffer (strlen (emsg), 121 emsg, 122 MHD_RESPMEM_MUST_COPY); 123 if (response == NULL) 124 return MHD_NO; 125 ret = MHD_queue_response (connection, MHD_HTTP_SERVICE_UNAVAILABLE, response); 126 MHD_destroy_response (response); 127 } 128 else 129 { 130 response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 131 32 * 1024, 132 &dir_reader, 133 dir, 134 &dir_free_callback); 135 if (response == NULL) 136 { 137 closedir (dir); 138 return MHD_NO; 139 } 140 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 141 MHD_destroy_response (response); 142 } 143 } 144 else 145 { 146 response = MHD_create_response_from_callback (buf.st_size, 32 * 1024, /* 32k page size */ 147 &file_reader, 148 file, 149 &file_free_callback); 150 if (response == NULL) 151 { 152 fclose (file); 153 return MHD_NO; 154 } 155 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 156 MHD_destroy_response (response); 157 } 158 return ret; 159 } 160 161 int 162 main (int argc, char *const *argv) 163 { 164 struct MHD_Daemon *d; 165 166 if (argc != 2) 167 { 168 printf ("%s PORT\n", argv[0]); 169 return 1; 170 } 171 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, 172 atoi (argv[1]), 173 NULL, NULL, &ahc_echo, PAGE, MHD_OPTION_END); 174 if (d == NULL) 175 return 1; 176 (void) getc (stdin); 177 MHD_stop_daemon (d); 178 return 0; 179 } 180