diff options
author | drh <drh@noemail.net> | 2004-02-01 01:22:50 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2004-02-01 01:22:50 +0000 |
commit | 22fbcb8dddf41b152d1b9e3cb45018bda2f9b1f5 (patch) | |
tree | f98edd8e0094ecdd8e4ab6121b2e216c00cdffee /src | |
parent | 3914aed1de102062f8d41585c7c21451c75f8917 (diff) | |
download | sqlite-22fbcb8dddf41b152d1b9e3cb45018bda2f9b1f5.tar.gz sqlite-22fbcb8dddf41b152d1b9e3cb45018bda2f9b1f5.zip |
Fix the handling of -init option to the sqlite shell. Ticket #568.
Also add hooks for encrypting the database. (CVS 1206)
FossilOrigin-Name: 3c796de8d1af55944f396f08feaa9e69c1652896
Diffstat (limited to 'src')
-rw-r--r-- | src/shell.c | 192 | ||||
-rw-r--r-- | src/sqlite.h.in | 29 | ||||
-rw-r--r-- | src/tclsqlite.c | 99 |
3 files changed, 205 insertions, 115 deletions
diff --git a/src/shell.c b/src/shell.c index 0fe82be3b..1cf0da0a1 100644 --- a/src/shell.c +++ b/src/shell.c @@ -12,7 +12,7 @@ ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** -** $Id: shell.c,v 1.83 2003/12/04 20:51:41 drh Exp $ +** $Id: shell.c,v 1.84 2004/02/01 01:22:51 drh Exp $ */ #include <stdlib.h> #include <string.h> @@ -186,6 +186,7 @@ struct callback_data { ** .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ + char *zKey; /* Encryption key */ }; /* @@ -489,6 +490,9 @@ static char zHelp[] = ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" +#ifdef SQLITE_HAS_CRYPTO + ".rekey OLD NEW NEW Change the encryption key\n" +#endif ".schema ?TABLE? Show the CREATE statements\n" ".separator STRING Change separator string for \"list\" mode\n" ".show Show the current values for various settings\n" @@ -507,20 +511,21 @@ static void process_input(struct callback_data *p, FILE *in); static void open_db(struct callback_data *p){ if( p->db==0 ){ char *zErrMsg = 0; - p->db = db = sqlite_open(p->zDbFilename, 0666, &zErrMsg); - if( db==0 ){ - p->db = db = sqlite_open(p->zDbFilename, 0444, &zErrMsg); - if( db==0 ){ - if( zErrMsg ){ - fprintf(stderr,"Unable to open database \"%s\": %s\n", - p->zDbFilename, zErrMsg); - }else{ - fprintf(stderr,"Unable to open database %s\n", p->zDbFilename); - } - exit(1); +#ifdef SQLITE_HAS_CRYPTO + if( p->zKey && p->zKey[0] ){ + int n = strlen(p->zKey); + p->db = sqlite_open_encrypted(p->zDbFilename, p->zKey, n, &zErrMsg); + }else +#endif + p->db = sqlite_open(p->zDbFilename, 0, &zErrMsg); + if( p->db==0 ){ + if( zErrMsg ){ + fprintf(stderr,"Unable to open database \"%s\": %s\n", + p->zDbFilename, zErrMsg); }else{ - fprintf(stderr,"Database \"%s\" opened READ ONLY!\n", p->zDbFilename); + fprintf(stderr,"Unable to open database %s\n", p->zDbFilename); } + exit(1); } } } @@ -781,6 +786,22 @@ static int do_meta_command(char *zLine, struct callback_data *p){ } }else +#ifdef SQLITE_HAS_CRYPTO + if( c=='r' && strncmp(azArg[0],"rekey", n)==0 && nArg==4 ){ + char *zOld = p->zKey; + if( zOld==0 ) zOld = ""; + if( strcmp(azArg[1],zOld) ){ + fprintf(stderr,"old key is incorrect\n"); + }else if( strcmp(azArg[2], azArg[3]) ){ + fprintf(stderr,"2nd copy of new key does not match the 1st\n"); + }else{ + sqlite_freemem(p->zKey); + p->zKey = sqlite_mprintf("%s", azArg[2]); + sqlite_rekey(p->db, p->zKey, strlen(p->zKey)); + } + }else +#endif + if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ struct callback_data data; char *zErrMsg = 0; @@ -1105,9 +1126,13 @@ static char *find_home_dir(void){ ** Read input from the file given by sqliterc_override. Or if that ** parameter is NULL, take input from ~/.sqliterc */ -static void process_sqliterc(struct callback_data *p, char *sqliterc_override){ +static void process_sqliterc( + struct callback_data *p, /* Configuration data */ + const char *sqliterc_override /* Name of config file. NULL to use default */ +){ char *home_dir = NULL; - char *sqliterc = sqliterc_override; + const char *sqliterc = sqliterc_override; + char *zBuf; FILE *in = NULL; if (sqliterc == NULL) { @@ -1116,17 +1141,20 @@ static void process_sqliterc(struct callback_data *p, char *sqliterc_override){ fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0); return; } - sqliterc = malloc(strlen(home_dir) + 15); - if( sqliterc==0 ){ + zBuf = malloc(strlen(home_dir) + 15); + if( zBuf==0 ){ fprintf(stderr,"%s: out of memory!\n", Argv0); exit(1); } - sprintf(sqliterc,"%s/.sqliterc",home_dir); + sprintf(zBuf,"%s/.sqliterc",home_dir); free(home_dir); + sqliterc = (const char*)zBuf; } in = fopen(sqliterc,"r"); - if(in && isatty(fileno(stdout))) { - printf("Loading resources from %s\n",sqliterc); + if( in ){ + if( isatty(fileno(stdout)) ){ + printf("Loading resources from %s\n",sqliterc); + } process_input(p,in); fclose(in); } @@ -1174,15 +1202,13 @@ void main_init(struct callback_data *data) { int main(int argc, char **argv){ char *zErrMsg = 0; struct callback_data data; - int origArgc = argc; - char **origArgv = argv; + const char *zInitFile = 0; + char *zFirstCmd = 0; int i; extern int sqliteOsFileExists(const char*); #ifdef __MACOS__ argc = ccommand(&argv); - origArgc = argc; - origArgv = argv; #endif Argv0 = argv[0]; @@ -1195,15 +1221,30 @@ int main(int argc, char **argv){ signal(SIGINT, interrupt_handler); #endif - /* Locate the name of the database file + /* Do an initial pass through the command-line argument to locate + ** the name of the database file, the name of the initialization file, + ** and the first command to execute. */ - for(i=1; i<argc; i++){ + for(i=1; i<argc-1; i++){ if( argv[i][0]!='-' ) break; if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){ i++; + }else if( strcmp(argv[i],"-init")==0 ){ + i++; + zInitFile = argv[i]; + }else if( strcmp(argv[i],"-key")==0 ){ + i++; + data.zKey = sqlite_mprintf("%s",argv[i]); } } - data.zDbFilename = i<argc ? argv[i] : ":memory:"; + if( i<argc ){ + data.zDbFilename = argv[i++]; + }else{ + data.zDbFilename = ":memory:"; + } + if( i<argc ){ + zFirstCmd = argv[i++]; + } data.out = stdout; /* Go ahead and open the database file if it already exists. If the @@ -1215,96 +1256,63 @@ int main(int argc, char **argv){ open_db(&data); } - /* Process the ~/.sqliterc file, if there is one + /* Process the initialization file if there is one. If no -init option + ** is given on the command line, look for a file named ~/.sqliterc and + ** try to process it. */ - process_sqliterc(&data,NULL); + process_sqliterc(&data,zInitFile); - /* Process command-line options + /* Make a second pass through the command-line argument and set + ** options. This second pass is delayed until after the initialization + ** file is processed so that the command-line arguments will override + ** settings in the initialization file. */ - while( argc>=2 && argv[1][0]=='-' ){ - if( argc>=3 && strcmp(argv[1],"-init")==0 ){ - /* If we get a -init to do, we have to pretend that - ** it replaced the .sqliterc file. Soooo, in order to - ** do that we need to start from scratch...*/ - main_init(&data); - - /* treat this file as the sqliterc... */ - process_sqliterc(&data,argv[2]); - - /* fix up the command line so we do not re-read - ** the option next time around... */ - { - int i = 1; - for(i=1;i<=argc-2;i++) { - argv[i] = argv[i+2]; - } - } - origArgc-=2; - - /* and reset the command line options to be re-read.*/ - argv = origArgv; - argc = origArgc; - - }else if( strcmp(argv[1],"-html")==0 ){ + for(i=1; i<argc && argv[i][0]=='-'; i++){ + char *z = argv[i]; + if( strcmp(z,"-init")==0 || strcmp(z,"-key")==0 ){ + i++; + }else if( strcmp(z,"-html")==0 ){ data.mode = MODE_Html; - argc--; - argv++; - }else if( strcmp(argv[1],"-list")==0 ){ + }else if( strcmp(z,"-list")==0 ){ data.mode = MODE_List; - argc--; - argv++; - }else if( strcmp(argv[1],"-line")==0 ){ + }else if( strcmp(z,"-line")==0 ){ data.mode = MODE_Line; - argc--; - argv++; - }else if( strcmp(argv[1],"-column")==0 ){ + }else if( strcmp(z,"-column")==0 ){ data.mode = MODE_Column; - argc--; - argv++; - }else if( argc>=3 && strcmp(argv[1],"-separator")==0 ){ - sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[2]); - argc -= 2; - argv += 2; - }else if( argc>=3 && strcmp(argv[1],"-nullvalue")==0 ){ - sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[2]); - argc -= 2; - argv += 2; - }else if( strcmp(argv[1],"-header")==0 ){ + }else if( strcmp(z,"-separator")==0 ){ + i++; + sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[i]); + }else if( strcmp(z,"-nullvalue")==0 ){ + i++; + sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[i]); + }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; - argc--; - argv++; - }else if( strcmp(argv[1],"-noheader")==0 ){ + }else if( strcmp(z,"-noheader")==0 ){ data.showHeader = 0; - argc--; - argv++; - }else if( strcmp(argv[1],"-echo")==0 ){ + }else if( strcmp(z,"-echo")==0 ){ data.echoOn = 1; - argc--; - argv++; - }else if( strcmp(argv[1],"-version")==0 ){ + }else if( strcmp(z,"-version")==0 ){ printf("%s\n", sqlite_version); return 1; - }else if( strcmp(argv[1],"-help")==0 ){ + }else if( strcmp(z,"-help")==0 ){ usage(1); }else{ - fprintf(stderr,"%s: unknown option: %s\n", Argv0, argv[1]); + fprintf(stderr,"%s: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } } - if( argc<2 ){ - usage(0); - }else if( argc==3 ){ + if( zFirstCmd ){ /* Run just the command that follows the database name */ - if( argv[2][0]=='.' ){ - do_meta_command(argv[2], &data); + if( zFirstCmd[0]=='.' ){ + do_meta_command(zFirstCmd, &data); exit(0); }else{ int rc; open_db(&data); - rc = sqlite_exec(db, argv[2], callback, &data, &zErrMsg); + rc = sqlite_exec(db, zFirstCmd, callback, &data, &zErrMsg); if( rc!=0 && zErrMsg!=0 ){ fprintf(stderr,"SQL error: %s\n", zErrMsg); exit(1); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index e493a13fe..a2c6d1557 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.54 2004/01/15 02:44:03 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.55 2004/02/01 01:22:52 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -773,6 +773,33 @@ void sqlite_progress_handler(sqlite*, int, int(*)(void*), void*); */ void *sqlite_commit_hook(sqlite*, int(*)(void*), void*); +/* +** Open an encrypted SQLite database. If pKey==0 or nKey==0, this routine +** is the same as sqlite_open(). +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +sqlite *sqlite_open_encrypted( + const char *zFilename, /* Name of the encrypted database */ + const void *pKey, /* Pointer to the key */ + int nKey, /* Number of bytes in the key */ + char **pzErrmsg /* Write error message here */ +); + +/* +** Change the key on an open database. If the current database is not +** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the +** database is decrypted. +** +** The code to implement this API is not available in the public release +** of SQLite. +*/ +int sqlite_rekey( + sqlite *db, /* Database to be rekeyed */ + const void *pKey, int nKey /* The new key */ +); + #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 70706306a..6fb3c3add 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.54 2004/01/15 02:44:03 drh Exp $ +** $Id: tclsqlite.c,v 1.55 2004/02/01 01:22:52 drh Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -486,19 +486,21 @@ static int auth_callback( static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ SqliteDb *pDb = (SqliteDb*)cd; int choice; + int rc = TCL_OK; static const char *DB_strs[] = { "authorizer", "busy", "changes", "close", "commit_hook", "complete", "errorcode", "eval", "function", "last_insert_rowid", "onecolumn", "progress", - "timeout", "trace", 0 + "rekey", "timeout", "trace", + 0 }; enum DB_enum { DB_AUTHORIZER, DB_BUSY, DB_CHANGES, DB_CLOSE, DB_COMMIT_HOOK, DB_COMPLETE, DB_ERRORCODE, DB_EVAL, DB_FUNCTION, DB_LAST_INSERT_ROWID, DB_ONECOLUMN, DB_PROGRESS, - DB_TIMEOUT, DB_TRACE, + DB_REKEY, DB_TIMEOUT, DB_TRACE, }; if( objc<2 ){ @@ -747,7 +749,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ CallbackData cbData; char *zErrMsg; char *zSql; - int rc; #ifdef UTF_TRANSLATION_NEEDED Tcl_DString dSql; int i; @@ -865,7 +866,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** Return a single column from a single row of the given SQL query. */ case DB_ONECOLUMN: { - int rc; char *zSql; char *zErrMsg = 0; if( objc!=3 ){ @@ -875,7 +875,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ zSql = Tcl_GetStringFromObj(objv[2], 0); rc = sqlite_exec(pDb->db, zSql, DbEvalCallback3, interp, &zErrMsg); if( rc==SQLITE_ABORT ){ - /* Do nothing. This is normal. */ + rc = SQLITE_OK; }else if( zErrMsg ){ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); free(zErrMsg); @@ -888,6 +888,29 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } /* + ** $db rekey KEY + ** + ** Change the encryption key on the currently open database. + */ + case DB_REKEY: { + int nKey; + void *pKey; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "KEY"); + return TCL_ERROR; + } + pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey); +#ifdef SQLITE_HAS_CRYPTO + rc = sqlite_rekey(pDb->db, pKey, nKey); + if( rc ){ + Tcl_AppendResult(interp, sqlite_error_string(rc), 0); + rc = TCL_ERROR; + } +#endif + break; + } + + /* ** $db timeout MILLESECONDS ** ** Delay for the number of milliseconds specified when a file is locked. @@ -940,11 +963,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } } /* End of the SWITCH statement */ - return TCL_OK; + return rc; } /* -** sqlite DBNAME FILENAME ?MODE? +** sqlite DBNAME FILENAME ?MODE? ?-key KEY? ** ** This is the main Tcl command. When the "sqlite" Tcl command is ** invoked, this routine runs to process that command. @@ -974,21 +997,34 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ ** not. Used by tests to make sure the library was compiled ** correctly. */ -static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){ +static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ int mode; SqliteDb *p; + void *pKey = 0; + int nKey = 0; + const char *zArg; char *zErrMsg; + const char *zFile; char zBuf[80]; - if( argc==2 ){ - if( strcmp(argv[1],"-encoding")==0 ){ + if( objc==2 ){ + zArg = Tcl_GetStringFromObj(objv[1], 0); + if( strcmp(zArg,"-encoding")==0 ){ Tcl_AppendResult(interp,sqlite_encoding,0); return TCL_OK; } - if( strcmp(argv[1],"-version")==0 ){ + if( strcmp(zArg,"-version")==0 ){ Tcl_AppendResult(interp,sqlite_version,0); return TCL_OK; } - if( strcmp(argv[1],"-tcl-uses-utf")==0 ){ + if( strcmp(zArg,"-has-crypto")==0 ){ +#ifdef SQLITE_HAS_CRYPTO + Tcl_AppendResult(interp,"1",0); +#else + Tcl_AppendResult(interp,"0",0); +#endif + return TCL_OK; + } + if( strcmp(zArg,"-tcl-uses-utf")==0 ){ #ifdef TCL_UTF_MAX Tcl_AppendResult(interp,"1",0); #else @@ -997,14 +1033,26 @@ static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){ return TCL_OK; } } - if( argc!=3 && argc!=4 ){ - Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0], - " HANDLE FILENAME ?MODE?\"", 0); + if( objc==5 || objc==6 ){ + zArg = Tcl_GetStringFromObj(objv[objc-2], 0); + if( strcmp(zArg,"-key")==0 ){ + pKey = Tcl_GetByteArrayFromObj(objv[objc-1], &nKey); + objc -= 2; + } + } + if( objc!=3 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, +#ifdef SQLITE_HAS_CRYPTO + "HANDLE FILENAME ?-key CRYPTOKEY?" +#else + "HANDLE FILENAME ?MODE?" +#endif + ); return TCL_ERROR; } - if( argc==3 ){ + if( objc==3 ){ mode = 0666; - }else if( Tcl_GetInt(interp, argv[3], &mode)!=TCL_OK ){ + }else if( Tcl_GetIntFromObj(interp, objv[3], &mode)!=TCL_OK ){ return TCL_ERROR; } zErrMsg = 0; @@ -1014,14 +1062,21 @@ static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){ return TCL_ERROR; } memset(p, 0, sizeof(*p)); - p->db = sqlite_open(argv[2], mode, &zErrMsg); + zFile = Tcl_GetStringFromObj(objv[2], 0); +#ifdef SQLITE_HAS_CRYPTO + if( nKey>0 ){ + p->db = sqlite_open_encrypted(zFile, pKey, nKey, &zErrMsg); + }else +#endif + p->db = sqlite_open(zFile, mode, &zErrMsg); if( p->db==0 ){ Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); Tcl_Free((char*)p); free(zErrMsg); return TCL_ERROR; } - Tcl_CreateObjCommand(interp, argv[1], DbObjCmd, (char*)p, DbDeleteCmd); + zArg = Tcl_GetStringFromObj(objv[1], 0); + Tcl_CreateObjCommand(interp, zArg, DbObjCmd, (char*)p, DbDeleteCmd); /* The return value is the value of the sqlite* pointer */ @@ -1063,13 +1118,13 @@ static int DbMain(void *cd, Tcl_Interp *interp, int argc, char **argv){ */ int Sqlite_Init(Tcl_Interp *interp){ Tcl_InitStubs(interp, "8.0", 0); - Tcl_CreateCommand(interp, "sqlite", (Tcl_CmdProc*)DbMain, 0, 0); + Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0); Tcl_PkgProvide(interp, "sqlite", "2.0"); return TCL_OK; } int Tclsqlite_Init(Tcl_Interp *interp){ Tcl_InitStubs(interp, "8.0", 0); - Tcl_CreateCommand(interp, "sqlite", (Tcl_CmdProc*)DbMain, 0, 0); + Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0); Tcl_PkgProvide(interp, "sqlite", "2.0"); return TCL_OK; } |