]> git.seodisparate.com - c_simple_http/commitdiff
Work on fetching static file and mime-type
authorStephen Seo <seo.disparate@gmail.com>
Tue, 29 Oct 2024 03:42:24 +0000 (12:42 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Tue, 29 Oct 2024 03:46:14 +0000 (12:46 +0900)
CMakeLists.txt
Makefile
src/static.c [new file with mode: 0644]
src/static.h [new file with mode: 0644]
src/test.c

index f20ee8d532cb96fa9f8b519a3d8f02af567a9135..03651120b1eeb9de60d9c60203b8481448a6a689 100644 (file)
@@ -12,6 +12,7 @@ set(c_simple_http_SOURCES
   "${CMAKE_CURRENT_SOURCE_DIR}/src/http_template.c"
   "${CMAKE_CURRENT_SOURCE_DIR}/src/helpers.c"
   "${CMAKE_CURRENT_SOURCE_DIR}/src/html_cache.c"
+  "${CMAKE_CURRENT_SOURCE_DIR}/src/static.c"
   "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/helpers.c"
   "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/linked_list.c"
   "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SimpleArchiver/src/data_structures/hash_map.c"
index a3ee88e0532f4b8369a94e02a2d374ba971e5f7d..f6c80a8b892c388c5a5c1cd3e6259e937b67b668 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -43,7 +43,8 @@ HEADERS = \
        src/config.h \
        src/http_template.h \
        src/helpers.h \
-       src/html_cache.h
+       src/html_cache.h \
+       src/static.h
 
 SOURCES = \
                src/main.c \
@@ -57,6 +58,7 @@ SOURCES = \
                src/http_template.c \
                src/helpers.c \
                src/html_cache.c \
+               src/static.c \
                third_party/SimpleArchiver/src/helpers.c \
                third_party/SimpleArchiver/src/data_structures/linked_list.c \
                third_party/SimpleArchiver/src/data_structures/hash_map.c \
diff --git a/src/static.c b/src/static.c
new file mode 100644 (file)
index 0000000..68f5259
--- /dev/null
@@ -0,0 +1,277 @@
+// ISC License
+// 
+// Copyright (c) 2024 Stephen Seo
+// 
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "static.h"
+
+// Standard library includes.
+#include <stdio.h>
+#include <stdlib.h>
+
+// Standard C library includes.
+#include <spawn.h>
+
+// Posix includes.
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <unistd.h>
+
+// Third party includes.
+#include "SimpleArchiver/src/helpers.h"
+
+char **environ;
+
+void internal_fd_cleanup_helper(int *fd) {
+  if (fd && *fd >= 0) {
+    close(*fd);
+    *fd = -1;
+  }
+}
+
+void internal_cleanup_file_actions(posix_spawn_file_actions_t **actions) {
+  if (actions && *actions) {
+    posix_spawn_file_actions_destroy(*actions);
+    free(*actions);
+    *actions = NULL;
+  }
+}
+
+void internal_cleanup_prev_cwd(char **path) {
+  if (path && *path) {
+    int ret = chdir(*path);
+    if (ret != 0) {
+      fprintf(stderr, "WARNING: chdir back to cwd failed! (errno %d)\n", errno);
+    }
+    free(*path);
+    *path = NULL;
+  }
+}
+
+int_fast8_t c_simple_http_is_xdg_mime_available(void) {
+  __attribute__((cleanup(internal_fd_cleanup_helper)))
+  int dev_null_fd = open("/dev/null", O_WRONLY);
+
+  __attribute__((cleanup(internal_cleanup_file_actions)))
+  posix_spawn_file_actions_t *actions =
+    malloc(sizeof(posix_spawn_file_actions_t));
+  int ret = posix_spawn_file_actions_init(actions);
+  if (ret != 0) {
+    free(actions);
+    actions = NULL;
+    return 0;
+  }
+
+  posix_spawn_file_actions_adddup2(actions, dev_null_fd, STDOUT_FILENO);
+  posix_spawn_file_actions_adddup2(actions, dev_null_fd, STDERR_FILENO);
+
+  pid_t pid;
+  ret = posix_spawnp(&pid,
+                     "xdg-mime",
+                     actions,
+                     NULL,
+                     (char *const[]){"xdg-mime", "--help", NULL},
+                     environ);
+  if (ret != 0) {
+    return 0;
+  }
+
+  waitpid(pid, &ret, 0);
+
+  return (ret == 0 ? 1 : 0);
+}
+
+void c_simple_http_cleanup_static_file_info(
+    C_SIMPLE_HTTP_StaticFileInfo *file_info) {
+  if (file_info->buf) {
+    free(file_info->buf);
+    file_info->buf = NULL;
+  }
+  file_info->buf_size = 0;
+  if (file_info->mime_type) {
+    free(file_info->mime_type);
+    file_info->mime_type = NULL;
+  }
+}
+
+C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(const char *static_dir,
+                                                    const char *path) {
+  C_SIMPLE_HTTP_StaticFileInfo file_info;
+  memset(&file_info, 0, sizeof(C_SIMPLE_HTTP_StaticFileInfo));
+
+  if (!static_dir || !path || !c_simple_http_is_xdg_mime_available()) {
+    return file_info;
+  }
+
+  uint64_t buf_size = 128;
+  char *buf = malloc(buf_size);
+  char *ptr;
+  while (1) {
+    ptr = getcwd(buf, buf_size);
+    if (ptr == NULL) {
+      if (errno == ERANGE) {
+        buf_size *= 2;
+        buf = realloc(buf, buf_size);
+        if (buf == NULL) {
+          return file_info;
+        }
+      } else {
+        free(buf);
+        return file_info;
+      }
+    } else {
+      break;
+    }
+  }
+
+  __attribute__((cleanup(internal_cleanup_prev_cwd)))
+  char *prev_cwd = buf;
+
+  int ret = chdir(static_dir);
+  if (ret != 0) {
+    fprintf(stderr,
+            "ERROR: Failed to chdir into \"%s\"! (errno %d)\n",
+            static_dir,
+            errno);
+    return file_info;
+  }
+
+  __attribute__((cleanup(simple_archiver_helper_cleanup_FILE)))
+  FILE *fd = NULL;
+  uint64_t idx = 0;
+  if (path[0] == '/') {
+    for(; path[idx] != 0; ++idx) {
+      if (path[idx] != '/') {
+        break;
+      }
+    }
+    if (path[idx] == 0) {
+      fprintf(stderr, "ERROR: Received invalid path \"%s\"!\n", path);
+      return file_info;
+    }
+  }
+  fd = fopen(path + idx, "rb");
+
+  if (fd == NULL) {
+    fprintf(stderr, "ERROR: Failed to open path \"%s\"!\n", path + idx);
+    return file_info;
+  }
+
+  fseek(fd, 0, SEEK_END);
+  long long_ret = ftell(fd);
+  if (long_ret < 0) {
+    fprintf(stderr, "ERROR: Failed to seek in path fd \"%s\"!\n", path);
+    return file_info;
+  }
+  fseek(fd, 0, SEEK_SET);
+  file_info.buf_size = (uint64_t)long_ret;
+  file_info.buf = malloc(file_info.buf_size);
+  size_t size_t_ret = fread(file_info.buf, 1, file_info.buf_size, fd);
+  if (size_t_ret != file_info.buf_size) {
+    fprintf(stderr, "ERROR: Failed to read path fd \"%s\"!\n", path);
+    free(file_info.buf);
+    file_info.buf_size = 0;
+    return file_info;
+  }
+
+  simple_archiver_helper_cleanup_FILE(&fd);
+
+  int from_xdg_mime_pipe[2];
+  ret = pipe(from_xdg_mime_pipe);
+
+  __attribute__((cleanup(internal_cleanup_file_actions)))
+  posix_spawn_file_actions_t *actions =
+    malloc(sizeof(posix_spawn_file_actions_t));
+  ret = posix_spawn_file_actions_init(actions);
+  if (ret != 0) {
+    free(actions);
+    actions = NULL;
+    c_simple_http_cleanup_static_file_info(&file_info);
+    close(from_xdg_mime_pipe[1]);
+    close(from_xdg_mime_pipe[0]);
+    return file_info;
+  }
+
+  posix_spawn_file_actions_adddup2(actions,
+                                   from_xdg_mime_pipe[1],
+                                   STDOUT_FILENO);
+
+  buf_size = 256;
+  buf = malloc(buf_size);
+  uint64_t buf_idx = 0;
+
+  char *path_plus_idx = (char*)path + idx;
+  pid_t pid;
+  ret = posix_spawnp(&pid,
+                     "xdg-mime",
+                     actions,
+                     NULL,
+                     (char *const[]){"xdg-mime",
+                                     "query",
+                                     "filetype",
+                                     path_plus_idx,
+                                     NULL},
+                     environ);
+  if (ret != 0) {
+    c_simple_http_cleanup_static_file_info(&file_info);
+    close(from_xdg_mime_pipe[1]);
+    close(from_xdg_mime_pipe[0]);
+    return file_info;
+  }
+
+  close(from_xdg_mime_pipe[1]);
+
+  ssize_t ssize_t_ret;
+  while (1) {
+    ssize_t_ret =
+      read(from_xdg_mime_pipe[0], buf + buf_idx, buf_size - buf_idx);
+    if (ssize_t_ret <= 0) {
+      break;
+    } else {
+      buf_idx += (uint64_t)ssize_t_ret;
+      if (buf_idx >= buf_size) {
+        buf_size *= 2;
+        buf = realloc(buf, buf_size);
+        if (buf == NULL) {
+          c_simple_http_cleanup_static_file_info(&file_info);
+          close(from_xdg_mime_pipe[0]);
+          return file_info;
+        }
+      }
+    }
+  }
+
+  close(from_xdg_mime_pipe[0]);
+  waitpid(pid, &ret, 0);
+
+  if (ret != 0) {
+    c_simple_http_cleanup_static_file_info(&file_info);
+    return file_info;
+  }
+
+  buf[buf_idx] = 0;
+  if (buf[buf_idx-1] == '\n') {
+    buf[buf_idx-1] = 0;
+  }
+
+  file_info.mime_type = buf;
+
+  return file_info;
+}
+
+// vim: et ts=2 sts=2 sw=2
diff --git a/src/static.h b/src/static.h
new file mode 100644 (file)
index 0000000..30379b0
--- /dev/null
@@ -0,0 +1,40 @@
+// ISC License
+// 
+// Copyright (c) 2024 Stephen Seo
+// 
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef SEODISPARATE_COM_C_SIMPLE_HTTP_STATIC_H_
+#define SEODISPARATE_COM_C_SIMPLE_HTTP_STATIC_H_
+
+// Standard library includes.
+#include <stdint.h>
+
+typedef struct C_SIMPLE_HTTP_StaticFileInfo {
+  char *buf;
+  uint64_t buf_size;
+  char *mime_type;
+} C_SIMPLE_HTTP_StaticFileInfo;
+
+/// Returns non-zero if "xdg_mime" is available.
+int_fast8_t c_simple_http_is_xdg_mime_available(void);
+
+void c_simple_http_cleanup_static_file_info(
+  C_SIMPLE_HTTP_StaticFileInfo *file_info);
+
+C_SIMPLE_HTTP_StaticFileInfo c_simple_http_get_file(
+  const char *static_dir, const char *path);
+
+#endif
+
+// vim: et ts=2 sts=2 sw=2
index d47079f4af053f22fce1a2fa145c452e40f97d24..fd7731c201773a644b6c991c5088d988920e74c7 100644 (file)
@@ -16,6 +16,7 @@
 #include "http.h"
 #include "html_cache.h"
 #include "constants.h"
+#include "static.h"
 
 // Third party includes.
 #include <SimpleArchiver/src/helpers.h>
@@ -107,7 +108,7 @@ int test_internal_check_matching_string_in_list(void *value, void *ud) {
   return 0;
 }
 
-int main(void) {
+int main(int argc, char **argv) {
   // Test config.
   {
     __attribute__((cleanup(test_internal_cleanup_delete_temporary_file)))
@@ -974,6 +975,26 @@ int main(void) {
     rmdir("/tmp/c_simple_http_cache_dir");
   }
 
+  // Test static.
+  {
+    FILE *fd = fopen("/usr/bin/xdg-mime", "rb");
+    int_fast8_t is_xdg_mime_exists = fd ? 1 : 0;
+    fclose(fd);
+
+    if (is_xdg_mime_exists) {
+      CHECK_TRUE(c_simple_http_is_xdg_mime_available());
+
+      C_SIMPLE_HTTP_StaticFileInfo info = c_simple_http_get_file(".", argv[0]);
+      CHECK_TRUE(info.buf);
+      CHECK_TRUE(info.buf_size > 0);
+      CHECK_TRUE(info.mime_type);
+      printf("unit test mime type is: %s\n", info.mime_type);
+      c_simple_http_cleanup_static_file_info(&info);
+    } else {
+      CHECK_FALSE(c_simple_http_is_xdg_mime_available());
+    }
+  }
+
   RETURN()
 }