Fossil

Check-in [652f20ef]
Login

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

Overview
Comment:Update the SSL branch to include all the latest changes.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | ssl
Files: files | file ages | folders
SHA1: 652f20ef9c55a6972485532d8110a0876f311e83
User & Date: drh 2010-01-21 22:03:24
References
2010-01-22
16:08 Ticket [0e3f8719] ssl-enabled fossil cannot log in to server status still Open with 1 other change artifact: c23da96e user: drh
06:36 Open ticket [0e3f8719]. artifact: bb5abf2e user: ron
2010-01-21
22:08 Fixed ticket [0e3f8719]. artifact: 65251cb8 user: drh
Context
2010-01-24
22:35
Pull in the latest changes from trunk. check-in: 97311bd9 user: drh tags: ssl
2010-01-21
22:03
Update the SSL branch to include all the latest changes. check-in: 652f20ef user: drh tags: ssl
20:53
Merge experimental remote-url password handling changes into the trunk. check-in: a3c97c90 user: drh tags: trunk, release
20:18
fix compile error check-in: 5fc80efd user: ron tags: ssl
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Makefile.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#### The suffix to add to executable files.  ".exe" for windows.
#    Nothing for unix.
#
E =

#### Enable HTTPS support via OpenSSL (links to libssl and libcrypto)
#
# FOSSIL_ENABLE_SSL=1

#### C Compile and options for use in building executables that 
#    will run on the target platform.  This is usually the same
#    as BCC, unless you are cross-compiling.  This C compiler builds
#    the finished binary for fossil.  The BCC compiler above is used
#    for building intermediate code-generator tools.
#







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#### The suffix to add to executable files.  ".exe" for windows.
#    Nothing for unix.
#
E =

#### Enable HTTPS support via OpenSSL (links to libssl and libcrypto)
#
FOSSIL_ENABLE_SSL=1

#### C Compile and options for use in building executables that 
#    will run on the target platform.  This is usually the same
#    as BCC, unless you are cross-compiling.  This C compiler builds
#    the finished binary for fossil.  The BCC compiler above is used
#    for building intermediate code-generator tools.
#

Changes to src/add.c.

50
51
52
53
54
55
56





57
58


59
60
61
62
63
64
65
66
...
140
141
142
143
144
145
146






147
148
149
150
151
152
153
   || blob_compare(&pathname, pOmit)==0
  ){
    fossil_warning("cannot add %s", zPath);
  }else{
    if( !file_is_simple_pathname(zPath) ){
      fossil_fatal("filename contains illegal characters: %s", zPath);
    }





    if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){
      db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath);


    }else{
      db_multi_exec(
        "INSERT INTO vfile(vid,deleted,rid,mrid,pathname)"
        "VALUES(%d,0,0,0,%Q)", vid, zPath);
    }
    printf("ADDED  %s\n", zPath);
  }
  blob_reset(&pathname);
................................................................................
    fossil_panic("no checkout to add to");
  }
  db_begin_transaction();
  if( !file_tree_name(g.zRepositoryName, &repo, 0) ){
    blob_zero(&repo);
  }
  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");






  for(i=2; i<g.argc; i++){
    char *zName;
    int isDir;

    zName = mprintf("%/", g.argv[i]);
    isDir = file_isdir(zName);
    if( isDir==1 ){







>
>
>
>
>


>
>
|







 







>
>
>
>
>
>







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
   || blob_compare(&pathname, pOmit)==0
  ){
    fossil_warning("cannot add %s", zPath);
  }else{
    if( !file_is_simple_pathname(zPath) ){
      fossil_fatal("filename contains illegal characters: %s", zPath);
    }
#ifdef __MINGW32__
    if( db_exists("SELECT 1 FROM vfile WHERE pathname LIKE %Q", zPath) ){
      db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname LIKE %Q", zPath);
    }
#else
    if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){
      db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath);
    }
#endif
    else{
      db_multi_exec(
        "INSERT INTO vfile(vid,deleted,rid,mrid,pathname)"
        "VALUES(%d,0,0,0,%Q)", vid, zPath);
    }
    printf("ADDED  %s\n", zPath);
  }
  blob_reset(&pathname);
................................................................................
    fossil_panic("no checkout to add to");
  }
  db_begin_transaction();
  if( !file_tree_name(g.zRepositoryName, &repo, 0) ){
    blob_zero(&repo);
  }
  db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
#ifdef __MINGW32__
  db_multi_exec(
     "CREATE INDEX IF NOT EXISTS vfile_pathname "
     "  ON vfile(pathname COLLATE nocase)"
  );
#endif
  for(i=2; i<g.argc; i++){
    char *zName;
    int isDir;

    zName = mprintf("%/", g.argv[i]);
    isDir = file_isdir(zName);
    if( isDir==1 ){

Changes to src/configure.c.

443
444
445
446
447
448
449

450
451
452
453
454
455
456


457
458
459
460
461

462
463

464
465
466
467
468
469
470
    db_multi_exec("%s", blob_str(&in));
    configure_finalize_receive();
    db_end_transaction(0);
  }else
  if( strncmp(zMethod, "pull", n)==0 || strncmp(zMethod, "push", n)==0 ){
    int mask;
    const char *zServer;

    url_proxy_options();
    if( g.argc!=4 && g.argc!=5 ){
      usage("pull AREA ?URL?");
    }
    mask = find_area(g.argv[3]);
    if( g.argc==5 ){
      zServer = g.argv[4];


    }else{
      zServer = db_get("last-sync-url", 0);
      if( zServer==0 ){
        fossil_fatal("no server specified");
      }

    }
    url_parse(zServer);

    user_select();
    if( strncmp(zMethod, "push", n)==0 ){
      client_sync(0,0,0,0,mask);
    }else{
      client_sync(0,0,0,mask,0);
    }
  }else







>







>
>





>


>







443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
    db_multi_exec("%s", blob_str(&in));
    configure_finalize_receive();
    db_end_transaction(0);
  }else
  if( strncmp(zMethod, "pull", n)==0 || strncmp(zMethod, "push", n)==0 ){
    int mask;
    const char *zServer;
    const char *zPw;
    url_proxy_options();
    if( g.argc!=4 && g.argc!=5 ){
      usage("pull AREA ?URL?");
    }
    mask = find_area(g.argv[3]);
    if( g.argc==5 ){
      zServer = g.argv[4];
      zPw = 0;
      g.dontKeepUrl = 1;
    }else{
      zServer = db_get("last-sync-url", 0);
      if( zServer==0 ){
        fossil_fatal("no server specified");
      }
      zPw = db_get("last-sync-pw", 0);
    }
    url_parse(zServer);
    if( g.urlPasswd==0 && zPw ) g.urlPasswd = mprintf("%s", zPw);
    user_select();
    if( strncmp(zMethod, "push", n)==0 ){
      client_sync(0,0,0,0,mask);
    }else{
      client_sync(0,0,0,mask,0);
    }
  }else

Changes to src/db.c.

1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
**
**    localauth        If enabled, require that HTTP connections from
**                     127.0.0.1 be authenticated by password.  If
**                     false, all HTTP requests from localhost have
**                     unrestricted access to the repository.
**
**    mtime-changes    Use file modification times (mtimes) to detect when
**                     files have been modified.  
**
**    pgp-command      Command used to clear-sign manifests at check-in.
**                     The default is "gpg --clearsign -o ".
**
**    proxy            URL of the HTTP proxy.  If undefined or "off" then
**                     the "http_proxy" environment variable is consulted.
**                     If the http_proxy environment variable is undefined







|







1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
**
**    localauth        If enabled, require that HTTP connections from
**                     127.0.0.1 be authenticated by password.  If
**                     false, all HTTP requests from localhost have
**                     unrestricted access to the repository.
**
**    mtime-changes    Use file modification times (mtimes) to detect when
**                     files have been modified.  (Default "on".)
**
**    pgp-command      Command used to clear-sign manifests at check-in.
**                     The default is "gpg --clearsign -o ".
**
**    proxy            URL of the HTTP proxy.  If undefined or "off" then
**                     the "http_proxy" environment variable is consulted.
**                     If the http_proxy environment variable is undefined

Changes to src/file.c.

25
26
27
28
29
30
31





32
33
34



35

36
37
38



39


40
41
42









43
44
45
46
47

48
49
50

































51
52






53
54

55
56
57
58
59
60
61
..
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "file.h"







/*
** Return the size of a file in bytes.  Return -1 if the file does not



** exist.

*/
i64 file_size(const char *zFilename){
  struct stat buf;



  if( stat(zFilename, &buf)!=0 ){


    return -1;
  }
  return buf.st_size;









}

/*
** Return the modification time for a file.  Return -1 if the file
** does not exist.

*/
i64 file_mtime(const char *zFilename){
  struct stat buf;

































  if( stat(zFilename, &buf)!=0 ){
    return -1;






  }
  return buf.st_mtime;

}

/*
** Return the tail of a file pathname.  The tail is the last component
** of the path.  For example, the tail of "/a/b/c.d" is "c.d".
*/
const char *file_tail(const char *z){
................................................................................
  while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ){
    fwrite(zBuf, 1, got, out);
  }
  fclose(in);
  fclose(out);
}

/*
** Return TRUE if the named file is an ordinary file.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isfile(const char *zFilename){
  struct stat buf;
  if( stat(zFilename, &buf)!=0 ){
    return 0;
  }
  return S_ISREG(buf.st_mode);
}

/*
** Return TRUE if the named file is an executable.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isexe(const char *zFilename){
  struct stat buf;
  if( stat(zFilename, &buf)!=0 ){
    return 0;
  }
  if( !S_ISREG(buf.st_mode) ) return 0;
#ifdef __MINGW32__
  return ((S_IXUSR)&buf.st_mode)!=0;
#else
  return ((S_IXUSR|S_IXGRP|S_IXOTH)&buf.st_mode)!=0;
#endif
}

/*
** Set or clear the execute bit on a file.
*/
void file_setexe(const char *zFilename, int onoff){
#ifndef __MINGW32__
  struct stat buf;
  if( stat(zFilename, &buf)!=0 ) return;
................................................................................
    if( (buf.st_mode & 0111)!=0 ){
      chmod(zFilename, buf.st_mode & ~0111);
    }
  }
#endif
}

/*
** Return 1 if zFilename is a directory.  Return 0 if zFilename
** does not exist.  Return 2 if zFilename exists but is something
** other than a directory.
*/
int file_isdir(const char *zFilename){
  struct stat buf;
  int rc;
  char *zFN;

  zFN = mprintf("%s", zFilename);
  file_simplify_name(zFN, strlen(zFN));
  rc = stat(zFN, &buf);
  free(zFN);
  return rc!=0 ? 0 : (S_ISDIR(buf.st_mode) ? 1 : 2);
}

/*
** Create the directory named in the argument, if it does not already
** exist.  If forceFlag is 1, delete any prior non-directory object 
** with the same name.
**
** Return the number of errors.
*/







>
>
>
>
>


<
>
>
>
|
>

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




|
>


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

<
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







25
26
27
28
29
30
31
32
33
34
35
36
37
38

39
40
41
42
43
44
45

46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

105
106
107
108
109
110
111

112
113
114
115
116
117
118
119
...
139
140
141
142
143
144
145





























146
147
148
149
150
151
152
...
158
159
160
161
162
163
164

















165
166
167
168
169
170
171
*/
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "file.h"

/*
** The file status information from the most recent stat() call.
*/
static struct stat fileStat;
static int fileStatValid = 0;

/*

** Fill in the fileStat variable for the file named zFilename.
** If zFilename==0, then use the previous value of fileStat if
** there is a previous value.
**
** Return the number of errors.  No error messages are generated.
*/
static int getStat(const char *zFilename){

  if( zFilename==0 ){
    if( fileStatValid==0 ) return 1;
  }else{
    if( stat(zFilename, &fileStat)!=0 ) return 1;
    fileStatValid = 1;
  }
  return 0;
}



/*
** Return the size of a file in bytes.  Return -1 if the file does not
** exist.  If zFilename is NULL, return the size of the most recently
** stat-ed file.
*/
i64 file_size(const char *zFilename){
  return getStat(zFilename) ? -1 : fileStat.st_size;
}

/*
** Return the modification time for a file.  Return -1 if the file
** does not exist.  If zFilename is NULL return the size of the most
** recently stat-ed file.
*/
i64 file_mtime(const char *zFilename){

  return getStat(zFilename) ? -1 : fileStat.st_mtime;
}

/*
** Return TRUE if the named file is an ordinary file.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isfile(const char *zFilename){
  return getStat(zFilename) ? 0 : S_ISREG(fileStat.st_mode);
}

/*
** Return TRUE if the named file is an executable.  Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isexe(const char *zFilename){
  if( getStat(zFilename) || !S_ISREG(fileStat.st_mode) ) return 0;
#ifdef __MINGW32__
  return ((S_IXUSR)&fileStat.st_mode)!=0;
#else
  return ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0;
#endif
}


/*
** Return 1 if zFilename is a directory.  Return 0 if zFilename
** does not exist.  Return 2 if zFilename exists but is something
** other than a directory.
*/
int file_isdir(const char *zFilename){
  int rc;

  if( zFilename ){

    char *zFN = mprintf("%s", zFilename);
    file_simplify_name(zFN, strlen(zFN));
    rc = getStat(zFN);
    free(zFN);
  }else{
    rc = getStat(0);
  }

  return rc ? 0 : (S_ISDIR(fileStat.st_mode) ? 1 : 2);
}

/*
** Return the tail of a file pathname.  The tail is the last component
** of the path.  For example, the tail of "/a/b/c.d" is "c.d".
*/
const char *file_tail(const char *z){
................................................................................
  while( (got=fread(zBuf, 1, sizeof(zBuf), in))>0 ){
    fwrite(zBuf, 1, got, out);
  }
  fclose(in);
  fclose(out);
}






























/*
** Set or clear the execute bit on a file.
*/
void file_setexe(const char *zFilename, int onoff){
#ifndef __MINGW32__
  struct stat buf;
  if( stat(zFilename, &buf)!=0 ) return;
................................................................................
    if( (buf.st_mode & 0111)!=0 ){
      chmod(zFilename, buf.st_mode & ~0111);
    }
  }
#endif
}


















/*
** Create the directory named in the argument, if it does not already
** exist.  If forceFlag is 1, delete any prior non-directory object 
** with the same name.
**
** Return the number of errors.
*/

Changes to src/http.c.

42
43
44
45
46
47
48




49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71

72
73
74
75
76
77
78
static void http_build_login_card(Blob *pPayload, Blob *pLogin){
  Blob nonce;          /* The nonce */
  const char *zLogin;  /* The user login name */
  const char *zPw;     /* The user password */
  Blob pw;             /* The nonce with user password appended */
  Blob sig;            /* The signature field */





  blob_zero(&nonce);
  blob_zero(&pw);
  sha1sum_blob(pPayload, &nonce);
  blob_copy(&pw, &nonce);
  blob_zero(pLogin);
  if( g.urlUser==0 ){
    user_select();
    zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", g.userUid);
    zLogin = g.zLogin;
  }else{
    if( g.urlPasswd==0 ){
      if( strcmp(g.urlUser,"anonymous")!=0 ){
        char *zPrompt = mprintf("password for %s: ", g.urlUser);
        Blob x;
        prompt_for_password(zPrompt, &x, 0);
        free(zPrompt);
        g.urlPasswd = blob_str(&x);
      }else{
        g.urlPasswd = "";
      }
    }

    zPw = g.urlPasswd;
    zLogin = g.urlUser;

  }

  /* The login card wants the SHA1 hash of the password, so convert the
  ** password to its SHA1 hash it it isn't already a SHA1 hash.
  **
  ** Except, if the password begins with "*" then use the characters
  ** after the "*" as a cleartext password.  Put an "*" at the beginning







>
>
>
>




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

<
>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56




57

58





59
60



61
62

63
64
65
66
67
68
69
70
static void http_build_login_card(Blob *pPayload, Blob *pLogin){
  Blob nonce;          /* The nonce */
  const char *zLogin;  /* The user login name */
  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 || 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;

  if( g.urlPasswd ){





    zPw = g.urlPasswd;
  }else{



    url_prompt_for_password();
    zPw = g.urlPasswd;

    if( !g.dontKeepUrl ) db_set("last-sync-pw", zPw, 0);
  }

  /* The login card wants the SHA1 hash of the password, so convert the
  ** password to its SHA1 hash it it isn't already a SHA1 hash.
  **
  ** Except, if the password begins with "*" then use the characters
  ** after the "*" as a cleartext password.  Put an "*" at the beginning

Changes to src/main.c.

99
100
101
102
103
104
105

106
107
108
109
110
111
112
  int urlPort;            /* TCP port number for http: or https: */
  int urlDfltPort;        /* The default port for the given protocol */
  char *urlPath;          /* Pathname for http: */
  char *urlUser;          /* User id for http: */
  char *urlPasswd;        /* Password for http: */
  char *urlCanonical;     /* Canonical representation of the URL */
  char *urlProxyAuth;     /* Proxy-Authorizer: string */


  const char *zLogin;     /* Login name.  "" if not logged in. */
  int noPswd;             /* Logged in without password (on 127.0.0.1) */
  int userUid;            /* Integer user id */

  /* Information used to populate the RCVFROM table */
  int rcvid;              /* The rcvid.  0 if not yet defined. */







>







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  int urlPort;            /* TCP port number for http: or https: */
  int urlDfltPort;        /* The default port for the given protocol */
  char *urlPath;          /* Pathname for http: */
  char *urlUser;          /* User id for http: */
  char *urlPasswd;        /* Password for http: */
  char *urlCanonical;     /* Canonical representation of the URL */
  char *urlProxyAuth;     /* Proxy-Authorizer: string */
  int dontKeepUrl;        /* Do not persist the URL */

  const char *zLogin;     /* Login name.  "" if not logged in. */
  int noPswd;             /* Logged in without password (on 127.0.0.1) */
  int userUid;            /* Integer user id */

  /* Information used to populate the RCVFROM table */
  int rcvid;              /* The rcvid.  0 if not yet defined. */

Changes to src/rebuild.c.

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
    if( blob_str(&ans)[0]!='y' ){
      exit(1);
    }
  }
  db_begin_transaction();
  db_multi_exec(
    "UPDATE user SET pw='';"
    "DELETE FROM config WHERE name='last-sync-url';"
  );
  if( bVerily ){
    bNeedRebuild = db_exists("SELECT 1 FROM private");
    db_multi_exec(
      "DELETE FROM concealed;"
      "UPDATE rcvfrom SET ipaddr='unknown';"
      "UPDATE user SET photo=NULL, info='';"







|







382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
    if( blob_str(&ans)[0]!='y' ){
      exit(1);
    }
  }
  db_begin_transaction();
  db_multi_exec(
    "UPDATE user SET pw='';"
    "DELETE FROM config WHERE name GLOB 'last-sync-*';"
  );
  if( bVerily ){
    bNeedRebuild = db_exists("SELECT 1 FROM private");
    db_multi_exec(
      "DELETE FROM concealed;"
      "UPDATE rcvfrom SET ipaddr='unknown';"
      "UPDATE user SET photo=NULL, info='';"

Changes to src/sync.c.

40
41
42
43
44
45
46

47
48
49
50
51
52
53
..
58
59
60
61
62
63
64

65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80
81
82
83

84
85
86
87
88
89

90
91
92
93
94
95
96
97
98
99




100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
...
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

** If the respository is configured for autosyncing, then do an
** autosync.  This will be a pull if the argument is true or a push
** if the argument is false.
*/
void autosync(int flags){
  const char *zUrl;
  const char *zAutosync;

  if( g.fNoSync ){
    return;
  }
  zAutosync = db_get("autosync", 0);
  if( zAutosync ){
    if( (flags & AUTOSYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
      return;   /* Do not auto-push when autosync=pullonly */
................................................................................
  }else{
    /* Autosync defaults on.  To make it default off, "return" here. */
  }
  zUrl = db_get("last-sync-url", 0);
  if( zUrl==0 ){
    return;  /* No default server */
  }

  url_parse(zUrl);
  if( g.urlPort!=g.urlDfltPort ){
    printf("Autosync:  %s://%s:%d%s\n", 
            g.urlProtocol, g.urlName, g.urlPort, g.urlPath);
  }else{
    printf("Autosync:  %s://%s%s\n", g.urlProtocol, g.urlName, g.urlPath);
  }

  url_enable_proxy("via proxy: ");
  client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, 0, 0);
}

/*
** This routine processes the command-line argument for push, pull,
** and sync.  If a command-line argument is given, that is the URL
** of a server to sync against.  If no argument is given, use the
** most recently synced URL.  Remember the current URL for next time.
*/
void process_sync_args(void){
  const char *zUrl = 0;

  int urlOptional = find_option("autourl",0,0)!=0;
  int dontKeepUrl = find_option("once",0,0)!=0;
  url_proxy_options();
  db_find_and_open_repository(1);
  if( g.argc==2 ){
    zUrl = db_get("last-sync-url", 0);

  }else if( g.argc==3 ){
    zUrl = g.argv[2];
  }
  if( zUrl==0 ){
    if( urlOptional ) exit(0);
    usage("URL");
  }
  url_parse(zUrl);
  if( !dontKeepUrl ){
    db_set("last-sync-url", g.urlIsFile ? g.urlCanonical : zUrl, 0);




  }
  user_select();
  if( g.argc==2 ){
    if( g.urlPort!=g.urlDfltPort ){
      printf("Server:    %s://%s:%d%s\n",
              g.urlProtocol, g.urlName, g.urlPort, g.urlPath);
    }else{
      printf("Server:    %s://%s%s\n", g.urlProtocol, g.urlName, g.urlPath);
    }
  }
  url_enable_proxy("via proxy: ");
}

/*
** COMMAND: pull
**
................................................................................
  process_sync_args();
  client_sync(1,1,0,0,0);
}

/*
** COMMAND: remote-url
**
** Usage: %fossil remote-url ?URL|off? --show-pw
**
** Query and/or change the default server URL used by the "pull", "push",
** and "sync" commands.
**
** The userid and password are of the URL are not printed unless
** the --show-pw option is used on the command-line.
**
** The remote-url is set automatically by a "clone" command or by any
** "sync", "push", or "pull" command that specifies an explicit URL.
** The default remote-url is used by auto-syncing and by "sync", "push",
** "pull" that omit the server URL.
**
** See also: clone, push, pull, sync
*/
void remote_url_cmd(void){
  char *zUrl;
  int showPw = find_option("show-pw",0,0)!=0;
  db_find_and_open_repository(1);
  if( g.argc!=2 && g.argc!=3 ){
    usage("remote-url ?URL|off?");
  }
  if( g.argc==3 ){
    if( strcmp(g.argv[2],"off")==0 ){
      db_unset("last-sync-url", 0);

    }else{
      url_parse(g.argv[2]);



      db_set("last-sync-url", g.urlIsFile ? g.urlCanonical : g.argv[2], 0);





    }
  }
  zUrl = db_get("last-sync-url", 0);
  if( zUrl==0 ){
    printf("off\n");
    return;
  }else if( showPw ){
    g.urlCanonical = zUrl;
  }else{
    url_parse(zUrl);
  }
  printf("%s\n", g.urlCanonical);
}








>







 







>

<
|
|
<
<

>












>

|




>








|
|
>
>
>
>



<
|
<
<
<
<







 







|




<
<
<









<







>


>
>
>
|
>
>
>
>
>






<
<


<
|
|
>
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
..
59
60
61
62
63
64
65
66
67

68
69


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

109




110
111
112
113
114
115
116
...
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
** If the respository is configured for autosyncing, then do an
** autosync.  This will be a pull if the argument is true or a push
** if the argument is false.
*/
void autosync(int flags){
  const char *zUrl;
  const char *zAutosync;
  const char *zPw;
  if( g.fNoSync ){
    return;
  }
  zAutosync = db_get("autosync", 0);
  if( zAutosync ){
    if( (flags & AUTOSYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
      return;   /* Do not auto-push when autosync=pullonly */
................................................................................
  }else{
    /* Autosync defaults on.  To make it default off, "return" here. */
  }
  zUrl = db_get("last-sync-url", 0);
  if( zUrl==0 ){
    return;  /* No default server */
  }
  zPw = db_get("last-sync-pw", 0);
  url_parse(zUrl);

  if( g.urlUser!=0 && g.urlPasswd==0 ){
    g.urlPasswd = mprintf("%s", zPw);


  }
  printf("Autosync:  %s\n", g.urlCanonical);
  url_enable_proxy("via proxy: ");
  client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, 0, 0);
}

/*
** This routine processes the command-line argument for push, pull,
** and sync.  If a command-line argument is given, that is the URL
** of a server to sync against.  If no argument is given, use the
** most recently synced URL.  Remember the current URL for next time.
*/
void process_sync_args(void){
  const char *zUrl = 0;
  const char *zPw = 0;
  int urlOptional = find_option("autourl",0,0)!=0;
  g.dontKeepUrl = find_option("once",0,0)!=0;
  url_proxy_options();
  db_find_and_open_repository(1);
  if( g.argc==2 ){
    zUrl = db_get("last-sync-url", 0);
    zPw = db_get("last-sync-pw", 0);
  }else if( g.argc==3 ){
    zUrl = g.argv[2];
  }
  if( zUrl==0 ){
    if( urlOptional ) exit(0);
    usage("URL");
  }
  url_parse(zUrl);
  if( !g.dontKeepUrl ){
    db_set("last-sync-url", g.urlCanonical, 0);
    if( g.urlPasswd ) db_set("last-sync-pw", g.urlPasswd, 0);
  }
  if( g.urlUser!=0 && g.urlPasswd==0 ){
    g.urlPasswd = mprintf("%s", zPw);
  }
  user_select();
  if( g.argc==2 ){

    printf("Server:    %s\n", g.urlCanonical);




  }
  url_enable_proxy("via proxy: ");
}

/*
** COMMAND: pull
**
................................................................................
  process_sync_args();
  client_sync(1,1,0,0,0);
}

/*
** COMMAND: remote-url
**
** Usage: %fossil remote-url ?URL|off?
**
** Query and/or change the default server URL used by the "pull", "push",
** and "sync" commands.
**



** The remote-url is set automatically by a "clone" command or by any
** "sync", "push", or "pull" command that specifies an explicit URL.
** The default remote-url is used by auto-syncing and by "sync", "push",
** "pull" that omit the server URL.
**
** See also: clone, push, pull, sync
*/
void remote_url_cmd(void){
  char *zUrl;

  db_find_and_open_repository(1);
  if( g.argc!=2 && g.argc!=3 ){
    usage("remote-url ?URL|off?");
  }
  if( g.argc==3 ){
    if( strcmp(g.argv[2],"off")==0 ){
      db_unset("last-sync-url", 0);
      db_unset("last-sync-pw", 0);
    }else{
      url_parse(g.argv[2]);
      if( g.urlUser && g.urlPasswd==0 ){
        url_prompt_for_password();
      }
      db_set("last-sync-url", g.urlCanonical, 0);
      if( g.urlPasswd ){
        db_set("last-sync-pw", g.urlPasswd, 0);
      }else{
        db_unset("last-sync-pw", 0);
      }
    }
  }
  zUrl = db_get("last-sync-url", 0);
  if( zUrl==0 ){
    printf("off\n");
    return;


  }else{
    url_parse(zUrl);

    printf("%s\n", g.urlCanonical);
  }
}

Changes to src/url.c.

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
..
71
72
73
74
75
76
77

78
79
80

81
82
83
84
85
86
87
..
92
93
94
95
96
97
98
99

100

101
102

103

104

105
106
107
108
109
110
111
...
294
295
296
297
298
299
300













**      g.urlName        Hostname for HTTP: or HTTPS:.  Filename for FILE:
**      g.urlPort        TCP port number for HTTP or HTTPS.
**      g.urlDfltPort    Default TCP port number (80 or 443).
**      g.urlPath        Path name for HTTP or HTTPS.
**      g.urlUser        Userid.
**      g.urlPasswd      Password.
**      g.urlHostname    HOST:PORT or just HOST if port is the default.
**      g.urlCanonical   The URL in canonical form, omitting userid/password
**
** HTTP url format is:
**
**     http://userid:password@host:port/path?query#fragment
**
*/
void url_parse(const char *zUrl){
  int i, j, c;
  char *zFile = 0;
  if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 ){
    int iStart;

    g.urlIsFile = 0;
    if( zUrl[4]=='s' ){
      g.urlIsHttps = 1;
      g.urlProtocol = "https";
      g.urlDfltPort = 443;
      iStart = 8;
    }else{
................................................................................
      if( j<i ){
        g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
        dehttpize(g.urlPasswd);
      }
      for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
      g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]);
      i = j;

    }else{
      for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){}
      g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]);

    }
    for(j=0; g.urlName[j]; j++){ g.urlName[j] = tolower(g.urlName[j]); }
    if( c==':' ){
      g.urlPort = 0;
      i++;
      while( (c = zUrl[i])!=0 && isdigit(c) ){
        g.urlPort = g.urlPort*10 + c - '0';
................................................................................
      g.urlPort = g.urlDfltPort;
      g.urlHostname = g.urlName;
    }
    g.urlPath = mprintf(&zUrl[i]);
    dehttpize(g.urlName);
    dehttpize(g.urlPath);
    if( g.urlDfltPort==g.urlPort ){
      g.urlCanonical = mprintf("%s://%T%T",

                               g.urlProtocol, g.urlName, g.urlPath);

    }else{
      g.urlCanonical = mprintf("%s://%T:%d%T",

                               g.urlProtocol, g.urlName, g.urlPort, g.urlPath);

    }

  }else if( strncmp(zUrl, "file:", 5)==0 ){
    g.urlIsFile = 1;
    if( zUrl[5]=='/' && zUrl[6]=='/' ){
      i = 7;
    }else{
      i = 5;
    }
................................................................................
    blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1);
  }
  if( zName2 && zValue2 ){
    blob_appendf(&p->url, "%s%s=%T", zSep, zName2, zValue2);
  }
  return blob_str(&p->url);
}




















|











>







 







>



>







 







|
>
|
>

|
>
|
>

>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
..
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
**      g.urlName        Hostname for HTTP: or HTTPS:.  Filename for FILE:
**      g.urlPort        TCP port number for HTTP or HTTPS.
**      g.urlDfltPort    Default TCP port number (80 or 443).
**      g.urlPath        Path name for HTTP or HTTPS.
**      g.urlUser        Userid.
**      g.urlPasswd      Password.
**      g.urlHostname    HOST:PORT or just HOST if port is the default.
**      g.urlCanonical   The URL in canonical form, omitting the password
**
** HTTP url format is:
**
**     http://userid:password@host:port/path?query#fragment
**
*/
void url_parse(const char *zUrl){
  int i, j, c;
  char *zFile = 0;
  if( strncmp(zUrl, "http://", 7)==0 || strncmp(zUrl, "https://", 8)==0 ){
    int iStart;
    char *zLogin;
    g.urlIsFile = 0;
    if( zUrl[4]=='s' ){
      g.urlIsHttps = 1;
      g.urlProtocol = "https";
      g.urlDfltPort = 443;
      iStart = 8;
    }else{
................................................................................
      if( j<i ){
        g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
        dehttpize(g.urlPasswd);
      }
      for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){}
      g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]);
      i = j;
      zLogin = mprintf("%t@", g.urlUser);
    }else{
      for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){}
      g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]);
      zLogin = mprintf("");
    }
    for(j=0; g.urlName[j]; j++){ g.urlName[j] = tolower(g.urlName[j]); }
    if( c==':' ){
      g.urlPort = 0;
      i++;
      while( (c = zUrl[i])!=0 && isdigit(c) ){
        g.urlPort = g.urlPort*10 + c - '0';
................................................................................
      g.urlPort = g.urlDfltPort;
      g.urlHostname = g.urlName;
    }
    g.urlPath = mprintf(&zUrl[i]);
    dehttpize(g.urlName);
    dehttpize(g.urlPath);
    if( g.urlDfltPort==g.urlPort ){
      g.urlCanonical = mprintf(
        "%s://%s%T%T", 
        g.urlProtocol, zLogin, g.urlName, g.urlPath
      );
    }else{
      g.urlCanonical = mprintf(
        "%s://%s%T:%d%T",
        g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath
      );
    }
    free(zLogin);
  }else if( strncmp(zUrl, "file:", 5)==0 ){
    g.urlIsFile = 1;
    if( zUrl[5]=='/' && zUrl[6]=='/' ){
      i = 7;
    }else{
      i = 5;
    }
................................................................................
    blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1);
  }
  if( zName2 && zValue2 ){
    blob_appendf(&p->url, "%s%s=%T", zSep, zName2, zValue2);
  }
  return blob_str(&p->url);
}

/*
** Prompt the user for the password for g.urlUser.  Store the result
** in g.urlPasswd.
*/
void url_prompt_for_password(void){
  char *zPrompt = mprintf("password for %s: ", g.urlUser);
  Blob x;
  prompt_for_password(zPrompt, &x, 0);
  free(zPrompt);
  g.urlPasswd = mprintf("%b", &x);
  blob_reset(&x);
}

Changes to src/vfile.c.

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

185
186
187
188
189
190
191
** If VFILE.DELETED is null or if VFILE.RID is zero, then we can assume
** the file has changed without having the check the on-disk image.
*/
void vfile_check_signature(int vid, int notFileIsFatal){
  int nErr = 0;
  Stmt q;
  Blob fileCksum, origCksum;
  int checkMtime = db_get_boolean("mtime-changes", 0);

  db_begin_transaction();
  db_prepare(&q, "SELECT id, %Q || pathname,"
                 "       vfile.mrid, deleted, chnged, uuid, mtime"
                 "  FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid"
                 " WHERE vid=%d ", g.zLocalRoot, vid);
  while( db_step(&q)==SQLITE_ROW ){
................................................................................

    id = db_column_int(&q, 0);
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 2);
    isDeleted = db_column_int(&q, 3);
    oldChnged = db_column_int(&q, 4);
    oldMtime = db_column_int64(&q, 6);
    if( !file_isfile(zName) && file_size(zName)>=0 ){
      if( notFileIsFatal ){
        fossil_warning("not a ordinary file: %s", zName);
        nErr++;
      }
      chnged = 1;
    }else if( oldChnged>=2 ){
      chnged = oldChnged;
    }else if( isDeleted || rid==0 ){
      chnged = 1;
    }
    if( chnged!=1 ){
      currentMtime = file_mtime(zName);

    }
    if( chnged!=1 && (checkMtime==0 || currentMtime!=oldMtime) ){
      db_ephemeral_blob(&q, 5, &origCksum);
      if( sha1sum_file(zName, &fileCksum) ){
        blob_zero(&fileCksum);
      }
      if( blob_compare(&fileCksum, &origCksum) ){







|







 







|











|
>







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
** If VFILE.DELETED is null or if VFILE.RID is zero, then we can assume
** the file has changed without having the check the on-disk image.
*/
void vfile_check_signature(int vid, int notFileIsFatal){
  int nErr = 0;
  Stmt q;
  Blob fileCksum, origCksum;
  int checkMtime = db_get_boolean("mtime-changes", 1);

  db_begin_transaction();
  db_prepare(&q, "SELECT id, %Q || pathname,"
                 "       vfile.mrid, deleted, chnged, uuid, mtime"
                 "  FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid"
                 " WHERE vid=%d ", g.zLocalRoot, vid);
  while( db_step(&q)==SQLITE_ROW ){
................................................................................

    id = db_column_int(&q, 0);
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 2);
    isDeleted = db_column_int(&q, 3);
    oldChnged = db_column_int(&q, 4);
    oldMtime = db_column_int64(&q, 6);
    if( !file_isfile(zName) && file_size(0)>=0 ){
      if( notFileIsFatal ){
        fossil_warning("not a ordinary file: %s", zName);
        nErr++;
      }
      chnged = 1;
    }else if( oldChnged>=2 ){
      chnged = oldChnged;
    }else if( isDeleted || rid==0 ){
      chnged = 1;
    }
    if( chnged!=1 ){
      currentMtime = file_mtime(0);
      assert( currentMtime>0 );
    }
    if( chnged!=1 && (checkMtime==0 || currentMtime!=oldMtime) ){
      db_ephemeral_blob(&q, 5, &origCksum);
      if( sha1sum_file(zName, &fileCksum) ){
        blob_zero(&fileCksum);
      }
      if( blob_compare(&fileCksum, &origCksum) ){

Changes to src/xfer.c.

340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
...
376
377
378
379
380
381
382


383
384
385
386
387
388
389
390
391
...
438
439
440
441
442
443
444

445
446
447
448
449
450
451
...
739
740
741
742
743
744
745

746
747






748
749
750
751
752
753
754
....
1065
1066
1067
1068
1069
1070
1071

1072
1073
1074
1075
1076
1077
1078
....
1206
1207
1208
1209
1210
1211
1212
1213



1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
....
1229
1230
1231
1232
1233
1234
1235





1236

1237
1238
1239
1240
1241
1242
1243
1244
....
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
    pXfer->nGimmeSent++;
  }
  db_finalize(&q);
}

/*
** Compute an SHA1 hash on the tail of pMsg.  Verify that it matches the
** the hash given in pHash.  Return 1 on a successful match.  Return 0
** if there is a mismatch.
*/
static int check_tail_hash(Blob *pHash, Blob *pMsg){
  Blob tail;
  Blob h2;
  int rc;
  blob_tail(pMsg, &tail);
  sha1sum_blob(&tail, &h2);
  rc = blob_compare(pHash, &h2);
  blob_reset(&h2);
  blob_reset(&tail);
  return rc==0;
}

/*
** Check the signature on an application/x-fossil payload received by
** the HTTP server.  The signature is a line of the following form:
**
**        login LOGIN NONCE SIGNATURE
................................................................................
** If everything checks out, the USER.CAP column for the USER table
** is consulted to set privileges in the global g variable.
**
** If anything fails to check out, no changes are made to privileges.
**
** Signature generation on the client side is handled by the 
** http_exchange() routine.


*/
void check_login(Blob *pLogin, Blob *pNonce, Blob *pSig){
  Stmt q;
  int rc = -1;
  char *zLogin = blob_terminate(pLogin);
  defossilize(zLogin);

  db_prepare(&q,
     "SELECT pw, cap, uid FROM user"
................................................................................
  }
  db_finalize(&q);

  if( rc==0 ){
    /* If the login was successful. */
    login_set_anon_nobody_capabilities();
  }

}

/*
** Send the content of all files in the unsent table.
**
** This is really just an optimization.  If you clear the
** unsent table, all the right files will still get transferred.
................................................................................
    ** The client can send multiple logins.  Permissions are cumulative.
    */
    if( blob_eq(&xfer.aToken[0], "login")
     && xfer.nToken==4
    ){
      if( disableLogin ){
        g.okRead = g.okWrite = 1;

      }else if( check_tail_hash(&xfer.aToken[2], xfer.pIn) ){
        check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3]);






      }
    }else
    
    /*    reqconfig  NAME
    **
    ** Request a configuration value
    */
................................................................................
      blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
      nCardSent++;
    }
    if( pushFlag ){
      blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
      nCardSent++;
    }


    /* Process the reply that came back from the server */
    while( blob_line(&recv, &xfer.line) ){
      if( blob_buffer(&xfer.line)[0]=='#' ){
        continue;
      }
      xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
................................................................................
      */
      if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
        db_set("cookie", blob_str(&xfer.aToken[1]), 0);
      }else

      /*   message MESSAGE
      **
      ** Print a message.  Similar to "error" but does not stop processing



      */        
      if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
        char *zMsg = blob_terminate(&xfer.aToken[1]);
        defossilize(zMsg);
        printf("\rServer says: %s\n", zMsg);
      }else

      /*   error MESSAGE
      **
      ** Report an error and abandon the sync session.
      **
      ** Except, when cloning we will sometimes get an error on the
................................................................................
      ** subsequent messages should be OK.  Nevertheless, we need to ignore
      ** the error card on the first message of a clone.
      */        
      if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
        if( !cloneFlag || nCycle>0 ){
          char *zMsg = blob_terminate(&xfer.aToken[1]);
          defossilize(zMsg);





          blob_appendf(&xfer.err, "server says: %s", zMsg);

          printf("Server Error: %s\n", zMsg);
        }
      }else

      /* Unknown message */
      {
        if( blob_str(&xfer.aToken[0])[0]=='<' ){
          fossil_fatal(
................................................................................
    if( nCardRcvd>0 ){
      printf(zValueFormat, "Received:",
              blob_size(&recv), nCardRcvd,
              xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
    }
    blob_reset(&recv);
    nCycle++;
    go = 0;

    /* If we received one or more files on the previous exchange but
    ** there are still phantoms, then go another round.
    */
    nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
    if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
      go = 1;







|
<










|







 







>
>

|







 







>







 







>
|
|
>
>
>
>
>
>







 







>







 







|
>
>
>




|







 







>
>
>
>
>
|
>
|







 







<







340
341
342
343
344
345
346
347

348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
...
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
...
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
...
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
....
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
....
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
....
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
....
1281
1282
1283
1284
1285
1286
1287

1288
1289
1290
1291
1292
1293
1294
    pXfer->nGimmeSent++;
  }
  db_finalize(&q);
}

/*
** Compute an SHA1 hash on the tail of pMsg.  Verify that it matches the
** the hash given in pHash.  Return non-zero for an error and 0 on success.

*/
static int check_tail_hash(Blob *pHash, Blob *pMsg){
  Blob tail;
  Blob h2;
  int rc;
  blob_tail(pMsg, &tail);
  sha1sum_blob(&tail, &h2);
  rc = blob_compare(pHash, &h2);
  blob_reset(&h2);
  blob_reset(&tail);
  return rc;
}

/*
** Check the signature on an application/x-fossil payload received by
** the HTTP server.  The signature is a line of the following form:
**
**        login LOGIN NONCE SIGNATURE
................................................................................
** If everything checks out, the USER.CAP column for the USER table
** is consulted to set privileges in the global g variable.
**
** If anything fails to check out, no changes are made to privileges.
**
** Signature generation on the client side is handled by the 
** http_exchange() routine.
**
** Return non-zero for a login failure and zero for success.
*/
int check_login(Blob *pLogin, Blob *pNonce, Blob *pSig){
  Stmt q;
  int rc = -1;
  char *zLogin = blob_terminate(pLogin);
  defossilize(zLogin);

  db_prepare(&q,
     "SELECT pw, cap, uid FROM user"
................................................................................
  }
  db_finalize(&q);

  if( rc==0 ){
    /* If the login was successful. */
    login_set_anon_nobody_capabilities();
  }
  return rc;
}

/*
** Send the content of all files in the unsent table.
**
** This is really just an optimization.  If you clear the
** unsent table, all the right files will still get transferred.
................................................................................
    ** The client can send multiple logins.  Permissions are cumulative.
    */
    if( blob_eq(&xfer.aToken[0], "login")
     && xfer.nToken==4
    ){
      if( disableLogin ){
        g.okRead = g.okWrite = 1;
      }else{
        if( check_tail_hash(&xfer.aToken[2], xfer.pIn)
         || check_login(&xfer.aToken[1], &xfer.aToken[2], &xfer.aToken[3])
        ){
          cgi_reset_content();
          @ error login\sfailed
          nErr++;
          break;
        }        
      }
    }else
    
    /*    reqconfig  NAME
    **
    ** Request a configuration value
    */
................................................................................
      blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
      nCardSent++;
    }
    if( pushFlag ){
      blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
      nCardSent++;
    }
    go = 0;

    /* Process the reply that came back from the server */
    while( blob_line(&recv, &xfer.line) ){
      if( blob_buffer(&xfer.line)[0]=='#' ){
        continue;
      }
      xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
................................................................................
      */
      if( blob_eq(&xfer.aToken[0], "cookie") && xfer.nToken==2 ){
        db_set("cookie", blob_str(&xfer.aToken[1]), 0);
      }else

      /*   message MESSAGE
      **
      ** Print a message.  Similar to "error" but does not stop processing.
      **
      ** If the "login failed" message is seen, clear the sync password prior
      ** to the next cycle.
      */        
      if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
        char *zMsg = blob_terminate(&xfer.aToken[1]);
        defossilize(zMsg);
        if( zMsg ) printf("\rServer says: %s\n", zMsg);
      }else

      /*   error MESSAGE
      **
      ** Report an error and abandon the sync session.
      **
      ** Except, when cloning we will sometimes get an error on the
................................................................................
      ** subsequent messages should be OK.  Nevertheless, we need to ignore
      ** the error card on the first message of a clone.
      */        
      if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
        if( !cloneFlag || nCycle>0 ){
          char *zMsg = blob_terminate(&xfer.aToken[1]);
          defossilize(zMsg);
          if( strcmp(zMsg, "login failed")==0 ){
            if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0);
            g.urlPasswd = 0;
            if( nCycle<2 ) go = 1;
          }else{
            blob_appendf(&xfer.err, "\rserver says: %s", zMsg);
          }
          printf("\rServer Error: %s\n", zMsg);
        }
      }else

      /* Unknown message */
      {
        if( blob_str(&xfer.aToken[0])[0]=='<' ){
          fossil_fatal(
................................................................................
    if( nCardRcvd>0 ){
      printf(zValueFormat, "Received:",
              blob_size(&recv), nCardRcvd,
              xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
    }
    blob_reset(&recv);
    nCycle++;


    /* If we received one or more files on the previous exchange but
    ** there are still phantoms, then go another round.
    */
    nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
    if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
      go = 1;

Changes to src/zip.c.

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
void baseline_zip_page(void){
  int rid;
  char *zName, *zRid;
  int nName, nRid;
  Blob zip;

  login_check_credentials();
  if( !g.okZip && (!g.okRead || !g.okHistory) ){ login_needed(); return; }
  zName = mprintf("%s", PD("name",""));
  nName = strlen(zName);
  zRid = mprintf("%s", PD("uuid",""));
  nRid = strlen(zRid);
  for(nName=strlen(zName)-1; nName>5; nName--){
    if( zName[nName]=='.' ){
      zName[nName] = 0;







|







406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
void baseline_zip_page(void){
  int rid;
  char *zName, *zRid;
  int nName, nRid;
  Blob zip;

  login_check_credentials();
  if( !g.okZip ){ login_needed(); return; }
  zName = mprintf("%s", PD("name",""));
  nName = strlen(zName);
  zRid = mprintf("%s", PD("uuid",""));
  nRid = strlen(zRid);
  for(nName=strlen(zName)-1; nName>5; nName--){
    if( zName[nName]=='.' ){
      zName[nName] = 0;