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