Fossil SCM
Merge the latest changes from trunk.
Commit
503a0ef555dd376998018d7620db63a5515bc2e1
Parent
850d3df44e03f4c…
6 files changed
+17
-6
+17
-6
+14
-12
+4
+4
+8
-3
M
src/db.c
+17
-6
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -81,19 +81,29 @@ | ||
| 81 | 81 | db_force_rollback(); |
| 82 | 82 | fossil_exit(1); |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 85 | static int nBegin = 0; /* Nesting depth of BEGIN */ |
| 86 | -static int isNewRepo = 0; /* True if the repository is newly created */ | |
| 87 | 86 | static int doRollback = 0; /* True to force a rollback */ |
| 88 | 87 | static int nCommitHook = 0; /* Number of commit hooks */ |
| 89 | 88 | static struct sCommitHook { |
| 90 | 89 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ |
| 91 | 90 | int sequence; /* Call functions in sequence order */ |
| 92 | 91 | } aHook[5]; |
| 93 | 92 | static Stmt *pAllStmt = 0; /* List of all unfinalized statements */ |
| 94 | 93 | static int nPrepare = 0; /* Number of calls to sqlite3_prepare() */ |
| 94 | +static int nDeleteOnFail = 0; /* Number of entries in azDeleteOnFail[] */ | |
| 95 | +static char *azDeleteOnFail[3]; /* Files to delete on a failure */ | |
| 96 | + | |
| 97 | + | |
| 98 | +/* | |
| 99 | +** Arrange for the given file to be deleted on a failure. | |
| 100 | +*/ | |
| 101 | +void db_delete_on_failure(const char *zFilename){ | |
| 102 | + assert( nDeleteOnFail<count(azDeleteOnFail) ); | |
| 103 | + azDeleteOnFail[nDeleteOnFail++] = fossil_strdup(zFilename); | |
| 104 | +} | |
| 95 | 105 | |
| 96 | 106 | /* |
| 97 | 107 | ** This routine is called by the SQLite commit-hook mechanism |
| 98 | 108 | ** just prior to each commit. All this routine does is verify |
| 99 | 109 | ** that nBegin really is zero. That insures that transactions |
| @@ -141,10 +151,11 @@ | ||
| 141 | 151 | |
| 142 | 152 | /* |
| 143 | 153 | ** Force a rollback and shutdown the database |
| 144 | 154 | */ |
| 145 | 155 | void db_force_rollback(void){ |
| 156 | + int i; | |
| 146 | 157 | static int busy = 0; |
| 147 | 158 | if( busy || g.db==0 ) return; |
| 148 | 159 | busy = 1; |
| 149 | 160 | undo_rollback(); |
| 150 | 161 | while( pAllStmt ){ |
| @@ -151,17 +162,16 @@ | ||
| 151 | 162 | db_finalize(pAllStmt); |
| 152 | 163 | } |
| 153 | 164 | if( nBegin ){ |
| 154 | 165 | sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); |
| 155 | 166 | nBegin = 0; |
| 156 | - if( isNewRepo ){ | |
| 157 | - db_close(0); | |
| 158 | - file_delete(g.zRepositoryName); | |
| 159 | - } | |
| 160 | 167 | } |
| 161 | 168 | busy = 0; |
| 162 | 169 | db_close(0); |
| 170 | + for(i=0; i<nDeleteOnFail; i++){ | |
| 171 | + file_delete(azDeleteOnFail[i]); | |
| 172 | + } | |
| 163 | 173 | } |
| 164 | 174 | |
| 165 | 175 | /* |
| 166 | 176 | ** Install a commit hook. Hooks are installed in sequence order. |
| 167 | 177 | ** It is an error to install the same commit hook more than once. |
| @@ -1044,11 +1054,11 @@ | ||
| 1044 | 1054 | zFilename, |
| 1045 | 1055 | zRepositorySchema1, |
| 1046 | 1056 | zRepositorySchema2, |
| 1047 | 1057 | (char*)0 |
| 1048 | 1058 | ); |
| 1049 | - isNewRepo = 1; | |
| 1059 | + db_delete_on_failure(zFilename); | |
| 1050 | 1060 | } |
| 1051 | 1061 | |
| 1052 | 1062 | /* |
| 1053 | 1063 | ** Create the default user accounts in the USER table. |
| 1054 | 1064 | */ |
| @@ -1555,10 +1565,11 @@ | ||
| 1555 | 1565 | fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); |
| 1556 | 1566 | } |
| 1557 | 1567 | file_canonical_name(g.argv[2], &path); |
| 1558 | 1568 | db_open_repository(blob_str(&path)); |
| 1559 | 1569 | db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); |
| 1570 | + db_delete_on_failure("./_FOSSIL_"); | |
| 1560 | 1571 | db_open_local(); |
| 1561 | 1572 | db_lset("repository", blob_str(&path)); |
| 1562 | 1573 | db_record_repository_filename(blob_str(&path)); |
| 1563 | 1574 | vid = db_int(0, "SELECT pid FROM plink y" |
| 1564 | 1575 | " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)"); |
| 1565 | 1576 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -81,19 +81,29 @@ | |
| 81 | db_force_rollback(); |
| 82 | fossil_exit(1); |
| 83 | } |
| 84 | |
| 85 | static int nBegin = 0; /* Nesting depth of BEGIN */ |
| 86 | static int isNewRepo = 0; /* True if the repository is newly created */ |
| 87 | static int doRollback = 0; /* True to force a rollback */ |
| 88 | static int nCommitHook = 0; /* Number of commit hooks */ |
| 89 | static struct sCommitHook { |
| 90 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ |
| 91 | int sequence; /* Call functions in sequence order */ |
| 92 | } aHook[5]; |
| 93 | static Stmt *pAllStmt = 0; /* List of all unfinalized statements */ |
| 94 | static int nPrepare = 0; /* Number of calls to sqlite3_prepare() */ |
| 95 | |
| 96 | /* |
| 97 | ** This routine is called by the SQLite commit-hook mechanism |
| 98 | ** just prior to each commit. All this routine does is verify |
| 99 | ** that nBegin really is zero. That insures that transactions |
| @@ -141,10 +151,11 @@ | |
| 141 | |
| 142 | /* |
| 143 | ** Force a rollback and shutdown the database |
| 144 | */ |
| 145 | void db_force_rollback(void){ |
| 146 | static int busy = 0; |
| 147 | if( busy || g.db==0 ) return; |
| 148 | busy = 1; |
| 149 | undo_rollback(); |
| 150 | while( pAllStmt ){ |
| @@ -151,17 +162,16 @@ | |
| 151 | db_finalize(pAllStmt); |
| 152 | } |
| 153 | if( nBegin ){ |
| 154 | sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); |
| 155 | nBegin = 0; |
| 156 | if( isNewRepo ){ |
| 157 | db_close(0); |
| 158 | file_delete(g.zRepositoryName); |
| 159 | } |
| 160 | } |
| 161 | busy = 0; |
| 162 | db_close(0); |
| 163 | } |
| 164 | |
| 165 | /* |
| 166 | ** Install a commit hook. Hooks are installed in sequence order. |
| 167 | ** It is an error to install the same commit hook more than once. |
| @@ -1044,11 +1054,11 @@ | |
| 1044 | zFilename, |
| 1045 | zRepositorySchema1, |
| 1046 | zRepositorySchema2, |
| 1047 | (char*)0 |
| 1048 | ); |
| 1049 | isNewRepo = 1; |
| 1050 | } |
| 1051 | |
| 1052 | /* |
| 1053 | ** Create the default user accounts in the USER table. |
| 1054 | */ |
| @@ -1555,10 +1565,11 @@ | |
| 1555 | fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); |
| 1556 | } |
| 1557 | file_canonical_name(g.argv[2], &path); |
| 1558 | db_open_repository(blob_str(&path)); |
| 1559 | db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); |
| 1560 | db_open_local(); |
| 1561 | db_lset("repository", blob_str(&path)); |
| 1562 | db_record_repository_filename(blob_str(&path)); |
| 1563 | vid = db_int(0, "SELECT pid FROM plink y" |
| 1564 | " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)"); |
| 1565 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -81,19 +81,29 @@ | |
| 81 | db_force_rollback(); |
| 82 | fossil_exit(1); |
| 83 | } |
| 84 | |
| 85 | static int nBegin = 0; /* Nesting depth of BEGIN */ |
| 86 | static int doRollback = 0; /* True to force a rollback */ |
| 87 | static int nCommitHook = 0; /* Number of commit hooks */ |
| 88 | static struct sCommitHook { |
| 89 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ |
| 90 | int sequence; /* Call functions in sequence order */ |
| 91 | } aHook[5]; |
| 92 | static Stmt *pAllStmt = 0; /* List of all unfinalized statements */ |
| 93 | static int nPrepare = 0; /* Number of calls to sqlite3_prepare() */ |
| 94 | static int nDeleteOnFail = 0; /* Number of entries in azDeleteOnFail[] */ |
| 95 | static char *azDeleteOnFail[3]; /* Files to delete on a failure */ |
| 96 | |
| 97 | |
| 98 | /* |
| 99 | ** Arrange for the given file to be deleted on a failure. |
| 100 | */ |
| 101 | void db_delete_on_failure(const char *zFilename){ |
| 102 | assert( nDeleteOnFail<count(azDeleteOnFail) ); |
| 103 | azDeleteOnFail[nDeleteOnFail++] = fossil_strdup(zFilename); |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | ** This routine is called by the SQLite commit-hook mechanism |
| 108 | ** just prior to each commit. All this routine does is verify |
| 109 | ** that nBegin really is zero. That insures that transactions |
| @@ -141,10 +151,11 @@ | |
| 151 | |
| 152 | /* |
| 153 | ** Force a rollback and shutdown the database |
| 154 | */ |
| 155 | void db_force_rollback(void){ |
| 156 | int i; |
| 157 | static int busy = 0; |
| 158 | if( busy || g.db==0 ) return; |
| 159 | busy = 1; |
| 160 | undo_rollback(); |
| 161 | while( pAllStmt ){ |
| @@ -151,17 +162,16 @@ | |
| 162 | db_finalize(pAllStmt); |
| 163 | } |
| 164 | if( nBegin ){ |
| 165 | sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); |
| 166 | nBegin = 0; |
| 167 | } |
| 168 | busy = 0; |
| 169 | db_close(0); |
| 170 | for(i=0; i<nDeleteOnFail; i++){ |
| 171 | file_delete(azDeleteOnFail[i]); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | /* |
| 176 | ** Install a commit hook. Hooks are installed in sequence order. |
| 177 | ** It is an error to install the same commit hook more than once. |
| @@ -1044,11 +1054,11 @@ | |
| 1054 | zFilename, |
| 1055 | zRepositorySchema1, |
| 1056 | zRepositorySchema2, |
| 1057 | (char*)0 |
| 1058 | ); |
| 1059 | db_delete_on_failure(zFilename); |
| 1060 | } |
| 1061 | |
| 1062 | /* |
| 1063 | ** Create the default user accounts in the USER table. |
| 1064 | */ |
| @@ -1555,10 +1565,11 @@ | |
| 1565 | fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); |
| 1566 | } |
| 1567 | file_canonical_name(g.argv[2], &path); |
| 1568 | db_open_repository(blob_str(&path)); |
| 1569 | db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); |
| 1570 | db_delete_on_failure("./_FOSSIL_"); |
| 1571 | db_open_local(); |
| 1572 | db_lset("repository", blob_str(&path)); |
| 1573 | db_record_repository_filename(blob_str(&path)); |
| 1574 | vid = db_int(0, "SELECT pid FROM plink y" |
| 1575 | " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)"); |
| 1576 |
M
src/db.c
+17
-6
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -81,19 +81,29 @@ | ||
| 81 | 81 | db_force_rollback(); |
| 82 | 82 | fossil_exit(1); |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 85 | static int nBegin = 0; /* Nesting depth of BEGIN */ |
| 86 | -static int isNewRepo = 0; /* True if the repository is newly created */ | |
| 87 | 86 | static int doRollback = 0; /* True to force a rollback */ |
| 88 | 87 | static int nCommitHook = 0; /* Number of commit hooks */ |
| 89 | 88 | static struct sCommitHook { |
| 90 | 89 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ |
| 91 | 90 | int sequence; /* Call functions in sequence order */ |
| 92 | 91 | } aHook[5]; |
| 93 | 92 | static Stmt *pAllStmt = 0; /* List of all unfinalized statements */ |
| 94 | 93 | static int nPrepare = 0; /* Number of calls to sqlite3_prepare() */ |
| 94 | +static int nDeleteOnFail = 0; /* Number of entries in azDeleteOnFail[] */ | |
| 95 | +static char *azDeleteOnFail[3]; /* Files to delete on a failure */ | |
| 96 | + | |
| 97 | + | |
| 98 | +/* | |
| 99 | +** Arrange for the given file to be deleted on a failure. | |
| 100 | +*/ | |
| 101 | +void db_delete_on_failure(const char *zFilename){ | |
| 102 | + assert( nDeleteOnFail<count(azDeleteOnFail) ); | |
| 103 | + azDeleteOnFail[nDeleteOnFail++] = fossil_strdup(zFilename); | |
| 104 | +} | |
| 95 | 105 | |
| 96 | 106 | /* |
| 97 | 107 | ** This routine is called by the SQLite commit-hook mechanism |
| 98 | 108 | ** just prior to each commit. All this routine does is verify |
| 99 | 109 | ** that nBegin really is zero. That insures that transactions |
| @@ -141,10 +151,11 @@ | ||
| 141 | 151 | |
| 142 | 152 | /* |
| 143 | 153 | ** Force a rollback and shutdown the database |
| 144 | 154 | */ |
| 145 | 155 | void db_force_rollback(void){ |
| 156 | + int i; | |
| 146 | 157 | static int busy = 0; |
| 147 | 158 | if( busy || g.db==0 ) return; |
| 148 | 159 | busy = 1; |
| 149 | 160 | undo_rollback(); |
| 150 | 161 | while( pAllStmt ){ |
| @@ -151,17 +162,16 @@ | ||
| 151 | 162 | db_finalize(pAllStmt); |
| 152 | 163 | } |
| 153 | 164 | if( nBegin ){ |
| 154 | 165 | sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); |
| 155 | 166 | nBegin = 0; |
| 156 | - if( isNewRepo ){ | |
| 157 | - db_close(0); | |
| 158 | - file_delete(g.zRepositoryName); | |
| 159 | - } | |
| 160 | 167 | } |
| 161 | 168 | busy = 0; |
| 162 | 169 | db_close(0); |
| 170 | + for(i=0; i<nDeleteOnFail; i++){ | |
| 171 | + file_delete(azDeleteOnFail[i]); | |
| 172 | + } | |
| 163 | 173 | } |
| 164 | 174 | |
| 165 | 175 | /* |
| 166 | 176 | ** Install a commit hook. Hooks are installed in sequence order. |
| 167 | 177 | ** It is an error to install the same commit hook more than once. |
| @@ -1044,11 +1054,11 @@ | ||
| 1044 | 1054 | zFilename, |
| 1045 | 1055 | zRepositorySchema1, |
| 1046 | 1056 | zRepositorySchema2, |
| 1047 | 1057 | (char*)0 |
| 1048 | 1058 | ); |
| 1049 | - isNewRepo = 1; | |
| 1059 | + db_delete_on_failure(zFilename); | |
| 1050 | 1060 | } |
| 1051 | 1061 | |
| 1052 | 1062 | /* |
| 1053 | 1063 | ** Create the default user accounts in the USER table. |
| 1054 | 1064 | */ |
| @@ -1555,10 +1565,11 @@ | ||
| 1555 | 1565 | fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); |
| 1556 | 1566 | } |
| 1557 | 1567 | file_canonical_name(g.argv[2], &path); |
| 1558 | 1568 | db_open_repository(blob_str(&path)); |
| 1559 | 1569 | db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); |
| 1570 | + db_delete_on_failure("./_FOSSIL_"); | |
| 1560 | 1571 | db_open_local(); |
| 1561 | 1572 | db_lset("repository", blob_str(&path)); |
| 1562 | 1573 | db_record_repository_filename(blob_str(&path)); |
| 1563 | 1574 | vid = db_int(0, "SELECT pid FROM plink y" |
| 1564 | 1575 | " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)"); |
| 1565 | 1576 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -81,19 +81,29 @@ | |
| 81 | db_force_rollback(); |
| 82 | fossil_exit(1); |
| 83 | } |
| 84 | |
| 85 | static int nBegin = 0; /* Nesting depth of BEGIN */ |
| 86 | static int isNewRepo = 0; /* True if the repository is newly created */ |
| 87 | static int doRollback = 0; /* True to force a rollback */ |
| 88 | static int nCommitHook = 0; /* Number of commit hooks */ |
| 89 | static struct sCommitHook { |
| 90 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ |
| 91 | int sequence; /* Call functions in sequence order */ |
| 92 | } aHook[5]; |
| 93 | static Stmt *pAllStmt = 0; /* List of all unfinalized statements */ |
| 94 | static int nPrepare = 0; /* Number of calls to sqlite3_prepare() */ |
| 95 | |
| 96 | /* |
| 97 | ** This routine is called by the SQLite commit-hook mechanism |
| 98 | ** just prior to each commit. All this routine does is verify |
| 99 | ** that nBegin really is zero. That insures that transactions |
| @@ -141,10 +151,11 @@ | |
| 141 | |
| 142 | /* |
| 143 | ** Force a rollback and shutdown the database |
| 144 | */ |
| 145 | void db_force_rollback(void){ |
| 146 | static int busy = 0; |
| 147 | if( busy || g.db==0 ) return; |
| 148 | busy = 1; |
| 149 | undo_rollback(); |
| 150 | while( pAllStmt ){ |
| @@ -151,17 +162,16 @@ | |
| 151 | db_finalize(pAllStmt); |
| 152 | } |
| 153 | if( nBegin ){ |
| 154 | sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); |
| 155 | nBegin = 0; |
| 156 | if( isNewRepo ){ |
| 157 | db_close(0); |
| 158 | file_delete(g.zRepositoryName); |
| 159 | } |
| 160 | } |
| 161 | busy = 0; |
| 162 | db_close(0); |
| 163 | } |
| 164 | |
| 165 | /* |
| 166 | ** Install a commit hook. Hooks are installed in sequence order. |
| 167 | ** It is an error to install the same commit hook more than once. |
| @@ -1044,11 +1054,11 @@ | |
| 1044 | zFilename, |
| 1045 | zRepositorySchema1, |
| 1046 | zRepositorySchema2, |
| 1047 | (char*)0 |
| 1048 | ); |
| 1049 | isNewRepo = 1; |
| 1050 | } |
| 1051 | |
| 1052 | /* |
| 1053 | ** Create the default user accounts in the USER table. |
| 1054 | */ |
| @@ -1555,10 +1565,11 @@ | |
| 1555 | fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); |
| 1556 | } |
| 1557 | file_canonical_name(g.argv[2], &path); |
| 1558 | db_open_repository(blob_str(&path)); |
| 1559 | db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); |
| 1560 | db_open_local(); |
| 1561 | db_lset("repository", blob_str(&path)); |
| 1562 | db_record_repository_filename(blob_str(&path)); |
| 1563 | vid = db_int(0, "SELECT pid FROM plink y" |
| 1564 | " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)"); |
| 1565 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -81,19 +81,29 @@ | |
| 81 | db_force_rollback(); |
| 82 | fossil_exit(1); |
| 83 | } |
| 84 | |
| 85 | static int nBegin = 0; /* Nesting depth of BEGIN */ |
| 86 | static int doRollback = 0; /* True to force a rollback */ |
| 87 | static int nCommitHook = 0; /* Number of commit hooks */ |
| 88 | static struct sCommitHook { |
| 89 | int (*xHook)(void); /* Functions to call at db_end_transaction() */ |
| 90 | int sequence; /* Call functions in sequence order */ |
| 91 | } aHook[5]; |
| 92 | static Stmt *pAllStmt = 0; /* List of all unfinalized statements */ |
| 93 | static int nPrepare = 0; /* Number of calls to sqlite3_prepare() */ |
| 94 | static int nDeleteOnFail = 0; /* Number of entries in azDeleteOnFail[] */ |
| 95 | static char *azDeleteOnFail[3]; /* Files to delete on a failure */ |
| 96 | |
| 97 | |
| 98 | /* |
| 99 | ** Arrange for the given file to be deleted on a failure. |
| 100 | */ |
| 101 | void db_delete_on_failure(const char *zFilename){ |
| 102 | assert( nDeleteOnFail<count(azDeleteOnFail) ); |
| 103 | azDeleteOnFail[nDeleteOnFail++] = fossil_strdup(zFilename); |
| 104 | } |
| 105 | |
| 106 | /* |
| 107 | ** This routine is called by the SQLite commit-hook mechanism |
| 108 | ** just prior to each commit. All this routine does is verify |
| 109 | ** that nBegin really is zero. That insures that transactions |
| @@ -141,10 +151,11 @@ | |
| 151 | |
| 152 | /* |
| 153 | ** Force a rollback and shutdown the database |
| 154 | */ |
| 155 | void db_force_rollback(void){ |
| 156 | int i; |
| 157 | static int busy = 0; |
| 158 | if( busy || g.db==0 ) return; |
| 159 | busy = 1; |
| 160 | undo_rollback(); |
| 161 | while( pAllStmt ){ |
| @@ -151,17 +162,16 @@ | |
| 162 | db_finalize(pAllStmt); |
| 163 | } |
| 164 | if( nBegin ){ |
| 165 | sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); |
| 166 | nBegin = 0; |
| 167 | } |
| 168 | busy = 0; |
| 169 | db_close(0); |
| 170 | for(i=0; i<nDeleteOnFail; i++){ |
| 171 | file_delete(azDeleteOnFail[i]); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | /* |
| 176 | ** Install a commit hook. Hooks are installed in sequence order. |
| 177 | ** It is an error to install the same commit hook more than once. |
| @@ -1044,11 +1054,11 @@ | |
| 1054 | zFilename, |
| 1055 | zRepositorySchema1, |
| 1056 | zRepositorySchema2, |
| 1057 | (char*)0 |
| 1058 | ); |
| 1059 | db_delete_on_failure(zFilename); |
| 1060 | } |
| 1061 | |
| 1062 | /* |
| 1063 | ** Create the default user accounts in the USER table. |
| 1064 | */ |
| @@ -1555,10 +1565,11 @@ | |
| 1565 | fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); |
| 1566 | } |
| 1567 | file_canonical_name(g.argv[2], &path); |
| 1568 | db_open_repository(blob_str(&path)); |
| 1569 | db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); |
| 1570 | db_delete_on_failure("./_FOSSIL_"); |
| 1571 | db_open_local(); |
| 1572 | db_lset("repository", blob_str(&path)); |
| 1573 | db_record_repository_filename(blob_str(&path)); |
| 1574 | vid = db_int(0, "SELECT pid FROM plink y" |
| 1575 | " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)"); |
| 1576 |
+14
-12
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -62,26 +62,21 @@ | ||
| 62 | 62 | /* Password failure while doing a sync from the command-line interface */ |
| 63 | 63 | url_prompt_for_password(); |
| 64 | 64 | zPw = g.urlPasswd; |
| 65 | 65 | if( !g.dontKeepUrl ) db_set("last-sync-pw", obscure(zPw), 0); |
| 66 | 66 | } |
| 67 | + | |
| 68 | + /* If the first character of the password is "#", then that character is | |
| 69 | + ** not really part of the password - it is an indicator that we should | |
| 70 | + ** use Basic Authentication. So skip that character. | |
| 71 | + */ | |
| 72 | + if( zPw && zPw[0]=='#' ) zPw++; | |
| 67 | 73 | |
| 68 | 74 | /* The login card wants the SHA1 hash of the password, so convert the |
| 69 | 75 | ** password to its SHA1 hash it it isn't already a SHA1 hash. |
| 70 | - ** | |
| 71 | - ** Except, if the password begins with "*" then use the characters | |
| 72 | - ** after the "*" as a cleartext password. Put an "*" at the beginning | |
| 73 | - ** of the password to trick a newer client to use the cleartext password | |
| 74 | - ** protocol required by legacy servers. | |
| 75 | 76 | */ |
| 76 | - if( zPw && zPw[0] ){ | |
| 77 | - if( zPw[0]=='*' ){ | |
| 78 | - zPw++; | |
| 79 | - }else{ | |
| 80 | - zPw = sha1_shared_secret(zPw, zLogin, 0); | |
| 81 | - } | |
| 82 | - } | |
| 77 | + if( zPw && zPw[0] ) zPw = sha1_shared_secret(zPw, zLogin, 0); | |
| 83 | 78 | |
| 84 | 79 | blob_append(&pw, zPw, -1); |
| 85 | 80 | sha1sum_blob(&pw, &sig); |
| 86 | 81 | blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig); |
| 87 | 82 | blob_reset(&pw); |
| @@ -106,10 +101,17 @@ | ||
| 106 | 101 | zSep = "/"; |
| 107 | 102 | } |
| 108 | 103 | blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.urlPath, zSep); |
| 109 | 104 | if( g.urlProxyAuth ){ |
| 110 | 105 | blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth); |
| 106 | + } | |
| 107 | + if( g.urlPasswd && g.urlUser && g.urlPasswd[0]=='#' ){ | |
| 108 | + char *zCredentials = mprintf("%s:%s", g.urlUser, &g.urlPasswd[1]); | |
| 109 | + char *zEncoded = encode64(zCredentials, -1); | |
| 110 | + blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); | |
| 111 | + fossil_free(zEncoded); | |
| 112 | + fossil_free(zCredentials); | |
| 111 | 113 | } |
| 112 | 114 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 113 | 115 | blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n"); |
| 114 | 116 | if( g.fHttpTrace ){ |
| 115 | 117 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 116 | 118 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -62,26 +62,21 @@ | |
| 62 | /* Password failure while doing a sync from the command-line interface */ |
| 63 | url_prompt_for_password(); |
| 64 | zPw = g.urlPasswd; |
| 65 | if( !g.dontKeepUrl ) db_set("last-sync-pw", obscure(zPw), 0); |
| 66 | } |
| 67 | |
| 68 | /* The login card wants the SHA1 hash of the password, so convert the |
| 69 | ** password to its SHA1 hash it it isn't already a SHA1 hash. |
| 70 | ** |
| 71 | ** Except, if the password begins with "*" then use the characters |
| 72 | ** after the "*" as a cleartext password. Put an "*" at the beginning |
| 73 | ** of the password to trick a newer client to use the cleartext password |
| 74 | ** protocol required by legacy servers. |
| 75 | */ |
| 76 | if( zPw && zPw[0] ){ |
| 77 | if( zPw[0]=='*' ){ |
| 78 | zPw++; |
| 79 | }else{ |
| 80 | zPw = sha1_shared_secret(zPw, zLogin, 0); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | blob_append(&pw, zPw, -1); |
| 85 | sha1sum_blob(&pw, &sig); |
| 86 | blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig); |
| 87 | blob_reset(&pw); |
| @@ -106,10 +101,17 @@ | |
| 106 | zSep = "/"; |
| 107 | } |
| 108 | blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.urlPath, zSep); |
| 109 | if( g.urlProxyAuth ){ |
| 110 | blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth); |
| 111 | } |
| 112 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 113 | blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n"); |
| 114 | if( g.fHttpTrace ){ |
| 115 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 116 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -62,26 +62,21 @@ | |
| 62 | /* Password failure while doing a sync from the command-line interface */ |
| 63 | url_prompt_for_password(); |
| 64 | zPw = g.urlPasswd; |
| 65 | if( !g.dontKeepUrl ) db_set("last-sync-pw", obscure(zPw), 0); |
| 66 | } |
| 67 | |
| 68 | /* If the first character of the password is "#", then that character is |
| 69 | ** not really part of the password - it is an indicator that we should |
| 70 | ** use Basic Authentication. So skip that character. |
| 71 | */ |
| 72 | if( zPw && zPw[0]=='#' ) zPw++; |
| 73 | |
| 74 | /* The login card wants the SHA1 hash of the password, so convert the |
| 75 | ** password to its SHA1 hash it it isn't already a SHA1 hash. |
| 76 | */ |
| 77 | if( zPw && zPw[0] ) zPw = sha1_shared_secret(zPw, zLogin, 0); |
| 78 | |
| 79 | blob_append(&pw, zPw, -1); |
| 80 | sha1sum_blob(&pw, &sig); |
| 81 | blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig); |
| 82 | blob_reset(&pw); |
| @@ -106,10 +101,17 @@ | |
| 101 | zSep = "/"; |
| 102 | } |
| 103 | blob_appendf(pHdr, "POST %s%sxfer/xfer HTTP/1.0\r\n", g.urlPath, zSep); |
| 104 | if( g.urlProxyAuth ){ |
| 105 | blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth); |
| 106 | } |
| 107 | if( g.urlPasswd && g.urlUser && g.urlPasswd[0]=='#' ){ |
| 108 | char *zCredentials = mprintf("%s:%s", g.urlUser, &g.urlPasswd[1]); |
| 109 | char *zEncoded = encode64(zCredentials, -1); |
| 110 | blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded); |
| 111 | fossil_free(zEncoded); |
| 112 | fossil_free(zCredentials); |
| 113 | } |
| 114 | blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); |
| 115 | blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n"); |
| 116 | if( g.fHttpTrace ){ |
| 117 | blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
| 118 |
+4
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -552,10 +552,13 @@ | ||
| 552 | 552 | defossilize(zLogin); |
| 553 | 553 | |
| 554 | 554 | if( strcmp(zLogin, "nobody")==0 || strcmp(zLogin,"anonymous")==0 ){ |
| 555 | 555 | return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ |
| 556 | 556 | } |
| 557 | + if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0 ){ | |
| 558 | + return 0; /* Accept Basic Authorization */ | |
| 559 | + } | |
| 557 | 560 | db_prepare(&q, |
| 558 | 561 | "SELECT pw, cap, uid FROM user" |
| 559 | 562 | " WHERE login=%Q" |
| 560 | 563 | " AND login NOT IN ('anonymous','nobody','developer','reader')" |
| 561 | 564 | " AND length(pw)>0", |
| @@ -809,10 +812,11 @@ | ||
| 809 | 812 | if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 810 | 813 | fossil_redirect_home(); |
| 811 | 814 | } |
| 812 | 815 | g.zLogin = "anonymous"; |
| 813 | 816 | login_set_anon_nobody_capabilities(); |
| 817 | + login_check_credentials(); | |
| 814 | 818 | memset(&xfer, 0, sizeof(xfer)); |
| 815 | 819 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 816 | 820 | cgi_set_content_type(g.zContentType); |
| 817 | 821 | if( db_schema_is_outofdate() ){ |
| 818 | 822 | @ error database\sschema\sis\sout-of-date\son\sthe\sserver. |
| 819 | 823 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -552,10 +552,13 @@ | |
| 552 | defossilize(zLogin); |
| 553 | |
| 554 | if( strcmp(zLogin, "nobody")==0 || strcmp(zLogin,"anonymous")==0 ){ |
| 555 | return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ |
| 556 | } |
| 557 | db_prepare(&q, |
| 558 | "SELECT pw, cap, uid FROM user" |
| 559 | " WHERE login=%Q" |
| 560 | " AND login NOT IN ('anonymous','nobody','developer','reader')" |
| 561 | " AND length(pw)>0", |
| @@ -809,10 +812,11 @@ | |
| 809 | if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 810 | fossil_redirect_home(); |
| 811 | } |
| 812 | g.zLogin = "anonymous"; |
| 813 | login_set_anon_nobody_capabilities(); |
| 814 | memset(&xfer, 0, sizeof(xfer)); |
| 815 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 816 | cgi_set_content_type(g.zContentType); |
| 817 | if( db_schema_is_outofdate() ){ |
| 818 | @ error database\sschema\sis\sout-of-date\son\sthe\sserver. |
| 819 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -552,10 +552,13 @@ | |
| 552 | defossilize(zLogin); |
| 553 | |
| 554 | if( strcmp(zLogin, "nobody")==0 || strcmp(zLogin,"anonymous")==0 ){ |
| 555 | return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ |
| 556 | } |
| 557 | if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0 ){ |
| 558 | return 0; /* Accept Basic Authorization */ |
| 559 | } |
| 560 | db_prepare(&q, |
| 561 | "SELECT pw, cap, uid FROM user" |
| 562 | " WHERE login=%Q" |
| 563 | " AND login NOT IN ('anonymous','nobody','developer','reader')" |
| 564 | " AND length(pw)>0", |
| @@ -809,10 +812,11 @@ | |
| 812 | if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 813 | fossil_redirect_home(); |
| 814 | } |
| 815 | g.zLogin = "anonymous"; |
| 816 | login_set_anon_nobody_capabilities(); |
| 817 | login_check_credentials(); |
| 818 | memset(&xfer, 0, sizeof(xfer)); |
| 819 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 820 | cgi_set_content_type(g.zContentType); |
| 821 | if( db_schema_is_outofdate() ){ |
| 822 | @ error database\sschema\sis\sout-of-date\son\sthe\sserver. |
| 823 |
+4
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -552,10 +552,13 @@ | ||
| 552 | 552 | defossilize(zLogin); |
| 553 | 553 | |
| 554 | 554 | if( strcmp(zLogin, "nobody")==0 || strcmp(zLogin,"anonymous")==0 ){ |
| 555 | 555 | return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ |
| 556 | 556 | } |
| 557 | + if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0 ){ | |
| 558 | + return 0; /* Accept Basic Authorization */ | |
| 559 | + } | |
| 557 | 560 | db_prepare(&q, |
| 558 | 561 | "SELECT pw, cap, uid FROM user" |
| 559 | 562 | " WHERE login=%Q" |
| 560 | 563 | " AND login NOT IN ('anonymous','nobody','developer','reader')" |
| 561 | 564 | " AND length(pw)>0", |
| @@ -809,10 +812,11 @@ | ||
| 809 | 812 | if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 810 | 813 | fossil_redirect_home(); |
| 811 | 814 | } |
| 812 | 815 | g.zLogin = "anonymous"; |
| 813 | 816 | login_set_anon_nobody_capabilities(); |
| 817 | + login_check_credentials(); | |
| 814 | 818 | memset(&xfer, 0, sizeof(xfer)); |
| 815 | 819 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 816 | 820 | cgi_set_content_type(g.zContentType); |
| 817 | 821 | if( db_schema_is_outofdate() ){ |
| 818 | 822 | @ error database\sschema\sis\sout-of-date\son\sthe\sserver. |
| 819 | 823 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -552,10 +552,13 @@ | |
| 552 | defossilize(zLogin); |
| 553 | |
| 554 | if( strcmp(zLogin, "nobody")==0 || strcmp(zLogin,"anonymous")==0 ){ |
| 555 | return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ |
| 556 | } |
| 557 | db_prepare(&q, |
| 558 | "SELECT pw, cap, uid FROM user" |
| 559 | " WHERE login=%Q" |
| 560 | " AND login NOT IN ('anonymous','nobody','developer','reader')" |
| 561 | " AND length(pw)>0", |
| @@ -809,10 +812,11 @@ | |
| 809 | if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 810 | fossil_redirect_home(); |
| 811 | } |
| 812 | g.zLogin = "anonymous"; |
| 813 | login_set_anon_nobody_capabilities(); |
| 814 | memset(&xfer, 0, sizeof(xfer)); |
| 815 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 816 | cgi_set_content_type(g.zContentType); |
| 817 | if( db_schema_is_outofdate() ){ |
| 818 | @ error database\sschema\sis\sout-of-date\son\sthe\sserver. |
| 819 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -552,10 +552,13 @@ | |
| 552 | defossilize(zLogin); |
| 553 | |
| 554 | if( strcmp(zLogin, "nobody")==0 || strcmp(zLogin,"anonymous")==0 ){ |
| 555 | return 0; /* Anybody is allowed to sync as "nobody" or "anonymous" */ |
| 556 | } |
| 557 | if( fossil_strcmp(P("REMOTE_USER"), zLogin)==0 ){ |
| 558 | return 0; /* Accept Basic Authorization */ |
| 559 | } |
| 560 | db_prepare(&q, |
| 561 | "SELECT pw, cap, uid FROM user" |
| 562 | " WHERE login=%Q" |
| 563 | " AND login NOT IN ('anonymous','nobody','developer','reader')" |
| 564 | " AND length(pw)>0", |
| @@ -809,10 +812,11 @@ | |
| 812 | if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ |
| 813 | fossil_redirect_home(); |
| 814 | } |
| 815 | g.zLogin = "anonymous"; |
| 816 | login_set_anon_nobody_capabilities(); |
| 817 | login_check_credentials(); |
| 818 | memset(&xfer, 0, sizeof(xfer)); |
| 819 | blobarray_zero(xfer.aToken, count(xfer.aToken)); |
| 820 | cgi_set_content_type(g.zContentType); |
| 821 | if( db_schema_is_outofdate() ){ |
| 822 | @ error database\sschema\sis\sout-of-date\son\sthe\sserver. |
| 823 |
+8
-3
| --- www/server.wiki | ||
| +++ www/server.wiki | ||
| @@ -15,11 +15,13 @@ | ||
| 15 | 15 | <p> |
| 16 | 16 | Both of these commands start a Fossil server on port 8080 on the local machine, |
| 17 | 17 | which can be accessed with the URL: <tt>http://localhost:8080/</tt> using any |
| 18 | 18 | handy web browser. The difference between the two commands is that "ui", in |
| 19 | 19 | addition to starting the Fossil server, also starts a web browser and points it |
| 20 | -to the URL mentioned above. | |
| 20 | +to the URL mentioned above. On the other hand, the "ui" command binds to | |
| 21 | +the loopback IP address only (127.0.0.1) so that the "ui" command cannot be | |
| 22 | +used to serve content to a different machine. | |
| 21 | 23 | </p> |
| 22 | 24 | <p> |
| 23 | 25 | NOTES: |
| 24 | 26 | <ol> |
| 25 | 27 | <li>The option "--port NNN" will start the server on port "NNN" instead of 8080. |
| @@ -70,19 +72,22 @@ | ||
| 70 | 72 | </p> |
| 71 | 73 | </blockquote> |
| 72 | 74 | |
| 73 | 75 | <h3>Serving multiple repositories with one script</h3><blockquote> |
| 74 | 76 | <p> |
| 75 | -This scenario is almost identical to the previous one. However, here we will assume you have multiple repositories, in one directory (call it 'fossils'). So as before, create a script (again, 'repo'): | |
| 77 | +This scenario is almost identical to the previous one. However, here we will assume you have multiple repositories, in one directory. | |
| 78 | +(Call the directory 'fossils'). All repositories served, in this case, must | |
| 79 | +use the ".fossil" filename suffix. | |
| 80 | +As before, create a script (again, 'repo'): | |
| 76 | 81 | <blockquote><tt> |
| 77 | 82 | #!/path-to/fossil<br> |
| 78 | 83 | directory: /path-to-repo/fossils<br> |
| 79 | 84 | notfound: http://url-to-go-to-if-repo-not-found/ |
| 80 | 85 | </tt></blockquote> |
| 81 | 86 | </p> |
| 82 | 87 | <p> |
| 83 | -Once deployed, a URL like: <tt>http://mydomain.org/cgi-bin/repo/XYZ</tt> will serve up the repository "fossils/XYX" (if it exists). This makes serving multiple projects on one server pretty painless. | |
| 88 | +Once deployed, a URL like: <tt>http://mydomain.org/cgi-bin/repo/XYZ</tt> will serve up the repository "fossils/XYX.fossil" (if it exists). This makes serving multiple projects on one server pretty painless. | |
| 84 | 89 | </p> |
| 85 | 90 | </blockquote> |
| 86 | 91 | |
| 87 | 92 | <h2>Securing a repository with SSL</h2><blockquote> |
| 88 | 93 | <p> |
| 89 | 94 |
| --- www/server.wiki | |
| +++ www/server.wiki | |
| @@ -15,11 +15,13 @@ | |
| 15 | <p> |
| 16 | Both of these commands start a Fossil server on port 8080 on the local machine, |
| 17 | which can be accessed with the URL: <tt>http://localhost:8080/</tt> using any |
| 18 | handy web browser. The difference between the two commands is that "ui", in |
| 19 | addition to starting the Fossil server, also starts a web browser and points it |
| 20 | to the URL mentioned above. |
| 21 | </p> |
| 22 | <p> |
| 23 | NOTES: |
| 24 | <ol> |
| 25 | <li>The option "--port NNN" will start the server on port "NNN" instead of 8080. |
| @@ -70,19 +72,22 @@ | |
| 70 | </p> |
| 71 | </blockquote> |
| 72 | |
| 73 | <h3>Serving multiple repositories with one script</h3><blockquote> |
| 74 | <p> |
| 75 | This scenario is almost identical to the previous one. However, here we will assume you have multiple repositories, in one directory (call it 'fossils'). So as before, create a script (again, 'repo'): |
| 76 | <blockquote><tt> |
| 77 | #!/path-to/fossil<br> |
| 78 | directory: /path-to-repo/fossils<br> |
| 79 | notfound: http://url-to-go-to-if-repo-not-found/ |
| 80 | </tt></blockquote> |
| 81 | </p> |
| 82 | <p> |
| 83 | Once deployed, a URL like: <tt>http://mydomain.org/cgi-bin/repo/XYZ</tt> will serve up the repository "fossils/XYX" (if it exists). This makes serving multiple projects on one server pretty painless. |
| 84 | </p> |
| 85 | </blockquote> |
| 86 | |
| 87 | <h2>Securing a repository with SSL</h2><blockquote> |
| 88 | <p> |
| 89 |
| --- www/server.wiki | |
| +++ www/server.wiki | |
| @@ -15,11 +15,13 @@ | |
| 15 | <p> |
| 16 | Both of these commands start a Fossil server on port 8080 on the local machine, |
| 17 | which can be accessed with the URL: <tt>http://localhost:8080/</tt> using any |
| 18 | handy web browser. The difference between the two commands is that "ui", in |
| 19 | addition to starting the Fossil server, also starts a web browser and points it |
| 20 | to the URL mentioned above. On the other hand, the "ui" command binds to |
| 21 | the loopback IP address only (127.0.0.1) so that the "ui" command cannot be |
| 22 | used to serve content to a different machine. |
| 23 | </p> |
| 24 | <p> |
| 25 | NOTES: |
| 26 | <ol> |
| 27 | <li>The option "--port NNN" will start the server on port "NNN" instead of 8080. |
| @@ -70,19 +72,22 @@ | |
| 72 | </p> |
| 73 | </blockquote> |
| 74 | |
| 75 | <h3>Serving multiple repositories with one script</h3><blockquote> |
| 76 | <p> |
| 77 | This scenario is almost identical to the previous one. However, here we will assume you have multiple repositories, in one directory. |
| 78 | (Call the directory 'fossils'). All repositories served, in this case, must |
| 79 | use the ".fossil" filename suffix. |
| 80 | As before, create a script (again, 'repo'): |
| 81 | <blockquote><tt> |
| 82 | #!/path-to/fossil<br> |
| 83 | directory: /path-to-repo/fossils<br> |
| 84 | notfound: http://url-to-go-to-if-repo-not-found/ |
| 85 | </tt></blockquote> |
| 86 | </p> |
| 87 | <p> |
| 88 | Once deployed, a URL like: <tt>http://mydomain.org/cgi-bin/repo/XYZ</tt> will serve up the repository "fossils/XYX.fossil" (if it exists). This makes serving multiple projects on one server pretty painless. |
| 89 | </p> |
| 90 | </blockquote> |
| 91 | |
| 92 | <h2>Securing a repository with SSL</h2><blockquote> |
| 93 | <p> |
| 94 |