├── LICENSE ├── README.md └── Web Tracker ├── Web Tracker.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── sriharish.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── sriharish.xcuserdatad │ └── xcschemes │ ├── Web Tracker.xcscheme │ └── xcschememanagement.plist └── Web Tracker ├── Chrome.h ├── ChromeBrowser.swift ├── SQLight ├── CustomTypes.swift ├── PreparedStatement.swift └── SQLight.swift ├── Web Tracker-Bridging-Header.h └── main.swift /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web Tracker 🔍 2 | 3 | 4 | It scans and logs the currently opened websites in Chrome Browser. It can even log incognito windows. 5 | 6 | And the best thing is, **It doesn't require any permissions to run**🔥. 7 | 8 | 9 | ## Usage 10 | 11 | Clone the repository, open the project in Xcode, build the project and run the executable. 12 | 13 | By default, it logs every `5 secs` and creates the database file `web-tracker.db` in the home directory. 14 | 15 | **To provide custom time and file name**: 16 | 17 | The executable can take multiple arguments 18 | 19 | **1)** Both time and file name. The order doesn't matter. 20 | 21 | **Example**: 22 | ```sh 23 | $ Web\ Tracker 2.5 "./Desktop/tracker.db" 24 | ``` 25 | *or* 26 | ```sh 27 | $ Web\ Tracker "./Desktop/tracker.db" 2.5 28 | ``` 29 | 30 | **2)** Either time or file name 31 | 32 | **Example**: 33 | ```sh 34 | $ Web\ Tracker 2.5 35 | ``` 36 | *or* 37 | ```sh 38 | $ Web\ Tracker "./Desktop/tracker.db" 39 | ``` 40 | 41 | ### To run it in the background 42 | 43 | To be able to close the Terminal when Web Tracker is running, use this command while running the executable. 44 | 45 | ```sh 46 | $ nohup ./Web\ Tracker & 47 | ``` 48 | And you can quit the Terminal. 49 | 50 | ### To quit/stop the Web Tracker 51 | 52 | To quit the Web Tracker, first find its PID using `ps` and use `kill` to stop the Web Tracker. 53 | 54 | ```sh 55 | $ ps -e | grep "Web Tracker" 56 | $ kill -9 pid_of_webtracker_from_above_command 57 | ``` 58 | 59 | **The database has these columns**: 60 | 61 | - Table name: `Data` 62 | 63 | | Column Name | Data Type | 64 | |:-----------:|:---------:| 65 | | url | varchar | 66 | | title | varchar | 67 | | incognito | int | 68 | | time | varchar | 69 | | date | varchar | 70 | 71 | **For incognito**: `0` means normal window, `1` means incognito window. 72 | 73 | 74 | 75 | ## Disclaimer 76 | If the use of this product causes the death of your firstborn or anyone, I'm not responsible ( no warranty, no liability, etc.) 77 | 78 | **For technical people**: It is only for educational purpose. 79 | 80 | ## Contributing 81 | 82 | Feel free to fork the project and submit a pull request with your changes! 83 | 84 | ##### Not experienced or lazy to fork and submit a pull request ? 85 | Open an issue for adding new features, enhancement, bugs etc. I might take a look into it. 86 | 87 | License 88 | ---- 89 | 90 | MIT 91 | 92 | 93 | **Free Software, Hell Yeah!** 94 | 95 | -------------------------------------------------------------------------------- /Web Tracker/Web Tracker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2462021C1F8EF95F00D800F5 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2462021B1F8EF95F00D800F5 /* main.swift */; }; 11 | 246202241F8EF96B00D800F5 /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 246202231F8EF96B00D800F5 /* ScriptingBridge.framework */; }; 12 | 246202261F8EF97000D800F5 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 246202251F8EF97000D800F5 /* libsqlite3.tbd */; }; 13 | 246202331F8EF9D300D800F5 /* CustomTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2462022D1F8EF9D300D800F5 /* CustomTypes.swift */; }; 14 | 246202341F8EF9D300D800F5 /* PreparedStatement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2462022E1F8EF9D300D800F5 /* PreparedStatement.swift */; }; 15 | 246202351F8EF9D300D800F5 /* SQLight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246202301F8EF9D300D800F5 /* SQLight.swift */; }; 16 | 246202371F8EFCF800D800F5 /* ChromeBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246202361F8EFCF800D800F5 /* ChromeBrowser.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 246202161F8EF95F00D800F5 /* CopyFiles */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = /usr/share/man/man1/; 24 | dstSubfolderSpec = 0; 25 | files = ( 26 | ); 27 | runOnlyForDeploymentPostprocessing = 1; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 246202181F8EF95F00D800F5 /* Web Tracker */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Web Tracker"; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 2462021B1F8EF95F00D800F5 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 34 | 246202231F8EF96B00D800F5 /* ScriptingBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScriptingBridge.framework; path = System/Library/Frameworks/ScriptingBridge.framework; sourceTree = SDKROOT; }; 35 | 246202251F8EF97000D800F5 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; 36 | 246202271F8EF99200D800F5 /* Chrome.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Chrome.h; sourceTree = ""; }; 37 | 246202281F8EF9A200D800F5 /* Web Tracker-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Web Tracker-Bridging-Header.h"; sourceTree = ""; }; 38 | 2462022D1F8EF9D300D800F5 /* CustomTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTypes.swift; sourceTree = ""; }; 39 | 2462022E1F8EF9D300D800F5 /* PreparedStatement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreparedStatement.swift; sourceTree = ""; }; 40 | 246202301F8EF9D300D800F5 /* SQLight.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLight.swift; sourceTree = ""; }; 41 | 246202361F8EFCF800D800F5 /* ChromeBrowser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromeBrowser.swift; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 246202151F8EF95F00D800F5 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | 246202261F8EF97000D800F5 /* libsqlite3.tbd in Frameworks */, 50 | 246202241F8EF96B00D800F5 /* ScriptingBridge.framework in Frameworks */, 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 2462020F1F8EF95F00D800F5 = { 58 | isa = PBXGroup; 59 | children = ( 60 | 2462021A1F8EF95F00D800F5 /* Web Tracker */, 61 | 246202191F8EF95F00D800F5 /* Products */, 62 | 246202221F8EF96A00D800F5 /* Frameworks */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | 246202191F8EF95F00D800F5 /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 246202181F8EF95F00D800F5 /* Web Tracker */, 70 | ); 71 | name = Products; 72 | sourceTree = ""; 73 | }; 74 | 2462021A1F8EF95F00D800F5 /* Web Tracker */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 2462021B1F8EF95F00D800F5 /* main.swift */, 78 | 246202361F8EFCF800D800F5 /* ChromeBrowser.swift */, 79 | 246202271F8EF99200D800F5 /* Chrome.h */, 80 | 246202281F8EF9A200D800F5 /* Web Tracker-Bridging-Header.h */, 81 | 2462022C1F8EF9D300D800F5 /* SQLight */, 82 | ); 83 | path = "Web Tracker"; 84 | sourceTree = ""; 85 | }; 86 | 246202221F8EF96A00D800F5 /* Frameworks */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 246202251F8EF97000D800F5 /* libsqlite3.tbd */, 90 | 246202231F8EF96B00D800F5 /* ScriptingBridge.framework */, 91 | ); 92 | name = Frameworks; 93 | sourceTree = ""; 94 | }; 95 | 2462022C1F8EF9D300D800F5 /* SQLight */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 2462022D1F8EF9D300D800F5 /* CustomTypes.swift */, 99 | 2462022E1F8EF9D300D800F5 /* PreparedStatement.swift */, 100 | 246202301F8EF9D300D800F5 /* SQLight.swift */, 101 | ); 102 | path = SQLight; 103 | sourceTree = ""; 104 | }; 105 | /* End PBXGroup section */ 106 | 107 | /* Begin PBXNativeTarget section */ 108 | 246202171F8EF95F00D800F5 /* Web Tracker */ = { 109 | isa = PBXNativeTarget; 110 | buildConfigurationList = 2462021F1F8EF95F00D800F5 /* Build configuration list for PBXNativeTarget "Web Tracker" */; 111 | buildPhases = ( 112 | 246202141F8EF95F00D800F5 /* Sources */, 113 | 246202151F8EF95F00D800F5 /* Frameworks */, 114 | 246202161F8EF95F00D800F5 /* CopyFiles */, 115 | ); 116 | buildRules = ( 117 | ); 118 | dependencies = ( 119 | ); 120 | name = "Web Tracker"; 121 | productName = "Web Tracker"; 122 | productReference = 246202181F8EF95F00D800F5 /* Web Tracker */; 123 | productType = "com.apple.product-type.tool"; 124 | }; 125 | /* End PBXNativeTarget section */ 126 | 127 | /* Begin PBXProject section */ 128 | 246202101F8EF95F00D800F5 /* Project object */ = { 129 | isa = PBXProject; 130 | attributes = { 131 | LastSwiftUpdateCheck = 0900; 132 | LastUpgradeCheck = 0900; 133 | ORGANIZATIONNAME = SkrewEverything; 134 | TargetAttributes = { 135 | 246202171F8EF95F00D800F5 = { 136 | CreatedOnToolsVersion = 9.0; 137 | LastSwiftMigration = 0900; 138 | ProvisioningStyle = Automatic; 139 | }; 140 | }; 141 | }; 142 | buildConfigurationList = 246202131F8EF95F00D800F5 /* Build configuration list for PBXProject "Web Tracker" */; 143 | compatibilityVersion = "Xcode 8.0"; 144 | developmentRegion = en; 145 | hasScannedForEncodings = 0; 146 | knownRegions = ( 147 | en, 148 | ); 149 | mainGroup = 2462020F1F8EF95F00D800F5; 150 | productRefGroup = 246202191F8EF95F00D800F5 /* Products */; 151 | projectDirPath = ""; 152 | projectRoot = ""; 153 | targets = ( 154 | 246202171F8EF95F00D800F5 /* Web Tracker */, 155 | ); 156 | }; 157 | /* End PBXProject section */ 158 | 159 | /* Begin PBXSourcesBuildPhase section */ 160 | 246202141F8EF95F00D800F5 /* Sources */ = { 161 | isa = PBXSourcesBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | 246202351F8EF9D300D800F5 /* SQLight.swift in Sources */, 165 | 246202341F8EF9D300D800F5 /* PreparedStatement.swift in Sources */, 166 | 246202331F8EF9D300D800F5 /* CustomTypes.swift in Sources */, 167 | 2462021C1F8EF95F00D800F5 /* main.swift in Sources */, 168 | 246202371F8EFCF800D800F5 /* ChromeBrowser.swift in Sources */, 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | /* End PBXSourcesBuildPhase section */ 173 | 174 | /* Begin XCBuildConfiguration section */ 175 | 2462021D1F8EF95F00D800F5 /* Debug */ = { 176 | isa = XCBuildConfiguration; 177 | buildSettings = { 178 | ALWAYS_SEARCH_USER_PATHS = NO; 179 | CLANG_ANALYZER_NONNULL = YES; 180 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 181 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 182 | CLANG_CXX_LIBRARY = "libc++"; 183 | CLANG_ENABLE_MODULES = YES; 184 | CLANG_ENABLE_OBJC_ARC = YES; 185 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 186 | CLANG_WARN_BOOL_CONVERSION = YES; 187 | CLANG_WARN_COMMA = YES; 188 | CLANG_WARN_CONSTANT_CONVERSION = YES; 189 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 190 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 191 | CLANG_WARN_EMPTY_BODY = YES; 192 | CLANG_WARN_ENUM_CONVERSION = YES; 193 | CLANG_WARN_INFINITE_RECURSION = YES; 194 | CLANG_WARN_INT_CONVERSION = YES; 195 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 196 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 198 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 199 | CLANG_WARN_STRICT_PROTOTYPES = YES; 200 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 201 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 202 | CLANG_WARN_UNREACHABLE_CODE = YES; 203 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 204 | CODE_SIGN_IDENTITY = "-"; 205 | COPY_PHASE_STRIP = NO; 206 | DEBUG_INFORMATION_FORMAT = dwarf; 207 | ENABLE_STRICT_OBJC_MSGSEND = YES; 208 | ENABLE_TESTABILITY = YES; 209 | GCC_C_LANGUAGE_STANDARD = gnu11; 210 | GCC_DYNAMIC_NO_PIC = NO; 211 | GCC_NO_COMMON_BLOCKS = YES; 212 | GCC_OPTIMIZATION_LEVEL = 0; 213 | GCC_PREPROCESSOR_DEFINITIONS = ( 214 | "DEBUG=1", 215 | "$(inherited)", 216 | ); 217 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 219 | GCC_WARN_UNDECLARED_SELECTOR = YES; 220 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 221 | GCC_WARN_UNUSED_FUNCTION = YES; 222 | GCC_WARN_UNUSED_VARIABLE = YES; 223 | MACOSX_DEPLOYMENT_TARGET = 10.13; 224 | MTL_ENABLE_DEBUG_INFO = YES; 225 | ONLY_ACTIVE_ARCH = YES; 226 | SDKROOT = macosx; 227 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 228 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 229 | }; 230 | name = Debug; 231 | }; 232 | 2462021E1F8EF95F00D800F5 /* Release */ = { 233 | isa = XCBuildConfiguration; 234 | buildSettings = { 235 | ALWAYS_SEARCH_USER_PATHS = NO; 236 | CLANG_ANALYZER_NONNULL = YES; 237 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 238 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 239 | CLANG_CXX_LIBRARY = "libc++"; 240 | CLANG_ENABLE_MODULES = YES; 241 | CLANG_ENABLE_OBJC_ARC = YES; 242 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 243 | CLANG_WARN_BOOL_CONVERSION = YES; 244 | CLANG_WARN_COMMA = YES; 245 | CLANG_WARN_CONSTANT_CONVERSION = YES; 246 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 247 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 248 | CLANG_WARN_EMPTY_BODY = YES; 249 | CLANG_WARN_ENUM_CONVERSION = YES; 250 | CLANG_WARN_INFINITE_RECURSION = YES; 251 | CLANG_WARN_INT_CONVERSION = YES; 252 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 253 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 255 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 256 | CLANG_WARN_STRICT_PROTOTYPES = YES; 257 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 258 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 259 | CLANG_WARN_UNREACHABLE_CODE = YES; 260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 261 | CODE_SIGN_IDENTITY = "-"; 262 | COPY_PHASE_STRIP = NO; 263 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 264 | ENABLE_NS_ASSERTIONS = NO; 265 | ENABLE_STRICT_OBJC_MSGSEND = YES; 266 | GCC_C_LANGUAGE_STANDARD = gnu11; 267 | GCC_NO_COMMON_BLOCKS = YES; 268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 269 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 270 | GCC_WARN_UNDECLARED_SELECTOR = YES; 271 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 272 | GCC_WARN_UNUSED_FUNCTION = YES; 273 | GCC_WARN_UNUSED_VARIABLE = YES; 274 | MACOSX_DEPLOYMENT_TARGET = 10.13; 275 | MTL_ENABLE_DEBUG_INFO = NO; 276 | SDKROOT = macosx; 277 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 278 | }; 279 | name = Release; 280 | }; 281 | 246202201F8EF95F00D800F5 /* Debug */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | CLANG_ENABLE_MODULES = YES; 285 | CODE_SIGN_STYLE = Automatic; 286 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 287 | PRODUCT_NAME = "$(TARGET_NAME)"; 288 | SWIFT_OBJC_BRIDGING_HEADER = "Web Tracker/Web Tracker-Bridging-Header.h"; 289 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 290 | SWIFT_VERSION = 4.0; 291 | }; 292 | name = Debug; 293 | }; 294 | 246202211F8EF95F00D800F5 /* Release */ = { 295 | isa = XCBuildConfiguration; 296 | buildSettings = { 297 | CLANG_ENABLE_MODULES = YES; 298 | CODE_SIGN_STYLE = Automatic; 299 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 300 | PRODUCT_NAME = "$(TARGET_NAME)"; 301 | SWIFT_OBJC_BRIDGING_HEADER = "Web Tracker/Web Tracker-Bridging-Header.h"; 302 | SWIFT_VERSION = 4.0; 303 | }; 304 | name = Release; 305 | }; 306 | /* End XCBuildConfiguration section */ 307 | 308 | /* Begin XCConfigurationList section */ 309 | 246202131F8EF95F00D800F5 /* Build configuration list for PBXProject "Web Tracker" */ = { 310 | isa = XCConfigurationList; 311 | buildConfigurations = ( 312 | 2462021D1F8EF95F00D800F5 /* Debug */, 313 | 2462021E1F8EF95F00D800F5 /* Release */, 314 | ); 315 | defaultConfigurationIsVisible = 0; 316 | defaultConfigurationName = Release; 317 | }; 318 | 2462021F1F8EF95F00D800F5 /* Build configuration list for PBXNativeTarget "Web Tracker" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | 246202201F8EF95F00D800F5 /* Debug */, 322 | 246202211F8EF95F00D800F5 /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | /* End XCConfigurationList section */ 328 | }; 329 | rootObject = 246202101F8EF95F00D800F5 /* Project object */; 330 | } 331 | -------------------------------------------------------------------------------- /Web Tracker/Web Tracker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Web Tracker/Web Tracker.xcodeproj/project.xcworkspace/xcuserdata/sriharish.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkrewEverything/Web-Tracker/51fb367bc6e749fffd28ee1db052c9d51d08435d/Web Tracker/Web Tracker.xcodeproj/project.xcworkspace/xcuserdata/sriharish.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Web Tracker/Web Tracker.xcodeproj/xcuserdata/sriharish.xcuserdatad/xcschemes/Web Tracker.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Web Tracker/Web Tracker.xcodeproj/xcuserdata/sriharish.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Web Tracker.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 246202171F8EF95F00D800F5 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Web Tracker/Web Tracker/Chrome.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Chrome.h 3 | */ 4 | 5 | #import 6 | #import 7 | 8 | 9 | @class ChromeApplication, ChromeWindow, ChromeTab, ChromeBookmarkFolder, ChromeBookmarkItem; 10 | 11 | @protocol ChromeGenericMethods 12 | 13 | - (void) saveIn:(NSURL *)in_ as:(NSString *)as; // Save an object. 14 | - (void) close; // Close a window. 15 | - (void) delete; // Delete an object. 16 | - (SBObject *) duplicateTo:(SBObject *)to withProperties:(NSDictionary *)withProperties; // Copy object(s) and put the copies at a new location. 17 | - (SBObject *) moveTo:(SBObject *)to; // Move object(s) to a new location. 18 | - (void) print; // Print an object. 19 | - (void) reload; // Reload a tab. 20 | - (void) goBack; // Go Back (If Possible). 21 | - (void) goForward; // Go Forward (If Possible). 22 | - (void) selectAll; // Select all. 23 | - (void) cutSelection; // Cut selected text (If Possible). 24 | - (void) copySelection NS_RETURNS_NOT_RETAINED; // Copy text. 25 | - (void) pasteSelection; // Paste text (If Possible). 26 | - (void) undo; // Undo the last change. 27 | - (void) redo; // Redo the last change. 28 | - (void) stop; // Stop the current tab from loading. 29 | - (void) viewSource; // View the HTML source of the tab. 30 | - (id) executeJavascript:(NSString *)javascript; // Execute a piece of javascript. 31 | 32 | @end 33 | 34 | 35 | 36 | /* 37 | * Standard Suite 38 | */ 39 | 40 | // The application's top-level scripting object. 41 | @interface ChromeApplication : SBApplication 42 | 43 | - (SBElementArray *) windows; 44 | 45 | @property (copy, readonly) NSString *name; // The name of the application. 46 | @property (readonly) BOOL frontmost; // Is this the frontmost (active) application? 47 | @property (copy, readonly) NSString *version; // The version of the application. 48 | 49 | - (void) open:(NSArray *)x; // Open a document. 50 | - (void) quit; // Quit the application. 51 | - (BOOL) exists:(id)x; // Verify if an object exists. 52 | 53 | @end 54 | 55 | // A window. 56 | @interface ChromeWindow : SBObject 57 | 58 | - (SBElementArray *) tabs; 59 | 60 | @property (copy, readonly) NSString *name; // The full title of the window. 61 | - (NSInteger) id; // The unique identifier of the window. 62 | @property NSInteger index; // The index of the window, ordered front to back. 63 | @property NSRect bounds; // The bounding rectangle of the window. 64 | @property (readonly) BOOL closeable; // Whether the window has a close box. 65 | @property (readonly) BOOL minimizable; // Whether the window can be minimized. 66 | @property BOOL minimized; // Whether the window is currently minimized. 67 | @property (readonly) BOOL resizable; // Whether the window can be resized. 68 | @property BOOL visible; // Whether the window is currently visible. 69 | @property (readonly) BOOL zoomable; // Whether the window can be zoomed. 70 | @property BOOL zoomed; // Whether the window is currently zoomed. 71 | @property (copy, readonly) ChromeTab *activeTab; // Returns the currently selected tab 72 | @property (copy) NSString *mode; // Represents the mode of the window which can be 'normal' or 'incognito', can be set only once during creation of the window. 73 | @property NSInteger activeTabIndex; // The index of the active tab. 74 | 75 | 76 | @end 77 | 78 | 79 | 80 | /* 81 | * Chromium Suite 82 | */ 83 | 84 | // The application's top-level scripting object. 85 | @interface ChromeApplication (ChromiumSuite) 86 | 87 | - (SBElementArray *) bookmarkFolders; 88 | 89 | @property (copy, readonly) ChromeBookmarkFolder *bookmarksBar; // The bookmarks bar bookmark folder. 90 | @property (copy, readonly) ChromeBookmarkFolder *otherBookmarks; // The other bookmarks bookmark folder. 91 | 92 | @end 93 | 94 | // A tab. 95 | @interface ChromeTab : SBObject 96 | 97 | - (NSInteger) id; // Unique ID of the tab. 98 | @property (copy, readonly) NSString *title; // The title of the tab. 99 | @property (copy) NSString *URL; // The url visible to the user. 100 | @property (readonly) BOOL loading; // Is loading? 101 | 102 | 103 | @end 104 | 105 | // A bookmarks folder that contains other bookmarks folder and bookmark items. 106 | @interface ChromeBookmarkFolder : SBObject 107 | 108 | - (SBElementArray *) bookmarkFolders; 109 | - (SBElementArray *) bookmarkItems; 110 | 111 | - (NSNumber *) id; // Unique ID of the bookmark folder. 112 | @property (copy) NSString *title; // The title of the folder. 113 | @property (copy, readonly) NSNumber *index; // Returns the index with respect to its parent bookmark folder 114 | 115 | 116 | @end 117 | 118 | // An item consists of an URL and the title of a bookmark 119 | @interface ChromeBookmarkItem : SBObject 120 | 121 | - (NSInteger) id; // Unique ID of the bookmark item. 122 | @property (copy) NSString *title; // The title of the bookmark item. 123 | @property (copy) NSString *URL; // The URL of the bookmark. 124 | @property (copy, readonly) NSNumber *index; // Returns the index with respect to its parent bookmark folder 125 | 126 | 127 | @end 128 | 129 | -------------------------------------------------------------------------------- /Web Tracker/Web Tracker/ChromeBrowser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChromeBrowser.swift 3 | // Web Tracker 4 | // 5 | // Created by Skrew Everything on 12/10/17. 6 | // Copyright © 2017 SkrewEverything. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import ScriptingBridge 11 | 12 | /// Scans the Chrome Browser and saves the currently opened tabs including incognito windows 13 | public class ChromeBrowser 14 | { 15 | /// `Timer` object to run the tracker periodically 16 | private var timer = Timer() 17 | 18 | /// Webpage Title 19 | private var webpageTitle: String = "" 20 | 21 | /// Webpage URL 22 | private var webpageURL: String = "" 23 | 24 | /// Date when scan performed 25 | private var date: String = "" 26 | 27 | /// Time when scan performed 28 | private var time: String = "" 29 | 30 | /// 0 for normal window, 1 for incognito window 31 | private var mode: Int = 0 32 | 33 | /// Previous list to check if the current webpages list have duplicates or not 34 | private var previousList: [String: [Any]] = [:] 35 | 36 | /// Current list of tabs in all windows 37 | private var currentList: [String: [Any]] = [:] 38 | 39 | /// Only distinct values after removing duplicates values from previousList and currentList 40 | private var outputList: [[Any]] = [] 41 | 42 | /// Database object 43 | private let DB: SQLight 44 | 45 | /// Insert query 46 | private var query: String = "" 47 | 48 | /// Insert query compiled into byte-code. Bind the required parameters 49 | private var bindQuery: PreparedStatement! = nil 50 | 51 | /// Path to database file 52 | private let pathToDB: String 53 | 54 | /// If it's true, then it tries to create a table in the database file 55 | private var initial: Bool = true 56 | 57 | /** 58 | Starts the Timer to scan the browser 59 | 60 | - parameters: 61 | - time: Time in secs after which the scan should be performed. Default is 5secs 62 | - pathToDB: Path to the already created or to be created Database file. Default is "web-tracker.db" and it is created in the directory where the executable is present. 63 | It can take relative path using dot notation but `~` produces error. 64 | */ 65 | init(time: Double = 5, pathToDB: String = Bundle.main.bundlePath + "/web-tracker.db") 66 | { 67 | do 68 | { 69 | self.DB = try SQLight(type: .file(pathToDB)) 70 | self.pathToDB = pathToDB 71 | 72 | } 73 | catch let error as DBError 74 | { 75 | print("Error message: ",error.message, "\nError code: ",error.errorCode) 76 | exit(-9) // If the Database fails to open the connection, then close the application 77 | } 78 | catch // This catch is just to silent the warning in Xcode 79 | { 80 | exit(-9) 81 | } 82 | 83 | // Starts the Timer 84 | timer = Timer.scheduledTimer(timeInterval: time, target: self, selector: #selector(run), userInfo: nil, repeats: true) 85 | 86 | } 87 | 88 | /** 89 | Performes the scan and adds the list to the Database 90 | */ 91 | @objc private func run() 92 | { 93 | let chromeObject: AnyObject = SBApplication.init(bundleIdentifier: "com.google.Chrome")! 94 | let chromeWindowsList = chromeObject.windows() 95 | 96 | self.currentList = [:] 97 | 98 | for eachWindow in chromeWindowsList! 99 | { 100 | let chromeTabsListInEachWindow = (eachWindow as AnyObject).tabs() 101 | for j in chromeTabsListInEachWindow! 102 | { 103 | self.webpageTitle = (j as AnyObject).title // Always define the data type explicitly to avoid the ambiguous error 104 | self.webpageURL = (j as AnyObject).url 105 | let mode: String = (eachWindow as AnyObject).mode 106 | if mode.caseInsensitiveCompare("incognito") == .orderedSame 107 | { 108 | //print("incognito") 109 | self.mode = 1 110 | } 111 | else 112 | { 113 | //print("normal") 114 | self.mode = 0 115 | } 116 | 117 | self.date = self.getDate() 118 | self.time = self.getTime() 119 | 120 | // Add it to the dictionary 121 | self.currentList[self.webpageURL] = [self.webpageURL, self.webpageTitle, self.mode, self.time, self.date] 122 | } 123 | } 124 | 125 | 126 | if self.previousList.count == 0 // Initial run 127 | { 128 | for ( _ , array) in self.currentList // Copy all the entries from currentlist to outputlist and previouslist 129 | { 130 | self.outputList.append(array) 131 | } 132 | } 133 | else 134 | { 135 | for (url, array) in self.currentList 136 | { 137 | if self.previousList[url] == nil // Check if the URL is present in the previous list. If not, then add it to the outputlist 138 | { 139 | self.outputList.append(array) 140 | } 141 | } 142 | } 143 | 144 | //print("=======================") 145 | //print(self.outputList) 146 | //print("=======================") 147 | self.previousList = self.currentList 148 | self.addToDB() 149 | 150 | 151 | } 152 | 153 | /** 154 | Add the list to the Database 155 | */ 156 | private func addToDB() 157 | { 158 | do 159 | { 160 | if self.initial 161 | { 162 | self.createTable() 163 | self.initial = false 164 | self.query = "insert into data values(@url, @title, @incognito, @time, @date);" 165 | self.bindQuery = try PreparedStatement(SQLQuery: self.query, SQLightDB: self.DB) 166 | } 167 | 168 | for row in self.outputList 169 | { 170 | try self.bindQuery.bindValues(row) 171 | let _ = try self.bindQuery.modify() //inserted data into database here 172 | self.bindQuery.resetBindValues() 173 | } 174 | 175 | // Clear the outputList after the list is inserted into the database 176 | self.outputList = [] 177 | } 178 | catch let error as DBError 179 | { 180 | print("Error message: ",error.message, "\nError code: ",error.errorCode) 181 | } 182 | catch let error // This catch is just to silent the warning in Xcode 183 | { 184 | print(error) 185 | } 186 | } 187 | 188 | /** 189 | Creates a new table - `data` 190 | */ 191 | private func createTable() 192 | { 193 | do 194 | { 195 | let createQuery = "create table data(url varchar, title varchar, incognito int, time varchar, date varchar);" 196 | let createPS = try PreparedStatement(SQLQuery: createQuery, SQLightDB: self.DB) 197 | let _ = try createPS.modify() 198 | 199 | } 200 | catch let error as DBError 201 | { 202 | if error.message.contains("already exists") 203 | { 204 | // Leave it 205 | } 206 | else 207 | { 208 | print("Error message: ",error.message, "\nError code: ",error.errorCode) 209 | } 210 | } 211 | catch let error // This catch is just to silent the warning in Xcode 212 | { 213 | print(error) 214 | } 215 | } 216 | 217 | /** 218 | Get the current Date 219 | 220 | - returns: 221 | Current Date as String 222 | */ 223 | private func getDate() -> String 224 | { 225 | let calendar = Calendar.current 226 | return "\(calendar.component(.day, from: Date()))-\(calendar.component(.month, from: Date()))-\(calendar.component(.year, from: Date()))" 227 | } 228 | 229 | /** 230 | Get the current Time 231 | 232 | - returns: 233 | Current Time as String 234 | */ 235 | private func getTime() -> String 236 | { 237 | let calendar = Calendar.current 238 | return "\(calendar.component(.hour, from: Date())):\(calendar.component(.minute, from: Date()))" 239 | } 240 | 241 | deinit { 242 | try! self.bindQuery.destroy() 243 | try! self.DB.close() 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /Web Tracker/Web Tracker/SQLight/CustomTypes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomTypes.swift 3 | // 4 | // SQLight 5 | // 6 | // Created by Skrew Everything on 29/06/17. 7 | // Copyright © 2017 SkrewEverything. All rights reserved. 8 | // 9 | 10 | import Foundation 11 | 12 | 13 | /* https://stackoverflow.com/questions/26883131/sqlite-transient-undefined-in-swift/26884081#26884081 14 | 15 | 16 | */ 17 | internal let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self) 18 | internal let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self) 19 | 20 | 21 | ///Callback closure type 22 | public typealias SQLiteExecCallBack = @convention(c) (_ void: UnsafeMutableRawPointer?, _ columnCount: Int32, _ values: UnsafeMutablePointer?>?, _ columns:UnsafeMutablePointer?>?) -> Int32 23 | 24 | 25 | /// Specifies what type of DataBase to use 26 | /// 27 | /// There are 2 possible values. 28 | /// * **DBType.inMemory**: Creates Database in RAM 29 | /// * **DBType.file(String)**: Creates or opens Database file specified by name as an argument 30 | public enum DBType 31 | { 32 | case inMemory 33 | case file(String) 34 | } 35 | 36 | /** 37 | Required to cast to appropriate data type while using "select" query. 38 | 39 | Taken from -> https://sqlite.org/c3ref/c_blob.html 40 | 41 | #define SQLITE_INTEGER 1 42 | #define SQLITE_FLOAT 2 43 | #define SQLITE_BLOB 4 44 | #define SQLITE_NULL 5 45 | #ifdef SQLITE_TEXT 46 | # undef SQLITE_TEXT 47 | #else 48 | # define SQLITE_TEXT 3 49 | #endif 50 | #define SQLITE3_TEXT 3 51 | 52 | */ 53 | internal enum SQLiteDataType: Int32 54 | { 55 | case integer = 1, float, text, blob, null 56 | } 57 | 58 | 59 | 60 | /// Error thrown by SQLight 61 | public struct DBError: Error 62 | { 63 | let message: String 64 | let errorCode: Int32 65 | 66 | init(db: SQLight, ec: Int32, customMessage: String? = nil) 67 | { 68 | self.errorCode = ec 69 | if let cm = customMessage 70 | { 71 | self.message = cm 72 | } 73 | else if String(cString: sqlite3_errmsg(db.dbPointer)) == "not an error" 74 | { 75 | self.message = String(cString: sqlite3_errstr(ec)) 76 | } 77 | else 78 | { 79 | self.message = String(cString: sqlite3_errmsg(db.dbPointer)) 80 | } 81 | } 82 | } 83 | 84 | extension DBError: LocalizedError 85 | { 86 | public var errorDescription: String? { 87 | return self.message 88 | } 89 | } 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /Web Tracker/Web Tracker/SQLight/PreparedStatement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreparedStatement.swift 3 | // SQLight 4 | // 5 | // Created by Skrew Everything on 30/06/17. 6 | // Copyright © 2017 SkrewEverything. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class PreparedStatement 12 | { 13 | /// SQL Query of the prepared statement 14 | public let SQLQuery: String 15 | 16 | /// Compiled SQL Query: byte-code 17 | internal var preparedStatement: OpaquePointer! 18 | 19 | /// Database connection currently opened where queries need to be executed 20 | private let db: SQLight 21 | 22 | /// Compiles SQL query into a byte-code for execution. 23 | /// - Note: To execute an SQL query, it must first be compiled into a byte-code program 24 | /// - parameter SQLQuery: Any SQL query 25 | /// - parameter SQLightDB: Object returned from SQLight(type:) 26 | /// - throws: If any error occurs during compiling. Mostly syntax errors. 27 | init(SQLQuery: String, SQLightDB: SQLight) throws 28 | { 29 | self.SQLQuery = SQLQuery 30 | self.db = SQLightDB 31 | self.preparedStatement = try self.prepareStatement() 32 | 33 | } 34 | 35 | /// Compiles SQL query into a byte-code for execution. 36 | /// - Note: To execute an SQL query, it must first be compiled into a byte-code program 37 | /// # More Info: 38 | /// [https://sqlite.org/c3ref/prepare.html]() 39 | /// 40 | /// Using: `sqlite3_prepare_v2()` 41 | /// - returns: Compiled SQL Query which can be used to execute 42 | /// - throws: If any error occurs during compiling. Mostly syntax errors. 43 | private func prepareStatement() throws -> OpaquePointer 44 | { 45 | /// Temporary prepared statement(byte-code) to return 46 | var pStmt: OpaquePointer? = nil 47 | // Third parameter can be -1 but giving length of string can increase performance slightly 48 | let rc = sqlite3_prepare_v2(self.db.dbPointer, self.SQLQuery, -1, &pStmt, nil) 49 | if rc != SQLITE_OK 50 | { 51 | throw DBError(db: self.db, ec: rc) 52 | } 53 | if pStmt == nil 54 | { 55 | throw DBError(db: self.db, ec: rc) 56 | } 57 | 58 | return pStmt! 59 | } 60 | 61 | /// Bind values from left to right in prepared statement. 62 | /// - parameter elements: All the values/elements to bind to prepared statement 63 | /// - throws: If any error occurs while binding values 64 | public func bindValues(_ elements: [Any]) throws 65 | { 66 | var parameterIndexesandElements = [Int:Any]() 67 | var index = 1 68 | for i in elements 69 | { 70 | parameterIndexesandElements[index] = i 71 | index += 1 72 | } 73 | try self.bind(parameterIndexesandElements) 74 | } 75 | 76 | /// Bind values based on parameter name. 77 | /// - parameter parameterNamesandElements: Parameter name as key and Binding value as value in the form of dictionary 78 | /// - throws: If any error occurs while binding values 79 | public func bindValues(_ parameterNamesandElements: [String:Any]) throws 80 | { 81 | var parameterIndexesandElements = [Int:Any]() 82 | for i in parameterNamesandElements 83 | { 84 | parameterIndexesandElements[self.getParameterIndex(parameterName: i.key)] = i.value 85 | } 86 | try self.bind(parameterIndexesandElements) 87 | } 88 | 89 | /// Bind values based on parameter index. 90 | /// - parameter parameterIndexesandElements: Parameter index as key and Binding value as value in the form of dictionary 91 | /// - throws: If any error occurs while binding values 92 | public func bindValues(_ parameterIndexesandElements: [Int:Any]) throws 93 | { 94 | try self.bind(parameterIndexesandElements) 95 | } 96 | 97 | /// Bind values based on parameter index. 98 | /// - parameter parameterIndexesandElements: Parameter index as key and Binding value as value in the form of dictionary 99 | /// - throws: If any error occurs while binding values 100 | private func bind(_ parameterIndexesandElements: [Int:Any]) throws 101 | { 102 | var rc: Int32 = 0 103 | for i in parameterIndexesandElements 104 | { 105 | if i.value is Int 106 | { 107 | rc = sqlite3_bind_int(self.preparedStatement, Int32(i.key), Int32(i.value as! Int)) 108 | } 109 | else if i.value is String 110 | { 111 | rc = sqlite3_bind_text(self.preparedStatement, Int32(i.key), i.value as! String, Int32((i.value as! String).lengthOfBytes(using: .utf8)), SQLITE_TRANSIENT) 112 | } 113 | else if i.value is Double 114 | { 115 | rc = sqlite3_bind_double(self.preparedStatement, Int32(i.key), i.value as! Double) 116 | } 117 | else 118 | { 119 | print("Unknown datatype") 120 | throw DBError(db: self.db, ec: 0, customMessage: "Unknown type or blob and null not supported") 121 | } 122 | 123 | if rc != SQLITE_OK 124 | { 125 | throw DBError(db: self.db, ec: rc) 126 | } 127 | } 128 | } 129 | 130 | /// Number of bind parameters in a prepared statement. 131 | /// - returns: Number of bind parameters. 132 | public func getParameterCount() -> Int 133 | { 134 | let count = sqlite3_bind_parameter_count(self.preparedStatement) 135 | return Int(count) 136 | } 137 | 138 | /// Get the parameter index using parameter name 139 | /// - note: Index starts from **1**, not from **0**. 140 | /// - parameter parameterName: Name of the parameter 141 | /// - returns: Index of the specified parameter. 142 | public func getParameterIndex(parameterName: String) -> Int 143 | { 144 | let id = sqlite3_bind_parameter_index(self.preparedStatement, parameterName) 145 | return Int(id) 146 | } 147 | 148 | /// Parameter name specified by index 149 | /// - note: Index starts from **1**, not from **0**. 150 | /// - parameter parameterIndex: Index of the required bind paramter. 151 | /// - returns: Parameter name or nil. 152 | public func getParameterName(parameterIndex: Int) -> String? 153 | { 154 | if let name = sqlite3_bind_parameter_name(self.preparedStatement, Int32(parameterIndex)) 155 | { 156 | return String(cString: name) 157 | } 158 | else 159 | { 160 | return nil 161 | } 162 | } 163 | 164 | /// Returns column names in `SELECT` statement. 165 | /// - returns: Column names as `String` array. If the statement is not `SELECT` then `nil` is returned. 166 | public func getColumnNames() -> [String]? 167 | { 168 | if SQLQuery.lowercased().contains("select") 169 | { 170 | var columns = [String]() 171 | for i in 0..? = nil 194 | var rc: Int32 = 0 195 | rc = sqlite3_exec(self.db.dbPointer, self.SQLQuery, callback ?? nil , nil, &zErrMsg) 196 | 197 | if rc != SQLITE_OK 198 | { 199 | let msg = String(cString: zErrMsg!) 200 | sqlite3_free(zErrMsg) 201 | throw DBError(db: self.db, ec: rc, customMessage: msg) 202 | } 203 | } 204 | 205 | /// Executes a query (which modifies the table like `UPDATE`, `INSERT`, `DELETE` etc). 206 | /// - note: Use `fetchAllRows(preparedStatement:)` or `fetchNextRow(preparedStatement:)` to execute queries with **SELECT**. 207 | /// - returns: Number of rows changed. 208 | /// - throws: If any error occurs while exeuting the command/query. 209 | public func modify() throws -> Int 210 | { 211 | let rc = sqlite3_step(self.preparedStatement) 212 | if rc == SQLITE_DONE // SQLITE_DONE is returned for sql queries other than select query(it returns SQLITE_ROW) 213 | { 214 | 215 | /* 216 | The sqlite3_reset() function is called to reset a prepared statement object back to its initial state, ready to be re-executed. 217 | It does not change the values of any bindings on the prepared statement 218 | Use sqlite3_clear_bindings() to reset the bindings. 219 | */ 220 | try self.reset() 221 | return Int(sqlite3_changes(self.db.dbPointer)) 222 | } 223 | else 224 | { 225 | try self.reset() 226 | throw DBError(db: self.db, ec: rc, customMessage: "") 227 | } 228 | 229 | } 230 | 231 | /// Executes and returns all the rows while using `SELECT` query. 232 | /// - warning: If the data being retrieved is large, it is advised not to use this method as all the retrieved rows are stored in the memory and returned. Use `fetchNextRow(preparedStatement:)` as it returns only 1 row at a time or use `execute(SQLQuery:callbackForSelectQuery:)` which uses closure 233 | /// - returns: All the retrieved rows and columns as a 2D Array 234 | /// - throws: If any error occurs while exeuting the command/query. 235 | public func fetchAllRows() throws -> [[Any]] 236 | { 237 | var data = [[Any]]() 238 | var data1 = [Any]() 239 | while true 240 | { 241 | let rc = sqlite3_step(self.preparedStatement) 242 | if rc == SQLITE_ROW // SQLITE_ROW is returned for select query. Other queries returns SQLITE_DONE 243 | { 244 | for i in 0.. [Any]? 290 | { 291 | var data = [Any]() 292 | let rc = sqlite3_step(self.preparedStatement) 293 | if rc == SQLITE_ROW // SQLITE_ROW is returned for select query. Other queries returns SQLITE_DONE 294 | { 295 | for i in 0.. 6 | #include "Chrome.h" 7 | -------------------------------------------------------------------------------- /Web Tracker/Web Tracker/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // Web Tracker 4 | // 5 | // Created by Skrew Everything on 12/10/17. 6 | // Copyright © 2017 SkrewEverything. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | let args = CommandLine.arguments 13 | 14 | if args.count == 3 // If time and path both are specified 15 | { 16 | if let time = Double(args[1]) // If first argument is time 17 | { 18 | let _ = ChromeBrowser(time: time, pathToDB: args[2]) 19 | } 20 | else if let time = Double(args[2]) // If second argument is time 21 | { 22 | let _ = ChromeBrowser(time: time, pathToDB: args[1]) 23 | } 24 | } 25 | else if args.count == 2 // If only one of them is specified 26 | { 27 | if let time = Double(args[1]) // Time is specified 28 | { 29 | let _ = ChromeBrowser(time: time) 30 | } 31 | else // Path is specified 32 | { 33 | let _ = ChromeBrowser(pathToDB: args[1]) 34 | } 35 | } 36 | else // Nothing is specified 37 | { 38 | let _ = ChromeBrowser() 39 | } 40 | 41 | RunLoop.main.run() 42 | --------------------------------------------------------------------------------