Folks,

As I  mentioned before, I've  been attempting some modifications  to the
fossil SSH URL handling. I'm at a  point where I could use some feedback
regarding the previous method for handling SSH.

Originally, fossil  would fork  an SSH  connection and  then communicate
with the remote  shell by issuing shell  commands to test that  it has a
working shell.  This was accomplished  by a series of  echo/reply tests.
Once it was decided that the  shell was working, it would issue ``fossil
test-http /path/to/repo.fossil'' and things would proceed from there.

Is there value  in keeping this old method? I  personally don't see any,
but then I don't  have the background to know why it  was done this way.
Removing it would simplify the code, but perhaps break unforseen things.
For now,  I think  it would  be alright to  leave it  in as  the default
behavior just to  be safe, despite the fact that  it introduces multiple
code paths.

With that said, I hope the changes I've introduced will allow for a more
flexible interaction  with SSH. All  of the critical changes  are client
side, so they  will even work against older fossil  instances. I've also
added some improved logging of SSH connections which will only be useful
when running a fossil http server that has the newer code.

For example,  after a Fossil user  ``guest'' clones the repo  to a local
account  ``amb'',  makes some  changes,  and  then commits  them,  while
non-admin accounts will only see that ``amb'' commited some changes, the
admin  privileged  accounts  will  actually see  the  following  in  the
timeline:

  [f78758bc9c] Leaf: test (user: amb [guest], tags: trunk) 

And in the detailed info for the commit:

  Received From:        guest @ 10.2.5.9 on 2013-07-10 06:52:50

I'm still debating on the value of  the timeline change, but I think the
detailed info is definitely valuable, as it shows the remote client's IP
address (instead of just 127.0.0.1).

The changes  are still against version-1.26  (I used it as  BASIS when I
branched). I did a test merge against trunk and there were no conflicts.
I'm not certain the changes are implemented in the best way, but they do
work and they also allow for more restricted control using SSH keys.

Specifically, now that I can restrict  an SSH key to a specific command,
I  can also  take advantage  of the  fossil Privileges  and Capabilities
(even though the user has write access to the file), which seems to work
correctly.

In the following example, on remote, the guest user has rw access to the
remote fossil file, but is only setup as a Reader in the fossil repo:

remote$ ls -l test.fossil
-rw-rw----  1 amb  guest  58368 Jul 10 00:19 test.fossil

Now I clone from the local system:

local$ fossil clone -h http ssh://guest@remote//tmp/test.fossil test.fossil
password for guest: 
remember password (Y/n)? y
Round-trips: 1   Artifacts sent: 0  received: 0
ssh -e none -T guest@remote fossil http /tmp/test.fossil
Round-trips: 2   Artifacts sent: 0  received: 9
ssh -e none -T guest@remote fossil http /tmp/test.fossil
Round-trips: 2   Artifacts sent: 0  received: 12
Clone finished with 549 bytes sent, 2344 bytes received
Rebuilding repository meta-data...
  100.0% complete...
project-id: 9ababc8b9a3e0b97d3da4467aa4effbad24d91c2
admin-user: amb (password is "e0b973")


Now  I'll open  and try  to  modify something  but it  will be  rejected
because guest does not have permissions (Reader only):

local$ fossil commit -m test
Autosync:  ssh://guest:*@remote//tmp/test.fossil
Round-trips: 1   Artifacts sent: 0  received: 0
ssh -e none -T guest@remote fossil http /tmp/test.fossil
Round-trips: 1   Artifacts sent: 0  received: 0
Pull finished with 350 bytes sent, 480 bytes received
New_Version: 783a69e327649a5942c1bad7134986a3adb1704a
Autosync:  ssh://guest:*@remote//tmp/test.fossil
Round-trips: 1   Artifacts sent: 2  received: 0
ssh -e none -T guest@remote fossil http /tmp/test.fossil
Error: not authorized to write
Round-trips: 1   Artifacts sent: 2  received: 0
Sync finished with 736 bytes sent, 512 bytes received
Autosync failed
^^^^^^^^^^^^^^^

Guess it didn't work out.  Let's try to delete the file:

local$ ssh guest@remote rm /tmp/test.fossil

HTTP/1.0 501 Not Implemented
Date: Wed, 10 Jul 2013 06:44:06 GMT
Connection: close
X-Frame-Options: SAMEORIGIN
Cache-control: no-cache
Content-Type: text/html; charset=utf-8
Content-Length: 52

<html><body>Unrecognized HTTP Request</body></html>


Nope, no go, key says I can only do fossil http /tmp/test.fossil.

Anyway, that's  where I'm at with  the changes. Also, I'm  not sure that
where I  setup the signal ignore  for SIGPIPE was in  the correct place,
however, without  it, fossil kept  failing with SIGSEGV when  the remote
SSH connection  closed the connection.  Some guidance there would  be of
value.

With that  said, attached is  an updated patch against  version-1.26 for
review if anyone is interested.

Definitely needs more testing than I have been able to give it.

Andy
Index: src/cgi.c
==================================================================
--- src/cgi.c
+++ src/cgi.c
@@ -1493,5 +1493,23 @@
   cgi_set_status(304,"Not Modified");
   cgi_reset_content();
   cgi_reply();
   fossil_exit(0);
 }
+
+/*
+** Check to see if the remote client is SSH and return
+** its IP or return default
+*/
+const char *cgi_ssh_remote_addr(const char *zDefault){
+  char *zIndex;
+  const char *zSshConn = fossil_getenv("SSH_CONNECTION");
+
+  if( zSshConn && zSshConn[0] ){
+    char *zSshClient = mprintf("%s",zSshConn);
+    if( zIndex = strchr(zSshClient,' ') ){
+      zSshClient[zIndex-zSshClient] = '\0';
+      return zSshClient;
+    }
+  }
+  return zDefault;
+}

Index: src/clone.c
==================================================================
--- src/clone.c
+++ src/clone.c
@@ -103,10 +103,11 @@
   const char *zDefaultUser;   /* Optional name of the default user */
   int nErr = 0;
   int bPrivate = 0;           /* Also clone private branches */
 
   if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
+  sync_ssh_options();
   url_proxy_options();
   if( g.argc < 4 ){
     usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
   }
   db_open_config(0);
@@ -152,10 +153,11 @@
     }
     db_multi_exec(
       "REPLACE INTO config(name,value,mtime)"
       " VALUES('server-code', lower(hex(randomblob(20))), now());"
     );
+    sync_ssh_db_options();
     url_enable_proxy(0);
     url_get_password_if_needed();
     g.xlinkClusterOnly = 1;
     nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
     g.xlinkClusterOnly = 0;

Index: src/http.c
==================================================================
--- src/http.c
+++ src/http.c
@@ -42,12 +42,12 @@
 
   blob_zero(pLogin);
   if( g.urlUser==0 || fossil_strcmp(g.urlUser, "anonymous")==0 ){
      return;  /* If no login card for users "nobody" and "anonymous" */
   }
-  if( g.urlIsSsh ){
-     return;  /* If no login card for SSH: */
+  if( g.urlIsSsh && g.fSshFossilCmd==0 ){
+    return;  /* If no login card for SSH: */
   }
   blob_zero(&nonce);
   blob_zero(&pw);
   sha1sum_blob(pPayload, &nonce);
   blob_copy(&pw, &nonce);

Index: src/http_transport.c
==================================================================
--- src/http_transport.c
+++ src/http_transport.c
@@ -173,85 +173,83 @@
   }
   fossil_free(zIn);
 }
 
 /*
-** Global initialization of the transport layer
+** SSH initialization of the transport layer
 */
-void transport_global_startup(void){
-  if( g.urlIsSsh ){
-    /* Only SSH requires a global initialization.  For SSH we need to create
-    ** and run an SSH command to talk to the remote machine.
-    */
-    const char *zSsh;  /* The base SSH command */
-    Blob zCmd;         /* The SSH command */
-    char *zHost;       /* The host name to contact */
-    int n;             /* Size of prefix string */
+int transport_ssh_open(void){
+  /* For SSH we need to create and run an SSH http 
+  ** to talk to the remote machine.
+  */
+  const char *zSsh;  /* The base SSH command */
+  Blob zCmd;         /* The SSH command */
+  char *zHost;       /* The host name to contact */
+  char *zPath;       /* The path to the remote file for SSH */
+  int n;             /* Size of prefix string */
 
-    zSsh = db_get("ssh-command", zDefaultSshCmd);
-    blob_init(&zCmd, zSsh, -1);
-    if( g.urlPort!=g.urlDfltPort ){
+  zSsh = db_get("ssh-command", zDefaultSshCmd);
+  blob_init(&zCmd, zSsh, -1);
+  if( g.urlPort!=g.urlDfltPort ){
 #ifdef __MINGW32__
-      blob_appendf(&zCmd, " -P %d", g.urlPort);
+    blob_appendf(&zCmd, " -P %d", g.urlPort);
 #else
-      blob_appendf(&zCmd, " -p %d", g.urlPort);
+    blob_appendf(&zCmd, " -p %d", g.urlPort);
 #endif
-    }
-    fossil_force_newline();
-    fossil_print("%s", blob_str(&zCmd));  /* Show the base of the SSH command */
-    if( g.urlUser && g.urlUser[0] ){
-      zHost = mprintf("%s@%s", g.urlUser, g.urlName);
+  }
+  fossil_force_newline();
+  fossil_print("%s", blob_str(&zCmd));  /* Show the base of the SSH command */
+  if( g.urlUser && g.urlUser[0] ){
+    zHost = mprintf("%s@%s", g.urlUser, g.urlName);
 #ifdef __MINGW32__
-      /* Only win32 (and specifically PLINK.EXE) support the -pw option */
-      if( g.urlPasswd && g.urlPasswd[0] ){
-        Blob pw;
-        blob_zero(&pw);
-        if( g.urlPasswd[0]=='*' ){
-          char *zPrompt;
-          zPrompt = mprintf("Password for [%s]: ", zHost);
-          prompt_for_password(zPrompt, &pw, 0);
-          free(zPrompt);
-        }else{
-          blob_init(&pw, g.urlPasswd, -1);
-        }
-        blob_append(&zCmd, " -pw ", -1);
-        shell_escape(&zCmd, blob_str(&pw));
-        blob_reset(&pw);
-        fossil_print(" -pw ********");  /* Do not show the password text */
+    /* Only win32 (and specifically PLINK.EXE) support the -pw option */
+    if( g.urlPasswd && g.urlPasswd[0] ){
+      Blob pw;
+      blob_zero(&pw);
+      if( g.urlPasswd[0]=='*' ){
+	char *zPrompt;
+	zPrompt = mprintf("Password for [%s]: ", zHost);
+	prompt_for_password(zPrompt, &pw, 0);
+	free(zPrompt);
+      }else{
+	blob_init(&pw, g.urlPasswd, -1);
       }
+      blob_append(&zCmd, " -pw ", -1);
+      shell_escape(&zCmd, blob_str(&pw));
+      blob_reset(&pw);
+      fossil_print(" -pw ********");  /* Do not show the password text */
+    }
 #endif
-    }else{
-      zHost = mprintf("%s", g.urlName);
-    }
-    n = blob_size(&zCmd);
+  }else{
+    zHost = mprintf("%s", g.urlName);
+  }
+  n = blob_size(&zCmd);
+  blob_append(&zCmd, " ", 1);
+  shell_escape(&zCmd, zHost);
+  if( g.urlShell ){
+    blob_appendf(&zCmd, " %s", g.urlShell);
+  }
+  if( g.fSshFossilCmd && g.fSshFossilCmd[0] ){
     blob_append(&zCmd, " ", 1);
-    shell_escape(&zCmd, zHost);
-    if( g.urlShell ){
-      blob_appendf(&zCmd, " %s", g.urlShell);
-    }else{
-#if defined(FOSSIL_ENABLE_SSH_FAR_SIDE)
-      /* The following works.  But only if the fossil on the remote side
-      ** is recent enough to support the test-ssh-far-side command.  That
-      ** command was added on 2013-02-06.  We will leave this turned off
-      ** until most fossil servers have upgraded to that version or a later
-      ** version.  The sync will still work as long as the shell on the far
-      ** side is bash and not tcsh.  And if the default far side shell is
-      ** tcsh, then the shell=/bin/bash query parameter can be used as a
-      ** work-around.  Enable this code after about a year...
-      */
-      blob_appendf(&zCmd, " exec %s test-ssh-far-side", g.urlFossil);
-#endif
+    shell_escape(&zCmd, g.fSshFossilCmd);
+    blob_appendf(&zCmd, " %s ", g.fSshHttpCmd);
+    if( g.urlPath && g.urlPath[0] ){
+      zPath = mprintf("%s", g.urlPath);
+      shell_escape(&zCmd, zPath);
     }
-    fossil_print("%s\n", blob_str(&zCmd)+n);  /* Show tail of SSH command */
-    free(zHost);
-    popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
-    if( sshPid==0 ){
-      fossil_fatal("cannot start ssh tunnel using [%b]", &zCmd);
-    }
-    blob_reset(&zCmd);
+  }
+  fossil_print("%s\n", blob_str(&zCmd)+n);  /* Show tail of SSH command */
+  free(zHost);
+  popen2(blob_str(&zCmd), &sshIn, &sshOut, &sshPid);
+  if( sshPid==0 ){
+    socket_set_errmsg("cannot start ssh tunnel using [%b]", &zCmd);
+  }
+  blob_reset(&zCmd);
+  if( g.fSshFossilCmd==0 ){
     transport_ssh_startup();
   }
+  return sshPid==0;
 }
 
 /*
 ** COMMAND: test-ssh-far-side
 **
@@ -287,11 +285,17 @@
 ** Return the number of errors.
 */
 int transport_open(void){
   int rc = 0;
   if( transport.isOpen==0 ){
-    if( g.urlIsSsh ){
+    if( g.urlIsSsh && g.fSshFossilCmd ){
+      rc = transport_ssh_open();
+      if( rc==0 ) transport.isOpen = 1;
+    }else if( g.urlIsSsh ){
+      if( sshPid==0 ){
+	rc = transport_ssh_open();
+      }
       Blob cmd;
       blob_zero(&cmd);
       shell_escape(&cmd, g.urlFossil);
       blob_append(&cmd, " test-http ", -1);
       shell_escape(&cmd, g.urlPath);
@@ -339,12 +343,14 @@
     transport.iCursor = 0;
     if( transport.pLog ){
       fclose(transport.pLog);
       transport.pLog = 0;
     }
-    if( g.urlIsSsh ){
-      /* No-op */
+    if( g.urlIsSsh && g.fSshFossilCmd ){
+      transport_ssh_close();
+    }else if( g.urlIsSsh ){
+      /* no-op */
     }else if( g.urlIsHttps ){
       #ifdef FOSSIL_ENABLE_SSL
       ssl_close();
       #endif
     }else if( g.urlIsFile ){
@@ -582,19 +588,23 @@
   if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]);
   return &transport.pBuf[iStart];
 }
 
 void transport_global_shutdown(void){
-  if( g.urlIsSsh && sshPid ){
-    /*printf("Closing SSH tunnel: ");*/
-    fflush(stdout);
-    pclose2(sshIn, sshOut, sshPid);
-    sshPid = 0;
-  }
+  transport_ssh_close();
   if( g.urlIsHttps ){
     #ifdef FOSSIL_ENABLE_SSL
     ssl_global_shutdown();
     #endif
   }else{
     socket_global_shutdown();
   }
 }
+
+void transport_ssh_close(void){
+  if( g.urlIsSsh && sshPid ){
+    /*printf("Closing SSH tunnel: ");*/
+    fflush(stdout);
+    pclose2(sshIn, sshOut, sshPid);
+    sshPid = 0;
+  }
+}

Index: src/main.c
==================================================================
--- src/main.c
+++ src/main.c
@@ -134,10 +134,13 @@
   int fSqlPrint;          /* True if -sqlprint flag is present */
   int fQuiet;             /* True if -quiet flag is present */
   int fHttpTrace;         /* Trace outbound HTTP requests */
   int fSystemTrace;       /* Trace calls to fossil_system(), --systemtrace */
   int fSshTrace;          /* Trace the SSH setup traffic */
+  char *fSshFossilCmd;    /* Path to remoe fossil command for SSH */
+  char *fSshHttpCmd;      /* Which http command to use for SSH */
+  char *fSshCmd;          /* SSH command string */
   int fNoSync;            /* Do not do an autosync ever.  --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 *zTop;             /* Parent directory of zPath */
@@ -578,10 +581,13 @@
     g.fQuiet = find_option("quiet", 0, 0)!=0;
     g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
     g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
     g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
     g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
+    g.fSshFossilCmd = 0;
+    g.fSshHttpCmd = 0;
+    g.fSshCmd = 0;
     if( g.fSqlTrace ) g.fSqlStats = 1;
     g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
     g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
     g.zLogin = find_option("user", "U", 1);
     g.zSSLIdentity = find_option("ssl-identity", 0, 1);
@@ -623,10 +629,11 @@
                  "%s: could be any of:%s\n"
                  "%s: use \"help\" for more information\n",
                  g.argv[0], zCmdName, g.argv[0], blob_str(&couldbe), g.argv[0]);
     fossil_exit(1);
   }
+  signal(SIGPIPE,SIG_IGN);
   atexit( fossil_atexit );
   aCommand[idx].xFunc();
   fossil_exit(0);
   /*NOT_REACHED*/
   return 0;
@@ -1685,10 +1692,13 @@
   }else{
     g.httpIn = stdin;
     g.httpOut = stdout;
     zIpAddr = 0;
   }
+  if( zIpAddr==0 ){
+    zIpAddr = cgi_ssh_remote_addr(0);
+  }
   find_server_repository(0);
   g.zRepositoryName = enter_chroot_jail(g.zRepositoryName);
   cgi_handle_http_request(zIpAddr);
   process_one_web_page(zNotFound, glob_create(zFileGlob));
 }
@@ -1701,17 +1711,16 @@
 */
 void cmd_test_http(void){
   Th_InitTraceLog();
   login_set_capabilities("sx", 0);
   g.useLocalauth = 1;
-  cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
   g.httpIn = stdin;
   g.httpOut = stdout;
   find_server_repository(0);
   g.cgiOutput = 1;
   g.fullHttpReply = 1;
-  cgi_handle_http_request(0);
+  cgi_handle_http_request(cgi_ssh_remote_addr("127.0.0.1"));
   process_one_web_page(0, 0);
 }
 
 #if !defined(_WIN32)
 #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)

Index: src/sync.c
==================================================================
--- src/sync.c
+++ src/sync.c
@@ -147,10 +147,11 @@
 */
 void pull_cmd(void){
   unsigned configFlags = 0;
   unsigned syncFlags = SYNC_PULL;
   process_sync_args(&configFlags, &syncFlags);
+  sync_ssh_options();
   client_sync(syncFlags, configFlags, 0);
 }
 
 /*
 ** COMMAND: push
@@ -176,10 +177,11 @@
 */
 void push_cmd(void){
   unsigned configFlags = 0;
   unsigned syncFlags = SYNC_PUSH;
   process_sync_args(&configFlags, &syncFlags);
+  sync_ssh_options();
   if( db_get_boolean("dont-push",0) ){
     fossil_fatal("pushing is prohibited: the 'dont-push' option is set");
   }
   client_sync(syncFlags, 0, 0);
 }
@@ -214,11 +216,13 @@
 */
 void sync_cmd(void){
   unsigned configFlags = 0;
   unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
   process_sync_args(&configFlags, &syncFlags);
+  sync_ssh_options();
   if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
+  sync_ssh_db_options();
   client_sync(syncFlags, configFlags, 0);
   if( (syncFlags & SYNC_PUSH)==0 ){
     fossil_warning("pull only: the 'dont-push' option is set");
   }
 }
@@ -257,5 +261,43 @@
   }else{
     url_parse(zUrl, 0);
     fossil_print("%s\n", g.urlCanonical);
   }
 }
+
+void sync_ssh_options(void){
+  const char *zSshFossilCmd;  /* Path to remote fossil command for SSH */
+  const char *zSshHttpCmd;    /* Name of remote HTTP command for SSH */
+  const char *zSshCmd;        /* Name of remote HTTP command for SSH */
+
+  zSshFossilCmd = find_option("sshfossilcmd","f",1);
+  if( zSshFossilCmd && zSshFossilCmd[0] ){
+    g.fSshFossilCmd = mprintf("%s", zSshFossilCmd);
+  }
+  zSshHttpCmd = find_option("sshhttpcmd","h",1);
+  if( zSshHttpCmd && zSshHttpCmd[0] ){
+    g.fSshHttpCmd = mprintf("%s", zSshHttpCmd);
+    if( zSshFossilCmd==0 ){
+      g.fSshFossilCmd = "fossil";
+    }
+  }
+  zSshCmd = find_option("sshcmd","s",1);
+  if( zSshCmd && zSshCmd[0] ){
+    g.fSshCmd = mprintf("%s", zSshCmd);
+  }
+}
+
+void sync_ssh_db_options(void){
+  if( g.fSshFossilCmd && g.fSshFossilCmd[0] ){
+    db_set("last-ssh-fossil-cmd", g.fSshFossilCmd, 0);
+  }else{
+    g.fSshFossilCmd = db_get("last-ssh-fossil-cmd", 0);
+  }
+  if( g.fSshHttpCmd && g.fSshHttpCmd[0] ){
+    db_set("last-ssh-http-cmd", g.fSshHttpCmd, 0);
+  }else{
+    g.fSshHttpCmd = db_get("last-ssh-http-cmd", "test-http");
+  }
+  if( g.fSshCmd && g.fSshCmd[0] ){
+    db_set("ssh-command", g.fSshCmd, 0);
+  }
+}

Index: src/timeline.c
==================================================================
--- src/timeline.c
+++ src/timeline.c
@@ -273,12 +273,14 @@
     const char *zBgClr = db_column_text(pQuery, 6);
     const char *zDate = db_column_text(pQuery, 2);
     const char *zType = db_column_text(pQuery, 7);
     const char *zUser = db_column_text(pQuery, 4);
     const char *zTagList = db_column_text(pQuery, 8);
+    const char *zLogin = db_column_text(pQuery, 12);
     int tagid = db_column_int(pQuery, 9);
     const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
+    const char *zDispLogin;
     const char *zBr = 0;      /* Branch */
     int commentColumn = 3;    /* Column containing comment text */
     int modPending;           /* Pending moderation */
     char zTime[8];
 
@@ -408,16 +410,26 @@
     blob_reset(&comment);
 
     /* Generate the "user: USERNAME" at the end of the comment, together
     ** with a hyperlink to another timeline for that user.
     */
+    zDispLogin = "";
+    if( g.perm.Admin ){
+      if( zLogin && zLogin[0] ){
+	if( fossil_strcmp(zLogin, zUser)!=0 ){
+	  zDispLogin = mprintf(" [%s]", zLogin);
+	}
+      }else{
+	zDispLogin = " [unknown]";
+      }
+    }
     if( zTagList && zTagList[0]==0 ) zTagList = 0;
     if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
       char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd", zDispUser, zDate);
-      @ (user: %z(href("%z",zLink))%h(zDispUser)</a>%s(zTagList?",":"\051")
+      @ (user: %z(href("%z",zLink))%h(zDispUser)%h(zDispLogin)</a>%s(zTagList?",":"\051")
     }else{
-      @ (user: %h(zDispUser)%s(zTagList?",":"\051")
+      @ (user: %h(zDispUser)%h(zDispLogin)%s(zTagList?",":"\051")
     }
 
     /* Generate a "detail" link for tags. */
     if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
       @ [%z(href("%R/info/%S",zUuid))details</a>]
@@ -852,11 +864,12 @@
     @   bgcolor TEXT,
     @   etype TEXT,
     @   taglist TEXT,
     @   tagid INTEGER,
     @   short TEXT,
-    @   sortby REAL
+    @   sortby REAL,
+    @   login TEXT
     @ )
   ;
   db_multi_exec(zSql);
 }
 
@@ -879,13 +892,17 @@
     @   (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref
     @     WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
     @       AND tagxref.rid=blob.rid AND tagxref.tagtype>0) AS tags,
     @   tagid AS tagid,
     @   brief AS brief,
-    @   event.mtime AS mtime
+    @   event.mtime AS mtime,
+    @   login AS login
     @  FROM event CROSS JOIN blob
+    @  CROSS JOIN rcvfrom CROSS JOIN user
     @ WHERE blob.rid=event.objid
+    @ AND blob.rcvid = rcvfrom.rcvid
+    @ AND rcvfrom.uid = user.uid
   ;
   if( zBase==0 ){
     zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH);
   }
   return zBase;

Index: src/url.c
==================================================================
--- src/url.c
+++ src/url.c
@@ -439,11 +439,11 @@
 /*
 ** Prompt the user for the password for g.urlUser.  Store the result
 ** in g.urlPasswd.
 */
 void url_prompt_for_password(void){
-  if( g.urlIsSsh || g.urlIsFile ) return;
+  if( g.urlIsSsh && g.fSshFossilCmd==0 || g.urlIsFile ) return;
   if( isatty(fileno(stdin))
    && (g.urlFlags & URL_PROMPT_PW)!=0
    && (g.urlFlags & URL_PROMPTED)==0
   ){
     char *zPrompt = mprintf("\rpassword for %s: ", g.urlUser);
@@ -490,10 +490,9 @@
 */
 void url_get_password_if_needed(void){
   if( (g.urlUser && g.urlUser[0])
    && (g.urlPasswd==0 || g.urlPasswd[0]==0)
    && isatty(fileno(stdin)) 
-   && g.urlIsSsh==0
   ){
     url_prompt_for_password();
   }
 }

Index: src/xfer.c
==================================================================
--- src/xfer.c
+++ src/xfer.c
@@ -1336,10 +1336,11 @@
 
   if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
   if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE))==0 
      && configRcvMask==0 && configSendMask==0 ) return 0;
 
+  sync_ssh_db_options();
   transport_stats(0, 0, 1);
   socket_global_init();
   memset(&xfer, 0, sizeof(xfer));
   xfer.pIn = &recv;
   xfer.pOut = &send;
@@ -1387,11 +1388,10 @@
     blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
     nCardSent++;
     if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
   }
   manifest_crosslink_begin();
-  transport_global_startup();
   if( syncFlags & SYNC_VERBOSE ){
     fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
   }
 
   while( go ){
@@ -1483,11 +1483,11 @@
     xfer.nFileSent = 0;
     xfer.nDeltaSent = 0;
     xfer.nGimmeSent = 0;
     xfer.nIGotSent = 0;
     if( syncFlags & SYNC_VERBOSE ){
-      fossil_print("waiting for server...");
+      fossil_print("waiting for server...\n");
     }
     fflush(stdout);
     if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
         MAX_REDIRECTS) ){
       nErr++;

TAI64 timestamp: 4000000051dd128c
_______________________________________________
fossil-users mailing list
[email protected]
http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users

Reply via email to