| | @@ -28,10 +28,34 @@ |
| 28 | 28 | #define URL_REMEMBER 0x002 /* Remember the url for later reuse */ |
| 29 | 29 | #define URL_ASK_REMEMBER_PW 0x004 /* Ask whether to remember prompted pw */ |
| 30 | 30 | #define URL_REMEMBER_PW 0x008 /* Should remember pw */ |
| 31 | 31 | #define URL_PROMPTED 0x010 /* Prompted for PW already */ |
| 32 | 32 | |
| 33 | +/* |
| 34 | +** The URL related data used with this subsystem. |
| 35 | +*/ |
| 36 | +struct UrlData { |
| 37 | + /* |
| 38 | + ** NOTE: These members MUST be kept in sync with the related ones in the |
| 39 | + ** "Global" structure defined in "main.c". |
| 40 | + */ |
| 41 | + int isFile; /* True if a "file:" url */ |
| 42 | + int isHttps; /* True if a "https:" url */ |
| 43 | + int isSsh; /* True if an "ssh:" url */ |
| 44 | + char *name; /* Hostname for http: or filename for file: */ |
| 45 | + char *hostname; /* The HOST: parameter on http headers */ |
| 46 | + char *protocol; /* "http" or "https" */ |
| 47 | + int port; /* TCP port number for http: or https: */ |
| 48 | + int dfltPort; /* The default port for the given protocol */ |
| 49 | + char *path; /* Pathname for http: */ |
| 50 | + char *user; /* User id for http: */ |
| 51 | + char *passwd; /* Password for http: */ |
| 52 | + char *canonical; /* Canonical representation of the URL */ |
| 53 | + char *proxyAuth; /* Proxy-Authorizer: string */ |
| 54 | + char *fossil; /* The fossil query parameter on ssh: */ |
| 55 | + unsigned flags; /* Boolean flags controlling URL processing */ |
| 56 | +}; |
| 33 | 57 | #endif /* INTERFACE */ |
| 34 | 58 | |
| 35 | 59 | |
| 36 | 60 | /* |
| 37 | 61 | ** Convert a string to lower-case. |
| | @@ -40,10 +64,203 @@ |
| 40 | 64 | while( *z ){ |
| 41 | 65 | *z = fossil_tolower(*z); |
| 42 | 66 | z++; |
| 43 | 67 | } |
| 44 | 68 | } |
| 69 | + |
| 70 | +/* |
| 71 | +** Parse the given URL. Populate members of the provided UrlData structure |
| 72 | +** as follows: |
| 73 | +** |
| 74 | +** isFile True if FILE: |
| 75 | +** isHttps True if HTTPS: |
| 76 | +** isSsh True if SSH: |
| 77 | +** protocol "http" or "https" or "file" |
| 78 | +** name Hostname for HTTP:, HTTPS:, SSH:. Filename for FILE: |
| 79 | +** port TCP port number for HTTP or HTTPS. |
| 80 | +** dfltPort Default TCP port number (80 or 443). |
| 81 | +** path Path name for HTTP or HTTPS. |
| 82 | +** user Userid. |
| 83 | +** passwd Password. |
| 84 | +** hostname HOST:PORT or just HOST if port is the default. |
| 85 | +** canonical The URL in canonical form, omitting the password |
| 86 | +** |
| 87 | +*/ |
| 88 | +void url_parse_local( |
| 89 | + const char *zUrl, |
| 90 | + unsigned int urlFlags, |
| 91 | + UrlData *pUrlData |
| 92 | +){ |
| 93 | + int i, j, c; |
| 94 | + char *zFile = 0; |
| 95 | + int bPrompted = 0; |
| 96 | + int bSetUrl = 1; |
| 97 | + |
| 98 | + memset(pUrlData, 0, sizeof(UrlData)); |
| 99 | + |
| 100 | + if( zUrl==0 ){ |
| 101 | + zUrl = db_get("last-sync-url", 0); |
| 102 | + if( zUrl==0 ) return; |
| 103 | + pUrlData->passwd = unobscure(db_get("last-sync-pw", 0)); |
| 104 | + bSetUrl = 0; |
| 105 | + } |
| 106 | + |
| 107 | + if( strncmp(zUrl, "http://", 7)==0 |
| 108 | + || strncmp(zUrl, "https://", 8)==0 |
| 109 | + || strncmp(zUrl, "ssh://", 6)==0 |
| 110 | + ){ |
| 111 | + int iStart; |
| 112 | + char *zLogin; |
| 113 | + char *zExe; |
| 114 | + char cQuerySep = '?'; |
| 115 | + |
| 116 | + pUrlData->isFile = 0; |
| 117 | + if( zUrl[4]=='s' ){ |
| 118 | + pUrlData->isHttps = 1; |
| 119 | + pUrlData->protocol = "https"; |
| 120 | + pUrlData->dfltPort = 443; |
| 121 | + iStart = 8; |
| 122 | + }else if( zUrl[0]=='s' ){ |
| 123 | + pUrlData->isSsh = 1; |
| 124 | + pUrlData->protocol = "ssh"; |
| 125 | + pUrlData->dfltPort = 22; |
| 126 | + pUrlData->fossil = "fossil"; |
| 127 | + iStart = 6; |
| 128 | + }else{ |
| 129 | + pUrlData->isHttps = 0; |
| 130 | + pUrlData->protocol = "http"; |
| 131 | + pUrlData->dfltPort = 80; |
| 132 | + iStart = 7; |
| 133 | + } |
| 134 | + for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} |
| 135 | + if( c=='@' ){ |
| 136 | + /* Parse up the user-id and password */ |
| 137 | + for(j=iStart; j<i && zUrl[j]!=':'; j++){} |
| 138 | + pUrlData->user = mprintf("%.*s", j-iStart, &zUrl[iStart]); |
| 139 | + dehttpize(pUrlData->user); |
| 140 | + if( j<i ){ |
| 141 | + pUrlData->passwd = mprintf("%.*s", i-j-1, &zUrl[j+1]); |
| 142 | + dehttpize(pUrlData->passwd); |
| 143 | + } |
| 144 | + if( pUrlData->isSsh && pUrlData->passwd ){ |
| 145 | + zLogin = mprintf("%t:*@", pUrlData->user); |
| 146 | + }else{ |
| 147 | + zLogin = mprintf("%t@", pUrlData->user); |
| 148 | + } |
| 149 | + for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} |
| 150 | + pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 151 | + i = j; |
| 152 | + }else{ |
| 153 | + for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} |
| 154 | + pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 155 | + zLogin = mprintf(""); |
| 156 | + } |
| 157 | + url_tolower(pUrlData->name); |
| 158 | + if( c==':' ){ |
| 159 | + pUrlData->port = 0; |
| 160 | + i++; |
| 161 | + while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 162 | + pUrlData->port = pUrlData->port*10 + c - '0'; |
| 163 | + i++; |
| 164 | + } |
| 165 | + pUrlData->hostname = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 166 | + }else{ |
| 167 | + pUrlData->port = pUrlData->dfltPort; |
| 168 | + pUrlData->hostname = pUrlData->name; |
| 169 | + } |
| 170 | + dehttpize(pUrlData->name); |
| 171 | + pUrlData->path = mprintf("%s", &zUrl[i]); |
| 172 | + for(i=0; pUrlData->path[i] && pUrlData->path[i]!='?'; i++){} |
| 173 | + if( pUrlData->path[i] ){ |
| 174 | + pUrlData->path[i] = 0; |
| 175 | + i++; |
| 176 | + } |
| 177 | + zExe = mprintf(""); |
| 178 | + while( pUrlData->path[i]!=0 ){ |
| 179 | + char *zName, *zValue; |
| 180 | + zName = &pUrlData->path[i]; |
| 181 | + zValue = zName; |
| 182 | + while( pUrlData->path[i] && pUrlData->path[i]!='=' ){ i++; } |
| 183 | + if( pUrlData->path[i]=='=' ){ |
| 184 | + pUrlData->path[i] = 0; |
| 185 | + i++; |
| 186 | + zValue = &pUrlData->path[i]; |
| 187 | + while( pUrlData->path[i] && pUrlData->path[i]!='&' ){ i++; } |
| 188 | + } |
| 189 | + if( pUrlData->path[i] ){ |
| 190 | + pUrlData->path[i] = 0; |
| 191 | + i++; |
| 192 | + } |
| 193 | + if( fossil_strcmp(zName,"fossil")==0 ){ |
| 194 | + pUrlData->fossil = zValue; |
| 195 | + dehttpize(pUrlData->fossil); |
| 196 | + zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil); |
| 197 | + cQuerySep = '&'; |
| 198 | + } |
| 199 | + } |
| 200 | + |
| 201 | + dehttpize(pUrlData->path); |
| 202 | + if( pUrlData->dfltPort==pUrlData->port ){ |
| 203 | + pUrlData->canonical = mprintf( |
| 204 | + "%s://%s%T%T%s", |
| 205 | + pUrlData->protocol, zLogin, pUrlData->name, pUrlData->path, zExe |
| 206 | + ); |
| 207 | + }else{ |
| 208 | + pUrlData->canonical = mprintf( |
| 209 | + "%s://%s%T:%d%T%s", |
| 210 | + pUrlData->protocol, zLogin, pUrlData->name, pUrlData->port, |
| 211 | + pUrlData->path, zExe |
| 212 | + ); |
| 213 | + } |
| 214 | + if( pUrlData->isSsh && pUrlData->path[1] ) pUrlData->path++; |
| 215 | + free(zLogin); |
| 216 | + }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 217 | + pUrlData->isFile = 1; |
| 218 | + if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 219 | + i = 7; |
| 220 | + }else{ |
| 221 | + i = 5; |
| 222 | + } |
| 223 | + zFile = mprintf("%s", &zUrl[i]); |
| 224 | + }else if( file_isfile(zUrl) ){ |
| 225 | + pUrlData->isFile = 1; |
| 226 | + zFile = mprintf("%s", zUrl); |
| 227 | + }else if( file_isdir(zUrl)==1 ){ |
| 228 | + zFile = mprintf("%s/FOSSIL", zUrl); |
| 229 | + if( file_isfile(zFile) ){ |
| 230 | + pUrlData->isFile = 1; |
| 231 | + }else{ |
| 232 | + free(zFile); |
| 233 | + fossil_fatal("unknown repository: %s", zUrl); |
| 234 | + } |
| 235 | + }else{ |
| 236 | + fossil_fatal("unknown repository: %s", zUrl); |
| 237 | + } |
| 238 | + pUrlData->flags = urlFlags; |
| 239 | + if( pUrlData->isFile ){ |
| 240 | + Blob cfile; |
| 241 | + dehttpize(zFile); |
| 242 | + file_canonical_name(zFile, &cfile, 0); |
| 243 | + free(zFile); |
| 244 | + pUrlData->protocol = "file"; |
| 245 | + pUrlData->path = ""; |
| 246 | + pUrlData->name = mprintf("%b", &cfile); |
| 247 | + pUrlData->canonical = mprintf("file://%T", pUrlData->name); |
| 248 | + blob_reset(&cfile); |
| 249 | + }else if( pUrlData->user!=0 && pUrlData->passwd==0 && (urlFlags & URL_PROMPT_PW) ){ |
| 250 | + url_prompt_for_password(); |
| 251 | + bPrompted = 1; |
| 252 | + } |
| 253 | + if( urlFlags & URL_REMEMBER ){ |
| 254 | + if( bSetUrl ){ |
| 255 | + db_set("last-sync-url", pUrlData->canonical, 0); |
| 256 | + } |
| 257 | + if( !bPrompted && pUrlData->passwd && pUrlData->user ){ |
| 258 | + db_set("last-sync-pw", obscure(pUrlData->passwd), 0); |
| 259 | + } |
| 260 | + } |
| 261 | +} |
| 45 | 262 | |
| 46 | 263 | /* |
| 47 | 264 | ** Parse the given URL, which describes a sync server. Populate variables |
| 48 | 265 | ** in the global "g" structure as follows: |
| 49 | 266 | ** |
| | @@ -68,175 +285,14 @@ |
| 68 | 285 | ** |
| 69 | 286 | ** ssh://userid:password@host:port/path?fossil=path/to/fossil.exe |
| 70 | 287 | ** |
| 71 | 288 | */ |
| 72 | 289 | void url_parse(const char *zUrl, unsigned int urlFlags){ |
| 73 | | - int i, j, c; |
| 74 | | - char *zFile = 0; |
| 75 | | - int bPrompted = 0; |
| 76 | | - int bSetUrl = 1; |
| 77 | | - |
| 78 | | - if( zUrl==0 ){ |
| 79 | | - zUrl = db_get("last-sync-url", 0); |
| 80 | | - if( zUrl==0 ) return; |
| 81 | | - g.urlPasswd = unobscure(db_get("last-sync-pw", 0)); |
| 82 | | - bSetUrl = 0; |
| 83 | | - } |
| 84 | | - |
| 85 | | - if( strncmp(zUrl, "http://", 7)==0 |
| 86 | | - || strncmp(zUrl, "https://", 8)==0 |
| 87 | | - || strncmp(zUrl, "ssh://", 6)==0 |
| 88 | | - ){ |
| 89 | | - int iStart; |
| 90 | | - char *zLogin; |
| 91 | | - char *zExe; |
| 92 | | - char cQuerySep = '?'; |
| 93 | | - |
| 94 | | - g.urlIsFile = 0; |
| 95 | | - if( zUrl[4]=='s' ){ |
| 96 | | - g.urlIsHttps = 1; |
| 97 | | - g.urlProtocol = "https"; |
| 98 | | - g.urlDfltPort = 443; |
| 99 | | - iStart = 8; |
| 100 | | - }else if( zUrl[0]=='s' ){ |
| 101 | | - g.urlIsSsh = 1; |
| 102 | | - g.urlProtocol = "ssh"; |
| 103 | | - g.urlDfltPort = 22; |
| 104 | | - g.urlFossil = "fossil"; |
| 105 | | - iStart = 6; |
| 106 | | - }else{ |
| 107 | | - g.urlIsHttps = 0; |
| 108 | | - g.urlProtocol = "http"; |
| 109 | | - g.urlDfltPort = 80; |
| 110 | | - iStart = 7; |
| 111 | | - } |
| 112 | | - for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!='@'; i++){} |
| 113 | | - if( c=='@' ){ |
| 114 | | - /* Parse up the user-id and password */ |
| 115 | | - for(j=iStart; j<i && zUrl[j]!=':'; j++){} |
| 116 | | - g.urlUser = mprintf("%.*s", j-iStart, &zUrl[iStart]); |
| 117 | | - dehttpize(g.urlUser); |
| 118 | | - if( j<i ){ |
| 119 | | - g.urlPasswd = mprintf("%.*s", i-j-1, &zUrl[j+1]); |
| 120 | | - dehttpize(g.urlPasswd); |
| 121 | | - } |
| 122 | | - if( g.urlIsSsh && g.urlPasswd ){ |
| 123 | | - zLogin = mprintf("%t:*@", g.urlUser); |
| 124 | | - }else{ |
| 125 | | - zLogin = mprintf("%t@", g.urlUser); |
| 126 | | - } |
| 127 | | - for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} |
| 128 | | - g.urlName = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 129 | | - i = j; |
| 130 | | - }else{ |
| 131 | | - for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} |
| 132 | | - g.urlName = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 133 | | - zLogin = mprintf(""); |
| 134 | | - } |
| 135 | | - url_tolower(g.urlName); |
| 136 | | - if( c==':' ){ |
| 137 | | - g.urlPort = 0; |
| 138 | | - i++; |
| 139 | | - while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 140 | | - g.urlPort = g.urlPort*10 + c - '0'; |
| 141 | | - i++; |
| 142 | | - } |
| 143 | | - g.urlHostname = mprintf("%s:%d", g.urlName, g.urlPort); |
| 144 | | - }else{ |
| 145 | | - g.urlPort = g.urlDfltPort; |
| 146 | | - g.urlHostname = g.urlName; |
| 147 | | - } |
| 148 | | - dehttpize(g.urlName); |
| 149 | | - g.urlPath = mprintf("%s", &zUrl[i]); |
| 150 | | - for(i=0; g.urlPath[i] && g.urlPath[i]!='?'; i++){} |
| 151 | | - if( g.urlPath[i] ){ |
| 152 | | - g.urlPath[i] = 0; |
| 153 | | - i++; |
| 154 | | - } |
| 155 | | - zExe = mprintf(""); |
| 156 | | - while( g.urlPath[i]!=0 ){ |
| 157 | | - char *zName, *zValue; |
| 158 | | - zName = &g.urlPath[i]; |
| 159 | | - zValue = zName; |
| 160 | | - while( g.urlPath[i] && g.urlPath[i]!='=' ){ i++; } |
| 161 | | - if( g.urlPath[i]=='=' ){ |
| 162 | | - g.urlPath[i] = 0; |
| 163 | | - i++; |
| 164 | | - zValue = &g.urlPath[i]; |
| 165 | | - while( g.urlPath[i] && g.urlPath[i]!='&' ){ i++; } |
| 166 | | - } |
| 167 | | - if( g.urlPath[i] ){ |
| 168 | | - g.urlPath[i] = 0; |
| 169 | | - i++; |
| 170 | | - } |
| 171 | | - if( fossil_strcmp(zName,"fossil")==0 ){ |
| 172 | | - g.urlFossil = zValue; |
| 173 | | - dehttpize(g.urlFossil); |
| 174 | | - zExe = mprintf("%cfossil=%T", cQuerySep, g.urlFossil); |
| 175 | | - cQuerySep = '&'; |
| 176 | | - } |
| 177 | | - } |
| 178 | | - |
| 179 | | - dehttpize(g.urlPath); |
| 180 | | - if( g.urlDfltPort==g.urlPort ){ |
| 181 | | - g.urlCanonical = mprintf( |
| 182 | | - "%s://%s%T%T%s", |
| 183 | | - g.urlProtocol, zLogin, g.urlName, g.urlPath, zExe |
| 184 | | - ); |
| 185 | | - }else{ |
| 186 | | - g.urlCanonical = mprintf( |
| 187 | | - "%s://%s%T:%d%T%s", |
| 188 | | - g.urlProtocol, zLogin, g.urlName, g.urlPort, g.urlPath, zExe |
| 189 | | - ); |
| 190 | | - } |
| 191 | | - if( g.urlIsSsh && g.urlPath[1] ) g.urlPath++; |
| 192 | | - free(zLogin); |
| 193 | | - }else if( strncmp(zUrl, "file:", 5)==0 ){ |
| 194 | | - g.urlIsFile = 1; |
| 195 | | - if( zUrl[5]=='/' && zUrl[6]=='/' ){ |
| 196 | | - i = 7; |
| 197 | | - }else{ |
| 198 | | - i = 5; |
| 199 | | - } |
| 200 | | - zFile = mprintf("%s", &zUrl[i]); |
| 201 | | - }else if( file_isfile(zUrl) ){ |
| 202 | | - g.urlIsFile = 1; |
| 203 | | - zFile = mprintf("%s", zUrl); |
| 204 | | - }else if( file_isdir(zUrl)==1 ){ |
| 205 | | - zFile = mprintf("%s/FOSSIL", zUrl); |
| 206 | | - if( file_isfile(zFile) ){ |
| 207 | | - g.urlIsFile = 1; |
| 208 | | - }else{ |
| 209 | | - free(zFile); |
| 210 | | - fossil_fatal("unknown repository: %s", zUrl); |
| 211 | | - } |
| 212 | | - }else{ |
| 213 | | - fossil_fatal("unknown repository: %s", zUrl); |
| 214 | | - } |
| 215 | | - g.urlFlags = urlFlags; |
| 216 | | - if( g.urlIsFile ){ |
| 217 | | - Blob cfile; |
| 218 | | - dehttpize(zFile); |
| 219 | | - file_canonical_name(zFile, &cfile, 0); |
| 220 | | - free(zFile); |
| 221 | | - g.urlProtocol = "file"; |
| 222 | | - g.urlPath = ""; |
| 223 | | - g.urlName = mprintf("%b", &cfile); |
| 224 | | - g.urlCanonical = mprintf("file://%T", g.urlName); |
| 225 | | - blob_reset(&cfile); |
| 226 | | - }else if( g.urlUser!=0 && g.urlPasswd==0 && (urlFlags & URL_PROMPT_PW) ){ |
| 227 | | - url_prompt_for_password(); |
| 228 | | - bPrompted = 1; |
| 229 | | - } |
| 230 | | - if( urlFlags & URL_REMEMBER ){ |
| 231 | | - if( bSetUrl ){ |
| 232 | | - db_set("last-sync-url", g.urlCanonical, 0); |
| 233 | | - } |
| 234 | | - if( !bPrompted && g.urlPasswd && g.urlUser ){ |
| 235 | | - db_set("last-sync-pw", obscure(g.urlPasswd), 0); |
| 236 | | - } |
| 237 | | - } |
| 290 | + UrlData urlData; |
| 291 | + memcpy(&urlData, GLOBAL_URL(), sizeof(UrlData)); |
| 292 | + url_parse_local(zUrl, urlFlags, &urlData); |
| 293 | + memcpy(GLOBAL_URL(), &urlData, sizeof(UrlData)); |
| 238 | 294 | } |
| 239 | 295 | |
| 240 | 296 | /* |
| 241 | 297 | ** COMMAND: test-urlparser |
| 242 | 298 | ** |
| | @@ -428,36 +484,48 @@ |
| 428 | 484 | } |
| 429 | 485 | return blob_str(&p->url); |
| 430 | 486 | } |
| 431 | 487 | |
| 432 | 488 | /* |
| 433 | | -** Prompt the user for the password for g.urlUser. Store the result |
| 434 | | -** in g.urlPasswd. |
| 489 | +** Prompt the user for the password that corresponds to the "user" member of |
| 490 | +** the provided UrlData structure. Store the result into the "passwd" member |
| 491 | +** of the provided UrlData structure. |
| 435 | 492 | */ |
| 436 | | -void url_prompt_for_password(void){ |
| 437 | | - if( g.urlIsSsh || g.urlIsFile ) return; |
| 493 | +void url_prompt_for_password_local(UrlData *pUrlData){ |
| 494 | + if( pUrlData->isSsh || pUrlData->isFile ) return; |
| 438 | 495 | if( isatty(fileno(stdin)) |
| 439 | | - && (g.urlFlags & URL_PROMPT_PW)!=0 |
| 440 | | - && (g.urlFlags & URL_PROMPTED)==0 |
| 496 | + && (pUrlData->flags & URL_PROMPT_PW)!=0 |
| 497 | + && (pUrlData->flags & URL_PROMPTED)==0 |
| 441 | 498 | ){ |
| 442 | | - g.urlFlags |= URL_PROMPTED; |
| 443 | | - g.urlPasswd = prompt_for_user_password(g.urlUser); |
| 444 | | - if( g.urlPasswd[0] |
| 445 | | - && (g.urlFlags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 |
| 499 | + pUrlData->flags |= URL_PROMPTED; |
| 500 | + pUrlData->passwd = prompt_for_user_password(pUrlData->user); |
| 501 | + if( pUrlData->passwd[0] |
| 502 | + && (pUrlData->flags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0 |
| 446 | 503 | ){ |
| 447 | 504 | if( save_password_prompt() ){ |
| 448 | | - g.urlFlags |= URL_REMEMBER_PW; |
| 449 | | - if( g.urlFlags & URL_REMEMBER ){ |
| 450 | | - db_set("last-sync-pw", obscure(g.urlPasswd), 0); |
| 505 | + pUrlData->flags |= URL_REMEMBER_PW; |
| 506 | + if( pUrlData->flags & URL_REMEMBER ){ |
| 507 | + db_set("last-sync-pw", obscure(pUrlData->passwd), 0); |
| 451 | 508 | } |
| 452 | 509 | } |
| 453 | 510 | } |
| 454 | 511 | }else{ |
| 455 | 512 | fossil_fatal("missing or incorrect password for user \"%s\"", |
| 456 | | - g.urlUser); |
| 513 | + pUrlData->user); |
| 457 | 514 | } |
| 458 | 515 | } |
| 516 | + |
| 517 | +/* |
| 518 | +** Prompt the user for the password for g.urlUser. Store the result |
| 519 | +** in g.urlPasswd. |
| 520 | +*/ |
| 521 | +void url_prompt_for_password(void){ |
| 522 | + UrlData urlData; |
| 523 | + memcpy(&urlData, GLOBAL_URL(), sizeof(UrlData)); |
| 524 | + url_prompt_for_password_local(&urlData); |
| 525 | + memcpy(GLOBAL_URL(), &urlData, sizeof(UrlData)); |
| 526 | +} |
| 459 | 527 | |
| 460 | 528 | /* |
| 461 | 529 | ** Remember the URL if requested. |
| 462 | 530 | */ |
| 463 | 531 | void url_remember(void){ |
| 464 | 532 | |