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

#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 );
}