cSvn-UI for SVN Repositories

cGit-UI – is a web interface for Subversion (SVN) Repositories. cSvn CGI script is writen in C and therefore it's fast enough

6 Commits   0 Branches   2 Tags
author: kx <kx@radix.pro> 2023-03-24 03:55:33 +0300 committer: kx <kx@radix.pro> 2023-03-24 03:55:33 +0300 commit: bfc1508d26c89c9a36d2d9a827fe2c4ed128884d parent: c836ae3775cf72f17e0b7e3792d156fdb389bee3
Commit Summary:
Version 0.1.4
Diffstat:
1 file changed, 256 insertions, 0 deletions
diff --git a/csvncgi/http.c b/csvncgi/http.c
new file mode 100644
index 0000000..18828e2
--- /dev/null
+++ b/csvncgi/http.c
@@ -0,0 +1,298 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysinfo.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <sys/stat.h> /* chmod(2)    */
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>   /* strdup(3)   */
+#include <libgen.h>   /* basename(3) */
+#include <ctype.h>    /* tolower(3)  */
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <defs.h>
+#include <fatal.h>
+#include <http.h>
+#include <html.h>
+#include <strbuf.h>
+
+
+const signed char hexval_table[256] = {
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 00-07 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 08-0f */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 10-17 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 18-1f */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 20-27 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 28-2f */
+   0,  1,  2,  3,  4,  5,  6,  7,  /* 30-37 */
+   8,  9, -1, -1, -1, -1, -1, -1,  /* 38-3f */
+  -1, 10, 11, 12, 13, 14, 15, -1,  /* 40-47 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 48-4f */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 50-57 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 58-5f */
+  -1, 10, 11, 12, 13, 14, 15, -1,  /* 60-67 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 68-67 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 70-77 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 78-7f */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 80-87 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 88-8f */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 90-97 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* 98-9f */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* a0-a7 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* a8-af */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* b0-b7 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* b8-bf */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* c0-c7 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* c8-cf */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* d0-d7 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* d8-df */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* e0-e7 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* e8-ef */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* f0-f7 */
+  -1, -1, -1, -1, -1, -1, -1, -1,  /* f8-ff */
+};
+
+
+static char *
+url_decode_internal( const char **query, int len,
+                     const char *stop_at, struct strbuf *out,
+                     int decode_plus )
+{
+  const char *q = *query;
+
+  while( len )
+  {
+    unsigned char c = *q;
+
+    if( !c )
+      break;
+    if( stop_at && strchr( stop_at, c ) )
+    {
+      ++q;
+      --len;
+      break;
+    }
+
+    if( c == '%' && (len < 0 || len >= 3) )
+    {
+      int val = hex2chr( q + 1 );
+      if( 0 < val )
+      {
+        strbuf_addch( out, val );
+        q += 3;
+        len -= 3;
+        continue;
+      }
+    }
+
+    if( decode_plus && c == '+' )
+      strbuf_addch( out, ' ' );
+    else
+      strbuf_addch( out, c );
+    ++q;
+    --len;
+  }
+  *query = q;
+
+  return strbuf_detach( out, NULL );
+}
+
+char *url_decode_mem( const char *url, int len )
+{
+  struct strbuf out = STRBUF_INIT;
+  const char *colon = memchr( url, ':', len );
+
+  /* Skip protocol part if present */
+  if( colon && url < colon )
+  {
+    strbuf_add( &out, url, colon - url );
+    len -= colon - url;
+    url = colon;
+  }
+  return url_decode_internal( &url, len, NULL, &out, 0 );
+}
+
+char *url_percent_decode( const char *encoded )
+{
+  struct strbuf out = STRBUF_INIT;
+  return url_decode_internal( &encoded, strlen(encoded), NULL, &out, 0 );
+}
+
+char *url_decode_parameter_name( const char **query )
+{
+  struct strbuf out = STRBUF_INIT;
+  return url_decode_internal( query, -1, "&=", &out, 1 );
+}
+
+char *url_decode_parameter_value( const char **query )
+{
+  struct strbuf out = STRBUF_INIT;
+  return url_decode_internal( query, -1, "&", &out, 1 );
+}
+
+void http_parse_querystring( const char *txt, void (*fn)(const char *name, const char *value) )
+{
+  const char *t = txt;
+
+  while( t && *t )
+  {
+    char *name = url_decode_parameter_name( &t );
+    if( *name )
+    {
+      char *value = url_decode_parameter_value( &t );
+      fn( name, value );
+      free( value );
+    }
+    free( name );
+  }
+}
+
+
+
+#define HTTP_ERRMSG_SIZE 4096
+
+void http_error( const char *fmt, ... )
+{
+  va_list arg_ptr;
+  char  buf[HTTP_ERRMSG_SIZE];
+  char  msg[HTTP_ERRMSG_SIZE];
+  char *format = "%s: %s\n";
+
+  va_start( arg_ptr, fmt );
+
+  vsnprintf( msg, HTTP_ERRMSG_SIZE, (const void *)fmt, arg_ptr );
+
+  va_end( arg_ptr ); /* Reset variable arguments. */
+
+  snprintf( buf, HTTP_ERRMSG_SIZE, format, "http", msg );
+
+  (void)write( STDERR_FILENO, buf, strlen( buf ) );
+
+  exit( 1 );
+}
+
+http_errfunc http_fatal = http_error;
+
+
+static struct
+{
+  int         code;
+  const char *desc;
+}
+status[] =
+{
+  { 100, "Continue" },
+  { 101, "Switching Protocols" },
+  { 200, "OK" },
+  { 201, "Created" },
+  { 202, "Accepted" },
+  { 203, "Non-Authoritative Information" },
+  { 204, "No Content" },
+  { 205, "Reset Content" },
+  { 206, "Partial Content" },
+  { 300, "Multiple Choices" },
+  { 301, "Moved Permanently" },
+  { 302, "Found" },
+  { 303, "See Other" },
+  { 304, "Not Modified" },
+  { 305, "Use Proxy" },
+  { 307, "Temporary Redirect" },
+  { 400, "Bad Request" },
+  { 401, "Unauthorized" },
+  { 402, "Payment Required" },
+  { 403, "Forbidden" },
+  { 404, "Not Found" },
+  { 405, "Method Not Allowed" },
+  { 406, "Not Acceptable" },
+  { 407, "Proxy Authentication Required" },
+  { 408, "Request Timeout" },
+  { 409, "Conflict" },
+  { 410, "Gone" },
+  { 411, "Length Required" },
+  { 412, "Precondition Failed" },
+  { 413, "Request Entity Too Large" },
+  { 414, "Request-URI Too Long" },
+  { 415, "Unsupported Media Type" },
+  { 416, "Requested Range Not Satisfiable" },
+  { 417, "Expectation Failed" },
+  { 418, "I'm a teapot" },
+  { 500, "Internal Server Error" },
+  { 501, "Not Implemented" },
+  { 502, "Bad Gateway" },
+  { 503, "Service Unavailable" },
+  { 504, "Gateway Timeout" },
+  { 505, "HTTP Version Not Supported" },
+  {   0, NULL }
+};
+
+const char *http_status( int status_code )
+{
+  int i = 0, code;
+
+  while( (code = status[i].code) )
+  {
+    if( code == status_code ) return status[i].desc;
+    ++i;
+  }
+  return NULL;
+}
+
+char *fmt( const char *format, ... )
+{
+  static char buf[8][1024];
+  static int bufidx;
+  int len;
+  va_list args;
+
+  bufidx++;
+  bufidx &= 7;
+
+  va_start( args, format );
+  len = vsnprintf( buf[bufidx], sizeof(buf[bufidx]), format, args );
+  va_end(args);
+  if( len > sizeof(buf[bufidx]) ) {
+    http_fatal( "string truncated: %s", format );
+  }
+  return buf[bufidx];
+}
+
+char *fmtalloc( const char *format, ... )
+{
+  struct strbuf sb = STRBUF_INIT;
+  va_list args;
+
+  va_start( args, format );
+  strbuf_vaddf( &sb, format, args );
+  va_end( args );
+
+  return strbuf_detach( &sb, NULL );
+}
+
+
+char *http_date( time_t t )
+{
+  static char day[][4] =
+    { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+  static char month[][4] =
+    { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+  struct tm *tm = gmtime(&t);
+  return fmt( "%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
+              tm->tm_mday, month[tm->tm_mon], 1900 + tm->tm_year,
+              tm->tm_hour, tm->tm_min, tm->tm_sec );
+}