Index: src/checkin.c ================================================================== --- src/checkin.c +++ src/checkin.c @@ -1352,10 +1352,11 @@ } } sCiInfo.zDateOvrd = find_option("date-override",0,1); sCiInfo.zUserOvrd = find_option("user-override",0,1); db_must_be_within_tree(); + clone_ssh_db_options(); noSign = db_get_boolean("omitsign", 0)|noSign; if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } useCksum = db_get_boolean("repo-cksum", 1); outputManifest = db_get_boolean("manifest", 0); verify_all_options(); Index: src/clone.c ================================================================== --- src/clone.c +++ src/clone.c @@ -95,10 +95,11 @@ ** --admin-user|-A USERNAME Make USERNAME the administrator ** --private Also clone private branches ** --ssl-identity=filename Use the SSL identity if requested by the server ** --ssh-fossil|-f /fossil Use this path as remote fossil command ** --ssh-command|-c 'command' Use this SSH command +** --ssh-fossil-user|-l user Fossil user to use for SSH if different. ** ** See also: init */ void clone_cmd(void){ char *zPassword; @@ -157,10 +158,11 @@ "REPLACE INTO config(name,value,mtime)" " VALUES('server-code', lower(hex(randomblob(20))), now());" ); url_enable_proxy(0); url_get_password_if_needed(); + clone_ssh_db_options(); g.xlinkClusterOnly = 1; nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0); g.xlinkClusterOnly = 0; verify_cancel(); db_end_transaction(0); @@ -184,18 +186,23 @@ ** Look for SSH clone command line options and setup in globals. */ void clone_ssh_options(void){ const char *zSshFossilCmd; /* Path to remote fossil command for SSH */ const char *zSshCmd; /* SSH command string */ + const char *zFossilUser; /* Fossil user if login specified for SSH */ zSshFossilCmd = find_option("ssh-fossil","f",1); if( zSshFossilCmd && zSshFossilCmd[0] ){ g.zSshFossilCmd = mprintf("%s", zSshFossilCmd); } zSshCmd = find_option("ssh-command","c",1); if( zSshCmd && zSshCmd[0] ){ g.zSshCmd = mprintf("%s", zSshCmd); + } + zFossilUser = find_option("ssh-fossil-user","l",1); + if( zFossilUser && zFossilUser[0] ){ + g.zFossilUser = mprintf("%s", zFossilUser); } } /* ** Set SSH options discovered in global variables (set from command line @@ -203,11 +210,16 @@ */ void clone_ssh_db_options(void){ if( g.zSshFossilCmd && g.zSshFossilCmd[0] ){ db_set("ssh-fossil", g.zSshFossilCmd, 0); }else{ - g.zSshFossilCmd = db_get("ssh-fossil","fossil"); + g.zSshFossilCmd = db_get("ssh-fossil", "fossil"); } if( g.zSshCmd && g.zSshCmd[0] ){ db_set("ssh-command", g.zSshCmd, 0); } + if( g.zFossilUser && g.zFossilUser[0] ){ + db_set("ssh-fossil-user", g.zFossilUser, 0); + }else{ + g.zFossilUser = db_get("ssh-fossil-user", 0); + } } Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -2128,10 +2128,11 @@ { "relative-paths",0, 0, 0, "on" }, { "repo-cksum", 0, 0, 0, "on" }, { "self-register", 0, 0, 0, "off" }, { "ssh-command", 0, 40, 0, "" }, { "ssh-fossil", 0, 40, 0, "" }, + { "ssh-fossil-user", 0, 40, 0, "" }, { "ssl-ca-location",0, 40, 0, "" }, { "ssl-identity", 0, 40, 0, "" }, #ifdef FOSSIL_ENABLE_TCL { "tcl", 0, 0, 0, "off" }, { "tcl-setup", 0, 40, 0, "" }, @@ -2300,10 +2301,12 @@ ** ** ssh-command Command used to talk to a remote machine with ** the "ssh://" protocol. ** ** ssh-fossil Remote fossil command to run with the "ssh://" protocol. +** +** ssh-fossil-user Fossil user to use instead of the URL user. ** ** ssl-ca-location The full pathname to a file containing PEM encoded ** CA root certificates, or a directory of certificates ** with filenames formed from the certificate hashes as ** required by OpenSSL. Index: src/http.c ================================================================== --- src/http.c +++ src/http.c @@ -39,18 +39,19 @@ const char *zPw; /* The user password */ Blob pw; /* The nonce with user password appended */ Blob sig; /* The signature field */ 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.urlUser==0 && g.zFossilUser==0 || + fossil_strcmp(g.urlUser, "anonymous")==0 ){ + return; /* If no login card for users "nobody" and "anonymous" */ } blob_zero(&nonce); blob_zero(&pw); sha1sum_blob(pPayload, &nonce); blob_copy(&pw, &nonce); - zLogin = g.urlUser; + zLogin = url_or_fossil_user(); if( g.urlPasswd ){ zPw = g.urlPasswd; }else if( g.cgiOutput ){ /* Password failure while doing a sync from the web interface */ cgi_printf("*** incorrect or missing password for user %h\n", zLogin); @@ -87,11 +88,13 @@ ** the complete payload (including the login card) already compressed. */ static void http_build_header(Blob *pPayload, Blob *pHdr){ int i; const char *zSep; + const char *zLogin; + zLogin = url_or_fossil_user(); blob_zero(pHdr); i = strlen(g.urlPath); if( i>0 && g.urlPath[i-1]=='/' ){ zSep = ""; }else{ @@ -99,12 +102,12 @@ } blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.urlPath, zSep); if( g.urlProxyAuth ){ blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.urlProxyAuth); } - if( g.urlPasswd && g.urlUser && g.urlPasswd[0]=='#' ){ - char *zCredentials = mprintf("%s:%s", g.urlUser, &g.urlPasswd[1]); + if( g.urlPasswd && zLogin && g.urlPasswd[0]=='#' ){ + char *zCredentials = mprintf("%s:%s", zLogin, &g.urlPasswd[1]); char *zEncoded = encode64(zCredentials, -1); blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); fossil_free(zEncoded); fossil_free(zCredentials); } Index: src/http_transport.c ================================================================== --- src/http_transport.c +++ src/http_transport.c @@ -123,29 +123,10 @@ } 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 */ - } -#endif }else{ zHost = mprintf("%s", g.urlName); } n = blob_size(&zCmd); blob_append(&zCmd, " ", 1); Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -136,10 +136,11 @@ int fHttpTrace; /* Trace outbound HTTP requests */ int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ int fSshTrace; /* Trace the SSH setup traffic */ char *zSshFossilCmd; /* Path to remoe fossil command for SSH */ char *zSshCmd; /* SSH command string */ + char *zFossilUser; /* Fossil user if different from URL user */ 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 */ @@ -580,10 +581,11 @@ 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.zSshFossilCmd = 0; g.zSshCmd = 0; + g.zFossilUser = 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); Index: src/sync.c ================================================================== --- src/sync.c +++ src/sync.c @@ -49,11 +49,11 @@ }else{ /* Autosync defaults on. To make it default off, "return" here. */ } url_parse(0, URL_REMEMBER); if( g.urlProtocol==0 ) return 0; - if( g.urlUser!=0 && g.urlPasswd==0 ){ + if( ( g.urlUser!=0 || g.zFossilUser!=0 ) && g.urlPasswd==0 ){ g.urlPasswd = unobscure(db_get("last-sync-pw", 0)); } #if 0 /* Disabled for now */ if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ /* When doing an automatic pull, also automatically pull shuns from @@ -65,11 +65,13 @@ */ configSync = CONFIGSET_SHUN; } #endif if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE; - fossil_print("Autosync: %s\n", g.urlCanonical); + ( g.zFossilUser && g.zFossilUser[0] ) ? + fossil_print("Autosync: (%s) %s\n", g.zFossilUser, g.urlCanonical) : + fossil_print("Autosync: %s\n", g.urlCanonical); url_enable_proxy("via proxy: "); rc = client_sync(flags, configSync, 0); if( rc ) fossil_warning("Autosync failed"); return rc; } @@ -102,23 +104,30 @@ if( g.argc==2 ){ if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN; }else if( g.argc==3 ){ zUrl = g.argv[2]; } + clone_ssh_db_options(); url_parse(zUrl, urlFlags); if( g.urlProtocol==0 ){ if( urlOptional ) fossil_exit(0); usage("URL"); } user_select(); if( g.argc==2 ){ if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){ - fossil_print("Sync with %s\n", g.urlCanonical); + ( g.zFossilUser && g.zFossilUser[0] ) ? + fossil_print("Sync with (%s) %s\n", g.zFossilUser, g.urlCanonical) : + fossil_print("Sync with %s\n", g.urlCanonical); }else if( (*pSyncFlags) & SYNC_PUSH ){ - fossil_print("Push to %s\n", g.urlCanonical); + ( g.zFossilUser && g.zFossilUser[0] ) ? + fossil_print("Push to (%s) %s\n", g.zFossilUser, g.urlCanonical) : + fossil_print("Push to %s\n", g.urlCanonical); }else if( (*pSyncFlags) & SYNC_PULL ){ - fossil_print("Pull from %s\n", g.urlCanonical); + ( g.zFossilUser && g.zFossilUser[0] ) ? + fossil_print("Pull from (%s) %s\n", g.zFossilUser, g.urlCanonical) : + fossil_print("Pull from %s\n", g.urlCanonical); } } url_enable_proxy("via proxy: "); *pConfigFlags |= configSync; } Index: src/url.c ================================================================== --- src/url.c +++ src/url.c @@ -197,19 +197,20 @@ g.urlProtocol = "file"; g.urlPath = ""; g.urlName = mprintf("%b", &cfile); g.urlCanonical = mprintf("file://%T", g.urlName); blob_reset(&cfile); - }else if( g.urlUser!=0 && g.urlPasswd==0 && (urlFlags & URL_PROMPT_PW) ){ + }else if( ( g.urlUser!=0 || g.zFossilUser!=0 ) + && g.urlPasswd==0 && (urlFlags & URL_PROMPT_PW) ){ url_prompt_for_password(); bPrompted = 1; } if( urlFlags & URL_REMEMBER ){ if( bSetUrl ){ db_set("last-sync-url", g.urlCanonical, 0); } - if( !bPrompted && g.urlPasswd && g.urlUser ){ + if( !bPrompted && g.urlPasswd && ( g.urlUser || g.zFossilUser ) ){ db_set("last-sync-pw", obscure(g.urlPasswd), 0); } } } @@ -412,35 +413,25 @@ if( 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); - Blob x; - fossil_force_newline(); - prompt_for_password(zPrompt, &x, 0); - free(zPrompt); - g.urlPasswd = mprintf("%b", &x); - blob_reset(&x); g.urlFlags |= URL_PROMPTED; + g.urlPasswd = prompt_for_user_password(url_or_fossil_user()); if( g.urlPasswd[0] && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 ){ - char c; - prompt_user("remember password (Y/n)? ", &x); - c = blob_str(&x)[0]; - blob_reset(&x); - if( c!='n' && c!='N' ){ + if( save_password_prompt() ){ g.urlFlags |= URL_REMEMBER_PW; if( g.urlFlags & URL_REMEMBER ){ db_set("last-sync-pw", obscure(g.urlPasswd), 0); } } } }else{ fossil_fatal("missing or incorrect password for user \"%s\"", - g.urlUser); + url_or_fossil_user() ); } } /* ** Remember the URL if requested. Index: src/user.c ================================================================== --- src/user.c +++ src/user.c @@ -128,10 +128,44 @@ break; } } blob_reset(&secondTry); } + +/* +** Prompt to save Fossil user password +*/ +int save_password_prompt(){ + Blob x; + char c; + prompt_user("remember password (Y/n)? ", &x); + c = blob_str(&x)[0]; + blob_reset(&x); + return ( c!='n' && c!='N' ); +} + +/* +** Prompt for Fossil user password +*/ +char *prompt_for_user_password(const char *zUser){ + char *zPrompt = mprintf("\rpassword for %s: ", zUser); + char *zPw; + Blob x; + fossil_force_newline(); + prompt_for_password(zPrompt, &x, 0); + free(zPrompt); + zPw = mprintf("%b", &x); + blob_reset(&x); + return zPw; +} + +/* +** Return Fossil user if defined or URL user +*/ +const char *url_or_fossil_user(void){ + return ( g.zFossilUser && g.zFossilUser[0] ) ? g.zFossilUser : g.urlUser; +} /* ** Prompt the user to enter a single line of text. */ void prompt_user(const char *zPrompt, Blob *pIn){ @@ -319,10 +353,12 @@ ** ** (7) Try the USERNAME environment variable. ** ** (8) Check if the user can be extracted from the remote URL. ** +** (9) Check if the user was supplied as SSH command-line option. +** ** The user name is stored in g.zLogin. The uid is in g.userUid. */ void user_select(void){ if( g.userUid ) return; if( g.zLogin ){ @@ -345,10 +381,12 @@ if( attempt_user(fossil_getenv("USERNAME")) ) return; url_parse(0, 0); if( g.urlUser && attempt_user(g.urlUser) ) return; + + if( g.zFossilUser && attempt_user(g.zFossilUser) ) return; fossil_print( "Cannot figure out who you are! Consider using the --user\n" "command line option, setting your USER environment variable,\n" "or setting a default user with \"fossil user default USER\".\n" Index: src/xfer.c ================================================================== --- src/xfer.c +++ src/xfer.c @@ -1336,11 +1336,10 @@ 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; - clone_ssh_db_options(); transport_stats(0, 0, 1); socket_global_init(); memset(&xfer, 0, sizeof(xfer)); xfer.pIn = &recv; xfer.pOut = &send;