Compare commits
504 Commits
1.2.1
...
2.1-releas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c97546a232 | ||
|
|
88fdc89f27 | ||
|
|
9109a59410 | ||
|
|
61bed8b29c | ||
|
|
72b1d36626 | ||
|
|
6e14554f95 | ||
|
|
fecbd2ea1c | ||
|
|
1c5f5675a5 | ||
|
|
76b717e06d | ||
|
|
eb48f749e2 | ||
|
|
398f7bdb66 | ||
|
|
d40ccee0fe | ||
|
|
a481626f80 | ||
|
|
24c48a78f8 | ||
|
|
edda3cf12a | ||
|
|
6c2cd01015 | ||
|
|
c9988d8cc2 | ||
|
|
a8bb434ded | ||
|
|
1e8a832cba | ||
|
|
4f70e0f676 | ||
|
|
9d7799c814 | ||
|
|
2adb74c971 | ||
|
|
cc80a66331 | ||
|
|
cf8ecb2952 | ||
|
|
adcea94a37 | ||
|
|
3ebef16007 | ||
|
|
3225985e1e | ||
|
|
76280ac71c | ||
|
|
f47ff67ba9 | ||
|
|
65cef6d8ed | ||
|
|
89145d6e13 | ||
|
|
63b4c605e2 | ||
|
|
b3b7858c1d | ||
|
|
eb4ea08a8b | ||
|
|
51d61b8bc0 | ||
|
|
bddbd199e2 | ||
|
|
b579dac180 | ||
|
|
609675f663 | ||
|
|
c57bd5d5d3 | ||
|
|
a4fe13842a | ||
|
|
56bb5bf5db | ||
|
|
f720d552bb | ||
|
|
ce8a00fd48 | ||
|
|
d72defb0bc | ||
|
|
27f6bd7905 | ||
|
|
2aebcadf70 | ||
|
|
cf750f30e0 | ||
|
|
cf52221175 | ||
|
|
adc79d331b | ||
|
|
942d10e7a2 | ||
|
|
aa60dcfbe4 | ||
|
|
a831e15b3e | ||
|
|
18ae7a316f | ||
|
|
7bcc631caf | ||
|
|
caab474355 | ||
|
|
2d4e26e009 | ||
|
|
7ce8df664c | ||
|
|
6597a44096 | ||
|
|
b481af2a51 | ||
|
|
d35434705c | ||
|
|
512f6faf84 | ||
|
|
60ecd8181e | ||
|
|
31b2f22ded | ||
|
|
9d5c2e29af | ||
|
|
3740ade240 | ||
|
|
9f2c83e4a9 | ||
|
|
82195ee090 | ||
|
|
eb2e070fe3 | ||
|
|
14311dfe66 | ||
|
|
10838aad00 | ||
|
|
d340278ec5 | ||
|
|
2eb074be87 | ||
|
|
b84c9b79c8 | ||
|
|
c367d5e6cb | ||
|
|
f8973d6c52 | ||
|
|
beaf4e67e7 | ||
|
|
06a150792d | ||
|
|
9816e783e7 | ||
|
|
8b26c301cf | ||
|
|
6ddd608a3f | ||
|
|
f1a72d8160 | ||
|
|
6d4d57441b | ||
|
|
971538d6b5 | ||
|
|
34d8dc375f | ||
|
|
f294a8c9f5 | ||
|
|
2bd4d57869 | ||
|
|
c7344dbb0c | ||
|
|
97809a1bc7 | ||
|
|
5056f9e2e4 | ||
|
|
150304941c | ||
|
|
ff36f6ce87 | ||
|
|
6422084fa0 | ||
|
|
651a398798 | ||
|
|
31b667c7f7 | ||
|
|
10affd615c | ||
|
|
126c962a98 | ||
|
|
2d6a298095 | ||
|
|
cc086e2dff | ||
|
|
c5fc87b7b5 | ||
|
|
1433c0851e | ||
|
|
c60780a197 | ||
|
|
bb48771989 | ||
|
|
5466e48629 | ||
|
|
d4969a776a | ||
|
|
bb81f5bd5b | ||
|
|
c38920f238 | ||
|
|
be0ae7ff45 | ||
|
|
6808016ab7 | ||
|
|
d3c2698790 | ||
|
|
d1cc9481c3 | ||
|
|
cced75cdfe | ||
|
|
b74bc79699 | ||
|
|
585268eb0f | ||
|
|
a713a639cd | ||
|
|
113c3790fb | ||
|
|
d732b03828 | ||
|
|
cf9dabcc82 | ||
|
|
2bc357cee6 | ||
|
|
c4dca14800 | ||
|
|
d8e2707ac0 | ||
|
|
25df56f90a | ||
|
|
2006e382d4 | ||
|
|
98bffa3755 | ||
|
|
789aa26066 | ||
|
|
773058da78 | ||
|
|
339ef8d1bc | ||
|
|
db55634ab2 | ||
|
|
c3364bbbef | ||
|
|
74e8001506 | ||
|
|
c1c15ddb89 | ||
|
|
c8eb2fdde7 | ||
|
|
da7abf1030 | ||
|
|
8250e9b2c4 | ||
|
|
e97d6bded4 | ||
|
|
8805a411ba | ||
|
|
a1ca633aaf | ||
|
|
78d5fc0904 | ||
|
|
d9e251c2cd | ||
|
|
fe2b6024bc | ||
|
|
1be65c0969 | ||
|
|
557eb95c7e | ||
|
|
9941dba008 | ||
|
|
fa57b8817b | ||
|
|
943c378206 | ||
|
|
b53de7a648 | ||
|
|
09afe61a4c | ||
|
|
7e96496614 | ||
|
|
d43c19a749 | ||
|
|
989cafda71 | ||
|
|
e165c6941d | ||
|
|
9105055ff4 | ||
|
|
6345899783 | ||
|
|
b1daeaf8ed | ||
|
|
9fee4a2bbe | ||
|
|
fc82790b8c | ||
|
|
d95e5ec263 | ||
|
|
99456a35ab | ||
|
|
b4e052fc9d | ||
|
|
d3852dc78c | ||
|
|
f41b7813f9 | ||
|
|
6df4a13d93 | ||
|
|
aa461b73c8 | ||
|
|
e070082f4a | ||
|
|
079434d62b | ||
|
|
f57de77545 | ||
|
|
18657271ba | ||
|
|
11d1dc711d | ||
|
|
965d5efe7f | ||
|
|
87b01fcaaf | ||
|
|
b2624c7572 | ||
|
|
bd37f1d6a7 | ||
|
|
f475c15360 | ||
|
|
d3d4aeea41 | ||
|
|
5913ce80e5 | ||
|
|
4c8bed2826 | ||
|
|
d036b43d6f | ||
|
|
318aca4d8f | ||
|
|
060c9f91f3 | ||
|
|
4184f609d6 | ||
|
|
658d710847 | ||
|
|
3fa9843855 | ||
|
|
de3f51b447 | ||
|
|
43c32e0f4c | ||
|
|
775a6fd4ea | ||
|
|
4f594c8c1d | ||
|
|
ebadac8cd8 | ||
|
|
c48bed5ebd | ||
|
|
4f3efde6f0 | ||
|
|
5b4e86a90a | ||
|
|
2be83752db | ||
|
|
bd1a0f4e25 | ||
|
|
70cd397591 | ||
|
|
1120529e34 | ||
|
|
f57415a08b | ||
|
|
f9da568bfd | ||
|
|
39c9f8c5a0 | ||
|
|
a645e22973 | ||
|
|
eaf86d3348 | ||
|
|
2565321a4a | ||
|
|
ec828a82fd | ||
|
|
411aa41226 | ||
|
|
00b164058d | ||
|
|
9808613c75 | ||
|
|
1d20d81652 | ||
|
|
f5c66ff35a | ||
|
|
4c3d3234f5 | ||
|
|
d543173b18 | ||
|
|
d8578e0162 | ||
|
|
d665833eba | ||
|
|
33e25a5fed | ||
|
|
c0737de939 | ||
|
|
a18679eba8 | ||
|
|
ce321aeceb | ||
|
|
6074547f64 | ||
|
|
8432932cb7 | ||
|
|
f8dccc04d7 | ||
|
|
b38ef59c93 | ||
|
|
c1162c76d3 | ||
|
|
231426d5b3 | ||
|
|
783eb95438 | ||
|
|
7bdc3e153d | ||
|
|
28460d4576 | ||
|
|
396f6fa7bd | ||
|
|
e39eafee7f | ||
|
|
f1549fe922 | ||
|
|
b16c539607 | ||
|
|
b11b33da5f | ||
|
|
6e9cd5a1f5 | ||
|
|
b67132671a | ||
|
|
14e34e8e7a | ||
|
|
16f5ab29fa | ||
|
|
2af717fe5e | ||
|
|
c5be6bd742 | ||
|
|
693eddb578 | ||
|
|
46e5bc2cbf | ||
|
|
f11afaaa45 | ||
|
|
cf64ce96f2 | ||
|
|
b1eae1b06a | ||
|
|
d19af3aba8 | ||
|
|
ece5341e27 | ||
|
|
7f9aaf4642 | ||
|
|
d59c3690d4 | ||
|
|
37070e482d | ||
|
|
7fd322a5c6 | ||
|
|
07dd98823f | ||
|
|
40d6019f71 | ||
|
|
32ec0038b6 | ||
|
|
99a1d505d3 | ||
|
|
62076d8170 | ||
|
|
009b1ff996 | ||
|
|
27870c7420 | ||
|
|
7a16b47e37 | ||
|
|
48324735e1 | ||
|
|
f0dcc4c34c | ||
|
|
36386c3213 | ||
|
|
8ea0f00cf0 | ||
|
|
0921796136 | ||
|
|
ab360066e5 | ||
|
|
34645c9433 | ||
|
|
b2a608824c | ||
|
|
af56920fd9 | ||
|
|
784b741b60 | ||
|
|
ea066bd828 | ||
|
|
c5d88bd5c7 | ||
|
|
a8115fdced | ||
|
|
c0406888fc | ||
|
|
c7f04f8449 | ||
|
|
ae4be86ecf | ||
|
|
d188985823 | ||
|
|
ac0d412123 | ||
|
|
19e35dbccd | ||
|
|
3086baf523 | ||
|
|
e3d8d3c31c | ||
|
|
7589b3a048 | ||
|
|
72f0d69a71 | ||
|
|
2399156ee4 | ||
|
|
3d3f08da9b | ||
|
|
2a0abf0da7 | ||
|
|
2d8146edbd | ||
|
|
dad198f5e1 | ||
|
|
0a5329fe17 | ||
|
|
64d9f09cf6 | ||
|
|
d583d12099 | ||
|
|
17bb5706f8 | ||
|
|
d583bdfd3c | ||
|
|
9eb9667163 | ||
|
|
a315a9dfc3 | ||
|
|
8375808cdc | ||
|
|
77439af486 | ||
|
|
76f4a00738 | ||
|
|
9d615b2d4c | ||
|
|
aaffc0297e | ||
|
|
5dc5885582 | ||
|
|
7389f5bf45 | ||
|
|
b06d461a36 | ||
|
|
802f57878b | ||
|
|
bccf35d057 | ||
|
|
137a1c531c | ||
|
|
e96df9f990 | ||
|
|
691670cc43 | ||
|
|
bd64bfec67 | ||
|
|
0813fb40d0 | ||
|
|
3cb8215d5a | ||
|
|
a7fe11e8db | ||
|
|
4a9fc66b5a | ||
|
|
fb81cd61a9 | ||
|
|
13034b4184 | ||
|
|
b2345da9f3 | ||
|
|
08d3d9ad30 | ||
|
|
797060f609 | ||
|
|
ea09116929 | ||
|
|
86758b498c | ||
|
|
9115b56a42 | ||
|
|
c400868026 | ||
|
|
b4dbb15853 | ||
|
|
195f70db53 | ||
|
|
f3248f446c | ||
|
|
80cb2c8a4f | ||
|
|
35a600080b | ||
|
|
af1efe7bc6 | ||
|
|
8219ef10f5 | ||
|
|
35411c3261 | ||
|
|
e8bab69ed7 | ||
|
|
9904fb2d57 | ||
|
|
d7f369350b | ||
|
|
0df322f648 | ||
|
|
cb860cec96 | ||
|
|
14c0b2dd27 | ||
|
|
a2521483c2 | ||
|
|
fa4b2a9e54 | ||
|
|
e3963a72ad | ||
|
|
cefe8d144d | ||
|
|
076cfb1257 | ||
|
|
71e3f44c8c | ||
|
|
c36f34346d | ||
|
|
cd6b83ffe8 | ||
|
|
d110fd18c1 | ||
|
|
e45b9985c2 | ||
|
|
8f4eb6df84 | ||
|
|
809bd60169 | ||
|
|
802c5949da | ||
|
|
407c5528ee | ||
|
|
fed3a2bc10 | ||
|
|
7329353c6c | ||
|
|
a7c861d1b0 | ||
|
|
9af8ab3360 | ||
|
|
ddb5328019 | ||
|
|
96d97d95e1 | ||
|
|
ab15694d9a | ||
|
|
81312e3ff4 | ||
|
|
5e1e88bdeb | ||
|
|
9882bf408c | ||
|
|
40f34f3d77 | ||
|
|
0ee1e176ed | ||
|
|
c73f89e4a1 | ||
|
|
fe9a1cdbc4 | ||
|
|
8a4eecd9fa | ||
|
|
d27a0fdbad | ||
|
|
07e44a46ae | ||
|
|
dfda47bca0 | ||
|
|
6fb36035c2 | ||
|
|
a6e3b83ebb | ||
|
|
dc3c30a2f7 | ||
|
|
3219fc764f | ||
|
|
5d5e9395b3 | ||
|
|
291b408995 | ||
|
|
ceb0333fcf | ||
|
|
44b4e5430a | ||
|
|
cb7e145e83 | ||
|
|
35ef2b0789 | ||
|
|
e34adae8f0 | ||
|
|
5137cbf14d | ||
|
|
32fb52f48f | ||
|
|
95d5d8b40c | ||
|
|
c0d57b5561 | ||
|
|
efcfbe2584 | ||
|
|
eeac1d0dd3 | ||
|
|
ac1358a0ec | ||
|
|
c31df49599 | ||
|
|
ee93412dd1 | ||
|
|
7cf2c7f5c6 | ||
|
|
5bf8842031 | ||
|
|
abe874cda3 | ||
|
|
036c04f65f | ||
|
|
418411769d | ||
|
|
12804984ba | ||
|
|
de36cf8645 | ||
|
|
922230c54e | ||
|
|
e0e548c611 | ||
|
|
5e40258f54 | ||
|
|
2f17639eeb | ||
|
|
e22be0f97c | ||
|
|
0a394265fa | ||
|
|
9c10a2c4e3 | ||
|
|
93a672b22f | ||
|
|
b301d0bdd6 | ||
|
|
1c79545245 | ||
|
|
02b323d640 | ||
|
|
4b8d30ac28 | ||
|
|
cd475580ff | ||
|
|
f937792fc2 | ||
|
|
31d4d5b6c6 | ||
|
|
d7d91d13c6 | ||
|
|
16a7b87d2a | ||
|
|
21f1e7ee49 | ||
|
|
ad8317a7a0 | ||
|
|
820be14845 | ||
|
|
b7d3b2d02c | ||
|
|
a710765336 | ||
|
|
9a68cbf533 | ||
|
|
2bb0e2e3ef | ||
|
|
17c47269a3 | ||
|
|
6c23134e47 | ||
|
|
cc015ada1b | ||
|
|
acd52e8ef6 | ||
|
|
f2ee139db6 | ||
|
|
725da285da | ||
|
|
1b3f5f5d25 | ||
|
|
ecd03ecf42 | ||
|
|
d5bffd86d6 | ||
|
|
b07298e203 | ||
|
|
4c19a29897 | ||
|
|
32f870406c | ||
|
|
05ab38b998 | ||
|
|
678545cc26 | ||
|
|
4c3e0f0c97 | ||
|
|
a8752b5f28 | ||
|
|
f75f5c1b30 | ||
|
|
735a75df92 | ||
|
|
d8414aaab9 | ||
|
|
02a5d48ce4 | ||
|
|
cdee54a0f7 | ||
|
|
b1f0f70849 | ||
|
|
850978cbe3 | ||
|
|
e8c00296bd | ||
|
|
52b8033c37 | ||
|
|
6f2bc83806 | ||
|
|
2a48c9d272 | ||
|
|
27d0373a6e | ||
|
|
96b50f7ef0 | ||
|
|
dc7ff5f668 | ||
|
|
0c9c9737d9 | ||
|
|
8ef099d707 | ||
|
|
6da15306b0 | ||
|
|
699d0869fc | ||
|
|
83efa853fc | ||
|
|
75de4b443f | ||
|
|
42c7fb446e | ||
|
|
bce6b96417 | ||
|
|
6f82cf7f15 | ||
|
|
fac419dd94 | ||
|
|
c03d547ad8 | ||
|
|
e2bf8cefa2 | ||
|
|
d75ec5c689 | ||
|
|
aa2d9eb202 | ||
|
|
ac52f38612 | ||
|
|
332808027f | ||
|
|
cd64bff5ef | ||
|
|
7813ddee38 | ||
|
|
af744235bc | ||
|
|
ca9cbc9fd1 | ||
|
|
5e7b6ed60e | ||
|
|
b9ccee398e | ||
|
|
7921734740 | ||
|
|
9d883b6ff7 | ||
|
|
75ef15bb4d | ||
|
|
479d357bd1 | ||
|
|
d429044f64 | ||
|
|
b38e8d9ea6 | ||
|
|
c928b1ca2c | ||
|
|
553a14dced | ||
|
|
8b8d727ee0 | ||
|
|
4cdeab4256 | ||
|
|
bc3aa3255e | ||
|
|
f2fdca6a03 | ||
|
|
647235616e | ||
|
|
b0b6dcc56b | ||
|
|
918a240dba | ||
|
|
830dcb45ff | ||
|
|
7be9884075 | ||
|
|
5ca0d954bb | ||
|
|
02d69261df | ||
|
|
fc60460935 | ||
|
|
559e11b16e | ||
|
|
0a72809b02 | ||
|
|
8c71ed0081 | ||
|
|
3e19a026ba | ||
|
|
8fa3c6c75d | ||
|
|
217cf56d94 | ||
|
|
6f37f28a4c | ||
|
|
3b7d2dc08e | ||
|
|
5e9af44736 | ||
|
|
be33a29fa0 | ||
|
|
29ed22d0b7 | ||
|
|
8b997528c9 | ||
|
|
04a6c8e68d | ||
|
|
0e8e4dc06d | ||
|
|
8b91c2a0b8 | ||
|
|
e967affddb | ||
|
|
dea7434bd4 | ||
|
|
1da63e450d | ||
|
|
029d240999 | ||
|
|
110f7069e1 | ||
|
|
d77cde1929 |
24
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
.DS_Store
|
||||
|
||||
# IntelliJ
|
||||
/MasterPassword/Java/.idea
|
||||
/.idea/*
|
||||
!/.idea/encodings.xml
|
||||
!/.idea/inspectionProfiles
|
||||
@@ -12,14 +13,23 @@
|
||||
/*.iws
|
||||
|
||||
# Xcode IDE
|
||||
/*.xcodeproj/*
|
||||
!/*.xcodeproj/*.pbxproj
|
||||
!/*.xcodeproj/xcshareddata
|
||||
!/*.xcodeproj/xcshareddata/*
|
||||
!/*.xcodeproj/project.xcworkspace
|
||||
!/*.xcodeproj/project.xcworkspace/*
|
||||
/*.xcodeproj/project.xcworkspace/xcuserdata
|
||||
xcuserdata/
|
||||
/DerivedData/
|
||||
|
||||
# Media
|
||||
Press/Background.png
|
||||
Press/Front-Page.png
|
||||
Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
|
||||
|
||||
# IPA
|
||||
/sendipa/*
|
||||
!/sendipa/sendipa.conf
|
||||
|
||||
# Java
|
||||
MasterPassword/Java/**/target
|
||||
|
||||
# C
|
||||
MasterPassword/C/*.o
|
||||
MasterPassword/C/mpw
|
||||
MasterPassword/C/lib/*/*
|
||||
!MasterPassword/C/lib/*/.source
|
||||
|
||||
14
.gitmodules
vendored
@@ -3,10 +3,10 @@
|
||||
url = git://github.com/Lyndir/Pearl.git
|
||||
[submodule "External/InAppSettingsKit"]
|
||||
path = External/InAppSettingsKit
|
||||
url = git://github.com/futuretap/InAppSettingsKit.git
|
||||
[submodule "External/iCloudStoreManager"]
|
||||
path = External/iCloudStoreManager
|
||||
url = git://github.com/lhunath/iCloudStoreManager.git
|
||||
[submodule "External/FontReplacer"]
|
||||
path = External/FontReplacer
|
||||
url = git://github.com/0xced/FontReplacer.git
|
||||
url = git://github.com/lhunath/InAppSettingsKit.git
|
||||
[submodule "External/UbiquityStoreManager"]
|
||||
path = External/UbiquityStoreManager
|
||||
url = git://github.com/lhunath/UbiquityStoreManager.git
|
||||
[submodule "External/RHStatusItemView"]
|
||||
path = External/RHStatusItemView
|
||||
url = git://github.com/lhunath/RHStatusItemView.git
|
||||
|
||||
8
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -2,8 +2,16 @@
|
||||
<profile version="1.0" is_locked="false">
|
||||
<option name="myName" value="Project Default" />
|
||||
<option name="myLocal" value="false" />
|
||||
<inspection_tool class="FunctionImplicitDeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="LossyEncoding" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MethodIsLaterInTheScope" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="OCNotLocalizedStringInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="OCUnusedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SignednessMismatch" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="UnavailableInDeploymentTarget" enabled="true" level="INFO" enabled_by_default="true" />
|
||||
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="UnusedParameter" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
||||
7
.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
language: objective-c
|
||||
xcode_workspace: MasterPassword.xcworkspace
|
||||
xcode_scheme: MasterPassword iOS (Development)
|
||||
xcode_sdk: iphonesimulator
|
||||
git:
|
||||
submodules: false
|
||||
before_install: ./Scripts/updateDependencies
|
||||
@@ -1 +0,0 @@
|
||||
../../../run
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
#
|
||||
# WARNING: DO NOT MODIFY THIS FILE.
|
||||
#
|
||||
# Crashlytics
|
||||
# Crashlytics Version: 1.0.0.1
|
||||
#
|
||||
# Copyright Crashlytics, Inc. 2012. All rights reserved.
|
||||
#
|
||||
|
||||
require 'pathname'
|
||||
|
||||
path = Pathname.new(__FILE__).parent
|
||||
`#{path}/../../../run`
|
||||
BIN
Crashlytics/Crashlytics.framework/run
vendored
1
External/FontReplacer
vendored
2
External/InAppSettingsKit
vendored
BIN
External/Mac/Crashlytics.framework/Versions/A/Crashlytics
vendored
Normal file
@@ -2,17 +2,17 @@
|
||||
// Crashlytics.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright 2012 Crashlytics, Inc. All rights reserved.
|
||||
// Copyright 2013 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
*
|
||||
* The CLS_LOG macro provides as easy way to gather more information in your log messages that are
|
||||
* sent with your crash data. CLS_LOG prepends your custom log message with the function name and
|
||||
* line number where the macro was used. If your app was built with the DEBUG preprocessor macro
|
||||
* defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
|
||||
* The CLS_LOG macro provides as easy way to gather more information in your log messages that are
|
||||
* sent with your crash data. CLS_LOG prepends your custom log message with the function name and
|
||||
* line number where the macro was used. If your app was built with the DEBUG preprocessor macro
|
||||
* defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
|
||||
* If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only.
|
||||
*
|
||||
* Example output:
|
||||
@@ -26,41 +26,30 @@
|
||||
*
|
||||
**/
|
||||
#ifdef DEBUG
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will not show up in the system.log
|
||||
* and will only be visible in your Crashlytics dashboard.
|
||||
*
|
||||
**/
|
||||
void CLSLog(NSString *format, ...);
|
||||
OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
|
||||
/**
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will show up in the system.log
|
||||
* and your Crashlytics dashboard. It is not reccomended for Release builds.
|
||||
* and your Crashlytics dashboard. It is not recommended for Release builds.
|
||||
*
|
||||
**/
|
||||
void CLSNSLog(NSString *format, ...);
|
||||
OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
|
||||
@protocol CrashlyticsDelegate;
|
||||
|
||||
@interface Crashlytics : NSObject {
|
||||
@private
|
||||
NSString *_apiKey;
|
||||
NSString *_dataDirectory;
|
||||
NSString *_bundleIdentifier;
|
||||
BOOL _installed;
|
||||
NSMutableDictionary *_customAttributes;
|
||||
id _user;
|
||||
NSInteger _sendButtonIndex;
|
||||
NSInteger _alwaysSendButtonIndex;
|
||||
NSObject <CrashlyticsDelegate> *_delegate;
|
||||
}
|
||||
@interface Crashlytics : NSObject
|
||||
|
||||
@property (nonatomic, readonly, copy) NSString *apiKey;
|
||||
@property (nonatomic, readonly, copy) NSString *version;
|
||||
@@ -73,7 +62,7 @@ void CLSNSLog(NSString *format, ...);
|
||||
* The recommended way to install Crashlytics into your application is to place a call
|
||||
* to +startWithAPIKey: in your -application:didFinishLaunchingWithOptions: method.
|
||||
*
|
||||
* This delay defaults to 1 second in order to generally give the application time to
|
||||
* This delay defaults to 1 second in order to generally give the application time to
|
||||
* fully finish launching.
|
||||
*
|
||||
**/
|
||||
@@ -105,21 +94,21 @@ void CLSNSLog(NSString *format, ...);
|
||||
|
||||
/**
|
||||
*
|
||||
* Many of our customers have requested the ability to tie crashes to specific end-users of their
|
||||
* application in order to facilitate responses to support requests or permit the ability to reach
|
||||
* out for more information. We allow you to specify up to three separate values for display within
|
||||
* Many of our customers have requested the ability to tie crashes to specific end-users of their
|
||||
* application in order to facilitate responses to support requests or permit the ability to reach
|
||||
* out for more information. We allow you to specify up to three separate values for display within
|
||||
* the Crashlytics UI - but please be mindful of your end-user's privacy.
|
||||
*
|
||||
* We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record
|
||||
* in your system. This could be a database id, hash, or other value that is meaningless to a
|
||||
* We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record
|
||||
* in your system. This could be a database id, hash, or other value that is meaningless to a
|
||||
* third-party observer but can be indexed and queried by you.
|
||||
*
|
||||
* Optionally, you may also specify the end-user's name or username, as well as email address if you
|
||||
* Optionally, you may also specify the end-user's name or username, as well as email address if you
|
||||
* do not have a system that works well with obscured identifiers.
|
||||
*
|
||||
* Pursuant to our EULA, this data is transferred securely throughout our system and we will not
|
||||
* disseminate end-user data unless required to by law. That said, if you choose to provide end-user
|
||||
* contact information, we strongly recommend that you disclose this in your application's privacy
|
||||
* Pursuant to our EULA, this data is transferred securely throughout our system and we will not
|
||||
* disseminate end-user data unless required to by law. That said, if you choose to provide end-user
|
||||
* contact information, we strongly recommend that you disclose this in your application's privacy
|
||||
* policy. Data privacy is of our utmost concern.
|
||||
*
|
||||
**/
|
||||
@@ -148,10 +137,55 @@ void CLSNSLog(NSString *format, ...);
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The CLSCrashReport protocol exposes methods that you can call on crash report objects passed
|
||||
* to delegate methods. If you want these values or the entire object to stay in memory retain
|
||||
* them or copy them.
|
||||
**/
|
||||
@protocol CLSCrashReport <NSObject>
|
||||
@required
|
||||
|
||||
/**
|
||||
* Returns the session identifier for the crash report.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *identifier;
|
||||
|
||||
/**
|
||||
* Returns the custom key value data for the crash report.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSDictionary *customKeys;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleVersion of the application that crashed.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *bundleVersion;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleShortVersionString of the application that crashed.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *bundleShortVersionString;
|
||||
|
||||
/**
|
||||
* Returns the date that the application crashed at.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSDate *crashedOnDate;
|
||||
|
||||
/**
|
||||
* Returns the os version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *OSVersion;
|
||||
|
||||
/**
|
||||
* Returns the os build version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *OSBuildVersion;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
*
|
||||
* The CrashlyticsDelegate protocol provides a mechanism for your application to take
|
||||
* action on events that occur in the Crashlytics crash reporting system. You can make
|
||||
* action on events that occur in the Crashlytics crash reporting system. You can make
|
||||
* use of these calls by assigning an object to the Crashlytics' delegate property directly,
|
||||
* or through the convenience startWithAPIKey:delegate:... methods.
|
||||
*
|
||||
@@ -161,7 +195,7 @@ void CLSNSLog(NSString *format, ...);
|
||||
|
||||
/**
|
||||
*
|
||||
* Called once a Crashlytics instance has determined that the last execution of the
|
||||
* Called once a Crashlytics instance has determined that the last execution of the
|
||||
* application ended in a crash. This is called some time after the crash reporting
|
||||
* process has begun. If you have specified a delay in one of the
|
||||
* startWithAPIKey:... calls, this will take at least that long to be invoked.
|
||||
@@ -169,4 +203,15 @@ void CLSNSLog(NSString *format, ...);
|
||||
**/
|
||||
- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics;
|
||||
|
||||
/**
|
||||
*
|
||||
* Just like crashlyticsDidDetectCrashDuringPreviousExecution this delegate method is
|
||||
* called once a Crashlytics instance has determined that the last execution of the
|
||||
* application ended in a crash. A CLSCrashReport is passed back that contains data about
|
||||
* the last crash report that was generated. See the CLSCrashReport protocol for method details.
|
||||
* This method is called after crashlyticsDidDetectCrashDuringPreviousExecution.
|
||||
*
|
||||
**/
|
||||
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
|
||||
|
||||
@end
|
||||
30
External/Mac/Crashlytics.framework/Versions/A/Resources/Info.plist
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Crashlytics</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.crashlytics.sdk.mac</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Crashlytics</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.1.2</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>macosx</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>9</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>10.6</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
External/Mac/Crashlytics.framework/run
vendored
Executable file
2
External/Pearl
vendored
1
External/RHStatusItemView
vendored
Submodule
1
External/UbiquityStoreManager
vendored
Submodule
1
External/iCloudStoreManager
vendored
1
External/iOS/Crashlytics.framework/Crashlytics
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Crashlytics
|
||||
1
External/iOS/Crashlytics.framework/Headers
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Headers
|
||||
6
External/iOS/Crashlytics.framework/Modules/module.modulemap
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
framework module Crashlytics {
|
||||
umbrella header "Crashlytics.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
1
External/iOS/Crashlytics.framework/Resources
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Resources
|
||||
BIN
External/iOS/Crashlytics.framework/Versions/A/Crashlytics
vendored
Normal file
217
External/iOS/Crashlytics.framework/Versions/A/Headers/Crashlytics.h
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
//
|
||||
// Crashlytics.h
|
||||
// Crashlytics
|
||||
//
|
||||
// Copyright 2013 Crashlytics, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
*
|
||||
* The CLS_LOG macro provides as easy way to gather more information in your log messages that are
|
||||
* sent with your crash data. CLS_LOG prepends your custom log message with the function name and
|
||||
* line number where the macro was used. If your app was built with the DEBUG preprocessor macro
|
||||
* defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
|
||||
* If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only.
|
||||
*
|
||||
* Example output:
|
||||
* -[AppDelegate login:] line 134 $ login start
|
||||
*
|
||||
* If you would like to change this macro, create a new header file, unset our define and then define
|
||||
* your own version. Make sure this new header file is imported after the Crashlytics header file.
|
||||
*
|
||||
* #undef CLS_LOG
|
||||
* #define CLS_LOG(__FORMAT__, ...) CLSNSLog...
|
||||
*
|
||||
**/
|
||||
#ifdef DEBUG
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will not show up in the system.log
|
||||
* and will only be visible in your Crashlytics dashboard.
|
||||
*
|
||||
**/
|
||||
OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
|
||||
/**
|
||||
*
|
||||
* Add logging that will be sent with your crash data. This logging will show up in the system.log
|
||||
* and your Crashlytics dashboard. It is not recommended for Release builds.
|
||||
*
|
||||
**/
|
||||
OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||
|
||||
@protocol CrashlyticsDelegate;
|
||||
|
||||
@interface Crashlytics : NSObject
|
||||
|
||||
@property (nonatomic, readonly, copy) NSString *apiKey;
|
||||
@property (nonatomic, readonly, copy) NSString *version;
|
||||
@property (nonatomic, assign) BOOL debugMode;
|
||||
|
||||
@property (nonatomic, assign) NSObject <CrashlyticsDelegate> *delegate;
|
||||
|
||||
/**
|
||||
*
|
||||
* The recommended way to install Crashlytics into your application is to place a call
|
||||
* to +startWithAPIKey: in your -application:didFinishLaunchingWithOptions: method.
|
||||
*
|
||||
* This delay defaults to 1 second in order to generally give the application time to
|
||||
* fully finish launching.
|
||||
*
|
||||
**/
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey;
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay;
|
||||
|
||||
/**
|
||||
*
|
||||
* If you need the functionality provided by the CrashlyticsDelegate protocol, you can use
|
||||
* these convenience methods to activate the framework and set the delegate in one call.
|
||||
*
|
||||
**/
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(NSObject <CrashlyticsDelegate> *)delegate;
|
||||
+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(NSObject <CrashlyticsDelegate> *)delegate afterDelay:(NSTimeInterval)delay;
|
||||
|
||||
/**
|
||||
*
|
||||
* Access the singleton Crashlytics instance.
|
||||
*
|
||||
**/
|
||||
+ (Crashlytics *)sharedInstance;
|
||||
|
||||
/**
|
||||
*
|
||||
* The easiest way to cause a crash - great for testing!
|
||||
*
|
||||
**/
|
||||
- (void)crash;
|
||||
|
||||
/**
|
||||
*
|
||||
* Many of our customers have requested the ability to tie crashes to specific end-users of their
|
||||
* application in order to facilitate responses to support requests or permit the ability to reach
|
||||
* out for more information. We allow you to specify up to three separate values for display within
|
||||
* the Crashlytics UI - but please be mindful of your end-user's privacy.
|
||||
*
|
||||
* We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record
|
||||
* in your system. This could be a database id, hash, or other value that is meaningless to a
|
||||
* third-party observer but can be indexed and queried by you.
|
||||
*
|
||||
* Optionally, you may also specify the end-user's name or username, as well as email address if you
|
||||
* do not have a system that works well with obscured identifiers.
|
||||
*
|
||||
* Pursuant to our EULA, this data is transferred securely throughout our system and we will not
|
||||
* disseminate end-user data unless required to by law. That said, if you choose to provide end-user
|
||||
* contact information, we strongly recommend that you disclose this in your application's privacy
|
||||
* policy. Data privacy is of our utmost concern.
|
||||
*
|
||||
**/
|
||||
- (void)setUserIdentifier:(NSString *)identifier;
|
||||
- (void)setUserName:(NSString *)name;
|
||||
- (void)setUserEmail:(NSString *)email;
|
||||
|
||||
+ (void)setUserIdentifier:(NSString *)identifier;
|
||||
+ (void)setUserName:(NSString *)name;
|
||||
+ (void)setUserEmail:(NSString *)email;
|
||||
|
||||
/**
|
||||
*
|
||||
* Set a value for a key to be associated with your crash data.
|
||||
*
|
||||
**/
|
||||
- (void)setObjectValue:(id)value forKey:(NSString *)key;
|
||||
- (void)setIntValue:(int)value forKey:(NSString *)key;
|
||||
- (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
|
||||
- (void)setFloatValue:(float)value forKey:(NSString *)key;
|
||||
|
||||
+ (void)setObjectValue:(id)value forKey:(NSString *)key;
|
||||
+ (void)setIntValue:(int)value forKey:(NSString *)key;
|
||||
+ (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
|
||||
+ (void)setFloatValue:(float)value forKey:(NSString *)key;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* The CLSCrashReport protocol exposes methods that you can call on crash report objects passed
|
||||
* to delegate methods. If you want these values or the entire object to stay in memory retain
|
||||
* them or copy them.
|
||||
**/
|
||||
@protocol CLSCrashReport <NSObject>
|
||||
@required
|
||||
|
||||
/**
|
||||
* Returns the session identifier for the crash report.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *identifier;
|
||||
|
||||
/**
|
||||
* Returns the custom key value data for the crash report.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSDictionary *customKeys;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleVersion of the application that crashed.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *bundleVersion;
|
||||
|
||||
/**
|
||||
* Returns the CFBundleShortVersionString of the application that crashed.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *bundleShortVersionString;
|
||||
|
||||
/**
|
||||
* Returns the date that the application crashed at.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSDate *crashedOnDate;
|
||||
|
||||
/**
|
||||
* Returns the os version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *OSVersion;
|
||||
|
||||
/**
|
||||
* Returns the os build version that the application crashed on.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *OSBuildVersion;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
*
|
||||
* The CrashlyticsDelegate protocol provides a mechanism for your application to take
|
||||
* action on events that occur in the Crashlytics crash reporting system. You can make
|
||||
* use of these calls by assigning an object to the Crashlytics' delegate property directly,
|
||||
* or through the convenience startWithAPIKey:delegate:... methods.
|
||||
*
|
||||
**/
|
||||
@protocol CrashlyticsDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
*
|
||||
* Called once a Crashlytics instance has determined that the last execution of the
|
||||
* application ended in a crash. This is called some time after the crash reporting
|
||||
* process has begun. If you have specified a delay in one of the
|
||||
* startWithAPIKey:... calls, this will take at least that long to be invoked.
|
||||
*
|
||||
**/
|
||||
- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics;
|
||||
|
||||
/**
|
||||
*
|
||||
* Just like crashlyticsDidDetectCrashDuringPreviousExecution this delegate method is
|
||||
* called once a Crashlytics instance has determined that the last execution of the
|
||||
* application ended in a crash. A CLSCrashReport is passed back that contains data about
|
||||
* the last crash report that was generated. See the CLSCrashReport protocol for method details.
|
||||
* This method is called after crashlyticsDidDetectCrashDuringPreviousExecution.
|
||||
*
|
||||
**/
|
||||
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
|
||||
|
||||
@end
|
||||
@@ -2,8 +2,6 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>11C74</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
@@ -17,38 +15,16 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.1.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<string>2.2.4</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>iPhoneOS</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0101.02.00</string>
|
||||
<key>CrashlyticsAPIKey</key>
|
||||
<string>0d10c90776f5ef5acd01ddbeaca9a6cba4814560</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>8H7</string>
|
||||
<string>38</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>iphoneos</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>4.3</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>8H7</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>iphoneos4.3</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0410</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>4B110</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>3.1</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
</array>
|
||||
<string>4.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
1
External/iOS/Crashlytics.framework/Versions/Current
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
A
|
||||
BIN
External/iOS/Crashlytics.framework/run
vendored
Executable file
BIN
External/iOS/Crashlytics.framework/submit
vendored
Executable file
1
External/iOS/Reveal.framework/Headers
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Headers
|
||||
1
External/iOS/Reveal.framework/Reveal
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Reveal
|
||||
17
External/iOS/Reveal.framework/Versions/A/Headers/IBARevealLoader.h
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Copyright (c) 2013 Itty Bitty Apps. All rights reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
extern NSString * const IBARevealLoaderRequestStartNotification;
|
||||
extern NSString * const IBARevealLoaderRequestStopNotification;
|
||||
|
||||
extern NSString * const IBARevealLoaderSetOptionsNotification;
|
||||
extern NSString * const IBARevealLoaderOptionsLogLevelMaskKey;
|
||||
|
||||
@interface IBARevealLoader : NSObject
|
||||
|
||||
+ (void)startServer;
|
||||
+ (void)stopServer;
|
||||
|
||||
@end
|
||||
57
External/iOS/Reveal.framework/Versions/A/Headers/IBARevealLogger.h
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2013 Itty Bitty Apps Pty Ltd. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
CF_EXTERN_C_BEGIN
|
||||
|
||||
/*!
|
||||
\brief The Reveal Log level bit flags.
|
||||
\discussion These flags are additive. i.e. you should bitwise OR them together.
|
||||
|
||||
\seealso IBARevealLoggerSetLevelMask
|
||||
\seealso IBARevealLoggerGetLevelMask
|
||||
|
||||
Example:
|
||||
|
||||
// Enable Error, Warning and Info logger levels.
|
||||
IBARevealLoggerSetLevelMask(IBARevealLogLevelError|IBARevealLogLevelWarn|IBARevealLogLevelInfo);
|
||||
|
||||
*/
|
||||
typedef NS_OPTIONS(int32_t, IBARevealLogLevel)
|
||||
{
|
||||
IBARevealLogLevelNone = 0,
|
||||
IBARevealLogLevelDebug = (1 << 0),
|
||||
IBARevealLogLevelInfo = (1 << 1),
|
||||
IBARevealLogLevelWarn = (1 << 2),
|
||||
IBARevealLogLevelError = (1 << 3)
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief Set the Reveal logger level mask.
|
||||
\param mask A bit mask which is a combination of the IBARevealLogLevel enum options.
|
||||
|
||||
\discussion If you do not wish to see log messages from Reveal you should call this function with an appropriate level mask as early in your application's lifecycle as possible. For example in your application's main() function.
|
||||
|
||||
Example:
|
||||
|
||||
// Enable Error, Warning and Info logger levels.
|
||||
IBARevealLoggerSetLevelMask(IBARevealLogLevelError|IBARevealLogLevelWarn|IBARevealLogLevelInfo);
|
||||
|
||||
*/
|
||||
CF_EXPORT void IBARevealLoggerSetLevelMask(int32_t mask);
|
||||
|
||||
/*!
|
||||
\brief Get the current Reveal logger level mask.
|
||||
\return A bit mask representing the levels at which Reveal is currently logging.
|
||||
\discussion The default Reveal Logger level mask is IBARevealLogLevelError|IBARevealLogLevelWarn|IBARevealLogLevelInfo.
|
||||
|
||||
Example:
|
||||
|
||||
// Turn off the Info log level.
|
||||
IBARevealLoggerSetLevelMask(IBARevealLoggerGetLevelMask() & ~IBARevealLogLevelInfo);
|
||||
|
||||
*/
|
||||
CF_EXPORT int32_t IBARevealLoggerGetLevelMask(void);
|
||||
|
||||
CF_EXTERN_C_END
|
||||
5
External/iOS/Reveal.framework/Versions/A/Headers/Reveal.h
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright (c) 2013 Itty Bitty Apps Pty Ltd. All rights reserved.
|
||||
//
|
||||
|
||||
#import "IBARevealLogger.h"
|
||||
#import "IBARevealLoader.h"
|
||||
BIN
External/iOS/Reveal.framework/Versions/A/Reveal
vendored
Normal file
1
External/iOS/Reveal.framework/Versions/Current
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
A
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Key.development</key>
|
||||
<string>e6238ceba8ec92832e77b1b-9ccd60bc-c39b-11e0-06e4-007f58cb3154</string>
|
||||
<key>Key.distribution</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// LocalyticsDatabase.h
|
||||
// LocalyticsDemo
|
||||
//
|
||||
// Created by jkaufman on 5/26/11.
|
||||
// Copyright 2011 Localytics. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <sqlite3.h>
|
||||
|
||||
#define MAX_DATABASE_SIZE 500000 // The maximum allowed disk size of the primary database file at open, in bytes
|
||||
#define VACUUM_THRESHOLD 0.8 // The database is vacuumed after its size exceeds this proportion of the maximum.
|
||||
|
||||
@interface LocalyticsDatabase : NSObject {
|
||||
sqlite3 *_databaseConnection;
|
||||
}
|
||||
|
||||
+ (LocalyticsDatabase *)sharedLocalyticsDatabase;
|
||||
|
||||
- (NSUInteger)databaseSize;
|
||||
- (int)eventCount;
|
||||
- (NSTimeInterval)createdTimestamp;
|
||||
|
||||
- (BOOL)beginTransaction:(NSString *)name;
|
||||
- (BOOL)releaseTransaction:(NSString *)name;
|
||||
- (BOOL)rollbackTransaction:(NSString *)name;
|
||||
|
||||
- (BOOL)incrementLastUploadNumber:(int *)uploadNumber;
|
||||
- (BOOL)incrementLastSessionNumber:(int *)sessionNumber;
|
||||
|
||||
- (BOOL)addEventWithBlobString:(NSString *)blob;
|
||||
- (BOOL)addCloseEventWithBlobString:(NSString *)blob;
|
||||
- (BOOL)addFlowEventWithBlobString:(NSString *)blob;
|
||||
- (BOOL)removeLastCloseAndFlowEvents;
|
||||
|
||||
- (BOOL)addHeaderWithSequenceNumber:(int)number blobString:(NSString *)blob rowId:(sqlite3_int64 *)insertedRowId;
|
||||
- (int)unstagedEventCount;
|
||||
- (BOOL)stageEventsForUpload:(sqlite3_int64)headerId;
|
||||
- (BOOL)updateAppKey:(NSString *)appKey;
|
||||
- (NSString *)uploadBlobString;
|
||||
- (BOOL)deleteUploadedData;
|
||||
- (BOOL)resetAnalyticsData;
|
||||
- (BOOL)vacuumIfRequired;
|
||||
|
||||
- (NSTimeInterval)lastSessionStartTimestamp;
|
||||
- (BOOL)setLastsessionStartTimestamp:(NSTimeInterval)timestamp;
|
||||
|
||||
- (BOOL)isOptedOut;
|
||||
- (BOOL)setOptedOut:(BOOL)optOut;
|
||||
- (NSString *)installId;
|
||||
- (NSString *)appKey; // Most recent app key-- may not be that used to open the session.
|
||||
|
||||
- (NSString *)customDimension:(int)dimension;
|
||||
- (BOOL)setCustomDimension:(int)dimension value:(NSString *)value;
|
||||
|
||||
@end
|
||||
@@ -1,743 +0,0 @@
|
||||
//
|
||||
// LocalyticsDatabase.m
|
||||
// LocalyticsDemo
|
||||
//
|
||||
// Created by jkaufman on 5/26/11.
|
||||
// Copyright 2011 Localytics. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LocalyticsDatabase.h"
|
||||
|
||||
#define LOCALYTICS_DIR @".localytics" // Name for the directory in which Localytics database is stored
|
||||
#define LOCALYTICS_DB @"localytics" // File name for the database (without extension)
|
||||
#define BUSY_TIMEOUT 30 // Maximum time SQlite will busy-wait for the database to unlock before returning SQLITE_BUSY
|
||||
|
||||
@interface LocalyticsDatabase ()
|
||||
- (int)schemaVersion;
|
||||
- (void)createSchema;
|
||||
- (void)upgradeToSchemaV2;
|
||||
- (void)upgradeToSchemaV3;
|
||||
- (void)moveDbToCaches;
|
||||
- (NSString *)randomUUID;
|
||||
@end
|
||||
|
||||
@implementation LocalyticsDatabase
|
||||
|
||||
// The singleton database object.
|
||||
static LocalyticsDatabase *_sharedLocalyticsDatabase = nil;
|
||||
|
||||
+ (NSString *)localyticsDirectoryPath {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
return [[paths objectAtIndex:0] stringByAppendingPathComponent:LOCALYTICS_DIR];
|
||||
}
|
||||
|
||||
+ (NSString *)localyticsDatabasePath {
|
||||
NSString *path = [[LocalyticsDatabase localyticsDirectoryPath] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.sqlite", LOCALYTICS_DB]];
|
||||
return path;
|
||||
}
|
||||
|
||||
#pragma mark Singleton Class
|
||||
+ (LocalyticsDatabase *)sharedLocalyticsDatabase {
|
||||
@synchronized(self) {
|
||||
if (_sharedLocalyticsDatabase == nil) {
|
||||
_sharedLocalyticsDatabase = [[self alloc] init];
|
||||
}
|
||||
}
|
||||
return _sharedLocalyticsDatabase;
|
||||
}
|
||||
|
||||
- (LocalyticsDatabase *)init {
|
||||
if((self = [super init])) {
|
||||
|
||||
// Mover any data that a previous library may have left in the documents directory
|
||||
[self moveDbToCaches];
|
||||
|
||||
// Create directory structure for Localytics.
|
||||
NSString *directoryPath = [LocalyticsDatabase localyticsDirectoryPath];
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath]) {
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
// Attempt to open database. It will be created if it does not exist, already.
|
||||
NSString *dbPath = [LocalyticsDatabase localyticsDatabasePath];
|
||||
int code = sqlite3_open([dbPath UTF8String], &_databaseConnection);
|
||||
|
||||
// If we were unable to open the database, it is likely corrupted. Clobber it and move on.
|
||||
if (code != SQLITE_OK) {
|
||||
[[NSFileManager defaultManager] removeItemAtPath:dbPath error:nil];
|
||||
code = sqlite3_open([dbPath UTF8String], &_databaseConnection);
|
||||
}
|
||||
|
||||
// Check db connection, creating schema if necessary.
|
||||
if (code == SQLITE_OK) {
|
||||
sqlite3_busy_timeout(_databaseConnection, BUSY_TIMEOUT); // Defaults to 0, otherwise.
|
||||
if ([self schemaVersion] == 0) {
|
||||
[self createSchema];
|
||||
}
|
||||
}
|
||||
|
||||
// Perform any Migrations if necessary
|
||||
if ([self schemaVersion] < 2) {
|
||||
[self upgradeToSchemaV2];
|
||||
}
|
||||
if ([self schemaVersion] < 3) {
|
||||
[self upgradeToSchemaV3];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Database
|
||||
|
||||
- (BOOL)beginTransaction:(NSString *)name {
|
||||
const char *sql = [[NSString stringWithFormat:@"SAVEPOINT %@", name] cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
int code = sqlite3_exec(_databaseConnection, sql, NULL, NULL, NULL);
|
||||
return code == SQLITE_OK;
|
||||
}
|
||||
|
||||
- (BOOL)releaseTransaction:(NSString *)name {
|
||||
const char *sql = [[NSString stringWithFormat:@"RELEASE SAVEPOINT %@", name] cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
int code = sqlite3_exec(_databaseConnection, sql, NULL, NULL, NULL);
|
||||
return code == SQLITE_OK;
|
||||
}
|
||||
|
||||
- (BOOL)rollbackTransaction:(NSString *)name {
|
||||
const char *sql = [[NSString stringWithFormat:@"ROLLBACK SAVEPOINT %@", name] cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
int code = sqlite3_exec(_databaseConnection, sql, NULL, NULL, NULL);
|
||||
return code == SQLITE_OK;
|
||||
}
|
||||
|
||||
- (int)schemaVersion {
|
||||
int version = 0;
|
||||
const char *sql = "SELECT MAX(schema_version) FROM localytics_info";
|
||||
sqlite3_stmt *selectSchemaVersion;
|
||||
if(sqlite3_prepare_v2(_databaseConnection, sql, -1, &selectSchemaVersion, NULL) == SQLITE_OK) {
|
||||
if(sqlite3_step(selectSchemaVersion) == SQLITE_ROW) {
|
||||
version = sqlite3_column_int(selectSchemaVersion, 0);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(selectSchemaVersion);
|
||||
return version;
|
||||
}
|
||||
|
||||
- (NSString *)installId {
|
||||
NSString *installId = nil;
|
||||
|
||||
sqlite3_stmt *selectInstallId;
|
||||
sqlite3_prepare_v2(_databaseConnection, "SELECT install_id FROM localytics_info", -1, &selectInstallId, NULL);
|
||||
int code = sqlite3_step(selectInstallId);
|
||||
if (code == SQLITE_ROW && sqlite3_column_text(selectInstallId, 0)) {
|
||||
installId = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectInstallId, 0)];
|
||||
}
|
||||
sqlite3_finalize(selectInstallId);
|
||||
|
||||
return installId;
|
||||
}
|
||||
|
||||
- (NSString *)appKey {
|
||||
NSString *appKey = nil;
|
||||
|
||||
sqlite3_stmt *selectAppKey;
|
||||
sqlite3_prepare_v2(_databaseConnection, "SELECT app_key FROM localytics_info", -1, &selectAppKey, NULL);
|
||||
int code = sqlite3_step(selectAppKey);
|
||||
if (code == SQLITE_ROW && sqlite3_column_text(selectAppKey, 0)) {
|
||||
appKey = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectAppKey, 0)];
|
||||
}
|
||||
sqlite3_finalize(selectAppKey);
|
||||
|
||||
return appKey;
|
||||
}
|
||||
|
||||
// Due to the new iOS storage guidelines it is necessary to move the database out of the documents directory
|
||||
// and into the /library/caches directory
|
||||
- (void)moveDbToCaches {
|
||||
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *localyticsDocumentsDirectory = [[documentPaths objectAtIndex:0] stringByAppendingPathComponent:LOCALYTICS_DIR];
|
||||
NSArray *cachesPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
NSString *localyticsCachesDirectory = [[cachesPaths objectAtIndex:0] stringByAppendingPathComponent:LOCALYTICS_DIR];
|
||||
|
||||
// If the old directory doesn't exist, there is nothing else to do here
|
||||
if([[NSFileManager defaultManager] fileExistsAtPath:localyticsDocumentsDirectory] == NO)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to move the directory
|
||||
if(NO == [[NSFileManager defaultManager] moveItemAtPath:localyticsDocumentsDirectory
|
||||
toPath:localyticsCachesDirectory
|
||||
error:nil])
|
||||
{
|
||||
// If the move failed try and, delete the old directory
|
||||
[ [NSFileManager defaultManager] removeItemAtPath:localyticsDocumentsDirectory error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createSchema {
|
||||
int code = SQLITE_OK;
|
||||
|
||||
// Execute schema creation within a single transaction.
|
||||
code = sqlite3_exec(_databaseConnection, "BEGIN", NULL, NULL, NULL);
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"CREATE TABLE upload_headers ("
|
||||
"sequence_number INTEGER PRIMARY KEY, "
|
||||
"blob_string TEXT)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"CREATE TABLE events ("
|
||||
"event_id INTEGER PRIMARY KEY AUTOINCREMENT, " // In case foreign key constraints are reintroduced.
|
||||
"upload_header INTEGER, "
|
||||
"blob_string TEXT NOT NULL)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"CREATE TABLE localytics_info ("
|
||||
"schema_version INTEGER PRIMARY KEY, "
|
||||
"last_upload_number INTEGER, "
|
||||
"last_session_number INTEGER, "
|
||||
"opt_out BOOLEAN, "
|
||||
"last_close_event INTEGER, "
|
||||
"last_flow_event INTEGER, "
|
||||
"last_session_start REAL, "
|
||||
"app_key CHAR(64), "
|
||||
"custom_d0 CHAR(64), "
|
||||
"custom_d1 CHAR(64), "
|
||||
"custom_d2 CHAR(64), "
|
||||
"custom_d3 CHAR(64) "
|
||||
")",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"INSERT INTO localytics_info (schema_version, last_upload_number, last_session_number, opt_out) "
|
||||
"VALUES (3, 0, 0, 0)", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
// Commit transaction.
|
||||
if (code == SQLITE_OK || code == SQLITE_DONE) {
|
||||
sqlite3_exec(_databaseConnection, "COMMIT", NULL, NULL, NULL);
|
||||
} else {
|
||||
sqlite3_exec(_databaseConnection, "ROLLBACK", NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// V2 adds a unique identifier for each installation
|
||||
// This identifier has been moved to user preferences so the database an live in the caches directory
|
||||
// Also adds storage for custom dimensions
|
||||
- (void)upgradeToSchemaV2 {
|
||||
int code = SQLITE_OK;
|
||||
|
||||
code = sqlite3_exec(_databaseConnection, "BEGIN", NULL, NULL, NULL);
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"ALTER TABLE localytics_info ADD install_id CHAR(40)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"ALTER TABLE localytics_info ADD custom_d0 CHAR(64)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"ALTER TABLE localytics_info ADD custom_d1 CHAR(64)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"ALTER TABLE localytics_info ADD custom_d2 CHAR(64)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
sqlite3_exec(_databaseConnection,
|
||||
"ALTER TABLE localytics_info ADD custom_d3 CHAR(64)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
// Attempt to set schema version and install_id regardless of the result code following the ALTER statements above.
|
||||
// This is necessary because a previous version of the library performed the migration without setting these values.
|
||||
// The transaction will succeed even if the individual statements fail with errors (eg. "duplicate column name").
|
||||
sqlite3_stmt *updateLocalyticsInfo;
|
||||
sqlite3_prepare_v2(_databaseConnection, "UPDATE localytics_info set install_id = ?, schema_version = 2 ", -1, &updateLocalyticsInfo, NULL);
|
||||
sqlite3_bind_text (updateLocalyticsInfo, 1, [[self randomUUID] UTF8String], -1, SQLITE_TRANSIENT);
|
||||
code = sqlite3_step(updateLocalyticsInfo);
|
||||
sqlite3_finalize(updateLocalyticsInfo);
|
||||
|
||||
// Commit transaction.
|
||||
if (code == SQLITE_OK || code == SQLITE_DONE) {
|
||||
sqlite3_exec(_databaseConnection, "COMMIT", NULL, NULL, NULL);
|
||||
} else {
|
||||
sqlite3_exec(_databaseConnection, "ROLLBACK", NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// V3 adds a field for the last app key and patches a V2 migration issue.
|
||||
- (void)upgradeToSchemaV3 {
|
||||
sqlite3_exec(_databaseConnection,
|
||||
"ALTER TABLE localytics_info ADD app_key CHAR(64)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
- (NSUInteger)databaseSize {
|
||||
NSUInteger size = 0;
|
||||
NSDictionary *fileAttributes = [[NSFileManager defaultManager]
|
||||
attributesOfItemAtPath:[LocalyticsDatabase localyticsDatabasePath]
|
||||
error:nil];
|
||||
size = [fileAttributes fileSize];
|
||||
return size;
|
||||
}
|
||||
|
||||
- (int) eventCount {
|
||||
int count = 0;
|
||||
const char *sql = "SELECT count(*) FROM events";
|
||||
sqlite3_stmt *selectEventCount;
|
||||
|
||||
if(sqlite3_prepare_v2(_databaseConnection, sql, -1, &selectEventCount, NULL) == SQLITE_OK)
|
||||
{
|
||||
if(sqlite3_step(selectEventCount) == SQLITE_ROW) {
|
||||
count = sqlite3_column_int(selectEventCount, 0);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(selectEventCount);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)createdTimestamp {
|
||||
NSTimeInterval timestamp = 0;
|
||||
NSDictionary *fileAttributes = [[NSFileManager defaultManager]
|
||||
attributesOfItemAtPath:[LocalyticsDatabase localyticsDatabasePath]
|
||||
error:nil];
|
||||
timestamp = [[fileAttributes fileCreationDate] timeIntervalSince1970];
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)lastSessionStartTimestamp {
|
||||
|
||||
NSTimeInterval lastSessionStart = 0;
|
||||
|
||||
sqlite3_stmt *selectLastSessionStart;
|
||||
sqlite3_prepare_v2(_databaseConnection, "SELECT last_session_start FROM localytics_info", -1, &selectLastSessionStart, NULL);
|
||||
int code = sqlite3_step(selectLastSessionStart);
|
||||
if (code == SQLITE_ROW) {
|
||||
lastSessionStart = sqlite3_column_double(selectLastSessionStart, 0) == 1;
|
||||
}
|
||||
sqlite3_finalize(selectLastSessionStart);
|
||||
|
||||
return lastSessionStart;
|
||||
}
|
||||
|
||||
- (BOOL)setLastsessionStartTimestamp:(NSTimeInterval)timestamp {
|
||||
sqlite3_stmt *updateLastSessionStart;
|
||||
sqlite3_prepare_v2(_databaseConnection, "UPDATE localytics_info SET last_session_start = ?", -1, &updateLastSessionStart, NULL);
|
||||
sqlite3_bind_double(updateLastSessionStart, 1, timestamp);
|
||||
int code = sqlite3_step(updateLastSessionStart);
|
||||
sqlite3_finalize(updateLastSessionStart);
|
||||
|
||||
return code == SQLITE_DONE;
|
||||
}
|
||||
|
||||
- (BOOL)isOptedOut {
|
||||
BOOL optedOut = NO;
|
||||
|
||||
sqlite3_stmt *selectOptOut;
|
||||
sqlite3_prepare_v2(_databaseConnection, "SELECT opt_out FROM localytics_info", -1, &selectOptOut, NULL);
|
||||
int code = sqlite3_step(selectOptOut);
|
||||
if (code == SQLITE_ROW) {
|
||||
optedOut = sqlite3_column_int(selectOptOut, 0) == 1;
|
||||
}
|
||||
sqlite3_finalize(selectOptOut);
|
||||
|
||||
return optedOut;
|
||||
}
|
||||
|
||||
- (BOOL)setOptedOut:(BOOL)optOut {
|
||||
sqlite3_stmt *updateOptedOut;
|
||||
sqlite3_prepare_v2(_databaseConnection, "UPDATE localytics_info SET opt_out = ?", -1, &updateOptedOut, NULL);
|
||||
sqlite3_bind_int(updateOptedOut, 1, optOut);
|
||||
int code = sqlite3_step(updateOptedOut);
|
||||
sqlite3_finalize(updateOptedOut);
|
||||
|
||||
return code == SQLITE_OK;
|
||||
}
|
||||
|
||||
- (NSString *)customDimension:(int)dimension {
|
||||
if(dimension < 0 || dimension > 3) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *value = nil;
|
||||
NSString *query = [NSString stringWithFormat:@"select custom_d%i from localytics_info", dimension];
|
||||
|
||||
sqlite3_stmt *selectCustomDim;
|
||||
sqlite3_prepare_v2(_databaseConnection, [query UTF8String], -1, &selectCustomDim, NULL);
|
||||
int code = sqlite3_step(selectCustomDim);
|
||||
if (code == SQLITE_ROW && sqlite3_column_text(selectCustomDim, 0)) {
|
||||
value = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectCustomDim, 0)];
|
||||
}
|
||||
sqlite3_finalize(selectCustomDim);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
- (BOOL)setCustomDimension:(int)dimension value:(NSString *)value {
|
||||
if(dimension < 0 || dimension > 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NSString *query = [NSString stringWithFormat:@"update localytics_info SET custom_d%i = %@",
|
||||
dimension,
|
||||
(value == nil) ? @"null" : [NSString stringWithFormat:@"\"%@\"", value]];
|
||||
|
||||
int code = sqlite3_exec(_databaseConnection, [query UTF8String], NULL, NULL, NULL);
|
||||
|
||||
return code == SQLITE_OK;
|
||||
}
|
||||
|
||||
- (BOOL)incrementLastUploadNumber:(int *)uploadNumber {
|
||||
NSString *t = @"increment_upload_number";
|
||||
int code = SQLITE_OK;
|
||||
|
||||
code = [self beginTransaction:t] ? SQLITE_OK : SQLITE_ERROR;
|
||||
|
||||
if(code == SQLITE_OK) {
|
||||
// Increment value
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"UPDATE localytics_info "
|
||||
"SET last_upload_number = (last_upload_number + 1)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if(code == SQLITE_OK) {
|
||||
// Retrieve new value
|
||||
sqlite3_stmt *selectUploadNumber;
|
||||
sqlite3_prepare_v2(_databaseConnection,
|
||||
"SELECT last_upload_number FROM localytics_info",
|
||||
-1, &selectUploadNumber, NULL);
|
||||
code = sqlite3_step(selectUploadNumber);
|
||||
if (code == SQLITE_ROW) {
|
||||
*uploadNumber = sqlite3_column_int(selectUploadNumber, 0);
|
||||
}
|
||||
sqlite3_finalize(selectUploadNumber);
|
||||
}
|
||||
|
||||
if(code == SQLITE_ROW) {
|
||||
[self releaseTransaction:t];
|
||||
} else {
|
||||
[self rollbackTransaction:t];
|
||||
}
|
||||
|
||||
return code == SQLITE_ROW;
|
||||
}
|
||||
|
||||
- (BOOL)incrementLastSessionNumber:(int *)sessionNumber {
|
||||
NSString *t = @"increment_session_number";
|
||||
int code = [self beginTransaction:t] ? SQLITE_OK : SQLITE_ERROR;
|
||||
|
||||
if(code == SQLITE_OK) {
|
||||
// Increment value
|
||||
code = sqlite3_exec(_databaseConnection,
|
||||
"UPDATE localytics_info "
|
||||
"SET last_session_number = (last_session_number + 1)",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if(code == SQLITE_OK) {
|
||||
// Retrieve new value
|
||||
sqlite3_stmt *selectSessionNumber;
|
||||
sqlite3_prepare_v2(_databaseConnection,
|
||||
"SELECT last_session_number FROM localytics_info",
|
||||
-1, &selectSessionNumber, NULL);
|
||||
code = sqlite3_step(selectSessionNumber);
|
||||
if (code == SQLITE_ROW && sessionNumber != NULL) {
|
||||
*sessionNumber = sqlite3_column_int(selectSessionNumber, 0);
|
||||
}
|
||||
sqlite3_finalize(selectSessionNumber);
|
||||
}
|
||||
|
||||
if(code == SQLITE_ROW) {
|
||||
[self releaseTransaction:t];
|
||||
} else {
|
||||
[self rollbackTransaction:t];
|
||||
}
|
||||
|
||||
return code == SQLITE_ROW;
|
||||
}
|
||||
|
||||
- (BOOL)addEventWithBlobString:(NSString *)blob {
|
||||
|
||||
int code = SQLITE_OK;
|
||||
sqlite3_stmt *insertEvent;
|
||||
sqlite3_prepare_v2(_databaseConnection, "INSERT INTO events (blob_string) VALUES (?)", -1, &insertEvent, NULL);
|
||||
sqlite3_bind_text(insertEvent, 1, [blob UTF8String], -1, SQLITE_TRANSIENT);
|
||||
code = sqlite3_step(insertEvent);
|
||||
sqlite3_finalize(insertEvent);
|
||||
|
||||
return code == SQLITE_DONE;
|
||||
}
|
||||
|
||||
- (BOOL)addCloseEventWithBlobString:(NSString *)blob {
|
||||
NSString *t = @"add_close_event";
|
||||
BOOL success = [self beginTransaction:t];
|
||||
|
||||
// Add close event.
|
||||
if (success) {
|
||||
success = [self addEventWithBlobString:blob];
|
||||
}
|
||||
|
||||
// Record row id to localytics_info so that it can be removed if the session resumes.
|
||||
if (success) {
|
||||
sqlite3_stmt *updateCloseEvent;
|
||||
sqlite3_prepare_v2(_databaseConnection, "UPDATE localytics_info SET last_close_event = (SELECT event_id FROM events WHERE rowid = ?)", -1, &updateCloseEvent, NULL);
|
||||
sqlite3_int64 lastRow = sqlite3_last_insert_rowid(_databaseConnection);
|
||||
sqlite3_bind_int64(updateCloseEvent, 1, lastRow);
|
||||
int code = sqlite3_step(updateCloseEvent);
|
||||
sqlite3_finalize(updateCloseEvent);
|
||||
success = code == SQLITE_DONE;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
[self releaseTransaction:t];
|
||||
} else {
|
||||
[self rollbackTransaction:t];
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)addFlowEventWithBlobString:(NSString *)blob {
|
||||
NSString *t = @"add_flow_event";
|
||||
BOOL success = [self beginTransaction:t];
|
||||
|
||||
// Add flow event.
|
||||
if (success) {
|
||||
success = [self addEventWithBlobString:blob];
|
||||
}
|
||||
|
||||
// Record row id to localytics_info so that it can be removed if the session resumes.
|
||||
if (success) {
|
||||
sqlite3_stmt *updateFlowEvent;
|
||||
sqlite3_prepare_v2(_databaseConnection, "UPDATE localytics_info SET last_flow_event = (SELECT event_id FROM events WHERE rowid = ?)", -1, &updateFlowEvent, NULL);
|
||||
sqlite3_int64 lastRow = sqlite3_last_insert_rowid(_databaseConnection);
|
||||
sqlite3_bind_int64(updateFlowEvent, 1, lastRow);
|
||||
int code = sqlite3_step(updateFlowEvent);
|
||||
sqlite3_finalize(updateFlowEvent);
|
||||
success = code == SQLITE_DONE;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
[self releaseTransaction:t];
|
||||
} else {
|
||||
[self rollbackTransaction:t];
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)removeLastCloseAndFlowEvents {
|
||||
// Attempt to remove the last recorded close event.
|
||||
// Fail quietly if none was saved or it was previously removed.
|
||||
int code = sqlite3_exec(_databaseConnection, "DELETE FROM events WHERE event_id = (SELECT last_close_event FROM localytics_info) OR event_id = (SELECT last_flow_event FROM localytics_info)", NULL, NULL, NULL);
|
||||
|
||||
return code == SQLITE_OK;
|
||||
}
|
||||
|
||||
- (BOOL)addHeaderWithSequenceNumber:(int)number blobString:(NSString *)blob rowId:(sqlite3_int64 *)insertedRowId {
|
||||
sqlite3_stmt *insertHeader;
|
||||
sqlite3_prepare_v2(_databaseConnection, "INSERT INTO upload_headers (sequence_number, blob_string) VALUES (?, ?)", -1, &insertHeader, NULL);
|
||||
sqlite3_bind_int(insertHeader, 1, number);
|
||||
sqlite3_bind_text(insertHeader, 2, [blob UTF8String], -1, SQLITE_TRANSIENT);
|
||||
int code = sqlite3_step(insertHeader);
|
||||
sqlite3_finalize(insertHeader);
|
||||
|
||||
if (code == SQLITE_DONE && insertedRowId != NULL) {
|
||||
*insertedRowId = sqlite3_last_insert_rowid(_databaseConnection);
|
||||
}
|
||||
|
||||
return code == SQLITE_DONE;
|
||||
}
|
||||
|
||||
- (int)unstagedEventCount {
|
||||
int rowCount = 0;
|
||||
sqlite3_stmt *selectEventCount;
|
||||
sqlite3_prepare_v2(_databaseConnection, "SELECT COUNT(*) FROM events WHERE UPLOAD_HEADER IS NULL", -1, &selectEventCount, NULL);
|
||||
int code = sqlite3_step(selectEventCount);
|
||||
if (code == SQLITE_ROW) {
|
||||
rowCount = sqlite3_column_int(selectEventCount, 0);
|
||||
}
|
||||
sqlite3_finalize(selectEventCount);
|
||||
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
- (BOOL)stageEventsForUpload:(sqlite3_int64)headerId {
|
||||
|
||||
// Associate all outstanding events with the given upload header ID.
|
||||
NSString *stageEvents = [NSString stringWithFormat:@"UPDATE events SET upload_header = ? WHERE upload_header IS NULL"];
|
||||
sqlite3_stmt *updateEvents;
|
||||
sqlite3_prepare_v2(_databaseConnection, [stageEvents UTF8String], -1, &updateEvents, NULL);
|
||||
sqlite3_bind_int(updateEvents, 1, headerId);
|
||||
int code = sqlite3_step(updateEvents);
|
||||
sqlite3_finalize(updateEvents);
|
||||
BOOL success = (code == SQLITE_DONE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
- (BOOL)updateAppKey:(NSString *)appKey {
|
||||
sqlite3_stmt *updateAppKey;
|
||||
sqlite3_prepare_v2(_databaseConnection, "UPDATE localytics_info set app_key = ?", -1, &updateAppKey, NULL);
|
||||
sqlite3_bind_text (updateAppKey, 1, [appKey UTF8String], -1, SQLITE_TRANSIENT);
|
||||
int code = sqlite3_step(updateAppKey);
|
||||
sqlite3_finalize(updateAppKey);
|
||||
BOOL success = (code == SQLITE_DONE);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
- (NSString *)uploadBlobString {
|
||||
|
||||
// Retrieve the blob strings of each upload header and its child events, in order.
|
||||
const char *sql = "SELECT * FROM ( "
|
||||
" SELECT h.blob_string AS 'blob', h.sequence_number as 'seq', 0 FROM upload_headers h"
|
||||
" UNION ALL "
|
||||
" SELECT e.blob_string AS 'blob', e.upload_header as 'seq', 1 FROM events e"
|
||||
") "
|
||||
"ORDER BY 2, 3";
|
||||
sqlite3_stmt *selectBlobs;
|
||||
sqlite3_prepare_v2(_databaseConnection, sql, -1, &selectBlobs, NULL);
|
||||
NSMutableString *uploadBlobString = [NSMutableString string];
|
||||
while (sqlite3_step(selectBlobs) == SQLITE_ROW) {
|
||||
const char *blob = (const char *)sqlite3_column_text(selectBlobs, 0);
|
||||
if (blob != NULL) {
|
||||
NSString *blobString = [[NSString alloc] initWithCString:blob encoding:NSUTF8StringEncoding];
|
||||
[uploadBlobString appendString:blobString];
|
||||
[blobString release];
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(selectBlobs);
|
||||
|
||||
return [[uploadBlobString copy] autorelease];
|
||||
}
|
||||
|
||||
- (BOOL)deleteUploadedData {
|
||||
// Delete all headers and staged events.
|
||||
NSString *t = @"delete_upload_data";
|
||||
int code = [self beginTransaction:t] ? SQLITE_OK : SQLITE_ERROR;
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection, "DELETE FROM events WHERE upload_header IS NOT NULL", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection, "DELETE FROM upload_headers", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
[self releaseTransaction:t];
|
||||
} else {
|
||||
[self rollbackTransaction:t];
|
||||
}
|
||||
|
||||
return code == SQLITE_OK;
|
||||
}
|
||||
|
||||
- (BOOL)resetAnalyticsData {
|
||||
// Delete or zero all analytics data.
|
||||
// Reset: headers, events, session number, upload number, last session start, last close event, and last flow event.
|
||||
// Unaffected: schema version, opt out status, install ID (deprecated), and app key.
|
||||
|
||||
NSString *t = @"reset_analytics_data";
|
||||
int code = [self beginTransaction:t] ? SQLITE_OK : SQLITE_ERROR;
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection, "DELETE FROM events", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection, "DELETE FROM upload_headers", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
code = sqlite3_exec(_databaseConnection,"UPDATE localytics_info SET last_session_number = 0, last_upload_number = 0,"
|
||||
"last_close_event = null, last_flow_event = null, last_session_start = null, "
|
||||
"custom_d0 = null, custom_d1 = null, custom_d2 = null, custom_d3 = null",
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (code == SQLITE_OK) {
|
||||
[self releaseTransaction:t];
|
||||
} else {
|
||||
[self rollbackTransaction:t];
|
||||
}
|
||||
|
||||
return code == SQLITE_OK;
|
||||
}
|
||||
|
||||
- (BOOL)vacuumIfRequired {
|
||||
int code = SQLITE_OK;
|
||||
if ([self databaseSize] > MAX_DATABASE_SIZE * VACUUM_THRESHOLD) {
|
||||
code = sqlite3_exec(_databaseConnection, "VACUUM", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
return code == SQLITE_OK;
|
||||
}
|
||||
|
||||
- (NSString *)randomUUID {
|
||||
CFUUIDRef theUUID = CFUUIDCreate(NULL);
|
||||
CFStringRef stringUUID = CFUUIDCreateString(NULL, theUUID);
|
||||
CFRelease(theUUID);
|
||||
return [(NSString *)stringUUID autorelease];
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
+ (id)allocWithZone:(NSZone *)zone {
|
||||
@synchronized(self) {
|
||||
if (_sharedLocalyticsDatabase == nil) {
|
||||
_sharedLocalyticsDatabase = [super allocWithZone:zone];
|
||||
return _sharedLocalyticsDatabase;
|
||||
}
|
||||
}
|
||||
// returns nil on subsequent allocations
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)retain {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (unsigned)retainCount {
|
||||
// maximum value of an unsigned int - prevents additional retains for the class
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
- (oneway void)release {
|
||||
// ignore release commands
|
||||
}
|
||||
|
||||
- (id)autorelease {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
sqlite3_close(_databaseConnection);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,216 +0,0 @@
|
||||
// LocalyticsSession.h
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
// Set this to true to enable localytics traces (useful for debugging)
|
||||
#define DO_LOCALYTICS_LOGGING false
|
||||
|
||||
/*!
|
||||
@class LocalyticsSession
|
||||
@discussion The class which manages creating, collecting, & uploading a Localytics session.
|
||||
Please see the following guides for information on how to best use this
|
||||
library, sample code, and other useful information:
|
||||
<ul>
|
||||
<li><a href="http://wiki.localytics.com/index.php?title=Developer's_Integration_Guide">Main Developer's Integration Guide</a></li>
|
||||
</ul>
|
||||
|
||||
<strong>Best Practices</strong>
|
||||
<ul>
|
||||
<li>Instantiate the LocalyticsSession object in applicationDidFinishLaunching.</li>
|
||||
<li>Open your session and begin your uploads in applicationDidFinishLaunching. This way the
|
||||
upload has time to complete and it all happens before your users have a
|
||||
chance to begin any data intensive actions of their own.</li>
|
||||
<li>Close the session in applicationWillTerminate, and in applicationDidEnterBackground.</li>
|
||||
<li>Resume the session in applicationWillEnterForeground.</li>
|
||||
<li>Do not call any Localytics functions inside a loop. Instead, calls
|
||||
such as <code>tagEvent</code> should follow user actions. This limits the
|
||||
amount of data which is stored and uploaded.</li>
|
||||
<li>Do not use multiple LocalticsSession objects to upload data with
|
||||
multiple application keys. This can cause invalid state.</li>
|
||||
</ul>
|
||||
|
||||
@author Localytics
|
||||
*/
|
||||
@interface LocalyticsSession : NSObject {
|
||||
|
||||
BOOL _hasInitialized; // Whether or not the session object has been initialized.
|
||||
BOOL _isSessionOpen; // Whether or not this session has been opened.
|
||||
float _backgroundSessionTimeout; // If an App stays in the background for more
|
||||
// than this many seconds, start a new session
|
||||
// when it returns to foreground.
|
||||
@private
|
||||
#pragma mark Member Variables
|
||||
dispatch_queue_t _queue; // Queue of Localytics block objects.
|
||||
dispatch_group_t _criticalGroup; // Group of blocks the must complete before backgrounding.
|
||||
NSString *_sessionUUID; // Unique identifier for this session.
|
||||
NSString *_applicationKey; // Unique identifier for the instrumented application
|
||||
NSTimeInterval _lastSessionStartTimestamp; // The start time of the most recent session.
|
||||
NSDate *_sessionResumeTime; // Time session was started or resumed.
|
||||
NSDate *_sessionCloseTime; // Time session was closed.
|
||||
NSMutableString *_unstagedFlowEvents; // Comma-delimited list of app screens and events tagged during this
|
||||
// session that have NOT been staged for upload.
|
||||
NSMutableString *_stagedFlowEvents; // App screens and events tagged during this session that HAVE been staged
|
||||
// for upload.
|
||||
NSMutableString *_screens; // Comma-delimited list of screens tagged during this session.
|
||||
NSTimeInterval _sessionActiveDuration; // Duration that session open.
|
||||
BOOL _sessionHasBeenOpen; // Whether or not this session has ever been open.
|
||||
}
|
||||
|
||||
@property dispatch_queue_t queue;
|
||||
@property dispatch_group_t criticalGroup;
|
||||
@property BOOL isSessionOpen;
|
||||
@property BOOL hasInitialized;
|
||||
@property float backgroundSessionTimeout;
|
||||
|
||||
#pragma mark Public Methods
|
||||
/*!
|
||||
@method sharedLocalyticsSession
|
||||
@abstract Accesses the Session object. This is a Singleton class which maintains
|
||||
a single session throughout your application. It is possible to manage your own
|
||||
session, but this is the easiest way to access the Localytics object throughout your code.
|
||||
The class is accessed within the code using the following syntax:
|
||||
[[LocalyticsSession sharedLocalyticsSession] functionHere]
|
||||
So, to tag an event, all that is necessary, anywhere in the code is:
|
||||
[[LocalyticsSession sharedLocalyticsSession] tagEvent:@"MY_EVENT"];
|
||||
*/
|
||||
+ (LocalyticsSession *)sharedLocalyticsSession;
|
||||
|
||||
/*!
|
||||
@method LocalyticsSession
|
||||
@abstract Initializes the Localytics Object. Not necessary if you choose to use startSession.
|
||||
@param applicationKey The key unique for each application generated at www.localytics.com
|
||||
*/
|
||||
- (void)LocalyticsSession:(NSString *)appKey;
|
||||
|
||||
/*!
|
||||
@method startSession
|
||||
@abstract An optional convenience initialize method that also calls the LocalyticsSession, open &
|
||||
upload methods. Best Practice is to call open & upload immediately after Localytics Session when loading an app,
|
||||
this method fascilitates that behavior.
|
||||
It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>.
|
||||
@param applicationKey The key unique for each application generated
|
||||
at www.localytics.com
|
||||
*/
|
||||
- (void)startSession:(NSString *)appKey;
|
||||
|
||||
/*!
|
||||
@method setOptIn
|
||||
@abstract (OPTIONAL) Allows the application to control whether or not it will collect user data.
|
||||
Even if this call is used, it is necessary to continue calling upload(). No new data will be
|
||||
collected, so nothing new will be uploaded but it is necessary to upload an event telling the
|
||||
server this user has opted out.
|
||||
@param optedIn True if the user is opted in, false otherwise.
|
||||
*/
|
||||
- (void)setOptIn:(BOOL)optedIn;
|
||||
|
||||
/*!
|
||||
@method isOptedIn
|
||||
@abstract (OPTIONAL) Whether or not this user has is opted in or out. The only way they can be
|
||||
opted out is if setOptIn(false) has been called before this. This function should only be
|
||||
used to pre-populate a checkbox in an options menu. It is not recommended that an application
|
||||
branch based on Localytics instrumentation because this creates an additional test case. If
|
||||
the app is opted out, all subsequent Localytics calls will return immediately.
|
||||
@result true if the user is opted in, false otherwise.
|
||||
*/
|
||||
- (BOOL)isOptedIn;
|
||||
|
||||
/*!
|
||||
@method open
|
||||
@abstract Opens the Localytics session. Not necessary if you choose to use startSession.
|
||||
The session time as presented on the website is the time between <code>open</code> and the
|
||||
final <code>close</code> so it is recommended to open the session as early as possible, and close
|
||||
it at the last moment. The session must be opened before any tags can
|
||||
be written. It is recommended that this call be placed in <code>applicationDidFinishLaunching</code>.
|
||||
<br>
|
||||
If for any reason this is called more than once every subsequent open call
|
||||
will be ignored.
|
||||
*/
|
||||
- (void)open;
|
||||
|
||||
/*!
|
||||
@method resume
|
||||
@abstract Resumes the Localytics session. When the App enters the background, the session is
|
||||
closed and the time of closing is recorded. When the app returns to the foreground, the session
|
||||
is resumed. If the time since closing is greater than BACKGROUND_SESSION_TIMEOUT, (15 seconds
|
||||
by default) a new session is created, and uploading is triggered. Otherwise, the previous session
|
||||
is reopened.
|
||||
*/
|
||||
- (void)resume;
|
||||
|
||||
/*!
|
||||
@method close
|
||||
@abstract Closes the Localytics session. This should be called in
|
||||
<code>applicationWillTerminate</code>.
|
||||
<br>
|
||||
If close is not called, the session will still be uploaded but no
|
||||
events will be processed and the session time will not appear. This is
|
||||
because the session is not yet closed so it should not be used in
|
||||
comparison with sessions which are closed.
|
||||
*/
|
||||
- (void)close;
|
||||
|
||||
/*!
|
||||
@method tagEvent
|
||||
@abstract Allows a session to tag a particular event as having occurred. For
|
||||
example, if a view has three buttons, it might make sense to tag
|
||||
each button click with the name of the button which was clicked.
|
||||
For another example, in a game with many levels it might be valuable
|
||||
to create a new tag every time the user gets to a new level in order
|
||||
to determine how far the average user is progressing in the game.
|
||||
<br>
|
||||
<strong>Tagging Best Practices</strong>
|
||||
<ul>
|
||||
<li>DO NOT use tags to record personally identifiable information.</li>
|
||||
<li>The best way to use tags is to create all the tag strings as predefined
|
||||
constants and only use those. This is more efficient and removes the risk of
|
||||
collecting personal information.</li>
|
||||
<li>Do not set tags inside loops or any other place which gets called
|
||||
frequently. This can cause a lot of data to be stored and uploaded.</li>
|
||||
</ul>
|
||||
<br>
|
||||
See the tagging guide at: http://wiki.localytics.com/
|
||||
@param event The name of the event which occurred.
|
||||
*/
|
||||
- (void)tagEvent:(NSString *)event;
|
||||
|
||||
- (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes;
|
||||
|
||||
- (void)tagEvent:(NSString *)event attributes:(NSDictionary *)attributes reportAttributes:(NSDictionary *)reportAttributes;
|
||||
|
||||
/*!
|
||||
@method tagScreen
|
||||
@abstract Allows tagging the flow of screens encountered during the session.
|
||||
@param screen The name of the screen
|
||||
*/
|
||||
- (void)tagScreen:(NSString *)screen;
|
||||
|
||||
/*!
|
||||
@method upload
|
||||
@abstract Creates a low priority thread which uploads any Localytics data already stored
|
||||
on the device. This should be done early in the process life in order to
|
||||
guarantee as much time as possible for slow connections to complete. It is also reasonable
|
||||
to upload again when the application is exiting because if the upload is cancelled the data
|
||||
will just get uploaded the next time the app comes up.
|
||||
*/
|
||||
- (void)upload;
|
||||
|
||||
/*!
|
||||
@method setCustomDimension
|
||||
@abstract (ENTERPRISE ONLY) Sets the value of a custom dimension. Custom dimensions are dimensions
|
||||
which contain user defined data unlike the predefined dimensions such as carrier, model, and country.
|
||||
Once a value for a custom dimension is set, the device it was set on will continue to upload that value
|
||||
until the value is changed. To clear a value pass nil as the value.
|
||||
The proper use of custom dimensions involves defining a dimension with less than ten distinct possible
|
||||
values and assigning it to one of the four available custom dimensions. Once assigned this definition should
|
||||
never be changed without changing the App Key otherwise old installs of the application will pollute new data.
|
||||
*/
|
||||
- (void)setCustomDimension:(int)dimension value:(NSString *)value;
|
||||
|
||||
@end
|
||||
@@ -1,42 +0,0 @@
|
||||
// LocalyticsUploader.h
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/*!
|
||||
@class LocalyticsUploader
|
||||
@discussion Singleton class to handle data uploads
|
||||
*/
|
||||
|
||||
@interface LocalyticsUploader : NSObject {
|
||||
}
|
||||
|
||||
@property (readonly) BOOL isUploading;
|
||||
|
||||
/*!
|
||||
@method sharedLocalyticsUploader
|
||||
@abstract Establishes this as a Singleton Class allowing for data persistence.
|
||||
The class is accessed within the code using the following syntax:
|
||||
[[LocalyticsUploader sharedLocalyticsUploader] functionHere]
|
||||
*/
|
||||
+ (LocalyticsUploader *)sharedLocalyticsUploader;
|
||||
|
||||
/*!
|
||||
@method LocalyticsUploader
|
||||
@abstract Creates a thread which uploads all queued header and event data.
|
||||
All files starting with sessionFilePrefix are renamed,
|
||||
uploaded and deleted on upload. This way the sessions can continue
|
||||
writing data regardless of whether or not the upload succeeds. Files
|
||||
which have been renamed still count towards the total number of Localytics
|
||||
files which can be stored on the disk.
|
||||
@param localyticsApplicationKey the Localytics application ID
|
||||
*/
|
||||
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey;
|
||||
|
||||
@end
|
||||
@@ -1,236 +0,0 @@
|
||||
// LocalyticsUploader.m
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import "LocalyticsUploader.h"
|
||||
#import "LocalyticsSession.h"
|
||||
#import "LocalyticsDatabase.h"
|
||||
#import <zlib.h>
|
||||
|
||||
#define LOCALYTICS_URL @"http://analytics.localytics.com/api/v2/applications/%@/uploads"
|
||||
|
||||
static LocalyticsUploader *_sharedUploader = nil;
|
||||
|
||||
@interface LocalyticsUploader ()
|
||||
- (void)finishUpload;
|
||||
- (NSData *)gzipDeflatedDataWithData:(NSData *)data;
|
||||
- (void)logMessage:(NSString *)message;
|
||||
|
||||
@property (readwrite) BOOL isUploading;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LocalyticsUploader
|
||||
@synthesize isUploading = _isUploading;
|
||||
|
||||
#pragma mark - Singleton Class
|
||||
+ (LocalyticsUploader *)sharedLocalyticsUploader {
|
||||
@synchronized(self) {
|
||||
if (_sharedUploader == nil) {
|
||||
_sharedUploader = [[self alloc] init];
|
||||
}
|
||||
}
|
||||
return _sharedUploader;
|
||||
}
|
||||
|
||||
#pragma mark - Class Methods
|
||||
|
||||
- (void)uploaderWithApplicationKey:(NSString *)localyticsApplicationKey {
|
||||
|
||||
// Do nothing if already uploading.
|
||||
if (self.isUploading == true)
|
||||
{
|
||||
[self logMessage:@"Upload already in progress. Aborting."];
|
||||
return;
|
||||
}
|
||||
|
||||
[self logMessage:@"Beginning upload process"];
|
||||
self.isUploading = true;
|
||||
|
||||
// Prepare the data for upload. The upload could take a long time, so some effort has to be made to be sure that events
|
||||
// which get written while the upload is taking place don't get lost or duplicated. To achieve this, the logic is:
|
||||
// 1) Append every header row blob string and and those of its associated events to the upload string.
|
||||
// 2) Deflate and upload the data.
|
||||
// 3) On success, delete all blob headers and staged events. Events added while an upload is in process are not
|
||||
// deleted because they are not associated a header (and cannot be until the upload completes).
|
||||
|
||||
// Step 1
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
LocalyticsDatabase *db = [LocalyticsDatabase sharedLocalyticsDatabase];
|
||||
NSString *blobString = [db uploadBlobString];
|
||||
|
||||
if ([blobString length] == 0) {
|
||||
// There is nothing outstanding to upload.
|
||||
[self logMessage:@"Abandoning upload. There are no new events."];
|
||||
[pool drain];
|
||||
[self finishUpload];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *requestData = [blobString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *myString = [[[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding] autorelease];
|
||||
[self logMessage:[NSString stringWithFormat:@"Uploading data (length: %u)", [myString length]]];
|
||||
|
||||
// Step 2
|
||||
NSData *deflatedRequestData = [[self gzipDeflatedDataWithData:requestData] retain];
|
||||
|
||||
[pool drain];
|
||||
|
||||
NSString *apiUrlString = [NSString stringWithFormat:LOCALYTICS_URL, [localyticsApplicationKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSMutableURLRequest *submitRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:apiUrlString]
|
||||
cachePolicy:NSURLRequestReloadIgnoringCacheData
|
||||
timeoutInterval:60.0];
|
||||
[submitRequest setHTTPMethod:@"POST"];
|
||||
[submitRequest setValue:@"application/x-gzip" forHTTPHeaderField:@"Content-Type"];
|
||||
[submitRequest setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
|
||||
[submitRequest setValue:[NSString stringWithFormat:@"%d", [deflatedRequestData length]] forHTTPHeaderField:@"Content-Length"];
|
||||
[submitRequest setHTTPBody:deflatedRequestData];
|
||||
[deflatedRequestData release];
|
||||
|
||||
// Perform synchronous upload in an async dispatch. This is necessary because the calling block will not persist to
|
||||
// receive the response data.
|
||||
dispatch_group_async([[LocalyticsSession sharedLocalyticsSession] criticalGroup], [[LocalyticsSession sharedLocalyticsSession] queue], ^{
|
||||
@try {
|
||||
NSURLResponse *response = nil;
|
||||
NSError *responseError = nil;
|
||||
[NSURLConnection sendSynchronousRequest:submitRequest returningResponse:&response error:&responseError];
|
||||
NSInteger responseStatusCode = [(NSHTTPURLResponse *)response statusCode];
|
||||
|
||||
if (responseError) {
|
||||
// On error, simply print the error and close the uploader. We have to assume the data was not transmited
|
||||
// so it is not deleted. In the event that we accidently store data which was succesfully uploaded, the
|
||||
// duplicate data will be ignored by the server when it is next uploaded.
|
||||
[self logMessage:[NSString stringWithFormat:
|
||||
@"Error Uploading. Code: %d, Description: %@",
|
||||
[responseError code],
|
||||
[responseError localizedDescription]]];
|
||||
} else {
|
||||
// Step 3
|
||||
// While response status codes in the 5xx range leave upload rows intact, the default case is to delete.
|
||||
if (responseStatusCode >= 500 && responseStatusCode < 600) {
|
||||
[self logMessage:[NSString stringWithFormat:@"Upload failed with response status code %d", responseStatusCode]];
|
||||
} else {
|
||||
// Because only one instance of the uploader can be running at a time it should not be possible for
|
||||
// new upload rows to appear so there is no fear of deleting data which has not yet been uploaded.
|
||||
[self logMessage:[NSString stringWithFormat:@"Upload completed successfully. Response code %d", responseStatusCode]];
|
||||
[[LocalyticsDatabase sharedLocalyticsDatabase] deleteUploadedData];
|
||||
}
|
||||
}
|
||||
}
|
||||
@catch (NSException * e) {}
|
||||
|
||||
[self finishUpload];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)finishUpload
|
||||
{
|
||||
self.isUploading = false;
|
||||
|
||||
// Upload data has been deleted. Recover the disk space if necessary.
|
||||
[[LocalyticsDatabase sharedLocalyticsDatabase] vacuumIfRequired];
|
||||
}
|
||||
|
||||
/*!
|
||||
@method gzipDeflatedDataWithData
|
||||
@abstract Deflates the provided data using gzip at the default compression level (6). Complete NSData gzip category available on CocoaDev. http://www.cocoadev.com/index.pl?NSDataCategory.
|
||||
@return the deflated data
|
||||
*/
|
||||
- (NSData *)gzipDeflatedDataWithData:(NSData *)data
|
||||
{
|
||||
if ([data length] == 0) return data;
|
||||
|
||||
z_stream strm;
|
||||
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.total_out = 0;
|
||||
strm.next_in=(Bytef *)[data bytes];
|
||||
strm.avail_in = [data length];
|
||||
|
||||
// Compresssion Levels:
|
||||
// Z_NO_COMPRESSION
|
||||
// Z_BEST_SPEED
|
||||
// Z_BEST_COMPRESSION
|
||||
// Z_DEFAULT_COMPRESSION
|
||||
|
||||
if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
|
||||
|
||||
NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion
|
||||
|
||||
do {
|
||||
|
||||
if (strm.total_out >= [compressed length])
|
||||
[compressed increaseLengthBy: 16384];
|
||||
|
||||
strm.next_out = [compressed mutableBytes] + strm.total_out;
|
||||
strm.avail_out = [compressed length] - strm.total_out;
|
||||
|
||||
deflate(&strm, Z_FINISH);
|
||||
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
deflateEnd(&strm);
|
||||
|
||||
[compressed setLength: strm.total_out];
|
||||
return [NSData dataWithData:compressed];
|
||||
}
|
||||
|
||||
/*!
|
||||
@method logMessage
|
||||
@abstract Logs a message with (localytics uploader) prepended to it
|
||||
@param message The message to log
|
||||
*/
|
||||
- (void) logMessage:(NSString *)message {
|
||||
if(DO_LOCALYTICS_LOGGING) {
|
||||
NSLog(@"(localytics uploader) %s\n", [message UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - System Functions
|
||||
+ (id)allocWithZone:(NSZone *)zone {
|
||||
@synchronized(self) {
|
||||
if (_sharedUploader == nil) {
|
||||
_sharedUploader = [super allocWithZone:zone];
|
||||
return _sharedUploader;
|
||||
}
|
||||
}
|
||||
// returns nil on subsequent allocations
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)retain {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (unsigned)retainCount {
|
||||
// maximum value of an unsigned int - prevents additional retains for the class
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
- (oneway void)release {
|
||||
// ignore release commands
|
||||
}
|
||||
|
||||
- (id)autorelease {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_sharedUploader release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,48 +0,0 @@
|
||||
// UploaderThread.h
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/*!
|
||||
@class UploaderThread
|
||||
@discussion Singleton class to handle data uploads
|
||||
*/
|
||||
|
||||
@interface UploaderThread : NSObject {
|
||||
NSURLConnection *_uploadConnection; // The connection which uploads the bits
|
||||
NSInteger _responseStatusCode; // The HTTP response status code for the current connection
|
||||
|
||||
BOOL _isUploading; // A flag to gaurantee only one uploader instance can happen at once
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) NSURLConnection *uploadConnection;
|
||||
|
||||
@property BOOL isUploading;
|
||||
|
||||
/*!
|
||||
@method sharedUploaderThread
|
||||
@abstract Establishes this as a Singleton Class allowing for data persistence.
|
||||
The class is accessed within the code using the following syntax:
|
||||
[[UploaderThread sharedUploaderThread] functionHere]
|
||||
*/
|
||||
+ (UploaderThread *)sharedUploaderThread;
|
||||
|
||||
/*!
|
||||
@method UploaderThread
|
||||
@abstract Creates a thread which uploads all queued header and event data.
|
||||
All files starting with sessionFilePrefix are renamed,
|
||||
uploaded and deleted on upload. This way the sessions can continue
|
||||
writing data regardless of whether or not the upload succeeds. Files
|
||||
which have been renamed still count towards the total number of Localytics
|
||||
files which can be stored on the disk.
|
||||
@param localyticsApplicationKey the Localytics application ID
|
||||
*/
|
||||
- (void)uploaderThreadwithApplicationKey:(NSString *)localyticsApplicationKey;
|
||||
|
||||
@end
|
||||
@@ -1,260 +0,0 @@
|
||||
// UploaderThread.m
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
#import "UploaderThread.h"
|
||||
#import "LocalyticsSession.h"
|
||||
#import "LocalyticsDatabase.h"
|
||||
#import <zlib.h>
|
||||
|
||||
#define LOCALYTICS_URL @"http://analytics.localytics.com/api/v2/applications/%@/uploads" // url to send the
|
||||
|
||||
static UploaderThread *_sharedUploaderThread = nil;
|
||||
|
||||
@interface UploaderThread ()
|
||||
- (void)complete;
|
||||
- (NSData *)gzipDeflatedDataWithData:(NSData *)data;
|
||||
- (void)logMessage:(NSString *)message;
|
||||
@end
|
||||
|
||||
@implementation UploaderThread
|
||||
|
||||
@synthesize uploadConnection = _uploadConnection;
|
||||
@synthesize isUploading = _isUploading;
|
||||
|
||||
#pragma mark Singleton Class
|
||||
+ (UploaderThread *)sharedUploaderThread {
|
||||
@synchronized(self) {
|
||||
if (_sharedUploaderThread == nil)
|
||||
{
|
||||
_sharedUploaderThread = [[self alloc] init];
|
||||
}
|
||||
}
|
||||
return _sharedUploaderThread;
|
||||
}
|
||||
|
||||
#pragma mark Class Methods
|
||||
- (void)uploaderThreadwithApplicationKey:(NSString *)localyticsApplicationKey {
|
||||
|
||||
// Do nothing if already uploading.
|
||||
if (self.uploadConnection != nil || self.isUploading == true)
|
||||
{
|
||||
[self logMessage:@"Upload already in progress. Aborting."];
|
||||
return;
|
||||
}
|
||||
|
||||
[self logMessage:@"Beginning upload process"];
|
||||
self.isUploading = true;
|
||||
|
||||
// Prepare the data for upload. The upload could take a long time, so some effort has to be made to be sure that events
|
||||
// which get written while the upload is taking place don't get lost or duplicated. To achieve this, the logic is:
|
||||
// 1) Append every header row blob string and and those of its associated events to the upload string.
|
||||
// 2) Deflate and upload the data.
|
||||
// 3) On success, delete all blob headers and staged events. Events added while an upload is in process are not
|
||||
// deleted because they are not associated a header (and cannot be until the upload completes).
|
||||
|
||||
// Step 1
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
LocalyticsDatabase *db = [LocalyticsDatabase sharedLocalyticsDatabase];
|
||||
NSString *blobString = [db uploadBlobString];
|
||||
|
||||
if ([blobString length] == 0) {
|
||||
// There is nothing outstanding to upload.
|
||||
[self logMessage:@"Abandoning upload. There are no new events."];
|
||||
|
||||
[pool drain];
|
||||
[self complete];
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *requestData = [blobString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *myString = [[[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding] autorelease];
|
||||
[self logMessage:@"Upload data:"];
|
||||
[self logMessage:myString];
|
||||
|
||||
// Step 2
|
||||
NSData *deflatedRequestData = [[self gzipDeflatedDataWithData:requestData] retain];
|
||||
|
||||
[pool drain];
|
||||
|
||||
NSString *apiUrlString = [NSString stringWithFormat:LOCALYTICS_URL, [localyticsApplicationKey stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSMutableURLRequest *submitRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:apiUrlString]
|
||||
cachePolicy:NSURLRequestReloadIgnoringCacheData
|
||||
timeoutInterval:60.0];
|
||||
[submitRequest setHTTPMethod:@"POST"];
|
||||
[submitRequest setValue:@"application/x-gzip" forHTTPHeaderField:@"Content-Type"];
|
||||
[submitRequest setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
|
||||
[submitRequest setValue:[NSString stringWithFormat:@"%d", [deflatedRequestData length]] forHTTPHeaderField:@"Content-Length"];
|
||||
[submitRequest setHTTPBody:deflatedRequestData];
|
||||
[deflatedRequestData release];
|
||||
|
||||
// The NSURLConnection Object automatically spawns its own thread as a default behavior.
|
||||
@try
|
||||
{
|
||||
[self logMessage:@"Spawning new thread for upload"];
|
||||
self.uploadConnection = [NSURLConnection connectionWithRequest:submitRequest delegate:self];
|
||||
|
||||
// Step 3 is handled by connectionDidFinishLoading.
|
||||
}
|
||||
@catch (NSException * e)
|
||||
{
|
||||
[self complete];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark **** NSURLConnection FUNCTIONS ****
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
// Used to gather response data from server - Not utilized in this version
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||
// Could receive multiple response callbacks, likely due to redirection.
|
||||
// Record status and act only when connection completes load.
|
||||
_responseStatusCode = [(NSHTTPURLResponse *)response statusCode];
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||
// If the connection finished loading, the files should be deleted. While response status codes in the 5xx range
|
||||
// leave upload rows intact, the default case is to delete.
|
||||
if (_responseStatusCode >= 500 && _responseStatusCode < 600)
|
||||
{
|
||||
[self logMessage:[NSString stringWithFormat:@"Upload failed with response status code %d", _responseStatusCode]];
|
||||
} else
|
||||
{
|
||||
// The connection finished loading and uploaded data should be deleted. Because only one instance of the
|
||||
// uploader can be running at a time it should not be possible for new upload rows to appear so there is no
|
||||
// fear of deleting data which has not yet been uploaded.
|
||||
[self logMessage:[NSString stringWithFormat:@"Upload completed successfully. Response code %d", _responseStatusCode]];
|
||||
[[LocalyticsDatabase sharedLocalyticsDatabase] deleteUploadData];
|
||||
}
|
||||
|
||||
// Close upload session
|
||||
[self complete];
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||
// On error, simply print the error and close the uploader. We have to assume the data was not transmited
|
||||
// so it is not deleted. In the event that we accidently store data which was succesfully uploaded, the
|
||||
// duplicate data will be ignored by the server when it is next uploaded.
|
||||
[self logMessage:[NSString stringWithFormat:
|
||||
@"Error Uploading. Code: %d, Description: %s",
|
||||
[error code],
|
||||
[error localizedDescription]]];
|
||||
|
||||
[self complete];
|
||||
}
|
||||
|
||||
/*!
|
||||
@method complete
|
||||
@abstract closes the upload connection and reports back to the session that the upload is complete
|
||||
*/
|
||||
- (void)complete {
|
||||
_responseStatusCode = 0;
|
||||
self.uploadConnection = nil;
|
||||
self.isUploading = false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@method gzipDeflatedDataWithData
|
||||
@abstract Deflates the provided data using gzip at the default compression level (6). Complete NSData gzip category available on CocoaDev. http://www.cocoadev.com/index.pl?NSDataCategory.
|
||||
@return the deflated data
|
||||
*/
|
||||
- (NSData *)gzipDeflatedDataWithData:(NSData *)data
|
||||
{
|
||||
if ([data length] == 0) return data;
|
||||
|
||||
z_stream strm;
|
||||
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.total_out = 0;
|
||||
strm.next_in=(Bytef *)[data bytes];
|
||||
strm.avail_in = [data length];
|
||||
|
||||
// Compresssion Levels:
|
||||
// Z_NO_COMPRESSION
|
||||
// Z_BEST_SPEED
|
||||
// Z_BEST_COMPRESSION
|
||||
// Z_DEFAULT_COMPRESSION
|
||||
|
||||
if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
|
||||
|
||||
NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion
|
||||
|
||||
do {
|
||||
|
||||
if (strm.total_out >= [compressed length])
|
||||
[compressed increaseLengthBy: 16384];
|
||||
|
||||
strm.next_out = [compressed mutableBytes] + strm.total_out;
|
||||
strm.avail_out = [compressed length] - strm.total_out;
|
||||
|
||||
deflate(&strm, Z_FINISH);
|
||||
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
deflateEnd(&strm);
|
||||
|
||||
[compressed setLength: strm.total_out];
|
||||
return [NSData dataWithData:compressed];
|
||||
}
|
||||
|
||||
/*!
|
||||
@method logMessage
|
||||
@abstract Logs a message with (localytics uploader) prepended to it
|
||||
@param message The message to log
|
||||
*/
|
||||
- (void) logMessage:(NSString *)message {
|
||||
if(DO_LOCALYTICS_LOGGING) {
|
||||
NSLog(@"(localytics uploader) %s\n", [message UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark System Functions
|
||||
+ (id)allocWithZone:(NSZone *)zone {
|
||||
@synchronized(self) {
|
||||
if (_sharedUploaderThread == nil) {
|
||||
_sharedUploaderThread = [super allocWithZone:zone];
|
||||
return _sharedUploaderThread;
|
||||
}
|
||||
}
|
||||
// returns nil on subsequent allocations
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)retain {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (unsigned)retainCount {
|
||||
// maximum value of an unsigned int - prevents additional retains for the class
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
- (oneway void)release {
|
||||
// ignore release commands
|
||||
}
|
||||
|
||||
- (id)autorelease {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_uploadConnection release];
|
||||
[_sharedUploaderThread release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,111 +0,0 @@
|
||||
// WebserviceConstants.h
|
||||
// Copyright (C) 2009 Char Software Inc., DBA Localytics
|
||||
//
|
||||
// This code is provided under the Localytics Modified BSD License.
|
||||
// A copy of this license has been distributed in a file called LICENSE
|
||||
// with this source code.
|
||||
//
|
||||
// Please visit www.localytics.com for more information.
|
||||
|
||||
// The constants which are used to make up the JSON blob
|
||||
// To save disk space and network bandwidth all the keywords have been
|
||||
// abbreviated and are exploded by the server.
|
||||
|
||||
/*********************
|
||||
* Shared Attributes *
|
||||
*********************/
|
||||
#define PARAM_UUID @"u" // UUID for JSON document
|
||||
#define PARAM_DATA_TYPE @"dt" // Data Type
|
||||
#define PARAM_CLIENT_TIME @"ct" // Client Time, seconds from Unix epoch (int)
|
||||
#define PARAM_LATITUDE @"lat" // Latitude - if available
|
||||
#define PARAM_LONGITUDE @"lon" // Longitude - if available
|
||||
#define PARAM_SESSION_UUID @"su" // UUID for an existing session
|
||||
#define PARAM_NEW_SESSION_UUID @"u" // UUID for a new session
|
||||
#define PARAM_ATTRIBUTES @"attrs" // Attributes (dictionary)
|
||||
|
||||
/***************
|
||||
* Blob Header *
|
||||
***************/
|
||||
|
||||
// PARAM_UUID
|
||||
// PARAM_DATA_TYPE => "h" for Header
|
||||
// PARAM_ATTRIBUTES => dictionary containing Header Common Attributes
|
||||
#define PARAM_PERSISTED_AT @"pa" // Persistent Storage Created At. A timestamp created when the app was
|
||||
// first launched and the persistent storage was created. Stores as
|
||||
// seconds from Unix epoch. (int)
|
||||
#define PARAM_SEQUENCE_NUMBER @"seq" // Sequence number - an increasing count for each blob, stored in the
|
||||
// persistent store Consistent across app starts. (int)
|
||||
|
||||
/****************************
|
||||
* Header Common Attributes *
|
||||
****************************/
|
||||
|
||||
// PARAM_DATA_TYPE
|
||||
#define PARAM_APP_KEY @"au" // Localytics Application ID
|
||||
#define PARAM_DEVICE_UUID @"du" // Device UUID
|
||||
#define PARAM_DEVICE_UUID_HASHED @"udid" // Hashed version of the UUID
|
||||
#define PARAM_DEVICE_MAC @"wmac" // Hashed version of the device Mac
|
||||
#define PARAM_INSTALL_ID @"iu" // Install ID
|
||||
#define PARAM_JAILBROKEN @"j" // Jailbroken (boolean)
|
||||
#define PARAM_LIBRARY_VERSION @"lv" // Client Version
|
||||
#define PARAM_APP_VERSION @"av" // Application Version
|
||||
#define PARAM_DEVICE_PLATFORM @"dp" // Device Platform
|
||||
#define PARAM_LOCALE_LANGUAGE @"dll" // Locale Language
|
||||
#define PARAM_LOCALE_COUNTRY @"dlc" // Locale Country
|
||||
#define PARAM_NETWORK_COUNTRY @"nc" // Network Country (iso code) // ???: Never used on iPhone.
|
||||
#define PARAM_DEVICE_COUNTRY @"dc" // Device Country (iso code)
|
||||
#define PARAM_DEVICE_MANUFACTURER @"dma" // Device Manufacturer // ???: Never used on iPhone. Used to be "Device Make".
|
||||
#define PARAM_DEVICE_MODEL @"dmo" // Device Model
|
||||
#define PARAM_DEVICE_OS_VERSION @"dov" // Device OS Version
|
||||
#define PARAM_NETWORK_CARRIER @"nca" // Network Carrier
|
||||
#define PARAM_DATA_CONNECTION @"dac" // Data Connection Type // ???: Never used on iPhone.
|
||||
#define PARAM_OPT_VALUE @"optin" // Opt In (boolean)
|
||||
#define PARAM_DEVICE_MEMORY @"dmem" // Device Memory
|
||||
|
||||
/*****************
|
||||
* Session Start *
|
||||
*****************/
|
||||
|
||||
// PARAM_UUID
|
||||
// PARAM_DATA_TYPE => "s" for Start
|
||||
// PARAM_CLIENT_TIME
|
||||
#define PARAM_SESSION_NUMBER @"nth" // This is the nth session on the device, 1-indexed (int)
|
||||
|
||||
/****************
|
||||
* Session Stop *
|
||||
****************/
|
||||
|
||||
// PARAM_UUID
|
||||
// PARAM_DATA_TYPE => "c" for Close
|
||||
// PARAM_CLIENT_TIME
|
||||
// PARAM_LATITUDE
|
||||
// PARAM_LONGITUDE
|
||||
// PARAM_SESSION_UUID => UUID of session being closed
|
||||
#define PARAM_SESSION_ACTIVE @"cta" // Active time in seconds (time app was active)
|
||||
#define PARAM_SESSION_TOTAL @"ctl" // Total session length
|
||||
#define PARAM_SESSION_SCREENFLOW @"fl" // Screens encountered during this session, in order
|
||||
|
||||
/*********************
|
||||
* Application Event *
|
||||
*********************/
|
||||
|
||||
// PARAM_UUID
|
||||
// PARAM_DATA_TYPE => "e" for Event
|
||||
// PARAM_CLIENT_TIME
|
||||
// PARAM_LATITUDE
|
||||
// PARAM_LONGITUDE
|
||||
// PARAM_SESSION_UUID => UUID of session event occured in
|
||||
// PARAM_ATTRIBUTES => dictionary containing attributes for this event as key-value string pairs
|
||||
#define PARAM_EVENT_NAME @"n" // Event Name, (eg. 'Button Click')
|
||||
#define PARAM_REPORT_ATTRIBUTES @"rattrs" // Attributes used in custom reports
|
||||
|
||||
/********************
|
||||
* Application flow *
|
||||
********************/
|
||||
|
||||
// PARAM_UUID
|
||||
// PARAM_DATA_TYPE => "f" for Flow
|
||||
// PARAM_CLIENT_TIME
|
||||
#define PARAM_SESSION_START @"ss" // Start time for the current session.
|
||||
#define PARAM_NEW_FLOW_EVENTS @"nw" // Events and screens encountered during this session that have NOT been staged for upload.
|
||||
#define PARAM_OLD_FLOW_EVENTS @"od" // Events and screens encountered during this session that HAVE been staged for upload.
|
||||
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 67 KiB |
10
MasterPassword.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>1DC75A27-0030-4493-ACE8-E1D49AB9A549</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>MasterPassword</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA</key>
|
||||
<string>git://github.com/lhunath/InAppSettingsKit.git</string>
|
||||
<key>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</key>
|
||||
<string>git://github.com/Lyndir/love-lyndir.client.git</string>
|
||||
<key>2A70319CE0F91B35406CA7D970AE7CB4957B0A75</key>
|
||||
<string>github.com:Lyndir/Lyndir.git</string>
|
||||
<key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key>
|
||||
<string>git://github.com/lhunath/uicolor-utilities.git</string>
|
||||
<key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key>
|
||||
<string>git://github.com/lhunath/RHStatusItemView.git</string>
|
||||
<key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key>
|
||||
<string>ssh://github.com/Lyndir/Pearl.git</string>
|
||||
<key>8A15A8EA0B3D0B497C4883425BC74DF995224BB3</key>
|
||||
<string>git://github.com/jonmarimba/jrswizzle.git</string>
|
||||
<key>E47DEC29CB0D0FDE3560EF46E1808FA1C723D657</key>
|
||||
<string>git://github.com/lhunath/UbiquityStoreManager.git</string>
|
||||
<key>F788B28042EDBEF29EFE34687DA79A778C2CC260</key>
|
||||
<string>ssh://github.com/Lyndir/MasterPassword.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>MasterPassword.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA</key>
|
||||
<string>../External/InAppSettingsKit</string>
|
||||
<key>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</key>
|
||||
<string>../External/LoveLyndir</string>
|
||||
<key>2A70319CE0F91B35406CA7D970AE7CB4957B0A75</key>
|
||||
<string>../..</string>
|
||||
<key>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</key>
|
||||
<string>../External/Pearl/External/uicolor-utilities</string>
|
||||
<key>3E67FB08419C920516AAC3B00DAAF23073B8CF77</key>
|
||||
<string>../External/RHStatusItemView</string>
|
||||
<key>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</key>
|
||||
<string>../External/Pearl</string>
|
||||
<key>8A15A8EA0B3D0B497C4883425BC74DF995224BB3</key>
|
||||
<string>../External/Pearl/External/jrswizzle</string>
|
||||
<key>E47DEC29CB0D0FDE3560EF46E1808FA1C723D657</key>
|
||||
<string>../External/UbiquityStoreManager</string>
|
||||
<key>F788B28042EDBEF29EFE34687DA79A778C2CC260</key>
|
||||
<string>..</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>ssh://github.com/Lyndir/MasterPassword.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>F788B28042EDBEF29EFE34687DA79A778C2CC260</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>2A70319CE0F91B35406CA7D970AE7CB4957B0A75</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>1712FC0BC3C9AABD8B7B5376E310E93FBDB3BCFA</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>InAppSettingsKit</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>8A15A8EA0B3D0B497C4883425BC74DF995224BB3</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>jrswizzle</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>1AA8C0BE-EEC3-4FBC-A801-8939A1AC093A</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>LoveLyndir</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>F788B28042EDBEF29EFE34687DA79A778C2CC260</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>MasterPassword</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>4DDCFFD91B41F00326AD14553BD66CFD366ABD91</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>Pearl</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>3E67FB08419C920516AAC3B00DAAF23073B8CF77</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>RHStatusItemView</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>E47DEC29CB0D0FDE3560EF46E1808FA1C723D657</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>UbiquityStoreManager</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>2FE140B36B7D26140DC8D5E5C639DC5900EFCF35</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>uicolor-utilities</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
1920
MasterPassword/C/bashlib
Executable file
9
MasterPassword/C/build
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run with -DDEBUG to enable trace-level output.
|
||||
|
||||
[[ -e lib/scrypt/scryptenc.o ]] || { echo >&2 "Missing scrypt. First get and build the scrypt source in lib/scrypt from <$(<lib/scrypt/.source)>.\n"; exit 1; }
|
||||
|
||||
deps=( -I"lib/scrypt/lib" -I"lib/scrypt/libcperciva" -l "crypto_aesctr.o" -l "sha256.o" -l "crypto_scrypt-nosse.o" -l "memlimit.o" -l "scryptenc_cpuperf.o" -l"scryptenc.o" -l"crypto" -L"." -L"lib/scrypt" )
|
||||
|
||||
gcc "${deps[@]}" -Qunused-arguments -c types.c -o types.o "$@"
|
||||
gcc "${deps[@]}" -Qunused-arguments -l"types.o" mpw.c -o mpw "$@"
|
||||
53
MasterPassword/C/install
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Install the Master Password CLI tool.
|
||||
set -e
|
||||
cd "${BASH_SOURCE%/*}"
|
||||
source bashlib
|
||||
|
||||
inf "This will install the mpw tool."
|
||||
|
||||
# Try to guess then ask for the bin dir to install to.
|
||||
IFS=: read -a paths <<< "$PATH"
|
||||
if inArray ~/bin "${paths[@]}"; then
|
||||
bindir=~/bin
|
||||
elif inArray ~/.bin "${paths[@]}"; then
|
||||
bindir=~/.bin
|
||||
elif inArray /usr/local/bin "${paths[@]}"; then
|
||||
bindir=/usr/local/bin
|
||||
else
|
||||
bindir=~/bin
|
||||
fi
|
||||
bindir=$(ask -d "$bindir" "What bin directory should I install to?")
|
||||
[[ -d "$bindir" ]] || mkdir "$bindir" || ftl 'Cannot create missing bin directory: %s' "$bindir" || exit
|
||||
[[ -w "$bindir" ]] || ftl 'Cannot write to bin directory: %s' "$bindir" || exit
|
||||
|
||||
# Install Master Password.
|
||||
install mpw "$bindir"
|
||||
[[ ! -e "$bindir/bashlib" ]] && install bashlib "$bindir" ||:
|
||||
|
||||
# Convenience bash function.
|
||||
inf "Installation successful!"
|
||||
echo
|
||||
|
||||
inf "To improve usability, you can install an mpw function in your bash shell."
|
||||
inf "This function adds the following features:"
|
||||
inf " - Automatically remember your user name in the shell if not set."
|
||||
inf " - Automatically put the password in the clipboard (some platforms)."
|
||||
echo
|
||||
inf "To do this you need the following function in ~/.bashrc:\n%s" "$(<mpw.bashrc)"
|
||||
echo
|
||||
inf "We can do this for you automatically now."
|
||||
if ask -c Y!n "Append the mpw function to your .bashrc?"; then
|
||||
cat mpw.bashrc >> ~/.bashrc
|
||||
inf "Done! Don't forget to run '%s' to apply the changes!" "source ~/.bashrc"
|
||||
fi
|
||||
echo
|
||||
|
||||
inf "You can also save your user name in ~/.bashrc. Leave blank to skip this step."
|
||||
if MP_USERNAME=$(ask "Your full name:") && [[ $MP_USERNAME ]] ; then
|
||||
printf 'export MP_USERNAME=%q\n' "$MP_USERNAME" >> ~/.bashrc
|
||||
fi
|
||||
echo
|
||||
|
||||
inf "To begin using Master Password, type: mpw [site name]"
|
||||
38
MasterPassword/C/lib/scrypt-osx.patch
Normal file
@@ -0,0 +1,38 @@
|
||||
diff -ruN /Users/lhunath/.src/scrypt/Makefile ./Makefile
|
||||
--- /Users/lhunath/.src/scrypt/Makefile 2014-05-02 11:28:58.000000000 -0400
|
||||
+++ ./Makefile 2014-05-02 12:07:27.000000000 -0400
|
||||
@@ -2,11 +2,11 @@
|
||||
VER?= nosse
|
||||
SRCS= main.c
|
||||
LDADD+= -lcrypto
|
||||
-WARNS?= 6
|
||||
+WARNS?= 0
|
||||
|
||||
# We have a config file for FreeBSD
|
||||
CFLAGS += -I .
|
||||
-CFLAGS += -DCONFIG_H_FILE=\"config_freebsd.h\"
|
||||
+CFLAGS += -DCONFIG_H_FILE=\"config_osx.h\"
|
||||
|
||||
# Include all possible object files containing built scrypt code.
|
||||
CLEANFILES += crypto_scrypt-ref.o
|
||||
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
|
||||
--- /Users/lhunath/.src/scrypt/lib/util/memlimit.c 2014-05-02 11:28:58.000000000 -0400
|
||||
+++ ./lib/util/memlimit.c 2014-05-02 11:52:42.000000000 -0400
|
||||
@@ -75,7 +75,7 @@
|
||||
* have returned to us.
|
||||
*/
|
||||
if (usermemlen == sizeof(uint64_t))
|
||||
- usermem = *(uint64_t *)usermembuf;
|
||||
+ usermem = *(uint64_t *)(void *)usermembuf;
|
||||
else if (usermemlen == sizeof(uint32_t))
|
||||
usermem = SIZE_MAX;
|
||||
else
|
||||
diff -ruN /Users/lhunath/.src/scrypt/lib/util/memlimit.c ./lib/util/memlimit.c
|
||||
--- /Users/lhunath/.src/scrypt/config_osx.h 1969-12-31 19:00:00.000000000 -0500
|
||||
+++ config_osx.h 2014-05-02 12:06:55.000000000 -0400
|
||||
@@ -0,0 +1,5 @@
|
||||
+/* A default configuration for FreeBSD, used if there is no config.h. */
|
||||
+
|
||||
+#define HAVE_POSIX_MEMALIGN 1
|
||||
+#define HAVE_SYSCTL_HW_USERMEM 1
|
||||
+#define HAVE_SYS_PARAM_H 1
|
||||
1
MasterPassword/C/lib/scrypt/.source
Normal file
@@ -0,0 +1 @@
|
||||
https://code.google.com/p/scrypt/
|
||||
BIN
MasterPassword/C/masterpassword-cli-freebsd.zip
Normal file
BIN
MasterPassword/C/masterpassword-cli-osx.zip
Normal file
24
MasterPassword/C/mpw.bashrc
Normal file
@@ -0,0 +1,24 @@
|
||||
## Added by Master Password
|
||||
source bashlib
|
||||
mpw() {
|
||||
_copy() {
|
||||
if hash pbcopy 2>/dev/null; then
|
||||
pbcopy
|
||||
elif hash xclip 2>/dev/null; then
|
||||
xclip
|
||||
else
|
||||
cat; echo 2>/dev/null
|
||||
return
|
||||
fi
|
||||
echo >&2 "Copied!"
|
||||
}
|
||||
|
||||
# Empty the clipboard
|
||||
:| _copy 2>/dev/null
|
||||
|
||||
# Ask for the user's name and password if not yet known.
|
||||
MP_USERNAME=${MP_USERNAME:-$(ask 'Your Full Name:')}
|
||||
|
||||
# Start Master Password and copy the output.
|
||||
printf %s "$(MP_USERNAME=$MP_USERNAME command mpw "$@")" | _copy
|
||||
}
|
||||
275
MasterPassword/C/mpw.c
Normal file
@@ -0,0 +1,275 @@
|
||||
#define _WITH_GETLINE
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#if defined(__linux__)
|
||||
#include <linux/fs.h>
|
||||
#elif defined(__CYGWIN__)
|
||||
#include <cygwin/fs.h>
|
||||
#else
|
||||
#include <sys/disk.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <pwd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <alg/sha256.h>
|
||||
#include <crypto/crypto_scrypt.h>
|
||||
#include "types.h"
|
||||
|
||||
#define MP_N 32768
|
||||
#define MP_r 8
|
||||
#define MP_p 2
|
||||
#define MP_dkLen 64
|
||||
#define MP_hash PearlHashSHA256
|
||||
|
||||
#define MP_env_username "MP_USERNAME"
|
||||
#define MP_env_sitetype "MP_SITETYPE"
|
||||
#define MP_env_sitecounter "MP_SITECOUNTER"
|
||||
|
||||
void usage() {
|
||||
fprintf(stderr, "Usage: mpw [-u name] [-t type] [-c counter] site\n\n");
|
||||
fprintf(stderr, " -u name Specify the full name of the user.\n"
|
||||
" Defaults to %s in env.\n\n", MP_env_username);
|
||||
fprintf(stderr, " -t type Specify the password's template.\n"
|
||||
" Defaults to %s in env or 'long' for password, 'name' for login.\n"
|
||||
" x, max, maximum | 20 characters, contains symbols.\n"
|
||||
" l, long | Copy-friendly, 14 characters, contains symbols.\n"
|
||||
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
|
||||
" b, basic | 8 characters, no symbols.\n"
|
||||
" s, short | Copy-friendly, 4 characters, no symbols.\n"
|
||||
" p, pin | 4 numbers.\n"
|
||||
" n, name | 9 letter name.\n\n", MP_env_sitetype);
|
||||
fprintf(stderr, " -c counter The value of the counter.\n"
|
||||
" Defaults to %s in env or '1'.\n\n", MP_env_sitecounter);
|
||||
fprintf(stderr, " -v variant The kind of content to generate.\n"
|
||||
" Defaults to 'password'.\n"
|
||||
" p, password | The password to log in with.\n"
|
||||
" l, login | The username to log in as.\n\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
char *homedir(const char *filename) {
|
||||
char *homedir = NULL;
|
||||
#if defined(__CYGWIN__)
|
||||
homedir = getenv("USERPROFILE");
|
||||
if (!homedir) {
|
||||
const char *homeDrive = getenv("HOMEDRIVE");
|
||||
const char *homePath = getenv("HOMEPATH");
|
||||
homedir = char[strlen(homeDrive) + strlen(homePath) + 1];
|
||||
sprintf(homedir, "%s/%s", homeDrive, homePath);
|
||||
}
|
||||
#else
|
||||
struct passwd* passwd = getpwuid(getuid());
|
||||
if (passwd)
|
||||
homedir = passwd->pw_dir;
|
||||
if (!homedir)
|
||||
homedir = getenv("HOME");
|
||||
#endif
|
||||
if (!homedir)
|
||||
homedir = getcwd(NULL, 0);
|
||||
|
||||
char *homefile = NULL;
|
||||
asprintf(&homefile, "%s/%s", homedir, filename);
|
||||
return homefile;
|
||||
}
|
||||
|
||||
int main(int argc, char *const argv[]) {
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
// Read the environment.
|
||||
const char *userName = getenv( MP_env_username );
|
||||
const char *masterPassword = NULL;
|
||||
const char *siteName = NULL;
|
||||
MPElementType siteType = MPElementTypeGeneratedLong;
|
||||
const char *siteTypeString = getenv( MP_env_sitetype );
|
||||
MPElementVariant siteVariant = MPElementVariantPassword;
|
||||
const char *siteVariantString = NULL;
|
||||
uint32_t siteCounter = 1;
|
||||
const char *siteCounterString = getenv( MP_env_sitecounter );
|
||||
|
||||
// Read the options.
|
||||
char opt;
|
||||
while ((opt = getopt(argc, argv, "u:t:c:v:h")) != -1)
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
usage();
|
||||
break;
|
||||
case 'u':
|
||||
userName = optarg;
|
||||
break;
|
||||
case 't':
|
||||
siteTypeString = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
siteVariantString = optarg;
|
||||
break;
|
||||
case 'c':
|
||||
siteCounterString = optarg;
|
||||
break;
|
||||
case '?':
|
||||
switch (optopt) {
|
||||
case 'u':
|
||||
fprintf(stderr, "Missing user name to option: -%c\n", optopt);
|
||||
break;
|
||||
case 't':
|
||||
fprintf(stderr, "Missing type name to option: -%c\n", optopt);
|
||||
break;
|
||||
case 'c':
|
||||
fprintf(stderr, "Missing counter value to option: -%c\n", optopt);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown option: -%c\n", optopt);
|
||||
}
|
||||
return 1;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if (optind < argc)
|
||||
siteName = argv[optind];
|
||||
|
||||
// Convert and validate input.
|
||||
if (!userName) {
|
||||
fprintf(stderr, "Missing user name.\n");
|
||||
return 1;
|
||||
}
|
||||
trc("userName: %s\n", userName);
|
||||
if (!siteName) {
|
||||
fprintf(stderr, "Missing site name.\n");
|
||||
return 1;
|
||||
}
|
||||
trc("siteName: %s\n", siteName);
|
||||
if (siteCounterString)
|
||||
siteCounter = atoi( siteCounterString );
|
||||
if (siteCounter < 1) {
|
||||
fprintf(stderr, "Invalid site counter: %d\n", siteCounter);
|
||||
return 1;
|
||||
}
|
||||
trc("siteCounter: %d\n", siteCounter);
|
||||
if (siteVariantString)
|
||||
siteVariant = VariantWithName( siteVariantString );
|
||||
trc("siteVariant: %d (%s)\n", siteVariant, siteVariantString);
|
||||
if (siteVariant == MPElementVariantLogin)
|
||||
siteType = MPElementTypeGeneratedName;
|
||||
if (siteTypeString)
|
||||
siteType = TypeWithName( siteTypeString );
|
||||
trc("siteType: %d (%s)\n", siteType, siteTypeString);
|
||||
|
||||
// Read the master password.
|
||||
char *mpwConfigPath = homedir(".mpw");
|
||||
if (!mpwConfigPath) {
|
||||
fprintf(stderr, "Couldn't resolve path for configuration file: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
trc("mpwConfigPath: %s\n", mpwConfigPath);
|
||||
FILE *mpwConfig = fopen(mpwConfigPath, "r");
|
||||
if (!mpwConfig) {
|
||||
fprintf(stderr, "Couldn't open configuration file: %s: %d\n", mpwConfigPath, errno);
|
||||
return 1;
|
||||
}
|
||||
free(mpwConfigPath);
|
||||
char *line = NULL;
|
||||
size_t linecap = 0;
|
||||
ssize_t linelen;
|
||||
while ((linelen = getline(&line, &linecap, mpwConfig)) > 0)
|
||||
if (strcmp(strsep(&line, ":"), userName) == 0) {
|
||||
masterPassword = strsep(&line, "\n");
|
||||
break;
|
||||
}
|
||||
if (!masterPassword) {
|
||||
fprintf(stderr, "Missing master password for user: %s\n", userName);
|
||||
return 1;
|
||||
}
|
||||
trc("masterPassword: %s\n", masterPassword);
|
||||
|
||||
// Calculate the master key salt.
|
||||
const char *mpKeyScope = ScopeForVariant(MPElementVariantPassword);
|
||||
trc("key scope: %s\n", mpKeyScope);
|
||||
const uint32_t n_userNameLength = htonl(strlen(userName));
|
||||
size_t masterKeySaltLength = strlen(mpKeyScope) + sizeof(n_userNameLength) + strlen(userName);
|
||||
char *masterKeySalt = malloc( masterKeySaltLength );
|
||||
if (!masterKeySalt) {
|
||||
fprintf(stderr, "Could not allocate master key salt: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *mKS = masterKeySalt;
|
||||
memcpy(mKS, mpKeyScope, strlen(mpKeyScope)); mKS += strlen(mpKeyScope);
|
||||
memcpy(mKS, &n_userNameLength, sizeof(n_userNameLength)); mKS += sizeof(n_userNameLength);
|
||||
memcpy(mKS, userName, strlen(userName)); mKS += strlen(userName);
|
||||
if (mKS - masterKeySalt != masterKeySaltLength)
|
||||
abort();
|
||||
trc("masterKeySalt ID: %s\n", IDForBuf(masterKeySalt, masterKeySaltLength));
|
||||
|
||||
// Calculate the master key.
|
||||
uint8_t *masterKey = malloc( MP_dkLen );
|
||||
if (!masterKey) {
|
||||
fprintf(stderr, "Could not allocate master key: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
if (crypto_scrypt( (const uint8_t *)masterPassword, strlen(masterPassword), (const uint8_t *)masterKeySalt, masterKeySaltLength, MP_N, MP_r, MP_p, masterKey, MP_dkLen ) < 0) {
|
||||
fprintf(stderr, "Could not generate master key: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
memset(masterKeySalt, 0, masterKeySaltLength);
|
||||
free(masterKeySalt);
|
||||
trc("masterPassword Hex: %s\n", Hex(masterPassword, strlen(masterPassword)));
|
||||
trc("masterPassword ID: %s\n", IDForBuf(masterPassword, strlen(masterPassword)));
|
||||
trc("masterKey ID: %s\n", IDForBuf(masterKey, MP_dkLen));
|
||||
|
||||
// Calculate the site seed.
|
||||
const char *mpSiteScope = ScopeForVariant(siteVariant);
|
||||
trc("site scope: %s\n", mpSiteScope);
|
||||
const uint32_t n_siteNameLength = htonl(strlen(siteName));
|
||||
const uint32_t n_siteCounter = htonl(siteCounter);
|
||||
size_t sitePasswordInfoLength = strlen(mpSiteScope) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter);
|
||||
char *sitePasswordInfo = malloc( sitePasswordInfoLength );
|
||||
if (!sitePasswordInfo) {
|
||||
fprintf(stderr, "Could not allocate site seed: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *sPI = sitePasswordInfo;
|
||||
memcpy(sPI, mpSiteScope, strlen(mpSiteScope)); sPI += strlen(mpSiteScope);
|
||||
memcpy(sPI, &n_siteNameLength, sizeof(n_siteNameLength)); sPI += sizeof(n_siteNameLength);
|
||||
memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName);
|
||||
memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter);
|
||||
if (sPI - sitePasswordInfo != sitePasswordInfoLength)
|
||||
abort();
|
||||
trc("seed from: hmac-sha256(masterKey, %s | %s | %s | %s)\n", mpSiteScope, Hex(&n_siteNameLength, sizeof(n_siteNameLength)), siteName, Hex(&n_siteCounter, sizeof(n_siteCounter)));
|
||||
trc("sitePasswordInfo ID: %s\n", IDForBuf(sitePasswordInfo, sitePasswordInfoLength));
|
||||
|
||||
uint8_t sitePasswordSeed[32];
|
||||
HMAC_SHA256_Buf(masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoLength, sitePasswordSeed);
|
||||
memset(masterKey, 0, MP_dkLen);
|
||||
memset(sitePasswordInfo, 0, sitePasswordInfoLength);
|
||||
free(masterKey);
|
||||
free(sitePasswordInfo);
|
||||
trc("sitePasswordSeed ID: %s\n", IDForBuf(sitePasswordSeed, 32));
|
||||
|
||||
// Determine the cipher.
|
||||
const char *cipher = CipherForType(siteType, sitePasswordSeed[0]);
|
||||
trc("type %s, cipher: %s\n", siteTypeString, cipher);
|
||||
if (strlen(cipher) > 32)
|
||||
abort();
|
||||
|
||||
// Encode the password from the seed using the cipher.
|
||||
char *sitePassword = calloc(strlen(cipher) + 1, sizeof(char));
|
||||
for (int c = 0; c < strlen(cipher); ++c) {
|
||||
sitePassword[c] = CharacterFromClass(cipher[c], sitePasswordSeed[c + 1]);
|
||||
trc("class %c, character: %c\n", cipher[c], sitePassword[c]);
|
||||
}
|
||||
memset(sitePasswordSeed, 0, sizeof(sitePasswordSeed));
|
||||
|
||||
// Output the password.
|
||||
fprintf( stdout, "%s\n", sitePassword );
|
||||
return 0;
|
||||
}
|
||||
175
MasterPassword/C/types.c
Normal file
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// MPTypes.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <alg/sha256.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
const MPElementType TypeWithName(const char *typeName) {
|
||||
char lowerTypeName[strlen(typeName)];
|
||||
strcpy(lowerTypeName, typeName);
|
||||
for (char *tN = lowerTypeName; *tN; ++tN)
|
||||
*tN = tolower(*tN);
|
||||
|
||||
if (0 == strcmp(lowerTypeName, "x") || 0 == strcmp(lowerTypeName, "max") || 0 == strcmp(lowerTypeName, "maximum"))
|
||||
return MPElementTypeGeneratedMaximum;
|
||||
if (0 == strcmp(lowerTypeName, "l") || 0 == strcmp(lowerTypeName, "long"))
|
||||
return MPElementTypeGeneratedLong;
|
||||
if (0 == strcmp(lowerTypeName, "m") || 0 == strcmp(lowerTypeName, "med") || 0 == strcmp(lowerTypeName, "medium"))
|
||||
return MPElementTypeGeneratedMedium;
|
||||
if (0 == strcmp(lowerTypeName, "b") || 0 == strcmp(lowerTypeName, "basic"))
|
||||
return MPElementTypeGeneratedBasic;
|
||||
if (0 == strcmp(lowerTypeName, "s") || 0 == strcmp(lowerTypeName, "short"))
|
||||
return MPElementTypeGeneratedShort;
|
||||
if (0 == strcmp(lowerTypeName, "p") || 0 == strcmp(lowerTypeName, "pin"))
|
||||
return MPElementTypeGeneratedPIN;
|
||||
if (0 == strcmp(lowerTypeName, "n") || 0 == strcmp(lowerTypeName, "name"))
|
||||
return MPElementTypeGeneratedName;
|
||||
|
||||
fprintf(stderr, "Not a generated type name: %s", lowerTypeName);
|
||||
abort();
|
||||
}
|
||||
|
||||
const char *CipherForType(MPElementType type, uint8_t seedByte) {
|
||||
if (!(type & MPElementTypeClassGenerated)) {
|
||||
fprintf(stderr, "Not a generated type: %d", type);
|
||||
abort();
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case MPElementTypeGeneratedMaximum: {
|
||||
char *ciphers[] = { "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
|
||||
return ciphers[seedByte % 2];
|
||||
}
|
||||
case MPElementTypeGeneratedLong: {
|
||||
char *ciphers[] = { "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno", "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno", "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno", "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno", "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno", "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno", "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" };
|
||||
return ciphers[seedByte % 21];
|
||||
}
|
||||
case MPElementTypeGeneratedMedium: {
|
||||
char *ciphers[] = { "CvcnoCvc", "CvcCvcno" };
|
||||
return ciphers[seedByte % 2];
|
||||
}
|
||||
case MPElementTypeGeneratedBasic: {
|
||||
char *ciphers[] = { "aaanaaan", "aannaaan", "aaannaaa" };
|
||||
return ciphers[seedByte % 3];
|
||||
}
|
||||
case MPElementTypeGeneratedShort: {
|
||||
return "Cvcn";
|
||||
}
|
||||
case MPElementTypeGeneratedPIN: {
|
||||
return "nnnn";
|
||||
}
|
||||
case MPElementTypeGeneratedName: {
|
||||
return "cvccvcvcv";
|
||||
}
|
||||
default: {
|
||||
fprintf(stderr, "Unknown generated type: %d", type);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MPElementVariant VariantWithName(const char *variantName) {
|
||||
char lowerVariantName[strlen(variantName)];
|
||||
strcpy(lowerVariantName, variantName);
|
||||
for (char *vN = lowerVariantName; *vN; ++vN)
|
||||
*vN = tolower(*vN);
|
||||
|
||||
if (0 == strcmp(lowerVariantName, "p") || 0 == strcmp(lowerVariantName, "password"))
|
||||
return MPElementVariantPassword;
|
||||
if (0 == strcmp(lowerVariantName, "l") || 0 == strcmp(lowerVariantName, "login"))
|
||||
return MPElementVariantLogin;
|
||||
|
||||
fprintf(stderr, "Not a variant name: %s", lowerVariantName);
|
||||
abort();
|
||||
}
|
||||
|
||||
const char *ScopeForVariant(MPElementVariant variant) {
|
||||
switch (variant) {
|
||||
case MPElementVariantPassword: {
|
||||
return "com.lyndir.masterpassword";
|
||||
}
|
||||
case MPElementVariantLogin: {
|
||||
return "com.lyndir.masterpassword.login";
|
||||
}
|
||||
default: {
|
||||
fprintf(stderr, "Unknown variant: %d", variant);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char CharacterFromClass(char characterClass, uint8_t seedByte) {
|
||||
const char *classCharacters;
|
||||
switch (characterClass) {
|
||||
case 'V': {
|
||||
classCharacters = "AEIOU";
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
classCharacters = "BCDFGHJKLMNPQRSTVWXYZ";
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
classCharacters = "aeiou";
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
classCharacters = "bcdfghjklmnpqrstvwxyz";
|
||||
break;
|
||||
}
|
||||
case 'A': {
|
||||
classCharacters = "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
|
||||
break;
|
||||
}
|
||||
case 'a': {
|
||||
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
|
||||
break;
|
||||
}
|
||||
case 'n': {
|
||||
classCharacters = "0123456789";
|
||||
break;
|
||||
}
|
||||
case 'o': {
|
||||
classCharacters = "@&%?,=[]_:-+*$#!'^~;()/.";
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
fprintf(stderr, "Unknown character class: %c", characterClass);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
return classCharacters[seedByte % strlen(classCharacters)];
|
||||
}
|
||||
const char *IDForBuf(const void *buf, size_t length) {
|
||||
uint8_t hash[32];
|
||||
SHA256_Buf(buf, length, hash);
|
||||
|
||||
char *id = calloc(65, sizeof(char));
|
||||
for (int kH = 0; kH < 32; kH++)
|
||||
sprintf(&(id[kH * 2]), "%02X", hash[kH]);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
const char *Hex(const void *buf, size_t length) {
|
||||
char *id = calloc(length*2+1, sizeof(char));
|
||||
for (int kH = 0; kH < length; kH++)
|
||||
sprintf(&(id[kH * 2]), "%02X", ((const uint8_t*)buf)[kH]);
|
||||
return id;
|
||||
}
|
||||
56
MasterPassword/C/types.h
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// MPTypes.h
|
||||
// MasterPassword
|
||||
//
|
||||
// Created by Maarten Billemont on 02/01/12.
|
||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
||||
//
|
||||
|
||||
typedef enum {
|
||||
/** Generate the password to log in with. */
|
||||
MPElementVariantPassword,
|
||||
/** Generate the login name to log in as. */
|
||||
MPElementVariantLogin,
|
||||
} MPElementVariant;
|
||||
|
||||
typedef enum {
|
||||
/** Generate the password. */
|
||||
MPElementTypeClassGenerated = 1 << 4,
|
||||
/** Store the password. */
|
||||
MPElementTypeClassStored = 1 << 5,
|
||||
} MPElementTypeClass;
|
||||
|
||||
typedef enum {
|
||||
/** Export the key-protected content data. */
|
||||
MPElementFeatureExportContent = 1 << 10,
|
||||
/** Never export content. */
|
||||
MPElementFeatureDevicePrivate = 1 << 11,
|
||||
} MPElementFeature;
|
||||
|
||||
typedef enum {
|
||||
MPElementTypeGeneratedMaximum = 0x0 | MPElementTypeClassGenerated | 0x0,
|
||||
MPElementTypeGeneratedLong = 0x1 | MPElementTypeClassGenerated | 0x0,
|
||||
MPElementTypeGeneratedMedium = 0x2 | MPElementTypeClassGenerated | 0x0,
|
||||
MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0,
|
||||
MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0,
|
||||
MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0,
|
||||
MPElementTypeGeneratedName = 0xF | MPElementTypeClassGenerated | 0x0,
|
||||
|
||||
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
|
||||
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
|
||||
} MPElementType;
|
||||
|
||||
#ifdef DEBUG
|
||||
#define trc(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define trc(...) do {} while (0)
|
||||
#endif
|
||||
|
||||
const MPElementVariant VariantWithName(const char *variantName);
|
||||
const char *ScopeForVariant(MPElementVariant variant);
|
||||
const MPElementType TypeWithName(const char *typeName);
|
||||
const char *CipherForType(MPElementType type, uint8_t seedByte);
|
||||
const char CharacterFromClass(char characterClass, uint8_t seedByte);
|
||||
const char *IDForBuf(const void *buf, size_t length);
|
||||
const char *Hex(const void *buf, size_t length);
|
||||
|
||||
0
MasterPassword/Java/.mvn-tools
Normal file
49
MasterPassword/Java/masterpassword-algorithm/pom.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- PROJECT METADATA -->
|
||||
<parent>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Master Password Algorithm Implementation</name>
|
||||
<description>The implementation of the Master Password algorithm</description>
|
||||
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword-algorithm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<!-- DEPENDENCY MANAGEMENT -->
|
||||
<dependencies>
|
||||
|
||||
<!-- PROJECT REFERENCES -->
|
||||
<dependency>
|
||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||
<artifactId>opal-system</artifactId>
|
||||
<version>1.6-p6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||
<artifactId>opal-crypto</artifactId>
|
||||
<version>1.6-p6</version>
|
||||
</dependency>
|
||||
|
||||
<!-- EXTERNAL DEPENDENCIES -->
|
||||
<dependency>
|
||||
<groupId>net.sf.plist</groupId>
|
||||
<artifactId>property-list</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lambdaworks</groupId>
|
||||
<artifactId>scrypt</artifactId>
|
||||
<version>1.4.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public enum MPElementFeature {
|
||||
|
||||
/** Export the key-protected content data. */
|
||||
ExportContent,
|
||||
/** Never export content. */
|
||||
DevicePrivate,
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public enum MPElementType {
|
||||
|
||||
GeneratedMaximum( "Maximum Security Password", "Maximum", "20 characters, contains symbols.", MPElementTypeClass.Generated ),
|
||||
GeneratedLong( "Long Password", "Long", "Copy-friendly, 14 characters, contains symbols.", MPElementTypeClass.Generated ),
|
||||
GeneratedMedium( "Medium Password", "Medium", "Copy-friendly, 8 characters, contains symbols.", MPElementTypeClass.Generated ),
|
||||
GeneratedBasic( "Basic Password", "Basic", "8 characters, no symbols.", MPElementTypeClass.Generated ),
|
||||
GeneratedShort( "Short Password", "Short", "Copy-friendly, 4 characters, no symbols.", MPElementTypeClass.Generated ),
|
||||
GeneratedPIN( "PIN", "PIN", "4 numbers.", MPElementTypeClass.Generated ),
|
||||
|
||||
StoredPersonal( "Personal Password", "Personal", "AES-encrypted, exportable.", MPElementTypeClass.Stored,
|
||||
MPElementFeature.ExportContent ),
|
||||
StoredDevicePrivate( "Device Private Password", "Private", "AES-encrypted, not exported.", MPElementTypeClass.Stored,
|
||||
MPElementFeature.DevicePrivate );
|
||||
|
||||
static final Logger logger = Logger.get( MPElementType.class );
|
||||
|
||||
private final MPElementTypeClass typeClass;
|
||||
private final Set<MPElementFeature> typeFeatures;
|
||||
private final String name;
|
||||
private final String shortName;
|
||||
private final String description;
|
||||
|
||||
MPElementType(final String name, final String shortName, final String description, final MPElementTypeClass typeClass,
|
||||
final MPElementFeature... typeFeatures) {
|
||||
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
this.typeClass = typeClass;
|
||||
this.description = description;
|
||||
|
||||
ImmutableSet.Builder<MPElementFeature> typeFeaturesBuilder = ImmutableSet.builder();
|
||||
for (final MPElementFeature typeFeature : typeFeatures) {
|
||||
typeFeaturesBuilder.add( typeFeature );
|
||||
}
|
||||
this.typeFeatures = typeFeaturesBuilder.build();
|
||||
}
|
||||
|
||||
public MPElementTypeClass getTypeClass() {
|
||||
|
||||
return typeClass;
|
||||
}
|
||||
|
||||
public Set<MPElementFeature> getTypeFeatures() {
|
||||
|
||||
return typeFeatures;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name The full or short name of the type we want to look up. It is matched case insensitively.
|
||||
*
|
||||
* @return The type with the given name.
|
||||
*/
|
||||
public static MPElementType forName(final String name) {
|
||||
|
||||
for (final MPElementType type : values())
|
||||
if (type.getName().equalsIgnoreCase( name ) || type.getShortName().equalsIgnoreCase( name ))
|
||||
return type;
|
||||
|
||||
throw logger.bug( "Element type not known: %s", name );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param typeClass The class for which we look up types.
|
||||
*
|
||||
* @return All types that support the given class.
|
||||
*/
|
||||
public static ImmutableList<MPElementType> forClass(final MPElementTypeClass typeClass) {
|
||||
|
||||
ImmutableList.Builder<MPElementType> types = ImmutableList.builder();
|
||||
for (final MPElementType type : values())
|
||||
if (type.getTypeClass() == typeClass)
|
||||
types.add( type );
|
||||
|
||||
return types.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.lyndir.masterpassword.entity.*;
|
||||
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public enum MPElementTypeClass {
|
||||
|
||||
Generated(MPElementGeneratedEntity.class),
|
||||
Stored(MPElementStoredEntity.class);
|
||||
|
||||
private final Class<? extends MPElementEntity> entityClass;
|
||||
|
||||
MPElementTypeClass(final Class<? extends MPElementEntity> entityClass) {
|
||||
|
||||
this.entityClass = entityClass;
|
||||
}
|
||||
|
||||
public Class<? extends MPElementEntity> getEntityClass() {
|
||||
|
||||
return entityClass;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public class MPTemplate extends MetaObject {
|
||||
|
||||
private final List<MPTemplateCharacterClass> template;
|
||||
|
||||
public MPTemplate(final String template, final Map<Character, MPTemplateCharacterClass> characterClasses) {
|
||||
|
||||
ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.<MPTemplateCharacterClass>builder();
|
||||
for (int i = 0; i < template.length(); ++i)
|
||||
builder.add( characterClasses.get( template.charAt( i ) ) );
|
||||
|
||||
this.template = builder.build();
|
||||
}
|
||||
|
||||
public MPTemplate(final List<MPTemplateCharacterClass> template) {
|
||||
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) {
|
||||
|
||||
return template.get( index );
|
||||
}
|
||||
|
||||
public int length() {
|
||||
|
||||
return template.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
||||
import com.lyndir.lhunath.opal.system.util.ObjectMeta;
|
||||
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public class MPTemplateCharacterClass extends MetaObject {
|
||||
|
||||
private final char identifier;
|
||||
@ObjectMeta(useFor = { })
|
||||
private final char[] characters;
|
||||
|
||||
public MPTemplateCharacterClass(final char identifier, final char[] characters) {
|
||||
|
||||
this.identifier = identifier;
|
||||
this.characters = characters;
|
||||
}
|
||||
|
||||
public char getIdentifier() {
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public char getCharacterAtRollingIndex(final int index) {
|
||||
|
||||
return characters[index % characters.length];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.Closeables;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.sf.plist.*;
|
||||
import net.sf.plist.io.PropertyListException;
|
||||
import net.sf.plist.io.PropertyListParser;
|
||||
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public class MPTemplates extends MetaObject {
|
||||
|
||||
static final Logger logger = Logger.get( MPTemplates.class );
|
||||
|
||||
private final Map<MPElementType, List<MPTemplate>> templates;
|
||||
|
||||
public MPTemplates(final Map<MPElementType, List<MPTemplate>> templates) {
|
||||
|
||||
this.templates = templates;
|
||||
}
|
||||
|
||||
public static MPTemplates load() {
|
||||
|
||||
return loadFromPList( "ciphers.plist" );
|
||||
}
|
||||
|
||||
public static MPTemplates loadFromPList(final String templateResource) {
|
||||
|
||||
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
|
||||
InputStream templateStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( templateResource );
|
||||
Preconditions.checkNotNull( templateStream, "Not found: %s", templateResource );
|
||||
try {
|
||||
NSObject plistObject = PropertyListParser.parse( templateStream );
|
||||
Preconditions.checkState( NSDictionary.class.isAssignableFrom( plistObject.getClass() ) );
|
||||
NSDictionary plist = (NSDictionary) plistObject;
|
||||
|
||||
NSDictionary characterClassesDict = (NSDictionary) plist.get( "MPCharacterClasses" );
|
||||
NSDictionary templatesDict = (NSDictionary) plist.get( "MPElementGeneratedEntity" );
|
||||
|
||||
ImmutableMap.Builder<Character, MPTemplateCharacterClass> characterClassesBuilder = ImmutableMap.builder();
|
||||
for (final Map.Entry<String, NSObject> characterClassEntry : characterClassesDict.entrySet()) {
|
||||
String key = characterClassEntry.getKey();
|
||||
NSObject value = characterClassEntry.getValue();
|
||||
Preconditions.checkState( key.length() == 1 );
|
||||
Preconditions.checkState( NSString.class.isAssignableFrom( value.getClass() ));
|
||||
|
||||
char character = key.charAt( 0 );
|
||||
char[] characterClass = ((NSString)value).getValue().toCharArray();
|
||||
characterClassesBuilder.put( character, new MPTemplateCharacterClass( character, characterClass ) );
|
||||
}
|
||||
ImmutableMap<Character, MPTemplateCharacterClass> characterClasses = characterClassesBuilder.build();
|
||||
|
||||
ImmutableMap.Builder<MPElementType, List<MPTemplate>> templatesBuilder = ImmutableMap.builder();
|
||||
for (final Map.Entry<String, NSObject> template : templatesDict.entrySet()) {
|
||||
String key = template.getKey();
|
||||
NSObject value = template.getValue();
|
||||
Preconditions.checkState( NSArray.class.isAssignableFrom( value.getClass() ) );
|
||||
|
||||
MPElementType type = MPElementType.forName( key );
|
||||
List<NSObject> templateStrings = ((NSArray) value).getValue();
|
||||
|
||||
ImmutableList.Builder<MPTemplate> typeTemplatesBuilder = ImmutableList.<MPTemplate>builder();
|
||||
for (final NSObject templateString : templateStrings)
|
||||
typeTemplatesBuilder.add( new MPTemplate( ((NSString) templateString).getValue(), characterClasses ) );
|
||||
|
||||
templatesBuilder.put( type, typeTemplatesBuilder.build() );
|
||||
}
|
||||
ImmutableMap<MPElementType, List<MPTemplate>> templates = templatesBuilder.build();
|
||||
|
||||
return new MPTemplates( templates );
|
||||
}
|
||||
catch (PropertyListException e) {
|
||||
logger.err( e, "Could not parse templates from: %s", templateResource );
|
||||
throw Throwables.propagate( e );
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.err( e, "Could not read templates from: %s", templateResource );
|
||||
throw Throwables.propagate( e );
|
||||
}
|
||||
finally {
|
||||
Closeables.closeQuietly( templateStream );
|
||||
}
|
||||
}
|
||||
|
||||
public MPTemplate getTemplateForTypeAtRollingIndex(final MPElementType type, final int templateIndex) {
|
||||
|
||||
List<MPTemplate> typeTemplates = templates.get( type );
|
||||
|
||||
return typeTemplates.get( templateIndex % typeTemplates.size() );
|
||||
}
|
||||
|
||||
public static void main(final String... arguments) {
|
||||
|
||||
load();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.io.CharSource;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.lambdaworks.crypto.SCrypt;
|
||||
import com.lyndir.lhunath.opal.crypto.CryptUtils;
|
||||
import com.lyndir.lhunath.opal.system.*;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
import javax.xml.stream.events.Characters;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2014-08-30
|
||||
*/
|
||||
public class MasterKey {
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final Logger logger = Logger.get( MasterKey.class );
|
||||
private static final int MP_N = 32768;
|
||||
private static final int MP_r = 8;
|
||||
private static final int MP_p = 2;
|
||||
private static final int MP_dkLen = 64;
|
||||
private static final Charset MP_charset = Charsets.UTF_8;
|
||||
private static final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
|
||||
private static final MessageDigests MP_hash = MessageDigests.SHA256;
|
||||
private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
|
||||
private static final MPTemplates templates = MPTemplates.load();
|
||||
|
||||
private final String userName;
|
||||
private final byte[] key;
|
||||
|
||||
private boolean valid;
|
||||
|
||||
public MasterKey(final String userName, final String masterPassword) {
|
||||
|
||||
this.userName = userName;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
byte[] userNameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE )
|
||||
.order( MP_byteOrder )
|
||||
.putInt( userName.length() )
|
||||
.array();
|
||||
byte[] salt = Bytes.concat( "com.lyndir.masterpassword".getBytes( MP_charset ), //
|
||||
userNameLengthBytes, userName.getBytes( MP_charset ) );
|
||||
|
||||
try {
|
||||
key = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), salt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||
valid = true;
|
||||
|
||||
logger.trc( "User: %s, master password derives to key ID: %s (took %.2fs)", //
|
||||
userName, getKeyID(), (double) (System.currentTimeMillis() - start) / 1000 );
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw logger.bug( e );
|
||||
}
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
|
||||
return userName;
|
||||
}
|
||||
|
||||
public String getKeyID() {
|
||||
|
||||
Preconditions.checkState( valid );
|
||||
return CodeUtils.encodeHex( MP_hash.of( key ) );
|
||||
}
|
||||
|
||||
private byte[] getSubkey(final int subkeyLength) {
|
||||
|
||||
Preconditions.checkState( valid );
|
||||
byte[] subkey = new byte[Math.min( subkeyLength, key.length )];
|
||||
System.arraycopy( key, 0, subkey, 0, subkey.length );
|
||||
|
||||
return subkey;
|
||||
}
|
||||
|
||||
public String encode(final String name, final MPElementType type, int counter) {
|
||||
|
||||
Preconditions.checkState( valid );
|
||||
Preconditions.checkArgument( type.getTypeClass() == MPElementTypeClass.Generated );
|
||||
Preconditions.checkArgument( !name.isEmpty() );
|
||||
|
||||
if (counter == 0)
|
||||
counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
|
||||
|
||||
byte[] nameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MP_byteOrder ).putInt( name.length() ).array();
|
||||
byte[] counterBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MP_byteOrder ).putInt( counter ).array();
|
||||
logger.trc( "seed from: hmac-sha256(%s, 'com.lyndir.masterpassword' | %s | %s | %s)", CryptUtils.encodeBase64( key ),
|
||||
CodeUtils.encodeHex( nameLengthBytes ), name, CodeUtils.encodeHex( counterBytes ) );
|
||||
byte[] seed = MP_mac.of( key, Bytes.concat( "com.lyndir.masterpassword".getBytes( MP_charset ), //
|
||||
nameLengthBytes, //
|
||||
name.getBytes( MP_charset ), //
|
||||
counterBytes ) );
|
||||
logger.trc( "seed is: %s", CryptUtils.encodeBase64( seed ) );
|
||||
|
||||
Preconditions.checkState( seed.length > 0 );
|
||||
int templateIndex = seed[0] & 0xFF; // Mask the integer's sign.
|
||||
MPTemplate template = templates.getTemplateForTypeAtRollingIndex( type, templateIndex );
|
||||
logger.trc( "type: %s, template: %s", type, template );
|
||||
|
||||
StringBuilder password = new StringBuilder( template.length() );
|
||||
for (int i = 0; i < template.length(); ++i) {
|
||||
int characterIndex = seed[i + 1] & 0xFF; // Mask the integer's sign.
|
||||
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||
logger.trc( "class: %s, index: %d, byte: 0x%02X, chosen password character: %s", characterClass, characterIndex, seed[i + 1],
|
||||
passwordCharacter );
|
||||
|
||||
password.append( passwordCharacter );
|
||||
}
|
||||
|
||||
return password.toString();
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
|
||||
valid = false;
|
||||
Arrays.fill( key, (byte) 0 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lyndir.masterpassword.entity;
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public class MPElementEntity {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lyndir.masterpassword.entity;
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public class MPElementGeneratedEntity extends MPElementEntity {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lyndir.masterpassword.entity;
|
||||
|
||||
/**
|
||||
* <i>07 04, 2012</i>
|
||||
*
|
||||
* @author lhunath
|
||||
*/
|
||||
public class MPElementStoredEntity extends MPElementEntity {
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../MasterPassword/Resources/Data/ciphers.plist
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.lyndir.masterpassword"
|
||||
android:versionCode="1"
|
||||
android:versionName="GIT-SNAPSHOT">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<application
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/app_name"
|
||||
android:allowBackup="true">
|
||||
<activity android:name=".EmergencyActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".UsersActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# File used by Eclipse to determine the target system
|
||||
# Project target.
|
||||
target=android-16
|
||||
@@ -0,0 +1,8 @@
|
||||
/*___Generated_by_IDEA___*/
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */
|
||||
public final class BuildConfig {
|
||||
public final static boolean DEBUG = Boolean.parseBoolean(null);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/*___Generated_by_IDEA___*/
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */
|
||||
public final class Manifest {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/*___Generated_by_IDEA___*/
|
||||
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */
|
||||
public final class R {
|
||||
}
|
||||
126
MasterPassword/Java/masterpassword-android/pom.xml
Normal file
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- PROJECT METADATA -->
|
||||
<parent>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Master Password Android</name>
|
||||
<description>An Android application to the Master Password algorithm</description>
|
||||
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword-android</artifactId>
|
||||
<packaging>apk</packaging>
|
||||
|
||||
<!-- BUILD CONFIGURATION -->
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||
<artifactId>android-maven-plugin</artifactId>
|
||||
|
||||
<configuration>
|
||||
<zipalign>
|
||||
<verbose>true</verbose>
|
||||
<skip>false</skip>
|
||||
</zipalign>
|
||||
<sdk>
|
||||
<platform>19</platform>
|
||||
</sdk>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>sign</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jarsigner-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>signing</id>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<inherited>true</inherited>
|
||||
<configuration>
|
||||
<archiveDirectory></archiveDirectory>
|
||||
<includes>
|
||||
<include>target/*.apk</include>
|
||||
</includes>
|
||||
<keystore>release.jks</keystore>
|
||||
<storepass>${env.PASSWORD}</storepass>
|
||||
<keypass>${env.PASSWORD}</keypass>
|
||||
<alias>android</alias>
|
||||
<arguments>
|
||||
<argument>-sigalg</argument><argument>MD5withRSA</argument>
|
||||
<argument>-digestalg</argument><argument>SHA1</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||
<artifactId>android-maven-plugin</artifactId>
|
||||
<inherited>true</inherited>
|
||||
<configuration>
|
||||
<sign>
|
||||
<debug>false</debug>
|
||||
</sign>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<!-- DEPENDENCY MANAGEMENT -->
|
||||
<dependencies>
|
||||
|
||||
<!-- PROJECT REFERENCES -->
|
||||
<dependency>
|
||||
<groupId>com.lyndir.masterpassword</groupId>
|
||||
<artifactId>masterpassword-algorithm</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jakewharton</groupId>
|
||||
<artifactId>butterknife</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-android</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- clone https://github.com/mosabua/maven-android-sdk-deployer.git
|
||||
run mvn install -P 4.4 -->
|
||||
<dependency>
|
||||
<groupId>android</groupId>
|
||||
<artifactId>android</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.lambdaworks</groupId>
|
||||
<artifactId>libscrypt</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<type>so</type>
|
||||
<classifier>android</classifier>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
5
MasterPassword/Java/masterpassword-android/proguard.cfg
Normal file
@@ -0,0 +1,5 @@
|
||||
-ignorewarnings
|
||||
-dontoptimize
|
||||
-dontobfuscate
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-dontskipnonpubliclibraryclassmembers
|
||||
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |