Check-in [ff159bfdd7]
Not logged in

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

Overview
Comment:Merge in fixes for the password embedded in the URL, save password prompting, and new clone --once option.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ff159bfdd76da13ee669002da9d9d1801a9cd6fd
User & Date: andybradford 2013-11-12 02:41:52.222
Context
2013-11-12
03:43
Merge change to fossil ui which first looks for default user if set before falling back to the Setup user. check-in: 1460b7476f user: andybradford tags: trunk
02:41
Merge in fixes for the password embedded in the URL, save password prompting, and new clone --once option. check-in: ff159bfdd7 user: andybradford tags: trunk
2013-11-11
23:22
Further revisions to the semantics of printing the timeline 'bounds' indicators. check-in: 8b03ef2489 user: mistachkin tags: trunk
2013-11-10
06:04
Merge in latest from trunk. Closed-Leaf check-in: 69a19a7d75 user: andybradford tags: url-password-fixes
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/clone.c.
103
104
105
106
107
108
109

110
111
112
113
114
115
116
117
118
119
120

121
122

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
**
** By default, your current login name is used to create the default
** admin user. This can be overridden using the -A|--admin-user
** parameter.
**
** Options:
**    --admin-user|-A USERNAME   Make USERNAME the administrator

**    --private                  Also clone private branches 
**    --ssl-identity=filename    Use the SSL identity if requested by the server
**    --ssh-command|-c 'command' Use this SSH command
**
** See also: init
*/
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;

  clone_ssh_find_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_fatal("file already exists: %s", g.argv[3]);
  }

  zDefaultUser = find_option("admin-user","A",1);

  url_parse(g.argv[2], URL_PROMPT_PW|URL_ASK_REMEMBER_PW);
  if( zDefaultUser==0 && g.urlUser!=0 ) zDefaultUser = g.urlUser;
  if( g.urlIsFile ){
    file_copy(g.urlName, g.argv[3]);
    db_close(1);
    db_open_repository(g.argv[3]);
    db_record_repository_filename(g.argv[3]);
    url_remember();







>











>


>












|







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
**
** By default, your current login name is used to create the default
** admin user. This can be overridden using the -A|--admin-user
** parameter.
**
** Options:
**    --admin-user|-A USERNAME   Make USERNAME the administrator
**    --once                     Don't save url.
**    --private                  Also clone private branches 
**    --ssl-identity=filename    Use the SSL identity if requested by the server
**    --ssh-command|-c 'command' Use this SSH command
**
** See also: init
*/
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 */
  int urlFlags = URL_PROMPT_PW | URL_REMEMBER;

  if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
  if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER;
  clone_ssh_find_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_fatal("file already exists: %s", g.argv[3]);
  }

  zDefaultUser = find_option("admin-user","A",1);

  url_parse(g.argv[2], urlFlags);
  if( zDefaultUser==0 && g.urlUser!=0 ) zDefaultUser = g.urlUser;
  if( g.urlIsFile ){
    file_copy(g.urlName, g.argv[3]);
    db_close(1);
    db_open_repository(g.argv[3]);
    db_record_repository_filename(g.argv[3]);
    url_remember();
Changes to src/sync.c.
52
53
54
55
56
57
58

59
60
61
62
63
64
65
  url_parse(0, URL_REMEMBER);
  if( g.urlProtocol==0 ) return 0;  
  if( g.urlUser!=0 && g.urlPasswd==0 ){
    g.urlPasswd = unobscure(db_get("last-sync-pw", 0));
    g.urlFlags |= URL_PROMPT_PW;
    url_prompt_for_password();
  }

#if 0 /* Disabled for now */
  if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
    /* When doing an automatic pull, also automatically pull shuns from
    ** the server if pull_shuns is enabled.
    **
    ** TODO:  What happens if the shun list gets really big? 
    ** Maybe the shunning list should only be pulled on every 10th







>







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  url_parse(0, URL_REMEMBER);
  if( g.urlProtocol==0 ) return 0;  
  if( g.urlUser!=0 && g.urlPasswd==0 ){
    g.urlPasswd = unobscure(db_get("last-sync-pw", 0));
    g.urlFlags |= URL_PROMPT_PW;
    url_prompt_for_password();
  }
  url_remember();
#if 0 /* Disabled for now */
  if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
    /* When doing an automatic pull, also automatically pull shuns from
    ** the server if pull_shuns is enabled.
    **
    ** TODO:  What happens if the shun list gets really big? 
    ** Maybe the shunning list should only be pulled on every 10th
113
114
115
116
117
118
119

120
121
122
123
124
125
126
  }else if( g.argc==3 ){
    zUrl = g.argv[2];
  }
  if( urlFlags & URL_REMEMBER ){
    clone_ssh_db_set_options();
  }
  url_parse(zUrl, urlFlags);

  if( g.urlProtocol==0 ){
    if( urlOptional ) fossil_exit(0);
    usage("URL");
  }
  user_select();
  if( g.argc==2 ){
    if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){







>







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  }else if( g.argc==3 ){
    zUrl = g.argv[2];
  }
  if( urlFlags & URL_REMEMBER ){
    clone_ssh_db_set_options();
  }
  url_parse(zUrl, urlFlags);
  url_remember();
  if( g.urlProtocol==0 ){
    if( urlOptional ) fossil_exit(0);
    usage("URL");
  }
  user_select();
  if( g.argc==2 ){
    if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
260
261
262
263
264
265
266
267
268

269
270
271
272
273
274
275
276
277
  if( g.argc!=2 && g.argc!=3 ){
    usage("remote-url ?URL|off?");
  }
  if( g.argc==3 ){
    db_unset("last-sync-url", 0);
    db_unset("last-sync-pw", 0);
    if( is_false(g.argv[2]) ) return;
    url_parse(g.argv[2], URL_REMEMBER|URL_PROMPT_PW);
  }

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







|

>









262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
  if( g.argc!=2 && g.argc!=3 ){
    usage("remote-url ?URL|off?");
  }
  if( g.argc==3 ){
    db_unset("last-sync-url", 0);
    db_unset("last-sync-pw", 0);
    if( is_false(g.argv[2]) ) return;
    url_parse(g.argv[2], URL_REMEMBER|URL_PROMPT_PW|URL_ASK_REMEMBER_PW);
  }
  url_remember();
  zUrl = db_get("last-sync-url", 0);
  if( zUrl==0 ){
    fossil_print("off\n");
    return;
  }else{
    url_parse(zUrl, 0);
    fossil_print("%s\n", g.urlCanonical);
  }
}
Changes to src/url.c.
15
16
17
18
19
20
21






22
23
24
25
26
27
28
**
*******************************************************************************
**
** This file contains code for parsing URLs that appear on the command-line
*/
#include "config.h"
#include "url.h"







#if INTERFACE
/*
** Flags for url_parse()
*/
#define URL_PROMPT_PW        0x001  /* Prompt for password if needed */
#define URL_REMEMBER         0x002  /* Remember the url for later reuse */







>
>
>
>
>
>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
**
*******************************************************************************
**
** This file contains code for parsing URLs that appear on the command-line
*/
#include "config.h"
#include "url.h"
#include <stdio.h>
#ifdef _WIN32
#include <io.h>
#define isatty(d) _isatty(d)
#define fileno(s) _fileno(s)
#endif

#if INTERFACE
/*
** Flags for url_parse()
*/
#define URL_PROMPT_PW        0x001  /* Prompt for password if needed */
#define URL_REMEMBER         0x002  /* Remember the url for later reuse */
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
**
** HTTP url format as follows (HTTPS is the same with a different scheme):
**
**     http://userid:password@host:port/path
**
** SSH url format is:
**
**     ssh://userid:password@host:port/path?fossil=path/to/fossil.exe
**
*/
void url_parse(const char *zUrl, unsigned int urlFlags){
  int i, j, c;
  char *zFile = 0;
  int bPrompted = 0;
  int bSetUrl = 1;
 
  if( zUrl==0 ){
    zUrl = db_get("last-sync-url", 0);
    if( zUrl==0 ) return;

    g.urlPasswd = unobscure(db_get("last-sync-pw", 0));
    bSetUrl = 0;

  }

  if( strncmp(zUrl, "http://", 7)==0
   || strncmp(zUrl, "https://", 8)==0
   || strncmp(zUrl, "ssh://", 6)==0
  ){
    int iStart;







|





<
<




>
|
<
>







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
**
** HTTP url format as follows (HTTPS is the same with a different scheme):
**
**     http://userid:password@host:port/path
**
** SSH url format is:
**
**     ssh://userid@host:port/path?fossil=path/to/fossil.exe
**
*/
void url_parse(const char *zUrl, unsigned int urlFlags){
  int i, j, c;
  char *zFile = 0;


 
  if( zUrl==0 ){
    zUrl = db_get("last-sync-url", 0);
    if( zUrl==0 ) return;
    if( g.urlPasswd==0 ){
      g.urlPasswd = unobscure(db_get("last-sync-pw", 0));

    }
  }

  if( strncmp(zUrl, "http://", 7)==0
   || strncmp(zUrl, "https://", 8)==0
   || strncmp(zUrl, "ssh://", 6)==0
  ){
    int iStart;
112
113
114
115
116
117
118



119
120
121
122
123
124
125
126

127
128
129
130
131
132
133
    for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
    if( c=='@' ){
      /* Parse up the user-id and password */
      for(j=iStart; j<i && zUrl[j]!=':'; j++){}
      g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]);
      dehttpize(g.urlUser);
      if( j<i ){



        g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
        dehttpize(g.urlPasswd);
      }
      if( g.urlIsSsh && g.urlPasswd ){
        zLogin = mprintf("%t:*@", g.urlUser);
      }else{
        zLogin = mprintf("%t@", g.urlUser);
      }

      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]);
      zLogin = mprintf("");







>
>
>



|
|
<
<

>







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131


132
133
134
135
136
137
138
139
140
    for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){}
    if( c=='@' ){
      /* Parse up the user-id and password */
      for(j=iStart; j<i && zUrl[j]!=':'; j++){}
      g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]);
      dehttpize(g.urlUser);
      if( j<i ){
        if( ( urlFlags & URL_REMEMBER ) && g.urlIsSsh==0 ){
          urlFlags |= URL_ASK_REMEMBER_PW;
        }
        g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]);
        dehttpize(g.urlPasswd);
      }
      if( g.urlIsSsh ){
        urlFlags &= ~URL_ASK_REMEMBER_PW;


      }
      zLogin = mprintf("%t@", g.urlUser);
      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]);
      zLogin = mprintf("");
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
    }else{
      free(zFile);
      fossil_fatal("unknown repository: %s", zUrl);
    }
  }else{
    fossil_fatal("unknown repository: %s", zUrl);
  }
  g.urlFlags = urlFlags;
  if( g.urlIsFile ){
    Blob cfile;
    dehttpize(zFile);  
    file_canonical_name(zFile, &cfile, 0);
    free(zFile);
    g.urlProtocol = "file";
    g.urlPath = "";
    g.urlName = mprintf("%b", &cfile);
    g.urlCanonical = mprintf("file://%T", g.urlName);
    blob_reset(&cfile);
  }else if( g.urlUser!=0 && g.urlPasswd==0 && (urlFlags & URL_PROMPT_PW) ){
    url_prompt_for_password();


    bPrompted = 1;

  }
  if( urlFlags & URL_REMEMBER ){
    if( bSetUrl ){
      db_set("last-sync-url", g.urlCanonical, 0);
    }
    if( !bPrompted && g.urlPasswd && g.urlUser ){
      db_set("last-sync-pw", obscure(g.urlPasswd), 0);
    }
  }
}

/*
** COMMAND: test-urlparser
**







|












>
>
|
>
|
|
<
<
|
<
<







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
    }else{
      free(zFile);
      fossil_fatal("unknown repository: %s", zUrl);
    }
  }else{
    fossil_fatal("unknown repository: %s", zUrl);
  }
  if( urlFlags ) g.urlFlags = urlFlags;
  if( g.urlIsFile ){
    Blob cfile;
    dehttpize(zFile);  
    file_canonical_name(zFile, &cfile, 0);
    free(zFile);
    g.urlProtocol = "file";
    g.urlPath = "";
    g.urlName = mprintf("%b", &cfile);
    g.urlCanonical = mprintf("file://%T", g.urlName);
    blob_reset(&cfile);
  }else if( g.urlUser!=0 && g.urlPasswd==0 && (urlFlags & URL_PROMPT_PW) ){
    url_prompt_for_password();
  }else if( g.urlUser!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){
    if( isatty(fileno(stdin)) ){
      if( save_password_prompt() ){
        g.urlFlags = urlFlags |= URL_REMEMBER_PW;
      }else{
        g.urlFlags = urlFlags &= ~URL_REMEMBER_PW;


      }


    }
  }
}

/*
** COMMAND: test-urlparser
**
442
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
476
477
478
479
480
481
    g.urlFlags |= URL_PROMPTED;
    g.urlPasswd = prompt_for_user_password(g.urlUser);
    if( g.urlPasswd[0]
     && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0
    ){
      if( save_password_prompt() ){
        g.urlFlags |= URL_REMEMBER_PW;

        if( g.urlFlags & URL_REMEMBER ){
          db_set("last-sync-pw", obscure(g.urlPasswd), 0);
        }
      }
    }
  }else{
    fossil_fatal("missing or incorrect password for user \"%s\"",
                 g.urlUser);
  }
}

/*
** Remember the URL if requested.
*/
void url_remember(void){

  db_set("last-sync-url", g.urlCanonical, 0);
  if( g.urlFlags & URL_REMEMBER_PW ){
    db_set("last-sync-pw", obscure(g.urlPasswd), 0);
  }
  g.urlFlags |= URL_REMEMBER;

}

/* 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();
  }
}







>
|
<
<









|


>
|
|
|
|
<
>








|




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
476
477
478
479
480
481
482
483
484
485
486
487
    g.urlFlags |= URL_PROMPTED;
    g.urlPasswd = prompt_for_user_password(g.urlUser);
    if( g.urlPasswd[0]
     && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0
    ){
      if( save_password_prompt() ){
        g.urlFlags |= URL_REMEMBER_PW;
      }else{
        g.urlFlags &= ~URL_REMEMBER_PW;


      }
    }
  }else{
    fossil_fatal("missing or incorrect password for user \"%s\"",
                 g.urlUser);
  }
}

/*
** Remember the URL and password if requested.
*/
void url_remember(void){
  if( g.urlFlags & URL_REMEMBER ){
    db_set("last-sync-url", g.urlCanonical, 0);
    if( g.urlUser!=0 && g.urlPasswd!=0 && ( g.urlFlags & URL_REMEMBER_PW ) ){
      db_set("last-sync-pw", obscure(g.urlPasswd), 0);
    }

  }
}

/* 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/user.c.
133
134
135
136
137
138
139




140
141
142
143
144
145
146

/*
** Prompt to save Fossil user password
*/
int save_password_prompt(){
  Blob x;
  char c;




  prompt_user("remember password (Y/n)? ", &x);
  c = blob_str(&x)[0];
  blob_reset(&x);
  return ( c!='n' && c!='N' );
}

/*







>
>
>
>







133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

/*
** Prompt to save Fossil user password
*/
int save_password_prompt(){
  Blob x;
  char c;
  const char *old = db_get("last-sync-pw", 0);
  if( (old!=0) && fossil_strcmp(unobscure(old), g.urlPasswd)==0 ){
     return 0;
  }
  prompt_user("remember password (Y/n)? ", &x);
  c = blob_str(&x)[0];
  blob_reset(&x);
  return ( c!='n' && c!='N' );
}

/*
Changes to src/xfer.c.
1779
1780
1781
1782
1783
1784
1785

1786

1787
1788
1789
1790
1791
1792
1793
          fossil_print("Error: %s\n", zMsg);
          if( fossil_strcmp(zMsg, "login failed")==0 ){
            if( nCycle<2 ){
              g.urlPasswd = 0;
              go = 1;
              if( g.cgiOutput==0 ){
                g.urlFlags |= URL_PROMPT_PW;

                url_prompt_for_password();

              }
            }
          }else{
            blob_appendf(&xfer.err, "server says: %s\n", zMsg);
            nErr++;
          }
          break;







>

>







1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
          fossil_print("Error: %s\n", zMsg);
          if( fossil_strcmp(zMsg, "login failed")==0 ){
            if( nCycle<2 ){
              g.urlPasswd = 0;
              go = 1;
              if( g.cgiOutput==0 ){
                g.urlFlags |= URL_PROMPT_PW;
                g.urlFlags &= ~URL_PROMPTED;
                url_prompt_for_password();
                url_remember();
              }
            }
          }else{
            blob_appendf(&xfer.err, "server says: %s\n", zMsg);
            nErr++;
          }
          break;
Changes to www/changes.wiki.
24
25
26
27
28
29
30



31
32
33
34
35
36
37
     and  "[/help?cmd=finfo | fossil finfo]" commands.
  *  Option -n|--limit of "[/help?cmd=timeline | fossil timeline]" now
     specifies the number of entries, just like all other commands which
     have the -n|--limit option. The various timeline-related functions
     now output "+++ end of timeline reached +++" whenever appropriate.
     Use "-n 0" if no limit is desired, the end of timeline marker will
     not be printed then.




<h2>Changes For Version 1.27 (2013-09-11)</h2>
  *  Enhance the [/help?cmd=changes | fossil changes],
     [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras],
     [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands
     to restrict operation to files and directories named on the command-line.
  *  New --integrate option to [/help?cmd=merge | fossil merge], which







>
>
>







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
     and  "[/help?cmd=finfo | fossil finfo]" commands.
  *  Option -n|--limit of "[/help?cmd=timeline | fossil timeline]" now
     specifies the number of entries, just like all other commands which
     have the -n|--limit option. The various timeline-related functions
     now output "+++ end of timeline reached +++" whenever appropriate.
     Use "-n 0" if no limit is desired, the end of timeline marker will
     not be printed then.
  *  Fix handling of password embedded in Fossil URL.
  *  New --once option to [/help?cmd=clone | fossil clone] command which does
     not store the URL or password when cloning.

<h2>Changes For Version 1.27 (2013-09-11)</h2>
  *  Enhance the [/help?cmd=changes | fossil changes],
     [/help?cmd=clean | fossil clean], [/help?cmd=extras | fossil extras],
     [/help?cmd=ls | fossil ls] and [/help?cmd=status | fossil status] commands
     to restrict operation to files and directories named on the command-line.
  *  New --integrate option to [/help?cmd=merge | fossil merge], which