Index: auto.def
==================================================================
--- auto.def
+++ auto.def
@@ -9,10 +9,11 @@
     with-tcl:path        => {Enable Tcl integration, with Tcl in the specified path}
     internal-sqlite=1    => {Don't use the internal sqlite, use the system one}
     static=0             => {Link a static executable}
     lineedit=1           => {Disable line editing}
     fossil-debug=0       => {Build with fossil debugging enabled}
+    ipv6=1		 => {Disable IPv6 support}
     json=0        => {Build with fossil JSON API enabled}
 }
 
 # sqlite wants these types if possible
 cc-with {-includes {stdint.h inttypes.h}} {
@@ -72,10 +73,19 @@
 
 if {[opt-bool static]} {
     # XXX: This will not work on all systems.
     define-append EXTRA_LDFLAGS -static
 }
+
+if {[opt-bool ipv6]} {
+   define-append EXTRA_CFLAGS -DWITH_IPV6
+   msg-result "IPv6 support enabled"
+   if {[cc-check-functions getaddrinfo]} {
+      define-append EXTRA_CFLAGS -DHAVE_GETADDRINFO
+      msg-result "getaddrinfo() enabled"
+   }
+}
 
 
 # Check for zlib, using the given location if specified
 set zlibpath [opt-val with-zlib]
 if {$zlibpath ne ""} {

Index: src/cgi.c
==================================================================
--- src/cgi.c
+++ src/cgi.c
@@ -34,10 +34,11 @@
 # include <arpa/inet.h>
 # include <sys/times.h>
 # include <sys/time.h>
 # include <sys/wait.h>
 # include <sys/select.h>
+# include <netdb.h>             /* for NI_NUMERICHOST */
 #endif
 #ifdef __EMX__
   typedef int socklen_t;
 #endif
 #include <time.h>
@@ -1116,12 +1117,12 @@
 ** and subsequent code handles the actual generation of the webpage.
 */
 void cgi_handle_http_request(const char *zIpAddr){
   char *z, *zToken;
   int i;
-  struct sockaddr_in remoteName;
-  socklen_t size = sizeof(struct sockaddr_in);
+  struct sockaddr_storage remoteName;
+  socklen_t size = sizeof(remoteName);
   char zLine[2000];     /* A single line of input. */
   g.fullHttpReply = 1;
   if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
     malformed_request();
   }
@@ -1142,20 +1143,10 @@
   cgi_setenv("REQUEST_URI", zToken);
   for(i=0; zToken[i] && zToken[i]!='?'; i++){}
   if( zToken[i] ) zToken[i++] = 0;
   cgi_setenv("PATH_INFO", zToken);
   cgi_setenv("QUERY_STRING", &zToken[i]);
-  if( zIpAddr==0 &&
-        getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, 
-                                &size)>=0
-  ){
-    zIpAddr = inet_ntoa(remoteName.sin_addr);
-  }
-  if( zIpAddr ){   
-    cgi_setenv("REMOTE_ADDR", zIpAddr);
-    g.zIpAddr = mprintf("%s", zIpAddr);
-  }
  
   /* Get all the optional fields that follow the first line.
   */
   while( fgets(zLine,sizeof(zLine),g.httpIn) ){
     char *zFieldName;
@@ -1182,18 +1173,53 @@
       cgi_setenv("HTTP_HOST", zVal);
     }else if( fossil_strcmp(zFieldName,"if-none-match:")==0 ){
       cgi_setenv("HTTP_IF_NONE_MATCH", zVal);
     }else if( fossil_strcmp(zFieldName,"if-modified-since:")==0 ){
       cgi_setenv("HTTP_IF_MODIFIED_SINCE", zVal);
+    }else if( fossil_strcmp(zFieldName,"x-forwarded-for:")==0 ){
+      char* p = zVal;
+      /*
+      ** x-forwarded-for header is a list of comma-separated addresses, 
+      ** with leftmost address corresponding to the client
+      */
+      while(*p && *p != ',') p++;
+      *p = '\0';
+      zIpAddr = mprintf( "%s", zVal );
 #if 0
     }else if( fossil_strcmp(zFieldName,"referer:")==0 ){
       cgi_setenv("HTTP_REFERER", zVal);
 #endif
     }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
       cgi_setenv("HTTP_USER_AGENT", zVal);
     }
   }
+
+  if( zIpAddr==0 &&
+      getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName, 
+                                &size)>=0
+  ){
+    sa_family_t family;
+    int v4mapped=0;
+    if( remoteName.ss_family == AF_INET6 && 
+        IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6*)&remoteName)->sin6_addr)) ){
+        v4mapped = 1;
+    }
+    if(!getnameinfo((struct sockaddr*)&remoteName, size, zLine, sizeof(zLine),
+                    NULL, 0, NI_NUMERICHOST)){
+      zIpAddr = zLine;
+    } else {
+      zIpAddr = NULL;
+    }
+    if(zIpAddr && v4mapped) {
+      /* ::ffff:172.16.0.2 */
+      zIpAddr += 7; 
+    }
+  }
+  if( zIpAddr ){
+    cgi_setenv("REMOTE_ADDR", zIpAddr);
+    g.zIpAddr = mprintf("%s", zIpAddr);
+  }
 
   cgi_init();
 }
 
 #if INTERFACE
@@ -1230,37 +1256,116 @@
   fd_set readfds;              /* Set of file descriptors for select() */
   socklen_t lenaddr;           /* Length of the inaddr structure */
   int child;                   /* PID of the child process */
   int nchildren = 0;           /* Number of child processes */
   struct timeval delay;        /* How long to wait inside select() */
+#ifdef HAVE_GETADDRINFO
+  struct addrinfo hints;
+  struct addrinfo* res;
+  struct addrinfo* i;
+  struct sockaddr_storage inaddr;   /* The socket address */
+  char* sPort;
+  int iRet;
+#else
+#ifdef WITH_IPV6
+  struct sockaddr_in6 inaddr;   /* The socket address */
+#else
   struct sockaddr_in inaddr;   /* The socket address */
+#endif
+#endif
   int opt = 1;                 /* setsockopt flag */
   int iPort = mnPort;
 
   while( iPort<=mxPort ){
-    memset(&inaddr, 0, sizeof(inaddr));
-    inaddr.sin_family = AF_INET;
-    if( flags & HTTP_SERVER_LOCALHOST ){
-      inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-    }else{
-      inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+#ifdef HAVE_GETADDRINFO
+    memset(&hints, 0, sizeof(struct addrinfo));
+#ifdef WITH_IPV6
+    hints.ai_family = PF_UNSPEC;
+#else
+    hints.ai_family = PF_INET;
+#endif
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = IPPROTO_TCP;
+    if(!(flags & HTTP_SERVER_LOCALHOST)) hints.ai_flags |= AI_PASSIVE;
+
+    sPort = mprintf("%d", iPort);
+
+    if(iRet = getaddrinfo(NULL, sPort, &hints, &res)) {
+      fossil_fatal("Unable to obtain address: %s", gai_strerror(iRet));
+    }
+
+    for(i = res; i; i = i->ai_next) {
+      listener = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
+      if(listener < 0) {
+        fossil_fatal("Unable to create socket");
+      }
+	  opt=1;
+      setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+      if(i->ai_family == AF_INET6) {
+        opt=0;
+        setsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
+      }
+      if( bind(listener, i->ai_addr, i->ai_addrlen)<0 ){
+        close(listener);
+        listener = -1;
+      }
+	  break;
     }
-    inaddr.sin_port = htons(iPort);
-    listener = socket(AF_INET, SOCK_STREAM, 0);
-    if( listener<0 ){
+
+    free(sPort);
+    freeaddrinfo(res);
+
+    if(listener == -1) {
       iPort++;
       continue;
     }
+#else
+    memset(&inaddr, 0, sizeof(inaddr));
+
+#ifdef WITH_IPV6
+    inaddr.sin6_family = AF_INET6;
+#else
+    inaddr.sin_family = AF_INET;
+#endif
+    if( flags & HTTP_SERVER_LOCALHOST ){
+#ifdef WITH_IPV6
+      memcpy(&inaddr.sin6_addr, &in6addr_loopback, sizeof(inaddr.sin6_addr));
+#else
+      inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+#endif
+    }else{
+#ifdef WITH_IPV6
+      memcpy(&inaddr.sin6_addr, &in6addr_any, sizeof(inaddr.sin6_addr));
+#else
+      inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif
+    }
+#ifdef WITH_IPV6
+    inaddr.sin6_port = htons(iPort);
+    listener = socket(AF_INET6, SOCK_STREAM, 0);
+#else
+    inaddr.sin_port = htons(iPort);
+    listener = socket(AF_INET, SOCK_STREAM, 0);
+#endif
+    if( listener<0 ){
+      fossil_fatal("Unable to create socket");
+    }
 
     /* if we can't terminate nicely, at least allow the socket to be reused */
     setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+
+#ifdef WITH_IPV6
+    opt=0;
+    setsockopt(listener, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
+#endif
 
     if( bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr))<0 ){
       close(listener);
       iPort++;
       continue;
     }
+#endif
     break;
   }
   if( iPort>mxPort ){
     if( mnPort==mxPort ){
       fossil_fatal("unable to open listening socket on ports %d", mnPort);

Index: src/http_socket.c
==================================================================
--- src/http_socket.c
+++ src/http_socket.c
@@ -135,10 +135,58 @@
 **    g.urlPort       TCP/IP port to use.  Ex: 80
 **
 ** Return the number of errors.
 */
 int socket_open(void){
+  int error = 0;
+#ifdef HAVE_GETADDRINFO
+  struct addrinfo hints;
+  struct addrinfo* res;
+  struct addrinfo* i;
+  char ip[INET6_ADDRSTRLEN];
+  void* addr;
+  char* sPort;
+
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_flags = AI_ADDRCONFIG;
+#ifdef WITH_IPV6
+  hints.ai_family = PF_UNSPEC;
+#else
+  hints.ai_family = PF_INET;
+#endif
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = IPPROTO_TCP;
+
+  sPort = mprintf("%d", g.urlPort);
+
+  if(getaddrinfo(g.urlName, sPort, &hints, &res)) {
+    socket_set_errmsg("can't resolve host name: %s", g.urlName);
+    free(sPort);
+    return 1;
+  }
+  for(i = res; i; i = i->ai_next) {
+    iSocket = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
+    if(iSocket < 0) {
+      continue;
+    }
+    if(connect(iSocket, i->ai_addr, i->ai_addrlen) < 0) {
+      close(iSocket);
+      iSocket = -1;
+      continue;
+    }
+    if(!getnameinfo(i->ai_addr, i->ai_addrlen, ip, sizeof(ip),
+                    NULL, 0, NI_NUMERICHOST))
+        g.zIpAddr = mprintf("%s", ip);
+    break;
+  }
+  if(iSocket == -1) {
+    socket_set_errmsg("cannot connect to host %s:%s", g.urlName, sPort);
+    error = 1;
+  }
+  free(sPort);
+  freeaddrinfo(res);
+#else
   static struct sockaddr_in addr;  /* The server address */
   static int addrIsInit = 0;       /* True once addr is initialized */
 
   socket_global_init();
   if( !addrIsInit ){
@@ -172,16 +220,18 @@
     return 1;
   }
   if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){
     socket_set_errmsg("cannot connect to host %s:%d", g.urlName, g.urlPort);
     socket_close();
-    return 1;
+    error = 1;
   }
+#endif
 #if !defined(_WIN32)
-  signal(SIGPIPE, SIG_IGN);
+  if(!error)
+    signal(SIGPIPE, SIG_IGN);
 #endif
-  return 0;
+  return error;
 }
 
 /*
 ** Send content out over the open socket connection.
 */

Index: src/login.c
==================================================================
--- src/login.c
+++ src/login.c
@@ -775,11 +775,12 @@
   **
   ** This feature allows the "fossil ui" command to give the user
   ** full access rights without having to log in.
   */
   zRemoteAddr = ipPrefix(zIpAddr = PD("REMOTE_ADDR","nil"));
-  if( fossil_strcmp(zIpAddr, "127.0.0.1")==0
+  if( ( fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
+        fossil_strcmp(zIpAddr, "::1")==0 )
    && g.useLocalauth
    && db_get_int("localauth",0)==0
    && P("HTTPS")==0
   ){
     uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
@@ -1114,11 +1115,11 @@
       db_exists("SELECT 1 FROM user"
                 " WHERE login='anonymous'"
                 "   AND cap LIKE '%%h%%'") ){
     const char *zUrl = PD("REQUEST_URI", "index");
     @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
-    @ Use <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
+    @ Use <a href="%s(g.zTop)/login?anon=1&amp;g=%T(g.zRoot)%T(zUrl)">anonymous login</a>
     @ to enable hyperlinks.</p>
   }
 }
 
 /*

Index: src/main.c
==================================================================
--- src/main.c
+++ src/main.c
@@ -118,10 +118,12 @@
   int fSystemTrace;       /* Trace calls to fossil_system(), --systemtrace */
   int fNoSync;            /* Do not do an autosync even.  --nosync */
   char *zPath;            /* Name of webpage being served */
   char *zExtra;           /* Extra path information past the webpage name */
   char *zBaseURL;         /* Full text of the URL being served */
+  char *zRedirectBaseURL; /* Full text of the URL being served to be used in redirect */
+  char *zRoot;            /* Repository web root */
   char *zTop;             /* Parent directory of zPath */
   const char *zContentType;  /* The content type of the input HTTP request */
   int iErrPriority;       /* Priority of current error message */
   char *zErrMsg;          /* Text of an error message */
   int sslNotAvailable;    /* SSL is not available.  Do not redirect to https: */
@@ -1073,20 +1075,21 @@
   const char *zHost;
   const char *zMode;
   const char *zCur;
 
   if( g.zBaseURL!=0 ) return;
+  if( g.zRoot==0 ) g.zRoot="";
   zHost = PD("HTTP_HOST","");
   zMode = PD("HTTPS","off");
   zCur = PD("SCRIPT_NAME","/");
   i = strlen(zCur);
   while( i>0 && zCur[i-1]=='/' ) i--;
   if( fossil_stricmp(zMode,"on")==0 ){
-    g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur);
+    g.zBaseURL = mprintf("https://%s%s%.*s", zHost, g.zRoot, i, zCur);
     g.zTop = &g.zBaseURL[8+strlen(zHost)];
   }else{
-    g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur);
+    g.zBaseURL = mprintf("http://%s%s%.*s", zHost, g.zRoot, i, zCur);
     g.zTop = &g.zBaseURL[7+strlen(zHost)];
   }
 }
 
 /*
@@ -1661,10 +1664,14 @@
 ** TCP port 8080, or on any other TCP port defined by the -P or
 ** --port option.  The optional argument is the name of the repository.
 ** The repository argument may be omitted if the working directory is
 ** within an open checkout.
 **
+** If HTTP requests are being reverse proxied to the fossil server,
+** and in proxy server fossil is mapped at a virtual directory, then
+** virtual directory can be specified using optional -R or --root option.
+**
 ** The "ui" command automatically starts a web browser after initializing
 ** the web server.  The "ui" command also binds to 127.0.0.1 and so will
 ** only process HTTP traffic from the local machine.
 **
 ** In the "server" command, the REPOSITORY can be a directory (aka folder)
@@ -1675,14 +1682,15 @@
 ** By default, the "ui" command provides full administrative access without
 ** having to log in.  This can be disabled by setting turning off the
 ** "localauth" setting.  Automatic login for the "server" command is available
 ** if the --localauth option is present and the "localauth" setting is off
 ** and the connection is from localhost.
-**
 ** Options:
 **   --localauth         enable automatic login for requests from localhost
 **   -P|--port TCPPORT   listen to request on port TCPPORT
+**   -R|--root ROOT      map fossil at virtual directory ROOT in reverse 
+**                       proxying
 **   --th-trace          trace TH1 execution (for debugging purposes)
 **
 ** See also: cgi, http, winsrv
 */
 void cmd_webserver(void){
@@ -1699,10 +1707,11 @@
   zStopperFile = find_option("stopper", 0, 1);
 #endif
 
   g.thTrace = find_option("th-trace", 0, 0)!=0;
   g.useLocalauth = find_option("localauth", 0, 0)!=0;
+  g.zRoot = find_option("root", "R", 1);
   if( g.thTrace ){
     blob_zero(&g.thLog);
   }
   zPort = find_option("port", "P", 1);
   zNotFound = find_option("notfound", 0, 1);
@@ -1736,11 +1745,11 @@
       }
     }
 #else
     zBrowser = db_get("web-browser", "open");
 #endif
-    zBrowserCmd = mprintf("%s http://localhost:%%d/ &", zBrowser);
+    zBrowserCmd = mprintf("%s http://localhost:%%d/%s &", zBrowser, (g.zRoot==0?"":g.zRoot));
   }
   db_close(1);
   if( cgi_http_server(iPort, mxPort, zBrowserCmd, flags) ){
     fossil_fatal("unable to listen on TCP socket %d", iPort);
   }