├── .gitignore ├── CMakeLists.txt ├── LICENSE.LGPLv21 ├── README.md ├── qt-private ├── README ├── qsql_sqlite.cpp └── qsql_sqlite_p.h ├── smain.cpp ├── sqlcipher.json ├── test-shared ├── CMakeLists.txt └── main.cpp └── test-static ├── CMakeLists.txt └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(qsqlcipher) 3 | 4 | find_package(Qt5Sql REQUIRED) 5 | find_package(PkgConfig REQUIRED) 6 | pkg_check_modules(SQLCIPHER REQUIRED sqlcipher) 7 | 8 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 9 | set(CMAKE_AUTOMOC ON) 10 | 11 | # Arrange output paths so that the plugin is found in the default search path 12 | # relative to the test binary. 13 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 14 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/sqldrivers) 15 | 16 | option(STATIC "Build plugin as a static library" OFF) 17 | if(STATIC) 18 | set(LIBTYPE STATIC) 19 | add_definitions(-DQT_STATICPLUGIN) 20 | set(TEST_DIR test-static) 21 | else() 22 | set(LIBTYPE MODULE) 23 | set(TEST_DIR test-shared) 24 | endif() 25 | 26 | add_library(qsqlcipher ${LIBTYPE} 27 | smain.cpp 28 | qt-private/qsql_sqlite.cpp 29 | ) 30 | 31 | target_include_directories(qsqlcipher PRIVATE 32 | ${Qt5Sql_PRIVATE_INCLUDE_DIRS} 33 | ${SQLCIPHER_INCLUDE_DIRS} 34 | ) 35 | 36 | target_link_libraries(qsqlcipher 37 | Qt5::Sql 38 | ${SQLCIPHER_LIBRARIES} 39 | ) 40 | 41 | include(CTest) 42 | if(BUILD_TESTING) 43 | add_subdirectory(${TEST_DIR}) 44 | endif() 45 | -------------------------------------------------------------------------------- /LICENSE.LGPLv21: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | 3 | The QSQLCipher plugin is Copyright (C) 2015 Simon Knopp. 4 | 5 | You may use, distribute and copy the QSQLCipher plugin under the terms of 6 | GNU Lesser General Public License version 2.1, which is displayed below. 7 | 8 | ------------------------------------------------------------------------- 9 | 10 | GNU LESSER GENERAL PUBLIC LICENSE 11 | Version 2.1, February 1999 12 | 13 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 15 | Everyone is permitted to copy and distribute verbatim copies 16 | of this license document, but changing it is not allowed. 17 | 18 | [This is the first released version of the Lesser GPL. It also counts 19 | as the successor of the GNU Library Public License, version 2, hence 20 | the version number 2.1.] 21 | 22 | Preamble 23 | 24 | The licenses for most software are designed to take away your 25 | freedom to share and change it. By contrast, the GNU General Public 26 | Licenses are intended to guarantee your freedom to share and change 27 | free software--to make sure the software is free for all its users. 28 | 29 | This license, the Lesser General Public License, applies to some 30 | specially designated software packages--typically libraries--of the 31 | Free Software Foundation and other authors who decide to use it. You 32 | can use it too, but we suggest you first think carefully about whether 33 | this license or the ordinary General Public License is the better 34 | strategy to use in any particular case, based on the explanations below. 35 | 36 | When we speak of free software, we are referring to freedom of use, 37 | not price. Our General Public Licenses are designed to make sure that 38 | you have the freedom to distribute copies of free software (and charge 39 | for this service if you wish); that you receive source code or can get 40 | it if you want it; that you can change the software and use pieces of 41 | it in new free programs; and that you are informed that you can do 42 | these things. 43 | 44 | To protect your rights, we need to make restrictions that forbid 45 | distributors to deny you these rights or to ask you to surrender these 46 | rights. These restrictions translate to certain responsibilities for 47 | you if you distribute copies of the library or if you modify it. 48 | 49 | For example, if you distribute copies of the library, whether gratis 50 | or for a fee, you must give the recipients all the rights that we gave 51 | you. You must make sure that they, too, receive or can get the source 52 | code. If you link other code with the library, you must provide 53 | complete object files to the recipients, so that they can relink them 54 | with the library after making changes to the library and recompiling 55 | it. And you must show them these terms so they know their rights. 56 | 57 | We protect your rights with a two-step method: (1) we copyright the 58 | library, and (2) we offer you this license, which gives you legal 59 | permission to copy, distribute and/or modify the library. 60 | 61 | To protect each distributor, we want to make it very clear that 62 | there is no warranty for the free library. Also, if the library is 63 | modified by someone else and passed on, the recipients should know 64 | that what they have is not the original version, so that the original 65 | author's reputation will not be affected by problems that might be 66 | introduced by others. 67 | 68 | Finally, software patents pose a constant threat to the existence of 69 | any free program. We wish to make sure that a company cannot 70 | effectively restrict the users of a free program by obtaining a 71 | restrictive license from a patent holder. Therefore, we insist that 72 | any patent license obtained for a version of the library must be 73 | consistent with the full freedom of use specified in this license. 74 | 75 | Most GNU software, including some libraries, is covered by the 76 | ordinary GNU General Public License. This license, the GNU Lesser 77 | General Public License, applies to certain designated libraries, and 78 | is quite different from the ordinary General Public License. We use 79 | this license for certain libraries in order to permit linking those 80 | libraries into non-free programs. 81 | 82 | When a program is linked with a library, whether statically or using 83 | a shared library, the combination of the two is legally speaking a 84 | combined work, a derivative of the original library. The ordinary 85 | General Public License therefore permits such linking only if the 86 | entire combination fits its criteria of freedom. The Lesser General 87 | Public License permits more lax criteria for linking other code with 88 | the library. 89 | 90 | We call this license the "Lesser" General Public License because it 91 | does Less to protect the user's freedom than the ordinary General 92 | Public License. It also provides other free software developers Less 93 | of an advantage over competing non-free programs. These disadvantages 94 | are the reason we use the ordinary General Public License for many 95 | libraries. However, the Lesser license provides advantages in certain 96 | special circumstances. 97 | 98 | For example, on rare occasions, there may be a special need to 99 | encourage the widest possible use of a certain library, so that it becomes 100 | a de-facto standard. To achieve this, non-free programs must be 101 | allowed to use the library. A more frequent case is that a free 102 | library does the same job as widely used non-free libraries. In this 103 | case, there is little to gain by limiting the free library to free 104 | software only, so we use the Lesser General Public License. 105 | 106 | In other cases, permission to use a particular library in non-free 107 | programs enables a greater number of people to use a large body of 108 | free software. For example, permission to use the GNU C Library in 109 | non-free programs enables many more people to use the whole GNU 110 | operating system, as well as its variant, the GNU/Linux operating 111 | system. 112 | 113 | Although the Lesser General Public License is Less protective of the 114 | users' freedom, it does ensure that the user of a program that is 115 | linked with the Library has the freedom and the wherewithal to run 116 | that program using a modified version of the Library. 117 | 118 | The precise terms and conditions for copying, distribution and 119 | modification follow. Pay close attention to the difference between a 120 | "work based on the library" and a "work that uses the library". The 121 | former contains code derived from the library, whereas the latter must 122 | be combined with the library in order to run. 123 | 124 | GNU LESSER GENERAL PUBLIC LICENSE 125 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 126 | 127 | 0. This License Agreement applies to any software library or other 128 | program which contains a notice placed by the copyright holder or 129 | other authorized party saying it may be distributed under the terms of 130 | this Lesser General Public License (also called "this License"). 131 | Each licensee is addressed as "you". 132 | 133 | A "library" means a collection of software functions and/or data 134 | prepared so as to be conveniently linked with application programs 135 | (which use some of those functions and data) to form executables. 136 | 137 | The "Library", below, refers to any such software library or work 138 | which has been distributed under these terms. A "work based on the 139 | Library" means either the Library or any derivative work under 140 | copyright law: that is to say, a work containing the Library or a 141 | portion of it, either verbatim or with modifications and/or translated 142 | straightforwardly into another language. (Hereinafter, translation is 143 | included without limitation in the term "modification".) 144 | 145 | "Source code" for a work means the preferred form of the work for 146 | making modifications to it. For a library, complete source code means 147 | all the source code for all modules it contains, plus any associated 148 | interface definition files, plus the scripts used to control compilation 149 | and installation of the library. 150 | 151 | Activities other than copying, distribution and modification are not 152 | covered by this License; they are outside its scope. The act of 153 | running a program using the Library is not restricted, and output from 154 | such a program is covered only if its contents constitute a work based 155 | on the Library (independent of the use of the Library in a tool for 156 | writing it). Whether that is true depends on what the Library does 157 | and what the program that uses the Library does. 158 | 159 | 1. You may copy and distribute verbatim copies of the Library's 160 | complete source code as you receive it, in any medium, provided that 161 | you conspicuously and appropriately publish on each copy an 162 | appropriate copyright notice and disclaimer of warranty; keep intact 163 | all the notices that refer to this License and to the absence of any 164 | warranty; and distribute a copy of this License along with the 165 | Library. 166 | 167 | You may charge a fee for the physical act of transferring a copy, 168 | and you may at your option offer warranty protection in exchange for a 169 | fee. 170 | 171 | 2. You may modify your copy or copies of the Library or any portion 172 | of it, thus forming a work based on the Library, and copy and 173 | distribute such modifications or work under the terms of Section 1 174 | above, provided that you also meet all of these conditions: 175 | 176 | a) The modified work must itself be a software library. 177 | 178 | b) You must cause the files modified to carry prominent notices 179 | stating that you changed the files and the date of any change. 180 | 181 | c) You must cause the whole of the work to be licensed at no 182 | charge to all third parties under the terms of this License. 183 | 184 | d) If a facility in the modified Library refers to a function or a 185 | table of data to be supplied by an application program that uses 186 | the facility, other than as an argument passed when the facility 187 | is invoked, then you must make a good faith effort to ensure that, 188 | in the event an application does not supply such function or 189 | table, the facility still operates, and performs whatever part of 190 | its purpose remains meaningful. 191 | 192 | (For example, a function in a library to compute square roots has 193 | a purpose that is entirely well-defined independent of the 194 | application. Therefore, Subsection 2d requires that any 195 | application-supplied function or table used by this function must 196 | be optional: if the application does not supply it, the square 197 | root function must still compute square roots.) 198 | 199 | These requirements apply to the modified work as a whole. If 200 | identifiable sections of that work are not derived from the Library, 201 | and can be reasonably considered independent and separate works in 202 | themselves, then this License, and its terms, do not apply to those 203 | sections when you distribute them as separate works. But when you 204 | distribute the same sections as part of a whole which is a work based 205 | on the Library, the distribution of the whole must be on the terms of 206 | this License, whose permissions for other licensees extend to the 207 | entire whole, and thus to each and every part regardless of who wrote 208 | it. 209 | 210 | Thus, it is not the intent of this section to claim rights or contest 211 | your rights to work written entirely by you; rather, the intent is to 212 | exercise the right to control the distribution of derivative or 213 | collective works based on the Library. 214 | 215 | In addition, mere aggregation of another work not based on the Library 216 | with the Library (or with a work based on the Library) on a volume of 217 | a storage or distribution medium does not bring the other work under 218 | the scope of this License. 219 | 220 | 3. You may opt to apply the terms of the ordinary GNU General Public 221 | License instead of this License to a given copy of the Library. To do 222 | this, you must alter all the notices that refer to this License, so 223 | that they refer to the ordinary GNU General Public License, version 2, 224 | instead of to this License. (If a newer version than version 2 of the 225 | ordinary GNU General Public License has appeared, then you can specify 226 | that version instead if you wish.) Do not make any other change in 227 | these notices. 228 | 229 | Once this change is made in a given copy, it is irreversible for 230 | that copy, so the ordinary GNU General Public License applies to all 231 | subsequent copies and derivative works made from that copy. 232 | 233 | This option is useful when you wish to copy part of the code of 234 | the Library into a program that is not a library. 235 | 236 | 4. You may copy and distribute the Library (or a portion or 237 | derivative of it, under Section 2) in object code or executable form 238 | under the terms of Sections 1 and 2 above provided that you accompany 239 | it with the complete corresponding machine-readable source code, which 240 | must be distributed under the terms of Sections 1 and 2 above on a 241 | medium customarily used for software interchange. 242 | 243 | If distribution of object code is made by offering access to copy 244 | from a designated place, then offering equivalent access to copy the 245 | source code from the same place satisfies the requirement to 246 | distribute the source code, even though third parties are not 247 | compelled to copy the source along with the object code. 248 | 249 | 5. A program that contains no derivative of any portion of the 250 | Library, but is designed to work with the Library by being compiled or 251 | linked with it, is called a "work that uses the Library". Such a 252 | work, in isolation, is not a derivative work of the Library, and 253 | therefore falls outside the scope of this License. 254 | 255 | However, linking a "work that uses the Library" with the Library 256 | creates an executable that is a derivative of the Library (because it 257 | contains portions of the Library), rather than a "work that uses the 258 | library". The executable is therefore covered by this License. 259 | Section 6 states terms for distribution of such executables. 260 | 261 | When a "work that uses the Library" uses material from a header file 262 | that is part of the Library, the object code for the work may be a 263 | derivative work of the Library even though the source code is not. 264 | Whether this is true is especially significant if the work can be 265 | linked without the Library, or if the work is itself a library. The 266 | threshold for this to be true is not precisely defined by law. 267 | 268 | If such an object file uses only numerical parameters, data 269 | structure layouts and accessors, and small macros and small inline 270 | functions (ten lines or less in length), then the use of the object 271 | file is unrestricted, regardless of whether it is legally a derivative 272 | work. (Executables containing this object code plus portions of the 273 | Library will still fall under Section 6.) 274 | 275 | Otherwise, if the work is a derivative of the Library, you may 276 | distribute the object code for the work under the terms of Section 6. 277 | Any executables containing that work also fall under Section 6, 278 | whether or not they are linked directly with the Library itself. 279 | 280 | 6. As an exception to the Sections above, you may also combine or 281 | link a "work that uses the Library" with the Library to produce a 282 | work containing portions of the Library, and distribute that work 283 | under terms of your choice, provided that the terms permit 284 | modification of the work for the customer's own use and reverse 285 | engineering for debugging such modifications. 286 | 287 | You must give prominent notice with each copy of the work that the 288 | Library is used in it and that the Library and its use are covered by 289 | this License. You must supply a copy of this License. If the work 290 | during execution displays copyright notices, you must include the 291 | copyright notice for the Library among them, as well as a reference 292 | directing the user to the copy of this License. Also, you must do one 293 | of these things: 294 | 295 | a) Accompany the work with the complete corresponding 296 | machine-readable source code for the Library including whatever 297 | changes were used in the work (which must be distributed under 298 | Sections 1 and 2 above); and, if the work is an executable linked 299 | with the Library, with the complete machine-readable "work that 300 | uses the Library", as object code and/or source code, so that the 301 | user can modify the Library and then relink to produce a modified 302 | executable containing the modified Library. (It is understood 303 | that the user who changes the contents of definitions files in the 304 | Library will not necessarily be able to recompile the application 305 | to use the modified definitions.) 306 | 307 | b) Use a suitable shared library mechanism for linking with the 308 | Library. A suitable mechanism is one that (1) uses at run time a 309 | copy of the library already present on the user's computer system, 310 | rather than copying library functions into the executable, and (2) 311 | will operate properly with a modified version of the library, if 312 | the user installs one, as long as the modified version is 313 | interface-compatible with the version that the work was made with. 314 | 315 | c) Accompany the work with a written offer, valid for at 316 | least three years, to give the same user the materials 317 | specified in Subsection 6a, above, for a charge no more 318 | than the cost of performing this distribution. 319 | 320 | d) If distribution of the work is made by offering access to copy 321 | from a designated place, offer equivalent access to copy the above 322 | specified materials from the same place. 323 | 324 | e) Verify that the user has already received a copy of these 325 | materials or that you have already sent this user a copy. 326 | 327 | For an executable, the required form of the "work that uses the 328 | Library" must include any data and utility programs needed for 329 | reproducing the executable from it. However, as a special exception, 330 | the materials to be distributed need not include anything that is 331 | normally distributed (in either source or binary form) with the major 332 | components (compiler, kernel, and so on) of the operating system on 333 | which the executable runs, unless that component itself accompanies 334 | the executable. 335 | 336 | It may happen that this requirement contradicts the license 337 | restrictions of other proprietary libraries that do not normally 338 | accompany the operating system. Such a contradiction means you cannot 339 | use both them and the Library together in an executable that you 340 | distribute. 341 | 342 | 7. You may place library facilities that are a work based on the 343 | Library side-by-side in a single library together with other library 344 | facilities not covered by this License, and distribute such a combined 345 | library, provided that the separate distribution of the work based on 346 | the Library and of the other library facilities is otherwise 347 | permitted, and provided that you do these two things: 348 | 349 | a) Accompany the combined library with a copy of the same work 350 | based on the Library, uncombined with any other library 351 | facilities. This must be distributed under the terms of the 352 | Sections above. 353 | 354 | b) Give prominent notice with the combined library of the fact 355 | that part of it is a work based on the Library, and explaining 356 | where to find the accompanying uncombined form of the same work. 357 | 358 | 8. You may not copy, modify, sublicense, link with, or distribute 359 | the Library except as expressly provided under this License. Any 360 | attempt otherwise to copy, modify, sublicense, link with, or 361 | distribute the Library is void, and will automatically terminate your 362 | rights under this License. However, parties who have received copies, 363 | or rights, from you under this License will not have their licenses 364 | terminated so long as such parties remain in full compliance. 365 | 366 | 9. You are not required to accept this License, since you have not 367 | signed it. However, nothing else grants you permission to modify or 368 | distribute the Library or its derivative works. These actions are 369 | prohibited by law if you do not accept this License. Therefore, by 370 | modifying or distributing the Library (or any work based on the 371 | Library), you indicate your acceptance of this License to do so, and 372 | all its terms and conditions for copying, distributing or modifying 373 | the Library or works based on it. 374 | 375 | 10. Each time you redistribute the Library (or any work based on the 376 | Library), the recipient automatically receives a license from the 377 | original licensor to copy, distribute, link with or modify the Library 378 | subject to these terms and conditions. You may not impose any further 379 | restrictions on the recipients' exercise of the rights granted herein. 380 | You are not responsible for enforcing compliance by third parties with 381 | this License. 382 | 383 | 11. If, as a consequence of a court judgment or allegation of patent 384 | infringement or for any other reason (not limited to patent issues), 385 | conditions are imposed on you (whether by court order, agreement or 386 | otherwise) that contradict the conditions of this License, they do not 387 | excuse you from the conditions of this License. If you cannot 388 | distribute so as to satisfy simultaneously your obligations under this 389 | License and any other pertinent obligations, then as a consequence you 390 | may not distribute the Library at all. For example, if a patent 391 | license would not permit royalty-free redistribution of the Library by 392 | all those who receive copies directly or indirectly through you, then 393 | the only way you could satisfy both it and this License would be to 394 | refrain entirely from distribution of the Library. 395 | 396 | If any portion of this section is held invalid or unenforceable under any 397 | particular circumstance, the balance of the section is intended to apply, 398 | and the section as a whole is intended to apply in other circumstances. 399 | 400 | It is not the purpose of this section to induce you to infringe any 401 | patents or other property right claims or to contest validity of any 402 | such claims; this section has the sole purpose of protecting the 403 | integrity of the free software distribution system which is 404 | implemented by public license practices. Many people have made 405 | generous contributions to the wide range of software distributed 406 | through that system in reliance on consistent application of that 407 | system; it is up to the author/donor to decide if he or she is willing 408 | to distribute software through any other system and a licensee cannot 409 | impose that choice. 410 | 411 | This section is intended to make thoroughly clear what is believed to 412 | be a consequence of the rest of this License. 413 | 414 | 12. If the distribution and/or use of the Library is restricted in 415 | certain countries either by patents or by copyrighted interfaces, the 416 | original copyright holder who places the Library under this License may add 417 | an explicit geographical distribution limitation excluding those countries, 418 | so that distribution is permitted only in or among countries not thus 419 | excluded. In such case, this License incorporates the limitation as if 420 | written in the body of this License. 421 | 422 | 13. The Free Software Foundation may publish revised and/or new 423 | versions of the Lesser General Public License from time to time. 424 | Such new versions will be similar in spirit to the present version, 425 | but may differ in detail to address new problems or concerns. 426 | 427 | Each version is given a distinguishing version number. If the Library 428 | specifies a version number of this License which applies to it and 429 | "any later version", you have the option of following the terms and 430 | conditions either of that version or of any later version published by 431 | the Free Software Foundation. If the Library does not specify a 432 | license version number, you may choose any version ever published by 433 | the Free Software Foundation. 434 | 435 | 14. If you wish to incorporate parts of the Library into other free 436 | programs whose distribution conditions are incompatible with these, 437 | write to the author to ask for permission. For software which is 438 | copyrighted by the Free Software Foundation, write to the Free 439 | Software Foundation; we sometimes make exceptions for this. Our 440 | decision will be guided by the two goals of preserving the free status 441 | of all derivatives of our free software and of promoting the sharing 442 | and reuse of software generally. 443 | 444 | NO WARRANTY 445 | 446 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 447 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 448 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 449 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 450 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 451 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 452 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 453 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 454 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 455 | 456 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 457 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 458 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 459 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 460 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 461 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 462 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 463 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 464 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 465 | DAMAGES. 466 | 467 | END OF TERMS AND CONDITIONS 468 | 469 | How to Apply These Terms to Your New Libraries 470 | 471 | If you develop a new library, and you want it to be of the greatest 472 | possible use to the public, we recommend making it free software that 473 | everyone can redistribute and change. You can do so by permitting 474 | redistribution under these terms (or, alternatively, under the terms of the 475 | ordinary General Public License). 476 | 477 | To apply these terms, attach the following notices to the library. It is 478 | safest to attach them to the start of each source file to most effectively 479 | convey the exclusion of warranty; and each file should have at least the 480 | "copyright" line and a pointer to where the full notice is found. 481 | 482 | 483 | Copyright (C) 484 | 485 | This library is free software; you can redistribute it and/or 486 | modify it under the terms of the GNU Lesser General Public 487 | License as published by the Free Software Foundation; either 488 | version 2.1 of the License, or (at your option) any later version. 489 | 490 | This library is distributed in the hope that it will be useful, 491 | but WITHOUT ANY WARRANTY; without even the implied warranty of 492 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 493 | Lesser General Public License for more details. 494 | 495 | You should have received a copy of the GNU Lesser General Public 496 | License along with this library; if not, write to the Free Software 497 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 498 | 499 | Also add information on how to contact you by electronic and paper mail. 500 | 501 | You should also get your employer (if you work as a programmer) or your 502 | school, if any, to sign a "copyright disclaimer" for the library, if 503 | necessary. Here is a sample; alter the names: 504 | 505 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 506 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 507 | 508 | , 1 April 1990 509 | Ty Coon, President of Vice 510 | 511 | That's all there is to it! 512 | 513 | 514 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Qt SQL driver plugin for SQLCipher 2 | ================================== 3 | 4 | This is a [QSqlDriverPlugin](http://doc.qt.io/qt-5/qsqldriverplugin.html) for 5 | [SQLCipher](https://www.zetetic.net/sqlcipher/open-source/). It is quite 6 | simple - it uses Qt's own SQLite driver code but links against SQLCipher 7 | instead of SQLite. 8 | 9 | ## Dependencies 10 | 11 | - Qt 5 12 | - SQLCipher 13 | - CMake >= 3.0 14 | - pkg-config 15 | 16 | 17 | ## Tested platforms 18 | 19 | - OS X 10.10 Yosemite 20 | 21 | - Qt 5.5.0 from Homebrew 22 | - SQLCipher 3.3.0 from Homebrew 23 | 24 | - Ubuntu 15.04 Vivid Vervet 25 | 26 | - Qt 5.4.1 27 | - SQLCipher 3.2.0 28 | - Also requires ``qtbase5-private-dev`` for Qt's private headers. 29 | 30 | 31 | ## Deployment 32 | 33 | Follow [Qt's plugin deployment guide](http://doc.qt.io/qt-5/deployment-plugins.html). 34 | In short, put the plugin at ``sqldrivers/libqsqlcipher.so`` relative to your 35 | executable. 36 | 37 | 38 | ## Static linking 39 | 40 | You can also build the plugin statically by passing ``-DSTATIC=ON`` to CMake. 41 | When you build your application which uses the static plugin, you'll need to 42 | include the line ``Q_IMPORT_PLUGIN(QSQLCipherDriverPlugin);`` in one of your 43 | source files and define ``QT_STATICPLUGIN`` at compile time. And link to the 44 | static plugin, of course. 45 | 46 | Note that setting ``-DSTATIC=ON`` only builds *this plugin* as a static library. 47 | If you also want to link to static versions of Qt and/or SQLCipher, it's up to 48 | you to make sure CMake finds static versions of those libraries. 49 | 50 | 51 | ## Tests 52 | 53 | Some basic tests are included - run ``make test``. Note that while pretty much 54 | any C++ compiler can build the actual plugin, the tests require support for 55 | C++14. If you have an old compiler you can pass ``-DBUILD_TESTING=OFF`` to CMake 56 | to skip building the tests. 57 | 58 | ----- 59 | 60 | ## Old version 61 | 62 | This repository used to contain a different method of achieving the same 63 | result, but which required you to have the full Qt source tree available. That 64 | code is available in the ``old`` branch of this repository. 65 | 66 | -------------------------------------------------------------------------------- /qt-private/README: -------------------------------------------------------------------------------- 1 | The files in this directory were copied directly from src/sql/drivers/sqlite/ 2 | in the qtbase repository. Licensing information is provided at the top of 3 | each file. 4 | 5 | If you build this plugin against a Qt version other than that stated in 6 | ../README.md, you may need to replace these files with the ones from that 7 | version of Qt. 8 | -------------------------------------------------------------------------------- /qt-private/qsql_sqlite.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd. 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the QtSql module of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:LGPL21$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see http://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at http://www.qt.io/contact-us. 16 | ** 17 | ** GNU Lesser General Public License Usage 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser 19 | ** General Public License version 2.1 or version 3 as published by the Free 20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 22 | ** following information to ensure the GNU Lesser General Public License 23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 25 | ** 26 | ** As a special exception, The Qt Company gives you certain additional 27 | ** rights. These rights are described in The Qt Company LGPL Exception 28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. 29 | ** 30 | ** $QT_END_LICENSE$ 31 | ** 32 | ****************************************************************************/ 33 | 34 | #include "qsql_sqlite_p.h" 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #if defined Q_OS_WIN 50 | # include 51 | #else 52 | # include 53 | #endif 54 | 55 | #include 56 | 57 | Q_DECLARE_OPAQUE_POINTER(sqlite3*) 58 | Q_DECLARE_METATYPE(sqlite3*) 59 | 60 | Q_DECLARE_OPAQUE_POINTER(sqlite3_stmt*) 61 | Q_DECLARE_METATYPE(sqlite3_stmt*) 62 | 63 | QT_BEGIN_NAMESPACE 64 | 65 | static QString _q_escapeIdentifier(const QString &identifier) 66 | { 67 | QString res = identifier; 68 | if(!identifier.isEmpty() && identifier.left(1) != QString(QLatin1Char('"')) && identifier.right(1) != QString(QLatin1Char('"')) ) { 69 | res.replace(QLatin1Char('"'), QLatin1String("\"\"")); 70 | res.prepend(QLatin1Char('"')).append(QLatin1Char('"')); 71 | res.replace(QLatin1Char('.'), QLatin1String("\".\"")); 72 | } 73 | return res; 74 | } 75 | 76 | static QVariant::Type qGetColumnType(const QString &tpName) 77 | { 78 | const QString typeName = tpName.toLower(); 79 | 80 | if (typeName == QLatin1String("integer") 81 | || typeName == QLatin1String("int")) 82 | return QVariant::Int; 83 | if (typeName == QLatin1String("double") 84 | || typeName == QLatin1String("float") 85 | || typeName == QLatin1String("real") 86 | || typeName.startsWith(QLatin1String("numeric"))) 87 | return QVariant::Double; 88 | if (typeName == QLatin1String("blob")) 89 | return QVariant::ByteArray; 90 | if (typeName == QLatin1String("boolean") 91 | || typeName == QLatin1String("bool")) 92 | return QVariant::Bool; 93 | return QVariant::String; 94 | } 95 | 96 | static QSqlError qMakeError(sqlite3 *access, const QString &descr, QSqlError::ErrorType type, 97 | int errorCode = -1) 98 | { 99 | return QSqlError(descr, 100 | QString(reinterpret_cast(sqlite3_errmsg16(access))), 101 | type, QString::number(errorCode)); 102 | } 103 | 104 | class QSQLiteResultPrivate; 105 | 106 | class QSQLiteResult : public QSqlCachedResult 107 | { 108 | friend class QSQLiteDriver; 109 | friend class QSQLiteResultPrivate; 110 | public: 111 | explicit QSQLiteResult(const QSQLiteDriver* db); 112 | ~QSQLiteResult(); 113 | QVariant handle() const Q_DECL_OVERRIDE; 114 | 115 | protected: 116 | bool gotoNext(QSqlCachedResult::ValueCache& row, int idx) Q_DECL_OVERRIDE; 117 | bool reset(const QString &query) Q_DECL_OVERRIDE; 118 | bool prepare(const QString &query) Q_DECL_OVERRIDE; 119 | bool exec() Q_DECL_OVERRIDE; 120 | int size() Q_DECL_OVERRIDE; 121 | int numRowsAffected() Q_DECL_OVERRIDE; 122 | QVariant lastInsertId() const Q_DECL_OVERRIDE; 123 | QSqlRecord record() const Q_DECL_OVERRIDE; 124 | void detachFromResultSet() Q_DECL_OVERRIDE; 125 | void virtual_hook(int id, void *data) Q_DECL_OVERRIDE; 126 | 127 | private: 128 | QSQLiteResultPrivate* d; 129 | }; 130 | 131 | class QSQLiteDriverPrivate : public QSqlDriverPrivate 132 | { 133 | public: 134 | inline QSQLiteDriverPrivate() : QSqlDriverPrivate(), access(0) { dbmsType = QSqlDriver::SQLite; } 135 | sqlite3 *access; 136 | QList results; 137 | }; 138 | 139 | 140 | class QSQLiteResultPrivate 141 | { 142 | public: 143 | QSQLiteResultPrivate(QSQLiteResult *res); 144 | void cleanup(); 145 | bool fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch); 146 | // initializes the recordInfo and the cache 147 | void initColumns(bool emptyResultset); 148 | void finalize(); 149 | 150 | QSQLiteResult* q; 151 | sqlite3 *access; 152 | 153 | sqlite3_stmt *stmt; 154 | 155 | bool skippedStatus; // the status of the fetchNext() that's skipped 156 | bool skipRow; // skip the next fetchNext()? 157 | QSqlRecord rInf; 158 | QVector firstRow; 159 | }; 160 | 161 | QSQLiteResultPrivate::QSQLiteResultPrivate(QSQLiteResult* res) : q(res), access(0), 162 | stmt(0), skippedStatus(false), skipRow(false) 163 | { 164 | } 165 | 166 | void QSQLiteResultPrivate::cleanup() 167 | { 168 | finalize(); 169 | rInf.clear(); 170 | skippedStatus = false; 171 | skipRow = false; 172 | q->setAt(QSql::BeforeFirstRow); 173 | q->setActive(false); 174 | q->cleanup(); 175 | } 176 | 177 | void QSQLiteResultPrivate::finalize() 178 | { 179 | if (!stmt) 180 | return; 181 | 182 | sqlite3_finalize(stmt); 183 | stmt = 0; 184 | } 185 | 186 | void QSQLiteResultPrivate::initColumns(bool emptyResultset) 187 | { 188 | int nCols = sqlite3_column_count(stmt); 189 | if (nCols <= 0) 190 | return; 191 | 192 | q->init(nCols); 193 | 194 | for (int i = 0; i < nCols; ++i) { 195 | QString colName = QString(reinterpret_cast( 196 | sqlite3_column_name16(stmt, i)) 197 | ).remove(QLatin1Char('"')); 198 | 199 | // must use typeName for resolving the type to match QSqliteDriver::record 200 | QString typeName = QString(reinterpret_cast( 201 | sqlite3_column_decltype16(stmt, i))); 202 | // sqlite3_column_type is documented to have undefined behavior if the result set is empty 203 | int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i); 204 | 205 | QVariant::Type fieldType; 206 | 207 | if (!typeName.isEmpty()) { 208 | fieldType = qGetColumnType(typeName); 209 | } else { 210 | // Get the proper type for the field based on stp value 211 | switch (stp) { 212 | case SQLITE_INTEGER: 213 | fieldType = QVariant::Int; 214 | break; 215 | case SQLITE_FLOAT: 216 | fieldType = QVariant::Double; 217 | break; 218 | case SQLITE_BLOB: 219 | fieldType = QVariant::ByteArray; 220 | break; 221 | case SQLITE_TEXT: 222 | fieldType = QVariant::String; 223 | break; 224 | case SQLITE_NULL: 225 | default: 226 | fieldType = QVariant::Invalid; 227 | break; 228 | } 229 | } 230 | 231 | QSqlField fld(colName, fieldType); 232 | fld.setSqlType(stp); 233 | rInf.append(fld); 234 | } 235 | } 236 | 237 | bool QSQLiteResultPrivate::fetchNext(QSqlCachedResult::ValueCache &values, int idx, bool initialFetch) 238 | { 239 | int res; 240 | int i; 241 | 242 | if (skipRow) { 243 | // already fetched 244 | Q_ASSERT(!initialFetch); 245 | skipRow = false; 246 | for(int i=0;isetLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", "Unable to fetch row"), 259 | QCoreApplication::translate("QSQLiteResult", "No query"), QSqlError::ConnectionError)); 260 | q->setAt(QSql::AfterLastRow); 261 | return false; 262 | } 263 | res = sqlite3_step(stmt); 264 | 265 | switch(res) { 266 | case SQLITE_ROW: 267 | // check to see if should fill out columns 268 | if (rInf.isEmpty()) 269 | // must be first call. 270 | initColumns(false); 271 | if (idx < 0 && !initialFetch) 272 | return true; 273 | for (i = 0; i < rInf.count(); ++i) { 274 | switch (sqlite3_column_type(stmt, i)) { 275 | case SQLITE_BLOB: 276 | values[i + idx] = QByteArray(static_cast( 277 | sqlite3_column_blob(stmt, i)), 278 | sqlite3_column_bytes(stmt, i)); 279 | break; 280 | case SQLITE_INTEGER: 281 | values[i + idx] = sqlite3_column_int64(stmt, i); 282 | break; 283 | case SQLITE_FLOAT: 284 | switch(q->numericalPrecisionPolicy()) { 285 | case QSql::LowPrecisionInt32: 286 | values[i + idx] = sqlite3_column_int(stmt, i); 287 | break; 288 | case QSql::LowPrecisionInt64: 289 | values[i + idx] = sqlite3_column_int64(stmt, i); 290 | break; 291 | case QSql::LowPrecisionDouble: 292 | case QSql::HighPrecision: 293 | default: 294 | values[i + idx] = sqlite3_column_double(stmt, i); 295 | break; 296 | }; 297 | break; 298 | case SQLITE_NULL: 299 | values[i + idx] = QVariant(QVariant::String); 300 | break; 301 | default: 302 | values[i + idx] = QString(reinterpret_cast( 303 | sqlite3_column_text16(stmt, i)), 304 | sqlite3_column_bytes16(stmt, i) / sizeof(QChar)); 305 | break; 306 | } 307 | } 308 | return true; 309 | case SQLITE_DONE: 310 | if (rInf.isEmpty()) 311 | // must be first call. 312 | initColumns(true); 313 | q->setAt(QSql::AfterLastRow); 314 | sqlite3_reset(stmt); 315 | return false; 316 | case SQLITE_CONSTRAINT: 317 | case SQLITE_ERROR: 318 | // SQLITE_ERROR is a generic error code and we must call sqlite3_reset() 319 | // to get the specific error message. 320 | res = sqlite3_reset(stmt); 321 | q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult", 322 | "Unable to fetch row"), QSqlError::ConnectionError, res)); 323 | q->setAt(QSql::AfterLastRow); 324 | return false; 325 | case SQLITE_MISUSE: 326 | case SQLITE_BUSY: 327 | default: 328 | // something wrong, don't get col info, but still return false 329 | q->setLastError(qMakeError(access, QCoreApplication::translate("QSQLiteResult", 330 | "Unable to fetch row"), QSqlError::ConnectionError, res)); 331 | sqlite3_reset(stmt); 332 | q->setAt(QSql::AfterLastRow); 333 | return false; 334 | } 335 | return false; 336 | } 337 | 338 | QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db) 339 | : QSqlCachedResult(db) 340 | { 341 | d = new QSQLiteResultPrivate(this); 342 | d->access = db->d_func()->access; 343 | const_cast(db->d_func())->results.append(this); 344 | } 345 | 346 | QSQLiteResult::~QSQLiteResult() 347 | { 348 | const QSqlDriver *sqlDriver = driver(); 349 | if (sqlDriver) 350 | const_cast(qobject_cast(sqlDriver)->d_func())->results.removeOne(this); 351 | d->cleanup(); 352 | delete d; 353 | } 354 | 355 | void QSQLiteResult::virtual_hook(int id, void *data) 356 | { 357 | QSqlCachedResult::virtual_hook(id, data); 358 | } 359 | 360 | bool QSQLiteResult::reset(const QString &query) 361 | { 362 | if (!prepare(query)) 363 | return false; 364 | return exec(); 365 | } 366 | 367 | bool QSQLiteResult::prepare(const QString &query) 368 | { 369 | if (!driver() || !driver()->isOpen() || driver()->isOpenError()) 370 | return false; 371 | 372 | d->cleanup(); 373 | 374 | setSelect(false); 375 | 376 | const void *pzTail = NULL; 377 | 378 | #if (SQLITE_VERSION_NUMBER >= 3003011) 379 | int res = sqlite3_prepare16_v2(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), 380 | &d->stmt, &pzTail); 381 | #else 382 | int res = sqlite3_prepare16(d->access, query.constData(), (query.size() + 1) * sizeof(QChar), 383 | &d->stmt, &pzTail); 384 | #endif 385 | 386 | if (res != SQLITE_OK) { 387 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", 388 | "Unable to execute statement"), QSqlError::StatementError, res)); 389 | d->finalize(); 390 | return false; 391 | } else if (pzTail && !QString(reinterpret_cast(pzTail)).trimmed().isEmpty()) { 392 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", 393 | "Unable to execute multiple statements at a time"), QSqlError::StatementError, SQLITE_MISUSE)); 394 | d->finalize(); 395 | return false; 396 | } 397 | return true; 398 | } 399 | 400 | bool QSQLiteResult::exec() 401 | { 402 | const QVector values = boundValues(); 403 | 404 | d->skippedStatus = false; 405 | d->skipRow = false; 406 | d->rInf.clear(); 407 | clearValues(); 408 | setLastError(QSqlError()); 409 | 410 | int res = sqlite3_reset(d->stmt); 411 | if (res != SQLITE_OK) { 412 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", 413 | "Unable to reset statement"), QSqlError::StatementError, res)); 414 | d->finalize(); 415 | return false; 416 | } 417 | int paramCount = sqlite3_bind_parameter_count(d->stmt); 418 | if (paramCount == values.count()) { 419 | for (int i = 0; i < paramCount; ++i) { 420 | res = SQLITE_OK; 421 | const QVariant value = values.at(i); 422 | 423 | if (value.isNull()) { 424 | res = sqlite3_bind_null(d->stmt, i + 1); 425 | } else { 426 | switch (value.type()) { 427 | case QVariant::ByteArray: { 428 | const QByteArray *ba = static_cast(value.constData()); 429 | res = sqlite3_bind_blob(d->stmt, i + 1, ba->constData(), 430 | ba->size(), SQLITE_STATIC); 431 | break; } 432 | case QVariant::Int: 433 | case QVariant::Bool: 434 | res = sqlite3_bind_int(d->stmt, i + 1, value.toInt()); 435 | break; 436 | case QVariant::Double: 437 | res = sqlite3_bind_double(d->stmt, i + 1, value.toDouble()); 438 | break; 439 | case QVariant::UInt: 440 | case QVariant::LongLong: 441 | res = sqlite3_bind_int64(d->stmt, i + 1, value.toLongLong()); 442 | break; 443 | case QVariant::DateTime: { 444 | const QDateTime dateTime = value.toDateTime(); 445 | const QString str = dateTime.toString(QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz")); 446 | res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), 447 | str.size() * sizeof(ushort), SQLITE_TRANSIENT); 448 | break; 449 | } 450 | case QVariant::Time: { 451 | const QTime time = value.toTime(); 452 | const QString str = time.toString(QStringLiteral("hh:mm:ss.zzz")); 453 | res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), 454 | str.size() * sizeof(ushort), SQLITE_TRANSIENT); 455 | break; 456 | } 457 | case QVariant::String: { 458 | // lifetime of string == lifetime of its qvariant 459 | const QString *str = static_cast(value.constData()); 460 | res = sqlite3_bind_text16(d->stmt, i + 1, str->utf16(), 461 | (str->size()) * sizeof(QChar), SQLITE_STATIC); 462 | break; } 463 | default: { 464 | QString str = value.toString(); 465 | // SQLITE_TRANSIENT makes sure that sqlite buffers the data 466 | res = sqlite3_bind_text16(d->stmt, i + 1, str.utf16(), 467 | (str.size()) * sizeof(QChar), SQLITE_TRANSIENT); 468 | break; } 469 | } 470 | } 471 | if (res != SQLITE_OK) { 472 | setLastError(qMakeError(d->access, QCoreApplication::translate("QSQLiteResult", 473 | "Unable to bind parameters"), QSqlError::StatementError, res)); 474 | d->finalize(); 475 | return false; 476 | } 477 | } 478 | } else { 479 | setLastError(QSqlError(QCoreApplication::translate("QSQLiteResult", 480 | "Parameter count mismatch"), QString(), QSqlError::StatementError)); 481 | return false; 482 | } 483 | d->skippedStatus = d->fetchNext(d->firstRow, 0, true); 484 | if (lastError().isValid()) { 485 | setSelect(false); 486 | setActive(false); 487 | return false; 488 | } 489 | setSelect(!d->rInf.isEmpty()); 490 | setActive(true); 491 | return true; 492 | } 493 | 494 | bool QSQLiteResult::gotoNext(QSqlCachedResult::ValueCache& row, int idx) 495 | { 496 | return d->fetchNext(row, idx, false); 497 | } 498 | 499 | int QSQLiteResult::size() 500 | { 501 | return -1; 502 | } 503 | 504 | int QSQLiteResult::numRowsAffected() 505 | { 506 | return sqlite3_changes(d->access); 507 | } 508 | 509 | QVariant QSQLiteResult::lastInsertId() const 510 | { 511 | if (isActive()) { 512 | qint64 id = sqlite3_last_insert_rowid(d->access); 513 | if (id) 514 | return id; 515 | } 516 | return QVariant(); 517 | } 518 | 519 | QSqlRecord QSQLiteResult::record() const 520 | { 521 | if (!isActive() || !isSelect()) 522 | return QSqlRecord(); 523 | return d->rInf; 524 | } 525 | 526 | void QSQLiteResult::detachFromResultSet() 527 | { 528 | if (d->stmt) 529 | sqlite3_reset(d->stmt); 530 | } 531 | 532 | QVariant QSQLiteResult::handle() const 533 | { 534 | return QVariant::fromValue(d->stmt); 535 | } 536 | 537 | ///////////////////////////////////////////////////////// 538 | 539 | QSQLiteDriver::QSQLiteDriver(QObject * parent) 540 | : QSqlDriver(*new QSQLiteDriverPrivate, parent) 541 | { 542 | } 543 | 544 | QSQLiteDriver::QSQLiteDriver(sqlite3 *connection, QObject *parent) 545 | : QSqlDriver(*new QSQLiteDriverPrivate, parent) 546 | { 547 | Q_D(QSQLiteDriver); 548 | d->access = connection; 549 | setOpen(true); 550 | setOpenError(false); 551 | } 552 | 553 | 554 | QSQLiteDriver::~QSQLiteDriver() 555 | { 556 | } 557 | 558 | bool QSQLiteDriver::hasFeature(DriverFeature f) const 559 | { 560 | switch (f) { 561 | case BLOB: 562 | case Transactions: 563 | case Unicode: 564 | case LastInsertId: 565 | case PreparedQueries: 566 | case PositionalPlaceholders: 567 | case SimpleLocking: 568 | case FinishQuery: 569 | case LowPrecisionNumbers: 570 | return true; 571 | case QuerySize: 572 | case NamedPlaceholders: 573 | case BatchOperations: 574 | case EventNotifications: 575 | case MultipleResultSets: 576 | case CancelQuery: 577 | return false; 578 | } 579 | return false; 580 | } 581 | 582 | /* 583 | SQLite dbs have no user name, passwords, hosts or ports. 584 | just file names. 585 | */ 586 | bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, const QString &, int, const QString &conOpts) 587 | { 588 | Q_D(QSQLiteDriver); 589 | if (isOpen()) 590 | close(); 591 | 592 | 593 | int timeOut = 5000; 594 | bool sharedCache = false; 595 | bool openReadOnlyOption = false; 596 | bool openUriOption = false; 597 | 598 | const QStringList opts = QString(conOpts).remove(QLatin1Char(' ')).split(QLatin1Char(';')); 599 | foreach (const QString &option, opts) { 600 | if (option.startsWith(QLatin1String("QSQLITE_BUSY_TIMEOUT="))) { 601 | bool ok; 602 | const int nt = option.midRef(21).toInt(&ok); 603 | if (ok) 604 | timeOut = nt; 605 | } else if (option == QLatin1String("QSQLITE_OPEN_READONLY")) { 606 | openReadOnlyOption = true; 607 | } else if (option == QLatin1String("QSQLITE_OPEN_URI")) { 608 | openUriOption = true; 609 | } else if (option == QLatin1String("QSQLITE_ENABLE_SHARED_CACHE")) { 610 | sharedCache = true; 611 | } 612 | } 613 | 614 | int openMode = (openReadOnlyOption ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)); 615 | if (openUriOption) 616 | openMode |= SQLITE_OPEN_URI; 617 | 618 | sqlite3_enable_shared_cache(sharedCache); 619 | 620 | if (sqlite3_open_v2(db.toUtf8().constData(), &d->access, openMode, NULL) == SQLITE_OK) { 621 | sqlite3_busy_timeout(d->access, timeOut); 622 | setOpen(true); 623 | setOpenError(false); 624 | return true; 625 | } else { 626 | if (d->access) { 627 | sqlite3_close(d->access); 628 | d->access = 0; 629 | } 630 | 631 | setLastError(qMakeError(d->access, tr("Error opening database"), 632 | QSqlError::ConnectionError)); 633 | setOpenError(true); 634 | return false; 635 | } 636 | } 637 | 638 | void QSQLiteDriver::close() 639 | { 640 | Q_D(QSQLiteDriver); 641 | if (isOpen()) { 642 | foreach (QSQLiteResult *result, d->results) { 643 | result->d->finalize(); 644 | } 645 | 646 | if (sqlite3_close(d->access) != SQLITE_OK) 647 | setLastError(qMakeError(d->access, tr("Error closing database"), 648 | QSqlError::ConnectionError)); 649 | d->access = 0; 650 | setOpen(false); 651 | setOpenError(false); 652 | } 653 | } 654 | 655 | QSqlResult *QSQLiteDriver::createResult() const 656 | { 657 | return new QSQLiteResult(this); 658 | } 659 | 660 | bool QSQLiteDriver::beginTransaction() 661 | { 662 | if (!isOpen() || isOpenError()) 663 | return false; 664 | 665 | QSqlQuery q(createResult()); 666 | if (!q.exec(QLatin1String("BEGIN"))) { 667 | setLastError(QSqlError(tr("Unable to begin transaction"), 668 | q.lastError().databaseText(), QSqlError::TransactionError)); 669 | return false; 670 | } 671 | 672 | return true; 673 | } 674 | 675 | bool QSQLiteDriver::commitTransaction() 676 | { 677 | if (!isOpen() || isOpenError()) 678 | return false; 679 | 680 | QSqlQuery q(createResult()); 681 | if (!q.exec(QLatin1String("COMMIT"))) { 682 | setLastError(QSqlError(tr("Unable to commit transaction"), 683 | q.lastError().databaseText(), QSqlError::TransactionError)); 684 | return false; 685 | } 686 | 687 | return true; 688 | } 689 | 690 | bool QSQLiteDriver::rollbackTransaction() 691 | { 692 | if (!isOpen() || isOpenError()) 693 | return false; 694 | 695 | QSqlQuery q(createResult()); 696 | if (!q.exec(QLatin1String("ROLLBACK"))) { 697 | setLastError(QSqlError(tr("Unable to rollback transaction"), 698 | q.lastError().databaseText(), QSqlError::TransactionError)); 699 | return false; 700 | } 701 | 702 | return true; 703 | } 704 | 705 | QStringList QSQLiteDriver::tables(QSql::TableType type) const 706 | { 707 | QStringList res; 708 | if (!isOpen()) 709 | return res; 710 | 711 | QSqlQuery q(createResult()); 712 | q.setForwardOnly(true); 713 | 714 | QString sql = QLatin1String("SELECT name FROM sqlite_master WHERE %1 " 715 | "UNION ALL SELECT name FROM sqlite_temp_master WHERE %1"); 716 | if ((type & QSql::Tables) && (type & QSql::Views)) 717 | sql = sql.arg(QLatin1String("type='table' OR type='view'")); 718 | else if (type & QSql::Tables) 719 | sql = sql.arg(QLatin1String("type='table'")); 720 | else if (type & QSql::Views) 721 | sql = sql.arg(QLatin1String("type='view'")); 722 | else 723 | sql.clear(); 724 | 725 | if (!sql.isEmpty() && q.exec(sql)) { 726 | while(q.next()) 727 | res.append(q.value(0).toString()); 728 | } 729 | 730 | if (type & QSql::SystemTables) { 731 | // there are no internal tables beside this one: 732 | res.append(QLatin1String("sqlite_master")); 733 | } 734 | 735 | return res; 736 | } 737 | 738 | static QSqlIndex qGetTableInfo(QSqlQuery &q, const QString &tableName, bool onlyPIndex = false) 739 | { 740 | QString schema; 741 | QString table(tableName); 742 | int indexOfSeparator = tableName.indexOf(QLatin1Char('.')); 743 | if (indexOfSeparator > -1) { 744 | schema = tableName.left(indexOfSeparator).append(QLatin1Char('.')); 745 | table = tableName.mid(indexOfSeparator + 1); 746 | } 747 | q.exec(QLatin1String("PRAGMA ") + schema + QLatin1String("table_info (") + _q_escapeIdentifier(table) + QLatin1String(")")); 748 | 749 | QSqlIndex ind; 750 | while (q.next()) { 751 | bool isPk = q.value(5).toInt(); 752 | if (onlyPIndex && !isPk) 753 | continue; 754 | QString typeName = q.value(2).toString().toLower(); 755 | QSqlField fld(q.value(1).toString(), qGetColumnType(typeName)); 756 | if (isPk && (typeName == QLatin1String("integer"))) 757 | // INTEGER PRIMARY KEY fields are auto-generated in sqlite 758 | // INT PRIMARY KEY is not the same as INTEGER PRIMARY KEY! 759 | fld.setAutoValue(true); 760 | fld.setRequired(q.value(3).toInt() != 0); 761 | fld.setDefaultValue(q.value(4)); 762 | ind.append(fld); 763 | } 764 | return ind; 765 | } 766 | 767 | QSqlIndex QSQLiteDriver::primaryIndex(const QString &tblname) const 768 | { 769 | if (!isOpen()) 770 | return QSqlIndex(); 771 | 772 | QString table = tblname; 773 | if (isIdentifierEscaped(table, QSqlDriver::TableName)) 774 | table = stripDelimiters(table, QSqlDriver::TableName); 775 | 776 | QSqlQuery q(createResult()); 777 | q.setForwardOnly(true); 778 | return qGetTableInfo(q, table, true); 779 | } 780 | 781 | QSqlRecord QSQLiteDriver::record(const QString &tbl) const 782 | { 783 | if (!isOpen()) 784 | return QSqlRecord(); 785 | 786 | QString table = tbl; 787 | if (isIdentifierEscaped(table, QSqlDriver::TableName)) 788 | table = stripDelimiters(table, QSqlDriver::TableName); 789 | 790 | QSqlQuery q(createResult()); 791 | q.setForwardOnly(true); 792 | return qGetTableInfo(q, table); 793 | } 794 | 795 | QVariant QSQLiteDriver::handle() const 796 | { 797 | Q_D(const QSQLiteDriver); 798 | return QVariant::fromValue(d->access); 799 | } 800 | 801 | QString QSQLiteDriver::escapeIdentifier(const QString &identifier, IdentifierType type) const 802 | { 803 | Q_UNUSED(type); 804 | return _q_escapeIdentifier(identifier); 805 | } 806 | 807 | QT_END_NAMESPACE 808 | -------------------------------------------------------------------------------- /qt-private/qsql_sqlite_p.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2015 The Qt Company Ltd. 4 | ** Contact: http://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the QtSql module of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:LGPL21$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see http://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at http://www.qt.io/contact-us. 16 | ** 17 | ** GNU Lesser General Public License Usage 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser 19 | ** General Public License version 2.1 or version 3 as published by the Free 20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 22 | ** following information to ensure the GNU Lesser General Public License 23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 25 | ** 26 | ** As a special exception, The Qt Company gives you certain additional 27 | ** rights. These rights are described in The Qt Company LGPL Exception 28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. 29 | ** 30 | ** $QT_END_LICENSE$ 31 | ** 32 | ****************************************************************************/ 33 | 34 | #ifndef QSQL_SQLITE_H 35 | #define QSQL_SQLITE_H 36 | 37 | // 38 | // W A R N I N G 39 | // ------------- 40 | // 41 | // This file is not part of the Qt API. It exists purely as an 42 | // implementation detail. This header file may change from version to 43 | // version without notice, or even be removed. 44 | // 45 | // We mean it. 46 | // 47 | 48 | #include 49 | #include 50 | 51 | struct sqlite3; 52 | 53 | #ifdef QT_PLUGIN 54 | #define Q_EXPORT_SQLDRIVER_SQLITE 55 | #else 56 | #define Q_EXPORT_SQLDRIVER_SQLITE Q_SQL_EXPORT 57 | #endif 58 | 59 | QT_BEGIN_NAMESPACE 60 | 61 | class QSQLiteDriverPrivate; 62 | class QSQLiteDriver; 63 | 64 | class Q_EXPORT_SQLDRIVER_SQLITE QSQLiteDriver : public QSqlDriver 65 | { 66 | Q_DECLARE_PRIVATE(QSQLiteDriver) 67 | Q_OBJECT 68 | friend class QSQLiteResult; 69 | public: 70 | explicit QSQLiteDriver(QObject *parent = 0); 71 | explicit QSQLiteDriver(sqlite3 *connection, QObject *parent = 0); 72 | ~QSQLiteDriver(); 73 | bool hasFeature(DriverFeature f) const Q_DECL_OVERRIDE; 74 | bool open(const QString & db, 75 | const QString & user, 76 | const QString & password, 77 | const QString & host, 78 | int port, 79 | const QString & connOpts) Q_DECL_OVERRIDE; 80 | void close() Q_DECL_OVERRIDE; 81 | QSqlResult *createResult() const Q_DECL_OVERRIDE; 82 | bool beginTransaction() Q_DECL_OVERRIDE; 83 | bool commitTransaction() Q_DECL_OVERRIDE; 84 | bool rollbackTransaction() Q_DECL_OVERRIDE; 85 | QStringList tables(QSql::TableType) const Q_DECL_OVERRIDE; 86 | 87 | QSqlRecord record(const QString& tablename) const Q_DECL_OVERRIDE; 88 | QSqlIndex primaryIndex(const QString &table) const Q_DECL_OVERRIDE; 89 | QVariant handle() const Q_DECL_OVERRIDE; 90 | QString escapeIdentifier(const QString &identifier, IdentifierType) const Q_DECL_OVERRIDE; 91 | }; 92 | 93 | QT_END_NAMESPACE 94 | 95 | #endif // QSQL_SQLITE_H 96 | -------------------------------------------------------------------------------- /smain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "qt-private/qsql_sqlite_p.h" 4 | 5 | QT_BEGIN_NAMESPACE 6 | 7 | class QSQLCipherDriverPlugin : public QSqlDriverPlugin 8 | { 9 | Q_OBJECT 10 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QSqlDriverFactoryInterface" FILE "sqlcipher.json") 11 | 12 | public: 13 | QSQLCipherDriverPlugin(); 14 | 15 | QSqlDriver* create(const QString &) Q_DECL_OVERRIDE; 16 | }; 17 | 18 | QSQLCipherDriverPlugin::QSQLCipherDriverPlugin() 19 | : QSqlDriverPlugin() 20 | { 21 | } 22 | 23 | QSqlDriver* QSQLCipherDriverPlugin::create(const QString &name) 24 | { 25 | if (name == QLatin1String("QSQLCIPHER")) { 26 | QSQLiteDriver* driver = new QSQLiteDriver(); 27 | return driver; 28 | } 29 | return 0; 30 | } 31 | 32 | QT_END_NAMESPACE 33 | 34 | #include "smain.moc" 35 | -------------------------------------------------------------------------------- /sqlcipher.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "QSQLCIPHER" ] 3 | } 4 | -------------------------------------------------------------------------------- /test-shared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_BUILD_TYPE Debug) 2 | set(CMAKE_CXX_FLAGS "-std=c++14") 3 | 4 | add_executable(qsqlcipher-test main.cpp) 5 | target_link_libraries(qsqlcipher-test Qt5::Sql) 6 | 7 | add_test(NAME qsqlcipher-test COMMAND qsqlcipher-test) 8 | -------------------------------------------------------------------------------- /test-shared/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef QT_DEBUG 7 | #error Must be built in debug mode! 8 | #endif 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | QCoreApplication app(argc, argv); 13 | 14 | for (const auto& driver : QSqlDatabase::drivers()) 15 | qDebug() << driver; 16 | 17 | Q_ASSERT(QSqlDatabase::isDriverAvailable("QSQLITE")); // from Qt 18 | Q_ASSERT(QSqlDatabase::isDriverAvailable("QSQLCIPHER")); // from our plugin 19 | 20 | QTemporaryDir tmp; 21 | Q_ASSERT(tmp.isValid()); 22 | 23 | auto withDB = [&](const char *driver, auto fn) { 24 | QString path = QDir(tmp.path()).absoluteFilePath(QString(driver) + ".db"); 25 | { 26 | QSqlDatabase db = QSqlDatabase::addDatabase(driver, "db"); 27 | db.setDatabaseName(path); 28 | Q_ASSERT(db.open()); 29 | fn(db); 30 | } 31 | QSqlDatabase::removeDatabase("db"); 32 | }; 33 | 34 | // QSQLITE 35 | { 36 | // Create a SQLite db 37 | withDB("QSQLITE", [](auto db){ 38 | db.exec("create table foo (bar integer)"); 39 | db.exec("insert into foo values (42)"); 40 | }); 41 | 42 | // Check that we can read from the SQLite db 43 | withDB("QSQLITE", [](auto db){ 44 | QSqlQuery q = db.exec("select bar from foo"); 45 | Q_ASSERT(q.next()); 46 | Q_ASSERT(q.value(0).toInt() == 42); 47 | }); 48 | 49 | // Check that SQLite is not SQLCipher 50 | withDB("QSQLITE", [](auto db){ 51 | QSqlQuery q = db.exec("select sqlcipher_export()"); 52 | QString errmsg = q.lastError().databaseText(); 53 | Q_ASSERT(errmsg.startsWith("no such function")); 54 | }); 55 | } 56 | 57 | // QSQLCIPHER 58 | { 59 | // Check that SQLCipher is not SQLite 60 | withDB("QSQLCIPHER", [](auto db){ 61 | QSqlQuery q = db.exec("select sqlcipher_export()"); 62 | QString errmsg = q.lastError().databaseText(); 63 | Q_ASSERT(errmsg.startsWith("wrong number of arguments")); 64 | }); 65 | 66 | // Create a SQLCipher db with a passphrase 67 | withDB("QSQLCIPHER", [](auto db){ 68 | db.exec("pragma key='foobar'"); 69 | db.exec("create table foo (bar integer)"); 70 | db.exec("insert into foo values (42)"); 71 | }); 72 | 73 | // Check that we can't read from the SQLCipher db without the passphrase 74 | withDB("QSQLCIPHER", [](auto db){ 75 | QSqlQuery q = db.exec("select bar from foo"); 76 | Q_ASSERT(!q.next()); 77 | }); 78 | 79 | // Check that we can read from the SQLCipher db with the passphrase 80 | withDB("QSQLCIPHER", [](auto db){ 81 | db.exec("pragma key='foobar'"); 82 | QSqlQuery q = db.exec("select bar from foo"); 83 | Q_ASSERT(q.next()); 84 | Q_ASSERT(q.value(0).toInt() == 42); 85 | }); 86 | } 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /test-static/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Same as for test-shared, but statically link the plugin 2 | include(../test-shared/CMakeLists.txt) 3 | target_link_libraries(qsqlcipher-test qsqlcipher) 4 | -------------------------------------------------------------------------------- /test-static/main.cpp: -------------------------------------------------------------------------------- 1 | // Same as for test-shared, but manually import the static plugin 2 | #include 3 | Q_IMPORT_PLUGIN(QSQLCipherDriverPlugin); 4 | #include "../test-shared/main.cpp" 5 | --------------------------------------------------------------------------------