Fossil

Check-in [a8c37c2c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:another patch version for ssh improvement from Andy Bradford.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | andy_bradford_ssh_imporvement_patch_2
Files: files | file ages | folders
SHA1:a8c37c2c48e88875f2e79b33c7a31b07b0f5ba7d
User & Date: mgagnon 2013-07-10 21:33:24
Context
2013-07-10
21:33
another patch version for ssh improvement from Andy Bradford. Leaf check-in: a8c37c2c user: mgagnon tags: andy_bradford_ssh_imporvement_patch_2
2013-06-18
21:09
Version 1.26 check-in: c9cb6e72 user: drh tags: trunk, release, version-1.26
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/cgi.c.

1491
1492
1493
1494
1495
1496
1497


















  if( zIf==0 ) return;
  if( objectTime > cgi_rfc822_parsedate(zIf) ) return;
  cgi_set_status(304,"Not Modified");
  cgi_reset_content();
  cgi_reply();
  fossil_exit(0);
}

























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
  if( zIf==0 ) return;
  if( objectTime > cgi_rfc822_parsedate(zIf) ) return;
  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;
}

Changes to src/clone.c.

101
102
103
104
105
106
107

108
109
110
111
112
113
114
...
150
151
152
153
154
155
156

157
158
159
160
161
162
163
void clone_cmd(void){
  char *zPassword;
  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;

  url_proxy_options();
  if( g.argc < 4 ){
    usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
  }
  db_open_config(0);
  if( file_size(g.argv[3])>0 ){
    fossil_panic("file already exists: %s", g.argv[3]);
................................................................................
      db_set("ssl-identity", blob_str(&fn), 0);
      blob_reset(&fn);
    }
    db_multi_exec(
      "REPLACE INTO config(name,value,mtime)"
      " VALUES('server-code', lower(hex(randomblob(20))), now());"
    );

    url_enable_proxy(0);
    url_get_password_if_needed();
    g.xlinkClusterOnly = 1;
    nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
    g.xlinkClusterOnly = 0;
    verify_cancel();
    db_end_transaction(0);







>







 







>







101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
void clone_cmd(void){
  char *zPassword;
  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);
  if( file_size(g.argv[3])>0 ){
    fossil_panic("file already exists: %s", g.argv[3]);
................................................................................
      db_set("ssl-identity", blob_str(&fn), 0);
      blob_reset(&fn);
    }
    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;
    verify_cancel();
    db_end_transaction(0);

Changes to src/http.c.

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  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.urlIsSsh ){
     return;  /* If no login card for SSH: */
  }
  blob_zero(&nonce);
  blob_zero(&pw);
  sha1sum_blob(pPayload, &nonce);
  blob_copy(&pw, &nonce);
  zLogin = g.urlUser;
  if( g.urlPasswd ){







|
|







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  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.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);
  zLogin = g.urlUser;
  if( g.urlPasswd ){

Changes to src/http_transport.c.

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
238
239
240
241
242



243

244
245
246
247
248
249
250

251
252

253
254
255
256
257
258
259
...
285
286
287
288
289
290
291



292



293
294
295
296
297
298
299
...
337
338
339
340
341
342
343


344
345
346
347
348
349
350
351
352
...
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600









    pclose2(sshIn, sshOut, sshPid);
    fossil_fatal("ssh connection failed: [%s]", zIn);
  }
  fossil_free(zIn);
}

/*
** Global 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 */

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



    }

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

    transport_ssh_startup();
  }

}

/*
** COMMAND: test-ssh-far-side
**
** Read lines of input text, one by one, and evaluate each line using
** system().  The ssh: sync protocol uses this on the far side of the
................................................................................
**   g.urlIsHttps     Use TLS for the connection
**
** Return the number of errors.
*/
int transport_open(void){
  int rc = 0;
  if( transport.isOpen==0 ){



    if( g.urlIsSsh ){



      Blob cmd;
      blob_zero(&cmd);
      shell_escape(&cmd, g.urlFossil);
      blob_append(&cmd, " test-http ", -1);
      shell_escape(&cmd, g.urlPath);
      fprintf(sshOut, "%s || true\n", blob_str(&cmd));
      fflush(sshOut);
................................................................................
    transport.nAlloc = 0;
    transport.nUsed = 0;
    transport.iCursor = 0;
    if( transport.pLog ){
      fclose(transport.pLog);
      transport.pLog = 0;
    }


    if( g.urlIsSsh ){
      /* No-op */
    }else if( g.urlIsHttps ){
      #ifdef FOSSIL_ENABLE_SSL
      ssl_close();
      #endif
    }else if( g.urlIsFile ){
      if( transport.pFile ){ 
        fclose(transport.pFile);
................................................................................
    i++;
  }
  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;
  }
  if( g.urlIsHttps ){
    #ifdef FOSSIL_ENABLE_SSL
    ssl_global_shutdown();
    #endif
  }else{
    socket_global_shutdown();
  }
}
















|

|
<
|
|
|
|
|
|
>
|

|
|
|

|

|

|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
<
>
|
|
|
<
<
<
<
<
<
<
|
<
>
>
>

>
|
|
|
|
|
|
|
>


>







 







>
>
>
|
>
>
>







 







>
>
|
|







 







|
<
<
<
<
<








>
>
>
>
>
>
>
>
>
171
172
173
174
175
176
177
178
179
180

181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229

230
231
232
233







234

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
...
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
...
586
587
588
589
590
591
592
593





594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
    pclose2(sshIn, sshOut, sshPid);
    fossil_fatal("ssh connection failed: [%s]", zIn);
  }
  fossil_free(zIn);
}

/*
** SSH initialization of the transport layer
*/
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 ){
#ifdef __MINGW32__
    blob_appendf(&zCmd, " -P %d", g.urlPort);
#else
    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);
#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);
  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, 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 ){
    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
**
** Read lines of input text, one by one, and evaluate each line using
** system().  The ssh: sync protocol uses this on the far side of the
................................................................................
**   g.urlIsHttps     Use TLS for the connection
**
** Return the number of errors.
*/
int transport_open(void){
  int rc = 0;
  if( transport.isOpen==0 ){
    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);
      fprintf(sshOut, "%s || true\n", blob_str(&cmd));
      fflush(sshOut);
................................................................................
    transport.nAlloc = 0;
    transport.nUsed = 0;
    transport.iCursor = 0;
    if( transport.pLog ){
      fclose(transport.pLog);
      transport.pLog = 0;
    }
    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 ){
      if( transport.pFile ){ 
        fclose(transport.pFile);
................................................................................
    i++;
  }
  if( g.fSshTrace ) printf("Got line: [%s]\n", &transport.pBuf[iStart]);
  return &transport.pBuf[iStart];
}

void transport_global_shutdown(void){
  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;
  }
}

Changes to src/main.c.

132
133
134
135
136
137
138



139
140
141
142
143
144
145
...
576
577
578
579
580
581
582



583
584
585
586
587
588
589
...
621
622
623
624
625
626
627

628
629
630
631
632
633
634
....
1683
1684
1685
1686
1687
1688
1689



1690
1691
1692
1693
1694
1695
1696
....
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
  int fSqlTrace;          /* True if --sqltrace flag is present */
  int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
  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 */



  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 */
  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */
................................................................................
    const char *zChdir = find_option("chdir",0,1);
    g.isHTTP = 0;
    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;



    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);
    if( find_option("utc",0,0) ) g.fTimeFormat = 1;
    if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
................................................................................
    }
    fossil_print("%s: ambiguous command prefix: %s\n"
                 "%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);
  }

  atexit( fossil_atexit );
  aCommand[idx].xFunc();
  fossil_exit(0);
  /*NOT_REACHED*/
  return 0;
}

................................................................................
    g.httpOut = fossil_fopen(g.argv[4], "wb");
    zIpAddr = g.argv[5];
  }else{
    g.httpIn = stdin;
    g.httpOut = stdout;
    zIpAddr = 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));
}

/*
................................................................................
** COMMAND: test-http
** Works like the http command but gives setup permission to all users.
*/
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);
  process_one_web_page(0, 0);
}

#if !defined(_WIN32)
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
/*
** Search for an executable on the PATH environment variable.







>
>
>







 







>
>
>







 







>







 







>
>
>







 







<





|







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
...
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
...
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
....
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
....
1709
1710
1711
1712
1713
1714
1715

1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
  int fSqlTrace;          /* True if --sqltrace flag is present */
  int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
  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 */
  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */
................................................................................
    const char *zChdir = find_option("chdir",0,1);
    g.isHTTP = 0;
    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);
    if( find_option("utc",0,0) ) g.fTimeFormat = 1;
    if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
................................................................................
    }
    fossil_print("%s: ambiguous command prefix: %s\n"
                 "%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;
}

................................................................................
    g.httpOut = fossil_fopen(g.argv[4], "wb");
    zIpAddr = g.argv[5];
  }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));
}

/*
................................................................................
** COMMAND: test-http
** Works like the http command but gives setup permission to all users.
*/
void cmd_test_http(void){
  Th_InitTraceLog();
  login_set_capabilities("sx", 0);
  g.useLocalauth = 1;

  g.httpIn = stdin;
  g.httpOut = stdout;
  find_server_repository(0);
  g.cgiOutput = 1;
  g.fullHttpReply = 1;
  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__)
/*
** Search for an executable on the PATH environment variable.

Changes to src/sync.c.

145
146
147
148
149
150
151

152
153
154
155
156
157
158
...
174
175
176
177
178
179
180

181
182
183
184
185
186
187
...
212
213
214
215
216
217
218

219

220
221
222
223
224
225
226
...
255
256
257
258
259
260
261






































**
** See also: clone, push, sync, remote-url
*/
void pull_cmd(void){
  unsigned configFlags = 0;
  unsigned syncFlags = SYNC_PULL;
  process_sync_args(&configFlags, &syncFlags);

  client_sync(syncFlags, configFlags, 0);
}

/*
** COMMAND: push
**
** Usage: %fossil push ?URL? ?options?
................................................................................
**
** See also: clone, pull, sync, remote-url
*/
void push_cmd(void){
  unsigned configFlags = 0;
  unsigned syncFlags = SYNC_PUSH;
  process_sync_args(&configFlags, &syncFlags);

  if( db_get_boolean("dont-push",0) ){
    fossil_fatal("pushing is prohibited: the 'dont-push' option is set");
  }
  client_sync(syncFlags, 0, 0);
}


................................................................................
**
** See also:  clone, push, pull, remote-url
*/
void sync_cmd(void){
  unsigned configFlags = 0;
  unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
  process_sync_args(&configFlags, &syncFlags);

  if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;

  client_sync(syncFlags, configFlags, 0);
  if( (syncFlags & SYNC_PUSH)==0 ){
    fossil_warning("pull only: the 'dont-push' option is set");
  }
}

/*
................................................................................
    fossil_print("off\n");
    return;
  }else{
    url_parse(zUrl, 0);
    fossil_print("%s\n", g.urlCanonical);
  }
}













































>







 







>







 







>

>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
...
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
**
** See also: clone, push, sync, remote-url
*/
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
**
** Usage: %fossil push ?URL? ?options?
................................................................................
**
** See also: clone, pull, sync, remote-url
*/
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);
}


................................................................................
**
** See also:  clone, push, pull, remote-url
*/
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");
  }
}

/*
................................................................................
    fossil_print("off\n");
    return;
  }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);
  }
}

Changes to src/timeline.c.

271
272
273
274
275
276
277

278
279

280
281
282
283
284
285
286
...
406
407
408
409
410
411
412










413
414
415
416
417
418
419
420
421
422
423
424
425
...
850
851
852
853
854
855
856
857

858
859
860
861
862
863
864
...
877
878
879
880
881
882
883
884

885

886


887
888
889
890
891
892
893
    const char *zUuid = db_column_text(pQuery, 1);
    int isLeaf = db_column_int(pQuery, 5);
    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);

    int tagid = db_column_int(pQuery, 9);
    const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";

    const char *zBr = 0;      /* Branch */
    int commentColumn = 3;    /* Column containing comment text */
    int modPending;           /* Pending moderation */
    char zTime[8];

    modPending =  moderation_pending(rid);
    if( tagid ){
................................................................................
      @ <span class="timelineComment">%w(blob_str(&comment))</span>
    }
    blob_reset(&comment);

    /* Generate the "user: USERNAME" at the end of the comment, together
    ** with a hyperlink to another timeline for that user.
    */










    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")
    }else{
      @ (user: %h(zDispUser)%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>]
    }

................................................................................
    @   user TEXT,
    @   isleaf BOOLEAN,
    @   bgcolor TEXT,
    @   etype TEXT,
    @   taglist TEXT,
    @   tagid INTEGER,
    @   short TEXT,
    @   sortby REAL

    @ )
  ;
  db_multi_exec(zSql);
}

/*
** Return a pointer to a constant string that forms the basis
................................................................................
    @   bgcolor AS bgColor,
    @   event.type AS eventType,
    @   (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

    @  FROM event CROSS JOIN blob

    @ WHERE blob.rid=event.objid


  ;
  if( zBase==0 ){
    zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH);
  }
  return zBase;
}








>


>







 







>
>
>
>
>
>
>
>
>
>



|

|







 







|
>







 







|
>

>

>
>







271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
...
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
...
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
...
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
    const char *zUuid = db_column_text(pQuery, 1);
    int isLeaf = db_column_int(pQuery, 5);
    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];

    modPending =  moderation_pending(rid);
    if( tagid ){
................................................................................
      @ <span class="timelineComment">%w(blob_str(&comment))</span>
    }
    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)%h(zDispLogin)</a>%s(zTagList?",":"\051")
    }else{
      @ (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>]
    }

................................................................................
    @   user TEXT,
    @   isleaf BOOLEAN,
    @   bgcolor TEXT,
    @   etype TEXT,
    @   taglist TEXT,
    @   tagid INTEGER,
    @   short TEXT,
    @   sortby REAL,
    @   login TEXT
    @ )
  ;
  db_multi_exec(zSql);
}

/*
** Return a pointer to a constant string that forms the basis
................................................................................
    @   bgcolor AS bgColor,
    @   event.type AS eventType,
    @   (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,
    @   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;
}

Changes to src/url.c.

437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
...
488
489
490
491
492
493
494
495
496
497
498
499
}

/*
** 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( 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();
................................................................................
/* Preemptively prompt for a password if a username is given in the
** URL but no password.
*/
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();
  }
}







|







 







<




437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
...
488
489
490
491
492
493
494

495
496
497
498
}

/*
** 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.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);
    Blob x;
    fossil_force_newline();
................................................................................
/* Preemptively prompt for a password if a username is given in the
** URL but no password.
*/
void url_get_password_if_needed(void){
  if( (g.urlUser && g.urlUser[0])
   && (g.urlPasswd==0 || g.urlPasswd[0]==0)
   && isatty(fileno(stdin)) 

  ){
    url_prompt_for_password();
  }
}

Changes to src/xfer.c.

1334
1335
1336
1337
1338
1339
1340

1341
1342
1343
1344
1345
1346
1347
....
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
....
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
  int nArtifactRcvd = 0;  /* Total artifacts received */
  const char *zOpType = 0;/* Push, Pull, Sync, Clone */

  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;


  transport_stats(0, 0, 1);
  socket_global_init();
  memset(&xfer, 0, sizeof(xfer));
  xfer.pIn = &recv;
  xfer.pOut = &send;
  xfer.mxSend = db_get_int("max-upload", 250000);
  xfer.maxTime = -1;
................................................................................
  }
  if( syncFlags & SYNC_PUSH ){
    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 ){
    int newPhantom = 0;
    char *zRandomness;
................................................................................
    nCardSent = 0;
    nCardRcvd = 0;
    xfer.nFileSent = 0;
    xfer.nDeltaSent = 0;
    xfer.nGimmeSent = 0;
    xfer.nIGotSent = 0;
    if( syncFlags & SYNC_VERBOSE ){
      fossil_print("waiting for server...");
    }
    fflush(stdout);
    if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
        MAX_REDIRECTS) ){
      nErr++;
      break;
    }







>







 







<







 







|







1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
....
1386
1387
1388
1389
1390
1391
1392

1393
1394
1395
1396
1397
1398
1399
....
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
  int nArtifactRcvd = 0;  /* Total artifacts received */
  const char *zOpType = 0;/* Push, Pull, Sync, Clone */

  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;
  xfer.mxSend = db_get_int("max-upload", 250000);
  xfer.maxTime = -1;
................................................................................
  }
  if( syncFlags & SYNC_PUSH ){
    blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
    nCardSent++;
    if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
  }
  manifest_crosslink_begin();

  if( syncFlags & SYNC_VERBOSE ){
    fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
  }

  while( go ){
    int newPhantom = 0;
    char *zRandomness;
................................................................................
    nCardSent = 0;
    nCardRcvd = 0;
    xfer.nFileSent = 0;
    xfer.nDeltaSent = 0;
    xfer.nGimmeSent = 0;
    xfer.nIGotSent = 0;
    if( syncFlags & SYNC_VERBOSE ){
      fossil_print("waiting for server...\n");
    }
    fflush(stdout);
    if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0,
        MAX_REDIRECTS) ){
      nErr++;
      break;
    }