/* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to push, pull, and sync a repository */ #include "config.h" #include "sync.h" #include /* ** If the repository is configured for autosyncing, then do an ** autosync. Bits of the "flags" parameter determine details of behavior: ** ** SYNC_PULL Pull content from the server to the local repo ** SYNC_PUSH Push content from local up to the server ** SYNC_CKIN_LOCK Take a check-in lock on the current checkout. ** SYNC_VERBOSE Extra output ** ** Return the number of errors. ** ** The autosync setting can be a boolean or "pullonly". No autosync ** is attempted if the autosync setting is off, and only auto-pull is ** attempted if autosync is set to "pullonly". The check-in lock is ** not acquired unless autosync is set to "on". ** ** If dont-push setting is true, that is the same as having autosync ** set to pullonly. */ int autosync(int flags){ const char *zAutosync; int rc; int configSync = 0; /* configuration changes transferred */ if( g.fNoSync ){ return 0; } zAutosync = db_get("autosync", 0); if( zAutosync==0 ) zAutosync = "on"; /* defend against misconfig */ if( is_false(zAutosync) ) return 0; if( db_get_boolean("dont-push",0) || fossil_strncmp(zAutosync,"pull",4)==0 ){ flags &= ~SYNC_CKIN_LOCK; if( flags & SYNC_PUSH ) return 0; } url_parse(0, URL_REMEMBER); if( g.url.protocol==0 ) return 0; if( g.url.user!=0 && g.url.passwd==0 ){ g.url.passwd = unobscure(db_get("last-sync-pw", 0)); g.url.flags |= URL_PROMPT_PW; url_prompt_for_password(); } g.zHttpAuth = get_httpauth(); url_remember(); if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE; fossil_print("Autosync: %s\n", g.url.canonical); url_enable_proxy("via proxy: "); rc = client_sync(flags, configSync, 0, 0); return rc; } /* ** This routine will try a number of times to perform autosync with a ** 0.5 second sleep between attempts. ** ** Return zero on success and non-zero on a failure. If failure occurs ** and doPrompt flag is true, ask the user if they want to continue, and ** if they answer "yes" then return zero in spite of the failure. */ int autosync_loop(int flags, int nTries, int doPrompt){ int n = 0; int rc = 0; if( (flags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) && db_get_boolean("uv-sync",0) ){ flags |= SYNC_UNVERSIONED; } while( (n==0 || n fossil remote ** ** With no arguments, this command shows the current default remote. ** Or if there is no default, it shows "off". The default remote is ** used by autosync. The default remote is whatever URL was specified ** for the most recent "sync", "push", or "pull" command that omitted ** the --once option. ** ** > fossil remote add NAME URL ** ** Add a new URL to the set of remotes. The new URL is assigned the ** symbolic identifier "NAME". Subsequently, NAME can be used in place ** of the full URL on commands like "push" and "pull". ** ** > fossil remote delete NAME ** ** Delete a URL previously added by the "add" subcommand. ** ** > fossil remote list ** ** Show all remote URLs ** ** > fossil remote off ** ** Disable the default URL. Use this as a shorthand to prevent ** autosync while in airplane mode, for example. ** ** > fossil remote URL ** ** Make URL the new default URL. The prior default URL is replaced. */ void remote_url_cmd(void){ char *zUrl, *zArg; int nArg; db_find_and_open_repository(0, 0); /* We should be done with options.. */ verify_all_options(); if( g.argc==2 ){ /* "fossil remote" with no arguments: Show the last sync URL. */ zUrl = db_get("last-sync-url", 0); if( zUrl==0 ){ fossil_print("off\n"); }else{ url_parse(zUrl, 0); fossil_print("%s\n", g.url.canonical); } return; } zArg = g.argv[2]; nArg = (int)strlen(zArg); if( strcmp(zArg,"off")==0 ){ /* fossil remote off ** Forget the last-sync-URL and its password */ if( g.argc!=3 ) usage("off"); remote_delete_default: db_multi_exec( "DELETE FROM config WHERE name GLOB 'last-sync-*';" ); return; } if( strncmp(zArg, "list", nArg)==0 || strcmp(zArg,"ls")==0 ){ Stmt q; if( g.argc!=3 ) usage("list"); db_prepare(&q, "SELECT 'default', value FROM config WHERE name='last-sync-url'" " UNION ALL " "SELECT substr(name,10), value FROM config" " WHERE name GLOB 'sync-url:*'" " ORDER BY 1" ); while( db_step(&q)==SQLITE_ROW ){ fossil_print("%-18s %s\n", db_column_text(&q,0), db_column_text(&q,1)); } db_finalize(&q); return; } if( strcmp(zArg, "add")==0 ){ char *zName; char *zUrl; UrlData x; if( g.argc!=5 ) usage("add NAME URL"); memset(&x, 0, sizeof(x)); zName = g.argv[3]; zUrl = g.argv[4]; if( strcmp(zName,"default")==0 ) goto remote_add_default; url_parse_local(zUrl, URL_PROMPT_PW, &x); db_begin_write(); db_multi_exec( "REPLACE INTO config(name, value, mtime)" " VALUES('sync-url:%q',%Q,now())", zName, x.canonical ); db_multi_exec( "REPLACE INTO config(name, value, mtime)" " VALUES('sync-pw:%q',obscure(%Q),now())", zName, x.passwd ); db_commit_transaction(); return; } if( strncmp(zArg, "delete", nArg)==0 ){ char *zName; if( g.argc!=4 ) usage("delete NAME"); zName = g.argv[3]; if( strcmp(zName,"default")==0 ) goto remote_delete_default; db_begin_write(); db_multi_exec("DELETE FROM config WHERE name glob 'sync-url:%q'", zName); db_multi_exec("DELETE FROM config WHERE name glob 'sync-pw:%q'", zName); db_commit_transaction(); return; } if( sqlite3_strlike("http://%",zArg,0)==0 || sqlite3_strlike("https://%",zArg,0)==0 || sqlite3_strlike("ssh:%",zArg,0)==0 || sqlite3_strlike("file:%",zArg,0)==0 || db_exists("SELECT 1 FROM config WHERE name='sync-url:%q'",zArg) ){ remote_add_default: db_unset("last-sync-url", 0); db_unset("last-sync-pw", 0); url_parse(g.argv[2], URL_REMEMBER|URL_PROMPT_PW|URL_ASK_REMEMBER_PW); url_remember(); return; } fossil_fatal("unknown command \"%s\" - should be a URL or one of: " "add delete list off", zArg); } /* ** COMMAND: backup* ** ** Usage: %fossil backup ?OPTIONS? FILE|DIRECTORY ** ** Make a backup of the repository into the named file or into the named ** directory. This backup is guaranteed to be consistent even if there are ** concurrent chnages taking place on the repository. In other words, it ** is safe to run "fossil backup" on a repository that is in active use. ** ** Only the main repository database is backed up by this command. The ** open checkout file (if any) is not saved. Nor is the global configuration ** database. ** ** Options: ** ** --overwrite OK to overwrite an existing file. ** -R NAME Filename of the repository to backup */ void backup_cmd(void){ char *zDest; int bOverwrite = 0; db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); bOverwrite = find_option("overwrite",0,0)!=0; verify_all_options(); if( g.argc!=3 ){ usage("FILE|DIRECTORY"); } zDest = g.argv[2]; if( file_isdir(zDest, ExtFILE)==1 ){ zDest = mprintf("%s/%s", zDest, file_tail(g.zRepositoryName)); } if( file_isfile(zDest, ExtFILE) ){ if( bOverwrite ){ if( file_delete(zDest) ){ fossil_fatal("unable to delete old copy of \"%s\"", zDest); } }else{ fossil_fatal("backup \"%s\" already exists", zDest); } } db_multi_exec("VACUUM repository INTO %Q", zDest); }