Fossil

Check-in [e621b6db]
Login

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

Overview
Comment:Use POST instead of GET for the /xfer method. Other bug fixes in the URL parser.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:e621b6dbe310661d111e8caba43bdac40fd9693b
User & Date: drh 2007-07-30 14:28:17
Context
2007-07-30
16:35
Only request the password one time on a push or pull. check-in: 3a25b683 user: drh tags: trunk
16:31
Merge in clone and sync changes. Fix a bug in undelta. check-in: e3c529c2 user: dan tags: trunk
14:28
Use POST instead of GET for the /xfer method. Other bug fixes in the URL parser. check-in: e621b6db user: drh tags: trunk
13:34
When errors occur during CGI, make sure the error is returned in a correct CGI reply. check-in: 396cc2a4 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/http.c.

155
156
157
158
159
160
161
162
163
164
165
166



167
168
169
170














171
172
173
174
175
176
177
...
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
*/
void http_exchange(Blob *pSend, Blob *pRecv){
  Blob login, nonce, sig, pw, payload, hdr;
  const char *zSep;
  int i;
  int cnt = 0;

  user_select();
  blob_zero(&nonce);
  blob_zero(&pw);
  db_blob(&nonce, "SELECT hex(randomblob(20))");
  blob_copy(&pw, &nonce);



  db_blob(&pw, "SELECT pw FROM user WHERE uid=%d", g.userUid);
  sha1sum_blob(&pw, &sig);
  blob_zero(&login);
  blob_appendf(&login, "login %s %b %b\n", g.zLogin, &nonce, &sig);














  blob_reset(&nonce);
  blob_reset(&pw);
  blob_reset(&sig);
  if( g.fHttpTrace ){
    payload = login;
    blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
  }else{
................................................................................
  blob_zero(&hdr);
  i = strlen(g.urlPath);
  if( i>0 && g.urlPath[i-1]=='/' ){
    zSep = "";
  }else{
    zSep = "/";
  }
  blob_appendf(&hdr, "GET %s%sxfer HTTP/1.1\r\n", g.urlPath, zSep);
  if( g.fHttpTrace ){
    blob_appendf(&hdr, "Content-Type: application/x-fossil-debug\r\n");
  }else{
    blob_appendf(&hdr, "Content-Type: application/x-fossil\r\n");
  }
  blob_appendf(&hdr, "Content-Length: %d\r\n\r\n", blob_size(&payload));








<




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







 







|







155
156
157
158
159
160
161

162
163
164
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
...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
*/
void http_exchange(Blob *pSend, Blob *pRecv){
  Blob login, nonce, sig, pw, payload, hdr;
  const char *zSep;
  int i;
  int cnt = 0;


  blob_zero(&nonce);
  blob_zero(&pw);
  db_blob(&nonce, "SELECT hex(randomblob(20))");
  blob_copy(&pw, &nonce);
  blob_zero(&login);
  if( g.urlUser==0 ){
    user_select();
    db_blob(&pw, "SELECT pw FROM user WHERE uid=%d", g.userUid);
    sha1sum_blob(&pw, &sig);

    blob_appendf(&login, "login %s %b %b\n", g.zLogin, &nonce, &sig);
  }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);
        blob_append(&pw, blob_buffer(&x), blob_size(&x));
        blob_reset(&x);
      }
    }
    sha1sum_blob(&pw, &sig);
    blob_appendf(&login, "login %s %b %b\n", g.urlUser, &nonce, &sig);
  }        
  blob_reset(&nonce);
  blob_reset(&pw);
  blob_reset(&sig);
  if( g.fHttpTrace ){
    payload = login;
    blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
  }else{
................................................................................
  blob_zero(&hdr);
  i = strlen(g.urlPath);
  if( i>0 && g.urlPath[i-1]=='/' ){
    zSep = "";
  }else{
    zSep = "/";
  }
  blob_appendf(&hdr, "POST %s%sxfer HTTP/1.1\r\n", g.urlPath, zSep);
  if( g.fHttpTrace ){
    blob_appendf(&hdr, "Content-Type: application/x-fossil-debug\r\n");
  }else{
    blob_appendf(&hdr, "Content-Type: application/x-fossil\r\n");
  }
  blob_appendf(&hdr, "Content-Length: %d\r\n\r\n", blob_size(&payload));

Changes to src/login.c.

201
202
203
204
205
206
207

208
209
210
211
212
213
214
215
216



217
218
219
220
221
222
223
    zCap = "s";
    g.noPswd = 1;
    g.isAnon = 0;
  }

  /* Check the login cookie to see if it matches a known valid user.
  */

  if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){
    uid = db_int(0, 
            "SELECT 1 FROM user"
            " WHERE uid=%d"
            "   AND cookie=%Q"
            "   AND ipaddr=%Q"
            "   AND cexpire>julianday('now')",
            atoi(zCookie), zCookie, zRemoteAddr
         );



  }

  if( uid==0 ){
    g.isAnon = 1;
    g.zLogin = "";
    zCap = db_get("nologin-cap","onrj");
  }else if( zCap==0 ){







>
|
|
|
|
|
|
|
|
|
>
>
>







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
    zCap = "s";
    g.noPswd = 1;
    g.isAnon = 0;
  }

  /* Check the login cookie to see if it matches a known valid user.
  */
  if( uid==0 ){
    if( (zCookie = P(login_cookie_name()))!=0 ){
      uid = db_int(0, 
              "SELECT uid FROM user"
              " WHERE uid=%d"
              "   AND cookie=%Q"
              "   AND ipaddr=%Q"
              "   AND cexpire>julianday('now')",
              atoi(zCookie), zCookie, zRemoteAddr
           );
    }else{
      uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'");
    }
  }

  if( uid==0 ){
    g.isAnon = 1;
    g.zLogin = "";
    zCap = db_get("nologin-cap","onrj");
  }else if( zCap==0 ){

Changes to src/main.c.

67
68
69
70
71
72
73


74
75
76
77
78
79
80
  Blob cgiIn;             /* Input to an xfer www method */
  int cgiPanic;           /* Write error messages to CGI */

  int urlIsFile;          /* True if a "file:" url */
  char *urlName;          /* Hostname for http: or filename for file: */
  int urlPort;            /* TCP port number for http: */
  char *urlPath;          /* Pathname for http: */


  char *urlCanonical;     /* Canonical representation of the URL */

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








>
>







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  Blob cgiIn;             /* Input to an xfer www method */
  int cgiPanic;           /* Write error messages to CGI */

  int urlIsFile;          /* True if a "file:" url */
  char *urlName;          /* Hostname for http: or filename for file: */
  int urlPort;            /* TCP port number for http: */
  char *urlPath;          /* Pathname for http: */
  char *urlUser;          /* User id for http: */
  char *urlPasswd;        /* Password for http: */
  char *urlCanonical;     /* Canonical representation of the URL */

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

Changes to src/url.c.

29
30
31
32
33
34
35


36
37




38
39
40
41
42
43
44











45

46
47
48
49
50
51
52
..
86
87
88
89
90
91
92

















/*
** Parse the given URL.  Populate variables in the global "g" structure.
**
**      g.urlIsFile      True if this is a file URL
**      g.urlName        Hostname for HTTP:.  Filename for FILE:
**      g.urlPort        Port name for HTTP.
**      g.urlPath        Path name for HTTP.


**      g.urlCanonical   The URL in canonical form
**




*/
void url_parse(const char *zUrl){
  int i, j, c;
  char *zFile;
  if( strncmp(zUrl, "http:", 5)==0 ){
    g.urlIsFile = 0;
    for(i=7; (c=zUrl[i])!=0 && c!=':' && c!='/'; i++){}











    g.urlName = mprintf("%.*s", i-7, &zUrl[7]);

    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';
        i++;
................................................................................
    file_canonical_name(zFile, &cfile);
    free(zFile);
    g.urlName = mprintf("%b", &cfile);
    g.urlCanonical = mprintf("file://%T", g.urlName);
    blob_reset(&cfile);
  }
}
























>
>


>
>
>
>



|


|
>
>
>
>
>
>
>
>
>
>
>
|
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
** Parse the given URL.  Populate variables in the global "g" structure.
**
**      g.urlIsFile      True if this is a file URL
**      g.urlName        Hostname for HTTP:.  Filename for FILE:
**      g.urlPort        Port name for HTTP.
**      g.urlPath        Path name for HTTP.
**      g.urlUser        Userid.
**      g.urlPasswd      Password.
**      g.urlCanonical   The URL in canonical form
**
** 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:", 5)==0 ){
    g.urlIsFile = 0;
    for(i=7; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
    if( c=='@' ){
      for(j=7; j<i && zUrl[j]!=':'; j++){}
      g.urlUser = mprintf("%.*s", j-7, &zUrl[7]);
      if( j<i ){
        g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
      }
      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=7; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){}
      g.urlName = mprintf("%.*s", i-7, &zUrl[7]);
    }
    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';
        i++;
................................................................................
    file_canonical_name(zFile, &cfile);
    free(zFile);
    g.urlName = mprintf("%b", &cfile);
    g.urlCanonical = mprintf("file://%T", g.urlName);
    blob_reset(&cfile);
  }
}

/*
** COMMAND: test-urlparser
*/
void cmd_test_urlparser(void){
  if( g.argc!=3 ){
    usage("URL");
  }
  url_parse(g.argv[2]);
  printf("g.urlIsFile    = %d\n", g.urlIsFile);
  printf("g.urlName      = %s\n", g.urlName);
  printf("g.urlPort      = %d\n", g.urlPort);
  printf("g.urlPath      = %s\n", g.urlPath);
  printf("g.urlUser      = %s\n", g.urlUser);
  printf("g.urlPasswd    = %s\n", g.urlPasswd);
  printf("g.urlCanonical = %s\n", g.urlCanonical);
}

Changes to src/user.c.

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
...
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
*/
static void prompt_for_passphrase(const char *zPrompt, Blob *pPassphrase){
  char *z = getpass(zPrompt);
  strip_string(pPassphrase, z);
}

/*
** Prompt the user for a passphrase used to encrypt the private key
** portion of an RSA key pair.
**
** Behavior is controlled by the verify parameter:
**
**     0     Just ask once.
**
**     1     If the first answer is a non-empty string, ask for
**           verification.  Repeat if the two strings do not match.
**
**     2     Ask twice, repeat if the strings do not match.
*/

static void get_passphrase(const char *zPrompt, Blob *pPassphrase, int verify){



  Blob secondTry;
  blob_zero(pPassphrase);
  blob_zero(&secondTry);
  while(1){
    prompt_for_passphrase(zPrompt, pPassphrase);
    if( verify==0 ) break;
    if( verify==1 && blob_size(pPassphrase)==0 ) break;
................................................................................
  }
  n = strlen(g.argv[2]);
  if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
    Blob passwd, login, contact;

    prompt_user("login: ", &login);
    prompt_user("contact-info: ", &contact);
    get_passphrase("password: ", &passwd, 1);
    db_multi_exec(
      "INSERT INTO user(login,pw,cap,info)"
      "VALUES(%B,%B,'jnor',%B)",
      &login, &passwd, &contact
    );
  }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
    user_select();
................................................................................
    Blob pw;
    if( g.argc!=4 ) usage("user password USERNAME");
    uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]);
    if( uid==0 ){
      fossil_fatal("no such user: %s", g.argv[3]);
    }
    zPrompt = mprintf("new passwd for %s: ", g.argv[3]);
    get_passphrase(zPrompt, &pw, 1);
    if( blob_size(&pw)==0 ){
      printf("password unchanged\n");
    }else{
      db_multi_exec("UPDATE user SET pw=%B WHERE uid=%d", &pw, uid);
    }
  }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
    int uid;







|
|










>
|
>
>
>







 







|







 







|







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
...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
*/
static void prompt_for_passphrase(const char *zPrompt, Blob *pPassphrase){
  char *z = getpass(zPrompt);
  strip_string(pPassphrase, z);
}

/*
** Prompt the user for a password.  Store the result in the pPassphrase
** blob.
**
** Behavior is controlled by the verify parameter:
**
**     0     Just ask once.
**
**     1     If the first answer is a non-empty string, ask for
**           verification.  Repeat if the two strings do not match.
**
**     2     Ask twice, repeat if the strings do not match.
*/
void prompt_for_password(
  const char *zPrompt,
  Blob *pPassphrase,
  int verify
){
  Blob secondTry;
  blob_zero(pPassphrase);
  blob_zero(&secondTry);
  while(1){
    prompt_for_passphrase(zPrompt, pPassphrase);
    if( verify==0 ) break;
    if( verify==1 && blob_size(pPassphrase)==0 ) break;
................................................................................
  }
  n = strlen(g.argv[2]);
  if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
    Blob passwd, login, contact;

    prompt_user("login: ", &login);
    prompt_user("contact-info: ", &contact);
    prompt_for_password("password: ", &passwd, 1);
    db_multi_exec(
      "INSERT INTO user(login,pw,cap,info)"
      "VALUES(%B,%B,'jnor',%B)",
      &login, &passwd, &contact
    );
  }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
    user_select();
................................................................................
    Blob pw;
    if( g.argc!=4 ) usage("user password USERNAME");
    uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]);
    if( uid==0 ){
      fossil_fatal("no such user: %s", g.argv[3]);
    }
    zPrompt = mprintf("new passwd for %s: ", g.argv[3]);
    prompt_for_password(zPrompt, &pw, 1);
    if( blob_size(&pw)==0 ){
      printf("password unchanged\n");
    }else{
      db_multi_exec("UPDATE user SET pw=%B WHERE uid=%d", &pw, uid);
    }
  }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
    int uid;

Changes to src/xfer.c.

358
359
360
361
362
363
364

365
366
367
368
369
370
371
...
383
384
385
386
387
388
389

390
391
392
393
394
395
396
      }
      if( !blob_eq_str(&aToken[2], zPCode, -1) ){
        cgi_reset_content();
        @ error wrong\sproject
        nErr++;
        break;
      }

      if( blob_eq(&aToken[0], "pull") ){
        if( !g.okRead ){
          cgi_reset_content();
          @ error not\sauthorized\sto\sread
          nErr++;
          break;
        }
................................................................................
    }else

    /*    clone
    **
    ** The client knows nothing.  Tell all.
    */
    if( blob_eq(&aToken[0], "clone") ){

      if( !g.okRead ){
        cgi_reset_content();
        @ error not\sauthorized\sto\sread
        nErr++;
        break;
      }
      isPull = 1;







>







 







>







358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
      }
      if( !blob_eq_str(&aToken[2], zPCode, -1) ){
        cgi_reset_content();
        @ error wrong\sproject
        nErr++;
        break;
      }
      login_check_credentials();
      if( blob_eq(&aToken[0], "pull") ){
        if( !g.okRead ){
          cgi_reset_content();
          @ error not\sauthorized\sto\sread
          nErr++;
          break;
        }
................................................................................
    }else

    /*    clone
    **
    ** The client knows nothing.  Tell all.
    */
    if( blob_eq(&aToken[0], "clone") ){
      login_check_credentials();
      if( !g.okRead ){
        cgi_reset_content();
        @ error not\sauthorized\sto\sread
        nErr++;
        break;
      }
      isPull = 1;