Compare commits
591 Commits
0.8.6
...
generic-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
541c22cb13 | ||
|
|
06f2ba19b4 | ||
|
|
5690c4ed89 | ||
|
|
62953e9ecf | ||
|
|
5142eea2eb | ||
|
|
5dd9e4b0e0 | ||
|
|
4ec4f09b35 | ||
|
|
e24819a405 | ||
|
|
d0d1fa9835 | ||
|
|
da1229814c | ||
|
|
d82df1273c | ||
|
|
9fef4b7d02 | ||
|
|
4359536a17 | ||
|
|
03d43132bc | ||
|
|
4d59a9fe45 | ||
|
|
40f2452fb0 | ||
|
|
1cf09b1870 | ||
|
|
5edc480932 | ||
|
|
641211ecae | ||
|
|
d8fd6e6d14 | ||
|
|
9410530507 | ||
|
|
d6d71e97f8 | ||
|
|
3f03de031c | ||
|
|
c7d410f38b | ||
|
|
fa960429b8 | ||
|
|
bfd0872aea | ||
|
|
c5b6026fc6 | ||
|
|
7fbf507f58 | ||
|
|
69f42360cf | ||
|
|
f40fcbe5d1 | ||
|
|
13d0354176 | ||
|
|
53d8eb17b6 | ||
|
|
9735af515a | ||
|
|
c30bd0d9ab | ||
|
|
fdd717a246 | ||
|
|
7894d15929 | ||
|
|
f49ccb3262 | ||
|
|
069787db51 | ||
|
|
9eba8e8738 | ||
|
|
f13ea6e556 | ||
|
|
29f68c0dd7 | ||
|
|
3c0966d74e | ||
|
|
c90128779f | ||
|
|
acb8e0f629 | ||
|
|
f3460460e9 | ||
|
|
37555868f6 | ||
|
|
b0855f75f5 | ||
|
|
977ef75b14 | ||
|
|
d7d7435f21 | ||
|
|
d22558e0e3 | ||
|
|
96ece6fbdb | ||
|
|
a60d2ea19d | ||
|
|
70a908e18b | ||
|
|
004a5bf7fc | ||
|
|
e9bd06354a | ||
|
|
54b92c0255 | ||
|
|
6881a2e770 | ||
|
|
e29f422511 | ||
|
|
1a8be1d617 | ||
|
|
8ea15cf656 | ||
|
|
a8807feb5c | ||
|
|
4d15f1db0b | ||
|
|
957725348e | ||
|
|
357edbd367 | ||
|
|
3d01915407 | ||
|
|
e90a6e9f71 | ||
|
|
3891b73c44 | ||
|
|
a66c3234d7 | ||
|
|
2a81327ab0 | ||
|
|
1d6714c59b | ||
|
|
a771b3d4fe | ||
|
|
700abd95b6 | ||
|
|
476dd4c4f7 | ||
|
|
f0383d50cd | ||
|
|
044398eeba | ||
|
|
675ba5b3fc | ||
|
|
62d1063560 | ||
|
|
d8c9ca49e1 | ||
|
|
91d6dac985 | ||
|
|
d7bd434f41 | ||
|
|
5ac415986e | ||
|
|
266fd0b1d4 | ||
|
|
10760238b3 | ||
|
|
d9acad3d91 | ||
|
|
27278c032b | ||
|
|
57bc4bbb75 | ||
|
|
3600756723 | ||
|
|
7111ee2f85 | ||
|
|
dcee2eea91 | ||
|
|
4a0c71a840 | ||
|
|
e1ee758b61 | ||
|
|
4b5e103d9c | ||
|
|
bddb80a192 | ||
|
|
06afe3d1c2 | ||
|
|
97b1452211 | ||
|
|
fc6ba89c61 | ||
|
|
a92115ab9f | ||
|
|
08158ee8ad | ||
|
|
528c16e6c8 | ||
|
|
870f2174d5 | ||
|
|
1406198087 | ||
|
|
53373261ad | ||
|
|
c1c697b177 | ||
|
|
808a981e86 | ||
|
|
879f40a0ae | ||
|
|
0365f03342 | ||
|
|
1e8dc3caa8 | ||
|
|
e32d39dbc1 | ||
|
|
2bc34b5847 | ||
|
|
4cf7805e9e | ||
|
|
a1206435eb | ||
|
|
69550f9be9 | ||
|
|
9c7b8580d8 | ||
|
|
ec5e942705 | ||
|
|
404464fd40 | ||
|
|
619bcc38e9 | ||
|
|
728e050a05 | ||
|
|
93e04b8d03 | ||
|
|
a97714da18 | ||
|
|
118087f2a1 | ||
|
|
3a3b4faa10 | ||
|
|
2850fd7792 | ||
|
|
db91f793dc | ||
|
|
3225bafd54 | ||
|
|
47f4d883c8 | ||
|
|
d8d0e75466 | ||
|
|
e9d983dec2 | ||
|
|
f3878b954a | ||
|
|
f693efd532 | ||
|
|
6a052ed0fc | ||
|
|
edb54ebe90 | ||
|
|
28f3ce19b8 | ||
|
|
b0631e322f | ||
|
|
c3c28cb4c1 | ||
|
|
ae5484ebd5 | ||
|
|
01f2c3128a | ||
|
|
07a4f778d3 | ||
|
|
44a5556278 | ||
|
|
b3e355736f | ||
|
|
058576e768 | ||
|
|
1c84ca3730 | ||
|
|
bf1348e89d | ||
|
|
795675d1aa | ||
|
|
12009a009d | ||
|
|
f23ac83c95 | ||
|
|
2dc20d13fb | ||
|
|
e26548375e | ||
|
|
e73cdb57f3 | ||
|
|
fcc4f8c680 | ||
|
|
a1de389e45 | ||
|
|
4ef5b2d449 | ||
|
|
ad9e9d5dbd | ||
|
|
4e53184eee | ||
|
|
f19ed572f0 | ||
|
|
f27d95fe43 | ||
|
|
2bf1988b3c | ||
|
|
05620ab506 | ||
|
|
0ea9c1d3a2 | ||
|
|
1f0a3f1ad1 | ||
|
|
2f491e0250 | ||
|
|
2509ed8424 | ||
|
|
f3e3e64a17 | ||
|
|
d7a28f97a6 | ||
|
|
2199d8d088 | ||
|
|
d1ce4b35ae | ||
|
|
e77cd53215 | ||
|
|
588aea4f54 | ||
|
|
ee5f9fb77b | ||
|
|
4790a61437 | ||
|
|
25d3a2d459 | ||
|
|
27be6acaeb | ||
|
|
9a0e070c24 | ||
|
|
e490011b68 | ||
|
|
3b4b664c28 | ||
|
|
18c95fa7bd | ||
|
|
cf2f7147d9 | ||
|
|
ecf6457cdb | ||
|
|
db8a511f68 | ||
|
|
2d5424bcff | ||
|
|
5792d52461 | ||
|
|
09229e2cd5 | ||
|
|
c07ac0a68d | ||
|
|
60ce94c267 | ||
|
|
99f2d4fe54 | ||
|
|
acced31192 | ||
|
|
9ee08f8f2a | ||
|
|
7c1061ccbb | ||
|
|
7eab7c3f5e | ||
|
|
4d93b6c0d6 | ||
|
|
65b3a0996f | ||
|
|
4dd78be10d | ||
|
|
8c076ac969 | ||
|
|
7c68aced35 | ||
|
|
197896ba1b | ||
|
|
bef411f319 | ||
|
|
7f5ce7d2ae | ||
|
|
47e27c837a | ||
|
|
50a4a983cd | ||
|
|
428681a185 | ||
|
|
3dcc5a60f2 | ||
|
|
cba21b5598 | ||
|
|
a65dd1059d | ||
|
|
7c83eb0efa | ||
|
|
f2c658723f | ||
|
|
7ac4b1cc12 | ||
|
|
1c4cb66f5a | ||
|
|
6a50c9cea6 | ||
|
|
37b12ad788 | ||
|
|
5530ac690f | ||
|
|
3063d886bd | ||
|
|
198bdd77d0 | ||
|
|
20c039fdb8 | ||
|
|
c92fc849e7 | ||
|
|
6fdac3ae72 | ||
|
|
623ff164de | ||
|
|
faa46d28a5 | ||
|
|
1ca10fd167 | ||
|
|
709e41fd10 | ||
|
|
771d5865ae | ||
|
|
fc064bc883 | ||
|
|
2ca2fcfedd | ||
|
|
d78b02ee24 | ||
|
|
2a94a49cef | ||
|
|
562a06425b | ||
|
|
eef07cef36 | ||
|
|
2e21d5a021 | ||
|
|
abd1b08a97 | ||
|
|
3feed16e2e | ||
|
|
6b38216124 | ||
|
|
8cfb2d2a03 | ||
|
|
1b10e8f987 | ||
|
|
178f566ae2 | ||
|
|
0326e5c5e2 | ||
|
|
212df6d036 | ||
|
|
4b04db6b2d | ||
|
|
fb327d4bd1 | ||
|
|
a811c868ff | ||
|
|
3f05bafffb | ||
|
|
133abd096d | ||
|
|
cecd6b20e8 | ||
|
|
2272681ea2 | ||
|
|
17f79cbbb8 | ||
|
|
2570084cae | ||
|
|
b2e6458736 | ||
|
|
c192a2b181 | ||
|
|
2e325e5e68 | ||
|
|
f0436490b0 | ||
|
|
243664926f | ||
|
|
466fa2adbf | ||
|
|
284e8f4421 | ||
|
|
6852da97ae | ||
|
|
35f37e8c23 | ||
|
|
cbe6701c14 | ||
|
|
296df2fca9 | ||
|
|
7caf88a6a8 | ||
|
|
ebaad72a35 | ||
|
|
962bf4de5e | ||
|
|
884dace400 | ||
|
|
234bef4025 | ||
|
|
45cbd51633 | ||
|
|
484e971323 | ||
|
|
038404cb99 | ||
|
|
6f075bfc71 | ||
|
|
7aed036896 | ||
|
|
0e8c88a5e4 | ||
|
|
a13847d994 | ||
|
|
18b67d6eaf | ||
|
|
886f45584c | ||
|
|
4f915d7515 | ||
|
|
e25ca7719f | ||
|
|
2bf5cc0e41 | ||
|
|
30f3cc9263 | ||
|
|
e120109644 | ||
|
|
b4f13205d8 | ||
|
|
4797522d88 | ||
|
|
5ee06776ee | ||
|
|
c40b4c12c1 | ||
|
|
d8329960c5 | ||
|
|
94ece33f16 | ||
|
|
b179c2d277 | ||
|
|
ab568a2666 | ||
|
|
c10c930e8f | ||
|
|
6ddf424192 | ||
|
|
6aff44184e | ||
|
|
9cc9ea86bb | ||
|
|
13a0bc463c | ||
|
|
8ff510a3af | ||
|
|
633d1e9b52 | ||
|
|
a13ba65cfb | ||
|
|
0e6b0ef84d | ||
|
|
9a86efabe5 | ||
|
|
a4b05cf156 | ||
|
|
06da6d5125 | ||
|
|
1bcf77608d | ||
|
|
aab0cc3e3e | ||
|
|
822f95071a | ||
|
|
27f613bc84 | ||
|
|
af1ef3855b | ||
|
|
7ede9c6d03 | ||
|
|
20c6f225b9 | ||
|
|
5b204b0938 | ||
|
|
6e6093ee7b | ||
|
|
f12d43989f | ||
|
|
032e8ec89d | ||
|
|
d0644c5e95 | ||
|
|
b1b827cde6 | ||
|
|
e73b577e0b | ||
|
|
8d2d59e914 | ||
|
|
212f3ea760 | ||
|
|
4aa7ea7e5b | ||
|
|
5f9c0399c3 | ||
|
|
b83347b464 | ||
|
|
d8a86a54d5 | ||
|
|
49289a9c05 | ||
|
|
456083d878 | ||
|
|
15e7032f0d | ||
|
|
cd2cf6eb23 | ||
|
|
666995531a | ||
|
|
9269d6b0d5 | ||
|
|
c319ee6583 | ||
|
|
cc53012cfa | ||
|
|
cc297eff45 | ||
|
|
e151096834 | ||
|
|
82040403bf | ||
|
|
a845c7ec3d | ||
|
|
5ecb0c5d0c | ||
|
|
da8dc2d3cb | ||
|
|
182695d1ca | ||
|
|
0d039f7192 | ||
|
|
ebe97e8f2a | ||
|
|
9505cace5e | ||
|
|
8ef027c54a | ||
|
|
0bdb5f1d5b | ||
|
|
49e7572059 | ||
|
|
c074a6166c | ||
|
|
4c504ee42b | ||
|
|
f226891520 | ||
|
|
43a68479ad | ||
|
|
1e422a70dd | ||
|
|
2a474bd809 | ||
|
|
704c157d24 | ||
|
|
d8528b597c | ||
|
|
c149caa32a | ||
|
|
fa63aac731 | ||
|
|
ba40bf1040 | ||
|
|
2f9bcfe145 | ||
|
|
b957213fdc | ||
|
|
f51b01a5b3 | ||
|
|
d2efc85ce3 | ||
|
|
940d76e9e2 | ||
|
|
59eb8d67ed | ||
|
|
78592650e5 | ||
|
|
92d006a41b | ||
|
|
0741820711 | ||
|
|
c9ddc2b7ef | ||
|
|
f776b34d8c | ||
|
|
f47609c1f0 | ||
|
|
443b4d3116 | ||
|
|
4cf7a0cb79 | ||
|
|
9a5fe8b96d | ||
|
|
80a7ea0b7c | ||
|
|
1092447edf | ||
|
|
d2c1daf4d5 | ||
|
|
d1a41c9d7d | ||
|
|
9931ad4fa2 | ||
|
|
5a1f282292 | ||
|
|
b360316b8b | ||
|
|
c6aaa96dcb | ||
|
|
f0706f56c4 | ||
|
|
4ef2740c36 | ||
|
|
5e0330f0ad | ||
|
|
717a19859f | ||
|
|
209f21905e | ||
|
|
38aa05624d | ||
|
|
136055024e | ||
|
|
8d3f3d837a | ||
|
|
151debf7ee | ||
|
|
d8da993a73 | ||
|
|
72173087a9 | ||
|
|
229215b49c | ||
|
|
dd7aeff5d2 | ||
|
|
3e95806d2e | ||
|
|
7ad42add73 | ||
|
|
936ddb0d15 | ||
|
|
233094af10 | ||
|
|
94e84f0643 | ||
|
|
8662611a34 | ||
|
|
60e6c191e9 | ||
|
|
211170822f | ||
|
|
c80f99bc68 | ||
|
|
53e44aa0e3 | ||
|
|
2b249130c9 | ||
|
|
84fa4466ee | ||
|
|
beaaab5742 | ||
|
|
f351d62355 | ||
|
|
b6fcb7035d | ||
|
|
c0a224cf84 | ||
|
|
384ef8023e | ||
|
|
f44232762e | ||
|
|
111659cc91 | ||
|
|
4ae4b8c94b | ||
|
|
2b2de142ef | ||
|
|
a45af3a929 | ||
|
|
3e77afb27e | ||
|
|
0c60e8d384 | ||
|
|
2bff3f8722 | ||
|
|
dc56737b71 | ||
|
|
3869906357 | ||
|
|
75b19be572 | ||
|
|
b0a1842a53 | ||
|
|
14066d0bff | ||
|
|
171b285cbe | ||
|
|
f46cf669a3 | ||
|
|
09e5103eee | ||
|
|
1a657c67e2 | ||
|
|
3e86987f5f | ||
|
|
e83d0d6b62 | ||
|
|
2ffb278aa4 | ||
|
|
7f57e3987a | ||
|
|
c62d1d64ab | ||
|
|
c9a5cba113 | ||
|
|
e4a4bdd3a3 | ||
|
|
565ace7982 | ||
|
|
71d90d60a5 | ||
|
|
b5d9b33b6f | ||
|
|
063f4deb22 | ||
|
|
192ab59608 | ||
|
|
a3da77b8f6 | ||
|
|
c0aad0bf00 | ||
|
|
a2834f8f92 | ||
|
|
2ef432f632 | ||
|
|
a50d706bcd | ||
|
|
ca7af9c837 | ||
|
|
a65679dd72 | ||
|
|
211748e65a | ||
|
|
4c0c12343f | ||
|
|
96873984b2 | ||
|
|
b06599963b | ||
|
|
ab80d72518 | ||
|
|
ba7422c893 | ||
|
|
4b444ea699 | ||
|
|
96dc61c772 | ||
|
|
f5c2c81142 | ||
|
|
838ed493a3 | ||
|
|
9d7b3394e7 | ||
|
|
9e4e274c4d | ||
|
|
e3b9fa1e53 | ||
|
|
85bf3f4241 | ||
|
|
6d70bf4b0d | ||
|
|
3aafeef215 | ||
|
|
e60895087e | ||
|
|
b169c0a91e | ||
|
|
968cb7dcdf | ||
|
|
367aeb5844 | ||
|
|
5518a9e10a | ||
|
|
6c1c2a86f8 | ||
|
|
cb79f5c10b | ||
|
|
b7e308d057 | ||
|
|
714b2f9c03 | ||
|
|
a7c18ba6c7 | ||
|
|
fc82aaeef2 | ||
|
|
c291513663 | ||
|
|
a413c68e52 | ||
|
|
5b41247bac | ||
|
|
fc563272fe | ||
|
|
38545af533 | ||
|
|
6f612d4f4d | ||
|
|
a1bc5e82cf | ||
|
|
98c8afd5fc | ||
|
|
cff4a4cd71 | ||
|
|
7bbfd00344 | ||
|
|
01af2deed9 | ||
|
|
a54def3e92 | ||
|
|
2aea1a627c | ||
|
|
e475ca13ea | ||
|
|
31582cac92 | ||
|
|
c012f20c99 | ||
|
|
d460a5eb16 | ||
|
|
3a56d68f47 | ||
|
|
9de0293405 | ||
|
|
21f742f888 | ||
|
|
0d4bcc0228 | ||
|
|
90a595f8ef | ||
|
|
64d65b3aed | ||
|
|
a39267c1b0 | ||
|
|
331db6f565 | ||
|
|
9cea7e4002 | ||
|
|
f63aba88d4 | ||
|
|
a78201bae0 | ||
|
|
a61dba0c13 | ||
|
|
60cfcc72e6 | ||
|
|
4cc9ba79c4 | ||
|
|
182803926b | ||
|
|
e41b7a06e4 | ||
|
|
0994022066 | ||
|
|
c156e13791 | ||
|
|
a47b71e52b | ||
|
|
db908563d0 | ||
|
|
b5e49a0600 | ||
|
|
e0a13ecdaa | ||
|
|
fc856f36a3 | ||
|
|
9b67acbfc4 | ||
|
|
b04c40d3ea | ||
|
|
442914c375 | ||
|
|
a4558b69df | ||
|
|
07751974ac | ||
|
|
8ff46add8c | ||
|
|
b7fbc81740 | ||
|
|
36af7afa27 | ||
|
|
254b9d73a1 | ||
|
|
8fcac79301 | ||
|
|
a48add8f13 | ||
|
|
8627fbb13f | ||
|
|
5e64c850bd | ||
|
|
533664903a | ||
|
|
cda805163f | ||
|
|
3d4c657467 | ||
|
|
8339fd0420 | ||
|
|
63946ff7ef | ||
|
|
e6bbfa7a62 | ||
|
|
b2a98fb9b6 | ||
|
|
f326c2f42f | ||
|
|
3b55c20879 | ||
|
|
a335466450 | ||
|
|
c73ddf065b | ||
|
|
fa585fd107 | ||
|
|
7a782df9f5 | ||
|
|
982d840472 | ||
|
|
75938845fd | ||
|
|
e70e755e19 | ||
|
|
5e37de2b63 | ||
|
|
10656ead46 | ||
|
|
75b1a633d6 | ||
|
|
109aaa71a5 | ||
|
|
92709a773c | ||
|
|
93f9a5d1e8 | ||
|
|
0acec5c335 | ||
|
|
0e8d407f93 | ||
|
|
ed5f982312 | ||
|
|
cd92f69c76 | ||
|
|
73080e0660 | ||
|
|
7cdef1e319 | ||
|
|
527bc0573a | ||
|
|
59d2dd55d2 | ||
|
|
b1cbcffdc7 | ||
|
|
154b70ed2c | ||
|
|
e33a98e498 | ||
|
|
239d365ea1 | ||
|
|
57ed30b2bc | ||
|
|
86f913b001 | ||
|
|
e2f1c1e94a | ||
|
|
f62d817c71 | ||
|
|
b753e6cf18 | ||
|
|
0fed8a55e4 | ||
|
|
d28520ba30 | ||
|
|
c52ba85846 | ||
|
|
3b906f6ceb | ||
|
|
5d76e052ec | ||
|
|
37a40dec23 | ||
|
|
2093aac817 | ||
|
|
29fa1bf27e | ||
|
|
f364ed184b | ||
|
|
40c9fe22b3 | ||
|
|
c33c0a6998 | ||
|
|
92e674e412 | ||
|
|
9f82aac8ce | ||
|
|
a3f1b83f3a | ||
|
|
71f75b2e2e | ||
|
|
b23104f920 | ||
|
|
057f9068c6 | ||
|
|
3231214344 | ||
|
|
55efc601cb | ||
|
|
dcbf3fb4d5 | ||
|
|
e2acd98612 | ||
|
|
ee4f6d4e3c | ||
|
|
db14f2bd4a | ||
|
|
e768604533 | ||
|
|
1afcd485db | ||
|
|
58613ea7f3 | ||
|
|
bf5bf96e95 | ||
|
|
529a9bed44 | ||
|
|
fd4f26e5d4 | ||
|
|
e7f87c652e | ||
|
|
65d2535302 | ||
|
|
a981c0a733 | ||
|
|
b08625ef4a | ||
|
|
ea719cd02a | ||
|
|
52dcd26b8c | ||
|
|
cf361d35fe | ||
|
|
077afae693 | ||
|
|
783bad0897 |
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Unify coding style with clang-format
|
||||||
|
562a06425b96450481cdbc88e8872a2bf5a7d8aa
|
||||||
49
.github/ISSUE_TEMPLATE.md
vendored
Normal file
49
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Provide a general summary of the issue in the Title above
|
||||||
|
|
||||||
|
This is a very generic template, remove items that do not apply. For completed items, change [ ] to [x].
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
* [ ] Checked that your issue isn't already filed: https://github.com/strophe/libstrophe/issues?q=
|
||||||
|
|
||||||
|
### Additional Information
|
||||||
|
|
||||||
|
<!--- Any additional information, configuration or data that might be necessary to reproduce the issue. -->
|
||||||
|
|
||||||
|
## Expected Behavior
|
||||||
|
|
||||||
|
<!--- If you're describing a bug, tell us what should happen -->
|
||||||
|
<!--- If you're suggesting a change/improvement, tell us how it should work -->
|
||||||
|
|
||||||
|
## Current Behavior
|
||||||
|
|
||||||
|
<!--- If describing a bug, tell us what happens instead of the expected behavior or even better, provide a functional code example reproducing your issue. -->
|
||||||
|
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
|
||||||
|
|
||||||
|
## Possible Solution
|
||||||
|
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||||
|
<!--- or ideas how to implement the addition or change -->
|
||||||
|
|
||||||
|
## Steps to Reproduce (for bugs)
|
||||||
|
|
||||||
|
<!--- Please either describe your issue in detail or even better, provide a functional code example reproducing your issue. -->
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
4.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
<!---
|
||||||
|
Please provide the version of the library you're using.
|
||||||
|
You can find the version either in the first line of the `configure.ac` file or if you're using the Git repository, please run `git describe --always --tags --dirty`.
|
||||||
|
Also, please include the compiler, the compiler version, the architecture, the OS and what version of the OS you're experiencing the issue.
|
||||||
|
-->
|
||||||
156
.github/workflows/main.yml
vendored
Normal file
156
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- next
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linux-tests:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
valgrind:
|
||||||
|
- { configure: '' , make: 'check' }
|
||||||
|
- { configure: '--enable-valgrind' , make: 'check-valgrind' }
|
||||||
|
options:
|
||||||
|
- { configure: '' }
|
||||||
|
- { configure: '--without-libxml2' }
|
||||||
|
- { configure: '--with-libxml2' }
|
||||||
|
- { configure: '--with-gnutls' }
|
||||||
|
- { configure: '--disable-tls' }
|
||||||
|
- { configure: '--enable-cares' }
|
||||||
|
- { configure: '--disable-getrandom' }
|
||||||
|
- { configure: '--disable-static' }
|
||||||
|
name: Regular Tests
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y libtool pkg-config libexpat1-dev libxml2-dev libssl-dev libgnutls28-dev libc-ares-dev valgrind
|
||||||
|
- name: Build the library
|
||||||
|
run: |
|
||||||
|
./bootstrap.sh
|
||||||
|
./configure ${{ matrix.options.configure }} ${{ matrix.valgrind.configure }} CFLAGS="-Werror -g3"
|
||||||
|
make -j$(nproc)
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
make -j$(nproc) ${{ matrix.valgrind.make }}
|
||||||
|
- name: Error logs
|
||||||
|
if: ${{ failure() }}
|
||||||
|
run: |
|
||||||
|
cat test-suite*.log || true
|
||||||
|
|
||||||
|
xssl-tests:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
xssl_versions:
|
||||||
|
- { version: "master", continue: true, libressl: true }
|
||||||
|
- { version: "OPENBSD_7_5", continue: true, libressl: true }
|
||||||
|
- { version: "v3.9.2", continue: true, libressl: true }
|
||||||
|
- { version: "OPENBSD_7_4", continue: true, libressl: true }
|
||||||
|
- { version: "v3.8.4", continue: true, libressl: true }
|
||||||
|
- { version: "OPENBSD_7_3", continue: true, libressl: true }
|
||||||
|
- { version: "OPENBSD_7_2", continue: true, libressl: true }
|
||||||
|
- { version: "OPENBSD_7_1", continue: true, libressl: true }
|
||||||
|
# https://github.com/libressl-portable/portable/issues/760
|
||||||
|
# - { version: "v3.5.2", continue: true, libressl: true }
|
||||||
|
- { version: "OPENBSD_7_0", continue: true, libressl: true }
|
||||||
|
# OPENBSD_7_0 is basically the "fixed v3.4.3"
|
||||||
|
# - { version: "v3.4.3", continue: true, libressl: true }
|
||||||
|
- { version: "v3.4.2", continue: true, libressl: true }
|
||||||
|
- { version: "OPENBSD_6_9", continue: true, libressl: true }
|
||||||
|
- { version: "v3.1.5", continue: true, libressl: true }
|
||||||
|
- { version: "v2.1.10", continue: true, libressl: true }
|
||||||
|
- { version: "openssl-3.0", continue: true, libressl: false }
|
||||||
|
- { version: "openssl-3.0.13", continue: false, libressl: false }
|
||||||
|
- { version: "openssl-3.1", continue: true, libressl: false }
|
||||||
|
- { version: "openssl-3.1.5", continue: false, libressl: false }
|
||||||
|
- { version: "openssl-3.2", continue: true, libressl: false }
|
||||||
|
- { version: "openssl-3.2.1", continue: false, libressl: false }
|
||||||
|
- { version: "openssl-3.3", continue: true, libressl: false }
|
||||||
|
- { version: "openssl-3.3.0", continue: false, libressl: false }
|
||||||
|
name: xSSL tests
|
||||||
|
continue-on-error: ${{ matrix.xssl_versions.continue }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y libtool pkg-config libexpat1-dev valgrind
|
||||||
|
- name: build&install the TLS stack
|
||||||
|
env:
|
||||||
|
XSSL_COMMITISH: ${{ matrix.xssl_versions.version }}
|
||||||
|
LIBRESSL: ${{ matrix.xssl_versions.libressl }}
|
||||||
|
run: |
|
||||||
|
./travis/before_script.sh
|
||||||
|
- name: Build the library
|
||||||
|
run: |
|
||||||
|
./bootstrap.sh
|
||||||
|
PKG_CONFIG_PATH="${HOME}/xssl/lib/pkgconfig" ./configure CFLAGS="-Werror -g3" --prefix="${HOME}/xssl"
|
||||||
|
make -j$(nproc)
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
LD_LIBRARY_PATH="${HOME}/xssl/lib" make -j$(nproc) check
|
||||||
|
- name: Build the library with Valgrind enabled
|
||||||
|
run: |
|
||||||
|
./bootstrap.sh
|
||||||
|
PKG_CONFIG_PATH="${HOME}/xssl/lib/pkgconfig" ./configure --enable-valgrind CFLAGS="-Werror -g3" --prefix="${HOME}/xssl"
|
||||||
|
make -j$(nproc)
|
||||||
|
- name: Run tests with Valgrind enabled
|
||||||
|
run: |
|
||||||
|
LD_LIBRARY_PATH="${HOME}/xssl/lib" make -j$(nproc) check-valgrind
|
||||||
|
- name: Error logs
|
||||||
|
if: ${{ failure() }}
|
||||||
|
run: |
|
||||||
|
cat test-suite*.log || true
|
||||||
|
|
||||||
|
release-test:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
name: Check if release would work
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install dependencies & bootstrap
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y libtool pkg-config libexpat1-dev dash
|
||||||
|
./bootstrap.sh
|
||||||
|
- name: Check if configure works with non-bash shells
|
||||||
|
# https://github.com/actions/runner/issues/241 requires us to use this following line...
|
||||||
|
shell: 'script --return --quiet --command "bash {0}"'
|
||||||
|
run: |
|
||||||
|
[ "`CONFIG_SHELL=/bin/dash ./configure 2>&1 1>/dev/null | tee /dev/tty | wc -l`" = "0" ]
|
||||||
|
- name: Re-run configure with the default shell
|
||||||
|
run: |
|
||||||
|
./configure
|
||||||
|
- name: Try release & tests
|
||||||
|
run: |
|
||||||
|
make test-release
|
||||||
|
- name: Show logs from release build
|
||||||
|
if: ${{ !failure() }}
|
||||||
|
run: |
|
||||||
|
cat testbuild.log
|
||||||
|
|
||||||
|
code-style:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
name: Check coding style
|
||||||
|
continue-on-error: true
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y libtool pkg-config libexpat1-dev libxml2-dev libssl-dev libgnutls28-dev libc-ares-dev dos2unix
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
./bootstrap.sh
|
||||||
|
./configure
|
||||||
|
- name: Check style
|
||||||
|
run: |
|
||||||
|
make format
|
||||||
|
git diff --exit-code
|
||||||
57
.gitignore
vendored
57
.gitignore
vendored
@@ -1,8 +1,8 @@
|
|||||||
|
*.swp
|
||||||
|
*.orig
|
||||||
Makefile
|
Makefile
|
||||||
test-driver
|
test-driver
|
||||||
.libs
|
.libs
|
||||||
src/*.lo
|
|
||||||
*.la
|
|
||||||
Makefile.in
|
Makefile.in
|
||||||
configure
|
configure
|
||||||
libtool
|
libtool
|
||||||
@@ -20,24 +20,69 @@ autom4te.cache
|
|||||||
.dirstamp
|
.dirstamp
|
||||||
.deps
|
.deps
|
||||||
.sconsign*
|
.sconsign*
|
||||||
|
src/*.gcda
|
||||||
|
src/*.gcno
|
||||||
src/*.o
|
src/*.o
|
||||||
|
src/*.lo
|
||||||
examples/*.o
|
examples/*.o
|
||||||
tests/*.o
|
|
||||||
tests/check_parser
|
|
||||||
*.a
|
*.a
|
||||||
|
*.la
|
||||||
|
*.pc
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
|
*.tar.bz2
|
||||||
|
*.tar.xz
|
||||||
*.zip
|
*.zip
|
||||||
docs/html
|
docs/html
|
||||||
TAGS
|
TAGS
|
||||||
examples/basic
|
tags
|
||||||
examples/active
|
examples/active
|
||||||
examples/roster
|
examples/basic
|
||||||
examples/bot
|
examples/bot
|
||||||
|
examples/complex
|
||||||
|
examples/component
|
||||||
|
examples/perf
|
||||||
|
examples/register
|
||||||
|
examples/roster
|
||||||
|
examples/uuid
|
||||||
|
examples/vcard
|
||||||
|
testbuild*.log
|
||||||
|
test-release/
|
||||||
test_stamp
|
test_stamp
|
||||||
|
test-suite*.log
|
||||||
tests/*.o
|
tests/*.o
|
||||||
|
tests/*.log
|
||||||
|
tests/*.trs
|
||||||
|
tests/check_parser
|
||||||
tests/test_base64
|
tests/test_base64
|
||||||
tests/test_ctx
|
tests/test_ctx
|
||||||
|
tests/test_fuzz_parser
|
||||||
|
tests/test_fuzz_resolver
|
||||||
tests/test_hash
|
tests/test_hash
|
||||||
tests/test_jid
|
tests/test_jid
|
||||||
|
tests/test_md5
|
||||||
|
tests/test_rand
|
||||||
|
tests/test_resolver
|
||||||
tests/test_sasl
|
tests/test_sasl
|
||||||
|
tests/test_scram
|
||||||
|
tests/test_send_queue
|
||||||
|
tests/test_sha1
|
||||||
|
tests/test_sha256
|
||||||
|
tests/test_sha512
|
||||||
|
tests/test_snprintf
|
||||||
tests/test_sock
|
tests/test_sock
|
||||||
|
tests/test_stanza
|
||||||
|
tests/test_string
|
||||||
|
tests/test_xmppaddr
|
||||||
|
m4/lt*
|
||||||
|
m4/libtool*
|
||||||
|
libstrophe.project
|
||||||
|
libs/
|
||||||
|
obj/
|
||||||
|
expat/
|
||||||
|
.settings/
|
||||||
|
.project
|
||||||
|
.cproject
|
||||||
|
coverage/
|
||||||
|
coverage.info
|
||||||
|
configure~
|
||||||
|
fuzz-*.log
|
||||||
|
|||||||
7
AUTHORS
7
AUTHORS
@@ -1 +1,8 @@
|
|||||||
|
Current maintainer:
|
||||||
|
Steffen Jaeckel <gh@jaeckel.eu>
|
||||||
|
|
||||||
|
Previous maintainers:
|
||||||
|
Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
Jack Moffit <jack@metajack.im>
|
Jack Moffit <jack@metajack.im>
|
||||||
|
|
||||||
|
Other contributors can be extracted from the Git log.
|
||||||
|
|||||||
224
ChangeLog
224
ChangeLog
@@ -1,3 +1,223 @@
|
|||||||
0.8.5
|
0.13.1
|
||||||
- libtoolize to generate .so
|
- Fix SCRAM-*-PLUS SASL mechanisms with OpenSSL and TLS < v1.3 (40f2452)
|
||||||
|
- Only signal "stream negotiation success" once (1cf09b1)
|
||||||
|
- Fix `sock_connect()` not looping over all DNS records returned if no `sockopt_cb` is set (5edc480)
|
||||||
|
- Replace usage of EBADFD, it's not in POSIX (#235)
|
||||||
|
|
||||||
|
0.13.0
|
||||||
|
- Fix connected/connecting signaling to user (#227)
|
||||||
|
- Fix wording of licensing terms (#225)
|
||||||
|
- Prepare for future changes in OpenSSL (#226)
|
||||||
|
- Improve Stream Management (#227) (#230)
|
||||||
|
- Add SCRAM-PLUS Variants (#228)
|
||||||
|
- Introduce XEP-0138 stream compression (#231)
|
||||||
|
- Deprecated the following API (#227):
|
||||||
|
- xmpp_conn_disable_tls() - replaced by a flag set by xmpp_conn_set_flags()
|
||||||
|
|
||||||
|
0.12.3
|
||||||
|
- Improve TCP-connection establishment (#221)
|
||||||
|
- Handle case where the server doesn't provide the `bind` feature (#224)
|
||||||
|
- Fix configure script for non-bash shells (#218)
|
||||||
|
- Parse JID's according to RFC7622 (#219)
|
||||||
|
- Fix potential memory leak in internal DNS resolver (#219)
|
||||||
|
- Fix potential memory leaks in `xmpp_conn_set_cafile()` and `xmpp_conn_set_capath()` (#219)
|
||||||
|
- Internal improvements (#219)
|
||||||
|
|
||||||
|
0.12.2
|
||||||
|
- Fix reconnect issues when Stream Management is enabled (#211)
|
||||||
|
- Fix resolver ... this time for real hopefully (fixup of #200) (#214)
|
||||||
|
- Fix clearing of password cache on resumed connection (#214)
|
||||||
|
- Improve detection&implementation of `va_copy` (#213)
|
||||||
|
- Fix Valgrind CI builds against LibreSSL (#212)
|
||||||
|
- Fix perf example on platforms where an `rdtsc()` equivalent isn't implemented (#212)
|
||||||
|
|
||||||
|
0.12.1
|
||||||
|
- Fix compilation in buildroot (#207)
|
||||||
|
- Fixes regarding OpenSSL (#208)
|
||||||
|
- Fix some build steps when builddir != srcdir (#208)
|
||||||
|
- Allow the user to disable build of examples (#209)
|
||||||
|
- CI builds against OpenSSL 3 (#206)
|
||||||
|
- Change the call signature of the following API (#208):
|
||||||
|
- xmpp_conn_set_client_cert() - the PKCS#12 file has now to be passed via the `cert`
|
||||||
|
parameter. Originally it was via `key`. Currently both styles are supported,
|
||||||
|
but in a future release only passing via `cert` will be accepted.
|
||||||
|
|
||||||
|
0.12.0
|
||||||
|
- Fix potential infinite loop in resolver (#200)
|
||||||
|
- Prevent potential memory leak in `xmpp_stanza_new_from_string()` (#205)
|
||||||
|
- Do proper SO versioning
|
||||||
|
- Add code coverage support (#188)
|
||||||
|
- Add support for password-protected TLS key & PKCS#12/PFX files (#195, #205)
|
||||||
|
- Stream-Management support - XEP-0198 (#184)
|
||||||
|
- New API:
|
||||||
|
- xmpp_conn_send_queue_len()
|
||||||
|
- xmpp_conn_send_queue_drop_element()
|
||||||
|
- xmpp_conn_get_sm_state()
|
||||||
|
- xmpp_conn_set_sm_state()
|
||||||
|
- xmpp_free_sm_state()
|
||||||
|
- xmpp_conn_get_keyfile()
|
||||||
|
- xmpp_conn_set_password_callback()
|
||||||
|
- xmpp_conn_set_password_retries()
|
||||||
|
- xmpp_stanza_get_child_by_path()
|
||||||
|
- xmpp_conn_set_sockopt_callback()
|
||||||
|
- xmpp_sockopt_cb_keepalive()
|
||||||
|
- The following APIs were public in the shared library, but not in `strophe.h`.
|
||||||
|
Now they are officially public API:
|
||||||
|
- xmpp_rand_new()
|
||||||
|
- xmpp_rand_free()
|
||||||
|
- xmpp_rand()
|
||||||
|
- xmpp_rand_bytes()
|
||||||
|
- xmpp_rand_nonce()
|
||||||
|
- xmpp_rand_bytes()
|
||||||
|
- The following APIs were public in the shared library, but not in `strophe.h`.
|
||||||
|
In a future version of the library they will be private without replacement:
|
||||||
|
- xmpp_alloc()
|
||||||
|
- xmpp_realloc()
|
||||||
|
- xmpp_strdup()
|
||||||
|
- xmpp_strndup()
|
||||||
|
- xmpp_strtok_r()
|
||||||
|
- xmpp_snprintf()
|
||||||
|
- xmpp_vsnprintf()
|
||||||
|
- xmpp_log()
|
||||||
|
- xmpp_error()
|
||||||
|
- xmpp_warn()
|
||||||
|
- xmpp_info()
|
||||||
|
- xmpp_debug()
|
||||||
|
- xmpp_debug_verbose()
|
||||||
|
- Deprecated the following API:
|
||||||
|
- xmpp_conn_set_keepalive() - replaced by xmpp_conn_set_sockopt_callback()
|
||||||
|
|
||||||
|
0.11.0
|
||||||
|
- SASL EXTERNAL support (XEP-0178)
|
||||||
|
- Client certificate can be provided for TLS negotiation. If the
|
||||||
|
certificate contains a single xmppAddr and JID is not provided with
|
||||||
|
xmpp_conn_set_jid(), the xmppAddr is chosen as JID
|
||||||
|
- <stream> element contains "from" attribute over TLS connections now
|
||||||
|
- GnuTLS can be selected optionally with configure script
|
||||||
|
- Support for manual certificate verification
|
||||||
|
- New API:
|
||||||
|
- xmpp_conn_set_client_cert()
|
||||||
|
- xmpp_conn_cert_xmppaddr_num()
|
||||||
|
- xmpp_conn_cert_xmppaddr()
|
||||||
|
- xmpp_conn_set_cafile()
|
||||||
|
- xmpp_conn_set_capath()
|
||||||
|
- xmpp_conn_set_certfail_handler()
|
||||||
|
- xmpp_conn_get_peer_cert()
|
||||||
|
- xmpp_tlscert_get_ctx()
|
||||||
|
- xmpp_tlscert_get_conn()
|
||||||
|
- xmpp_tlscert_get_pem()
|
||||||
|
- xmpp_tlscert_get_dnsname()
|
||||||
|
- xmpp_tlscert_get_string()
|
||||||
|
- xmpp_tlscert_get_description()
|
||||||
|
- xmpp_tlscert_free()
|
||||||
|
|
||||||
|
0.10.1
|
||||||
|
- Fixed compilation error when LibreSSL is used
|
||||||
|
- Fixed crash when NULL is provided as password
|
||||||
|
|
||||||
|
0.10.0
|
||||||
|
- Coding style has been unified
|
||||||
|
- SCRAM-SHA-256 and SCRAM-SHA-512 support
|
||||||
|
- c-ares support
|
||||||
|
- LibreSSL support
|
||||||
|
- Introduced global timed handlers that fire periodically regardless of
|
||||||
|
connections status, such a handler can be used to implement deferred
|
||||||
|
re-connection
|
||||||
|
- examples/register implements XEP-0077
|
||||||
|
- Fixed issue with IPv6 on Windows (#153)
|
||||||
|
- Improved portability across systems such as Haiku, Windows
|
||||||
|
- New API:
|
||||||
|
- xmpp_stanza_get_child_by_name_and_ns()
|
||||||
|
- xmpp_conn_is_connecting()
|
||||||
|
- xmpp_conn_is_connected()
|
||||||
|
- xmpp_conn_is_disconnected()
|
||||||
|
- xmpp_stanza_new_from_string()
|
||||||
|
- xmpp_stanza_add_child_ex()
|
||||||
|
- xmpp_stanza_get_context()
|
||||||
|
- xmpp_stanza_reply_error()
|
||||||
|
- xmpp_global_timed_handler_add()
|
||||||
|
- xmpp_global_timed_handler_delete()
|
||||||
|
|
||||||
|
0.9.3
|
||||||
|
- PLAIN mechanism is used only when no other mechanisms are supported
|
||||||
|
- Legacy authentication is disabled by default, can be enabled with
|
||||||
|
connection flag XMPP_CONN_FLAG_LEGACY_AUTH
|
||||||
|
- Session is not established if it is optional
|
||||||
|
- Fixed a bug causing a reused connection not to cleanup properly
|
||||||
|
- Improved debug logging in OpenSSL module
|
||||||
|
- Few memory leaks fixed
|
||||||
|
|
||||||
|
0.9.2
|
||||||
|
- OpenSSL tls module verifies certificate by default. Set flag
|
||||||
|
XMPP_CONN_FLAG_TRUST_TLS to ignore result of the verification
|
||||||
|
- Certificate hostname verification is forced for openssl-1.0.2 and
|
||||||
|
newer
|
||||||
|
- OpenSSL tls module disables insecure SSLv2 SSLv3 and TLSv1
|
||||||
|
- Support of handlers with the same callback function, but different
|
||||||
|
userdata
|
||||||
|
- System handlers are deleted on xmpp_conn_t reconnection. Old system
|
||||||
|
handlers could cause problems
|
||||||
|
- Default timeout for xmpp_run() is increased from 1 millisecond to 1
|
||||||
|
second in order to reduce CPU consumption
|
||||||
|
- Reduced memory usage in expat module
|
||||||
|
- New functions:
|
||||||
|
- xmpp_error_new()
|
||||||
|
- xmpp_send_error()
|
||||||
|
- xmpp_ctx_set_timeout()
|
||||||
|
- xmpp_sha1_digest()
|
||||||
|
|
||||||
|
0.9.1
|
||||||
|
- Fixed bug #95 (DNS lookup failing on Cygwin)
|
||||||
|
- Removed dependency on the check package
|
||||||
|
|
||||||
|
0.9.0
|
||||||
|
- IPv6 support
|
||||||
|
- Legacy SSL support
|
||||||
|
- Initial Android support
|
||||||
|
- Resolver returns all SRV records instead of one. Lookup is performed
|
||||||
|
according to RFC2052
|
||||||
|
- xmpp_connect_raw() provides access to a xmpp_conn object just after
|
||||||
|
establishing TCP connection. This allows to implement in-band
|
||||||
|
registration, authentication mechanisms or serverless messaging
|
||||||
|
- xmpp_conn_t object is reusable now and can be reconnected with saving
|
||||||
|
all handlers, flags, jid and password
|
||||||
|
- New API:
|
||||||
|
- xmpp_uuid_gen()
|
||||||
|
- xmpp_connect_raw()
|
||||||
|
- xmpp_conn_open_stream_default()
|
||||||
|
- xmpp_conn_open_stream()
|
||||||
|
- xmpp_conn_tls_start()
|
||||||
|
- xmpp_conn_get_flags()
|
||||||
|
- xmpp_conn_set_flags()
|
||||||
|
- xmpp_conn_set_keepalive()
|
||||||
|
- xmpp_conn_is_secured()
|
||||||
|
- xmpp_stanza_del_attribute()
|
||||||
|
- xmpp_stanza_get_to()
|
||||||
|
- xmpp_stanza_get_from()
|
||||||
|
- xmpp_stanza_set_to()
|
||||||
|
- xmpp_stanza_set_from()
|
||||||
|
- xmpp_stanza_reply()
|
||||||
|
- xmpp_message_new()
|
||||||
|
- xmpp_message_get_body()
|
||||||
|
- xmpp_message_set_body()
|
||||||
|
- xmpp_iq_new()
|
||||||
|
- xmpp_presence_new()
|
||||||
|
- Exposed private API:
|
||||||
|
- xmpp_jid_new()
|
||||||
|
- xmpp_jid_bare()
|
||||||
|
- xmpp_jid_node()
|
||||||
|
- xmpp_jid_domain()
|
||||||
|
- xmpp_jid_resource()
|
||||||
|
- xmpp_stanza_get_attribute_count()
|
||||||
|
- xmpp_stanza_get_attributes()
|
||||||
|
|
||||||
|
0.8.8
|
||||||
|
- XML namespace support
|
||||||
|
- XEP-0114 support
|
||||||
|
|
||||||
|
0.8.7
|
||||||
|
- SCRAM-SHA-1 authentication mechanism
|
||||||
|
- pkg-config support
|
||||||
|
|
||||||
|
0.8.5
|
||||||
|
- libtoolize to generate .so
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
libstrophe XMPP client library
|
libstrophe XMPP client library
|
||||||
Copyright (C) 2005-2009 Collecta, Inc.
|
Copyright (C) 2005-2009 Collecta, Inc.
|
||||||
|
|
||||||
This program is dual licensed under the MIT and GPLv3 licenses.
|
This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
Please the files MIT-LICENSE.txt and GPL-LICENSE.txt for details.
|
Please the files MIT-LICENSE.txt and GPL-LICENSE.txt for details.
|
||||||
|
|||||||
336
Makefile.am
336
Makefile.am
@@ -1,27 +1,109 @@
|
|||||||
AUTOMAKE_OPTIONS = subdir-objects
|
AUTOMAKE_OPTIONS = subdir-objects
|
||||||
|
|
||||||
AM_CFLAGS = -g -Wall
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
|
|
||||||
|
COVERAGE_CFLAGS=@COVERAGE_CFLAGS@
|
||||||
|
COVERAGE_LDFLAGS=@COVERAGE_LDFLAGS@
|
||||||
|
COVERAGE_PRE=@COVERAGE_PRE@
|
||||||
|
COVERAGE_POST=@COVERAGE_POST@
|
||||||
|
|
||||||
PARSER_CFLAGS=@PARSER_CFLAGS@
|
PARSER_CFLAGS=@PARSER_CFLAGS@
|
||||||
PARSER_LIBS=@PARSER_LIBS@
|
PARSER_LIBS=@PARSER_LIBS@
|
||||||
|
|
||||||
SSL_LIBS = @openssl_LIBS@
|
ZLIB_CFLAGS=@ZLIB_CFLAGS@
|
||||||
|
ZLIB_LIBS=@ZLIB_LIBS@
|
||||||
|
|
||||||
STROPHE_FLAGS = -I$(top_srcdir)
|
if TLS_WITH_GNUTLS
|
||||||
STROPHE_LIBS = libstrophe.la
|
SSL_CFLAGS = @gnutls_CFLAGS@
|
||||||
|
SSL_LIBS = @gnutls_LIBS@
|
||||||
|
else
|
||||||
|
if !TLS_WITH_SCHANNEL
|
||||||
|
SSL_CFLAGS = @openssl_CFLAGS@
|
||||||
|
SSL_LIBS = @openssl_LIBS@
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
MINGW_LIBS = @MINGW_LIBS@
|
||||||
|
|
||||||
|
RESOLV_CFLAGS = @RESOLV_CFLAGS@
|
||||||
|
RESOLV_LIBS = @RESOLV_LIBS@
|
||||||
|
|
||||||
|
WARNING_FLAGS = @WARNING_FLAGS@
|
||||||
|
STROPHE_FLAGS = -I$(top_srcdir) $(WARNING_FLAGS)
|
||||||
|
STROPHE_LIBS = $(COVERAGE_PRE) libstrophe.la $(COVERAGE_POST) $(COVERAGE_LDFLAGS)
|
||||||
|
|
||||||
## Main build targets
|
## Main build targets
|
||||||
lib_LTLIBRARIES = libstrophe.la
|
lib_LTLIBRARIES = libstrophe.la
|
||||||
|
|
||||||
libstrophe_la_CFLAGS=$(STROPHE_FLAGS) $(PARSER_CFLAGS)
|
libstrophe_la_CFLAGS = $(SSL_CFLAGS) $(STROPHE_FLAGS) $(PARSER_CFLAGS) $(RESOLV_CFLAGS) $(COVERAGE_CFLAGS) $(ZLIB_CFLAGS)
|
||||||
libstrophe_la_LDFLAGS=$(SSL_LIBS) $(PARSER_LIBS)
|
libstrophe_la_LDFLAGS = $(SSL_LIBS) $(PARSER_LIBS) $(RESOLV_LIBS) $(MINGW_LIBS) $(ZLIB_LIBS) -no-undefined
|
||||||
libstrophe_la_SOURCES = src/auth.c src/conn.c src/ctx.c \
|
# Export only public API
|
||||||
src/event.c src/handler.c src/hash.c \
|
libstrophe_la_LDFLAGS += -export-symbols-regex '^xmpp_' -version-info @VERSION_INFO@
|
||||||
src/jid.c src/md5.c src/sasl.c src/sha1.c \
|
|
||||||
src/snprintf.c src/sock.c src/stanza.c src/thread.c \
|
libstrophe_la_SOURCES = \
|
||||||
src/tls_openssl.c src/util.c \
|
src/auth.c \
|
||||||
src/common.h src/hash.h src/md5.h src/ostypes.h src/parser.h \
|
src/conn.c \
|
||||||
src/sasl.h src/sha1.h src/sock.h src/thread.h src/tls.h src/util.h
|
src/crypto.c \
|
||||||
|
src/ctx.c \
|
||||||
|
src/deprecated.c \
|
||||||
|
src/event.c \
|
||||||
|
src/handler.c \
|
||||||
|
src/hash.c \
|
||||||
|
src/jid.c \
|
||||||
|
src/md5.c \
|
||||||
|
src/rand.c \
|
||||||
|
src/resolver.c \
|
||||||
|
src/sasl.c \
|
||||||
|
src/scram.c \
|
||||||
|
src/sha1.c \
|
||||||
|
src/sha256.c \
|
||||||
|
src/sha512.c \
|
||||||
|
src/sock.c \
|
||||||
|
src/stanza.c \
|
||||||
|
src/tls.c \
|
||||||
|
src/util.c \
|
||||||
|
src/uuid.c
|
||||||
|
libstrophe_la_SOURCES += \
|
||||||
|
src/common.h \
|
||||||
|
src/hash.h \
|
||||||
|
src/md5.h \
|
||||||
|
src/ostypes.h \
|
||||||
|
src/parser.h \
|
||||||
|
src/resolver.h \
|
||||||
|
src/sasl.h \
|
||||||
|
src/scram.h \
|
||||||
|
src/sha.h \
|
||||||
|
src/sha1.h \
|
||||||
|
src/sha256.h \
|
||||||
|
src/sha512.h \
|
||||||
|
src/snprintf.h \
|
||||||
|
src/sock.h \
|
||||||
|
src/tls.h \
|
||||||
|
src/util.h
|
||||||
|
|
||||||
|
if NEED_SNPRINTF
|
||||||
|
libstrophe_la_SOURCES += src/snprintf.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
if DISABLE_COMPRESSION
|
||||||
|
libstrophe_la_SOURCES += src/compression_dummy.c
|
||||||
|
else
|
||||||
|
libstrophe_la_SOURCES += src/compression.c
|
||||||
|
endif
|
||||||
|
|
||||||
|
if DISABLE_TLS
|
||||||
|
libstrophe_la_SOURCES += src/tls_dummy.c
|
||||||
|
else
|
||||||
|
if TLS_WITH_GNUTLS
|
||||||
|
libstrophe_la_SOURCES += src/tls_gnutls.c
|
||||||
|
else
|
||||||
|
if TLS_WITH_SCHANNEL
|
||||||
|
libstrophe_la_SOURCES += src/tls_schannel.c
|
||||||
|
else
|
||||||
|
libstrophe_la_SOURCES += src/tls_openssl.c
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
if PARSER_EXPAT
|
if PARSER_EXPAT
|
||||||
libstrophe_la_SOURCES += src/parser_expat.c
|
libstrophe_la_SOURCES += src/parser_expat.c
|
||||||
@@ -30,30 +112,236 @@ libstrophe_la_SOURCES += src/parser_libxml2.c
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
include_HEADERS = strophe.h
|
include_HEADERS = strophe.h
|
||||||
noinst_HEADERS = strophepp.h
|
|
||||||
|
|
||||||
EXTRA_DIST = docs
|
pkgconfig_DATA = libstrophe.pc
|
||||||
|
|
||||||
|
EXTRA_DIST = \
|
||||||
|
_clang-format \
|
||||||
|
Doxyfile \
|
||||||
|
GPL-LICENSE.txt \
|
||||||
|
LICENSE.txt \
|
||||||
|
MIT-LICENSE.txt \
|
||||||
|
bootstrap.sh \
|
||||||
|
build-android.sh \
|
||||||
|
testbuild.sh \
|
||||||
|
examples/README.md \
|
||||||
|
jni/Android.mk \
|
||||||
|
jni/Application.mk \
|
||||||
|
m4/ax_valgrind_check.m4 \
|
||||||
|
tests/cert.pem \
|
||||||
|
tests/cert.pfx \
|
||||||
|
tests/cert.emptypass.pfx \
|
||||||
|
tests/cert.nopass.pfx \
|
||||||
|
tests/key.pem \
|
||||||
|
tests/key_encrypted.pem \
|
||||||
|
tests/res_query_dump.c
|
||||||
|
|
||||||
|
if EXAMPLES
|
||||||
## Examples
|
## Examples
|
||||||
noinst_PROGRAMS = examples/active examples/roster examples/basic examples/bot
|
noinst_PROGRAMS = \
|
||||||
|
examples/active \
|
||||||
|
examples/basic \
|
||||||
|
examples/bot \
|
||||||
|
examples/complex \
|
||||||
|
examples/component \
|
||||||
|
examples/perf \
|
||||||
|
examples/register \
|
||||||
|
examples/roster \
|
||||||
|
examples/uuid \
|
||||||
|
examples/vcard
|
||||||
|
|
||||||
examples_active_SOURCES = examples/active.c
|
examples_active_SOURCES = examples/active.c
|
||||||
examples_active_CFLAGS = $(STROPHE_FLAGS)
|
examples_active_CFLAGS = $(STROPHE_FLAGS)
|
||||||
examples_active_LDADD = $(STROPHE_LIBS)
|
examples_active_LDADD = $(STROPHE_LIBS)
|
||||||
examples_roster_SOURCES = examples/roster.c
|
|
||||||
examples_roster_CFLAGS = $(STROPHE_FLAGS)
|
|
||||||
examples_roster_LDADD = $(STROPHE_LIBS)
|
|
||||||
examples_basic_SOURCES = examples/basic.c
|
examples_basic_SOURCES = examples/basic.c
|
||||||
examples_basic_CFLAGS = $(STROPHE_FLAGS)
|
examples_basic_CFLAGS = $(STROPHE_FLAGS)
|
||||||
examples_basic_LDADD = $(STROPHE_LIBS)
|
examples_basic_LDADD = $(STROPHE_LIBS)
|
||||||
examples_bot_SOURCES = examples/bot.c
|
examples_bot_SOURCES = examples/bot.c
|
||||||
examples_bot_CFLAGS = $(STROPHE_FLAGS)
|
examples_bot_CFLAGS = $(STROPHE_FLAGS)
|
||||||
examples_bot_LDADD = $(STROPHE_LIBS)
|
examples_bot_LDADD = $(STROPHE_LIBS)
|
||||||
|
examples_complex_SOURCES = examples/complex.c
|
||||||
|
examples_complex_CFLAGS = $(STROPHE_FLAGS)
|
||||||
|
examples_complex_LDADD = $(STROPHE_LIBS)
|
||||||
|
examples_component_SOURCES = examples/component.c
|
||||||
|
examples_component_CFLAGS = $(STROPHE_FLAGS)
|
||||||
|
examples_component_LDADD = $(STROPHE_LIBS)
|
||||||
|
examples_perf_SOURCES = examples/perf.c
|
||||||
|
examples_perf_CFLAGS = $(STROPHE_FLAGS)
|
||||||
|
examples_perf_LDADD = $(STROPHE_LIBS)
|
||||||
|
examples_register_SOURCES = examples/register.c
|
||||||
|
examples_register_CFLAGS = $(STROPHE_FLAGS)
|
||||||
|
examples_register_LDADD = $(STROPHE_LIBS)
|
||||||
|
examples_roster_SOURCES = examples/roster.c
|
||||||
|
examples_roster_CFLAGS = $(STROPHE_FLAGS)
|
||||||
|
examples_roster_LDADD = $(STROPHE_LIBS)
|
||||||
|
examples_uuid_SOURCES = examples/uuid.c
|
||||||
|
examples_uuid_CFLAGS = $(STROPHE_FLAGS)
|
||||||
|
examples_uuid_LDADD = $(STROPHE_LIBS)
|
||||||
|
examples_vcard_SOURCES = examples/vcard.c
|
||||||
|
examples_vcard_CFLAGS = $(STROPHE_FLAGS)
|
||||||
|
examples_vcard_LDADD = $(STROPHE_LIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
TESTS = tests/check_parser
|
STATIC_TESTS = \
|
||||||
check_PROGRAMS = tests/check_parser
|
tests/check_parser \
|
||||||
tests_check_parser_SOURCES = tests/check_parser.c tests/test.h
|
tests/test_hash \
|
||||||
tests_check_parser_CFLAGS = @check_CFLAGS@ $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
|
tests/test_jid \
|
||||||
|
tests/test_ctx \
|
||||||
|
tests/test_send_queue \
|
||||||
|
tests/test_string \
|
||||||
|
tests/test_resolver
|
||||||
|
|
||||||
|
TESTS = \
|
||||||
|
tests/test_sha1 \
|
||||||
|
tests/test_sha256 \
|
||||||
|
tests/test_sha512 \
|
||||||
|
tests/test_md5 \
|
||||||
|
tests/test_rand \
|
||||||
|
tests/test_scram \
|
||||||
|
tests/test_base64 \
|
||||||
|
tests/test_snprintf \
|
||||||
|
tests/test_stanza
|
||||||
|
|
||||||
|
if !DISABLE_STATIC
|
||||||
|
TESTS += $(STATIC_TESTS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !DISABLE_TLS
|
||||||
|
TESTS += tests/test_xmppaddr
|
||||||
|
endif
|
||||||
|
|
||||||
|
check_PROGRAMS = $(TESTS)
|
||||||
|
|
||||||
|
if FUZZ
|
||||||
|
check_PROGRAMS += tests/test_fuzz_parser tests/test_fuzz_resolver
|
||||||
|
|
||||||
|
tests_test_fuzz_parser_SOURCES = tests/test_fuzz_parser.c
|
||||||
|
tests_test_fuzz_parser_CFLAGS = -fsanitize=fuzzer,address $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
|
||||||
-I$(top_srcdir)/src
|
-I$(top_srcdir)/src
|
||||||
tests_check_parser_LDADD = @check_LIBS@ $(STROPHE_LIBS)
|
tests_test_fuzz_parser_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_fuzz_parser_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_fuzz_resolver_SOURCES = tests/test_fuzz_resolver.c
|
||||||
|
tests_test_fuzz_resolver_CFLAGS = -fsanitize=fuzzer,address $(resolver_CFLAGS) $(STROPHE_FLAGS) \
|
||||||
|
-I$(top_srcdir)/src
|
||||||
|
tests_test_fuzz_resolver_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_fuzz_resolver_LDFLAGS = -static
|
||||||
|
endif
|
||||||
|
|
||||||
|
tests_check_parser_SOURCES = tests/check_parser.c tests/test.h
|
||||||
|
tests_check_parser_CFLAGS = $(PARSER_CFLAGS) $(STROPHE_FLAGS) \
|
||||||
|
-I$(top_srcdir)/src
|
||||||
|
tests_check_parser_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_check_parser_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_ctx_SOURCES = tests/test_ctx.c
|
||||||
|
tests_test_ctx_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
tests_test_ctx_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_ctx_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_base64_SOURCES = tests/test_base64.c tests/test.c tests/test.h
|
||||||
|
tests_test_base64_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
tests_test_base64_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_base64_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_hash_SOURCES = tests/test_hash.c
|
||||||
|
tests_test_hash_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
tests_test_hash_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_hash_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_jid_SOURCES = tests/test_jid.c
|
||||||
|
tests_test_jid_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
tests_test_jid_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_jid_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_resolver_SOURCES = tests/test_resolver.c tests/test.h
|
||||||
|
tests_test_resolver_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
tests_test_resolver_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_resolver_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_rand_SOURCES = tests/test_rand.c tests/test.c src/sha1.c
|
||||||
|
tests_test_rand_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
|
||||||
|
tests_test_scram_SOURCES = tests/test_scram.c tests/test.c src/sha1.c \
|
||||||
|
src/sha256.c src/sha512.c
|
||||||
|
tests_test_scram_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
|
||||||
|
tests_test_sha1_SOURCES = tests/test_sha1.c src/sha1.c
|
||||||
|
tests_test_sha1_CFLAGS = -I$(top_srcdir)/src
|
||||||
|
|
||||||
|
tests_test_sha256_SOURCES = tests/test_sha256.c tests/test.c src/sha256.c
|
||||||
|
tests_test_sha256_CFLAGS = -I$(top_srcdir)/src
|
||||||
|
|
||||||
|
tests_test_sha512_SOURCES = tests/test_sha512.c tests/test.c src/sha512.c
|
||||||
|
tests_test_sha512_CFLAGS = -I$(top_srcdir)/src
|
||||||
|
|
||||||
|
tests_test_md5_SOURCES = tests/test_md5.c tests/test.c src/md5.c
|
||||||
|
tests_test_md5_CFLAGS = -I$(top_srcdir)/src
|
||||||
|
|
||||||
|
tests_test_send_queue_SOURCES = tests/test_send_queue.c
|
||||||
|
tests_test_send_queue_CFLAGS = -I$(top_srcdir)/src
|
||||||
|
tests_test_send_queue_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_send_queue_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_snprintf_SOURCES = tests/test_snprintf.c
|
||||||
|
tests_test_snprintf_CFLAGS = -I$(top_srcdir)/src
|
||||||
|
|
||||||
|
tests_test_string_SOURCES = tests/test_string.c tests/test.h
|
||||||
|
tests_test_string_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
tests_test_string_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_string_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_stanza_SOURCES = tests/test_stanza.c tests/test.h
|
||||||
|
tests_test_stanza_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
tests_test_stanza_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_stanza_LDFLAGS = -static
|
||||||
|
|
||||||
|
tests_test_xmppaddr_SOURCES = tests/test_xmppaddr.c
|
||||||
|
tests_test_xmppaddr_CFLAGS = $(STROPHE_FLAGS) -I$(top_srcdir)/src
|
||||||
|
tests_test_xmppaddr_LDADD = $(STROPHE_LIBS)
|
||||||
|
tests_test_xmppaddr_LDFLAGS = -static
|
||||||
|
|
||||||
|
format:
|
||||||
|
@echo " * run clang-format on all sources"
|
||||||
|
@dos2unix -k -q $(top_srcdir)/src/*.[ch] $(top_srcdir)/*.h $(top_srcdir)/tests/*.[ch] $(top_srcdir)/examples/*.c
|
||||||
|
@clang-format -i $(top_srcdir)/src/*.[ch] $(top_srcdir)/*.h $(top_srcdir)/tests/*.[ch] $(top_srcdir)/examples/*.c
|
||||||
|
|
||||||
|
if COVERAGE
|
||||||
|
MOSTLYCLEANFILES = src/*.gcno src/*.gcda coverage.info
|
||||||
|
clean-local:
|
||||||
|
-rm -rf coverage/
|
||||||
|
|
||||||
|
coverage: check
|
||||||
|
@lcov --capture --no-external --directory src -q --output-file coverage.info
|
||||||
|
@genhtml coverage.info --output-directory coverage -q
|
||||||
|
endif
|
||||||
|
|
||||||
|
release: test-release-all
|
||||||
|
|
||||||
|
dist-archives:
|
||||||
|
$(MAKE) dist
|
||||||
|
$(MAKE) dist-bzip2
|
||||||
|
$(MAKE) dist-xz
|
||||||
|
|
||||||
|
test-release: dist
|
||||||
|
@touch testbuild-$(PACKAGE_VERSION).log && ln -sf testbuild-$(PACKAGE_VERSION).log testbuild.log
|
||||||
|
@mkdir -p test-release && cp $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.* test-release && pushd test-release && \
|
||||||
|
tar xzf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
|
||||||
|
echo "Success" && popd
|
||||||
|
|
||||||
|
test-release-all: dist-archives
|
||||||
|
@touch testbuild-$(PACKAGE_VERSION).log && ln -sf testbuild-$(PACKAGE_VERSION).log testbuild.log
|
||||||
|
@mkdir -p test-release && cp $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.* test-release && pushd test-release && \
|
||||||
|
tar xJf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.xz && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
|
||||||
|
tar xjf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.bz2 && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
|
||||||
|
tar xzf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION).tar.gz && pushd $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && ./testbuild.sh && popd && rm -rf $(PACKAGE_TARNAME)-$(PACKAGE_VERSION) && \
|
||||||
|
echo "Success" && popd
|
||||||
|
|
||||||
|
docs:
|
||||||
|
@SRCDIR=$(top_srcdir) doxygen -q $(top_srcdir)/Doxyfile
|
||||||
|
|
||||||
|
@VALGRIND_CHECK_RULES@
|
||||||
|
|
||||||
|
.PHONY: docs test-release
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
# libstrophe
|
libstrophe [](https://github.com/strophe/libstrophe/actions/workflows/main.yml?query=branch%3Amaster+++)
|
||||||
|
==========
|
||||||
|
|
||||||
libstrophe is a lightweight XMPP client library written in C. It has
|
libstrophe is a lightweight XMPP client library written in C. It has
|
||||||
minimal dependencies and is configurable for various environments. It
|
minimal dependencies and is configurable for various environments. It
|
||||||
runs well on both Linux, Unix, and Windows based platforms.
|
runs well on Linux, Unix and Windows based platforms.
|
||||||
|
|
||||||
Its goals are:
|
libstrophe is dual licensed under MIT or GPLv3.
|
||||||
|
|
||||||
- usable quickly
|
Build Instructions
|
||||||
- well documented
|
------------------
|
||||||
- reliable
|
|
||||||
|
|
||||||
## Build Instructions
|
|
||||||
|
|
||||||
If you are building from a source control checkout, run:
|
If you are building from a source control checkout, run:
|
||||||
|
|
||||||
@@ -23,9 +21,7 @@ From the top-level directory, run the following commands:
|
|||||||
./configure
|
./configure
|
||||||
make
|
make
|
||||||
|
|
||||||
This will create a static library, also in the top-level
|
The public API is defined in `strophe.h` which is in the
|
||||||
directory, which can be linked into other programs. The
|
|
||||||
public API is defined in `strophe.h` which is also in the
|
|
||||||
top-level directory.
|
top-level directory.
|
||||||
|
|
||||||
The `examples` directory contains some examples of how to
|
The `examples` directory contains some examples of how to
|
||||||
@@ -41,26 +37,44 @@ another path use the `--prefix` option during configure, e.g.:
|
|||||||
|
|
||||||
./configure --prefix=/usr
|
./configure --prefix=/usr
|
||||||
|
|
||||||
## Requirements
|
### Android
|
||||||
|
|
||||||
|
Run script `build-android.sh` and follow the instructions. You will
|
||||||
|
need expat sources and android-ndk.
|
||||||
|
|
||||||
|
### Code Coverage
|
||||||
|
|
||||||
|
If you want to create a code coverage report, run:
|
||||||
|
|
||||||
|
./configure --enable-coverage
|
||||||
|
make coverage
|
||||||
|
|
||||||
|
The coverage report can be found in `./coverage/index.html`.
|
||||||
|
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
libstrophe requires:
|
libstrophe requires:
|
||||||
|
|
||||||
- expat or libxml2 - expat is the default; use --with-libxml2 to
|
- expat or libxml2 - expat is the default; use `--with-libxml2` to
|
||||||
switch
|
switch
|
||||||
- libresolv on UNIX systems - make sure you include -lresolv
|
- openssl or GnuTLS on UNIX systems - openssl is default; use
|
||||||
if you are compiling by hand.
|
`--with-gnutls` to switch
|
||||||
- libtool
|
|
||||||
|
|
||||||
In addition, if you wish to run the unit tests, you will need the
|
To build libstrophe using autotools you will need `autoconf`,
|
||||||
check package.
|
`automake`, `libtool` and `pkg-config`.
|
||||||
|
|
||||||
### OS X (with Homebrew package manager)
|
To run code coverage analysis you will need `gcov` and `lcov`.
|
||||||
|
|
||||||
You can install libstrophe with:
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
brew install libstrophe
|
libstrophe package has been added to popular Linux distributions,
|
||||||
|
BSD systems and OSX package managers.
|
||||||
|
|
||||||
## Documentation
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
API documentation is inline with the code and conforms to Doxygen
|
API documentation is inline with the code and conforms to Doxygen
|
||||||
standards. You can generate an HTML version of the API documentation
|
standards. You can generate an HTML version of the API documentation
|
||||||
@@ -68,4 +82,17 @@ by running:
|
|||||||
|
|
||||||
doxygen
|
doxygen
|
||||||
|
|
||||||
|
or if you have everything configured properly:
|
||||||
|
|
||||||
|
make docs
|
||||||
|
|
||||||
Then open `docs/html/index.html`.
|
Then open `docs/html/index.html`.
|
||||||
|
|
||||||
|
An online version of the documentation of the latest release is available on https://strophe.im/libstrophe/
|
||||||
|
|
||||||
|
Releases
|
||||||
|
--------
|
||||||
|
|
||||||
|
Releases are signed with the GPG key with ID `F8ADC1F9A68A7AFF0E2C89E4391A5EFC2D1709DE`.
|
||||||
|
|
||||||
|
It can be found e.g. on https://keys.openpgp.org/
|
||||||
|
|||||||
18
_clang-format
Normal file
18
_clang-format
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
AlignConsecutiveAssignments: 'false'
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AllowShortBlocksOnASingleLine: 'true'
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: 'false'
|
||||||
|
BinPackParameters: 'false'
|
||||||
|
BreakBeforeBraces: Linux
|
||||||
|
ColumnLimit: '80'
|
||||||
|
DerivePointerAlignment: 'false'
|
||||||
|
IndentGotoLabels: false
|
||||||
|
IndentWidth: '4'
|
||||||
|
PointerAlignment: Right
|
||||||
|
SortIncludes: 'false'
|
||||||
|
TabWidth: '4'
|
||||||
|
UseTab: Never
|
||||||
|
|
||||||
|
...
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Some versions of aclocal fail if m4/ doesn't exist
|
||||||
|
mkdir -p m4
|
||||||
autoreconf -i
|
autoreconf -i
|
||||||
|
|||||||
39
build-android.sh
Executable file
39
build-android.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
DIR=`dirname $0`
|
||||||
|
EXPAT_PATH="$DIR/expat"
|
||||||
|
EXPAT_FILE='lib/expat.h'
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo Attention: libstrophe will be built without TLS support. To enable TLS
|
||||||
|
echo you need to replace \'src/tls_dummy.c\' with \'src/tls_opnessl.c\' in
|
||||||
|
echo \'jni/Android.mk\', add path to the openssl headers and link your program
|
||||||
|
echo with libssl and libcrypto.
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ ! -d $EXPAT_PATH ]; then
|
||||||
|
mkdir $EXPAT_PATH
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TODO Accept expat tarball as argument and extract it to the right place.
|
||||||
|
# Or download sources from sf.net.
|
||||||
|
|
||||||
|
if [ ! -d $EXPAT_PATH/lib -o ! -f "$EXPAT_PATH/$EXPAT_FILE" ]; then
|
||||||
|
cat <<EOT
|
||||||
|
Error: expat sources not found.
|
||||||
|
|
||||||
|
Extract expat sources to $EXPAT_PATH. Make sure $EXPAT_PATH/$EXPAT_FILE and
|
||||||
|
other source files exist.
|
||||||
|
EOT
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ndk-build -C "$DIR" clean || exit 1
|
||||||
|
ndk-build -C "$DIR" || exit 1
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "basic example:"
|
||||||
|
ls -l "$DIR"/libs/*/basic
|
||||||
|
echo
|
||||||
|
echo "libstrophe.a:"
|
||||||
|
ls -l "$DIR"/obj/local/*/libstrophe.a
|
||||||
323
configure.ac
323
configure.ac
@@ -1,4 +1,14 @@
|
|||||||
AC_INIT([libstrophe], [0.8.5], [jack@metajack.im])
|
m4_define([v_maj], [0])
|
||||||
|
m4_define([v_min], [13])
|
||||||
|
m4_define([v_patch], [1])
|
||||||
|
m4_define([project_version], [v_maj.v_min.v_patch])
|
||||||
|
|
||||||
|
m4_define([lt_cur], m4_eval(v_maj + v_min))
|
||||||
|
m4_define([lt_rev], v_patch)
|
||||||
|
m4_define([lt_age], v_min)
|
||||||
|
|
||||||
|
AC_INIT([libstrophe], [project_version], [jack@metajack.im])
|
||||||
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
AM_INIT_AUTOMAKE([foreign])
|
AM_INIT_AUTOMAKE([foreign])
|
||||||
LT_INIT([dlopen])
|
LT_INIT([dlopen])
|
||||||
|
|
||||||
@@ -8,61 +18,294 @@ PKG_PROG_PKG_CONFIG
|
|||||||
|
|
||||||
AC_CANONICAL_HOST
|
AC_CANONICAL_HOST
|
||||||
AS_CASE([$host_os],
|
AS_CASE([$host_os],
|
||||||
[freebsd*], [PLATFORM="freebsd"],
|
[freebsd*], [PLATFORM="bsd"],
|
||||||
[PLATFORM="nix"])
|
[openbsd*], [PLATFORM="bsd"],
|
||||||
|
[netbsd*], [PLATFORM="bsd"],
|
||||||
|
[*nto*|*qnx*], [PLATFORM="qnx"],
|
||||||
|
[*solaris*], [PLATFORM="solaris"],
|
||||||
|
[*android*], [PLATFORM="android"],
|
||||||
|
[*haiku*], [PLATFORM="haiku"],
|
||||||
|
[*mingw*], [PLATFORM="win32"
|
||||||
|
MINGW_LIBS="-lws2_32"],
|
||||||
|
[PLATFORM="nix"])
|
||||||
|
|
||||||
PKG_CHECK_MODULES([openssl], [openssl], [], [AC_MSG_ERROR([openssl not found; openssl required])])
|
WARNING_FLAGS="-Wall"
|
||||||
PKG_CHECK_MODULES([check], [check >= 0.9.4], [], [AC_MSG_WARN([libcheck not found; unit tests will not be compilable])])
|
|
||||||
|
|
||||||
|
AS_CASE([$PLATFORM],
|
||||||
|
[haiku], [],
|
||||||
|
[WARNING_FLAGS="$WARNING_FLAGS -Wextra"])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([examples],
|
||||||
|
[AS_HELP_STRING([--disable-examples], [turn off examples])],
|
||||||
|
[case "${enableval}" in yes) examples=true ;; no) examples=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --disable-examples]) ;; esac],[examples=true])
|
||||||
|
AM_CONDITIONAL([EXAMPLES], [test x$examples = xtrue])
|
||||||
AC_ARG_WITH([libxml2],
|
AC_ARG_WITH([libxml2],
|
||||||
[AS_HELP_STRING([--with-libxml2], [use libxml2 for XML parsing])],
|
[AS_HELP_STRING([--with-libxml2], [use libxml2 for XML parsing, expat is the default])])
|
||||||
[with_libxml2=check],
|
AC_ARG_WITH([gnutls],
|
||||||
[with_libxml2=no])
|
[AS_HELP_STRING([--with-gnutls], [use GnuTLS for TLS support, OpenSSL is the default])])
|
||||||
|
AC_ARG_WITH([schannel],
|
||||||
|
[AS_HELP_STRING([--with-schannel], [use Windows Schannel for TLS support, OpenSSL is the default])])
|
||||||
|
AC_ARG_ENABLE([tls],
|
||||||
|
[AS_HELP_STRING([--disable-tls], [disable TLS support])])
|
||||||
|
AC_ARG_ENABLE([cares],
|
||||||
|
[AS_HELP_STRING([--enable-cares], [use c-ares for DNS resolution])])
|
||||||
|
AC_ARG_ENABLE([getrandom],
|
||||||
|
[AS_HELP_STRING([--disable-getrandom], [disable usage of the getrandom() system call])])
|
||||||
|
AC_ARG_ENABLE([zlib],
|
||||||
|
[AS_HELP_STRING([--disable-zlib], [disable compression support])])
|
||||||
|
|
||||||
if test "x$with_libxml2" != xno; then
|
AC_ARG_ENABLE([fuzzing],
|
||||||
PKG_CHECK_MODULES([libxml2], [libxml-2.0 >= 2.7],
|
[AS_HELP_STRING([--enable-fuzzing], [turn on fuzzing test])],
|
||||||
[with_libxml2=yes], [dummy=1])
|
[case "${enableval}" in yes) fuzzing=true ;; no) fuzzing=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-fuzzing]) ;; esac],[fuzzing=false])
|
||||||
if test "x$with_libxml2" != xyes; then
|
AM_CONDITIONAL([FUZZ], [test x$fuzzing = xtrue])
|
||||||
old_CFLAGS=$CFLAGS
|
|
||||||
CFLAGS="-I${includedir}/libxml2"
|
if test "x$enable_fuzzing" = "xyes" ; then
|
||||||
AC_CHECK_HEADER([libxml/parser.h],
|
if test "x$CC" != "xclang" ; then
|
||||||
[with_libxml2=yes; libxml2_CFLAGS="-I${includedir}/libxml2"; libxml2_LIBS="-lxml2"],
|
AC_MSG_ERROR(["You need to set CC=clang to use --enable-fuzzing, used $CC"])
|
||||||
[AC_MSG_ERROR([libxml2 not found.])])
|
fi
|
||||||
CFLAGS=$old_CFLAGS
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
PKG_CHECK_MODULES([expat], [expat >= 2.0.0], [],
|
|
||||||
[AC_CHECK_HEADER(expat.h, [expat_LIBS="-lexpat"], [AC_MSG_ERROR([expat not found; expat required.])])])
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "x$with_libxml2" = xyes; then
|
AC_ARG_ENABLE([coverage],
|
||||||
with_parser=libxml2
|
[AS_HELP_STRING([--enable-coverage], [turn on coverage for tests])],
|
||||||
PARSER_NAME=libxml2
|
[case "${enableval}" in yes) coverage=true ;; no) coverage=false ;; *) AC_MSG_ERROR([bad value ${enableval} for --enable-coverage]) ;; esac],[coverage=false])
|
||||||
PARSER_CFLAGS=$libxml2_CFLAGS
|
AM_CONDITIONAL([COVERAGE], [test x$coverage = xtrue])
|
||||||
PARSER_LIBS=$libxml2_LIBS
|
|
||||||
|
if test "$enable_coverage" = "yes"; then
|
||||||
|
COVERAGE_CFLAGS="--coverage -g"
|
||||||
|
COVERAGE_LDFLAGS="--coverage -lgcov"
|
||||||
|
COVERAGE_PRE="-Wl,--whole-archive"
|
||||||
|
COVERAGE_POST="-Wl,--no-whole-archive"
|
||||||
else
|
else
|
||||||
with_parser=expat
|
COVERAGE_CFLAGS=""
|
||||||
PARSER_NAME=expat
|
COVERAGE_LDFLAGS=""
|
||||||
PARSER_CFLAGS=$expat_CFLAGS
|
COVERAGE_PRE=""
|
||||||
PARSER_LIBS=$expat_LIBS
|
COVERAGE_POST=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
m4_include([m4/ax_valgrind_check.m4])
|
||||||
|
AX_VALGRIND_DFLT([drd], [off])
|
||||||
|
AX_VALGRIND_DFLT([helgrind], [off])
|
||||||
|
AX_VALGRIND_DFLT([sgcheck], [off])
|
||||||
|
AX_VALGRIND_CHECK
|
||||||
|
|
||||||
|
AC_SEARCH_LIBS([clock_gettime], [rt], AC_DEFINE(HAVE_CLOCK_GETTIME,1))
|
||||||
|
AC_SEARCH_LIBS([socket], [network socket])
|
||||||
|
AC_CHECK_FUNCS([snprintf vsnprintf], [], [have_snprintf=no])
|
||||||
|
|
||||||
|
dnl Checking for va_copy availability
|
||||||
|
AC_MSG_CHECKING([for va_copy])
|
||||||
|
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdarg.h>
|
||||||
|
va_list ap1,ap2;]], [[va_copy(ap1,ap2);]])],
|
||||||
|
have_va_copy=yes,
|
||||||
|
have_va_copy=no)
|
||||||
|
AC_MSG_RESULT($have_va_copy)
|
||||||
|
if test x"$have_va_copy" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_VA_COPY,1,[Whether va_copy() is available])
|
||||||
|
else
|
||||||
|
AC_MSG_CHECKING([for __va_copy])
|
||||||
|
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <stdarg.h>
|
||||||
|
va_list ap1,ap2;]], [[__va_copy(ap1,ap2);]])],
|
||||||
|
have___va_copy=yes,
|
||||||
|
have___va_copy=no)
|
||||||
|
AC_MSG_RESULT($have___va_copy)
|
||||||
|
if test x"$have___va_copy" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE___VA_COPY,1,[Whether __va_copy() is available])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test x"$have_va_copy$have___va_copy" = x"nono"; then
|
||||||
|
dnl Checking whether va_list is an array type
|
||||||
|
AC_MSG_CHECKING([whether va_list is an array type])
|
||||||
|
AC_TRY_COMPILE2([
|
||||||
|
#include <stdarg.h>
|
||||||
|
void a(va_list * ap) {}],[
|
||||||
|
va_list ap1, ap2; a(&ap1); ap2 = (va_list) ap1],[
|
||||||
|
AC_MSG_RESULT(no)],[
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
AC_DEFINE([VA_LIST_IS_ARRAY], [1],[Define if va_list is an array type])])
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$enable_tls" != xno -a "x$with_gnutls" = xyes; then
|
||||||
|
PKG_CHECK_MODULES([gnutls], [gnutls],
|
||||||
|
[PC_REQUIRES="gnutls ${PC_REQUIRES}"],
|
||||||
|
[AC_CHECK_HEADER([gnutls/gnutls.h],
|
||||||
|
[
|
||||||
|
gnutls_LIBS="-lgnutls"
|
||||||
|
PC_LIBS="${gnutls_LIBS} ${PC_LIBS}"
|
||||||
|
],
|
||||||
|
[AC_MSG_ERROR([gnutls not found; gnutls required])]
|
||||||
|
)])
|
||||||
|
elif test "x$enable_tls" != xno -a "x$with_schannel" = xyes; then
|
||||||
|
if test "x$PLATFORM" != xwin32; then
|
||||||
|
AC_MSG_ERROR([schannel is only supported on Windows])
|
||||||
|
fi
|
||||||
|
elif test "x$enable_tls" != xno; then
|
||||||
|
PKG_CHECK_MODULES([openssl], [openssl],
|
||||||
|
[PC_REQUIRES="openssl ${PC_REQUIRES}"],
|
||||||
|
[AC_CHECK_HEADER([openssl/ssl.h],
|
||||||
|
[
|
||||||
|
openssl_LIBS="-lssl -lcrypto"
|
||||||
|
PC_LIBS="${openssl_LIBS} ${PC_LIBS}"
|
||||||
|
],
|
||||||
|
[AC_MSG_ERROR([openssl not found; openssl required])]
|
||||||
|
)])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_CHECK_FUNCS([getrandom], [], [enable_getrandom=no])
|
||||||
|
if test "$enable_getrandom" = "no"; then
|
||||||
|
AC_DEFINE(DONT_USE_GETRANDOM)
|
||||||
|
AC_MSG_NOTICE([libstrophe will not use the getrandom() system call])
|
||||||
|
fi
|
||||||
|
|
||||||
|
with_parser=""
|
||||||
|
if test "x$with_libxml2" != xyes; then
|
||||||
|
PKG_CHECK_MODULES([expat], [expat >= 2.0.0],
|
||||||
|
[
|
||||||
|
with_parser="expat"
|
||||||
|
PC_REQUIRES="expat ${PC_REQUIRES}"
|
||||||
|
],
|
||||||
|
[AC_CHECK_HEADER([expat.h],
|
||||||
|
[
|
||||||
|
with_parser="expat"
|
||||||
|
expat_LIBS="-lexpat"
|
||||||
|
PC_LIBS="${expat_LIBS} ${PC_LIBS}"
|
||||||
|
])
|
||||||
|
])
|
||||||
|
fi
|
||||||
|
if test "x$with_libxml2" != xno -a "x$with_parser" = x; then
|
||||||
|
PKG_CHECK_MODULES([libxml2], [libxml-2.0 >= 2.7],
|
||||||
|
[
|
||||||
|
with_parser="libxml2"
|
||||||
|
PC_REQUIRES="libxml-2.0 ${PC_REQUIRES}"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
old_CFLAGS=$CFLAGS
|
||||||
|
CFLAGS="-I${includedir}/libxml2"
|
||||||
|
AC_CHECK_HEADER([libxml/parser.h],
|
||||||
|
[
|
||||||
|
with_parser="libxml2"
|
||||||
|
libxml2_CFLAGS="-I${includedir}/libxml2"
|
||||||
|
libxml2_LIBS="-lxml2 -lm"
|
||||||
|
PC_LIBS="${libxml2_LIBS} ${PC_LIBS}"
|
||||||
|
PC_CFLAGS="${libxml2_CFLAGS} ${PC_CFLAGS}"
|
||||||
|
])
|
||||||
|
CFLAGS=$old_CFLAGS
|
||||||
|
])
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$with_parser" = xlibxml2; then
|
||||||
|
PARSER_CFLAGS=$libxml2_CFLAGS
|
||||||
|
PARSER_LIBS=$libxml2_LIBS
|
||||||
|
elif test "x$with_parser" = xexpat; then
|
||||||
|
PARSER_CFLAGS=$expat_CFLAGS
|
||||||
|
PARSER_LIBS=$expat_LIBS
|
||||||
|
else
|
||||||
|
AC_MSG_ERROR([no XML parser was found, libstrophe requires expat or libxml2])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_MSG_NOTICE([libstrophe will use the $with_parser XML parser])
|
AC_MSG_NOTICE([libstrophe will use the $with_parser XML parser])
|
||||||
AC_SEARCH_LIBS([socket], [socket])
|
|
||||||
|
|
||||||
if test "x$PLATFORM" != xfreebsd; then
|
if test "x$enable_cares" = xyes; then
|
||||||
AC_CHECK_LIB([resolv], [res_query], [],
|
PKG_CHECK_MODULES([libcares], [libcares >= 1.7.0],
|
||||||
[AC_CHECK_LIB([resolv], [__res_query], [],
|
[
|
||||||
[AC_MSG_ERROR([libresolv not found; libresolv required. ])])])
|
PC_REQUIRES="libcares ${PC_REQUIRES}"
|
||||||
|
RESOLV_CFLAGS=$libcares_CFLAGS
|
||||||
|
RESOLV_LIBS=$libcares_LIBS
|
||||||
|
AC_DEFINE([HAVE_CARES])
|
||||||
|
],
|
||||||
|
[AC_MSG_ERROR([libcares not found])])
|
||||||
|
# TODO: if pkg-config doesn't find, check the library manually
|
||||||
else
|
else
|
||||||
AC_MSG_NOTICE([skipping libresolv checks for freebsd])
|
|
||||||
|
AS_CASE([$PLATFORM],
|
||||||
|
[bsd], [RESOLV_LIBS=""],
|
||||||
|
[qnx], [RESOLV_LIBS="-lsocket"],
|
||||||
|
[solaris], [RESOLV_LIBS="-lresolv -lsocket -lnsl"],
|
||||||
|
[android], [RESOLV_LIBS=""],
|
||||||
|
[haiku], [RESOLV_LIBS="-lnetwork"],
|
||||||
|
[win32], [RESOLV_LIBS=""],
|
||||||
|
[RESOLV_LIBS="-lresolv"])
|
||||||
|
|
||||||
|
if test "x$PLATFORM" != xwin32; then
|
||||||
|
LIBS_TMP="${LIBS}"
|
||||||
|
LIBS="${RESOLV_LIBS}"
|
||||||
|
AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/nameser.h>
|
||||||
|
#include <resolv.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
(void)res_query(NULL, 0, 0, NULL, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
])],
|
||||||
|
[],
|
||||||
|
[AC_MSG_ERROR([res_query() not found with LIBS="${LIBS}"])])
|
||||||
|
LIBS="${LIBS_TMP}"
|
||||||
|
PC_LIBS="${RESOLV_LIBS} ${PC_LIBS}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_CHECK_HEADERS([arpa/nameser_compat.h])
|
fi
|
||||||
|
|
||||||
|
if test "x$enable_zlib" != xno; then
|
||||||
|
PKG_CHECK_MODULES([zlib], [zlib >= 1.2.0],
|
||||||
|
[
|
||||||
|
PC_REQUIRES="zlib ${PC_REQUIRES}"
|
||||||
|
ZLIB_CFLAGS=$zlib_CFLAGS
|
||||||
|
ZLIB_LIBS=$zlib_LIBS
|
||||||
|
AC_DEFINE([HAVE_ZLIB])
|
||||||
|
],
|
||||||
|
[AC_MSG_ERROR([zlib not found])])
|
||||||
|
# TODO: if pkg-config doesn't find, check the library manually
|
||||||
|
fi
|
||||||
|
|
||||||
|
m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR],
|
||||||
|
[AC_ARG_WITH([pkgconfigdir],
|
||||||
|
[AS_HELP_STRING([--with-pkgconfigdir],
|
||||||
|
[install directory for libstrophe.pc pkg-config file])],
|
||||||
|
[], [with_pkgconfigdir='$(libdir)/pkgconfig'])
|
||||||
|
AC_SUBST([pkgconfigdir], [${with_pkgconfigdir}])])
|
||||||
|
|
||||||
AM_CONDITIONAL([PARSER_EXPAT], [test x$with_parser != xlibxml2])
|
AM_CONDITIONAL([PARSER_EXPAT], [test x$with_parser != xlibxml2])
|
||||||
AC_SUBST(PARSER_NAME)
|
AM_CONDITIONAL([DISABLE_COMPRESSION], [test x$enable_zlib = xno])
|
||||||
|
AM_CONDITIONAL([DISABLE_TLS], [test x$enable_tls = xno])
|
||||||
|
AM_CONDITIONAL([DISABLE_STATIC], [test x$enable_static = xno])
|
||||||
|
AM_CONDITIONAL([NEED_SNPRINTF], [test x$have_snprintf = xno])
|
||||||
|
AM_CONDITIONAL([TLS_WITH_GNUTLS], [test x$with_gnutls = xyes])
|
||||||
|
AM_CONDITIONAL([TLS_WITH_SCHANNEL], [test x$with_schannel = xyes])
|
||||||
|
|
||||||
|
# define while compiling
|
||||||
|
AC_DEFINE_UNQUOTED(LIBXMPP_VERSION_MAJOR, [v_maj], [Major version])
|
||||||
|
AC_DEFINE_UNQUOTED(LIBXMPP_VERSION_MINOR, [v_min], [Minor version])
|
||||||
|
|
||||||
|
VMAJ=v_maj
|
||||||
|
VMIN=v_min
|
||||||
|
AC_SUBST(VMAJ)
|
||||||
|
AC_SUBST(VMIN)
|
||||||
|
# define in package-config file
|
||||||
|
PC_CFLAGS="${PC_CFLAGS} -DLIBXMPP_VERSION_MAJOR=${VMAJ} -DLIBXMPP_VERSION_MINOR=${VMIN}"
|
||||||
|
|
||||||
|
# set the SO version of the installed library
|
||||||
|
VERSION_INFO="lt_cur:lt_rev:lt_age"
|
||||||
|
AC_SUBST([VERSION_INFO])
|
||||||
|
|
||||||
|
AC_SUBST([PC_REQUIRES], [${PC_REQUIRES}])
|
||||||
|
AC_SUBST([PC_CFLAGS], [${PC_CFLAGS}])
|
||||||
|
AC_SUBST([PC_LIBS], [${PC_LIBS}])
|
||||||
|
|
||||||
|
AC_SUBST(COVERAGE_CFLAGS)
|
||||||
|
AC_SUBST(COVERAGE_LDFLAGS)
|
||||||
|
AC_SUBST(COVERAGE_PRE)
|
||||||
|
AC_SUBST(COVERAGE_POST)
|
||||||
|
AC_SUBST(MINGW_LIBS)
|
||||||
AC_SUBST(PARSER_CFLAGS)
|
AC_SUBST(PARSER_CFLAGS)
|
||||||
AC_SUBST(PARSER_LIBS)
|
AC_SUBST(PARSER_LIBS)
|
||||||
AC_CONFIG_FILES([Makefile])
|
AC_SUBST(RESOLV_CFLAGS)
|
||||||
|
AC_SUBST(RESOLV_LIBS)
|
||||||
|
AC_SUBST(WARNING_FLAGS)
|
||||||
|
AC_SUBST(ZLIB_CFLAGS)
|
||||||
|
AC_SUBST(ZLIB_LIBS)
|
||||||
|
AC_CONFIG_FILES([Makefile libstrophe.pc])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|||||||
0
docs/.keep
Normal file
0
docs/.keep
Normal file
@@ -1,2 +0,0 @@
|
|||||||
</body>
|
|
||||||
</html>
|
|
||||||
7
examples/README.md
Normal file
7
examples/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Build Instructions
|
||||||
|
==================
|
||||||
|
|
||||||
|
Compile example files with the following gcc flags:
|
||||||
|
```
|
||||||
|
gcc -o bot bot.c `pkg-config --cflags --libs libstrophe`
|
||||||
|
```
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* active.c
|
/* active.c
|
||||||
** libstrophe XMPP client library -- basic usage example
|
** libstrophe XMPP client library -- basic usage example
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* This example demonstrates basic handler functions by printing out
|
/* This example demonstrates basic handler functions by printing out
|
||||||
@@ -22,23 +20,23 @@
|
|||||||
|
|
||||||
#include <strophe.h>
|
#include <strophe.h>
|
||||||
|
|
||||||
int handle_reply(xmpp_conn_t * const conn,
|
int handle_reply(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||||
xmpp_stanza_t * const stanza,
|
|
||||||
void * const userdata)
|
|
||||||
{
|
{
|
||||||
xmpp_stanza_t *query, *item;
|
xmpp_stanza_t *query, *item;
|
||||||
char *type;
|
const char *type;
|
||||||
|
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
type = xmpp_stanza_get_type(stanza);
|
type = xmpp_stanza_get_type(stanza);
|
||||||
if (strcmp(type, "error") == 0)
|
if (strcmp(type, "error") == 0)
|
||||||
fprintf(stderr, "ERROR: query failed\n");
|
fprintf(stderr, "ERROR: query failed\n");
|
||||||
else {
|
else {
|
||||||
query = xmpp_stanza_get_child_by_name(stanza, "query");
|
query = xmpp_stanza_get_child_by_name(stanza, "query");
|
||||||
printf("Active Sessions:\n");
|
printf("Active Sessions:\n");
|
||||||
for (item = xmpp_stanza_get_children(query); item;
|
for (item = xmpp_stanza_get_children(query); item;
|
||||||
item = xmpp_stanza_get_next(item))
|
item = xmpp_stanza_get_next(item))
|
||||||
printf("\t %s\n", xmpp_stanza_get_attribute(item, "jid"));
|
printf("\t %s\n", xmpp_stanza_get_attribute(item, "jid"));
|
||||||
printf("END OF LIST\n");
|
printf("END OF LIST\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* disconnect */
|
/* disconnect */
|
||||||
@@ -47,44 +45,46 @@ int handle_reply(xmpp_conn_t * const conn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
|
void conn_handler(xmpp_conn_t *conn,
|
||||||
const int error, xmpp_stream_error_t * const stream_error,
|
xmpp_conn_event_t status,
|
||||||
void * const userdata)
|
int error,
|
||||||
|
xmpp_stream_error_t *stream_error,
|
||||||
|
void *userdata)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||||
xmpp_stanza_t *iq, *query;
|
xmpp_stanza_t *iq, *query;
|
||||||
|
|
||||||
|
(void)error;
|
||||||
|
(void)stream_error;
|
||||||
|
|
||||||
if (status == XMPP_CONN_CONNECT) {
|
if (status == XMPP_CONN_CONNECT) {
|
||||||
fprintf(stderr, "DEBUG: connected\n");
|
fprintf(stderr, "DEBUG: connected\n");
|
||||||
|
|
||||||
/* create iq stanza for request */
|
/* create iq stanza for request */
|
||||||
iq = xmpp_stanza_new(ctx);
|
iq = xmpp_iq_new(ctx, "get", "active1");
|
||||||
xmpp_stanza_set_name(iq, "iq");
|
xmpp_stanza_set_to(iq, "xxxxxxxxx.com");
|
||||||
xmpp_stanza_set_type(iq, "get");
|
|
||||||
xmpp_stanza_set_id(iq, "active1");
|
|
||||||
xmpp_stanza_set_attribute(iq, "to", "xxxxxxxxx.com");
|
|
||||||
|
|
||||||
query = xmpp_stanza_new(ctx);
|
query = xmpp_stanza_new(ctx);
|
||||||
xmpp_stanza_set_name(query, "query");
|
xmpp_stanza_set_name(query, "query");
|
||||||
xmpp_stanza_set_ns(query, XMPP_NS_DISCO_ITEMS);
|
xmpp_stanza_set_ns(query, XMPP_NS_DISCO_ITEMS);
|
||||||
xmpp_stanza_set_attribute(query, "node", "sessions");
|
xmpp_stanza_set_attribute(query, "node", "sessions");
|
||||||
|
|
||||||
xmpp_stanza_add_child(iq, query);
|
xmpp_stanza_add_child(iq, query);
|
||||||
|
|
||||||
/* we can release the stanza since it belongs to iq now */
|
/* we can release the stanza since it belongs to iq now */
|
||||||
xmpp_stanza_release(query);
|
xmpp_stanza_release(query);
|
||||||
|
|
||||||
/* set up reply handler */
|
/* set up reply handler */
|
||||||
xmpp_id_handler_add(conn, handle_reply, "active1", ctx);
|
xmpp_id_handler_add(conn, handle_reply, "active1", ctx);
|
||||||
|
|
||||||
/* send out the stanza */
|
/* send out the stanza */
|
||||||
xmpp_send(conn, iq);
|
xmpp_send(conn, iq);
|
||||||
|
|
||||||
/* release the stanza */
|
/* release the stanza */
|
||||||
xmpp_stanza_release(iq);
|
xmpp_stanza_release(iq);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "DEBUG: disconnected\n");
|
fprintf(stderr, "DEBUG: disconnected\n");
|
||||||
xmpp_stop(ctx);
|
xmpp_stop(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,8 +94,8 @@ int main(int argc, char **argv)
|
|||||||
xmpp_conn_t *conn;
|
xmpp_conn_t *conn;
|
||||||
|
|
||||||
if (argc != 3) {
|
if (argc != 3) {
|
||||||
fprintf(stderr, "Usage: active <jid> <pass>\n\n");
|
fprintf(stderr, "Usage: active <jid> <pass>\n\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize lib */
|
/* initialize lib */
|
||||||
@@ -107,9 +107,16 @@ int main(int argc, char **argv)
|
|||||||
/* create a connection */
|
/* create a connection */
|
||||||
conn = xmpp_conn_new(ctx);
|
conn = xmpp_conn_new(ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* also you can disable TLS support or force legacy SSL
|
||||||
|
* connection without STARTTLS
|
||||||
|
*
|
||||||
|
* see xmpp_conn_set_flags() or examples/basic.c
|
||||||
|
*/
|
||||||
|
|
||||||
/* setup authentication information */
|
/* setup authentication information */
|
||||||
xmpp_conn_set_jid(conn, argv[1]);
|
xmpp_conn_set_string(conn, XMPP_SETTING_JID, argv[1]);
|
||||||
xmpp_conn_set_pass(conn, argv[2]);
|
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, argv[2]);
|
||||||
|
|
||||||
/* initiate connection */
|
/* initiate connection */
|
||||||
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);
|
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);
|
||||||
|
|||||||
105
examples/basic.c
105
examples/basic.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* basic.c
|
/* basic.c
|
||||||
** libstrophe XMPP client library -- basic usage example
|
** libstrophe XMPP client library -- basic usage example
|
||||||
**
|
**
|
||||||
@@ -6,74 +7,126 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <strophe.h>
|
#include <strophe.h>
|
||||||
|
|
||||||
|
|
||||||
/* define a handler for connection events */
|
/* define a handler for connection events */
|
||||||
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
|
static void conn_handler(xmpp_conn_t *conn,
|
||||||
const int error, xmpp_stream_error_t * const stream_error,
|
xmpp_conn_event_t status,
|
||||||
void * const userdata)
|
int error,
|
||||||
|
xmpp_stream_error_t *stream_error,
|
||||||
|
void *userdata)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||||
|
|
||||||
|
(void)error;
|
||||||
|
(void)stream_error;
|
||||||
|
|
||||||
if (status == XMPP_CONN_CONNECT) {
|
if (status == XMPP_CONN_CONNECT) {
|
||||||
fprintf(stderr, "DEBUG: connected\n");
|
fprintf(stderr, "DEBUG: connected\n");
|
||||||
xmpp_disconnect(conn);
|
xmpp_disconnect(conn);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
fprintf(stderr, "DEBUG: disconnected\n");
|
fprintf(stderr, "DEBUG: disconnected\n");
|
||||||
xmpp_stop(ctx);
|
xmpp_stop(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void usage(int exit_code)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: basic [options] [<host> [<port>]]\n\n"
|
||||||
|
"Options:\n"
|
||||||
|
" --jid <jid> The JID to use to authenticate.\n"
|
||||||
|
" --pass <pass> The password of the JID.\n"
|
||||||
|
" --disable-tls Disable TLS.\n"
|
||||||
|
" --mandatory-tls Deny plaintext connection.\n"
|
||||||
|
" --trust-tls Trust TLS certificate.\n"
|
||||||
|
" --legacy-ssl Use old style SSL.\n"
|
||||||
|
" --legacy-auth Allow legacy authentication.\n"
|
||||||
|
"Note: --disable-tls conflicts with --mandatory-tls or "
|
||||||
|
"--legacy-ssl\n");
|
||||||
|
|
||||||
|
exit(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
xmpp_conn_t *conn;
|
xmpp_conn_t *conn;
|
||||||
xmpp_log_t *log;
|
xmpp_log_t *log;
|
||||||
char *jid, *pass, *host;
|
char *jid = NULL, *password = NULL, *host = NULL;
|
||||||
|
long flags = 0;
|
||||||
|
int i;
|
||||||
|
unsigned long port = 0;
|
||||||
|
|
||||||
/* take a jid and password on the command line */
|
/* take a jid and password on the command line */
|
||||||
if (argc < 3 || argc > 4) {
|
for (i = 1; i < argc; ++i) {
|
||||||
fprintf(stderr, "Usage: basic <jid> <pass> [<host>]\n\n");
|
if (strcmp(argv[i], "--help") == 0)
|
||||||
return 1;
|
usage(0);
|
||||||
|
else if (strcmp(argv[i], "--disable-tls") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
|
||||||
|
else if (strcmp(argv[i], "--mandatory-tls") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_MANDATORY_TLS;
|
||||||
|
else if (strcmp(argv[i], "--trust-tls") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_TRUST_TLS;
|
||||||
|
else if (strcmp(argv[i], "--legacy-ssl") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
|
||||||
|
else if (strcmp(argv[i], "--legacy-auth") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
|
||||||
|
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
|
||||||
|
jid = argv[i];
|
||||||
|
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
|
||||||
|
password = argv[i];
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((!jid) || (argc - i) > 2) {
|
||||||
|
usage(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
jid = argv[1];
|
if (i < argc)
|
||||||
pass = argv[2];
|
host = argv[i];
|
||||||
host = NULL;
|
if (i + 1 < argc)
|
||||||
|
port = strtoul(argv[i + 1], NULL, 0);
|
||||||
|
|
||||||
if (argc == 4)
|
/*
|
||||||
host = argv[3];
|
* Note, this example doesn't handle errors. Applications should check
|
||||||
|
* return values of non-void functions.
|
||||||
|
*/
|
||||||
|
|
||||||
/* init library */
|
/* init library */
|
||||||
xmpp_initialize();
|
xmpp_initialize();
|
||||||
|
|
||||||
|
/* pass NULL instead to silence output */
|
||||||
|
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||||
/* create a context */
|
/* create a context */
|
||||||
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */
|
|
||||||
ctx = xmpp_ctx_new(NULL, log);
|
ctx = xmpp_ctx_new(NULL, log);
|
||||||
|
|
||||||
/* create a connection */
|
/* create a connection */
|
||||||
conn = xmpp_conn_new(ctx);
|
conn = xmpp_conn_new(ctx);
|
||||||
|
|
||||||
|
/* configure connection properties (optional) */
|
||||||
|
xmpp_conn_set_flags(conn, flags);
|
||||||
|
|
||||||
/* setup authentication information */
|
/* setup authentication information */
|
||||||
xmpp_conn_set_jid(conn, jid);
|
if (jid)
|
||||||
xmpp_conn_set_pass(conn, pass);
|
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
|
||||||
|
if (password)
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password);
|
||||||
|
|
||||||
/* initiate connection */
|
/* initiate connection */
|
||||||
xmpp_connect_client(conn, host, 0, conn_handler, ctx);
|
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {
|
||||||
|
|
||||||
/* enter the event loop -
|
/* enter the event loop -
|
||||||
our connect handler will trigger an exit */
|
our connect handler will trigger an exit */
|
||||||
xmpp_run(ctx);
|
xmpp_run(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
/* release our connection and context */
|
/* release our connection and context */
|
||||||
xmpp_conn_release(conn);
|
xmpp_conn_release(conn);
|
||||||
|
|||||||
343
examples/bot.c
343
examples/bot.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* bot.c
|
/* bot.c
|
||||||
** libstrophe XMPP client library -- basic usage example
|
** libstrophe XMPP client library -- basic usage example
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* simple bot example
|
/* simple bot example
|
||||||
@@ -22,113 +20,209 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <conio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <strophe.h>
|
#include <strophe.h>
|
||||||
|
|
||||||
|
static int reconnect;
|
||||||
|
|
||||||
int version_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
|
int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||||
{
|
{
|
||||||
xmpp_stanza_t *reply, *query, *name, *version, *text;
|
xmpp_stanza_t *reply, *query, *name, *version, *text;
|
||||||
char *ns;
|
const char *ns;
|
||||||
xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata;
|
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||||
printf("Received version request from %s\n", xmpp_stanza_get_attribute(stanza, "from"));
|
|
||||||
|
|
||||||
reply = xmpp_stanza_new(ctx);
|
printf("Received version request from %s\n", xmpp_stanza_get_from(stanza));
|
||||||
xmpp_stanza_set_name(reply, "iq");
|
|
||||||
xmpp_stanza_set_type(reply, "result");
|
|
||||||
xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
|
|
||||||
xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));
|
|
||||||
|
|
||||||
query = xmpp_stanza_new(ctx);
|
reply = xmpp_stanza_reply(stanza);
|
||||||
xmpp_stanza_set_name(query, "query");
|
xmpp_stanza_set_type(reply, "result");
|
||||||
|
|
||||||
|
query = xmpp_stanza_new(ctx);
|
||||||
|
xmpp_stanza_set_name(query, "query");
|
||||||
ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza));
|
ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza));
|
||||||
if (ns) {
|
if (ns) {
|
||||||
xmpp_stanza_set_ns(query, ns);
|
xmpp_stanza_set_ns(query, ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
name = xmpp_stanza_new(ctx);
|
name = xmpp_stanza_new(ctx);
|
||||||
xmpp_stanza_set_name(name, "name");
|
xmpp_stanza_set_name(name, "name");
|
||||||
xmpp_stanza_add_child(query, name);
|
xmpp_stanza_add_child(query, name);
|
||||||
|
xmpp_stanza_release(name);
|
||||||
|
|
||||||
text = xmpp_stanza_new(ctx);
|
text = xmpp_stanza_new(ctx);
|
||||||
xmpp_stanza_set_text(text, "libstrophe example bot");
|
xmpp_stanza_set_text(text, "libstrophe example bot");
|
||||||
xmpp_stanza_add_child(name, text);
|
xmpp_stanza_add_child(name, text);
|
||||||
|
xmpp_stanza_release(text);
|
||||||
|
|
||||||
version = xmpp_stanza_new(ctx);
|
version = xmpp_stanza_new(ctx);
|
||||||
xmpp_stanza_set_name(version, "version");
|
xmpp_stanza_set_name(version, "version");
|
||||||
xmpp_stanza_add_child(query, version);
|
xmpp_stanza_add_child(query, version);
|
||||||
|
xmpp_stanza_release(version);
|
||||||
|
|
||||||
text = xmpp_stanza_new(ctx);
|
text = xmpp_stanza_new(ctx);
|
||||||
xmpp_stanza_set_text(text, "1.0");
|
xmpp_stanza_set_text(text, "1.0");
|
||||||
xmpp_stanza_add_child(version, text);
|
xmpp_stanza_add_child(version, text);
|
||||||
|
xmpp_stanza_release(text);
|
||||||
|
|
||||||
xmpp_stanza_add_child(reply, query);
|
xmpp_stanza_add_child(reply, query);
|
||||||
|
xmpp_stanza_release(query);
|
||||||
|
|
||||||
xmpp_send(conn, reply);
|
xmpp_send(conn, reply);
|
||||||
xmpp_stanza_release(reply);
|
xmpp_stanza_release(reply);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _quit_handler(xmpp_conn_t *conn, void *userdata)
|
||||||
int message_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
|
|
||||||
{
|
{
|
||||||
xmpp_stanza_t *reply, *body, *text;
|
(void)userdata;
|
||||||
char *intext, *replytext;
|
xmpp_disconnect(conn);
|
||||||
xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(!xmpp_stanza_get_child_by_name(stanza, "body")) return 1;
|
int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||||
if(xmpp_stanza_get_attribute(stanza, "type") !=NULL && !strcmp(xmpp_stanza_get_attribute(stanza, "type"), "error")) return 1;
|
{
|
||||||
|
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||||
|
xmpp_stanza_t *body, *reply;
|
||||||
|
const char *type;
|
||||||
|
char *intext, *replytext;
|
||||||
|
|
||||||
intext = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body"));
|
body = xmpp_stanza_get_child_by_name(stanza, "body");
|
||||||
|
if (body == NULL)
|
||||||
|
return 1;
|
||||||
|
type = xmpp_stanza_get_type(stanza);
|
||||||
|
if (type != NULL && strcmp(type, "error") == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
printf("Incoming message from %s: %s\n", xmpp_stanza_get_attribute(stanza, "from"), intext);
|
intext = xmpp_stanza_get_text(body);
|
||||||
|
|
||||||
reply = xmpp_stanza_new(ctx);
|
printf("Incoming message from %s: %s\n", xmpp_stanza_get_from(stanza),
|
||||||
xmpp_stanza_set_name(reply, "message");
|
intext);
|
||||||
xmpp_stanza_set_type(reply, xmpp_stanza_get_type(stanza)?xmpp_stanza_get_type(stanza):"chat");
|
|
||||||
xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));
|
|
||||||
|
|
||||||
body = xmpp_stanza_new(ctx);
|
reply = xmpp_stanza_reply(stanza);
|
||||||
xmpp_stanza_set_name(body, "body");
|
if (xmpp_stanza_get_type(reply) == NULL)
|
||||||
|
xmpp_stanza_set_type(reply, "chat");
|
||||||
|
|
||||||
replytext = malloc(strlen(" to you too!") + strlen(intext) + 1);
|
if (strcmp(intext, "quit") == 0) {
|
||||||
strcpy(replytext, intext);
|
replytext = strdup("bye!");
|
||||||
strcat(replytext, " to you too!");
|
xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
|
||||||
|
} else if (strcmp(intext, "reconnect") == 0) {
|
||||||
|
replytext = strdup("alright, let's see what happens!");
|
||||||
|
reconnect = 1;
|
||||||
|
xmpp_timed_handler_add(conn, _quit_handler, 500, NULL);
|
||||||
|
} else {
|
||||||
|
replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1);
|
||||||
|
strcpy(replytext, intext);
|
||||||
|
strcat(replytext, " to you too!");
|
||||||
|
}
|
||||||
|
xmpp_free(ctx, intext);
|
||||||
|
xmpp_message_set_body(reply, replytext);
|
||||||
|
|
||||||
text = xmpp_stanza_new(ctx);
|
xmpp_send(conn, reply);
|
||||||
xmpp_stanza_set_text(text, replytext);
|
xmpp_stanza_release(reply);
|
||||||
xmpp_stanza_add_child(body, text);
|
free(replytext);
|
||||||
xmpp_stanza_add_child(reply, body);
|
|
||||||
|
|
||||||
xmpp_send(conn, reply);
|
return 1;
|
||||||
xmpp_stanza_release(reply);
|
|
||||||
free(replytext);
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* define a handler for connection events */
|
/* define a handler for connection events */
|
||||||
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
|
void conn_handler(xmpp_conn_t *conn,
|
||||||
const int error, xmpp_stream_error_t * const stream_error,
|
xmpp_conn_event_t status,
|
||||||
void * const userdata)
|
int error,
|
||||||
|
xmpp_stream_error_t *stream_error,
|
||||||
|
void *userdata)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||||
|
|
||||||
if (status == XMPP_CONN_CONNECT) {
|
(void)error;
|
||||||
xmpp_stanza_t* pres;
|
(void)stream_error;
|
||||||
fprintf(stderr, "DEBUG: connected\n");
|
|
||||||
xmpp_handler_add(conn,version_handler, "jabber:iq:version", "iq", NULL, ctx);
|
|
||||||
xmpp_handler_add(conn,message_handler, NULL, "message", NULL, ctx);
|
|
||||||
|
|
||||||
/* Send initial <presence/> so that we appear online to contacts */
|
if (status == XMPP_CONN_CONNECT) {
|
||||||
pres = xmpp_stanza_new(ctx);
|
xmpp_stanza_t *pres;
|
||||||
xmpp_stanza_set_name(pres, "presence");
|
fprintf(stderr, "DEBUG: connected\n");
|
||||||
xmpp_send(conn, pres);
|
xmpp_handler_add(conn, version_handler, "jabber:iq:version", "iq", NULL,
|
||||||
xmpp_stanza_release(pres);
|
ctx);
|
||||||
|
xmpp_handler_add(conn, message_handler, NULL, "message", NULL, ctx);
|
||||||
|
|
||||||
|
/* Send initial <presence/> so that we appear online to contacts */
|
||||||
|
pres = xmpp_presence_new(ctx);
|
||||||
|
xmpp_send(conn, pres);
|
||||||
|
xmpp_stanza_release(pres);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "DEBUG: disconnected\n");
|
||||||
|
xmpp_stop(ctx);
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
fprintf(stderr, "DEBUG: disconnected\n");
|
|
||||||
xmpp_stop(ctx);
|
#ifdef _WIN32
|
||||||
|
static char *getpassword(const char *prompt, size_t maxlen)
|
||||||
|
{
|
||||||
|
char *b, *buffer = malloc(maxlen);
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
b = buffer;
|
||||||
|
fputs(prompt, stderr);
|
||||||
|
for (i = 0; i < maxlen; i++, b++) {
|
||||||
|
char c = _getch();
|
||||||
|
if (c == '\r' || c == '\n')
|
||||||
|
break;
|
||||||
|
*b = c;
|
||||||
}
|
}
|
||||||
|
*b = '\0';
|
||||||
|
fputs("\n", stderr);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define getpassword(prompt, maxlen) getpass(prompt)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int
|
||||||
|
password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn, void *userdata)
|
||||||
|
{
|
||||||
|
(void)userdata;
|
||||||
|
printf("Trying to unlock %s\n", xmpp_conn_get_keyfile(conn));
|
||||||
|
char *pass = getpassword("Please enter password: ", pw_max);
|
||||||
|
if (!pass)
|
||||||
|
return -1;
|
||||||
|
size_t passlen = strlen(pass);
|
||||||
|
int ret;
|
||||||
|
if (passlen >= pw_max) {
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = passlen + 1;
|
||||||
|
memcpy(pw, pass, ret);
|
||||||
|
out:
|
||||||
|
memset(pass, 0, passlen);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(int exit_code)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: bot [options] <jid> <pass>\n\n"
|
||||||
|
"Options:\n"
|
||||||
|
" --jid <jid> The JID to use to authenticate.\n"
|
||||||
|
" --pass <pass> The password of the JID.\n"
|
||||||
|
" --tls-cert <cert> Path to client certificate.\n"
|
||||||
|
" --tls-key <key> Path to private key or P12 file.\n\n"
|
||||||
|
" --tcp-keepalive Configure TCP keepalive.\n"
|
||||||
|
" --disable-tls Disable TLS.\n"
|
||||||
|
" --mandatory-tls Deny plaintext connection.\n"
|
||||||
|
" --trust-tls Trust TLS certificate.\n"
|
||||||
|
" --legacy-ssl Use old style SSL.\n"
|
||||||
|
" --legacy-auth Allow legacy authentication.\n"
|
||||||
|
"Note: --disable-tls conflicts with --mandatory-tls or "
|
||||||
|
"--legacy-ssl\n"
|
||||||
|
" --zlib Enable compression via zlib.\n"
|
||||||
|
" --dont-reset When using zlib, don't do a full-flush "
|
||||||
|
"after compression.\n");
|
||||||
|
|
||||||
|
exit(exit_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
@@ -136,40 +230,113 @@ int main(int argc, char **argv)
|
|||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
xmpp_conn_t *conn;
|
xmpp_conn_t *conn;
|
||||||
xmpp_log_t *log;
|
xmpp_log_t *log;
|
||||||
char *jid, *pass;
|
xmpp_sm_state_t *sm_state = NULL;
|
||||||
|
char *jid = NULL, *password = NULL, *host = NULL, *cert = NULL, *key = NULL;
|
||||||
|
long flags = 0;
|
||||||
|
int i, tcp_keepalive = 0;
|
||||||
|
unsigned long port = 0;
|
||||||
|
|
||||||
/* take a jid and password on the command line */
|
/* take a jid and password on the command line */
|
||||||
if (argc != 3) {
|
for (i = 1; i < argc; ++i) {
|
||||||
fprintf(stderr, "Usage: bot <jid> <pass>\n\n");
|
if (strcmp(argv[i], "--help") == 0)
|
||||||
return 1;
|
usage(0);
|
||||||
|
else if (strcmp(argv[i], "--disable-tls") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
|
||||||
|
else if (strcmp(argv[i], "--mandatory-tls") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_MANDATORY_TLS;
|
||||||
|
else if (strcmp(argv[i], "--trust-tls") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_TRUST_TLS;
|
||||||
|
else if (strcmp(argv[i], "--legacy-ssl") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
|
||||||
|
else if (strcmp(argv[i], "--legacy-auth") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
|
||||||
|
else if (strcmp(argv[i], "--zlib") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
|
||||||
|
else if (strcmp(argv[i], "--dont-reset") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
|
||||||
|
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
|
||||||
|
jid = argv[i];
|
||||||
|
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
|
||||||
|
password = argv[i];
|
||||||
|
else if ((strcmp(argv[i], "--tls-cert") == 0) && (++i < argc))
|
||||||
|
cert = argv[i];
|
||||||
|
else if ((strcmp(argv[i], "--tls-key") == 0) && (++i < argc))
|
||||||
|
key = argv[i];
|
||||||
|
else if (strcmp(argv[i], "--tcp-keepalive") == 0)
|
||||||
|
tcp_keepalive = 1;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((!jid && !key) || (argc - i) > 2) {
|
||||||
|
usage(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
jid = argv[1];
|
if (i < argc)
|
||||||
pass = argv[2];
|
host = argv[i];
|
||||||
|
if (i + 1 < argc)
|
||||||
|
port = strtoul(argv[i + 1], NULL, 0);
|
||||||
|
|
||||||
/* init library */
|
/* init library */
|
||||||
xmpp_initialize();
|
xmpp_initialize();
|
||||||
|
|
||||||
|
/* pass NULL instead to silence output */
|
||||||
|
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||||
/* create a context */
|
/* create a context */
|
||||||
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */
|
|
||||||
ctx = xmpp_ctx_new(NULL, log);
|
ctx = xmpp_ctx_new(NULL, log);
|
||||||
|
|
||||||
|
create_connection:
|
||||||
|
reconnect = 0;
|
||||||
/* create a connection */
|
/* create a connection */
|
||||||
conn = xmpp_conn_new(ctx);
|
conn = xmpp_conn_new(ctx);
|
||||||
|
|
||||||
|
/* configure connection properties (optional) */
|
||||||
|
xmpp_conn_set_flags(conn, flags);
|
||||||
|
|
||||||
|
/* ask for a password if key is protected */
|
||||||
|
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK,
|
||||||
|
password_callback);
|
||||||
|
/* try at max 3 times in case the user enters the password wrong */
|
||||||
|
xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, 3);
|
||||||
/* setup authentication information */
|
/* setup authentication information */
|
||||||
xmpp_conn_set_jid(conn, jid);
|
if (key) {
|
||||||
xmpp_conn_set_pass(conn, pass);
|
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert);
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key);
|
||||||
|
}
|
||||||
|
if (jid)
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
|
||||||
|
if (password)
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password);
|
||||||
|
|
||||||
|
/* enable TCP keepalive, using canned callback function */
|
||||||
|
if (tcp_keepalive)
|
||||||
|
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
|
||||||
|
xmpp_sockopt_cb_keepalive);
|
||||||
|
|
||||||
|
/* set Stream-Mangement state if available */
|
||||||
|
if (sm_state) {
|
||||||
|
xmpp_conn_set_sm_state(conn, sm_state);
|
||||||
|
sm_state = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* initiate connection */
|
/* initiate connection */
|
||||||
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);
|
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {
|
||||||
|
|
||||||
/* enter the event loop -
|
/* enter the event loop -
|
||||||
our connect handler will trigger an exit */
|
our connect handler will trigger an exit */
|
||||||
xmpp_run(ctx);
|
xmpp_run(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
/* release our connection and context */
|
/* save the Stream-Mangement state if we should re-connect */
|
||||||
|
if (reconnect)
|
||||||
|
sm_state = xmpp_conn_get_sm_state(conn);
|
||||||
|
|
||||||
|
/* release our connection */
|
||||||
xmpp_conn_release(conn);
|
xmpp_conn_release(conn);
|
||||||
|
|
||||||
|
if (reconnect)
|
||||||
|
goto create_connection;
|
||||||
|
|
||||||
|
/* release our context */
|
||||||
xmpp_ctx_free(ctx);
|
xmpp_ctx_free(ctx);
|
||||||
|
|
||||||
/* final shutdown of the library */
|
/* final shutdown of the library */
|
||||||
|
|||||||
376
examples/complex.c
Normal file
376
examples/complex.c
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* complex.c
|
||||||
|
** libstrophe XMPP client library -- more complex usage example
|
||||||
|
**
|
||||||
|
** Copyright (C) 2005-2009 Collecta, Inc.
|
||||||
|
**
|
||||||
|
** This software is provided AS-IS with no warranty, either express
|
||||||
|
** or implied.
|
||||||
|
**
|
||||||
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <conio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
#include <mstcpip.h> /* tcp_keepalive */
|
||||||
|
#else
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <strophe.h>
|
||||||
|
|
||||||
|
/* hardcoded TCP keepalive timeout and interval */
|
||||||
|
#define KA_TIMEOUT 60
|
||||||
|
#define KA_INTERVAL 30
|
||||||
|
#define KA_COUNT 3
|
||||||
|
#define USER_TIMEOUT 150
|
||||||
|
|
||||||
|
static void print_tlscert(const xmpp_tlscert_t *cert)
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
size_t n;
|
||||||
|
for (n = 0; n < (unsigned)XMPP_CERT_ELEMENT_MAX; ++n) {
|
||||||
|
printf("\t%32s: %s\n", xmpp_tlscert_get_description(n),
|
||||||
|
xmpp_tlscert_get_string(cert, n));
|
||||||
|
}
|
||||||
|
n = 0;
|
||||||
|
while ((name = xmpp_tlscert_get_dnsname(cert, n++)) != NULL)
|
||||||
|
printf("\t%32s: %s\n", "dnsName", name);
|
||||||
|
printf("PEM:\n%s\n", xmpp_tlscert_get_pem(cert));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* define a handler for connection events */
|
||||||
|
static void conn_handler(xmpp_conn_t *conn,
|
||||||
|
xmpp_conn_event_t status,
|
||||||
|
int error,
|
||||||
|
xmpp_stream_error_t *stream_error,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||||
|
int secured;
|
||||||
|
|
||||||
|
(void)error;
|
||||||
|
(void)stream_error;
|
||||||
|
|
||||||
|
if (status == XMPP_CONN_CONNECT) {
|
||||||
|
fprintf(stderr, "DEBUG: connected\n");
|
||||||
|
secured = xmpp_conn_is_secured(conn);
|
||||||
|
fprintf(stderr, "DEBUG: connection is %s.\n",
|
||||||
|
secured ? "secured" : "NOT secured");
|
||||||
|
if (secured) {
|
||||||
|
xmpp_tlscert_t *cert = xmpp_conn_get_peer_cert(conn);
|
||||||
|
print_tlscert(cert);
|
||||||
|
xmpp_tlscert_free(cert);
|
||||||
|
}
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "DEBUG: disconnected\n");
|
||||||
|
xmpp_stop(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int certfail_handler(const xmpp_tlscert_t *cert,
|
||||||
|
const char *const errormsg)
|
||||||
|
{
|
||||||
|
char read_char[16] = {0};
|
||||||
|
printf("Received certificate can't be validated!\n");
|
||||||
|
printf("Reason: %s\n", errormsg);
|
||||||
|
print_tlscert(cert);
|
||||||
|
printf("Do you agree to connect?\n[y(es)|n(o)]: ");
|
||||||
|
fflush(stdout);
|
||||||
|
if (fgets(read_char, sizeof(read_char), stdin) == NULL) {
|
||||||
|
printf("fgets() failed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
return read_char[0] == 'y' || read_char[0] == 'Y';
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static char *getpassword(const char *prompt, size_t maxlen)
|
||||||
|
{
|
||||||
|
char *b, *buffer = malloc(maxlen);
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
b = buffer;
|
||||||
|
fputs(prompt, stderr);
|
||||||
|
for (i = 0; i < maxlen; i++, b++) {
|
||||||
|
char c = _getch();
|
||||||
|
if (c == '\r' || c == '\n')
|
||||||
|
break;
|
||||||
|
*b = c;
|
||||||
|
}
|
||||||
|
*b = '\0';
|
||||||
|
fputs("\n", stderr);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define getpassword(prompt, maxlen) getpass(prompt)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int
|
||||||
|
password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn, void *userdata)
|
||||||
|
{
|
||||||
|
(void)userdata;
|
||||||
|
printf("Trying to unlock %s\n", xmpp_conn_get_keyfile(conn));
|
||||||
|
char *pass = getpassword("Please enter password: ", pw_max);
|
||||||
|
if (!pass)
|
||||||
|
return -1;
|
||||||
|
size_t passlen = strlen(pass);
|
||||||
|
int ret;
|
||||||
|
if (passlen >= pw_max) {
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = passlen + 1;
|
||||||
|
memcpy(pw, pass, ret);
|
||||||
|
out:
|
||||||
|
memset(pass, 0, passlen);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sockopt_cb(xmpp_conn_t *conn, void *socket)
|
||||||
|
{
|
||||||
|
int timeout = KA_TIMEOUT;
|
||||||
|
int interval = KA_INTERVAL;
|
||||||
|
int count = KA_COUNT;
|
||||||
|
unsigned int user_timeout = USER_TIMEOUT;
|
||||||
|
int ret;
|
||||||
|
int optval = (timeout && interval) ? 1 : 0;
|
||||||
|
|
||||||
|
(void)conn;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
(void)count;
|
||||||
|
(void)user_timeout;
|
||||||
|
|
||||||
|
SOCKET sock = *((SOCKET *)socket);
|
||||||
|
struct tcp_keepalive ka;
|
||||||
|
DWORD dw = 0;
|
||||||
|
|
||||||
|
ka.onoff = optval;
|
||||||
|
ka.keepalivetime = timeout * 1000;
|
||||||
|
ka.keepaliveinterval = interval * 1000;
|
||||||
|
ret = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &dw,
|
||||||
|
NULL, NULL);
|
||||||
|
#else
|
||||||
|
int sock = *((int *)socket);
|
||||||
|
|
||||||
|
fprintf(stderr, "DEBUG: setting socket options\n");
|
||||||
|
|
||||||
|
ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (optval) {
|
||||||
|
#ifdef TCP_KEEPIDLE
|
||||||
|
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &timeout,
|
||||||
|
sizeof(timeout));
|
||||||
|
#elif defined(TCP_KEEPALIVE)
|
||||||
|
/* QNX receives `struct timeval' as argument, but it seems OSX does int
|
||||||
|
*/
|
||||||
|
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &timeout,
|
||||||
|
sizeof(timeout));
|
||||||
|
#endif /* TCP_KEEPIDLE */
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
#ifdef TCP_KEEPINTVL
|
||||||
|
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval,
|
||||||
|
sizeof(interval));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
#endif /* TCP_KEEPINTVL */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count) {
|
||||||
|
#ifdef TCP_KEEPCNT
|
||||||
|
ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
#endif /* TCP_KEEPCNT */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_timeout) {
|
||||||
|
#ifdef TCP_USER_TIMEOUT
|
||||||
|
ret = setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout,
|
||||||
|
sizeof(user_timeout));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
#elif defined(TCP_RXT_CONNDROPTIME)
|
||||||
|
int rxt = user_timeout / 1000;
|
||||||
|
ret = setsockopt(sock, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &rxt,
|
||||||
|
sizeof(rxt));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
#endif /* TCP_USER_TIMEOUT */
|
||||||
|
}
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(int exit_code)
|
||||||
|
{
|
||||||
|
fprintf(stderr,
|
||||||
|
"Usage: complex [options] [<host> [<port>]]\n\n"
|
||||||
|
"Options:\n"
|
||||||
|
" --jid <jid> The JID to use to authenticate.\n"
|
||||||
|
" --pass <pass> The password of the JID.\n"
|
||||||
|
" --tls-cert <cert> Path to client certificate.\n"
|
||||||
|
" --tls-key <key> Path to private key or P12 file.\n\n"
|
||||||
|
" --capath <path> Path to an additional CA trust store "
|
||||||
|
"(directory).\n"
|
||||||
|
" --cafile <path> Path to an additional CA trust store "
|
||||||
|
"(single file).\n"
|
||||||
|
" --disable-tls Disable TLS.\n"
|
||||||
|
" --mandatory-tls Deny plaintext connection.\n"
|
||||||
|
" --trust-tls Trust TLS certificate.\n"
|
||||||
|
" --enable-certfail Enable certfail handler.\n"
|
||||||
|
" --legacy-ssl Use old style SSL.\n"
|
||||||
|
" --legacy-auth Allow legacy authentication.\n"
|
||||||
|
" --zlib Enable compression via zlib.\n"
|
||||||
|
" --dont-reset When using zlib, don't do a full-flush "
|
||||||
|
"after compression.\n"
|
||||||
|
" --verbose Increase the verbosity level.\n"
|
||||||
|
" --tcp-keepalive Configure TCP keepalive.\n\n"
|
||||||
|
"Note: --disable-tls conflicts with --mandatory-tls or "
|
||||||
|
"--legacy-ssl\n");
|
||||||
|
|
||||||
|
exit(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
xmpp_ctx_t *ctx;
|
||||||
|
xmpp_conn_t *conn;
|
||||||
|
xmpp_log_t *log;
|
||||||
|
char *jid = NULL, *password = NULL, *cert = NULL, *key = NULL, *host = NULL,
|
||||||
|
*capath = NULL, *cafile = NULL;
|
||||||
|
long flags = 0;
|
||||||
|
int tcp_keepalive = 0, verbosity = 0, certfail = 0;
|
||||||
|
int i;
|
||||||
|
unsigned long port = 0;
|
||||||
|
|
||||||
|
/* take a jid and password on the command line */
|
||||||
|
for (i = 1; i < argc; ++i) {
|
||||||
|
if (strcmp(argv[i], "--help") == 0)
|
||||||
|
usage(0);
|
||||||
|
else if (strcmp(argv[i], "--disable-tls") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
|
||||||
|
else if (strcmp(argv[i], "--mandatory-tls") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_MANDATORY_TLS;
|
||||||
|
else if (strcmp(argv[i], "--trust-tls") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_TRUST_TLS;
|
||||||
|
else if (strcmp(argv[i], "--legacy-ssl") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_LEGACY_SSL;
|
||||||
|
else if (strcmp(argv[i], "--legacy-auth") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_LEGACY_AUTH;
|
||||||
|
else if (strcmp(argv[i], "--zlib") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_ENABLE_COMPRESSION;
|
||||||
|
else if (strcmp(argv[i], "--dont-reset") == 0)
|
||||||
|
flags |= XMPP_CONN_FLAG_COMPRESSION_DONT_RESET;
|
||||||
|
else if (strcmp(argv[i], "--verbose") == 0)
|
||||||
|
verbosity++;
|
||||||
|
else if (strcmp(argv[i], "--tcp-keepalive") == 0)
|
||||||
|
tcp_keepalive = 1;
|
||||||
|
else if (strcmp(argv[i], "--enable-certfail") == 0)
|
||||||
|
certfail = 1;
|
||||||
|
else if ((strcmp(argv[i], "--jid") == 0) && (++i < argc))
|
||||||
|
jid = argv[i];
|
||||||
|
else if ((strcmp(argv[i], "--pass") == 0) && (++i < argc))
|
||||||
|
password = argv[i];
|
||||||
|
else if ((strcmp(argv[i], "--tls-cert") == 0) && (++i < argc))
|
||||||
|
cert = argv[i];
|
||||||
|
else if ((strcmp(argv[i], "--tls-key") == 0) && (++i < argc))
|
||||||
|
key = argv[i];
|
||||||
|
else if ((strcmp(argv[i], "--capath") == 0) && (++i < argc))
|
||||||
|
capath = argv[i];
|
||||||
|
else if ((strcmp(argv[i], "--cafile") == 0) && (++i < argc))
|
||||||
|
cafile = argv[i];
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((!jid && !key) || (argc - i) > 2) {
|
||||||
|
usage(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < argc)
|
||||||
|
host = argv[i];
|
||||||
|
if (i + 1 < argc)
|
||||||
|
port = strtoul(argv[i + 1], NULL, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note, this example doesn't handle errors. Applications should check
|
||||||
|
* return values of non-void functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* init library */
|
||||||
|
xmpp_initialize();
|
||||||
|
|
||||||
|
/* pass NULL instead to silence output */
|
||||||
|
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||||
|
/* create a context */
|
||||||
|
ctx = xmpp_ctx_new(NULL, log);
|
||||||
|
xmpp_ctx_set_verbosity(ctx, verbosity);
|
||||||
|
|
||||||
|
/* create a connection */
|
||||||
|
conn = xmpp_conn_new(ctx);
|
||||||
|
|
||||||
|
/* configure connection properties (optional) */
|
||||||
|
xmpp_conn_set_flags(conn, flags);
|
||||||
|
/* configure TCP keepalive (optional) */
|
||||||
|
if (tcp_keepalive)
|
||||||
|
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
|
||||||
|
sockopt_cb);
|
||||||
|
|
||||||
|
/* ask for a password if key is protected */
|
||||||
|
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK,
|
||||||
|
password_callback);
|
||||||
|
/* try at max 3 times in case the user enters the password wrong */
|
||||||
|
xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, 3);
|
||||||
|
/* setup authentication information */
|
||||||
|
if (key) {
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert);
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key);
|
||||||
|
}
|
||||||
|
if (jid)
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
|
||||||
|
if (password)
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, password);
|
||||||
|
|
||||||
|
if (certfail)
|
||||||
|
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_CERTFAIL_HANDLER,
|
||||||
|
certfail_handler);
|
||||||
|
if (capath)
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CAPATH, capath);
|
||||||
|
if (cafile)
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CAFILE, cafile);
|
||||||
|
|
||||||
|
/* initiate connection */
|
||||||
|
if (xmpp_connect_client(conn, host, port, conn_handler, ctx) == XMPP_EOK) {
|
||||||
|
|
||||||
|
/* enter the event loop -
|
||||||
|
our connect handler will trigger an exit */
|
||||||
|
xmpp_run(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* release our connection and context */
|
||||||
|
xmpp_conn_release(conn);
|
||||||
|
xmpp_ctx_free(ctx);
|
||||||
|
|
||||||
|
/* final shutdown of the library */
|
||||||
|
xmpp_shutdown();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
101
examples/component.c
Normal file
101
examples/component.c
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* component.c
|
||||||
|
** libstrophe XMPP client library -- external component (XEP-0114) example
|
||||||
|
**
|
||||||
|
** Copyright (C) 2005-2009 Collecta, Inc.
|
||||||
|
**
|
||||||
|
** This software is provided AS-IS with no warranty, either express
|
||||||
|
** or implied.
|
||||||
|
**
|
||||||
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This example demonstrates simple connection to a server
|
||||||
|
** as an external component. See XEP-0114 for more details.
|
||||||
|
** This program requires correctly configured server to run.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <strophe.h>
|
||||||
|
|
||||||
|
/* define a handler for connection events */
|
||||||
|
void conn_handler(xmpp_conn_t *conn,
|
||||||
|
xmpp_conn_event_t status,
|
||||||
|
int error,
|
||||||
|
xmpp_stream_error_t *stream_error,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||||
|
|
||||||
|
(void)error;
|
||||||
|
(void)stream_error;
|
||||||
|
|
||||||
|
if (status == XMPP_CONN_CONNECT) {
|
||||||
|
fprintf(stderr, "DEBUG: connected\n");
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "DEBUG: disconnected\n");
|
||||||
|
xmpp_stop(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
xmpp_ctx_t *ctx;
|
||||||
|
xmpp_conn_t *conn;
|
||||||
|
xmpp_log_t *log;
|
||||||
|
char *jid, *pass, *host, *port_err = NULL;
|
||||||
|
unsigned short port = 0;
|
||||||
|
|
||||||
|
/* take a jid and password on the command line */
|
||||||
|
if (argc < 4 || argc > 5) {
|
||||||
|
fprintf(stderr, "Usage: component <jid> <pass> <host> [<port>]\n\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jid = argv[1];
|
||||||
|
pass = argv[2];
|
||||||
|
host = argv[3];
|
||||||
|
|
||||||
|
if (argc == 5) {
|
||||||
|
short tmp_port = (short)strtol(argv[4], &port_err, 10);
|
||||||
|
if (tmp_port < 0 || *port_err != '\0') {
|
||||||
|
fprintf(stderr, "Invalid value of <port> [%s].\n", argv[4]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
port = (unsigned short)tmp_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init library */
|
||||||
|
xmpp_initialize();
|
||||||
|
|
||||||
|
/* pass NULL instead to silence output */
|
||||||
|
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||||
|
/* create a context */
|
||||||
|
ctx = xmpp_ctx_new(NULL, log);
|
||||||
|
|
||||||
|
/* create a connection */
|
||||||
|
conn = xmpp_conn_new(ctx);
|
||||||
|
|
||||||
|
/* setup authentication information */
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass);
|
||||||
|
|
||||||
|
/* initiate connection */
|
||||||
|
xmpp_connect_component(conn, host, port, conn_handler, ctx);
|
||||||
|
|
||||||
|
/* enter the event loop -
|
||||||
|
our connect handler will trigger an exit */
|
||||||
|
xmpp_run(ctx);
|
||||||
|
|
||||||
|
/* release our connection and context */
|
||||||
|
xmpp_conn_release(conn);
|
||||||
|
xmpp_ctx_free(ctx);
|
||||||
|
|
||||||
|
/* final shutdown of the library */
|
||||||
|
xmpp_shutdown();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
190
examples/perf.c
Normal file
190
examples/perf.c
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* perf.c
|
||||||
|
* strophe XMPP client library -- performance measure
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* performance measure
|
||||||
|
*
|
||||||
|
* Timing code shamelessly borrowed from libtomcrypt/demos/timing.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <strophe.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static void init_timer(void);
|
||||||
|
static void t_start(void);
|
||||||
|
static uint64_t t_read(void);
|
||||||
|
static const char *perf_time_unit;
|
||||||
|
|
||||||
|
#define NUM_SAMPLES 1000u
|
||||||
|
|
||||||
|
static void perf_rand(xmpp_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
xmpp_rand_t *rng = xmpp_rand_new(ctx);
|
||||||
|
|
||||||
|
uint64_t t1, t2, t3 = 0;
|
||||||
|
unsigned int n;
|
||||||
|
const size_t alloc_sz = 0x1000u;
|
||||||
|
size_t sz;
|
||||||
|
unsigned char *buf = malloc(alloc_sz);
|
||||||
|
|
||||||
|
/* pre-heat */
|
||||||
|
for (n = 1; n < 4; ++n) {
|
||||||
|
xmpp_rand_bytes(rng, buf, alloc_sz / n);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (sz = 2; sz <= alloc_sz; sz <<= 1) {
|
||||||
|
t2 = 0;
|
||||||
|
for (n = 0; n < NUM_SAMPLES; ++n) {
|
||||||
|
|
||||||
|
t_start();
|
||||||
|
t1 = t_read();
|
||||||
|
|
||||||
|
xmpp_rand_bytes(rng, buf, sz);
|
||||||
|
|
||||||
|
t1 = t_read() - t1;
|
||||||
|
t2 += t1;
|
||||||
|
}
|
||||||
|
t2 /= NUM_SAMPLES;
|
||||||
|
fprintf(stderr, "Reading %6zu bytes from PRNG took %8" PRIu64 " %s\n",
|
||||||
|
sz, t2, perf_time_unit);
|
||||||
|
if (t3) {
|
||||||
|
double d3 = (double)t3, d2 = (double)t2;
|
||||||
|
fprintf(stderr, " +%.2f%%\n",
|
||||||
|
(d2 - d3) / ((d2 + d3) * 0.5) * 100.);
|
||||||
|
}
|
||||||
|
t3 = t2;
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
xmpp_rand_free(ctx, rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
/* pass NULL instead to silence output */
|
||||||
|
xmpp_log_t *log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||||
|
/* create a context */
|
||||||
|
xmpp_ctx_t *ctx = xmpp_ctx_new(NULL, log);
|
||||||
|
|
||||||
|
init_timer();
|
||||||
|
|
||||||
|
perf_rand(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TIMES 100000
|
||||||
|
static uint64_t timer, skew = 0;
|
||||||
|
|
||||||
|
/* RDTSC from Scott Duplichan */
|
||||||
|
static uint64_t rdtsc(void)
|
||||||
|
{
|
||||||
|
#if defined __GNUC__
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
/* version from http://www.mcs.anl.gov/~kazutomo/rdtsc.html
|
||||||
|
* the old code always got a warning issued by gcc, clang did not
|
||||||
|
* complain...
|
||||||
|
*/
|
||||||
|
unsigned hi, lo;
|
||||||
|
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
|
||||||
|
return ((uint64_t)lo) | (((uint64_t)hi) << 32);
|
||||||
|
#elif defined(__POWERPC__)
|
||||||
|
unsigned long a, b;
|
||||||
|
__asm__ __volatile__("mftbu %1 \nmftb %0\n" : "=r"(a), "=r"(b));
|
||||||
|
return (((uint64_t)b) << 32ULL) | ((uint64_t)a);
|
||||||
|
#elif defined(__ia64__) /* gcc-IA64 version */
|
||||||
|
unsigned long result;
|
||||||
|
__asm__ __volatile__("mov %0=ar.itc" : "=r"(result)::"memory");
|
||||||
|
while (__builtin_expect((int)result == -1, 0))
|
||||||
|
__asm__ __volatile__("mov %0=ar.itc" : "=r"(result)::"memory");
|
||||||
|
return result;
|
||||||
|
#elif defined(__sparc__)
|
||||||
|
#if defined(__arch64__)
|
||||||
|
uint64_t a;
|
||||||
|
asm volatile("rd %%tick,%0" : "=r"(a));
|
||||||
|
return a;
|
||||||
|
#else
|
||||||
|
register unsigned long x, y;
|
||||||
|
__asm__ __volatile__("rd %%tick, %0; clruw %0, %1; srlx %0, 32, %0"
|
||||||
|
: "=r"(x), "=r"(y)
|
||||||
|
: "0"(x), "1"(y));
|
||||||
|
return ((unsigned long long)x << 32) | y;
|
||||||
|
#endif
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
uint64_t CNTVCT_EL0;
|
||||||
|
__asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(CNTVCT_EL0));
|
||||||
|
return CNTVCT_EL0;
|
||||||
|
#elif defined HAVE_CLOCK_GETTIME
|
||||||
|
#define USE_CLOCK_GETTIME
|
||||||
|
struct timespec result;
|
||||||
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &result);
|
||||||
|
return result.tv_sec * 1000000000 + result.tv_nsec;
|
||||||
|
#else
|
||||||
|
return clock();
|
||||||
|
#endif /* __GNUC__ */
|
||||||
|
|
||||||
|
/* Microsoft and Intel Windows compilers */
|
||||||
|
#elif defined _M_IX86
|
||||||
|
__asm rdtsc
|
||||||
|
#elif defined _M_AMD64
|
||||||
|
return __rdtsc();
|
||||||
|
#elif defined _M_IA64
|
||||||
|
#if defined __INTEL_COMPILER
|
||||||
|
#include <ia64intrin.h>
|
||||||
|
#endif
|
||||||
|
return __getReg(3116);
|
||||||
|
#elif defined HAVE_CLOCK_GETTIME
|
||||||
|
#define USE_CLOCK_GETTIME
|
||||||
|
struct timespec result;
|
||||||
|
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &result);
|
||||||
|
return result.tv_sec * 1000000000 + result.tv_nsec;
|
||||||
|
#else
|
||||||
|
return clock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined USE_CLOCK_GETTIME
|
||||||
|
static const char *perf_time_unit = "ns";
|
||||||
|
#else
|
||||||
|
static const char *perf_time_unit = "cycles";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void t_start(void)
|
||||||
|
{
|
||||||
|
timer = rdtsc();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t t_read(void)
|
||||||
|
{
|
||||||
|
return rdtsc() - timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_timer(void)
|
||||||
|
{
|
||||||
|
uint64_t c1, c2, t1, t2;
|
||||||
|
unsigned long y1;
|
||||||
|
|
||||||
|
c1 = c2 = (uint64_t)-1;
|
||||||
|
for (y1 = 0; y1 < TIMES * 100; y1++) {
|
||||||
|
t_start();
|
||||||
|
t1 = t_read();
|
||||||
|
t2 = (t_read() - t1) >> 1;
|
||||||
|
|
||||||
|
c1 = (t1 > c1) ? t1 : c1;
|
||||||
|
c2 = (t2 > c2) ? t2 : c2;
|
||||||
|
}
|
||||||
|
skew = c2 - c1;
|
||||||
|
fprintf(stderr, "Clock Skew: %lu\n", (unsigned long)skew);
|
||||||
|
}
|
||||||
364
examples/register.c
Normal file
364
examples/register.c
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* register.c
|
||||||
|
* strophe XMPP client library -- In-band registration (XEP-0077)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This example uses a "raw" connection to establish connection to a server
|
||||||
|
* without account. Then it queries server to register new account according
|
||||||
|
* to XEP-0077.
|
||||||
|
*
|
||||||
|
* How to use it. After the application connects and receives instructions
|
||||||
|
* from the server, user will be prompted to type information such as
|
||||||
|
* username, password, etc. Press enter without typing if you want to skip
|
||||||
|
* a field and not to send it to the server.
|
||||||
|
*
|
||||||
|
* Notice, the example doesn't implement forms. Therefore, it won't work
|
||||||
|
* in complicated scenarios.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <strophe.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
xmpp_ctx_t *ctx;
|
||||||
|
const char *jid;
|
||||||
|
} xmpp_reg_t;
|
||||||
|
|
||||||
|
#define FEATURES_TIMEOUT 5000 /* 5 seconds */
|
||||||
|
|
||||||
|
static void
|
||||||
|
iq_reg_send_form(xmpp_reg_t *reg, xmpp_conn_t *conn, xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
xmpp_ctx_t *ctx = reg->ctx;
|
||||||
|
xmpp_stanza_t *query;
|
||||||
|
xmpp_stanza_t *next;
|
||||||
|
xmpp_stanza_t *elem;
|
||||||
|
xmpp_stanza_t *text;
|
||||||
|
xmpp_stanza_t *iq;
|
||||||
|
const char *name;
|
||||||
|
size_t len;
|
||||||
|
char buf[256];
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
query = xmpp_stanza_get_child_by_name(stanza, "query");
|
||||||
|
if (!query) {
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next = xmpp_stanza_get_children(query);
|
||||||
|
query = xmpp_stanza_new(ctx);
|
||||||
|
xmpp_stanza_set_name(query, "query");
|
||||||
|
xmpp_stanza_set_ns(query, XMPP_NS_REGISTER);
|
||||||
|
while (next) {
|
||||||
|
name = xmpp_stanza_get_name(next);
|
||||||
|
if (name && strcmp(name, "instructions") == 0) {
|
||||||
|
s = xmpp_stanza_get_text(next);
|
||||||
|
printf("instructions: %s\n", s);
|
||||||
|
xmpp_free(ctx, s);
|
||||||
|
} else {
|
||||||
|
printf("%s: ", name);
|
||||||
|
s = fgets(buf, sizeof(buf), stdin);
|
||||||
|
if (s != NULL) {
|
||||||
|
len = strlen(s);
|
||||||
|
if (len > 0 && s[len - 1] == '\n') {
|
||||||
|
s[len - 1] = '\0';
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
|
elem = xmpp_stanza_new(ctx);
|
||||||
|
text = xmpp_stanza_new(ctx);
|
||||||
|
xmpp_stanza_set_text(text, s);
|
||||||
|
xmpp_stanza_set_name(elem, name);
|
||||||
|
xmpp_stanza_add_child(elem, text);
|
||||||
|
xmpp_stanza_add_child(query, elem);
|
||||||
|
xmpp_stanza_release(text);
|
||||||
|
xmpp_stanza_release(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next = xmpp_stanza_get_next(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xmpp_stanza_get_children(query) == NULL) {
|
||||||
|
fprintf(stderr, "DEBUG: nothing to send\n");
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
} else {
|
||||||
|
iq = xmpp_iq_new(ctx, "set", "reg2");
|
||||||
|
xmpp_stanza_add_child(iq, query);
|
||||||
|
xmpp_stanza_release(query);
|
||||||
|
xmpp_send(conn, iq);
|
||||||
|
xmpp_stanza_release(iq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iq_reg2_cb(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||||
|
{
|
||||||
|
const char *type;
|
||||||
|
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
type = xmpp_stanza_get_type(stanza);
|
||||||
|
if (!type || strcmp(type, "error") == 0) {
|
||||||
|
fprintf(stderr, "DEBUG: error during registration\n");
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(type, "result") != 0) {
|
||||||
|
fprintf(stderr, "DEBUG: expected type 'result', but got %s\n", type);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "DEBUG: successful registration\n");
|
||||||
|
|
||||||
|
quit:
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int iq_reg_cb(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||||
|
{
|
||||||
|
xmpp_reg_t *reg = (xmpp_reg_t *)userdata;
|
||||||
|
xmpp_stanza_t *registered = NULL;
|
||||||
|
xmpp_stanza_t *query;
|
||||||
|
const char *type;
|
||||||
|
|
||||||
|
type = xmpp_stanza_get_type(stanza);
|
||||||
|
if (!type || strcmp(type, "error") == 0) {
|
||||||
|
fprintf(stderr, "DEBUG: error during registration\n");
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(type, "result") != 0) {
|
||||||
|
fprintf(stderr, "DEBUG: expected type 'result', but got %s\n", type);
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
query = xmpp_stanza_get_child_by_name(stanza, "query");
|
||||||
|
if (query)
|
||||||
|
registered = xmpp_stanza_get_child_by_name(query, "registered");
|
||||||
|
if (registered != NULL) {
|
||||||
|
fprintf(stderr, "DEBUG: already registered\n");
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
xmpp_id_handler_add(conn, iq_reg2_cb, "reg2", reg);
|
||||||
|
iq_reg_send_form(reg, conn, stanza);
|
||||||
|
|
||||||
|
quit:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_handle_error(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||||
|
{
|
||||||
|
(void)stanza;
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
fprintf(stderr, "DEBUG: received stream error\n");
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _handle_proceedtls_default(xmpp_conn_t *conn,
|
||||||
|
xmpp_stanza_t *stanza,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
const char *name = xmpp_stanza_get_name(stanza);
|
||||||
|
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
if (strcmp(name, "proceed") == 0) {
|
||||||
|
fprintf(stderr, "DEBUG: proceeding with TLS\n");
|
||||||
|
if (xmpp_conn_tls_start(conn) == 0) {
|
||||||
|
xmpp_handler_delete(conn, _handle_error);
|
||||||
|
xmpp_conn_open_stream_default(conn);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "DEBUG: TLS failed\n");
|
||||||
|
/* failed tls spoils the connection, so disconnect */
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _handle_missing_features(xmpp_conn_t *conn, void *userdata)
|
||||||
|
{
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
fprintf(stderr, "DEBUG: timeout\n");
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||||
|
{
|
||||||
|
xmpp_reg_t *reg = (xmpp_reg_t *)userdata;
|
||||||
|
xmpp_ctx_t *ctx = reg->ctx;
|
||||||
|
xmpp_stanza_t *child;
|
||||||
|
xmpp_stanza_t *iq;
|
||||||
|
char *domain;
|
||||||
|
|
||||||
|
xmpp_timed_handler_delete(conn, _handle_missing_features);
|
||||||
|
|
||||||
|
/* secure connection if possible */
|
||||||
|
child = xmpp_stanza_get_child_by_name(stanza, "starttls");
|
||||||
|
if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0)) {
|
||||||
|
fprintf(stderr, "DEBUG: server supports TLS, try to establish\n");
|
||||||
|
child = xmpp_stanza_new(ctx);
|
||||||
|
xmpp_stanza_set_name(child, "starttls");
|
||||||
|
xmpp_stanza_set_ns(child, XMPP_NS_TLS);
|
||||||
|
xmpp_handler_add(conn, _handle_proceedtls_default, XMPP_NS_TLS, NULL,
|
||||||
|
NULL, NULL);
|
||||||
|
xmpp_send(conn, child);
|
||||||
|
xmpp_stanza_release(child);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check whether server supports in-band registration */
|
||||||
|
child = xmpp_stanza_get_child_by_name(stanza, "register");
|
||||||
|
if (child && strcmp(xmpp_stanza_get_ns(child), XMPP_NS_REGISTER) == 0) {
|
||||||
|
fprintf(stderr, "DEBUG: server doesn't support in-band registration\n");
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "DEBUG: server supports in-band registration\n");
|
||||||
|
domain = xmpp_jid_domain(ctx, reg->jid);
|
||||||
|
iq = xmpp_iq_new(ctx, "get", "reg1");
|
||||||
|
xmpp_stanza_set_to(iq, domain);
|
||||||
|
child = xmpp_stanza_new(ctx);
|
||||||
|
xmpp_stanza_set_name(child, "query");
|
||||||
|
xmpp_stanza_set_ns(child, XMPP_NS_REGISTER);
|
||||||
|
xmpp_stanza_add_child(iq, child);
|
||||||
|
|
||||||
|
xmpp_handler_add(conn, iq_reg_cb, XMPP_NS_REGISTER, "iq", NULL, reg);
|
||||||
|
xmpp_send(conn, iq);
|
||||||
|
|
||||||
|
xmpp_free(ctx, domain);
|
||||||
|
xmpp_stanza_release(child);
|
||||||
|
xmpp_stanza_release(iq);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void conn_handler(xmpp_conn_t *conn,
|
||||||
|
xmpp_conn_event_t status,
|
||||||
|
int error,
|
||||||
|
xmpp_stream_error_t *stream_error,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
xmpp_reg_t *reg = (xmpp_reg_t *)userdata;
|
||||||
|
int secured;
|
||||||
|
|
||||||
|
(void)error;
|
||||||
|
(void)stream_error;
|
||||||
|
|
||||||
|
if (status == XMPP_CONN_RAW_CONNECT) {
|
||||||
|
fprintf(stderr, "DEBUG: raw connection established\n");
|
||||||
|
xmpp_conn_open_stream_default(conn);
|
||||||
|
} else if (status == XMPP_CONN_CONNECT) {
|
||||||
|
fprintf(stderr, "DEBUG: stream opened\n");
|
||||||
|
secured = xmpp_conn_is_secured(conn);
|
||||||
|
fprintf(stderr, "DEBUG: connection is %s.\n",
|
||||||
|
secured ? "secured" : "NOT secured");
|
||||||
|
|
||||||
|
/* setup handler for stream:error */
|
||||||
|
xmpp_handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* setup handlers for incoming <stream:features> */
|
||||||
|
xmpp_handler_add(conn, _handle_features, XMPP_NS_STREAMS, "features",
|
||||||
|
NULL, reg);
|
||||||
|
xmpp_timed_handler_add(conn, _handle_missing_features, FEATURES_TIMEOUT,
|
||||||
|
NULL);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "DEBUG: disconnected\n");
|
||||||
|
xmpp_stop(reg->ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xmpp_reg_t *xmpp_reg_new(void)
|
||||||
|
{
|
||||||
|
xmpp_reg_t *reg;
|
||||||
|
|
||||||
|
reg = malloc(sizeof(*reg));
|
||||||
|
if (reg != NULL) {
|
||||||
|
memset(reg, 0, sizeof(*reg));
|
||||||
|
}
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xmpp_reg_release(xmpp_reg_t *reg)
|
||||||
|
{
|
||||||
|
free(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
xmpp_ctx_t *ctx;
|
||||||
|
xmpp_conn_t *conn;
|
||||||
|
xmpp_log_t *log;
|
||||||
|
xmpp_reg_t *reg;
|
||||||
|
const char *jid;
|
||||||
|
const char *host = NULL;
|
||||||
|
char *domain;
|
||||||
|
|
||||||
|
if (argc < 2 || argc > 3) {
|
||||||
|
fprintf(stderr, "Usage: %s <jid> [<host>]\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jid = argv[1];
|
||||||
|
if (argc > 2)
|
||||||
|
host = argv[2];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note, this example doesn't handle errors. Applications should check
|
||||||
|
* return values of non-void functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
xmpp_initialize();
|
||||||
|
log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG);
|
||||||
|
ctx = xmpp_ctx_new(NULL, log);
|
||||||
|
conn = xmpp_conn_new(ctx);
|
||||||
|
|
||||||
|
/* jid can be a jid or domain for "raw" connection */
|
||||||
|
domain = xmpp_jid_domain(ctx, jid);
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_JID, domain);
|
||||||
|
xmpp_free(ctx, domain);
|
||||||
|
|
||||||
|
/* private data */
|
||||||
|
reg = xmpp_reg_new();
|
||||||
|
reg->ctx = ctx;
|
||||||
|
reg->jid = jid;
|
||||||
|
|
||||||
|
xmpp_connect_raw(conn, host, 0, conn_handler, reg);
|
||||||
|
xmpp_run(ctx);
|
||||||
|
|
||||||
|
/* release private data */
|
||||||
|
xmpp_reg_release(reg);
|
||||||
|
|
||||||
|
xmpp_conn_release(conn);
|
||||||
|
xmpp_ctx_free(ctx);
|
||||||
|
xmpp_shutdown();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* roster.c
|
/* roster.c
|
||||||
** libstrophe XMPP client library -- handler example
|
** libstrophe XMPP client library -- handler example
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* This example demonstrates basic handler functions by printing out
|
/* This example demonstrates basic handler functions by printing out
|
||||||
@@ -21,31 +19,29 @@
|
|||||||
|
|
||||||
#include <strophe.h>
|
#include <strophe.h>
|
||||||
|
|
||||||
int handle_reply(xmpp_conn_t * const conn,
|
int handle_reply(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||||
xmpp_stanza_t * const stanza,
|
|
||||||
void * const userdata)
|
|
||||||
{
|
{
|
||||||
xmpp_stanza_t *query, *item;
|
xmpp_stanza_t *query, *item;
|
||||||
char *type, *name;
|
const char *type, *name;
|
||||||
|
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
type = xmpp_stanza_get_type(stanza);
|
type = xmpp_stanza_get_type(stanza);
|
||||||
if (strcmp(type, "error") == 0)
|
if (strcmp(type, "error") == 0)
|
||||||
fprintf(stderr, "ERROR: query failed\n");
|
fprintf(stderr, "ERROR: query failed\n");
|
||||||
else {
|
else {
|
||||||
query = xmpp_stanza_get_child_by_name(stanza, "query");
|
query = xmpp_stanza_get_child_by_name(stanza, "query");
|
||||||
printf("Roster:\n");
|
printf("Roster:\n");
|
||||||
for (item = xmpp_stanza_get_children(query); item;
|
for (item = xmpp_stanza_get_children(query); item;
|
||||||
item = xmpp_stanza_get_next(item))
|
item = xmpp_stanza_get_next(item))
|
||||||
if ((name = xmpp_stanza_get_attribute(item, "name")))
|
if ((name = xmpp_stanza_get_attribute(item, "name")))
|
||||||
printf("\t %s (%s) sub=%s\n",
|
printf("\t %s (%s) sub=%s\n", name,
|
||||||
name,
|
xmpp_stanza_get_attribute(item, "jid"),
|
||||||
xmpp_stanza_get_attribute(item, "jid"),
|
xmpp_stanza_get_attribute(item, "subscription"));
|
||||||
xmpp_stanza_get_attribute(item, "subscription"));
|
else
|
||||||
else
|
printf("\t %s sub=%s\n", xmpp_stanza_get_attribute(item, "jid"),
|
||||||
printf("\t %s sub=%s\n",
|
xmpp_stanza_get_attribute(item, "subscription"));
|
||||||
xmpp_stanza_get_attribute(item, "jid"),
|
printf("END OF LIST\n");
|
||||||
xmpp_stanza_get_attribute(item, "subscription"));
|
|
||||||
printf("END OF LIST\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* disconnect */
|
/* disconnect */
|
||||||
@@ -54,42 +50,44 @@ int handle_reply(xmpp_conn_t * const conn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
|
void conn_handler(xmpp_conn_t *conn,
|
||||||
const int error, xmpp_stream_error_t * const stream_error,
|
xmpp_conn_event_t status,
|
||||||
void * const userdata)
|
int error,
|
||||||
|
xmpp_stream_error_t *stream_error,
|
||||||
|
void *userdata)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
|
||||||
xmpp_stanza_t *iq, *query;
|
xmpp_stanza_t *iq, *query;
|
||||||
|
|
||||||
|
(void)error;
|
||||||
|
(void)stream_error;
|
||||||
|
|
||||||
if (status == XMPP_CONN_CONNECT) {
|
if (status == XMPP_CONN_CONNECT) {
|
||||||
fprintf(stderr, "DEBUG: connected\n");
|
fprintf(stderr, "DEBUG: connected\n");
|
||||||
|
|
||||||
/* create iq stanza for request */
|
/* create iq stanza for request */
|
||||||
iq = xmpp_stanza_new(ctx);
|
iq = xmpp_iq_new(ctx, "get", "roster1");
|
||||||
xmpp_stanza_set_name(iq, "iq");
|
|
||||||
xmpp_stanza_set_type(iq, "get");
|
|
||||||
xmpp_stanza_set_id(iq, "roster1");
|
|
||||||
|
|
||||||
query = xmpp_stanza_new(ctx);
|
query = xmpp_stanza_new(ctx);
|
||||||
xmpp_stanza_set_name(query, "query");
|
xmpp_stanza_set_name(query, "query");
|
||||||
xmpp_stanza_set_ns(query, XMPP_NS_ROSTER);
|
xmpp_stanza_set_ns(query, XMPP_NS_ROSTER);
|
||||||
|
|
||||||
xmpp_stanza_add_child(iq, query);
|
xmpp_stanza_add_child(iq, query);
|
||||||
|
|
||||||
/* we can release the stanza since it belongs to iq now */
|
/* we can release the stanza since it belongs to iq now */
|
||||||
xmpp_stanza_release(query);
|
xmpp_stanza_release(query);
|
||||||
|
|
||||||
/* set up reply handler */
|
/* set up reply handler */
|
||||||
xmpp_id_handler_add(conn, handle_reply, "roster1", ctx);
|
xmpp_id_handler_add(conn, handle_reply, "roster1", ctx);
|
||||||
|
|
||||||
/* send out the stanza */
|
/* send out the stanza */
|
||||||
xmpp_send(conn, iq);
|
xmpp_send(conn, iq);
|
||||||
|
|
||||||
/* release the stanza */
|
/* release the stanza */
|
||||||
xmpp_stanza_release(iq);
|
xmpp_stanza_release(iq);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "DEBUG: disconnected\n");
|
fprintf(stderr, "DEBUG: disconnected\n");
|
||||||
xmpp_stop(ctx);
|
xmpp_stop(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,8 +97,8 @@ int main(int argc, char **argv)
|
|||||||
xmpp_conn_t *conn;
|
xmpp_conn_t *conn;
|
||||||
|
|
||||||
if (argc != 3) {
|
if (argc != 3) {
|
||||||
fprintf(stderr, "Usage: roster <jid> <pass>\n\n");
|
fprintf(stderr, "Usage: roster <jid> <pass>\n\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize lib */
|
/* initialize lib */
|
||||||
@@ -112,9 +110,16 @@ int main(int argc, char **argv)
|
|||||||
/* create a connection */
|
/* create a connection */
|
||||||
conn = xmpp_conn_new(ctx);
|
conn = xmpp_conn_new(ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* also you can disable TLS support or force legacy SSL
|
||||||
|
* connection without STARTTLS
|
||||||
|
*
|
||||||
|
* see xmpp_conn_set_flags() or examples/basic.c
|
||||||
|
*/
|
||||||
|
|
||||||
/* setup authentication information */
|
/* setup authentication information */
|
||||||
xmpp_conn_set_jid(conn, argv[1]);
|
xmpp_conn_set_string(conn, XMPP_SETTING_JID, argv[1]);
|
||||||
xmpp_conn_set_pass(conn, argv[2]);
|
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, argv[2]);
|
||||||
|
|
||||||
/* initiate connection */
|
/* initiate connection */
|
||||||
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);
|
xmpp_connect_client(conn, NULL, 0, conn_handler, ctx);
|
||||||
|
|||||||
23
examples/uuid.c
Normal file
23
examples/uuid.c
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <strophe.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
xmpp_ctx_t *ctx;
|
||||||
|
char *uuid;
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
ctx = xmpp_ctx_new(NULL, NULL);
|
||||||
|
|
||||||
|
uuid = xmpp_uuid_gen(ctx);
|
||||||
|
if (uuid != NULL) {
|
||||||
|
printf("%s\n", uuid);
|
||||||
|
xmpp_free(ctx, uuid);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Couldn't allocate memory.\n");
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
xmpp_ctx_free(ctx);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
282
examples/vcard.c
Normal file
282
examples/vcard.c
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* vcard.c
|
||||||
|
* strophe XMPP client library -- vCard example
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <libgen.h> /* basename */
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <strophe.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
xmpp_ctx_t *ctx;
|
||||||
|
const char *recipient;
|
||||||
|
const char *img_path;
|
||||||
|
} vcard_t;
|
||||||
|
|
||||||
|
typedef void (*vcard_cb_t)(vcard_t *, xmpp_stanza_t *);
|
||||||
|
|
||||||
|
#define REQ_TIMEOUT 5000
|
||||||
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
|
||||||
|
static void vcard_photo(vcard_t *vc, xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
xmpp_stanza_t *tmp;
|
||||||
|
char *s;
|
||||||
|
char *tok;
|
||||||
|
char *saveptr = NULL;
|
||||||
|
char *copy;
|
||||||
|
unsigned char *img;
|
||||||
|
size_t img_size;
|
||||||
|
size_t written;
|
||||||
|
FILE *fd;
|
||||||
|
|
||||||
|
tmp = xmpp_stanza_get_child_by_name(stanza, "TYPE");
|
||||||
|
assert(tmp != NULL);
|
||||||
|
s = xmpp_stanza_get_text(tmp);
|
||||||
|
assert(s != NULL);
|
||||||
|
printf("PHOTO: %s, saving to file %s\n", s, vc->img_path);
|
||||||
|
xmpp_free(vc->ctx, s);
|
||||||
|
|
||||||
|
tmp = xmpp_stanza_get_child_by_name(stanza, "BINVAL");
|
||||||
|
assert(tmp != NULL);
|
||||||
|
s = xmpp_stanza_get_text(tmp);
|
||||||
|
assert(s != NULL);
|
||||||
|
|
||||||
|
/* remove \n and \r */
|
||||||
|
copy = (char *)malloc(strlen(s) + 1);
|
||||||
|
assert(copy != NULL);
|
||||||
|
copy[0] = '\0';
|
||||||
|
tok = strtok_r(s, "\n\r", &saveptr);
|
||||||
|
while (tok != NULL) {
|
||||||
|
strcat(copy, tok);
|
||||||
|
tok = strtok_r(NULL, "\n\r", &saveptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
xmpp_base64_decode_bin(vc->ctx, copy, strlen(copy), &img, &img_size);
|
||||||
|
assert(img != NULL);
|
||||||
|
|
||||||
|
fd = fopen(vc->img_path, "w");
|
||||||
|
assert(fd != NULL);
|
||||||
|
written = fwrite(img, 1, img_size, fd);
|
||||||
|
if (written < img_size)
|
||||||
|
printf("Saving photo failed\n");
|
||||||
|
fclose(fd);
|
||||||
|
|
||||||
|
free(copy);
|
||||||
|
xmpp_free(vc->ctx, s);
|
||||||
|
xmpp_free(vc->ctx, img);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vcard_print_string(vcard_t *vc, xmpp_stanza_t *stanza, const char *info)
|
||||||
|
{
|
||||||
|
char *s = xmpp_stanza_get_text(stanza);
|
||||||
|
|
||||||
|
assert(s != NULL);
|
||||||
|
printf("%s: %s\n", info, s);
|
||||||
|
xmpp_free(vc->ctx, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcard_bday(vcard_t *vc, xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
vcard_print_string(vc, stanza, "Birthday");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcard_desc(vcard_t *vc, xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
vcard_print_string(vc, stanza, "Description");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcard_email(vcard_t *vc, xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
xmpp_stanza_t *userid = xmpp_stanza_get_child_by_name(stanza, "USERID");
|
||||||
|
|
||||||
|
if (userid != NULL)
|
||||||
|
vcard_print_string(vc, userid, "E-mail");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcard_fn(vcard_t *vc, xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
vcard_print_string(vc, stanza, "Full name");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcard_name(vcard_t *vc, xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
xmpp_stanza_t *name = xmpp_stanza_get_child_by_name(stanza, "GIVEN");
|
||||||
|
xmpp_stanza_t *family = xmpp_stanza_get_child_by_name(stanza, "FAMILY");
|
||||||
|
|
||||||
|
if (name != NULL)
|
||||||
|
vcard_print_string(vc, name, "Given name");
|
||||||
|
if (family != NULL)
|
||||||
|
vcard_print_string(vc, family, "Family name");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcard_nick(vcard_t *vc, xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
vcard_print_string(vc, stanza, "Nickname");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vcard_url(vcard_t *vc, xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
vcard_print_string(vc, stanza, "URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
static vcard_cb_t vcard_cb_get(xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
vcard_cb_t cb = NULL;
|
||||||
|
const char *tag;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
const char *tag;
|
||||||
|
vcard_cb_t cb;
|
||||||
|
} vcard_tbl[] = {
|
||||||
|
{"PHOTO", vcard_photo}, {"BDAY", vcard_bday}, {"DESC", vcard_desc},
|
||||||
|
{"EMAIL", vcard_email}, {"FN", vcard_fn}, {"N", vcard_name},
|
||||||
|
{"NICKNAME", vcard_nick}, {"URL", vcard_url},
|
||||||
|
};
|
||||||
|
|
||||||
|
tag = xmpp_stanza_get_name(stanza);
|
||||||
|
if (tag == NULL)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(vcard_tbl); ++i) {
|
||||||
|
if (strcmp(tag, vcard_tbl[i].tag) == 0) {
|
||||||
|
cb = vcard_tbl[i].cb;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int timedout(xmpp_conn_t *conn, void *userdata)
|
||||||
|
{
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
fprintf(stderr, "Timeout reached.\n");
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int recv_vcard(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
|
||||||
|
{
|
||||||
|
vcard_t *vc = userdata;
|
||||||
|
vcard_cb_t cb;
|
||||||
|
xmpp_stanza_t *child;
|
||||||
|
char *s;
|
||||||
|
size_t s_size;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
printf("Received vCard.\n\n");
|
||||||
|
|
||||||
|
child = xmpp_stanza_get_child_by_name(stanza, "error");
|
||||||
|
if (child != NULL) {
|
||||||
|
rc = xmpp_stanza_to_text(child, &s, &s_size);
|
||||||
|
assert(rc == XMPP_EOK);
|
||||||
|
printf("Error returned: %s.\n", s);
|
||||||
|
xmpp_free(vc->ctx, s);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
child = xmpp_stanza_get_child_by_name(stanza, "vCard");
|
||||||
|
assert(child != NULL);
|
||||||
|
child = xmpp_stanza_get_children(child);
|
||||||
|
while (child != NULL) {
|
||||||
|
cb = vcard_cb_get(child);
|
||||||
|
if (cb != NULL)
|
||||||
|
cb(vc, child);
|
||||||
|
child = xmpp_stanza_get_next(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
xmpp_disconnect(conn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_vcard_req(xmpp_conn_t *conn, const char *to, const char *id)
|
||||||
|
{
|
||||||
|
printf("Requesting vCard from %s.\n", to);
|
||||||
|
xmpp_send_raw_string(conn,
|
||||||
|
"<iq from='%s' to='%s' type='get' id='%s'>"
|
||||||
|
"<vCard xmlns='vcard-temp'/></iq>",
|
||||||
|
xmpp_conn_get_bound_jid(conn), to, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void conn_handler(xmpp_conn_t *conn,
|
||||||
|
xmpp_conn_event_t status,
|
||||||
|
int error,
|
||||||
|
xmpp_stream_error_t *stream_error,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
vcard_t *vc = userdata;
|
||||||
|
|
||||||
|
if (status == XMPP_CONN_CONNECT) {
|
||||||
|
send_vcard_req(conn, vc->recipient, "vc1");
|
||||||
|
xmpp_id_handler_add(conn, recv_vcard, "vc1", vc);
|
||||||
|
xmpp_timed_handler_add(conn, timedout, REQ_TIMEOUT, NULL);
|
||||||
|
} else {
|
||||||
|
if (error != 0)
|
||||||
|
fprintf(stderr, "Disconnected with error=%d.\n", error);
|
||||||
|
if (stream_error != NULL)
|
||||||
|
fprintf(stderr, "Stream error type=%d text=%s.\n",
|
||||||
|
stream_error->type, stream_error->text);
|
||||||
|
xmpp_stop(vc->ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
xmpp_log_t *log;
|
||||||
|
xmpp_ctx_t *ctx;
|
||||||
|
xmpp_conn_t *conn;
|
||||||
|
const char *jid;
|
||||||
|
const char *pass;
|
||||||
|
char *prog;
|
||||||
|
vcard_t vcard;
|
||||||
|
|
||||||
|
if (argc < 4 || argc > 5) {
|
||||||
|
prog = argc > 0 ? strdup(argv[0]) : NULL;
|
||||||
|
printf("Usage: %s <login-jid> <password> <recipient-jid> "
|
||||||
|
"[image-file]\n\n",
|
||||||
|
prog == NULL ? "vcard" : basename(prog));
|
||||||
|
printf("If vCard contains a photo it will be stored to "
|
||||||
|
"image-file. If you don't provide the image-file "
|
||||||
|
"default filename will be generated.\n");
|
||||||
|
free(prog);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
jid = argv[1];
|
||||||
|
pass = argv[2];
|
||||||
|
vcard.recipient = argv[3];
|
||||||
|
vcard.img_path = argc > 4 ? argv[4] : "vcard.jpg";
|
||||||
|
|
||||||
|
xmpp_initialize();
|
||||||
|
log = xmpp_get_default_logger(XMPP_LEVEL_INFO);
|
||||||
|
ctx = xmpp_ctx_new(NULL, log);
|
||||||
|
conn = xmpp_conn_new(ctx);
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass);
|
||||||
|
vcard.ctx = ctx;
|
||||||
|
xmpp_connect_client(conn, NULL, 0, conn_handler, &vcard);
|
||||||
|
xmpp_run(ctx);
|
||||||
|
xmpp_conn_release(conn);
|
||||||
|
xmpp_ctx_free(ctx);
|
||||||
|
xmpp_shutdown();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
1
expat
1
expat
Submodule expat deleted from adf19a3186
80
jni/Android.mk
Normal file
80
jni/Android.mk
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
|
||||||
|
#
|
||||||
|
# examples/basic
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := basic
|
||||||
|
LOCAL_CFLAGS :=
|
||||||
|
LOCAL_C_INCLUDES := \
|
||||||
|
$(LOCAL_PATH)/..
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
../examples/basic.c
|
||||||
|
|
||||||
|
LOCAL_STATIC_LIBRARIES := libstrophe libexpat
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
|
#
|
||||||
|
# libstrohe (static library)
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libstrophe
|
||||||
|
LOCAL_CFLAGS := -DHAVE_DECL_VA_COPY
|
||||||
|
LOCAL_C_INCLUDES := \
|
||||||
|
$(LOCAL_PATH)/.. \
|
||||||
|
$(LOCAL_PATH)/../src \
|
||||||
|
$(LOCAL_PATH)/../expat/lib
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
../src/auth.c \
|
||||||
|
../src/conn.c \
|
||||||
|
../src/crypto.c \
|
||||||
|
../src/ctx.c \
|
||||||
|
../src/event.c \
|
||||||
|
../src/handler.c \
|
||||||
|
../src/hash.c \
|
||||||
|
../src/jid.c \
|
||||||
|
../src/md5.c \
|
||||||
|
../src/parser_expat.c \
|
||||||
|
../src/rand.c \
|
||||||
|
../src/resolver.c \
|
||||||
|
../src/sasl.c \
|
||||||
|
../src/scram.c \
|
||||||
|
../src/sha1.c \
|
||||||
|
../src/sha256.c \
|
||||||
|
../src/sha512.c \
|
||||||
|
../src/snprintf.c \
|
||||||
|
../src/sock.c \
|
||||||
|
../src/stanza.c \
|
||||||
|
../src/tls_dummy.c \
|
||||||
|
../src/util.c \
|
||||||
|
../src/uuid.c
|
||||||
|
|
||||||
|
LOCAL_STATIC_LIBRARIES := libstrophe
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
#
|
||||||
|
# expat
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libexpat
|
||||||
|
LOCAL_CFLAGS := -DHAVE_MEMMOVE -DXML_DEV_URANDOM
|
||||||
|
#LOCAL_C_INCLUDES := \
|
||||||
|
# $(LOCAL_PATH)/expat
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := \
|
||||||
|
../expat/lib/xmlparse.c \
|
||||||
|
../expat/lib/xmlrole.c \
|
||||||
|
../expat/lib/xmltok.c \
|
||||||
|
../expat/lib/xmltok_impl.c \
|
||||||
|
../expat/lib/xmltok_ns.c
|
||||||
|
|
||||||
|
LOCAL_STATIC_LIBRARIES := libexpat
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
2
jni/Application.mk
Normal file
2
jni/Application.mk
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
APP_ABI := armeabi-v7a arm64-v8a
|
||||||
|
APP_PLATFORM := android-19
|
||||||
14
libstrophe.pc.in
Normal file
14
libstrophe.pc.in
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
prefix=@prefix@
|
||||||
|
exec_prefix=@exec_prefix@
|
||||||
|
libdir=@libdir@
|
||||||
|
includedir=@includedir@
|
||||||
|
|
||||||
|
Name: libstrophe
|
||||||
|
Description: A simple, lightweight C library for writing XMPP clients
|
||||||
|
URL: https://strophe.im/libstrophe/
|
||||||
|
Version: @VERSION@
|
||||||
|
Requires:
|
||||||
|
Requires.private: @PC_REQUIRES@
|
||||||
|
Libs: -L${libdir} -lstrophe
|
||||||
|
Libs.private: @PC_LIBS@
|
||||||
|
Cflags: -I${includedir} @PC_CFLAGS@
|
||||||
28
m4/ac_try_compile2.m4
Normal file
28
m4/ac_try_compile2.m4
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
dnl Like AC_TRY_EVAL but also errors out if the compiler generates
|
||||||
|
dnl _any_ output. Some compilers might issue warnings which we want
|
||||||
|
dnl to catch.
|
||||||
|
AC_DEFUN([AC_TRY_EVAL2],
|
||||||
|
[{ (eval echo configure:__oline__: \"[$]$1\") 1>&AS_MESSAGE_LOG_FD; dnl
|
||||||
|
(eval [$]$1) 2>&AS_MESSAGE_LOG_FD; _out=`eval [$]$1 2>&1` && test "x$_out" = x; }])
|
||||||
|
|
||||||
|
dnl Like AC_TRY_COMPILE but calls AC_TRY_EVAL2 instead of AC_TRY_EVAL
|
||||||
|
AC_DEFUN([AC_TRY_COMPILE2],
|
||||||
|
[cat > conftest.$ac_ext <<EOF
|
||||||
|
[#]line __oline__ "configure"
|
||||||
|
#include "confdefs.h"
|
||||||
|
[$1]
|
||||||
|
int main(void) {
|
||||||
|
[$2]
|
||||||
|
; return 0; }
|
||||||
|
EOF
|
||||||
|
if AC_TRY_EVAL2(ac_compile); then
|
||||||
|
ifelse([$3], , :, [rm -rf conftest*
|
||||||
|
$3])
|
||||||
|
else
|
||||||
|
echo "configure: failed program was:" >&AS_MESSAGE_LOG_FD
|
||||||
|
cat conftest.$ac_ext >&AS_MESSAGE_LOG_FD
|
||||||
|
ifelse([$4], , , [ rm -rf conftest*
|
||||||
|
$4
|
||||||
|
])dnl
|
||||||
|
fi
|
||||||
|
rm -f conftest*])
|
||||||
239
m4/ax_valgrind_check.m4
Normal file
239
m4/ax_valgrind_check.m4
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# ===========================================================================
|
||||||
|
# https://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html
|
||||||
|
# ===========================================================================
|
||||||
|
#
|
||||||
|
# SYNOPSIS
|
||||||
|
#
|
||||||
|
# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off)
|
||||||
|
# AX_VALGRIND_CHECK()
|
||||||
|
#
|
||||||
|
# DESCRIPTION
|
||||||
|
#
|
||||||
|
# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows
|
||||||
|
# running `make check` under a variety of Valgrind tools to check for
|
||||||
|
# memory and threading errors.
|
||||||
|
#
|
||||||
|
# Defines VALGRIND_CHECK_RULES which should be substituted in your
|
||||||
|
# Makefile; and $enable_valgrind which can be used in subsequent configure
|
||||||
|
# output. VALGRIND_ENABLED is defined and substituted, and corresponds to
|
||||||
|
# the value of the --enable-valgrind option, which defaults to being
|
||||||
|
# enabled if Valgrind is installed and disabled otherwise. Individual
|
||||||
|
# Valgrind tools can be disabled via --disable-valgrind-<tool>, the
|
||||||
|
# default is configurable via the AX_VALGRIND_DFLT command or is to use
|
||||||
|
# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT
|
||||||
|
# calls must be made before the call to AX_VALGRIND_CHECK.
|
||||||
|
#
|
||||||
|
# If unit tests are written using a shell script and automake's
|
||||||
|
# LOG_COMPILER system, the $(VALGRIND) variable can be used within the
|
||||||
|
# shell scripts to enable Valgrind, as described here:
|
||||||
|
#
|
||||||
|
# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html
|
||||||
|
#
|
||||||
|
# Usage example:
|
||||||
|
#
|
||||||
|
# configure.ac:
|
||||||
|
#
|
||||||
|
# AX_VALGRIND_DFLT([sgcheck], [off])
|
||||||
|
# AX_VALGRIND_CHECK
|
||||||
|
#
|
||||||
|
# in each Makefile.am with tests:
|
||||||
|
#
|
||||||
|
# @VALGRIND_CHECK_RULES@
|
||||||
|
# VALGRIND_SUPPRESSIONS_FILES = my-project.supp
|
||||||
|
# EXTRA_DIST = my-project.supp
|
||||||
|
#
|
||||||
|
# This results in a "check-valgrind" rule being added. Running `make
|
||||||
|
# check-valgrind` in that directory will recursively run the module's test
|
||||||
|
# suite (`make check`) once for each of the available Valgrind tools (out
|
||||||
|
# of memcheck, helgrind and drd) while the sgcheck will be skipped unless
|
||||||
|
# enabled again on the commandline with --enable-valgrind-sgcheck. The
|
||||||
|
# results for each check will be output to test-suite-$toolname.log. The
|
||||||
|
# target will succeed if there are zero errors and fail otherwise.
|
||||||
|
#
|
||||||
|
# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in
|
||||||
|
# memcheck, helgrind, drd and sgcheck. These are useful because often only
|
||||||
|
# some of those tools can be ran cleanly on a codebase.
|
||||||
|
#
|
||||||
|
# The macro supports running with and without libtool.
|
||||||
|
#
|
||||||
|
# LICENSE
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014, 2015, 2016 Philip Withnall <philip.withnall@collabora.co.uk>
|
||||||
|
#
|
||||||
|
# Copying and distribution of this file, with or without modification, are
|
||||||
|
# permitted in any medium without royalty provided the copyright notice
|
||||||
|
# and this notice are preserved. This file is offered as-is, without any
|
||||||
|
# warranty.
|
||||||
|
|
||||||
|
#serial 23
|
||||||
|
|
||||||
|
dnl Configured tools
|
||||||
|
m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]])
|
||||||
|
m4_set_add_all([valgrind_exp_tool_set], [sgcheck])
|
||||||
|
m4_foreach([vgtool], [valgrind_tool_list],
|
||||||
|
[m4_define([en_dflt_valgrind_]vgtool, [on])])
|
||||||
|
|
||||||
|
AC_DEFUN([AX_VALGRIND_DFLT],[
|
||||||
|
m4_define([en_dflt_valgrind_$1], [$2])
|
||||||
|
])dnl
|
||||||
|
|
||||||
|
AC_DEFUN([AX_VALGRIND_CHECK],[
|
||||||
|
AM_EXTRA_RECURSIVE_TARGETS([check-valgrind])
|
||||||
|
m4_foreach([vgtool], [valgrind_tool_list],
|
||||||
|
[AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)])
|
||||||
|
|
||||||
|
dnl Check for --enable-valgrind
|
||||||
|
AC_ARG_ENABLE([valgrind],
|
||||||
|
[AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])],
|
||||||
|
[enable_valgrind=$enableval],[enable_valgrind=])
|
||||||
|
|
||||||
|
AS_IF([test "$enable_valgrind" != "no"],[
|
||||||
|
# Check for Valgrind.
|
||||||
|
AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind])
|
||||||
|
AS_IF([test "$VALGRIND" = ""],[
|
||||||
|
AS_IF([test "$enable_valgrind" = "yes"],[
|
||||||
|
AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind])
|
||||||
|
],[
|
||||||
|
enable_valgrind=no
|
||||||
|
])
|
||||||
|
],[
|
||||||
|
enable_valgrind=yes
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
|
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
|
||||||
|
AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind])
|
||||||
|
|
||||||
|
# Check for Valgrind tools we care about.
|
||||||
|
[valgrind_enabled_tools=]
|
||||||
|
m4_foreach([vgtool],[valgrind_tool_list],[
|
||||||
|
AC_ARG_ENABLE([valgrind-]vgtool,
|
||||||
|
m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl
|
||||||
|
[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl
|
||||||
|
[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]),
|
||||||
|
[enable_valgrind_]vgtool[=$enableval],
|
||||||
|
[enable_valgrind_]vgtool[=])
|
||||||
|
AS_IF([test "$enable_valgrind" = "no"],[
|
||||||
|
enable_valgrind_]vgtool[=no],
|
||||||
|
[test "$enable_valgrind_]vgtool[" ]dnl
|
||||||
|
m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[
|
||||||
|
AC_CACHE_CHECK([for Valgrind tool ]vgtool,
|
||||||
|
[ax_cv_valgrind_tool_]vgtool,[
|
||||||
|
ax_cv_valgrind_tool_]vgtool[=no
|
||||||
|
m4_set_contains([valgrind_exp_tool_set],vgtool,
|
||||||
|
[m4_define([vgtoolx],[exp-]vgtool)],
|
||||||
|
[m4_define([vgtoolx],vgtool)])
|
||||||
|
AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[
|
||||||
|
ax_cv_valgrind_tool_]vgtool[=yes
|
||||||
|
])
|
||||||
|
])
|
||||||
|
AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[
|
||||||
|
AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[
|
||||||
|
AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool)
|
||||||
|
],[
|
||||||
|
enable_valgrind_]vgtool[=no
|
||||||
|
])
|
||||||
|
],[
|
||||||
|
enable_valgrind_]vgtool[=yes
|
||||||
|
])
|
||||||
|
])
|
||||||
|
AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[
|
||||||
|
valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])["
|
||||||
|
])
|
||||||
|
AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool)
|
||||||
|
])
|
||||||
|
AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["])
|
||||||
|
AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools])
|
||||||
|
|
||||||
|
[VALGRIND_CHECK_RULES='
|
||||||
|
# Valgrind check
|
||||||
|
#
|
||||||
|
# Optional:
|
||||||
|
# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions
|
||||||
|
# files to load. (Default: empty)
|
||||||
|
# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools.
|
||||||
|
# (Default: --num-callers=30)
|
||||||
|
# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of:
|
||||||
|
# memcheck, helgrind, drd, sgcheck). (Default: various)
|
||||||
|
|
||||||
|
# Optional variables
|
||||||
|
VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES))
|
||||||
|
VALGRIND_FLAGS ?= --num-callers=30
|
||||||
|
VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no
|
||||||
|
VALGRIND_helgrind_FLAGS ?= --history-level=approx
|
||||||
|
VALGRIND_drd_FLAGS ?=
|
||||||
|
VALGRIND_sgcheck_FLAGS ?=
|
||||||
|
|
||||||
|
# Internal use
|
||||||
|
valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools)))
|
||||||
|
|
||||||
|
valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS)
|
||||||
|
valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS)
|
||||||
|
valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS)
|
||||||
|
valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS)
|
||||||
|
|
||||||
|
valgrind_quiet = $(valgrind_quiet_$(V))
|
||||||
|
valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY))
|
||||||
|
valgrind_quiet_0 = --quiet
|
||||||
|
valgrind_v_use = $(valgrind_v_use_$(V))
|
||||||
|
valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY))
|
||||||
|
valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%-local,%,$''@):;
|
||||||
|
|
||||||
|
# Support running with and without libtool.
|
||||||
|
ifneq ($(LIBTOOL),)
|
||||||
|
valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute
|
||||||
|
else
|
||||||
|
valgrind_lt =
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Use recursive makes in order to ignore errors during check
|
||||||
|
check-valgrind-local:
|
||||||
|
ifeq ($(VALGRIND_ENABLED),yes)
|
||||||
|
$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k \
|
||||||
|
$(foreach tool, $(valgrind_enabled_tools), check-valgrind-$(tool))
|
||||||
|
else
|
||||||
|
@echo "Need to reconfigure with --enable-valgrind"
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Valgrind running
|
||||||
|
VALGRIND_TESTS_ENVIRONMENT = \
|
||||||
|
$(TESTS_ENVIRONMENT) \
|
||||||
|
env VALGRIND=$(VALGRIND) \
|
||||||
|
G_SLICE=always-malloc,debug-blocks \
|
||||||
|
G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly
|
||||||
|
|
||||||
|
VALGRIND_LOG_COMPILER = \
|
||||||
|
$(valgrind_lt) \
|
||||||
|
$(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS)
|
||||||
|
|
||||||
|
define valgrind_tool_rule
|
||||||
|
check-valgrind-$(1)-local:
|
||||||
|
ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes)
|
||||||
|
ifneq ($$(TESTS),)
|
||||||
|
$$(valgrind_v_use)$$(MAKE) check-TESTS \
|
||||||
|
TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \
|
||||||
|
LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \
|
||||||
|
LOG_FLAGS="$$(valgrind_$(1)_flags)" \
|
||||||
|
TEST_SUITE_LOG=test-suite-$(1).log
|
||||||
|
endif
|
||||||
|
else ifeq ($$(VALGRIND_ENABLED),yes)
|
||||||
|
@echo "Need to reconfigure with --enable-valgrind-$(1)"
|
||||||
|
else
|
||||||
|
@echo "Need to reconfigure with --enable-valgrind"
|
||||||
|
endif
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool))))
|
||||||
|
|
||||||
|
A''M_DISTCHECK_CONFIGURE_FLAGS ?=
|
||||||
|
A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind
|
||||||
|
|
||||||
|
MOSTLYCLEANFILES ?=
|
||||||
|
MOSTLYCLEANFILES += $(valgrind_log_files)
|
||||||
|
|
||||||
|
.PHONY: check-valgrind $(addprefix check-valgrind-,$(valgrind_tools))
|
||||||
|
']
|
||||||
|
|
||||||
|
AC_SUBST([VALGRIND_CHECK_RULES])
|
||||||
|
m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])])
|
||||||
|
])
|
||||||
31
rpm/README
31
rpm/README
@@ -1,31 +0,0 @@
|
|||||||
1. Setup
|
|
||||||
|
|
||||||
yum install gcc make expat-devel libstrophe
|
|
||||||
|
|
||||||
Fedora:
|
|
||||||
|
|
||||||
yum install fedora-packager
|
|
||||||
cd ~
|
|
||||||
rpmdev-setuptree
|
|
||||||
|
|
||||||
CentOS:
|
|
||||||
|
|
||||||
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
|
||||||
|
|
||||||
Then make a tarball of sources:
|
|
||||||
|
|
||||||
tar czf libstrophe_git.tar.gz libstrophe
|
|
||||||
|
|
||||||
2. Build
|
|
||||||
|
|
||||||
cp libstrophe.spec ~/rpmbuild/SPECS
|
|
||||||
cp libstrophe_git.tar.gz ~/rpmbuild/SOURCES
|
|
||||||
rpmbuild -bb ~/rpmbuild/SPECS/libstrophe.spec
|
|
||||||
|
|
||||||
3. Install
|
|
||||||
|
|
||||||
yum localinstall ~/rpmbuild/RPMS/<arch>/libstrophe-1.1<dist>.<arch>.rpm
|
|
||||||
|
|
||||||
Or, if localinstall is not recognised:
|
|
||||||
|
|
||||||
rpm -i ~/rpmbuild/RPMS/<arch>/libstrophe-1.1<dist>.<arch>.rpm
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
Name: libstrophe
|
|
||||||
Version: 1
|
|
||||||
Release: 1%{?dist}_git
|
|
||||||
Summary: xmpp library in C
|
|
||||||
|
|
||||||
Group: Application/System
|
|
||||||
License: MIT/GPLv3
|
|
||||||
URL: http://strophe.im/libstrophe/
|
|
||||||
Source0: libstrophe_git.tar.gz
|
|
||||||
|
|
||||||
BuildRequires: expat-devel
|
|
||||||
Requires: expat
|
|
||||||
|
|
||||||
%description
|
|
||||||
XMPP library in C
|
|
||||||
|
|
||||||
%prep
|
|
||||||
%setup -n libstrophe
|
|
||||||
./bootstrap.sh
|
|
||||||
|
|
||||||
%build
|
|
||||||
%configure
|
|
||||||
make %{?_smp_mflags}
|
|
||||||
|
|
||||||
%install
|
|
||||||
make install DESTDIR=%{buildroot}
|
|
||||||
|
|
||||||
%files
|
|
||||||
%{_libdir}/libstrophe.a
|
|
||||||
%{_includedir}/strophe.h
|
|
||||||
|
|
||||||
%changelog
|
|
||||||
2048
src/auth.c
2048
src/auth.c
File diff suppressed because it is too large
Load Diff
361
src/common.h
361
src/common.h
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* common.h
|
/* common.h
|
||||||
** strophe XMPP client library -- internal common structures
|
** strophe XMPP client library -- internal common structures
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express or
|
** This software is provided AS-IS with no warranty, either express or
|
||||||
** implied.
|
** implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -21,17 +19,47 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#ifndef _WIN32
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "strophe.h"
|
#include "strophe.h"
|
||||||
|
#include "ostypes.h"
|
||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
#include "snprintf.h"
|
||||||
|
|
||||||
|
/** handlers **/
|
||||||
|
|
||||||
|
typedef struct _xmpp_handlist_t xmpp_handlist_t;
|
||||||
|
struct _xmpp_handlist_t {
|
||||||
|
/* common members */
|
||||||
|
int user_handler;
|
||||||
|
int (*handler)();
|
||||||
|
void *userdata;
|
||||||
|
int enabled; /* handlers are added disabled and enabled after the
|
||||||
|
* handler chain is processed to prevent stanzas from
|
||||||
|
* getting processed by newly added handlers */
|
||||||
|
xmpp_handlist_t *next;
|
||||||
|
|
||||||
|
union {
|
||||||
|
/* timed handlers */
|
||||||
|
struct {
|
||||||
|
unsigned long period;
|
||||||
|
uint64_t last_stamp;
|
||||||
|
};
|
||||||
|
/* id handlers */
|
||||||
|
struct {
|
||||||
|
char *id;
|
||||||
|
};
|
||||||
|
/* normal handlers */
|
||||||
|
struct {
|
||||||
|
char *ns;
|
||||||
|
char *name;
|
||||||
|
char *type;
|
||||||
|
};
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
/** run-time context **/
|
/** run-time context **/
|
||||||
|
|
||||||
@@ -49,52 +77,61 @@ typedef struct _xmpp_connlist_t {
|
|||||||
struct _xmpp_ctx_t {
|
struct _xmpp_ctx_t {
|
||||||
const xmpp_mem_t *mem;
|
const xmpp_mem_t *mem;
|
||||||
const xmpp_log_t *log;
|
const xmpp_log_t *log;
|
||||||
|
int verbosity;
|
||||||
|
|
||||||
|
xmpp_rand_t *rand;
|
||||||
xmpp_loop_status_t loop_status;
|
xmpp_loop_status_t loop_status;
|
||||||
xmpp_connlist_t *connlist;
|
xmpp_connlist_t *connlist;
|
||||||
|
xmpp_handlist_t *timed_handlers;
|
||||||
|
|
||||||
|
unsigned long timeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* convenience functions for accessing the context */
|
/* convenience functions for accessing the context */
|
||||||
void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size);
|
void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size);
|
||||||
void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
|
void *strophe_realloc(const xmpp_ctx_t *ctx, void *p, size_t size);
|
||||||
const size_t size);
|
char *strophe_strdup(const xmpp_ctx_t *ctx, const char *s);
|
||||||
char *xmpp_strdup(const xmpp_ctx_t * const ctx, const char * const s);
|
char *strophe_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len);
|
||||||
|
void strophe_free(const xmpp_ctx_t *ctx, void *p);
|
||||||
void xmpp_log(const xmpp_ctx_t * const ctx,
|
#define strophe_free_and_null(ctx, p) \
|
||||||
const xmpp_log_level_t level,
|
do { \
|
||||||
const char * const area,
|
if (p) { \
|
||||||
const char * const fmt,
|
strophe_free(ctx, (p)); \
|
||||||
va_list ap);
|
(p) = NULL; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/* wrappers for xmpp_log at specific levels */
|
/* wrappers for xmpp_log at specific levels */
|
||||||
void xmpp_error(const xmpp_ctx_t * const ctx,
|
void strophe_error(const xmpp_ctx_t *ctx,
|
||||||
const char * const area,
|
const char *area,
|
||||||
const char * const fmt,
|
const char *fmt,
|
||||||
...);
|
...);
|
||||||
void xmpp_warn(const xmpp_ctx_t * const ctx,
|
void strophe_warn(const xmpp_ctx_t *ctx,
|
||||||
const char * const area,
|
const char *area,
|
||||||
const char * const fmt,
|
const char *fmt,
|
||||||
...);
|
...);
|
||||||
void xmpp_info(const xmpp_ctx_t * const ctx,
|
void strophe_info(const xmpp_ctx_t *ctx,
|
||||||
const char * const area,
|
const char *area,
|
||||||
const char * const fmt,
|
const char *fmt,
|
||||||
...);
|
...);
|
||||||
void xmpp_debug(const xmpp_ctx_t * const ctx,
|
void strophe_debug(const xmpp_ctx_t *ctx,
|
||||||
const char * const area,
|
const char *area,
|
||||||
const char * const fmt,
|
const char *fmt,
|
||||||
...);
|
...);
|
||||||
|
void strophe_debug_verbose(
|
||||||
|
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||||
|
|
||||||
/** jid */
|
void strophe_log_internal(const xmpp_ctx_t *ctx,
|
||||||
/* these return new strings that must be xmpp_free()'d */
|
xmpp_log_level_t level,
|
||||||
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
|
const char *area,
|
||||||
const char *domain,
|
const char *fmt,
|
||||||
const char *resource);
|
va_list ap);
|
||||||
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid);
|
|
||||||
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid);
|
|
||||||
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid);
|
|
||||||
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
|
|
||||||
|
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
#define STR_MAYBE_NULL(p) (p) ? (p) : "(null)"
|
||||||
|
#else
|
||||||
|
#define STR_MAYBE_NULL(p) (p)
|
||||||
|
#endif
|
||||||
|
|
||||||
/** connection **/
|
/** connection **/
|
||||||
|
|
||||||
@@ -105,78 +142,150 @@ typedef enum {
|
|||||||
XMPP_STATE_CONNECTED
|
XMPP_STATE_CONNECTED
|
||||||
} xmpp_conn_state_t;
|
} xmpp_conn_state_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
XMPP_QUEUE_STROPHE = 0x1,
|
||||||
|
XMPP_QUEUE_USER = 0x2,
|
||||||
|
XMPP_QUEUE_SM = 0x800,
|
||||||
|
XMPP_QUEUE_SM_STROPHE = XMPP_QUEUE_SM | XMPP_QUEUE_STROPHE,
|
||||||
|
} xmpp_send_queue_owner_t;
|
||||||
|
|
||||||
typedef struct _xmpp_send_queue_t xmpp_send_queue_t;
|
typedef struct _xmpp_send_queue_t xmpp_send_queue_t;
|
||||||
struct _xmpp_send_queue_t {
|
struct _xmpp_send_queue_t {
|
||||||
char *data;
|
char *data;
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t written;
|
size_t written;
|
||||||
|
int wip;
|
||||||
xmpp_send_queue_t *next;
|
xmpp_send_queue_owner_t owner;
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _xmpp_handlist_t xmpp_handlist_t;
|
|
||||||
struct _xmpp_handlist_t {
|
|
||||||
/* common members */
|
|
||||||
int user_handler;
|
|
||||||
void *handler;
|
|
||||||
void *userdata;
|
void *userdata;
|
||||||
int enabled; /* handlers are added disabled and enabled after the
|
uint32_t sm_h;
|
||||||
* handler chain is processed to prevent stanzas from
|
|
||||||
* getting processed by newly added handlers */
|
|
||||||
xmpp_handlist_t *next;
|
|
||||||
|
|
||||||
union {
|
xmpp_send_queue_t *prev, *next;
|
||||||
/* timed handlers */
|
|
||||||
struct {
|
|
||||||
unsigned long period;
|
|
||||||
uint64_t last_stamp;
|
|
||||||
};
|
|
||||||
/* id handlers */
|
|
||||||
struct {
|
|
||||||
char *id;
|
|
||||||
};
|
|
||||||
/* normal handlers */
|
|
||||||
struct {
|
|
||||||
char *ns;
|
|
||||||
char *name;
|
|
||||||
char *type;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SASL_MASK_PLAIN 0x01
|
#define UNUSED(x) ((void)(x))
|
||||||
#define SASL_MASK_DIGESTMD5 0x02
|
|
||||||
#define SASL_MASK_ANONYMOUS 0x04
|
|
||||||
|
|
||||||
typedef void (*xmpp_open_handler)(xmpp_conn_t * const conn);
|
#define MAX_DOMAIN_LEN 256
|
||||||
|
|
||||||
|
#define SASL_MASK_PLAIN (1 << 0)
|
||||||
|
#define SASL_MASK_DIGESTMD5 (1 << 1)
|
||||||
|
#define SASL_MASK_ANONYMOUS (1 << 2)
|
||||||
|
#define SASL_MASK_SCRAMSHA1 (1 << 3)
|
||||||
|
#define SASL_MASK_SCRAMSHA256 (1 << 4)
|
||||||
|
#define SASL_MASK_SCRAMSHA512 (1 << 5)
|
||||||
|
#define SASL_MASK_EXTERNAL (1 << 6)
|
||||||
|
#define SASL_MASK_SCRAMSHA1_PLUS (1 << 7)
|
||||||
|
#define SASL_MASK_SCRAMSHA256_PLUS (1 << 8)
|
||||||
|
#define SASL_MASK_SCRAMSHA512_PLUS (1 << 9)
|
||||||
|
|
||||||
|
#define SASL_MASK_SCRAM_PLUS \
|
||||||
|
(SASL_MASK_SCRAMSHA1_PLUS | SASL_MASK_SCRAMSHA256_PLUS | \
|
||||||
|
SASL_MASK_SCRAMSHA512_PLUS)
|
||||||
|
#define SASL_MASK_SCRAM_WEAK \
|
||||||
|
(SASL_MASK_SCRAMSHA1 | SASL_MASK_SCRAMSHA256 | SASL_MASK_SCRAMSHA512)
|
||||||
|
#define SASL_MASK_SCRAM (SASL_MASK_SCRAM_PLUS | SASL_MASK_SCRAM_WEAK)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
XMPP_PORT_CLIENT = 5222,
|
||||||
|
XMPP_PORT_CLIENT_LEGACY_SSL = 5223,
|
||||||
|
XMPP_PORT_COMPONENT = 5347,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*xmpp_open_handler)(xmpp_conn_t *conn);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
xmpp_send_queue_t *head, *tail;
|
||||||
|
} xmpp_queue_t;
|
||||||
|
|
||||||
|
struct _xmpp_sm_t {
|
||||||
|
xmpp_ctx_t *ctx;
|
||||||
|
int sm_support;
|
||||||
|
int sm_enabled;
|
||||||
|
int can_resume, resume, dont_request_resume;
|
||||||
|
xmpp_queue_t sm_queue;
|
||||||
|
int r_sent;
|
||||||
|
uint32_t sm_handled_nr;
|
||||||
|
uint32_t sm_sent_nr;
|
||||||
|
char *id, *previd, *bound_jid;
|
||||||
|
xmpp_stanza_t *bind;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct conn_interface {
|
||||||
|
int (*read)(struct conn_interface *intf, void *buff, size_t len);
|
||||||
|
int (*write)(struct conn_interface *intf, const void *buff, size_t len);
|
||||||
|
int (*flush)(struct conn_interface *intf);
|
||||||
|
int (*pending)(struct conn_interface *intf);
|
||||||
|
int (*get_error)(struct conn_interface *intf);
|
||||||
|
int (*error_is_recoverable)(struct conn_interface *intf, int err);
|
||||||
|
xmpp_conn_t *conn;
|
||||||
|
};
|
||||||
|
|
||||||
|
int conn_interface_write(struct conn_interface *intf,
|
||||||
|
const void *buff,
|
||||||
|
size_t len);
|
||||||
|
int conn_int_nop(struct conn_interface *intf);
|
||||||
|
|
||||||
|
int compression_init(xmpp_conn_t *conn);
|
||||||
|
void compression_free(xmpp_conn_t *conn);
|
||||||
|
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text);
|
||||||
|
|
||||||
struct _xmpp_conn_t {
|
struct _xmpp_conn_t {
|
||||||
|
struct conn_interface intf;
|
||||||
|
|
||||||
unsigned int ref;
|
unsigned int ref;
|
||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
xmpp_conn_type_t type;
|
xmpp_conn_type_t type;
|
||||||
|
int is_raw;
|
||||||
|
|
||||||
xmpp_conn_state_t state;
|
xmpp_conn_state_t state;
|
||||||
uint64_t timeout_stamp;
|
uint64_t timeout_stamp;
|
||||||
int error;
|
int error;
|
||||||
xmpp_stream_error_t *stream_error;
|
xmpp_stream_error_t *stream_error;
|
||||||
sock_t sock;
|
|
||||||
tls_t *tls;
|
|
||||||
|
|
||||||
|
xmpp_sock_t *xsock;
|
||||||
|
sock_t sock;
|
||||||
|
int ka_timeout; /* TCP keepalive timeout */
|
||||||
|
int ka_interval; /* TCP keepalive interval */
|
||||||
|
int ka_count; /* TCP keepalive count */
|
||||||
|
|
||||||
|
tls_t *tls;
|
||||||
int tls_support;
|
int tls_support;
|
||||||
int tls_disabled;
|
int tls_disabled;
|
||||||
int tls_failed; /* set when tls fails, so we don't try again */
|
int tls_mandatory;
|
||||||
|
int tls_legacy_ssl;
|
||||||
|
int tls_trust;
|
||||||
|
char *tls_cafile;
|
||||||
|
char *tls_capath;
|
||||||
|
char *tls_client_cert;
|
||||||
|
char *tls_client_key;
|
||||||
|
int tls_failed; /* set when tls fails, so we don't try again */
|
||||||
int sasl_support; /* if true, field is a bitfield of supported
|
int sasl_support; /* if true, field is a bitfield of supported
|
||||||
mechanisms */
|
mechanisms */
|
||||||
|
int auth_legacy_enabled;
|
||||||
int secured; /* set when stream is secured with TLS */
|
int secured; /* set when stream is secured with TLS */
|
||||||
|
xmpp_certfail_handler certfail_handler;
|
||||||
|
xmpp_password_callback password_callback;
|
||||||
|
void *password_callback_userdata;
|
||||||
|
struct {
|
||||||
|
char pass[1024];
|
||||||
|
unsigned char fname_hash[XMPP_SHA1_DIGEST_SIZE];
|
||||||
|
size_t passlen, fnamelen;
|
||||||
|
} password_cache;
|
||||||
|
unsigned int password_retries;
|
||||||
|
|
||||||
/* if server returns <bind/> or <session/> we must do them */
|
/* if server returns <bind/> or <session/> we must do them */
|
||||||
int bind_required;
|
int bind_required;
|
||||||
int session_required;
|
int session_required;
|
||||||
|
int sm_disable;
|
||||||
|
xmpp_sm_state_t *sm_state;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct xmpp_compression *state;
|
||||||
|
int allowed, dont_reset, level;
|
||||||
|
int supported;
|
||||||
|
} compression;
|
||||||
|
|
||||||
char *lang;
|
char *lang;
|
||||||
char *domain;
|
char *domain;
|
||||||
char *connectdomain;
|
|
||||||
char *connectport;
|
|
||||||
char *jid;
|
char *jid;
|
||||||
char *pass;
|
char *pass;
|
||||||
char *bound_jid;
|
char *bound_jid;
|
||||||
@@ -186,6 +295,7 @@ struct _xmpp_conn_t {
|
|||||||
int blocking_send;
|
int blocking_send;
|
||||||
int send_queue_max;
|
int send_queue_max;
|
||||||
int send_queue_len;
|
int send_queue_len;
|
||||||
|
int send_queue_user_len;
|
||||||
xmpp_send_queue_t *send_queue_head;
|
xmpp_send_queue_t *send_queue_head;
|
||||||
xmpp_send_queue_t *send_queue_tail;
|
xmpp_send_queue_t *send_queue_tail;
|
||||||
|
|
||||||
@@ -201,8 +311,9 @@ struct _xmpp_conn_t {
|
|||||||
/* stream open handler */
|
/* stream open handler */
|
||||||
xmpp_open_handler open_handler;
|
xmpp_open_handler open_handler;
|
||||||
|
|
||||||
/* user handlers only get called after authentication */
|
/* user handlers only get called after the stream negotiation has completed
|
||||||
int authenticated;
|
*/
|
||||||
|
int stream_negotiation_completed;
|
||||||
|
|
||||||
/* connection events handler */
|
/* connection events handler */
|
||||||
xmpp_conn_handler conn_handler;
|
xmpp_conn_handler conn_handler;
|
||||||
@@ -212,14 +323,16 @@ struct _xmpp_conn_t {
|
|||||||
xmpp_handlist_t *timed_handlers;
|
xmpp_handlist_t *timed_handlers;
|
||||||
hash_t *id_handlers;
|
hash_t *id_handlers;
|
||||||
xmpp_handlist_t *handlers;
|
xmpp_handlist_t *handlers;
|
||||||
|
xmpp_sockopt_callback sockopt_cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
void conn_disconnect(xmpp_conn_t * const conn);
|
void conn_disconnect(xmpp_conn_t *conn);
|
||||||
void conn_disconnect_clean(xmpp_conn_t * const conn);
|
void conn_disconnect_clean(xmpp_conn_t *conn);
|
||||||
void conn_open_stream(xmpp_conn_t * const conn);
|
void conn_established(xmpp_conn_t *conn);
|
||||||
void conn_prepare_reset(xmpp_conn_t * const conn, xmpp_open_handler handler);
|
void conn_open_stream(xmpp_conn_t *conn);
|
||||||
void conn_parser_reset(xmpp_conn_t * const conn);
|
int conn_tls_start(xmpp_conn_t *conn);
|
||||||
|
void conn_prepare_reset(xmpp_conn_t *conn, xmpp_open_handler handler);
|
||||||
|
void conn_parser_reset(xmpp_conn_t *conn);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
XMPP_STANZA_UNKNOWN,
|
XMPP_STANZA_UNKNOWN,
|
||||||
@@ -244,33 +357,53 @@ struct _xmpp_stanza_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* handler management */
|
/* handler management */
|
||||||
void handler_fire_stanza(xmpp_conn_t * const conn,
|
void handler_fire_stanza(xmpp_conn_t *conn, xmpp_stanza_t *stanza);
|
||||||
xmpp_stanza_t * const stanza);
|
uint64_t handler_fire_timed(xmpp_ctx_t *ctx);
|
||||||
uint64_t handler_fire_timed(xmpp_ctx_t * const ctx);
|
|
||||||
void handler_reset_timed(xmpp_conn_t *conn, int user_only);
|
void handler_reset_timed(xmpp_conn_t *conn, int user_only);
|
||||||
void handler_add_timed(xmpp_conn_t * const conn,
|
void handler_add_timed(xmpp_conn_t *conn,
|
||||||
xmpp_timed_handler handler,
|
xmpp_timed_handler handler,
|
||||||
const unsigned long period,
|
unsigned long period,
|
||||||
void * const userdata);
|
void *userdata);
|
||||||
void handler_add_id(xmpp_conn_t * const conn,
|
void handler_add_id(xmpp_conn_t *conn,
|
||||||
xmpp_handler handler,
|
xmpp_handler handler,
|
||||||
const char * const id,
|
const char *id,
|
||||||
void * const userdata);
|
void *userdata);
|
||||||
void handler_add(xmpp_conn_t * const conn,
|
void handler_add(xmpp_conn_t *conn,
|
||||||
xmpp_handler handler,
|
xmpp_handler handler,
|
||||||
const char * const ns,
|
const char *ns,
|
||||||
const char * const name,
|
const char *name,
|
||||||
const char * const type,
|
const char *type,
|
||||||
void * const userdata);
|
void *userdata);
|
||||||
|
void handler_system_delete_all(xmpp_conn_t *conn);
|
||||||
|
|
||||||
/* utility functions */
|
/* utility functions */
|
||||||
void disconnect_mem_error(xmpp_conn_t * const conn);
|
void reset_sm_state(xmpp_sm_state_t *sm_state);
|
||||||
|
void disconnect_mem_error(xmpp_conn_t *conn);
|
||||||
|
|
||||||
/* auth functions */
|
/* auth functions */
|
||||||
void auth_handle_open(xmpp_conn_t * const conn);
|
void auth_handle_open(xmpp_conn_t *conn);
|
||||||
|
void auth_handle_component_open(xmpp_conn_t *conn);
|
||||||
|
void auth_handle_open_raw(xmpp_conn_t *conn);
|
||||||
|
void auth_handle_open_stub(xmpp_conn_t *conn);
|
||||||
|
|
||||||
/* replacement snprintf and vsnprintf */
|
/* queue functions */
|
||||||
int xmpp_snprintf (char *str, size_t count, const char *fmt, ...);
|
void add_queue_back(xmpp_queue_t *queue, xmpp_send_queue_t *item);
|
||||||
int xmpp_vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
|
xmpp_send_queue_t *peek_queue_front(xmpp_queue_t *queue);
|
||||||
|
xmpp_send_queue_t *pop_queue_front(xmpp_queue_t *queue);
|
||||||
|
char *queue_element_free(xmpp_ctx_t *ctx, xmpp_send_queue_t *e);
|
||||||
|
|
||||||
|
/* send functions */
|
||||||
|
void send_raw(xmpp_conn_t *conn,
|
||||||
|
const char *data,
|
||||||
|
size_t len,
|
||||||
|
xmpp_send_queue_owner_t owner,
|
||||||
|
void *userdata);
|
||||||
|
/* this is a bit special as it will always mark the sent string as
|
||||||
|
* owned by libstrophe
|
||||||
|
*/
|
||||||
|
void send_raw_string(xmpp_conn_t *conn, const char *fmt, ...);
|
||||||
|
void send_stanza(xmpp_conn_t *conn,
|
||||||
|
xmpp_stanza_t *stanza,
|
||||||
|
xmpp_send_queue_owner_t owner);
|
||||||
|
|
||||||
#endif /* __LIBSTROPHE_COMMON_H__ */
|
#endif /* __LIBSTROPHE_COMMON_H__ */
|
||||||
|
|||||||
270
src/compression.c
Normal file
270
src/compression.c
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* compression.c
|
||||||
|
** strophe XMPP client library -- XEP-0138 Stream Compression
|
||||||
|
**
|
||||||
|
** Copyright (C) 2024 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
|
||||||
|
**
|
||||||
|
** This software is provided AS-IS with no warranty, either express
|
||||||
|
** or implied.
|
||||||
|
**
|
||||||
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* XEP-0138 Stream Compression.
|
||||||
|
*/
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#ifndef STROPHE_COMPRESSION_BUFFER_SIZE
|
||||||
|
/** Max buffer size for compressed data (send & receive). */
|
||||||
|
#define STROPHE_COMPRESSION_BUFFER_SIZE 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct zlib_compression {
|
||||||
|
void *buffer, *buffer_end;
|
||||||
|
z_stream stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xmpp_compression {
|
||||||
|
xmpp_conn_t *conn;
|
||||||
|
struct zlib_compression compression, decompression;
|
||||||
|
struct conn_interface next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int _conn_decompress(struct xmpp_compression *comp,
|
||||||
|
size_t c_len,
|
||||||
|
void *buff,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
if (comp->decompression.stream.next_in == NULL) {
|
||||||
|
comp->decompression.stream.next_in = comp->decompression.buffer;
|
||||||
|
comp->decompression.buffer_end =
|
||||||
|
comp->decompression.stream.next_in + c_len;
|
||||||
|
comp->decompression.stream.avail_in = c_len;
|
||||||
|
} else if (c_len) {
|
||||||
|
strophe_error(comp->conn->ctx, "zlib",
|
||||||
|
"_conn_decompress() called with c_len=%zu", c_len);
|
||||||
|
}
|
||||||
|
comp->decompression.stream.next_out = buff;
|
||||||
|
comp->decompression.stream.avail_out = len;
|
||||||
|
int ret = inflate(&comp->decompression.stream, Z_SYNC_FLUSH);
|
||||||
|
switch (ret) {
|
||||||
|
case Z_STREAM_END:
|
||||||
|
case Z_OK:
|
||||||
|
if (comp->decompression.buffer_end ==
|
||||||
|
comp->decompression.stream.next_in)
|
||||||
|
comp->decompression.stream.next_in = NULL;
|
||||||
|
return comp->decompression.stream.next_out - (Bytef *)buff;
|
||||||
|
case Z_BUF_ERROR:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strophe_error(comp->conn->ctx, "zlib", "inflate error %d", ret);
|
||||||
|
comp->conn->error = ret;
|
||||||
|
conn_disconnect(comp->conn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compression_read(struct conn_interface *intf, void *buff, size_t len)
|
||||||
|
{
|
||||||
|
xmpp_conn_t *conn = intf->conn;
|
||||||
|
struct xmpp_compression *comp = conn->compression.state;
|
||||||
|
void *dbuff = buff;
|
||||||
|
size_t dlen = len;
|
||||||
|
if (comp->decompression.stream.next_in != NULL) {
|
||||||
|
return _conn_decompress(comp, 0, buff, len);
|
||||||
|
}
|
||||||
|
dbuff = comp->decompression.buffer;
|
||||||
|
dlen = STROPHE_COMPRESSION_BUFFER_SIZE;
|
||||||
|
int ret = comp->next.read(intf, dbuff, dlen);
|
||||||
|
if (ret > 0) {
|
||||||
|
return _conn_decompress(comp, ret, buff, len);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _try_compressed_write_to_network(xmpp_conn_t *conn, int force)
|
||||||
|
{
|
||||||
|
struct xmpp_compression *comp = conn->compression.state;
|
||||||
|
int ret = 0;
|
||||||
|
ptrdiff_t len =
|
||||||
|
comp->compression.stream.next_out - (Bytef *)comp->compression.buffer;
|
||||||
|
int buffer_full =
|
||||||
|
comp->compression.stream.next_out == comp->compression.buffer_end;
|
||||||
|
if ((buffer_full || force) && len > 0) {
|
||||||
|
ret = conn_interface_write(&comp->next, comp->compression.buffer, len);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
comp->compression.stream.next_out = comp->compression.buffer;
|
||||||
|
comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_compression_write(xmpp_conn_t *conn, const void *buff, size_t len, int flush)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const void *buff_end = buff + len;
|
||||||
|
struct xmpp_compression *comp = conn->compression.state;
|
||||||
|
comp->compression.stream.next_in = (Bytef *)buff;
|
||||||
|
comp->compression.stream.avail_in = len;
|
||||||
|
do {
|
||||||
|
ret = _try_compressed_write_to_network(conn, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = deflate(&comp->compression.stream, flush);
|
||||||
|
if (ret == Z_STREAM_END) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (flush && ret == Z_BUF_ERROR) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret != Z_OK) {
|
||||||
|
strophe_error(conn->ctx, "zlib", "deflate error %d", ret);
|
||||||
|
conn->error = ret;
|
||||||
|
conn_disconnect(conn);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = comp->compression.stream.next_in - (Bytef *)buff;
|
||||||
|
} while (comp->compression.stream.next_in < (Bytef *)buff_end);
|
||||||
|
if (flush) {
|
||||||
|
ret = _try_compressed_write_to_network(conn, 1);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
compression_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||||
|
{
|
||||||
|
return _compression_write(intf->conn, buff, len, Z_NO_FLUSH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compression_flush(struct conn_interface *intf)
|
||||||
|
{
|
||||||
|
xmpp_conn_t *conn = intf->conn;
|
||||||
|
struct xmpp_compression *comp = conn->compression.state;
|
||||||
|
return _compression_write(conn, comp->compression.buffer, 0,
|
||||||
|
conn->compression.dont_reset ? Z_SYNC_FLUSH
|
||||||
|
: Z_FULL_FLUSH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compression_pending(struct conn_interface *intf)
|
||||||
|
{
|
||||||
|
xmpp_conn_t *conn = intf->conn;
|
||||||
|
struct xmpp_compression *comp = conn->compression.state;
|
||||||
|
return comp->decompression.stream.next_in != NULL ||
|
||||||
|
comp->next.pending(intf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compression_get_error(struct conn_interface *intf)
|
||||||
|
{
|
||||||
|
struct conn_interface *next = &intf->conn->compression.state->next;
|
||||||
|
return next->get_error(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compression_is_recoverable(struct conn_interface *intf, int err)
|
||||||
|
{
|
||||||
|
struct conn_interface *next = &intf->conn->compression.state->next;
|
||||||
|
return next->error_is_recoverable(next, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct conn_interface compression_intf = {
|
||||||
|
compression_read,
|
||||||
|
compression_write,
|
||||||
|
compression_flush,
|
||||||
|
compression_pending,
|
||||||
|
compression_get_error,
|
||||||
|
compression_is_recoverable,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *_zlib_alloc(void *opaque, unsigned int items, unsigned int size)
|
||||||
|
{
|
||||||
|
size_t sz = items * size;
|
||||||
|
/* Poor man's multiplication overflow check */
|
||||||
|
if (sz < items || sz < size)
|
||||||
|
return NULL;
|
||||||
|
return strophe_alloc(opaque, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _init_zlib_compression(xmpp_ctx_t *ctx, struct zlib_compression *s)
|
||||||
|
{
|
||||||
|
s->buffer = strophe_alloc(ctx, STROPHE_COMPRESSION_BUFFER_SIZE);
|
||||||
|
s->buffer_end = s->buffer + STROPHE_COMPRESSION_BUFFER_SIZE;
|
||||||
|
|
||||||
|
s->stream.opaque = ctx;
|
||||||
|
s->stream.zalloc = _zlib_alloc;
|
||||||
|
s->stream.zfree = (free_func)strophe_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compression_init(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
if (!conn->compression.allowed || !conn->compression.supported)
|
||||||
|
return -1;
|
||||||
|
conn->compression.state =
|
||||||
|
strophe_alloc(conn->ctx, sizeof(*conn->compression.state));
|
||||||
|
struct xmpp_compression *comp = conn->compression.state;
|
||||||
|
memset(comp, 0, sizeof(*comp));
|
||||||
|
|
||||||
|
comp->conn = conn;
|
||||||
|
|
||||||
|
comp->next = conn->intf;
|
||||||
|
conn->intf = compression_intf;
|
||||||
|
conn->intf.conn = conn;
|
||||||
|
|
||||||
|
_init_zlib_compression(conn->ctx, &comp->compression);
|
||||||
|
|
||||||
|
comp->compression.stream.next_out = comp->compression.buffer;
|
||||||
|
comp->compression.stream.avail_out = STROPHE_COMPRESSION_BUFFER_SIZE;
|
||||||
|
int ret = deflateInit(&comp->compression.stream, conn->compression.level);
|
||||||
|
if (ret != Z_OK) {
|
||||||
|
strophe_free_and_null(conn->ctx, comp->compression.buffer);
|
||||||
|
conn->error = ret;
|
||||||
|
conn_disconnect(conn);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
_init_zlib_compression(conn->ctx, &comp->decompression);
|
||||||
|
|
||||||
|
ret = inflateInit(&comp->decompression.stream);
|
||||||
|
if (ret != Z_OK) {
|
||||||
|
strophe_free_and_null(conn->ctx, comp->decompression.buffer);
|
||||||
|
conn->error = ret;
|
||||||
|
conn_disconnect(conn);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void compression_free(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
struct xmpp_compression *comp = conn->compression.state;
|
||||||
|
if (!comp)
|
||||||
|
return;
|
||||||
|
if (comp->compression.buffer) {
|
||||||
|
deflateEnd(&comp->compression.stream);
|
||||||
|
strophe_free_and_null(conn->ctx, comp->compression.buffer);
|
||||||
|
}
|
||||||
|
if (comp->decompression.buffer) {
|
||||||
|
inflateEnd(&comp->decompression.stream);
|
||||||
|
strophe_free_and_null(conn->ctx, comp->decompression.buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text)
|
||||||
|
{
|
||||||
|
if (strcasecmp(text, "zlib") == 0) {
|
||||||
|
conn->compression.supported = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/compression_dummy.c
Normal file
36
src/compression_dummy.c
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* compression_dummy.c
|
||||||
|
** strophe XMPP client library -- Dummy Compression
|
||||||
|
**
|
||||||
|
** Copyright (C) 2024 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
|
||||||
|
**
|
||||||
|
** This software is provided AS-IS with no warranty, either express
|
||||||
|
** or implied.
|
||||||
|
**
|
||||||
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* Dummy Compression.
|
||||||
|
*/
|
||||||
|
#include <strings.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
int compression_init(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
conn->compression.supported = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void compression_free(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
UNUSED(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compression_handle_feature_children(xmpp_conn_t *conn, const char *text)
|
||||||
|
{
|
||||||
|
UNUSED(text);
|
||||||
|
conn->compression.supported = 0;
|
||||||
|
}
|
||||||
2104
src/conn.c
2104
src/conn.c
File diff suppressed because it is too large
Load Diff
509
src/crypto.c
Normal file
509
src/crypto.c
Normal file
@@ -0,0 +1,509 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* crypto.c
|
||||||
|
* strophe XMPP client library -- public interface for digests, encodings
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* Public interface for digests and encodings used in XEPs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup Digests Message digests
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup Encodings Encodings
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h> /* memset, memcpy */
|
||||||
|
|
||||||
|
#include "common.h" /* strophe_alloc */
|
||||||
|
#include "ostypes.h" /* uint8_t, size_t */
|
||||||
|
#include "sha1.h"
|
||||||
|
#include "snprintf.h" /* xmpp_snprintf */
|
||||||
|
#include "strophe.h" /* xmpp_ctx_t, strophe_free */
|
||||||
|
|
||||||
|
struct _xmpp_sha1_t {
|
||||||
|
xmpp_ctx_t *xmpp_ctx;
|
||||||
|
SHA1_CTX ctx;
|
||||||
|
uint8_t digest[SHA1_DIGEST_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *digest_to_string(const uint8_t *digest, char *s, size_t len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (len < SHA1_DIGEST_SIZE * 2 + 1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
|
||||||
|
strophe_snprintf(s + i * 2, 3, "%02x", digest[i]);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *digest_to_string_alloc(xmpp_ctx_t *ctx, const uint8_t *digest)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
size_t slen;
|
||||||
|
|
||||||
|
slen = SHA1_DIGEST_SIZE * 2 + 1;
|
||||||
|
s = strophe_alloc(ctx, slen);
|
||||||
|
if (s) {
|
||||||
|
s = digest_to_string(digest, s, slen);
|
||||||
|
assert(s != NULL);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Compute SHA1 message digest.
|
||||||
|
* Returns an allocated string which represents SHA1 message digest in
|
||||||
|
* hexadecimal notation. The string must be freed with xmpp_free().
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param data buffer for digest computation
|
||||||
|
* @param len size of the data buffer
|
||||||
|
*
|
||||||
|
* @return an allocated string or NULL on allocation error
|
||||||
|
*
|
||||||
|
* @ingroup Digests
|
||||||
|
*/
|
||||||
|
char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t digest[SHA1_DIGEST_SIZE];
|
||||||
|
|
||||||
|
crypto_SHA1((const uint8_t *)data, len, digest);
|
||||||
|
return digest_to_string_alloc(ctx, digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Compute SHA1 message digest.
|
||||||
|
* Stores digest in user's buffer which must be at least XMPP_SHA1_DIGEST_SIZE
|
||||||
|
* bytes long.
|
||||||
|
*
|
||||||
|
* @param data buffer for digest computation
|
||||||
|
* @param len size of the data buffer
|
||||||
|
* @param digest output buffer of XMPP_SHA1_DIGEST_SIZE bytes
|
||||||
|
*
|
||||||
|
* @ingroup Digests
|
||||||
|
*/
|
||||||
|
void xmpp_sha1_digest(const unsigned char *data,
|
||||||
|
size_t len,
|
||||||
|
unsigned char *digest)
|
||||||
|
{
|
||||||
|
crypto_SHA1((const uint8_t *)data, len, digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create new SHA1 object.
|
||||||
|
* SHA1 object is used to compute SHA1 digest of a buffer that is split
|
||||||
|
* in multiple chunks or provided in stream mode. A single buffer can be
|
||||||
|
* processed by short functions xmpp_sha1() and xmpp_sha1_digest().
|
||||||
|
* Follow the next use-case for xmpp_sha1_t object:
|
||||||
|
* @code
|
||||||
|
* xmpp_sha1_t *sha1 = xmpp_sha1_new(ctx);
|
||||||
|
* // Repeat update for all chunks of data
|
||||||
|
* xmpp_sha1_update(sha1, data, len);
|
||||||
|
* xmpp_sha1_final(sha1);
|
||||||
|
* char *digest = xmpp_sha1_to_string_alloc(sha1);
|
||||||
|
* xmpp_sha1_free(sha1);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
*
|
||||||
|
* @return new SHA1 object
|
||||||
|
*
|
||||||
|
* @ingroup Digests
|
||||||
|
*/
|
||||||
|
xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
xmpp_sha1_t *sha1;
|
||||||
|
|
||||||
|
sha1 = strophe_alloc(ctx, sizeof(*sha1));
|
||||||
|
if (sha1) {
|
||||||
|
memset(sha1, 0, sizeof(*sha1));
|
||||||
|
crypto_SHA1_Init(&sha1->ctx);
|
||||||
|
sha1->xmpp_ctx = ctx;
|
||||||
|
}
|
||||||
|
return sha1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Destroy SHA1 object.
|
||||||
|
*
|
||||||
|
* @param sha1 a SHA1 object
|
||||||
|
*
|
||||||
|
* @ingroup Digests
|
||||||
|
*/
|
||||||
|
void xmpp_sha1_free(xmpp_sha1_t *sha1)
|
||||||
|
{
|
||||||
|
strophe_free(sha1->xmpp_ctx, sha1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Update SHA1 context with the next portion of data.
|
||||||
|
* Can be called repeatedly.
|
||||||
|
*
|
||||||
|
* @param sha1 a SHA1 object
|
||||||
|
* @param data pointer to a buffer to be hashed
|
||||||
|
* @param len size of the data buffer
|
||||||
|
*
|
||||||
|
* @ingroup Digests
|
||||||
|
*/
|
||||||
|
void xmpp_sha1_update(xmpp_sha1_t *sha1, const unsigned char *data, size_t len)
|
||||||
|
{
|
||||||
|
crypto_SHA1_Update(&sha1->ctx, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finish SHA1 computation.
|
||||||
|
* Don't call xmpp_sha1_update() after this function. Retrieve resulting
|
||||||
|
* message digest with xmpp_sha1_to_string() or xmpp_sha1_to_digest().
|
||||||
|
*
|
||||||
|
* @param sha1 a SHA1 object
|
||||||
|
*
|
||||||
|
* @ingroup Digests
|
||||||
|
*/
|
||||||
|
void xmpp_sha1_final(xmpp_sha1_t *sha1)
|
||||||
|
{
|
||||||
|
crypto_SHA1_Final(&sha1->ctx, sha1->digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return message digest rendered as a string.
|
||||||
|
* Stores the string to a user's buffer and returns the buffer. Call this
|
||||||
|
* function after xmpp_sha1_final().
|
||||||
|
*
|
||||||
|
* @param sha1 a SHA1 object
|
||||||
|
* @param s output string
|
||||||
|
* @param slen size reserved for the string including '\0'
|
||||||
|
*
|
||||||
|
* @return pointer s or NULL if resulting string is bigger than slen bytes
|
||||||
|
*
|
||||||
|
* @ingroup Digests
|
||||||
|
*/
|
||||||
|
char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen)
|
||||||
|
{
|
||||||
|
return digest_to_string(sha1->digest, s, slen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return message digest rendered as a string.
|
||||||
|
* Returns an allocated string. Free the string by calling xmpp_free() using
|
||||||
|
* the Strophe context which is passed to xmpp_sha1_new(). Call this function
|
||||||
|
* after xmpp_sha1_final().
|
||||||
|
*
|
||||||
|
* @param sha1 a SHA1 object
|
||||||
|
*
|
||||||
|
* @return an allocated string
|
||||||
|
*
|
||||||
|
* @ingroup Digests
|
||||||
|
*/
|
||||||
|
char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1)
|
||||||
|
{
|
||||||
|
return digest_to_string_alloc(sha1->xmpp_ctx, sha1->digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stores message digest to a user's buffer.
|
||||||
|
*
|
||||||
|
* @param sha1 a SHA1 object
|
||||||
|
* @param digest output buffer of XMPP_SHA1_DIGEST_SIZE bytes
|
||||||
|
*
|
||||||
|
* @ingroup Digests
|
||||||
|
*/
|
||||||
|
void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest)
|
||||||
|
{
|
||||||
|
assert(SHA1_DIGEST_SIZE == XMPP_SHA1_DIGEST_SIZE);
|
||||||
|
memcpy(digest, sha1->digest, SHA1_DIGEST_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base64 encoding routines. Implemented according to RFC 3548. */
|
||||||
|
|
||||||
|
/* map of all byte values to the base64 values, or to
|
||||||
|
'65' which indicates an invalid character. '=' is '64' */
|
||||||
|
static const unsigned char _base64_invcharmap[256] = {
|
||||||
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
|
65, 65, 65, 65, 65, 62, 65, 65, 65, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
|
||||||
|
61, 65, 65, 65, 64, 65, 65, 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||||
|
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 65, 65, 65, 65,
|
||||||
|
65, 65, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
|
||||||
|
43, 44, 45, 46, 47, 48, 49, 50, 51, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
|
65, 65, 65, 65, 65, 65, 65, 65, 65};
|
||||||
|
|
||||||
|
/* map of all 6-bit values to their corresponding byte
|
||||||
|
in the base64 alphabet. Padding char is the value '64' */
|
||||||
|
static const char _base64_charmap[65] = {
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||||
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
||||||
|
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '='};
|
||||||
|
|
||||||
|
static size_t base64_encoded_len(size_t len)
|
||||||
|
{
|
||||||
|
/* encoded steam is 4 bytes for every three, rounded up */
|
||||||
|
return ((len + 2) / 3) << 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
base64_encode(xmpp_ctx_t *ctx, const unsigned char *buffer, size_t len)
|
||||||
|
{
|
||||||
|
size_t clen;
|
||||||
|
char *cbuf, *c;
|
||||||
|
uint32_t word, hextet;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
clen = base64_encoded_len(len);
|
||||||
|
cbuf = strophe_alloc(ctx, clen + 1);
|
||||||
|
if (cbuf != NULL) {
|
||||||
|
c = cbuf;
|
||||||
|
/* loop over data, turning every 3 bytes into 4 characters */
|
||||||
|
for (i = 0; i + 2 < len; i += 3) {
|
||||||
|
word = buffer[i] << 16 | buffer[i + 1] << 8 | buffer[i + 2];
|
||||||
|
hextet = (word & 0x00FC0000) >> 18;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (word & 0x0003F000) >> 12;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (word & 0x00000FC0) >> 6;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (word & 0x000003F);
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
}
|
||||||
|
/* zero, one or two bytes left */
|
||||||
|
switch (len - i) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
hextet = (buffer[len - 1] & 0xFC) >> 2;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (buffer[len - 1] & 0x03) << 4;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
*c++ = _base64_charmap[64]; /* pad */
|
||||||
|
*c++ = _base64_charmap[64]; /* pad */
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
hextet = (buffer[len - 2] & 0xFC) >> 2;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = ((buffer[len - 2] & 0x03) << 4) |
|
||||||
|
((buffer[len - 1] & 0xF0) >> 4);
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
hextet = (buffer[len - 1] & 0x0F) << 2;
|
||||||
|
*c++ = _base64_charmap[hextet];
|
||||||
|
*c++ = _base64_charmap[64]; /* pad */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* add a terminal null */
|
||||||
|
*c = '\0';
|
||||||
|
}
|
||||||
|
return cbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t base64_decoded_len(const char *buffer, size_t len)
|
||||||
|
{
|
||||||
|
size_t nudge = 0;
|
||||||
|
unsigned char c;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (len < 4)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* count the padding characters for the remainder */
|
||||||
|
for (i = len; i > 0; --i) {
|
||||||
|
c = _base64_invcharmap[(unsigned char)buffer[i - 1]];
|
||||||
|
if (c < 64)
|
||||||
|
break;
|
||||||
|
if (c == 64)
|
||||||
|
++nudge;
|
||||||
|
if (c > 64)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (nudge > 2)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* decoded steam is 3 bytes for every four */
|
||||||
|
return 3 * (len >> 2) - nudge;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void base64_decode(xmpp_ctx_t *ctx,
|
||||||
|
const char *buffer,
|
||||||
|
size_t len,
|
||||||
|
unsigned char **out,
|
||||||
|
size_t *outlen)
|
||||||
|
{
|
||||||
|
size_t dlen;
|
||||||
|
unsigned char *dbuf, *d;
|
||||||
|
uint32_t word, hextet = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* len must be a multiple of 4 */
|
||||||
|
if (len & 0x03)
|
||||||
|
goto _base64_error;
|
||||||
|
|
||||||
|
dlen = base64_decoded_len(buffer, len);
|
||||||
|
if (dlen == 0)
|
||||||
|
goto _base64_error;
|
||||||
|
|
||||||
|
dbuf = strophe_alloc(ctx, dlen + 1);
|
||||||
|
if (dbuf != NULL) {
|
||||||
|
d = dbuf;
|
||||||
|
/* loop over each set of 4 characters, decoding 3 bytes */
|
||||||
|
for (i = 0; i + 3 < len; i += 4) {
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[i]];
|
||||||
|
if (hextet & 0xC0)
|
||||||
|
break;
|
||||||
|
word = hextet << 18;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[i + 1]];
|
||||||
|
if (hextet & 0xC0)
|
||||||
|
break;
|
||||||
|
word |= hextet << 12;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[i + 2]];
|
||||||
|
if (hextet & 0xC0)
|
||||||
|
break;
|
||||||
|
word |= hextet << 6;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[i + 3]];
|
||||||
|
if (hextet & 0xC0)
|
||||||
|
break;
|
||||||
|
word |= hextet;
|
||||||
|
*d++ = (word & 0x00FF0000) >> 16;
|
||||||
|
*d++ = (word & 0x0000FF00) >> 8;
|
||||||
|
*d++ = (word & 0x000000FF);
|
||||||
|
}
|
||||||
|
if (hextet > 64)
|
||||||
|
goto _base64_decode_error;
|
||||||
|
/* handle the remainder */
|
||||||
|
switch (dlen % 3) {
|
||||||
|
case 0:
|
||||||
|
/* nothing to do */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* redo the last quartet, checking for correctness */
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len - 4]];
|
||||||
|
if (hextet & 0xC0)
|
||||||
|
goto _base64_decode_error;
|
||||||
|
word = hextet << 2;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len - 3]];
|
||||||
|
if (hextet & 0xC0)
|
||||||
|
goto _base64_decode_error;
|
||||||
|
word |= hextet >> 4;
|
||||||
|
*d++ = word & 0xFF;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len - 2]];
|
||||||
|
if (hextet != 64)
|
||||||
|
goto _base64_decode_error;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len - 1]];
|
||||||
|
if (hextet != 64)
|
||||||
|
goto _base64_decode_error;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* redo the last quartet, checking for correctness */
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len - 4]];
|
||||||
|
if (hextet & 0xC0)
|
||||||
|
goto _base64_decode_error;
|
||||||
|
word = hextet << 10;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len - 3]];
|
||||||
|
if (hextet & 0xC0)
|
||||||
|
goto _base64_decode_error;
|
||||||
|
word |= hextet << 4;
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len - 2]];
|
||||||
|
if (hextet & 0xC0)
|
||||||
|
goto _base64_decode_error;
|
||||||
|
word |= hextet >> 2;
|
||||||
|
*d++ = (word & 0xFF00) >> 8;
|
||||||
|
*d++ = (word & 0x00FF);
|
||||||
|
hextet = _base64_invcharmap[(unsigned char)buffer[len - 1]];
|
||||||
|
if (hextet != 64)
|
||||||
|
goto _base64_decode_error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*d = '\0';
|
||||||
|
}
|
||||||
|
*out = dbuf;
|
||||||
|
*outlen = dbuf == NULL ? 0 : dlen;
|
||||||
|
return;
|
||||||
|
|
||||||
|
_base64_decode_error:
|
||||||
|
/* invalid character; abort decoding! */
|
||||||
|
strophe_free(ctx, dbuf);
|
||||||
|
_base64_error:
|
||||||
|
*out = NULL;
|
||||||
|
*outlen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base64 encoding routine.
|
||||||
|
* Returns an allocated string which must be freed with xmpp_free().
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context
|
||||||
|
* @param data buffer to encode
|
||||||
|
* @param len size of the data buffer
|
||||||
|
*
|
||||||
|
* @return an allocated null-terminated string or NULL on error
|
||||||
|
*
|
||||||
|
* @ingroup Encodings
|
||||||
|
*/
|
||||||
|
char *xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len)
|
||||||
|
{
|
||||||
|
return base64_encode(ctx, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base64 decoding routine.
|
||||||
|
* Returns an allocated string which must be freed with xmpp_free(). User
|
||||||
|
* calls this function when the result must be a string. When decoded buffer
|
||||||
|
* contains '\0' NULL is returned.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context
|
||||||
|
* @param base64 encoded buffer
|
||||||
|
* @param len size of the buffer
|
||||||
|
*
|
||||||
|
* @return an allocated null-terminated string or NULL on error
|
||||||
|
*
|
||||||
|
* @ingroup Encodings
|
||||||
|
*/
|
||||||
|
char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len)
|
||||||
|
{
|
||||||
|
unsigned char *buf = NULL;
|
||||||
|
size_t buflen;
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
/* handle empty string */
|
||||||
|
buf = strophe_alloc(ctx, 1);
|
||||||
|
if (buf)
|
||||||
|
buf[0] = '\0';
|
||||||
|
buflen = 0;
|
||||||
|
} else {
|
||||||
|
base64_decode(ctx, base64, len, &buf, &buflen);
|
||||||
|
}
|
||||||
|
if (buf) {
|
||||||
|
if (buflen != strlen((char *)buf)) {
|
||||||
|
strophe_free(ctx, buf);
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (char *)buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base64 decoding routine.
|
||||||
|
* Returns an allocated buffer which must be freed with xmpp_free().
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context
|
||||||
|
* @param base64 encoded buffer
|
||||||
|
* @param len size of the encoded buffer
|
||||||
|
* @param out allocated buffer is stored here
|
||||||
|
* @param outlen size of the allocated buffer
|
||||||
|
*
|
||||||
|
* @note on an error the `*out` will be NULL
|
||||||
|
*
|
||||||
|
* @ingroup Encodings
|
||||||
|
*/
|
||||||
|
void xmpp_base64_decode_bin(xmpp_ctx_t *ctx,
|
||||||
|
const char *base64,
|
||||||
|
size_t len,
|
||||||
|
unsigned char **out,
|
||||||
|
size_t *outlen)
|
||||||
|
{
|
||||||
|
base64_decode(ctx, base64, len, out, outlen);
|
||||||
|
}
|
||||||
300
src/ctx.c
300
src/ctx.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* ctx.c
|
/* ctx.c
|
||||||
** strophe XMPP client library -- run-time context implementation
|
** strophe XMPP client library -- run-time context implementation
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -50,17 +48,37 @@
|
|||||||
|
|
||||||
#include "strophe.h"
|
#include "strophe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "resolver.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifndef va_copy
|
||||||
|
#ifdef HAVE_VA_COPY
|
||||||
|
#define va_copy(dest, src) va_copy(dest, src)
|
||||||
|
#else
|
||||||
|
#ifdef HAVE___VA_COPY
|
||||||
|
#define va_copy(dest, src) __va_copy(dest, src)
|
||||||
|
#else
|
||||||
|
#ifndef VA_LIST_IS_ARRAY
|
||||||
|
#define va_copy(dest, src) (dest) = (src)
|
||||||
|
#else
|
||||||
|
#include <string.h>
|
||||||
|
#define va_copy(dest, src) \
|
||||||
|
memcpy((char *)(dest), (char *)(src), sizeof(va_list))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Initialize the Strophe library.
|
/** Initialize the Strophe library.
|
||||||
* This function initializes subcomponents of the Strophe library and must
|
* This function initializes subcomponents of the Strophe library and must
|
||||||
* be called for Strophe to operate correctly.
|
* be called for Strophe to operate correctly.
|
||||||
*
|
*
|
||||||
* @ingroup Init
|
* @ingroup Init
|
||||||
*/
|
*/
|
||||||
void xmpp_initialize(void)
|
void xmpp_initialize(void)
|
||||||
{
|
{
|
||||||
sock_initialize();
|
sock_initialize();
|
||||||
|
resolver_initialize();
|
||||||
tls_initialize();
|
tls_initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +89,7 @@
|
|||||||
void xmpp_shutdown(void)
|
void xmpp_shutdown(void)
|
||||||
{
|
{
|
||||||
tls_shutdown();
|
tls_shutdown();
|
||||||
|
resolver_shutdown();
|
||||||
sock_shutdown();
|
sock_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +108,14 @@ void xmpp_shutdown(void)
|
|||||||
#define LIBXMPP_VERSION_MINOR (0)
|
#define LIBXMPP_VERSION_MINOR (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef EVENT_LOOP_DEFAULT_TIMEOUT
|
||||||
|
/** @def EVENT_LOOP_DEFAULT_TIMEOUT
|
||||||
|
* The default timeout in milliseconds for the event loop.
|
||||||
|
* This is set to 1 second.
|
||||||
|
*/
|
||||||
|
#define EVENT_LOOP_DEFAULT_TIMEOUT 1000
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Check that Strophe supports a specific API version.
|
/** Check that Strophe supports a specific API version.
|
||||||
*
|
*
|
||||||
* @param major the major version number
|
* @param major the major version number
|
||||||
@@ -100,8 +127,7 @@ void xmpp_shutdown(void)
|
|||||||
*/
|
*/
|
||||||
int xmpp_version_check(int major, int minor)
|
int xmpp_version_check(int major, int minor)
|
||||||
{
|
{
|
||||||
return (major == LIBXMPP_VERSION_MAJOR) &&
|
return (major == LIBXMPP_VERSION_MAJOR) && (minor >= LIBXMPP_VERSION_MINOR);
|
||||||
(minor >= LIBXMPP_VERSION_MINOR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We define the global default allocator, logger, and context here. */
|
/* We define the global default allocator, logger, and context here. */
|
||||||
@@ -109,35 +135,33 @@ int xmpp_version_check(int major, int minor)
|
|||||||
/* Wrap stdlib routines malloc, free, and realloc for default memory
|
/* Wrap stdlib routines malloc, free, and realloc for default memory
|
||||||
* management.
|
* management.
|
||||||
*/
|
*/
|
||||||
static void *_malloc(const size_t size, void * const userdata)
|
static void *_malloc(size_t size, void *userdata)
|
||||||
{
|
{
|
||||||
|
UNUSED(userdata);
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _free(void *p, void * const userdata)
|
static void _free(void *p, void *userdata)
|
||||||
{
|
{
|
||||||
|
UNUSED(userdata);
|
||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *_realloc(void *p, const size_t size, void * const userdata)
|
static void *_realloc(void *p, size_t size, void *userdata)
|
||||||
{
|
{
|
||||||
|
UNUSED(userdata);
|
||||||
return realloc(p, size);
|
return realloc(p, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* default memory function map */
|
/* default memory function map */
|
||||||
static xmpp_mem_t xmpp_default_mem = {
|
static xmpp_mem_t xmpp_default_mem = {
|
||||||
_malloc, /* use the thinly wrapped stdlib routines by default */
|
_malloc, /* use the thinly wrapped stdlib routines by default */
|
||||||
_free,
|
_free, _realloc, NULL};
|
||||||
_realloc,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
/* log levels and names */
|
/* log levels and names */
|
||||||
static const char * const _xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"};
|
static const char *_xmpp_log_level_name[4] = {"DEBUG", "INFO", "WARN", "ERROR"};
|
||||||
static const xmpp_log_level_t _xmpp_default_logger_levels[] = {XMPP_LEVEL_DEBUG,
|
static const xmpp_log_level_t _xmpp_default_logger_levels[] = {
|
||||||
XMPP_LEVEL_INFO,
|
XMPP_LEVEL_DEBUG, XMPP_LEVEL_INFO, XMPP_LEVEL_WARN, XMPP_LEVEL_ERROR};
|
||||||
XMPP_LEVEL_WARN,
|
|
||||||
XMPP_LEVEL_ERROR};
|
|
||||||
|
|
||||||
/** Log a message.
|
/** Log a message.
|
||||||
* The default logger writes to stderr.
|
* The default logger writes to stderr.
|
||||||
@@ -148,22 +172,25 @@ static const xmpp_log_level_t _xmpp_default_logger_levels[] = {XMPP_LEVEL_DEBUG,
|
|||||||
* @param area the area the log message is for
|
* @param area the area the log message is for
|
||||||
* @param msg the log message
|
* @param msg the log message
|
||||||
*/
|
*/
|
||||||
void xmpp_default_logger(void * const userdata,
|
static void xmpp_default_logger(void *userdata,
|
||||||
const xmpp_log_level_t level,
|
xmpp_log_level_t level,
|
||||||
const char * const area,
|
const char *area,
|
||||||
const char * const msg)
|
const char *msg)
|
||||||
{
|
{
|
||||||
xmpp_log_level_t filter_level = * (xmpp_log_level_t*)userdata;
|
xmpp_log_level_t filter_level = *(xmpp_log_level_t *)userdata;
|
||||||
if (level >= filter_level)
|
if (level >= filter_level)
|
||||||
fprintf(stderr, "%s %s %s\n", area, _xmpp_log_level_name[level], msg);
|
fprintf(stderr, "%s %s %s\n", area, _xmpp_log_level_name[level], msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const xmpp_log_t _xmpp_default_loggers[] = {
|
static const xmpp_log_t _xmpp_default_loggers[] = {
|
||||||
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]},
|
{&xmpp_default_logger,
|
||||||
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_INFO]},
|
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_DEBUG]},
|
||||||
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_WARN]},
|
{&xmpp_default_logger,
|
||||||
{&xmpp_default_logger, (void*)&_xmpp_default_logger_levels[XMPP_LEVEL_ERROR]}
|
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_INFO]},
|
||||||
};
|
{&xmpp_default_logger,
|
||||||
|
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_WARN]},
|
||||||
|
{&xmpp_default_logger,
|
||||||
|
(void *)&_xmpp_default_logger_levels[XMPP_LEVEL_ERROR]}};
|
||||||
|
|
||||||
/** Get a default logger with filtering.
|
/** Get a default logger with filtering.
|
||||||
* The default logger provides a basic logging setup which writes log
|
* The default logger provides a basic logging setup which writes log
|
||||||
@@ -179,13 +206,13 @@ static const xmpp_log_t _xmpp_default_loggers[] = {
|
|||||||
xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level)
|
xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level)
|
||||||
{
|
{
|
||||||
/* clamp to the known range */
|
/* clamp to the known range */
|
||||||
if (level > XMPP_LEVEL_ERROR) level = XMPP_LEVEL_ERROR;
|
if (level > XMPP_LEVEL_ERROR)
|
||||||
if (level < XMPP_LEVEL_DEBUG) level = XMPP_LEVEL_DEBUG;
|
level = XMPP_LEVEL_ERROR;
|
||||||
|
|
||||||
return (xmpp_log_t*)&_xmpp_default_loggers[level];
|
return (xmpp_log_t *)&_xmpp_default_loggers[level];
|
||||||
}
|
}
|
||||||
|
|
||||||
static xmpp_log_t xmpp_default_log = { NULL, NULL };
|
static xmpp_log_t xmpp_default_log = {NULL, NULL};
|
||||||
|
|
||||||
/* convenience functions for accessing the context */
|
/* convenience functions for accessing the context */
|
||||||
|
|
||||||
@@ -197,7 +224,7 @@ static xmpp_log_t xmpp_default_log = { NULL, NULL };
|
|||||||
*
|
*
|
||||||
* @return a pointer to the allocated memory or NULL on an error
|
* @return a pointer to the allocated memory or NULL on an error
|
||||||
*/
|
*/
|
||||||
void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size)
|
void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size)
|
||||||
{
|
{
|
||||||
return ctx->mem->alloc(size, ctx->mem->userdata);
|
return ctx->mem->alloc(size, ctx->mem->userdata);
|
||||||
}
|
}
|
||||||
@@ -208,11 +235,21 @@ void *xmpp_alloc(const xmpp_ctx_t * const ctx, const size_t size)
|
|||||||
* @param ctx a Strophe context object
|
* @param ctx a Strophe context object
|
||||||
* @param p a pointer referencing memory to be freed
|
* @param p a pointer referencing memory to be freed
|
||||||
*/
|
*/
|
||||||
void xmpp_free(const xmpp_ctx_t * const ctx, void *p)
|
void strophe_free(const xmpp_ctx_t *ctx, void *p)
|
||||||
{
|
{
|
||||||
ctx->mem->free(p, ctx->mem->userdata);
|
ctx->mem->free(p, ctx->mem->userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Trampoline to \ref strophe_free
|
||||||
|
*
|
||||||
|
* @param ctx \ref strophe_free
|
||||||
|
* @param p \ref strophe_free
|
||||||
|
*/
|
||||||
|
void xmpp_free(const xmpp_ctx_t *ctx, void *p)
|
||||||
|
{
|
||||||
|
strophe_free(ctx, p);
|
||||||
|
}
|
||||||
|
|
||||||
/** Reallocate memory in a Strophe context.
|
/** Reallocate memory in a Strophe context.
|
||||||
* All Strophe functions will use this to reallocate memory.
|
* All Strophe functions will use this to reallocate memory.
|
||||||
*
|
*
|
||||||
@@ -222,8 +259,7 @@ void xmpp_free(const xmpp_ctx_t * const ctx, void *p)
|
|||||||
*
|
*
|
||||||
* @return a pointer to the reallocated memory or NULL on an error
|
* @return a pointer to the reallocated memory or NULL on an error
|
||||||
*/
|
*/
|
||||||
void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
|
void *strophe_realloc(const xmpp_ctx_t *ctx, void *p, size_t size)
|
||||||
const size_t size)
|
|
||||||
{
|
{
|
||||||
return ctx->mem->realloc(p, size, ctx->mem->userdata);
|
return ctx->mem->realloc(p, size, ctx->mem->userdata);
|
||||||
}
|
}
|
||||||
@@ -232,8 +268,8 @@ void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
|
|||||||
* Write a log message to the logger for the context for the specified
|
* Write a log message to the logger for the context for the specified
|
||||||
* level and area. This function takes a printf-style format string and a
|
* level and area. This function takes a printf-style format string and a
|
||||||
* variable argument list (in va_list) format. This function is not meant
|
* variable argument list (in va_list) format. This function is not meant
|
||||||
* to be called directly, but is used via xmpp_error, xmpp_warn, xmpp_info,
|
* to be called directly, but is used via strophe_error, strophe_warn,
|
||||||
* and xmpp_debug.
|
* strophe_info, and strophe_debug.
|
||||||
*
|
*
|
||||||
* @param ctx a Strophe context object
|
* @param ctx a Strophe context object
|
||||||
* @param level the level at which to log
|
* @param level the level at which to log
|
||||||
@@ -241,40 +277,62 @@ void *xmpp_realloc(const xmpp_ctx_t * const ctx, void *p,
|
|||||||
* @param fmt a printf-style format string for the message
|
* @param fmt a printf-style format string for the message
|
||||||
* @param ap variable argument list supplied for the format string
|
* @param ap variable argument list supplied for the format string
|
||||||
*/
|
*/
|
||||||
void xmpp_log(const xmpp_ctx_t * const ctx,
|
static void _strophe_log(const xmpp_ctx_t *ctx,
|
||||||
const xmpp_log_level_t level,
|
xmpp_log_level_t level,
|
||||||
const char * const area,
|
const char *area,
|
||||||
const char * const fmt,
|
const char *fmt,
|
||||||
va_list ap)
|
va_list ap)
|
||||||
{
|
{
|
||||||
int oldret, ret;
|
int oldret, ret;
|
||||||
char smbuf[1024];
|
char smbuf[1024];
|
||||||
char *buf;
|
char *buf;
|
||||||
va_list copy;
|
va_list copy;
|
||||||
|
|
||||||
buf = smbuf;
|
if (!ctx->log->handler)
|
||||||
va_copy(copy, ap);
|
return;
|
||||||
ret = xmpp_vsnprintf(buf, 1023, fmt, ap);
|
|
||||||
if (ret > 1023) {
|
|
||||||
buf = (char *)xmpp_alloc(ctx, ret + 1);
|
|
||||||
if (!buf) {
|
|
||||||
buf = NULL;
|
|
||||||
xmpp_error(ctx, "log", "Failed allocating memory for log message.");
|
|
||||||
va_end(copy);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
oldret = ret;
|
|
||||||
ret = xmpp_vsnprintf(buf, ret + 1, fmt, copy);
|
|
||||||
if (ret > oldret) {
|
|
||||||
xmpp_error(ctx, "log", "Unexpected error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
va_end(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->log->handler)
|
if (ctx->log->handler == xmpp_default_logger &&
|
||||||
ctx->log->handler(ctx->log->userdata, level, area, buf);
|
level < *(xmpp_log_level_t *)ctx->log->userdata)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_copy(copy, ap);
|
||||||
|
ret = strophe_vsnprintf(smbuf, sizeof(smbuf), fmt, ap);
|
||||||
|
if (ret >= (int)sizeof(smbuf)) {
|
||||||
|
buf = (char *)strophe_alloc(ctx, ret + 1);
|
||||||
|
if (!buf) {
|
||||||
|
buf = NULL;
|
||||||
|
strophe_error(ctx, "log",
|
||||||
|
"Failed allocating memory for log message.");
|
||||||
|
va_end(copy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
oldret = ret;
|
||||||
|
ret = strophe_vsnprintf(buf, ret + 1, fmt, copy);
|
||||||
|
if (ret > oldret) {
|
||||||
|
strophe_error(ctx, "log", "Unexpected error");
|
||||||
|
strophe_free(ctx, buf);
|
||||||
|
va_end(copy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf = smbuf;
|
||||||
|
}
|
||||||
|
va_end(copy);
|
||||||
|
|
||||||
|
ctx->log->handler(ctx->log->userdata, level, area, buf);
|
||||||
|
|
||||||
|
if (buf != smbuf)
|
||||||
|
strophe_free(ctx, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dummy trampoline, will be removed when deprecated.c is deleted */
|
||||||
|
void strophe_log_internal(const xmpp_ctx_t *ctx,
|
||||||
|
xmpp_log_level_t level,
|
||||||
|
const char *area,
|
||||||
|
const char *fmt,
|
||||||
|
va_list ap)
|
||||||
|
{
|
||||||
|
_strophe_log(ctx, level, area, fmt, ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write to the log at the ERROR level.
|
/** Write to the log at the ERROR level.
|
||||||
@@ -287,15 +345,15 @@ void xmpp_log(const xmpp_ctx_t * const ctx,
|
|||||||
* @param fmt a printf-style format string followed by a variable list of
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
* arguments to format
|
* arguments to format
|
||||||
*/
|
*/
|
||||||
void xmpp_error(const xmpp_ctx_t * const ctx,
|
void strophe_error(const xmpp_ctx_t *ctx,
|
||||||
const char * const area,
|
const char *area,
|
||||||
const char * const fmt,
|
const char *fmt,
|
||||||
...)
|
...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
xmpp_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
|
_strophe_log(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,15 +367,12 @@ void xmpp_error(const xmpp_ctx_t * const ctx,
|
|||||||
* @param fmt a printf-style format string followed by a variable list of
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
* arguments to format
|
* arguments to format
|
||||||
*/
|
*/
|
||||||
void xmpp_warn(const xmpp_ctx_t * const ctx,
|
void strophe_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||||
const char * const area,
|
|
||||||
const char * const fmt,
|
|
||||||
...)
|
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
xmpp_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
|
_strophe_log(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,15 +386,12 @@ void xmpp_warn(const xmpp_ctx_t * const ctx,
|
|||||||
* @param fmt a printf-style format string followed by a variable list of
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
* arguments to format
|
* arguments to format
|
||||||
*/
|
*/
|
||||||
void xmpp_info(const xmpp_ctx_t * const ctx,
|
void strophe_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||||
const char * const area,
|
|
||||||
const char * const fmt,
|
|
||||||
...)
|
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
xmpp_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
|
_strophe_log(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,15 +405,39 @@ void xmpp_info(const xmpp_ctx_t * const ctx,
|
|||||||
* @param fmt a printf-style format string followed by a variable list of
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
* arguments to format
|
* arguments to format
|
||||||
*/
|
*/
|
||||||
void xmpp_debug(const xmpp_ctx_t * const ctx,
|
void strophe_debug(const xmpp_ctx_t *ctx,
|
||||||
const char * const area,
|
const char *area,
|
||||||
const char * const fmt,
|
const char *fmt,
|
||||||
...)
|
...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
xmpp_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
_strophe_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write to the log at the DEBUG level if verbosity is enabled.
|
||||||
|
* This is a convenience function for writing to the log at the DEBUG level.
|
||||||
|
* It takes a printf-style format string followed by a variable list of
|
||||||
|
* arguments for formatting.
|
||||||
|
*
|
||||||
|
* @param level the verbosity level
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param area the area to log for
|
||||||
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
|
* arguments to format
|
||||||
|
*/
|
||||||
|
void strophe_debug_verbose(
|
||||||
|
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (ctx->verbosity < level)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
_strophe_log(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,29 +455,35 @@ void xmpp_debug(const xmpp_ctx_t * const ctx,
|
|||||||
*
|
*
|
||||||
* @ingroup Context
|
* @ingroup Context
|
||||||
*/
|
*/
|
||||||
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
|
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log)
|
||||||
const xmpp_log_t * const log)
|
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx = NULL;
|
xmpp_ctx_t *ctx = NULL;
|
||||||
|
|
||||||
if (mem == NULL)
|
if (mem == NULL)
|
||||||
ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t), NULL);
|
ctx = xmpp_default_mem.alloc(sizeof(xmpp_ctx_t), NULL);
|
||||||
else
|
else
|
||||||
ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
|
ctx = mem->alloc(sizeof(xmpp_ctx_t), mem->userdata);
|
||||||
|
|
||||||
if (ctx != NULL) {
|
if (ctx != NULL) {
|
||||||
if (mem != NULL)
|
memset(ctx, 0, sizeof(xmpp_ctx_t));
|
||||||
ctx->mem = mem;
|
|
||||||
else
|
|
||||||
ctx->mem = &xmpp_default_mem;
|
|
||||||
|
|
||||||
if (log == NULL)
|
if (mem != NULL)
|
||||||
ctx->log = &xmpp_default_log;
|
ctx->mem = mem;
|
||||||
else
|
else
|
||||||
ctx->log = log;
|
ctx->mem = &xmpp_default_mem;
|
||||||
|
|
||||||
ctx->connlist = NULL;
|
if (log == NULL)
|
||||||
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
|
ctx->log = &xmpp_default_log;
|
||||||
|
else
|
||||||
|
ctx->log = log;
|
||||||
|
|
||||||
|
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
|
||||||
|
ctx->rand = xmpp_rand_new(ctx);
|
||||||
|
ctx->timeout = EVENT_LOOP_DEFAULT_TIMEOUT;
|
||||||
|
if (ctx->rand == NULL) {
|
||||||
|
strophe_free(ctx, ctx);
|
||||||
|
ctx = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
@@ -413,9 +495,21 @@ xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
|
|||||||
*
|
*
|
||||||
* @ingroup Context
|
* @ingroup Context
|
||||||
*/
|
*/
|
||||||
void xmpp_ctx_free(xmpp_ctx_t * const ctx)
|
void xmpp_ctx_free(xmpp_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
/* mem and log are owned by their suppliers */
|
/* mem and log are owned by their suppliers */
|
||||||
xmpp_free(ctx, ctx); /* pull the hole in after us */
|
xmpp_rand_free(ctx, ctx->rand);
|
||||||
|
strophe_free(ctx, ctx); /* pull the hole in after us */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set the verbosity level of a Strophe context.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param level the verbosity level
|
||||||
|
*
|
||||||
|
* @ingroup Context
|
||||||
|
*/
|
||||||
|
void xmpp_ctx_set_verbosity(xmpp_ctx_t *ctx, int level)
|
||||||
|
{
|
||||||
|
ctx->verbosity = level;
|
||||||
|
}
|
||||||
|
|||||||
459
src/deprecated.c
Normal file
459
src/deprecated.c
Normal file
@@ -0,0 +1,459 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* deprecated.c
|
||||||
|
** strophe XMPP client library -- File with deprecated API functions.
|
||||||
|
**
|
||||||
|
** Copyright (C) 2022 Steffen Jaeckel
|
||||||
|
**
|
||||||
|
** This software is provided AS-IS with no warranty, either express
|
||||||
|
** or implied.
|
||||||
|
**
|
||||||
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* File with deprecated API functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup Deprecated All deprecated functions
|
||||||
|
* These functions will be removed in the next release.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
/** Allocate memory in a Strophe context.
|
||||||
|
* All Strophe functions will use this to allocate memory.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param size the number of bytes to allocate
|
||||||
|
*
|
||||||
|
* @return a pointer to the allocated memory or NULL on an error
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size)
|
||||||
|
{
|
||||||
|
return strophe_alloc(ctx, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reallocate memory in a Strophe context.
|
||||||
|
* All Strophe functions will use this to reallocate memory.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param p a pointer to previously allocated memory
|
||||||
|
* @param size the new size in bytes to allocate
|
||||||
|
*
|
||||||
|
* @return a pointer to the reallocated memory or NULL on an error
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void *xmpp_realloc(const xmpp_ctx_t *ctx, void *p, size_t size)
|
||||||
|
{
|
||||||
|
return strophe_realloc(ctx, p, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** implement our own strdup that uses the ctx allocator */
|
||||||
|
/** Duplicate a string.
|
||||||
|
* This function replaces the standard strdup library call with a version
|
||||||
|
* that uses the Strophe context object's allocator.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param s a string
|
||||||
|
*
|
||||||
|
* @return a newly allocated string with the same data as s or NULL on error
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
char *xmpp_strdup(const xmpp_ctx_t *ctx, const char *s)
|
||||||
|
{
|
||||||
|
return strophe_strdup(ctx, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Duplicate a string with a maximum length.
|
||||||
|
* This function replaces the standard strndup library call with a version
|
||||||
|
* that uses the Strophe context object's allocator.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param s a string
|
||||||
|
* @param len the maximum length of the string to copy
|
||||||
|
*
|
||||||
|
* @return a newly allocated string that contains at most `len` symbols
|
||||||
|
* of the original string or NULL on error
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
char *xmpp_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len)
|
||||||
|
{
|
||||||
|
return strophe_strndup(ctx, s, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write to the log.
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_log(const xmpp_ctx_t *ctx,
|
||||||
|
xmpp_log_level_t level,
|
||||||
|
const char *area,
|
||||||
|
const char *fmt,
|
||||||
|
va_list ap)
|
||||||
|
{
|
||||||
|
strophe_log_internal(ctx, level, area, fmt, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write to the log at the ERROR level.
|
||||||
|
* This is a convenience function for writing to the log at the
|
||||||
|
* ERROR level. It takes a printf-style format string followed by a
|
||||||
|
* variable list of arguments for formatting.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param area the area to log for
|
||||||
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
|
* arguments to format
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_error(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
strophe_log_internal(ctx, XMPP_LEVEL_ERROR, area, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write to the log at the WARN level.
|
||||||
|
* This is a convenience function for writing to the log at the WARN level.
|
||||||
|
* It takes a printf-style format string followed by a variable list of
|
||||||
|
* arguments for formatting.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param area the area to log for
|
||||||
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
|
* arguments to format
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
strophe_log_internal(ctx, XMPP_LEVEL_WARN, area, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write to the log at the INFO level.
|
||||||
|
* This is a convenience function for writing to the log at the INFO level.
|
||||||
|
* It takes a printf-style format string followed by a variable list of
|
||||||
|
* arguments for formatting.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param area the area to log for
|
||||||
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
|
* arguments to format
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
strophe_log_internal(ctx, XMPP_LEVEL_INFO, area, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write to the log at the DEBUG level.
|
||||||
|
* This is a convenience function for writing to the log at the DEBUG level.
|
||||||
|
* It takes a printf-style format string followed by a variable list of
|
||||||
|
* arguments for formatting.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param area the area to log for
|
||||||
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
|
* arguments to format
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_debug(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
strophe_log_internal(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write to the log at the DEBUG level if verbosity is enabled.
|
||||||
|
* This is a convenience function for writing to the log at the DEBUG level.
|
||||||
|
* It takes a printf-style format string followed by a variable list of
|
||||||
|
* arguments for formatting.
|
||||||
|
*
|
||||||
|
* @param level the verbosity level
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param area the area to log for
|
||||||
|
* @param fmt a printf-style format string followed by a variable list of
|
||||||
|
* arguments to format
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_debug_verbose(
|
||||||
|
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (ctx->verbosity < level)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
strophe_log_internal(ctx, XMPP_LEVEL_DEBUG, area, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** strtok_r(3) implementation.
|
||||||
|
* This function has appeared in POSIX.1-2001, but not in C standard.
|
||||||
|
* For example, visual studio older than 2005 doesn't provide strtok_r()
|
||||||
|
* nor strtok_s().
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr)
|
||||||
|
{
|
||||||
|
return strophe_strtok_r(s, delim, saveptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** snprintf(3) implementation.
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = strophe_vsnprintf(str, count, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** vsnprintf(3) implementation.
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg)
|
||||||
|
{
|
||||||
|
return strophe_vsnprintf(str, count, fmt, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the JID of the user that will be bound to the connection.
|
||||||
|
* If any JID was previously set, it will be discarded. This should not be
|
||||||
|
* be used after a connection is created. The function will make a copy of
|
||||||
|
* the JID string. If the supplied JID is missing the node, SASL
|
||||||
|
* ANONYMOUS authentication will be used.
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
* @param jid a full or bare JID
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid)
|
||||||
|
{
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_JID, jid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the password used to authenticate the connection.
|
||||||
|
* If any password was previously set, it will be discarded. The function
|
||||||
|
* will make a copy of the password string.
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
* @param pass the password
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass)
|
||||||
|
{
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_PASS, pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the CAfile
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
* @param path path to a certificate file
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path)
|
||||||
|
{
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CAFILE, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the CApath
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
* @param path path to a folder containing certificates
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path)
|
||||||
|
{
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CAPATH, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the Client Certificate and Private Key or PKCS#12 encoded file that
|
||||||
|
* will be bound to the connection. If any of them was previously set, it
|
||||||
|
* will be discarded. This should not be used after a connection is created.
|
||||||
|
* The function will make a copy of the strings passed in.
|
||||||
|
*
|
||||||
|
* In case the Private Key is encrypted, a callback must be set via
|
||||||
|
* \ref xmpp_conn_set_password_callback so the TLS stack can retrieve the
|
||||||
|
* password.
|
||||||
|
*
|
||||||
|
* In case one wants to use a PKCS#12 encoded file, it should be passed via
|
||||||
|
* the `cert` parameter and `key` should be NULL. Passing a PKCS#12 file in
|
||||||
|
* `key` is deprecated.
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
* @param cert path to a certificate file or a P12 file
|
||||||
|
* @param key path to a private key file or a P12 file
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_set_client_cert(xmpp_conn_t *const conn,
|
||||||
|
const char *const cert,
|
||||||
|
const char *const key)
|
||||||
|
{
|
||||||
|
strophe_debug(conn->ctx, "conn", "set client cert %s %s", cert, key);
|
||||||
|
if (!cert && key) {
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, key);
|
||||||
|
strophe_warn(conn->ctx, "xmpp",
|
||||||
|
"xmpp_conn_set_client_cert: Passing PKCS#12 in 'key' "
|
||||||
|
"parameter is deprecated. Use 'cert' instead");
|
||||||
|
} else {
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_CERT, cert);
|
||||||
|
xmpp_conn_set_string(conn, XMPP_SETTING_CLIENT_KEY, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the number of retry attempts to decrypt a private key file.
|
||||||
|
*
|
||||||
|
* In case the user enters the password manually it can be useful to
|
||||||
|
* directly retry if the decryption of the key file failed.
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
* @param retries The number of retries that should be tried
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries)
|
||||||
|
{
|
||||||
|
int val;
|
||||||
|
if (retries > INT_MAX) {
|
||||||
|
val = INT_MAX;
|
||||||
|
strophe_warn(conn->ctx, "xmpp", "retries capped from %u to %d", retries,
|
||||||
|
val);
|
||||||
|
} else {
|
||||||
|
val = (int)retries;
|
||||||
|
}
|
||||||
|
xmpp_conn_set_int(conn, XMPP_SETTING_PASSWORD_RETRIES, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the Callback function which will be called when the TLS stack can't
|
||||||
|
* decrypt a password protected key file.
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
* @param cb The callback function that shall be called
|
||||||
|
* @param userdata An opaque data pointer that will be passed to the callback
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
|
||||||
|
xmpp_password_callback cb,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_PASSWORD_CALLBACK, cb);
|
||||||
|
xmpp_conn_set_pointer(conn, XMPP_SETTING_PASSWORD_CALLBACK_USERDATA,
|
||||||
|
userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the Handler function which will be called when the TLS stack can't
|
||||||
|
* verify the CA of the server we're trying to connect to.
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
* @param hndl certfail Handler function
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
|
||||||
|
xmpp_certfail_handler hndl)
|
||||||
|
{
|
||||||
|
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_CERTFAIL_HANDLER, hndl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Register sockopt callback
|
||||||
|
* Set function to be called when a new socket is created to allow setting
|
||||||
|
* socket options before connection is started.
|
||||||
|
*
|
||||||
|
* If the connection is already connected, this callback will be called
|
||||||
|
* immediately.
|
||||||
|
*
|
||||||
|
* To set options that can only be applied to disconnected sockets, the
|
||||||
|
* callback must be registered before connecting.
|
||||||
|
*
|
||||||
|
* @param conn The Strophe connection object this callback is being registered
|
||||||
|
* for
|
||||||
|
* @param callback a xmpp_sockopt_callback callback function that will receive
|
||||||
|
* notifications of connection status
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
|
||||||
|
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
|
||||||
|
xmpp_sockopt_callback callback)
|
||||||
|
{
|
||||||
|
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
|
||||||
|
callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set TCP keepalive parameters
|
||||||
|
* Turn on TCP keepalive and set timeout and interval. Zero timeout
|
||||||
|
* disables TCP keepalives. The parameters are applied immediately for
|
||||||
|
* a non disconnected object. Also, they are applied when the connection
|
||||||
|
* object connects successfully.
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
* @param timeout TCP keepalive timeout in seconds
|
||||||
|
* @param interval TCP keepalive interval in seconds
|
||||||
|
*
|
||||||
|
* @note this function is deprecated
|
||||||
|
* @see xmpp_conn_set_sockopt_callback()
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval)
|
||||||
|
{
|
||||||
|
conn->ka_timeout = timeout;
|
||||||
|
conn->ka_interval = interval;
|
||||||
|
conn->ka_count = 0;
|
||||||
|
xmpp_conn_set_functionpointer(conn, XMPP_SETTING_SOCKOPT_CALLBACK,
|
||||||
|
xmpp_sockopt_cb_keepalive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Disable TLS for this connection, called by users of the library.
|
||||||
|
* Occasionally a server will be misconfigured to send the starttls
|
||||||
|
* feature, but will not support the handshake.
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
*
|
||||||
|
* @note this function is deprecated
|
||||||
|
* @see xmpp_conn_set_flags()
|
||||||
|
*
|
||||||
|
* @ingroup Deprecated
|
||||||
|
*/
|
||||||
|
void xmpp_conn_disable_tls(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
long flags = xmpp_conn_get_flags(conn);
|
||||||
|
|
||||||
|
flags |= XMPP_CONN_FLAG_DISABLE_TLS;
|
||||||
|
(void)xmpp_conn_set_flags(conn, flags);
|
||||||
|
}
|
||||||
424
src/event.c
424
src/event.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* event.c
|
/* event.c
|
||||||
** strophe XMPP client library -- event loop and management
|
** strophe XMPP client library -- event loop and management
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -40,25 +38,43 @@
|
|||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#define _sleep(x) usleep((x)*1000)
|
||||||
#else
|
#else
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
|
#ifndef ETIMEDOUT
|
||||||
#define ETIMEDOUT WSAETIMEDOUT
|
#define ETIMEDOUT WSAETIMEDOUT
|
||||||
|
#endif
|
||||||
|
#ifndef ECONNRESET
|
||||||
#define ECONNRESET WSAECONNRESET
|
#define ECONNRESET WSAECONNRESET
|
||||||
|
#endif
|
||||||
|
#ifndef ECONNABORTED
|
||||||
#define ECONNABORTED WSAECONNABORTED
|
#define ECONNABORTED WSAECONNABORTED
|
||||||
#endif
|
#endif
|
||||||
|
#define _sleep(x) Sleep(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <strophe.h>
|
#include "strophe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
#ifndef DEFAULT_TIMEOUT
|
#ifndef STROPHE_MESSAGE_BUFFER_SIZE
|
||||||
/** @def DEFAULT_TIMEOUT
|
/** Max buffer size for receiving messages. */
|
||||||
* The default timeout in milliseconds for the event loop.
|
#define STROPHE_MESSAGE_BUFFER_SIZE 4096
|
||||||
* This is set to 1 millisecond.
|
|
||||||
*/
|
|
||||||
#define DEFAULT_TIMEOUT 1
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int _connect_next(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
sock_close(conn->sock);
|
||||||
|
conn->sock = sock_connect(conn->xsock);
|
||||||
|
if (conn->sock == INVALID_SOCKET)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
conn->timeout_stamp = time_stamp();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** Run the event loop once.
|
/** Run the event loop once.
|
||||||
* This function will run send any data that has been queued by
|
* This function will run send any data that has been queued by
|
||||||
* xmpp_send and related functions and run through the Strophe even
|
* xmpp_send and related functions and run through the Strophe even
|
||||||
@@ -72,117 +88,120 @@
|
|||||||
*
|
*
|
||||||
* @ingroup EventLoop
|
* @ingroup EventLoop
|
||||||
*/
|
*/
|
||||||
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout)
|
||||||
{
|
{
|
||||||
xmpp_connlist_t *connitem;
|
xmpp_connlist_t *connitem;
|
||||||
xmpp_conn_t *conn;
|
xmpp_conn_t *conn;
|
||||||
|
struct conn_interface *intf;
|
||||||
fd_set rfds, wfds;
|
fd_set rfds, wfds;
|
||||||
sock_t max = 0;
|
sock_t max = 0;
|
||||||
int ret;
|
int ret;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
xmpp_send_queue_t *sq, *tsq;
|
xmpp_send_queue_t *sq, *tsq;
|
||||||
int towrite;
|
int towrite;
|
||||||
char buf[4096];
|
char buf[STROPHE_MESSAGE_BUFFER_SIZE];
|
||||||
uint64_t next;
|
uint64_t next;
|
||||||
long usec;
|
uint64_t usec;
|
||||||
int tls_read_bytes = 0;
|
int tls_read_bytes = 0;
|
||||||
|
|
||||||
if (ctx->loop_status == XMPP_LOOP_QUIT) return;
|
if (ctx->loop_status == XMPP_LOOP_QUIT)
|
||||||
ctx->loop_status = XMPP_LOOP_RUNNING;
|
return;
|
||||||
|
|
||||||
/* send queued data */
|
/* send queued data */
|
||||||
connitem = ctx->connlist;
|
connitem = ctx->connlist;
|
||||||
while (connitem) {
|
while (connitem) {
|
||||||
conn = connitem->conn;
|
conn = connitem->conn;
|
||||||
if (conn->state != XMPP_STATE_CONNECTED) {
|
if (conn->state != XMPP_STATE_CONNECTED) {
|
||||||
connitem = connitem->next;
|
connitem = connitem->next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
intf = &conn->intf;
|
||||||
|
|
||||||
/* if we're running tls, there may be some remaining data waiting to
|
/* if we're running tls, there may be some remaining data waiting to
|
||||||
* be sent, so push that out */
|
* be sent, so push that out */
|
||||||
if (conn->tls) {
|
if (conn->tls) {
|
||||||
ret = tls_clear_pending_write(conn->tls);
|
ret = tls_clear_pending_write(intf);
|
||||||
|
|
||||||
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
|
if (ret < 0 && !tls_is_recoverable(intf, tls_error(intf))) {
|
||||||
/* an error occured */
|
/* an error occurred */
|
||||||
xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting.");
|
strophe_debug(
|
||||||
conn->error = ECONNABORTED;
|
ctx, "xmpp",
|
||||||
conn_disconnect(conn);
|
"Send error of pending data occurred, disconnecting.");
|
||||||
}
|
conn->error = ECONNABORTED;
|
||||||
}
|
conn_disconnect(conn);
|
||||||
|
goto next_item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* write all data from the send queue to the socket */
|
/* write all data from the send queue to the socket */
|
||||||
sq = conn->send_queue_head;
|
sq = conn->send_queue_head;
|
||||||
while (sq) {
|
while (sq) {
|
||||||
towrite = sq->len - sq->written;
|
towrite = sq->len - sq->written;
|
||||||
|
|
||||||
if (conn->tls) {
|
ret = conn_interface_write(intf, &sq->data[sq->written], towrite);
|
||||||
ret = tls_write(conn->tls, &sq->data[sq->written], towrite);
|
if (ret > 0 && ret < towrite)
|
||||||
|
sq->written += ret; /* not all data could be sent now */
|
||||||
|
sq->wip = 1;
|
||||||
|
if (ret != towrite)
|
||||||
|
break; /* partial write or an error */
|
||||||
|
|
||||||
if (ret < 0 && !tls_is_recoverable(tls_error(conn->tls))) {
|
/* all data for this queue item written, delete and move on */
|
||||||
/* an error occured */
|
strophe_debug(conn->ctx, "conn", "SENT: %s", sq->data);
|
||||||
conn->error = tls_error(conn->tls);
|
strophe_debug_verbose(1, ctx, "xmpp", "Q_SENT: %p", sq);
|
||||||
break;
|
tsq = sq;
|
||||||
} else if (ret < towrite) {
|
sq = sq->next;
|
||||||
/* not all data could be sent now */
|
conn->send_queue_len--;
|
||||||
if (ret >= 0) sq->written += ret;
|
if (tsq->owner & XMPP_QUEUE_USER)
|
||||||
break;
|
conn->send_queue_user_len--;
|
||||||
}
|
if (!(tsq->owner & XMPP_QUEUE_SM) && conn->sm_state->sm_enabled) {
|
||||||
|
tsq->sm_h = conn->sm_state->sm_sent_nr;
|
||||||
|
conn->sm_state->sm_sent_nr++;
|
||||||
|
strophe_debug_verbose(1, ctx, "xmpp", "SM_Q_MOVE: %p, h=%lu",
|
||||||
|
tsq, tsq->sm_h);
|
||||||
|
add_queue_back(&conn->sm_state->sm_queue, tsq);
|
||||||
|
tsq = NULL;
|
||||||
|
}
|
||||||
|
if (tsq) {
|
||||||
|
strophe_debug_verbose(2, ctx, "xmpp", "Q_FREE: %p", tsq);
|
||||||
|
strophe_debug_verbose(3, ctx, "conn", "Q_CONTENT: %s",
|
||||||
|
tsq->data);
|
||||||
|
strophe_free(ctx, tsq->data);
|
||||||
|
strophe_free(ctx, tsq);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
/* pop the top item */
|
||||||
ret = sock_write(conn->sock, &sq->data[sq->written], towrite);
|
conn->send_queue_head = sq;
|
||||||
|
/* if we've sent everything update the tail */
|
||||||
|
if (!sq)
|
||||||
|
conn->send_queue_tail = NULL;
|
||||||
|
}
|
||||||
|
intf->flush(intf);
|
||||||
|
|
||||||
if (ret < 0 && !sock_is_recoverable(sock_error())) {
|
/* tear down connection on error */
|
||||||
/* an error occured */
|
if (conn->error) {
|
||||||
conn->error = sock_error();
|
/* FIXME: need to tear down send queues and random other things
|
||||||
break;
|
* maybe this should be abstracted */
|
||||||
} else if (ret < towrite) {
|
strophe_debug(ctx, "xmpp", "Send error occurred, disconnecting.");
|
||||||
/* not all data could be sent now */
|
conn->error = ECONNABORTED;
|
||||||
if (ret >= 0) sq->written += ret;
|
conn_disconnect(conn);
|
||||||
break;
|
}
|
||||||
}
|
next_item:
|
||||||
}
|
connitem = connitem->next;
|
||||||
|
|
||||||
/* all data for this queue item written, delete and move on */
|
|
||||||
xmpp_free(ctx, sq->data);
|
|
||||||
tsq = sq;
|
|
||||||
sq = sq->next;
|
|
||||||
xmpp_free(ctx, tsq);
|
|
||||||
|
|
||||||
/* pop the top item */
|
|
||||||
conn->send_queue_head = sq;
|
|
||||||
/* if we've sent everything update the tail */
|
|
||||||
if (!sq) conn->send_queue_tail = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tear down connection on error */
|
|
||||||
if (conn->error) {
|
|
||||||
/* FIXME: need to tear down send queues and random other things
|
|
||||||
* maybe this should be abstracted */
|
|
||||||
xmpp_debug(ctx, "xmpp", "Send error occured, disconnecting.");
|
|
||||||
conn->error = ECONNABORTED;
|
|
||||||
conn_disconnect(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
connitem = connitem->next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reset parsers if needed */
|
/* reset parsers if needed */
|
||||||
for (connitem = ctx->connlist; connitem; connitem = connitem->next) {
|
for (connitem = ctx->connlist; connitem; connitem = connitem->next) {
|
||||||
if (connitem->conn->reset_parser)
|
if (connitem->conn->reset_parser)
|
||||||
conn_parser_reset(connitem->conn);
|
conn_parser_reset(connitem->conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* fire any ready timed handlers, then make sure we don't wait past
|
||||||
/* fire any ready timed handlers, then
|
the time when timed handlers need to be called */
|
||||||
make sure we don't wait past the time when timed handlers need
|
|
||||||
to be called */
|
|
||||||
next = handler_fire_timed(ctx);
|
next = handler_fire_timed(ctx);
|
||||||
|
|
||||||
usec = ((next < timeout) ? next : timeout) * 1000;
|
usec = ((next < timeout) ? next : timeout) * 1000;
|
||||||
tv.tv_sec = usec / 1000000;
|
tv.tv_sec = (long)(usec / 1000000);
|
||||||
tv.tv_usec = usec % 1000000;
|
tv.tv_usec = (long)(usec % 1000000);
|
||||||
|
|
||||||
FD_ZERO(&rfds);
|
FD_ZERO(&rfds);
|
||||||
FD_ZERO(&wfds);
|
FD_ZERO(&wfds);
|
||||||
@@ -190,124 +209,139 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
|||||||
/* find events to watch */
|
/* find events to watch */
|
||||||
connitem = ctx->connlist;
|
connitem = ctx->connlist;
|
||||||
while (connitem) {
|
while (connitem) {
|
||||||
conn = connitem->conn;
|
conn = connitem->conn;
|
||||||
|
intf = &conn->intf;
|
||||||
|
|
||||||
switch (conn->state) {
|
switch (conn->state) {
|
||||||
case XMPP_STATE_CONNECTING:
|
case XMPP_STATE_CONNECTING:
|
||||||
/* connect has been called and we're waiting for it to complete */
|
/* connect has been called and we're waiting for it to complete */
|
||||||
/* connection will give us write or error events */
|
/* connection will give us write or error events */
|
||||||
|
|
||||||
/* make sure the timeout hasn't expired */
|
/* make sure the timeout hasn't expired */
|
||||||
if (time_elapsed(conn->timeout_stamp, time_stamp()) <=
|
if (time_elapsed(conn->timeout_stamp, time_stamp()) <=
|
||||||
conn->connect_timeout)
|
conn->connect_timeout)
|
||||||
FD_SET(conn->sock, &wfds);
|
FD_SET(conn->sock, &wfds);
|
||||||
else {
|
else {
|
||||||
conn->error = ETIMEDOUT;
|
strophe_info(ctx, "xmpp", "Connection attempt timed out.");
|
||||||
xmpp_info(ctx, "xmpp", "Connection attempt timed out.");
|
ret = _connect_next(conn);
|
||||||
conn_disconnect(conn);
|
if (ret != 0) {
|
||||||
}
|
conn->error = ETIMEDOUT;
|
||||||
break;
|
conn_disconnect(conn);
|
||||||
case XMPP_STATE_CONNECTED:
|
} else {
|
||||||
FD_SET(conn->sock, &rfds);
|
FD_SET(conn->sock, &wfds);
|
||||||
break;
|
}
|
||||||
case XMPP_STATE_DISCONNECTED:
|
}
|
||||||
/* do nothing */
|
break;
|
||||||
default:
|
case XMPP_STATE_CONNECTED:
|
||||||
break;
|
FD_SET(conn->sock, &rfds);
|
||||||
}
|
if (conn->send_queue_len > 0)
|
||||||
|
FD_SET(conn->sock, &wfds);
|
||||||
|
break;
|
||||||
|
case XMPP_STATE_DISCONNECTED:
|
||||||
|
/* do nothing */
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check if there is something in the SSL buffer. */
|
/* Check if there is something in the SSL buffer. */
|
||||||
if (conn->tls) {
|
if (conn->tls)
|
||||||
tls_read_bytes += tls_pending(conn->tls);
|
tls_read_bytes += tls_pending(intf);
|
||||||
}
|
|
||||||
|
|
||||||
if (conn->sock > max) max = conn->sock;
|
if (conn->state != XMPP_STATE_DISCONNECTED && conn->sock > max)
|
||||||
|
max = conn->sock;
|
||||||
|
|
||||||
connitem = connitem->next;
|
connitem = connitem->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for events */
|
/* check for events */
|
||||||
ret = select(max + 1, &rfds, &wfds, NULL, &tv);
|
if (max > 0)
|
||||||
|
ret = select(max + 1, &rfds, &wfds, NULL, &tv);
|
||||||
|
else {
|
||||||
|
if (timeout > 0)
|
||||||
|
_sleep(timeout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* select errored */
|
/* select errored */
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (!sock_is_recoverable(sock_error()))
|
if (!sock_is_recoverable(NULL, sock_error(NULL)))
|
||||||
xmpp_error(ctx, "xmpp", "event watcher internal error %d",
|
strophe_error(ctx, "xmpp", "event watcher internal error %d",
|
||||||
sock_error());
|
sock_error(NULL));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* no events happened */
|
/* no events happened */
|
||||||
if (ret == 0 && tls_read_bytes == 0) return;
|
if (ret == 0 && tls_read_bytes == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* process events */
|
/* process events */
|
||||||
connitem = ctx->connlist;
|
connitem = ctx->connlist;
|
||||||
while (connitem) {
|
while (connitem) {
|
||||||
conn = connitem->conn;
|
conn = connitem->conn;
|
||||||
|
intf = &conn->intf;
|
||||||
|
|
||||||
switch (conn->state) {
|
switch (conn->state) {
|
||||||
case XMPP_STATE_CONNECTING:
|
case XMPP_STATE_CONNECTING:
|
||||||
if (FD_ISSET(conn->sock, &wfds)) {
|
if (FD_ISSET(conn->sock, &wfds)) {
|
||||||
/* connection complete */
|
/* connection complete */
|
||||||
|
|
||||||
/* check for error */
|
/* check for error */
|
||||||
if (sock_connect_error(conn->sock) != 0) {
|
ret = sock_connect_error(conn->sock);
|
||||||
/* connection failed */
|
if (ret != 0) {
|
||||||
xmpp_debug(ctx, "xmpp", "connection failed");
|
/* connection failed */
|
||||||
conn_disconnect(conn);
|
strophe_debug(ctx, "xmpp", "connection failed, error %d",
|
||||||
break;
|
ret);
|
||||||
}
|
ret = _connect_next(conn);
|
||||||
|
if (ret != 0) {
|
||||||
|
conn->error = ret;
|
||||||
|
conn_disconnect(conn);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
conn->state = XMPP_STATE_CONNECTED;
|
conn->state = XMPP_STATE_CONNECTED;
|
||||||
xmpp_debug(ctx, "xmpp", "connection successful");
|
strophe_debug(ctx, "xmpp", "connection successful");
|
||||||
|
conn_established(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case XMPP_STATE_CONNECTED:
|
||||||
|
if (FD_ISSET(conn->sock, &rfds) || intf->pending(intf)) {
|
||||||
|
|
||||||
/* send stream init */
|
ret = intf->read(intf, buf, STROPHE_MESSAGE_BUFFER_SIZE);
|
||||||
conn_open_stream(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
if (ret > 0) {
|
||||||
case XMPP_STATE_CONNECTED:
|
ret = parser_feed(conn->parser, buf, ret);
|
||||||
if (FD_ISSET(conn->sock, &rfds) || (conn->tls && tls_pending(conn->tls))) {
|
if (!ret) {
|
||||||
if (conn->tls) {
|
strophe_debug(ctx, "xmpp", "parse error [%s]", buf);
|
||||||
ret = tls_read(conn->tls, buf, 4096);
|
xmpp_send_error(conn, XMPP_SE_INVALID_XML,
|
||||||
} else {
|
"parse error");
|
||||||
ret = sock_read(conn->sock, buf, 4096);
|
}
|
||||||
}
|
} else {
|
||||||
|
int err = intf->get_error(intf);
|
||||||
|
if (!intf->error_is_recoverable(intf, err)) {
|
||||||
|
strophe_debug(ctx, "xmpp", "Unrecoverable error: %d.",
|
||||||
|
err);
|
||||||
|
conn->error = err;
|
||||||
|
conn_disconnect(conn);
|
||||||
|
} else if (!conn->tls) {
|
||||||
|
/* return of 0 means socket closed by server */
|
||||||
|
strophe_debug(ctx, "xmpp",
|
||||||
|
"Socket closed by remote host.");
|
||||||
|
conn->error = ECONNRESET;
|
||||||
|
conn_disconnect(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ret > 0) {
|
break;
|
||||||
ret = parser_feed(conn->parser, buf, ret);
|
case XMPP_STATE_DISCONNECTED:
|
||||||
if (!ret) {
|
/* do nothing */
|
||||||
/* parse error, we need to shut down */
|
default:
|
||||||
/* FIXME */
|
break;
|
||||||
xmpp_debug(ctx, "xmpp", "parse error, disconnecting");
|
}
|
||||||
conn_disconnect(conn);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (conn->tls) {
|
|
||||||
if (!tls_is_recoverable(tls_error(conn->tls)))
|
|
||||||
{
|
|
||||||
xmpp_debug(ctx, "xmpp", "Unrecoverable TLS error, %d.", tls_error(conn->tls));
|
|
||||||
conn->error = tls_error(conn->tls);
|
|
||||||
conn_disconnect(conn);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* return of 0 means socket closed by server */
|
|
||||||
xmpp_debug(ctx, "xmpp", "Socket closed by remote host.");
|
|
||||||
conn->error = ECONNRESET;
|
|
||||||
conn_disconnect(conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
connitem = connitem->next;
|
||||||
case XMPP_STATE_DISCONNECTED:
|
|
||||||
/* do nothing */
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
connitem = connitem->next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fire any ready handlers */
|
/* fire any ready handlers */
|
||||||
@@ -324,14 +358,18 @@ void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout)
|
|||||||
*/
|
*/
|
||||||
void xmpp_run(xmpp_ctx_t *ctx)
|
void xmpp_run(xmpp_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
if (ctx->loop_status != XMPP_LOOP_NOTSTARTED) return;
|
if (ctx->loop_status != XMPP_LOOP_NOTSTARTED)
|
||||||
|
return;
|
||||||
|
|
||||||
ctx->loop_status = XMPP_LOOP_RUNNING;
|
ctx->loop_status = XMPP_LOOP_RUNNING;
|
||||||
while (ctx->loop_status == XMPP_LOOP_RUNNING) {
|
while (ctx->loop_status == XMPP_LOOP_RUNNING) {
|
||||||
xmpp_run_once(ctx, DEFAULT_TIMEOUT);
|
xmpp_run_once(ctx, ctx->timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
xmpp_debug(ctx, "event", "Event loop completed.");
|
/* make it possible to start event loop again */
|
||||||
|
ctx->loop_status = XMPP_LOOP_NOTSTARTED;
|
||||||
|
|
||||||
|
strophe_debug(ctx, "event", "Event loop completed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stop the event loop.
|
/** Stop the event loop.
|
||||||
@@ -344,8 +382,20 @@ void xmpp_run(xmpp_ctx_t *ctx)
|
|||||||
*/
|
*/
|
||||||
void xmpp_stop(xmpp_ctx_t *ctx)
|
void xmpp_stop(xmpp_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
xmpp_debug(ctx, "event", "Stopping event loop.");
|
strophe_debug(ctx, "event", "Stopping event loop.");
|
||||||
|
|
||||||
if (ctx->loop_status == XMPP_LOOP_RUNNING)
|
if (ctx->loop_status == XMPP_LOOP_RUNNING)
|
||||||
ctx->loop_status = XMPP_LOOP_QUIT;
|
ctx->loop_status = XMPP_LOOP_QUIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the timeout to use when calling xmpp_run().
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param timeout the time to wait for events in milliseconds
|
||||||
|
*
|
||||||
|
* @ingroup EventLoop
|
||||||
|
*/
|
||||||
|
void xmpp_ctx_set_timeout(xmpp_ctx_t *ctx, unsigned long timeout)
|
||||||
|
{
|
||||||
|
ctx->timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|||||||
751
src/handler.c
751
src/handler.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* handler.c
|
/* handler.c
|
||||||
** strophe XMPP client library -- event handler management
|
** strophe XMPP client library -- event handler management
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -23,14 +21,42 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include <stdint.h>
|
|
||||||
#else
|
|
||||||
#include "ostypes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "strophe.h"
|
#include "strophe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "ostypes.h"
|
||||||
|
|
||||||
|
typedef int (*xmpp_void_handler)();
|
||||||
|
|
||||||
|
/* Remove item from the list pointed by head, but don't free it.
|
||||||
|
* There can be a situation when user's handler deletes another handler which
|
||||||
|
* is the previous in the list. handler_fire_stanza() and handler_fire_timed()
|
||||||
|
* must handle this situation correctly. Current function helps to avoid
|
||||||
|
* list corruption in described scenario.
|
||||||
|
*
|
||||||
|
* TODO Convert handler lists to double-linked lists. Current implementation
|
||||||
|
* works for O(n).
|
||||||
|
*/
|
||||||
|
static void _handler_item_remove(xmpp_handlist_t **head, xmpp_handlist_t *item)
|
||||||
|
{
|
||||||
|
while (*head) {
|
||||||
|
if (*head == item) {
|
||||||
|
*head = item->next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
head = &(*head)->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _free_handlist_item(xmpp_ctx_t *ctx, xmpp_handlist_t *item)
|
||||||
|
{
|
||||||
|
if (item->u.ns)
|
||||||
|
strophe_free(ctx, item->u.ns);
|
||||||
|
if (item->u.name)
|
||||||
|
strophe_free(ctx, item->u.name);
|
||||||
|
if (item->u.type)
|
||||||
|
strophe_free(ctx, item->u.type);
|
||||||
|
strophe_free(ctx, item);
|
||||||
|
}
|
||||||
|
|
||||||
/** Fire off all stanza handlers that match.
|
/** Fire off all stanza handlers that match.
|
||||||
* This function is called internally by the event loop whenever stanzas
|
* This function is called internally by the event loop whenever stanzas
|
||||||
@@ -39,41 +65,45 @@
|
|||||||
* @param conn a Strophe connection object
|
* @param conn a Strophe connection object
|
||||||
* @param stanza a Strophe stanza object
|
* @param stanza a Strophe stanza object
|
||||||
*/
|
*/
|
||||||
void handler_fire_stanza(xmpp_conn_t * const conn,
|
void handler_fire_stanza(xmpp_conn_t *conn, xmpp_stanza_t *stanza)
|
||||||
xmpp_stanza_t * const stanza)
|
|
||||||
{
|
{
|
||||||
xmpp_handlist_t *item, *prev;
|
xmpp_handlist_t *item, *next, *head, *head_old;
|
||||||
char *id, *ns, *name, *type;
|
const char *id, *ns, *name, *type;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* call id handlers */
|
/* call id handlers */
|
||||||
id = xmpp_stanza_get_id(stanza);
|
id = xmpp_stanza_get_id(stanza);
|
||||||
if (id) {
|
if (id) {
|
||||||
prev = NULL;
|
head = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
/* enable all added handlers */
|
||||||
while (item) {
|
for (item = head; item; item = item->next)
|
||||||
xmpp_handlist_t *next = item->next;
|
item->enabled = 1;
|
||||||
|
|
||||||
if (item->user_handler && !conn->authenticated) {
|
item = head;
|
||||||
item = next;
|
while (item) {
|
||||||
continue;
|
/* don't fire user handlers until stream negotiation has completed
|
||||||
}
|
and skip newly added handlers */
|
||||||
|
if ((item->user_handler && !conn->stream_negotiation_completed) ||
|
||||||
|
!item->enabled) {
|
||||||
|
item = item->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
|
ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
|
||||||
/* handler is one-shot, so delete it */
|
next = item->next;
|
||||||
if (prev)
|
if (!ret) {
|
||||||
prev->next = next;
|
/* handler is one-shot, so delete it */
|
||||||
else {
|
head_old = head;
|
||||||
hash_drop(conn->id_handlers, id);
|
_handler_item_remove(&head, item);
|
||||||
hash_add(conn->id_handlers, id, next);
|
if (head != head_old) {
|
||||||
}
|
/* replace old value */
|
||||||
xmpp_free(conn->ctx, item->id);
|
hash_add(conn->id_handlers, id, head);
|
||||||
xmpp_free(conn->ctx, item);
|
}
|
||||||
item = NULL;
|
strophe_free(conn->ctx, item->u.id);
|
||||||
}
|
strophe_free(conn->ctx, item);
|
||||||
if (item)
|
}
|
||||||
prev = item;
|
item = next;
|
||||||
item = next;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* call handlers */
|
/* call handlers */
|
||||||
@@ -83,49 +113,34 @@ void handler_fire_stanza(xmpp_conn_t * const conn,
|
|||||||
|
|
||||||
/* enable all added handlers */
|
/* enable all added handlers */
|
||||||
for (item = conn->handlers; item; item = item->next)
|
for (item = conn->handlers; item; item = item->next)
|
||||||
item->enabled = 1;
|
item->enabled = 1;
|
||||||
|
|
||||||
prev = NULL;
|
|
||||||
item = conn->handlers;
|
item = conn->handlers;
|
||||||
while (item) {
|
while (item) {
|
||||||
/* skip newly added handlers */
|
/* don't fire user handlers until stream negotiation has completed and
|
||||||
if (!item->enabled) {
|
skip newly added handlers */
|
||||||
prev = item;
|
if ((item->user_handler && !conn->stream_negotiation_completed) ||
|
||||||
item = item->next;
|
!item->enabled) {
|
||||||
continue;
|
item = item->next;
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* don't call user handlers until authentication succeeds */
|
next = item->next;
|
||||||
if (item->user_handler && !conn->authenticated) {
|
if ((!item->u.ns || (ns && strcmp(ns, item->u.ns) == 0) ||
|
||||||
prev = item;
|
xmpp_stanza_get_child_by_ns(stanza, item->u.ns)) &&
|
||||||
item = item->next;
|
(!item->u.name || (name && strcmp(name, item->u.name) == 0)) &&
|
||||||
continue;
|
(!item->u.type || (type && strcmp(type, item->u.type) == 0))) {
|
||||||
}
|
|
||||||
|
|
||||||
if ((!item->ns || (ns && strcmp(ns, item->ns) == 0) ||
|
ret = ((xmpp_handler)(item->handler))(conn, stanza, item->userdata);
|
||||||
xmpp_stanza_get_child_by_ns(stanza, item->ns)) &&
|
/* list may be changed during execution of a handler */
|
||||||
(!item->name || (name && strcmp(name, item->name) == 0)) &&
|
next = item->next;
|
||||||
(!item->type || (type && strcmp(type, item->type) == 0)))
|
if (!ret) {
|
||||||
if (!((xmpp_handler)(item->handler))(conn, stanza, item->userdata)) {
|
/* handler is one-shot, so delete it */
|
||||||
/* handler is one-shot, so delete it */
|
_handler_item_remove(&conn->handlers, item);
|
||||||
if (prev)
|
_free_handlist_item(conn->ctx, item);
|
||||||
prev->next = item->next;
|
}
|
||||||
else
|
}
|
||||||
conn->handlers = item->next;
|
item = next;
|
||||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
|
||||||
if (item->name) xmpp_free(conn->ctx, item->name);
|
|
||||||
if (item->type) xmpp_free(conn->ctx, item->type);
|
|
||||||
xmpp_free(conn->ctx, item);
|
|
||||||
item = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
prev = item;
|
|
||||||
item = item->next;
|
|
||||||
} else if (prev)
|
|
||||||
item = prev->next;
|
|
||||||
else
|
|
||||||
item = conn->handlers;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,60 +151,88 @@ void handler_fire_stanza(xmpp_conn_t * const conn,
|
|||||||
*
|
*
|
||||||
* @return the time in milliseconds until the next handler will be ready
|
* @return the time in milliseconds until the next handler will be ready
|
||||||
*/
|
*/
|
||||||
uint64_t handler_fire_timed(xmpp_ctx_t * const ctx)
|
uint64_t handler_fire_timed(xmpp_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
xmpp_connlist_t *connitem;
|
xmpp_connlist_t *connitem;
|
||||||
xmpp_handlist_t *handitem, *temp;
|
xmpp_handlist_t *item, *next;
|
||||||
int ret, fired;
|
xmpp_conn_t *conn;
|
||||||
uint64_t elapsed, min;
|
uint64_t elapsed, min;
|
||||||
|
uint64_t timestamp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
min = (uint64_t)(-1);
|
min = (uint64_t)(-1);
|
||||||
|
|
||||||
connitem = ctx->connlist;
|
connitem = ctx->connlist;
|
||||||
while (connitem) {
|
while (connitem) {
|
||||||
if (connitem->conn->state != XMPP_STATE_CONNECTED) {
|
conn = connitem->conn;
|
||||||
connitem = connitem->next;
|
if (conn->state != XMPP_STATE_CONNECTED) {
|
||||||
continue;
|
connitem = connitem->next;
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* enable all handlers that were added */
|
/* enable all handlers that were added */
|
||||||
for (handitem = connitem->conn->timed_handlers; handitem;
|
for (item = conn->timed_handlers; item; item = item->next)
|
||||||
handitem = handitem->next)
|
item->enabled = 1;
|
||||||
handitem->enabled = 1;
|
|
||||||
|
|
||||||
handitem = connitem->conn->timed_handlers;
|
item = conn->timed_handlers;
|
||||||
while (handitem) {
|
while (item) {
|
||||||
/* skip newly added handlers */
|
/* don't fire user handlers until stream negotiation has completed
|
||||||
if (!handitem->enabled) {
|
and skip newly added handlers */
|
||||||
handitem = handitem->next;
|
if ((item->user_handler && !conn->stream_negotiation_completed) ||
|
||||||
continue;
|
!item->enabled) {
|
||||||
}
|
item = item->next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* only fire user handlers after authentication */
|
next = item->next;
|
||||||
if (handitem->user_handler && !connitem->conn->authenticated) {
|
timestamp = time_stamp();
|
||||||
handitem = handitem->next;
|
elapsed = time_elapsed(item->u.last_stamp, timestamp);
|
||||||
continue;
|
if (elapsed >= item->u.period) {
|
||||||
}
|
/* fire! */
|
||||||
|
item->u.last_stamp = timestamp;
|
||||||
|
ret = ((xmpp_timed_handler)item->handler)(conn, item->userdata);
|
||||||
|
/* list may be changed during execution of a handler */
|
||||||
|
next = item->next;
|
||||||
|
if (!ret) {
|
||||||
|
/* delete handler if it returned false */
|
||||||
|
_handler_item_remove(&conn->timed_handlers, item);
|
||||||
|
strophe_free(ctx, item);
|
||||||
|
}
|
||||||
|
} else if (min > (item->u.period - elapsed))
|
||||||
|
min = item->u.period - elapsed;
|
||||||
|
|
||||||
fired = 0;
|
item = next;
|
||||||
elapsed = time_elapsed(handitem->last_stamp, time_stamp());
|
}
|
||||||
if (elapsed >= handitem->period) {
|
|
||||||
/* fire! */
|
|
||||||
fired = 1;
|
|
||||||
handitem->last_stamp = time_stamp();
|
|
||||||
ret = ((xmpp_timed_handler)handitem->handler)(connitem->conn, handitem->userdata);
|
|
||||||
} else if (min > (handitem->period - elapsed))
|
|
||||||
min = handitem->period - elapsed;
|
|
||||||
|
|
||||||
temp = handitem;
|
connitem = connitem->next;
|
||||||
handitem = handitem->next;
|
}
|
||||||
|
|
||||||
/* delete handler if it returned false */
|
/*
|
||||||
if (fired && !ret)
|
* Check timed handlers in context. These handlers fire periodically
|
||||||
xmpp_timed_handler_delete(connitem->conn, temp->handler);
|
* regardless of connections state.
|
||||||
}
|
* TODO Reduce copy-paste.
|
||||||
|
*/
|
||||||
|
item = ctx->timed_handlers;
|
||||||
|
while (item) {
|
||||||
|
next = item->next;
|
||||||
|
timestamp = time_stamp();
|
||||||
|
elapsed = time_elapsed(item->u.last_stamp, timestamp);
|
||||||
|
if (elapsed >= item->u.period) {
|
||||||
|
/* fire! */
|
||||||
|
item->u.last_stamp = timestamp;
|
||||||
|
ret =
|
||||||
|
((xmpp_global_timed_handler)item->handler)(ctx, item->userdata);
|
||||||
|
/* list may be changed during execution of a handler */
|
||||||
|
next = item->next;
|
||||||
|
if (!ret) {
|
||||||
|
/* delete handler if it returned false */
|
||||||
|
_handler_item_remove(&ctx->timed_handlers, item);
|
||||||
|
strophe_free(ctx, item);
|
||||||
|
}
|
||||||
|
} else if (min > (item->u.period - elapsed))
|
||||||
|
min = item->u.period - elapsed;
|
||||||
|
|
||||||
connitem = connitem->next;
|
item = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return min;
|
return min;
|
||||||
@@ -207,49 +250,64 @@ void handler_reset_timed(xmpp_conn_t *conn, int user_only)
|
|||||||
|
|
||||||
handitem = conn->timed_handlers;
|
handitem = conn->timed_handlers;
|
||||||
while (handitem) {
|
while (handitem) {
|
||||||
if ((user_only && handitem->user_handler) || !user_only)
|
if ((user_only && handitem->user_handler) || !user_only)
|
||||||
handitem->last_stamp = time_stamp();
|
handitem->u.last_stamp = time_stamp();
|
||||||
|
|
||||||
handitem = handitem->next;
|
handitem = handitem->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _timed_handler_add(xmpp_conn_t * const conn,
|
static void _timed_handler_add(xmpp_ctx_t *ctx,
|
||||||
xmpp_timed_handler handler,
|
xmpp_handlist_t **handlers_list,
|
||||||
const unsigned long period,
|
xmpp_void_handler handler,
|
||||||
void * const userdata,
|
unsigned long period,
|
||||||
const int user_handler)
|
void *userdata,
|
||||||
|
int user_handler)
|
||||||
{
|
{
|
||||||
xmpp_handlist_t *item, *tail;
|
xmpp_handlist_t *item;
|
||||||
|
|
||||||
/* check if handler is already in the list */
|
/* check if handler is already in the list */
|
||||||
for (item = conn->timed_handlers; item; item = item->next) {
|
for (item = *handlers_list; item; item = item->next) {
|
||||||
if (item->handler == (void *)handler)
|
if (item->handler == handler && item->userdata == userdata) {
|
||||||
break;
|
strophe_warn(ctx, "xmpp", "Timed handler already exists.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (item) return;
|
if (item)
|
||||||
|
return;
|
||||||
|
|
||||||
/* build new item */
|
/* build new item */
|
||||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
item = strophe_alloc(ctx, sizeof(xmpp_handlist_t));
|
||||||
if (!item) return;
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
item->user_handler = user_handler;
|
item->user_handler = user_handler;
|
||||||
item->handler = (void *)handler;
|
item->handler = handler;
|
||||||
item->userdata = userdata;
|
item->userdata = userdata;
|
||||||
item->enabled = 0;
|
item->enabled = 0;
|
||||||
item->next = NULL;
|
|
||||||
|
|
||||||
item->period = period;
|
item->u.period = period;
|
||||||
item->last_stamp = time_stamp();
|
item->u.last_stamp = time_stamp();
|
||||||
|
|
||||||
/* append item to list */
|
/* append item to list */
|
||||||
if (!conn->timed_handlers)
|
item->next = *handlers_list;
|
||||||
conn->timed_handlers = item;
|
*handlers_list = item;
|
||||||
else {
|
}
|
||||||
tail = conn->timed_handlers;
|
|
||||||
while (tail->next)
|
static void _timed_handler_delete(xmpp_ctx_t *ctx,
|
||||||
tail = tail->next;
|
xmpp_handlist_t **handlers_list,
|
||||||
tail->next = item;
|
xmpp_void_handler handler)
|
||||||
|
{
|
||||||
|
xmpp_handlist_t *item;
|
||||||
|
|
||||||
|
while (*handlers_list) {
|
||||||
|
item = *handlers_list;
|
||||||
|
if (item->handler == handler) {
|
||||||
|
*handlers_list = item->next;
|
||||||
|
strophe_free(ctx, item);
|
||||||
|
} else {
|
||||||
|
handlers_list = &item->next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,72 +318,56 @@ static void _timed_handler_add(xmpp_conn_t * const conn,
|
|||||||
*
|
*
|
||||||
* @ingroup Handlers
|
* @ingroup Handlers
|
||||||
*/
|
*/
|
||||||
void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
|
void xmpp_timed_handler_delete(xmpp_conn_t *conn, xmpp_timed_handler handler)
|
||||||
xmpp_timed_handler handler)
|
|
||||||
{
|
{
|
||||||
xmpp_handlist_t *item, *prev;
|
_timed_handler_delete(conn->ctx, &conn->timed_handlers, handler);
|
||||||
|
|
||||||
if (!conn->timed_handlers) return;
|
|
||||||
|
|
||||||
prev = NULL;
|
|
||||||
item = conn->timed_handlers;
|
|
||||||
while (item) {
|
|
||||||
if (item->handler == (void *)handler)
|
|
||||||
break;
|
|
||||||
prev = item;
|
|
||||||
item = item->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
if (prev)
|
|
||||||
prev->next = item->next;
|
|
||||||
else
|
|
||||||
conn->timed_handlers = item->next;
|
|
||||||
|
|
||||||
xmpp_free(conn->ctx, item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _id_handler_add(xmpp_conn_t * const conn,
|
static void _id_handler_add(xmpp_conn_t *conn,
|
||||||
xmpp_handler handler,
|
xmpp_handler handler,
|
||||||
const char * const id,
|
const char *id,
|
||||||
void * const userdata, int user_handler)
|
void *userdata,
|
||||||
|
int user_handler)
|
||||||
{
|
{
|
||||||
xmpp_handlist_t *item, *tail;
|
xmpp_handlist_t *item, *tail;
|
||||||
|
|
||||||
/* check if handler is already in the list */
|
/* check if handler is already in the list */
|
||||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||||
while (item) {
|
while (item) {
|
||||||
if (item->handler == (void *)handler)
|
if (item->handler == handler && item->userdata == userdata) {
|
||||||
break;
|
strophe_warn(conn->ctx, "xmpp", "Id handler already exists.");
|
||||||
item = item->next;
|
break;
|
||||||
|
}
|
||||||
|
item = item->next;
|
||||||
}
|
}
|
||||||
if (item) return;
|
if (item)
|
||||||
|
return;
|
||||||
|
|
||||||
/* build new item */
|
/* build new item */
|
||||||
item = xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
item = strophe_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||||
if (!item) return;
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
item->user_handler = user_handler;
|
item->user_handler = user_handler;
|
||||||
item->handler = (void *)handler;
|
item->handler = handler;
|
||||||
item->userdata = userdata;
|
item->userdata = userdata;
|
||||||
item->enabled = 0;
|
item->enabled = 0;
|
||||||
item->next = NULL;
|
item->next = NULL;
|
||||||
|
|
||||||
item->id = xmpp_strdup(conn->ctx, id);
|
item->u.id = strophe_strdup(conn->ctx, id);
|
||||||
if (!item->id) {
|
if (!item->u.id) {
|
||||||
xmpp_free(conn->ctx, item);
|
strophe_free(conn->ctx, item);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* put on list in hash table */
|
/* put on list in hash table */
|
||||||
tail = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
tail = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||||
if (!tail)
|
if (!tail)
|
||||||
hash_add(conn->id_handlers, id, item);
|
hash_add(conn->id_handlers, id, item);
|
||||||
else {
|
else {
|
||||||
while (tail->next)
|
while (tail->next)
|
||||||
tail = tail->next;
|
tail = tail->next;
|
||||||
tail->next = item;
|
tail->next = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,99 +379,102 @@ static void _id_handler_add(xmpp_conn_t * const conn,
|
|||||||
*
|
*
|
||||||
* @ingroup Handlers
|
* @ingroup Handlers
|
||||||
*/
|
*/
|
||||||
void xmpp_id_handler_delete(xmpp_conn_t * const conn,
|
void xmpp_id_handler_delete(xmpp_conn_t *conn,
|
||||||
xmpp_handler handler,
|
xmpp_handler handler,
|
||||||
const char * const id)
|
const char *id)
|
||||||
{
|
{
|
||||||
xmpp_handlist_t *item, *prev;
|
xmpp_handlist_t *item, *prev, *next;
|
||||||
|
|
||||||
prev = NULL;
|
prev = NULL;
|
||||||
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
item = (xmpp_handlist_t *)hash_get(conn->id_handlers, id);
|
||||||
if (!item) return;
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
while (item) {
|
while (item) {
|
||||||
if (item->handler == (void *)handler)
|
next = item->next;
|
||||||
break;
|
|
||||||
|
|
||||||
prev = item;
|
if (item->handler == handler) {
|
||||||
item = item->next;
|
if (prev)
|
||||||
}
|
prev->next = next;
|
||||||
|
else {
|
||||||
|
hash_drop(conn->id_handlers, id);
|
||||||
|
hash_add(conn->id_handlers, id, next);
|
||||||
|
}
|
||||||
|
|
||||||
if (item) {
|
strophe_free(conn->ctx, item->u.id);
|
||||||
if (prev)
|
strophe_free(conn->ctx, item);
|
||||||
prev->next = item->next;
|
item = next;
|
||||||
else {
|
} else {
|
||||||
hash_drop(conn->id_handlers, id);
|
prev = item;
|
||||||
hash_add(conn->id_handlers, id, item->next);
|
item = next;
|
||||||
}
|
}
|
||||||
xmpp_free(conn->ctx, item->id);
|
|
||||||
xmpp_free(conn->ctx, item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _dup_string(xmpp_ctx_t *ctx, const char *src, char **dest)
|
||||||
|
{
|
||||||
|
if (src) {
|
||||||
|
*dest = strophe_strdup(ctx, src);
|
||||||
|
if (!(*dest))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* add a stanza handler */
|
/* add a stanza handler */
|
||||||
static void _handler_add(xmpp_conn_t * const conn,
|
static void _handler_add(xmpp_conn_t *conn,
|
||||||
xmpp_handler handler,
|
xmpp_handler handler,
|
||||||
const char * const ns,
|
const char *ns,
|
||||||
const char * const name,
|
const char *name,
|
||||||
const char * const type,
|
const char *type,
|
||||||
void * const userdata, int user_handler)
|
void *userdata,
|
||||||
|
int user_handler)
|
||||||
{
|
{
|
||||||
xmpp_handlist_t *item, *tail;
|
xmpp_handlist_t *item, *tail;
|
||||||
|
|
||||||
/* check if handler already in list */
|
/* check if handler already in list */
|
||||||
for (item = conn->handlers; item; item = item->next) {
|
for (item = conn->handlers; item; item = item->next) {
|
||||||
if (item->handler == (void *)handler)
|
/* same handler function can process different stanzas and
|
||||||
break;
|
distinguish them according to userdata. */
|
||||||
|
if (item->handler == handler && item->userdata == userdata) {
|
||||||
|
strophe_warn(conn->ctx, "xmpp", "Stanza handler already exists.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (item) return;
|
if (item)
|
||||||
|
return;
|
||||||
|
|
||||||
/* build new item */
|
/* build new item */
|
||||||
item = (xmpp_handlist_t *)xmpp_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
item = (xmpp_handlist_t *)strophe_alloc(conn->ctx, sizeof(xmpp_handlist_t));
|
||||||
if (!item) return;
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(item, 0, sizeof(*item));
|
||||||
item->user_handler = user_handler;
|
item->user_handler = user_handler;
|
||||||
item->handler = (void *)handler;
|
item->handler = handler;
|
||||||
item->userdata = userdata;
|
item->userdata = userdata;
|
||||||
item->enabled = 0;
|
|
||||||
item->next = NULL;
|
|
||||||
|
|
||||||
if (ns) {
|
if (_dup_string(conn->ctx, ns, &item->u.ns))
|
||||||
item->ns = xmpp_strdup(conn->ctx, ns);
|
goto error_out;
|
||||||
if (!item->ns) {
|
if (_dup_string(conn->ctx, name, &item->u.name))
|
||||||
xmpp_free(conn->ctx, item);
|
goto error_out;
|
||||||
return;
|
if (_dup_string(conn->ctx, type, &item->u.type))
|
||||||
}
|
goto error_out;
|
||||||
} else
|
|
||||||
item->ns = NULL;
|
|
||||||
if (name) {
|
|
||||||
item->name = xmpp_strdup(conn->ctx, name);
|
|
||||||
if (!item->name) {
|
|
||||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
|
||||||
xmpp_free(conn->ctx, item);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
item->name = NULL;
|
|
||||||
if (type) {
|
|
||||||
item->type = xmpp_strdup(conn->ctx, type);
|
|
||||||
if (!item->type) {
|
|
||||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
|
||||||
if (item->name) xmpp_free(conn->ctx, item->name);
|
|
||||||
xmpp_free(conn->ctx, item);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
item->type = NULL;
|
|
||||||
|
|
||||||
/* append to list */
|
/* append to list */
|
||||||
if (!conn->handlers)
|
if (!conn->handlers)
|
||||||
conn->handlers = item;
|
conn->handlers = item;
|
||||||
else {
|
else {
|
||||||
tail = conn->handlers;
|
tail = conn->handlers;
|
||||||
while (tail->next)
|
while (tail->next)
|
||||||
tail = tail->next;
|
tail = tail->next;
|
||||||
tail->next = item;
|
tail->next = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
error_out:
|
||||||
|
_free_handlist_item(conn->ctx, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete a stanza handler.
|
/** Delete a stanza handler.
|
||||||
@@ -439,33 +484,28 @@ static void _handler_add(xmpp_conn_t * const conn,
|
|||||||
*
|
*
|
||||||
* @ingroup Handlers
|
* @ingroup Handlers
|
||||||
*/
|
*/
|
||||||
void xmpp_handler_delete(xmpp_conn_t * const conn,
|
void xmpp_handler_delete(xmpp_conn_t *conn, xmpp_handler handler)
|
||||||
xmpp_handler handler)
|
|
||||||
{
|
{
|
||||||
xmpp_handlist_t *prev, *item;
|
xmpp_handlist_t *prev, *item;
|
||||||
|
|
||||||
if (!conn->handlers) return;
|
if (!conn->handlers)
|
||||||
|
return;
|
||||||
|
|
||||||
prev = NULL;
|
prev = NULL;
|
||||||
item = conn->handlers;
|
item = conn->handlers;
|
||||||
while (item) {
|
while (item) {
|
||||||
if (item->handler == (void *)handler)
|
if (item->handler == handler) {
|
||||||
break;
|
if (prev)
|
||||||
|
prev->next = item->next;
|
||||||
|
else
|
||||||
|
conn->handlers = item->next;
|
||||||
|
|
||||||
prev = item;
|
_free_handlist_item(conn->ctx, item);
|
||||||
item = item->next;
|
item = prev ? prev->next : conn->handlers;
|
||||||
}
|
} else {
|
||||||
|
prev = item;
|
||||||
if (item) {
|
item = item->next;
|
||||||
if (prev)
|
}
|
||||||
prev->next = item->next;
|
|
||||||
else
|
|
||||||
conn->handlers = item->next;
|
|
||||||
|
|
||||||
if (item->ns) xmpp_free(conn->ctx, item->ns);
|
|
||||||
if (item->name) xmpp_free(conn->ctx, item->name);
|
|
||||||
if (item->type) xmpp_free(conn->ctx, item->type);
|
|
||||||
xmpp_free(conn->ctx, item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,12 +525,13 @@ void xmpp_handler_delete(xmpp_conn_t * const conn,
|
|||||||
*
|
*
|
||||||
* @ingroup Handlers
|
* @ingroup Handlers
|
||||||
*/
|
*/
|
||||||
void xmpp_timed_handler_add(xmpp_conn_t * const conn,
|
void xmpp_timed_handler_add(xmpp_conn_t *conn,
|
||||||
xmpp_timed_handler handler,
|
xmpp_timed_handler handler,
|
||||||
const unsigned long period,
|
unsigned long period,
|
||||||
void * const userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
_timed_handler_add(conn, handler, period, userdata, 1);
|
_timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
|
||||||
|
userdata, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add a timed system handler.
|
/** Add a timed system handler.
|
||||||
@@ -502,12 +543,13 @@ void xmpp_timed_handler_add(xmpp_conn_t * const conn,
|
|||||||
* @param period the time in milliseconds between firings
|
* @param period the time in milliseconds between firings
|
||||||
* @param userdata an opaque data pointer that will be passed to the handler
|
* @param userdata an opaque data pointer that will be passed to the handler
|
||||||
*/
|
*/
|
||||||
void handler_add_timed(xmpp_conn_t * const conn,
|
void handler_add_timed(xmpp_conn_t *conn,
|
||||||
xmpp_timed_handler handler,
|
xmpp_timed_handler handler,
|
||||||
const unsigned long period,
|
unsigned long period,
|
||||||
void * const userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
_timed_handler_add(conn, handler, period, userdata, 0);
|
_timed_handler_add(conn->ctx, &conn->timed_handlers, handler, period,
|
||||||
|
userdata, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add an id based stanza handler.
|
/** Add an id based stanza handler.
|
||||||
@@ -526,10 +568,10 @@ void handler_add_timed(xmpp_conn_t * const conn,
|
|||||||
*
|
*
|
||||||
* @ingroup Handlers
|
* @ingroup Handlers
|
||||||
*/
|
*/
|
||||||
void xmpp_id_handler_add(xmpp_conn_t * const conn,
|
void xmpp_id_handler_add(xmpp_conn_t *conn,
|
||||||
xmpp_handler handler,
|
xmpp_handler handler,
|
||||||
const char * const id,
|
const char *id,
|
||||||
void * const userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
_id_handler_add(conn, handler, id, userdata, 1);
|
_id_handler_add(conn, handler, id, userdata, 1);
|
||||||
}
|
}
|
||||||
@@ -543,10 +585,10 @@ void xmpp_id_handler_add(xmpp_conn_t * const conn,
|
|||||||
* @param id a string with the id
|
* @param id a string with the id
|
||||||
* @param userdata an opaque data pointer that will be passed to the handler
|
* @param userdata an opaque data pointer that will be passed to the handler
|
||||||
*/
|
*/
|
||||||
void handler_add_id(xmpp_conn_t * const conn,
|
void handler_add_id(xmpp_conn_t *conn,
|
||||||
xmpp_handler handler,
|
xmpp_handler handler,
|
||||||
const char * const id,
|
const char *id,
|
||||||
void * const userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
_id_handler_add(conn, handler, id, userdata, 0);
|
_id_handler_add(conn, handler, id, userdata, 0);
|
||||||
}
|
}
|
||||||
@@ -573,12 +615,12 @@ void handler_add_id(xmpp_conn_t * const conn,
|
|||||||
*
|
*
|
||||||
* @ingroup Handlers
|
* @ingroup Handlers
|
||||||
*/
|
*/
|
||||||
void xmpp_handler_add(xmpp_conn_t * const conn,
|
void xmpp_handler_add(xmpp_conn_t *conn,
|
||||||
xmpp_handler handler,
|
xmpp_handler handler,
|
||||||
const char * const ns,
|
const char *ns,
|
||||||
const char * const name,
|
const char *name,
|
||||||
const char * const type,
|
const char *type,
|
||||||
void * const userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
_handler_add(conn, handler, ns, name, type, userdata, 1);
|
_handler_add(conn, handler, ns, name, type, userdata, 1);
|
||||||
}
|
}
|
||||||
@@ -594,12 +636,129 @@ void xmpp_handler_add(xmpp_conn_t * const conn,
|
|||||||
* @param type a string with the 'type' attribute value to match
|
* @param type a string with the 'type' attribute value to match
|
||||||
* @param userdata an opaque data pointer that will be passed to the handler
|
* @param userdata an opaque data pointer that will be passed to the handler
|
||||||
*/
|
*/
|
||||||
void handler_add(xmpp_conn_t * const conn,
|
void handler_add(xmpp_conn_t *conn,
|
||||||
xmpp_handler handler,
|
xmpp_handler handler,
|
||||||
const char * const ns,
|
const char *ns,
|
||||||
const char * const name,
|
const char *name,
|
||||||
const char * const type,
|
const char *type,
|
||||||
void * const userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
_handler_add(conn, handler, ns, name, type, userdata, 0);
|
_handler_add(conn, handler, ns, name, type, userdata, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Delete all system handlers.
|
||||||
|
* This function is used to reset conn object before re-connecting.
|
||||||
|
*
|
||||||
|
* @param conn a Strophe connection object
|
||||||
|
*/
|
||||||
|
void handler_system_delete_all(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
xmpp_handlist_t *item, *next, *head, *head_old;
|
||||||
|
hash_iterator_t *iter;
|
||||||
|
const char *key, *key2;
|
||||||
|
|
||||||
|
/* TODO unify all kinds of handlers and avoid copy-paste below */
|
||||||
|
|
||||||
|
item = conn->handlers;
|
||||||
|
while (item) {
|
||||||
|
if (!item->user_handler) {
|
||||||
|
next = item->next;
|
||||||
|
_handler_item_remove(&conn->handlers, item);
|
||||||
|
_free_handlist_item(conn->ctx, item);
|
||||||
|
item = next;
|
||||||
|
} else
|
||||||
|
item = item->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
item = conn->timed_handlers;
|
||||||
|
while (item) {
|
||||||
|
if (!item->user_handler) {
|
||||||
|
next = item->next;
|
||||||
|
_handler_item_remove(&conn->timed_handlers, item);
|
||||||
|
strophe_free(conn->ctx, item);
|
||||||
|
item = next;
|
||||||
|
} else
|
||||||
|
item = item->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = hash_iter_new(conn->id_handlers);
|
||||||
|
key = iter == NULL ? NULL : hash_iter_next(iter);
|
||||||
|
while (key != NULL) {
|
||||||
|
head = head_old = (xmpp_handlist_t *)hash_get(conn->id_handlers, key);
|
||||||
|
item = head;
|
||||||
|
while (item) {
|
||||||
|
if (!item->user_handler) {
|
||||||
|
next = item->next;
|
||||||
|
_handler_item_remove(&head, item);
|
||||||
|
strophe_free(conn->ctx, item->u.id);
|
||||||
|
strophe_free(conn->ctx, item);
|
||||||
|
item = next;
|
||||||
|
} else
|
||||||
|
item = item->next;
|
||||||
|
}
|
||||||
|
/* Hash table implementation is not perfect, so we need to find next
|
||||||
|
key before dropping current one. Otherwise, we will get access to
|
||||||
|
freed memory. */
|
||||||
|
key2 = hash_iter_next(iter);
|
||||||
|
if (head != head_old) {
|
||||||
|
/* hash_add() replaces value if the key exists */
|
||||||
|
if (head != NULL)
|
||||||
|
hash_add(conn->id_handlers, key, head);
|
||||||
|
else
|
||||||
|
hash_drop(conn->id_handlers, key);
|
||||||
|
}
|
||||||
|
key = key2;
|
||||||
|
}
|
||||||
|
if (iter)
|
||||||
|
hash_iter_release(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add a global timed handler.
|
||||||
|
* The handler will fire for the first time once the period has elapsed,
|
||||||
|
* and continue firing regularly after that. Strophe will try its best
|
||||||
|
* to fire handlers as close to the period times as it can, but accuracy
|
||||||
|
* will vary depending on the resolution of the event loop.
|
||||||
|
*
|
||||||
|
* The main difference between global and ordinary handlers:
|
||||||
|
* - Ordinary handler is related to a connection, fires only when the
|
||||||
|
* connection is in connected state and is removed once the connection is
|
||||||
|
* destroyed.
|
||||||
|
* - Global handler fires regardless of connections state and is related to
|
||||||
|
* a Strophe context.
|
||||||
|
*
|
||||||
|
* The handler is executed in context of the respective event loop.
|
||||||
|
*
|
||||||
|
* If the handler function returns true, it will be kept, and if it
|
||||||
|
* returns false, it will be deleted from the list of handlers.
|
||||||
|
*
|
||||||
|
* Notice, the same handler pointer may be added multiple times with different
|
||||||
|
* userdata pointers. However, xmpp_global_timed_handler_delete() deletes
|
||||||
|
* all occurrences.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param handler a function pointer to a timed handler
|
||||||
|
* @param period the time in milliseconds between firings
|
||||||
|
* @param userdata an opaque data pointer that will be passed to the handler
|
||||||
|
*
|
||||||
|
* @ingroup Handlers
|
||||||
|
*/
|
||||||
|
void xmpp_global_timed_handler_add(xmpp_ctx_t *ctx,
|
||||||
|
xmpp_global_timed_handler handler,
|
||||||
|
unsigned long period,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
_timed_handler_add(ctx, &ctx->timed_handlers, handler, period, userdata, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Delete a global timed handler.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param handler function pointer to the handler
|
||||||
|
*
|
||||||
|
* @ingroup Handlers
|
||||||
|
*/
|
||||||
|
void xmpp_global_timed_handler_delete(xmpp_ctx_t *ctx,
|
||||||
|
xmpp_global_timed_handler handler)
|
||||||
|
{
|
||||||
|
_timed_handler_delete(ctx, &ctx->timed_handlers, handler);
|
||||||
|
}
|
||||||
|
|||||||
274
src/hash.c
274
src/hash.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* hash.c
|
/* hash.c
|
||||||
** strophe XMPP client library -- hash table implementation
|
** strophe XMPP client library -- hash table implementation
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -49,163 +47,175 @@ struct _hash_iterator_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** allocate and initialize a new hash table */
|
/** allocate and initialize a new hash table */
|
||||||
hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
|
hash_t *hash_new(xmpp_ctx_t *ctx, int size, hash_free_func free_func)
|
||||||
hash_free_func free)
|
|
||||||
{
|
{
|
||||||
hash_t *result = NULL;
|
hash_t *result = NULL;
|
||||||
|
|
||||||
result = xmpp_alloc(ctx, sizeof(hash_t));
|
result = strophe_alloc(ctx, sizeof(hash_t));
|
||||||
if (result != NULL) {
|
if (result != NULL) {
|
||||||
result->entries = xmpp_alloc(ctx, size * sizeof(hashentry_t *));
|
result->entries = strophe_alloc(ctx, size * sizeof(hashentry_t *));
|
||||||
if (result->entries == NULL) {
|
if (result->entries == NULL) {
|
||||||
xmpp_free(ctx, result);
|
strophe_free(ctx, result);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memset(result->entries, 0, size * sizeof(hashentry_t *));
|
memset(result->entries, 0, size * sizeof(hashentry_t *));
|
||||||
result->length = size;
|
result->length = size;
|
||||||
|
|
||||||
result->ctx = ctx;
|
result->ctx = ctx;
|
||||||
result->free = free;
|
result->free = free_func;
|
||||||
result->num_keys = 0;
|
result->num_keys = 0;
|
||||||
/* give the caller a reference */
|
/* give the caller a reference */
|
||||||
result->ref = 1;
|
result->ref = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** obtain a new reference to an existing hash table */
|
/** obtain a new reference to an existing hash table */
|
||||||
hash_t *hash_clone(hash_t * const table)
|
hash_t *hash_clone(hash_t *table)
|
||||||
{
|
{
|
||||||
table->ref++;
|
table->ref++;
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** release a hash table that is no longer needed */
|
/** release a hash table that is no longer needed */
|
||||||
void hash_release(hash_t * const table)
|
void hash_release(hash_t *table)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx = table->ctx;
|
xmpp_ctx_t *ctx = table->ctx;
|
||||||
hashentry_t *entry, *next;
|
hashentry_t *entry, *next;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (table->ref > 1)
|
if (table->ref > 1)
|
||||||
table->ref--;
|
table->ref--;
|
||||||
else {
|
else {
|
||||||
for (i = 0; i < table->length; i++) {
|
for (i = 0; i < table->length; i++) {
|
||||||
entry = table->entries[i];
|
entry = table->entries[i];
|
||||||
while (entry != NULL) {
|
while (entry != NULL) {
|
||||||
next = entry->next;
|
next = entry->next;
|
||||||
xmpp_free(ctx, entry->key);
|
strophe_free(ctx, entry->key);
|
||||||
if (table->free) table->free(ctx, entry->value);
|
if (table->free)
|
||||||
xmpp_free(ctx, entry);
|
table->free(ctx, entry->value);
|
||||||
entry = next;
|
strophe_free(ctx, entry);
|
||||||
}
|
entry = next;
|
||||||
}
|
}
|
||||||
xmpp_free(ctx, table->entries);
|
}
|
||||||
xmpp_free(ctx, table);
|
strophe_free(ctx, table->entries);
|
||||||
|
strophe_free(ctx, table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** hash a key for our table lookup */
|
/** hash a key for our table lookup */
|
||||||
static int _hash_key(hash_t *table, const char *key)
|
static int _hash_key(hash_t *table, const char *key)
|
||||||
{
|
{
|
||||||
int hash = 0;
|
unsigned hash = 0;
|
||||||
int shift = 0;
|
unsigned shift = 0;
|
||||||
const char *c = key;
|
const unsigned char *c = (const unsigned char *)key;
|
||||||
|
|
||||||
while (*c != '\0') {
|
while (*c != 0) {
|
||||||
/* assume 32 bit ints */
|
/* assume 32 bit ints */
|
||||||
hash ^= ((int)*c++ << shift);
|
hash ^= ((unsigned)*c++ << shift);
|
||||||
shift += 8;
|
shift += 8;
|
||||||
if (shift > 24) shift = 0;
|
if (shift > 24)
|
||||||
}
|
shift = 0;
|
||||||
|
}
|
||||||
|
return hash % (unsigned)table->length;
|
||||||
|
}
|
||||||
|
|
||||||
return hash % table->length;
|
hashentry_t *_hash_entry_find(hash_t *table, const char *key)
|
||||||
|
{
|
||||||
|
hashentry_t *entry;
|
||||||
|
int table_index = _hash_key(table, key);
|
||||||
|
|
||||||
|
/* look up the hash entry */
|
||||||
|
entry = table->entries[table_index];
|
||||||
|
while (entry != NULL) {
|
||||||
|
/* traverse the linked list looking for the key */
|
||||||
|
if (!strcmp(key, entry->key)) {
|
||||||
|
/* match */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** add a key, value pair to a hash table.
|
/** add a key, value pair to a hash table.
|
||||||
* each key can appear only once; the value of any
|
* each key can appear only once; the value of any
|
||||||
* identical key will be replaced
|
* identical key will be replaced
|
||||||
*/
|
*/
|
||||||
int hash_add(hash_t *table, const char * const key, void *data)
|
int hash_add(hash_t *table, const char *key, void *data)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx = table->ctx;
|
xmpp_ctx_t *ctx = table->ctx;
|
||||||
hashentry_t *entry = NULL;
|
hashentry_t *entry = NULL;
|
||||||
int index = _hash_key(table, key);
|
int table_index = _hash_key(table, key);
|
||||||
|
|
||||||
/* drop existing entry, if any */
|
/* find and replace existing entry, if any */
|
||||||
hash_drop(table, key);
|
entry = _hash_entry_find(table, key);
|
||||||
|
|
||||||
/* allocate and fill a new entry */
|
if (entry == NULL) {
|
||||||
entry = xmpp_alloc(ctx, sizeof(hashentry_t));
|
/* allocate and fill a new entry */
|
||||||
if (!entry) return -1;
|
entry = strophe_alloc(ctx, sizeof(hashentry_t));
|
||||||
entry->key = xmpp_strdup(ctx, key);
|
if (!entry)
|
||||||
if (!entry->key) {
|
return -1;
|
||||||
xmpp_free(ctx, entry);
|
entry->key = strophe_strdup(ctx, key);
|
||||||
return -1;
|
if (!entry->key) {
|
||||||
}
|
strophe_free(ctx, entry);
|
||||||
entry->value = data;
|
return -1;
|
||||||
/* insert ourselves in the linked list */
|
}
|
||||||
/* TODO: this leaks duplicate keys */
|
/* insert ourselves in the linked list */
|
||||||
entry->next = table->entries[index];
|
entry->next = table->entries[table_index];
|
||||||
table->entries[index] = entry;
|
table->entries[table_index] = entry;
|
||||||
table->num_keys++;
|
table->num_keys++;
|
||||||
|
} else {
|
||||||
|
if (table->free)
|
||||||
|
table->free(ctx, entry->value);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
entry->value = data;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** look up a key in a hash table */
|
/** look up a key in a hash table */
|
||||||
void *hash_get(hash_t *table, const char *key)
|
void *hash_get(hash_t *table, const char *key)
|
||||||
{
|
{
|
||||||
hashentry_t *entry;
|
hashentry_t *entry;
|
||||||
int index = _hash_key(table, key);
|
|
||||||
void *result = NULL;
|
|
||||||
|
|
||||||
/* look up the hash entry */
|
entry = _hash_entry_find(table, key);
|
||||||
entry = table->entries[index];
|
return entry == NULL ? NULL : entry->value;
|
||||||
while (entry != NULL) {
|
|
||||||
/* traverse the linked list looking for the key */
|
|
||||||
if (!strcmp(key, entry->key)) {
|
|
||||||
/* match */
|
|
||||||
result = entry->value;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
entry = entry->next;
|
|
||||||
}
|
|
||||||
/* no match */
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** delete a key from a hash table */
|
/** delete a key from a hash table */
|
||||||
int hash_drop(hash_t *table, const char *key)
|
int hash_drop(hash_t *table, const char *key)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx = table->ctx;
|
xmpp_ctx_t *ctx = table->ctx;
|
||||||
hashentry_t *entry, *prev;
|
hashentry_t *entry, *prev;
|
||||||
int index = _hash_key(table, key);
|
int table_index = _hash_key(table, key);
|
||||||
|
|
||||||
/* look up the hash entry */
|
/* look up the hash entry */
|
||||||
entry = table->entries[index];
|
entry = table->entries[table_index];
|
||||||
prev = NULL;
|
prev = NULL;
|
||||||
while (entry != NULL) {
|
while (entry != NULL) {
|
||||||
/* traverse the linked list looking for the key */
|
/* traverse the linked list looking for the key */
|
||||||
if (!strcmp(key, entry->key)) {
|
if (!strcmp(key, entry->key)) {
|
||||||
/* match, remove the entry */
|
/* match, remove the entry */
|
||||||
xmpp_free(ctx, entry->key);
|
strophe_free(ctx, entry->key);
|
||||||
if (table->free) table->free(ctx, entry->value);
|
if (table->free)
|
||||||
if (prev == NULL) {
|
table->free(ctx, entry->value);
|
||||||
table->entries[index] = entry->next;
|
if (prev == NULL) {
|
||||||
} else {
|
table->entries[table_index] = entry->next;
|
||||||
prev->next = entry->next;
|
} else {
|
||||||
}
|
prev->next = entry->next;
|
||||||
xmpp_free(ctx, entry);
|
}
|
||||||
table->num_keys--;
|
strophe_free(ctx, entry);
|
||||||
return 0;
|
table->num_keys--;
|
||||||
}
|
return 0;
|
||||||
prev = entry;
|
}
|
||||||
entry = entry->next;
|
prev = entry;
|
||||||
}
|
entry = entry->next;
|
||||||
/* no match */
|
}
|
||||||
return -1;
|
/* no match */
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hash_num_keys(hash_t *table)
|
int hash_num_keys(hash_t *table)
|
||||||
@@ -219,18 +229,17 @@ hash_iterator_t *hash_iter_new(hash_t *table)
|
|||||||
xmpp_ctx_t *ctx = table->ctx;
|
xmpp_ctx_t *ctx = table->ctx;
|
||||||
hash_iterator_t *iter;
|
hash_iterator_t *iter;
|
||||||
|
|
||||||
iter = xmpp_alloc(ctx, sizeof(*iter));
|
iter = strophe_alloc(ctx, sizeof(*iter));
|
||||||
if (iter != NULL) {
|
if (iter != NULL) {
|
||||||
iter->ref = 1;
|
iter->ref = 1;
|
||||||
iter->table = hash_clone(table);
|
iter->table = hash_clone(table);
|
||||||
iter->entry = NULL;
|
iter->entry = NULL;
|
||||||
iter->index = -1;
|
iter->index = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** release an iterator that is no longer needed */
|
/** release an iterator that is no longer needed */
|
||||||
void hash_iter_release(hash_iterator_t *iter)
|
void hash_iter_release(hash_iterator_t *iter)
|
||||||
{
|
{
|
||||||
@@ -238,41 +247,42 @@ void hash_iter_release(hash_iterator_t *iter)
|
|||||||
|
|
||||||
iter->ref--;
|
iter->ref--;
|
||||||
|
|
||||||
if (iter->ref <= 0) {
|
if (iter->ref == 0) { // ref is unsigned!!!
|
||||||
hash_release(iter->table);
|
hash_release(iter->table);
|
||||||
xmpp_free(ctx, iter);
|
strophe_free(ctx, iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** return the next hash table key from the iterator.
|
/** return the next hash table key from the iterator.
|
||||||
the returned key should not be freed */
|
the returned key should not be freed */
|
||||||
const char * hash_iter_next(hash_iterator_t *iter)
|
const char *hash_iter_next(hash_iterator_t *iter)
|
||||||
{
|
{
|
||||||
hash_t *table = iter->table;
|
hash_t *table = iter->table;
|
||||||
hashentry_t *entry = iter->entry;
|
hashentry_t *entry = iter->entry;
|
||||||
int i = iter->index + 1;
|
int i;
|
||||||
|
|
||||||
/* advance until we find the next entry */
|
/* advance until we find the next entry */
|
||||||
if (entry != NULL) entry = entry->next;
|
if (entry != NULL)
|
||||||
|
entry = entry->next;
|
||||||
if (entry == NULL) {
|
if (entry == NULL) {
|
||||||
/* we're off the end of list, search for a new entry */
|
/* we're off the end of list, search for a new entry */
|
||||||
while (i < iter->table->length) {
|
i = iter->index + 1;
|
||||||
entry = table->entries[i];
|
while (i < iter->table->length) {
|
||||||
if (entry != NULL) {
|
entry = table->entries[i];
|
||||||
iter->index = i;
|
if (entry != NULL) {
|
||||||
break;
|
iter->index = i;
|
||||||
}
|
break;
|
||||||
i++;
|
}
|
||||||
}
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((entry == NULL) || (i >= table->length)) {
|
if (entry == NULL) {
|
||||||
/* no more keys! */
|
/* no more keys! */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remember our current match */
|
/* remember our current match */
|
||||||
iter->entry = entry;
|
iter->entry = entry;
|
||||||
return entry->key;
|
return entry->key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
src/hash.h
19
src/hash.h
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* hash.h
|
/* hash.h
|
||||||
** strophe XMPP client library -- hash table interface
|
** strophe XMPP client library -- hash table interface
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -21,23 +19,22 @@
|
|||||||
|
|
||||||
typedef struct _hash_t hash_t;
|
typedef struct _hash_t hash_t;
|
||||||
|
|
||||||
typedef void (*hash_free_func)(const xmpp_ctx_t * const ctx, void *p);
|
typedef void (*hash_free_func)(const xmpp_ctx_t *ctx, void *p);
|
||||||
|
|
||||||
/** allocate and initialize a new hash table */
|
/** allocate and initialize a new hash table */
|
||||||
hash_t *hash_new(xmpp_ctx_t * const ctx, const int size,
|
hash_t *hash_new(xmpp_ctx_t *ctx, int size, hash_free_func free_func);
|
||||||
hash_free_func free);
|
|
||||||
|
|
||||||
/** allocate a new reference to an existing hash table */
|
/** allocate a new reference to an existing hash table */
|
||||||
hash_t *hash_clone(hash_t * const table);
|
hash_t *hash_clone(hash_t *table);
|
||||||
|
|
||||||
/** release a hash table when no longer needed */
|
/** release a hash table when no longer needed */
|
||||||
void hash_release(hash_t * const table);
|
void hash_release(hash_t *table);
|
||||||
|
|
||||||
/** add a key, value pair to a hash table.
|
/** add a key, value pair to a hash table.
|
||||||
* each key can appear only once; the value of any
|
* each key can appear only once; the value of any
|
||||||
* identical key will be replaced
|
* identical key will be replaced
|
||||||
*/
|
*/
|
||||||
int hash_add(hash_t *table, const char * const key, void *data);
|
int hash_add(hash_t *table, const char *key, void *data);
|
||||||
|
|
||||||
/** look up a key in a hash table */
|
/** look up a key in a hash table */
|
||||||
void *hash_get(hash_t *table, const char *key);
|
void *hash_get(hash_t *table, const char *key);
|
||||||
@@ -59,6 +56,6 @@ void hash_iter_release(hash_iterator_t *iter);
|
|||||||
|
|
||||||
/** return the next hash table key from the iterator.
|
/** return the next hash table key from the iterator.
|
||||||
the returned key should not be freed */
|
the returned key should not be freed */
|
||||||
const char * hash_iter_next(hash_iterator_t *iter);
|
const char *hash_iter_next(hash_iterator_t *iter);
|
||||||
|
|
||||||
#endif /* __LIBXMPPP_HASH_H__ */
|
#endif /* __LIBXMPPP_HASH_H__ */
|
||||||
|
|||||||
150
src/jid.c
150
src/jid.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* jid.c
|
/* jid.c
|
||||||
** strophe XMPP client library -- helper functions for parsing JIDs
|
** strophe XMPP client library -- helper functions for parsing JIDs
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -31,15 +29,19 @@
|
|||||||
* @return an allocated string with the full JID or NULL if no domain
|
* @return an allocated string with the full JID or NULL if no domain
|
||||||
* is specified
|
* is specified
|
||||||
*/
|
*/
|
||||||
char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
|
char *xmpp_jid_new(xmpp_ctx_t *ctx,
|
||||||
const char *domain,
|
const char *node,
|
||||||
const char *resource)
|
const char *domain,
|
||||||
|
const char *resource)
|
||||||
{
|
{
|
||||||
char *result;
|
char *result;
|
||||||
int len,nlen,dlen,rlen;
|
size_t len, nlen, dlen, rlen;
|
||||||
|
|
||||||
/* jid must at least have a domain */
|
/* jid must at least have a domain */
|
||||||
if (domain == NULL) return NULL;
|
if (domain == NULL) {
|
||||||
|
strophe_error(ctx, "jid", "domainpart missing.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* accumulate lengths */
|
/* accumulate lengths */
|
||||||
dlen = strlen(domain);
|
dlen = strlen(domain);
|
||||||
@@ -47,19 +49,39 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
|
|||||||
rlen = (resource) ? strlen(resource) + 1 : 0;
|
rlen = (resource) ? strlen(resource) + 1 : 0;
|
||||||
len = nlen + dlen + rlen;
|
len = nlen + dlen + rlen;
|
||||||
|
|
||||||
|
if (dlen > 1023) {
|
||||||
|
strophe_error(ctx, "jid", "domainpart too long.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (nlen > 1024) {
|
||||||
|
strophe_error(ctx, "jid", "localpart too long.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (rlen > 1024) {
|
||||||
|
strophe_error(ctx, "jid", "resourcepart too long.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
if (strcspn(node, "\"&'/:<>@") != nlen - 1) {
|
||||||
|
strophe_error(ctx, "jid", "localpart contained invalid character.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* concat components */
|
/* concat components */
|
||||||
result = xmpp_alloc(ctx, len + 1);
|
result = strophe_alloc(ctx, len + 1);
|
||||||
if (result != NULL) {
|
if (result != NULL) {
|
||||||
if (node != NULL) {
|
if (node != NULL) {
|
||||||
memcpy(result, node, nlen - 1);
|
memcpy(result, node, nlen - 1);
|
||||||
result[nlen-1] = '@';
|
result[nlen - 1] = '@';
|
||||||
}
|
}
|
||||||
memcpy(result + nlen, domain, dlen);
|
memcpy(result + nlen, domain, dlen);
|
||||||
if (resource != NULL) {
|
if (resource != NULL) {
|
||||||
result[nlen+dlen] = '/';
|
result[nlen + dlen] = '/';
|
||||||
memcpy(result+nlen+dlen+1, resource, rlen - 1);
|
memcpy(result + nlen + dlen + 1, resource, rlen - 1);
|
||||||
}
|
}
|
||||||
result[nlen+dlen+rlen] = '\0';
|
result[len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -75,15 +97,13 @@ char *xmpp_jid_new(xmpp_ctx_t *ctx, const char *node,
|
|||||||
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
|
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
|
||||||
{
|
{
|
||||||
char *result;
|
char *result;
|
||||||
const char *c;
|
size_t len;
|
||||||
|
|
||||||
c = strchr(jid, '/');
|
len = strcspn(jid, "/");
|
||||||
if (c == NULL) return xmpp_strdup(ctx, jid);
|
result = strophe_alloc(ctx, len + 1);
|
||||||
|
|
||||||
result = xmpp_alloc(ctx, c-jid+1);
|
|
||||||
if (result != NULL) {
|
if (result != NULL) {
|
||||||
memcpy(result, jid, c-jid);
|
memcpy(result, jid, len);
|
||||||
result[c-jid] = '\0';
|
result[len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -99,18 +119,30 @@ char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid)
|
|||||||
*/
|
*/
|
||||||
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
|
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
|
||||||
{
|
{
|
||||||
|
char *dup_jid = strophe_strdup(ctx, jid);
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
const char *c;
|
const char *c;
|
||||||
|
|
||||||
c = strchr(jid, '@');
|
/* Apply the same parsing rules from rfc7622 Section 3.2
|
||||||
if (c != NULL) {
|
* 1. Strip resource
|
||||||
result = xmpp_alloc(ctx, (c-jid) + 1);
|
* 2. take part before the '@'
|
||||||
if (result != NULL) {
|
*/
|
||||||
memcpy(result, jid, (c-jid));
|
|
||||||
result[c-jid] = '\0';
|
char *resource = strchr(dup_jid, '/');
|
||||||
}
|
if (resource != NULL) {
|
||||||
|
*resource = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c = strchr(dup_jid, '@');
|
||||||
|
if (c != NULL) {
|
||||||
|
result = strophe_alloc(ctx, (c - dup_jid) + 1);
|
||||||
|
if (result != NULL) {
|
||||||
|
memcpy(result, dup_jid, (c - dup_jid));
|
||||||
|
result[c - dup_jid] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strophe_free(ctx, dup_jid);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,27 +155,29 @@ char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid)
|
|||||||
*/
|
*/
|
||||||
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
|
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
|
||||||
{
|
{
|
||||||
char *result = NULL;
|
char *dup_jid = strophe_strdup(ctx, jid);
|
||||||
const char *c,*s;
|
|
||||||
|
|
||||||
c = strchr(jid, '@');
|
/* rfc7622 Section 3.2
|
||||||
if (c == NULL) {
|
* 1. Remove any portion from the first '/' character to the end of the
|
||||||
/* no node, assume domain */
|
* string (if there is a '/' character present).
|
||||||
c = jid;
|
*/
|
||||||
|
|
||||||
|
char *resource = strchr(dup_jid, '/');
|
||||||
|
if (resource != NULL) {
|
||||||
|
*resource = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Remove any portion from the beginning of the string to the first
|
||||||
|
* '@' character (if there is an '@' character present).
|
||||||
|
*/
|
||||||
|
char *at_sign = strchr(dup_jid, '@');
|
||||||
|
char *result = NULL;
|
||||||
|
if (at_sign != NULL) {
|
||||||
|
result = strophe_strdup(ctx, (at_sign + 1));
|
||||||
} else {
|
} else {
|
||||||
/* advance past the separator */
|
result = strophe_strdup(ctx, dup_jid);
|
||||||
c++;
|
|
||||||
}
|
|
||||||
s = strchr(c, '/');
|
|
||||||
if (s == NULL) {
|
|
||||||
/* no resource */
|
|
||||||
s = c + strlen(c);
|
|
||||||
}
|
|
||||||
result = xmpp_alloc(ctx, (s-c) + 1);
|
|
||||||
if (result != NULL) {
|
|
||||||
memcpy(result, c, (s-c));
|
|
||||||
result[s-c] = '\0';
|
|
||||||
}
|
}
|
||||||
|
strophe_free(ctx, dup_jid);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -158,20 +192,8 @@ char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid)
|
|||||||
*/
|
*/
|
||||||
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid)
|
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid)
|
||||||
{
|
{
|
||||||
char *result = NULL;
|
|
||||||
const char *c;
|
const char *c;
|
||||||
int len;
|
|
||||||
|
|
||||||
c = strchr(jid, '/');
|
c = strchr(jid, '/');
|
||||||
if (c != NULL) {
|
return c != NULL ? strophe_strdup(ctx, c + 1) : NULL;
|
||||||
c++;
|
|
||||||
len = strlen(c);
|
|
||||||
result = xmpp_alloc(ctx, len + 1);
|
|
||||||
if (result != NULL) {
|
|
||||||
memcpy(result, c, len);
|
|
||||||
result[len] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|||||||
113
src/md5.c
113
src/md5.c
@@ -29,19 +29,21 @@
|
|||||||
#include "md5.h"
|
#include "md5.h"
|
||||||
|
|
||||||
/* little-endian word access macros */
|
/* little-endian word access macros */
|
||||||
#define GET_32BIT_LSB_FIRST(cp) \
|
#define GET_32BIT_LSB_FIRST(cp) \
|
||||||
(((uint32_t)(unsigned char)(cp)[0]) | \
|
(((uint32_t)(unsigned char)(cp)[0]) | \
|
||||||
((uint32_t)(unsigned char)(cp)[1] << 8 ) | \
|
((uint32_t)(unsigned char)(cp)[1] << 8) | \
|
||||||
((uint32_t)(unsigned char)(cp)[2] << 16) | \
|
((uint32_t)(unsigned char)(cp)[2] << 16) | \
|
||||||
((uint32_t)(unsigned char)(cp)[3] << 24))
|
((uint32_t)(unsigned char)(cp)[3] << 24))
|
||||||
|
|
||||||
#define PUT_32BIT_LSB_FIRST(cp, value) \
|
#define PUT_32BIT_LSB_FIRST(cp, value) \
|
||||||
do { \
|
do { \
|
||||||
(cp)[0] = (value) & 0xFF; \
|
(cp)[0] = (value)&0xFF; \
|
||||||
(cp)[1] = ((value) >> 8) & 0xFF; \
|
(cp)[1] = ((value) >> 8) & 0xFF; \
|
||||||
(cp)[2] = ((value) >> 16) & 0xFF; \
|
(cp)[2] = ((value) >> 16) & 0xFF; \
|
||||||
(cp)[3] = ((value) >> 24) & 0xFF; \
|
(cp)[3] = ((value) >> 24) & 0xFF; \
|
||||||
} while(0)
|
} while (0)
|
||||||
|
|
||||||
|
static void MD5Transform(uint32_t buf[4], const unsigned char inext[64]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||||
@@ -57,7 +59,7 @@ void MD5Init(struct MD5Context *ctx)
|
|||||||
ctx->bits[0] = 0;
|
ctx->bits[0] = 0;
|
||||||
ctx->bits[1] = 0;
|
ctx->bits[1] = 0;
|
||||||
|
|
||||||
memset(ctx->in, 0, 64);
|
memset(ctx->in, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -72,33 +74,33 @@ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, uint32_t len)
|
|||||||
|
|
||||||
t = ctx->bits[0];
|
t = ctx->bits[0];
|
||||||
if ((ctx->bits[0] = (t + ((uint32_t)len << 3)) & 0xffffffff) < t)
|
if ((ctx->bits[0] = (t + ((uint32_t)len << 3)) & 0xffffffff) < t)
|
||||||
ctx->bits[1]++; /* Carry from low to high */
|
ctx->bits[1]++; /* Carry from low to high */
|
||||||
ctx->bits[1] += len >> 29;
|
ctx->bits[1] += len >> 29;
|
||||||
|
|
||||||
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
|
||||||
|
|
||||||
/* Handle any leading odd-sized chunks */
|
/* Handle any leading odd-sized chunks */
|
||||||
|
|
||||||
if (t) {
|
if (t) {
|
||||||
unsigned char *p = ctx->in + t;
|
unsigned char *p = ctx->in + t;
|
||||||
|
|
||||||
t = 64 - t;
|
t = 64 - t;
|
||||||
if (len < t) {
|
if (len < t) {
|
||||||
memcpy(p, buf, len);
|
memcpy(p, buf, len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memcpy(p, buf, t);
|
memcpy(p, buf, t);
|
||||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
MD5Transform(ctx->buf, ctx->in);
|
||||||
buf += t;
|
buf += t;
|
||||||
len -= t;
|
len -= t;
|
||||||
}
|
}
|
||||||
/* Process data in 64-byte chunks */
|
/* Process data in 64-byte chunks */
|
||||||
|
|
||||||
while (len >= 64) {
|
while (len >= 64) {
|
||||||
memcpy(ctx->in, buf, 64);
|
memcpy(ctx->in, buf, 64);
|
||||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
MD5Transform(ctx->buf, ctx->in);
|
||||||
buf += 64;
|
buf += 64;
|
||||||
len -= 64;
|
len -= 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle any remaining bytes of data. */
|
/* Handle any remaining bytes of data. */
|
||||||
@@ -128,31 +130,29 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
|
|||||||
|
|
||||||
/* Pad out to 56 mod 64 */
|
/* Pad out to 56 mod 64 */
|
||||||
if (count < 8) {
|
if (count < 8) {
|
||||||
/* Two lots of padding: Pad the first block to 64 bytes */
|
/* Two lots of padding: Pad the first block to 64 bytes */
|
||||||
memset(p, 0, count);
|
memset(p, 0, count);
|
||||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
MD5Transform(ctx->buf, ctx->in);
|
||||||
|
|
||||||
/* Now fill the next block with 56 bytes */
|
/* Now fill the next block with 56 bytes */
|
||||||
memset(ctx->in, 0, 56);
|
memset(ctx->in, 0, 56);
|
||||||
} else {
|
} else {
|
||||||
/* Pad block to 56 bytes */
|
/* Pad block to 56 bytes */
|
||||||
memset(p, 0, count - 8);
|
memset(p, 0, count - 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Append length in bits and transform */
|
/* Append length in bits and transform */
|
||||||
PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]);
|
PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]);
|
||||||
PUT_32BIT_LSB_FIRST(ctx->in + 60, ctx->bits[1]);
|
PUT_32BIT_LSB_FIRST(ctx->in + 60, ctx->bits[1]);
|
||||||
|
|
||||||
MD5Transform(ctx->buf, ctx->in, ctx);
|
MD5Transform(ctx->buf, ctx->in);
|
||||||
PUT_32BIT_LSB_FIRST(digest, ctx->buf[0]);
|
PUT_32BIT_LSB_FIRST(digest, ctx->buf[0]);
|
||||||
PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]);
|
PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]);
|
||||||
PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]);
|
PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]);
|
||||||
PUT_32BIT_LSB_FIRST(digest + 12, ctx->buf[3]);
|
PUT_32BIT_LSB_FIRST(digest + 12, ctx->buf[3]);
|
||||||
memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
|
memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef ASM_MD5
|
|
||||||
|
|
||||||
/* The four core functions - F1 is optimized somewhat */
|
/* The four core functions - F1 is optimized somewhat */
|
||||||
|
|
||||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||||
@@ -165,28 +165,27 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
|
|||||||
/* debugging version: */
|
/* debugging version: */
|
||||||
/*
|
/*
|
||||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||||
printf("MD5STEP: w: %x x: %x y: %x z: %x data: %x s: %x\n", \
|
printf("MD5STEP: w: %x x: %x y: %x z: %x data: %x s: %x\n", \
|
||||||
w, x, y, z, data, s); \
|
w, x, y, z, data, s); \
|
||||||
printf("f(x,y,z) = %x\n", f(x,y,z)+data); \
|
printf("f(x,y,z) = %x\n", f(x,y,z)+data); \
|
||||||
( w += f(x, y, z) + data, printf(" - w: %x ", w), \
|
( w += f(x, y, z) + data, printf(" - w: %x ", w), \
|
||||||
w = w<<s | w>>(32-s), printf(" - w: %x\n", w), w += x )
|
w = w<<s | w>>(32-s), printf(" - w: %x\n", w), w += x )
|
||||||
*/
|
*/
|
||||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
(w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||||
* the data and converts bytes into longwords for this routine.
|
* the data and converts bytes into longwords for this routine.
|
||||||
*/
|
*/
|
||||||
void MD5Transform(uint32_t buf[4], const unsigned char inext[64],
|
static void MD5Transform(uint32_t buf[4], const unsigned char inext[64])
|
||||||
struct MD5Context *ctx)
|
|
||||||
{
|
{
|
||||||
register uint32_t a, b, c, d, i;
|
register uint32_t a, b, c, d, i;
|
||||||
uint32_t in[16];
|
uint32_t in[16];
|
||||||
|
|
||||||
for (i = 0; i < 16; i++)
|
for (i = 0; i < 16; i++)
|
||||||
in[i] = GET_32BIT_LSB_FIRST(inext + 4 * i);
|
in[i] = GET_32BIT_LSB_FIRST(inext + 4 * i);
|
||||||
|
|
||||||
a = buf[0];
|
a = buf[0];
|
||||||
b = buf[1];
|
b = buf[1];
|
||||||
@@ -267,23 +266,3 @@ void MD5Transform(uint32_t buf[4], const unsigned char inext[64],
|
|||||||
buf[2] += c;
|
buf[2] += c;
|
||||||
buf[3] += d;
|
buf[3] += d;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_MD5
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
void MD5DumpBytes(unsigned char *b, int len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i=0; i<len; i++) {
|
|
||||||
if (i%32==0 && i!=0) {
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
printf("%02x", b[i]&0xff);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* DEBUG_MD5 */
|
|
||||||
|
|
||||||
#endif /* !MD5_ASM */
|
|
||||||
|
|||||||
26
src/md5.h
26
src/md5.h
@@ -11,31 +11,19 @@
|
|||||||
#ifndef MD5_H
|
#ifndef MD5_H
|
||||||
#define MD5_H
|
#define MD5_H
|
||||||
|
|
||||||
/* we use the uint32_t type from stdint.h
|
|
||||||
* if it is not available, add a typedef here:
|
|
||||||
*/
|
|
||||||
/* make sure the stdint.h types are available */
|
/* make sure the stdint.h types are available */
|
||||||
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
#include "ostypes.h"
|
||||||
typedef unsigned int uint32_t;
|
|
||||||
#else
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct MD5Context {
|
struct MD5Context {
|
||||||
uint32_t buf[4];
|
uint32_t buf[4];
|
||||||
uint32_t bits[2];
|
uint32_t bits[2];
|
||||||
unsigned char in[64];
|
unsigned char in[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
void MD5Init(struct MD5Context *context);
|
void MD5Init(struct MD5Context *context);
|
||||||
void MD5Update(struct MD5Context *context, unsigned char const *buf,
|
void MD5Update(struct MD5Context *context,
|
||||||
uint32_t len);
|
unsigned char const *buf,
|
||||||
|
uint32_t len);
|
||||||
void MD5Final(unsigned char digest[16], struct MD5Context *context);
|
void MD5Final(unsigned char digest[16], struct MD5Context *context);
|
||||||
void MD5Transform(uint32_t buf[4], const unsigned char in[64],
|
|
||||||
struct MD5Context *ctx);
|
|
||||||
|
|
||||||
#ifdef DEBUG_MD5
|
|
||||||
void MD5DumpBytes(unsigned char *b, int len);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* !MD5_H */
|
#endif /* !MD5_H */
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
/* oocontext.cpp
|
|
||||||
** strophe XMPP client library -- C++ context implementation
|
|
||||||
**
|
|
||||||
** Copyright (C) 2005-2009 Collecta, Inc.
|
|
||||||
**
|
|
||||||
** This software is provided AS-IS with no warranty, either express
|
|
||||||
** or implied.
|
|
||||||
**
|
|
||||||
** This software is distributed under license and may not be copied,
|
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "strophe.h"
|
|
||||||
#include "strophepp.h"
|
|
||||||
|
|
||||||
XMPP::Context::Context()
|
|
||||||
{
|
|
||||||
m_mem.alloc = callAlloc;
|
|
||||||
m_mem.realloc = callRealloc;
|
|
||||||
m_mem.free = callFree;
|
|
||||||
m_mem.userdata = (void *)this;
|
|
||||||
|
|
||||||
m_log.handler = callLog;
|
|
||||||
m_log.userdata = (void *)this;
|
|
||||||
|
|
||||||
m_ctx = ::xmpp_ctx_new(&m_mem, &m_log);
|
|
||||||
}
|
|
||||||
|
|
||||||
XMPP::Context::~Context()
|
|
||||||
{
|
|
||||||
::xmpp_ctx_free(m_ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *XMPP::Context::alloc(const size_t size)
|
|
||||||
{
|
|
||||||
return ::malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *XMPP::Context::realloc(void *p, const size_t size)
|
|
||||||
{
|
|
||||||
return ::realloc(p, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMPP::Context::free(void *p)
|
|
||||||
{
|
|
||||||
::free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMPP::Context::log(const xmpp_log_level_t level,
|
|
||||||
const char * const area,
|
|
||||||
const char * const msg)
|
|
||||||
{
|
|
||||||
/* do nothing by default */
|
|
||||||
}
|
|
||||||
|
|
||||||
xmpp_ctx_t *XMPP::Context::getContext()
|
|
||||||
{
|
|
||||||
return m_ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *XMPP::Context::callAlloc(const size_t size, void * const userdata)
|
|
||||||
{
|
|
||||||
return reinterpret_cast<Context *>(userdata)->alloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *XMPP::Context::callRealloc(void *p, const size_t size,
|
|
||||||
void * const userdata)
|
|
||||||
{
|
|
||||||
return reinterpret_cast<Context *>(userdata)->realloc(p, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMPP::Context::callFree(void *p, void * const userdata)
|
|
||||||
{
|
|
||||||
reinterpret_cast<Context *>(userdata)->free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XMPP::Context::callLog(void * const userdata,
|
|
||||||
const xmpp_log_level_t level,
|
|
||||||
const char * const area,
|
|
||||||
const char * const msg)
|
|
||||||
{
|
|
||||||
reinterpret_cast<Context *>(userdata)->log(level, area, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
/* oostanza.cpp
|
|
||||||
** strophe XMPP client library -- C++ context implementation
|
|
||||||
**
|
|
||||||
** Copyright (C) 2005-2009 Collecta, Inc.
|
|
||||||
**
|
|
||||||
** This software is provided AS-IS with no warranty, either express
|
|
||||||
** or implied.
|
|
||||||
**
|
|
||||||
** This software is distributed under license and may not be copied,
|
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "strophe.h"
|
|
||||||
#include "strophepp.h"
|
|
||||||
|
|
||||||
using namespace XMPP;
|
|
||||||
|
|
||||||
void *Stanza::operator new(size_t size, Context *ctx)
|
|
||||||
{
|
|
||||||
void *p;
|
|
||||||
|
|
||||||
/* we must allocate extra room for the Context object so that the
|
|
||||||
destructor can access it to free the object. C++ does not allow
|
|
||||||
us to access normal members in the destructor, so we have to hide
|
|
||||||
it. This must be prepended as well, since C++ will add stuff to
|
|
||||||
the end in subclasses. */
|
|
||||||
|
|
||||||
p = ctx->alloc(size + sizeof(Context *));
|
|
||||||
if (!p) return p;
|
|
||||||
|
|
||||||
*reinterpret_cast<Context **>(p) = ctx;
|
|
||||||
p = reinterpret_cast<void *>(reinterpret_cast<char *>(p) +
|
|
||||||
sizeof(Context *));
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stanza::operator delete(void *p)
|
|
||||||
{
|
|
||||||
Context *ctx;
|
|
||||||
|
|
||||||
ctx = *reinterpret_cast<Context **>(reinterpret_cast<char *>(p) - 4);
|
|
||||||
ctx->free(reinterpret_cast<char *>(p) - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
Stanza::Stanza(Context *ctx)
|
|
||||||
{
|
|
||||||
m_ctx = ctx;
|
|
||||||
m_stanza = ::xmpp_stanza_new(ctx->getContext());
|
|
||||||
// TODO: check for errors
|
|
||||||
}
|
|
||||||
|
|
||||||
Stanza::~Stanza()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Stanza *Stanza::create(Context *ctx)
|
|
||||||
{
|
|
||||||
return new (ctx) Stanza(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stanza::release()
|
|
||||||
{
|
|
||||||
if (::xmpp_stanza_release(m_stanza))
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stanza *Stanza::clone()
|
|
||||||
{
|
|
||||||
::xmpp_stanza_clone(m_stanza);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stanza *Stanza::copy()
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* ostypes.h
|
/* ostypes.h
|
||||||
** strophe XMPP client library -- type definitions for platforms
|
** strophe XMPP client library -- type definitions for platforms
|
||||||
** without stdint.h
|
** without stdint.h
|
||||||
@@ -7,10 +8,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -20,8 +18,31 @@
|
|||||||
#ifndef __LIBSTROPHE_OSTYPES_H__
|
#ifndef __LIBSTROPHE_OSTYPES_H__
|
||||||
#define __LIBSTROPHE_OSTYPES_H__
|
#define __LIBSTROPHE_OSTYPES_H__
|
||||||
|
|
||||||
#ifdef _WIN32
|
#include <stddef.h> /* size_t */
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1600
|
||||||
|
typedef signed char int8_t;
|
||||||
|
typedef short int int16_t;
|
||||||
|
typedef int int32_t;
|
||||||
|
typedef __int64 int64_t;
|
||||||
|
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned short int uint16_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
typedef unsigned __int64 uint64_t;
|
typedef unsigned __int64 uint64_t;
|
||||||
|
|
||||||
|
#ifndef UINT16_MAX
|
||||||
|
#define UINT16_MAX ((uint16_t)0xffff)
|
||||||
|
#endif /* UINT16_MAX */
|
||||||
|
#ifndef UINT32_MAX
|
||||||
|
#define UINT32_MAX ((uint32_t)0xffffffff)
|
||||||
|
#endif /* UINT32_MAX */
|
||||||
|
#ifndef SIZE_MAX
|
||||||
|
#define SIZE_MAX UINT32_MAX
|
||||||
|
#endif /* SIZE_MAX */
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __LIBSTROPHE_OSTYPES_H__ */
|
#endif /* __LIBSTROPHE_OSTYPES_H__ */
|
||||||
|
|||||||
19
src/parser.h
19
src/parser.h
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* parser.h
|
/* parser.h
|
||||||
** strophe XMPP client library -- parser structures and functions
|
** strophe XMPP client library -- parser structures and functions
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express or
|
** This software is provided AS-IS with no warranty, either express or
|
||||||
** implied.
|
** implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -23,20 +21,17 @@
|
|||||||
|
|
||||||
typedef struct _parser_t parser_t;
|
typedef struct _parser_t parser_t;
|
||||||
|
|
||||||
typedef void (*parser_start_callback)(char *name,
|
typedef void (*parser_start_callback)(char *name, char **attrs, void *userdata);
|
||||||
char **attrs,
|
typedef void (*parser_end_callback)(char *name, void *userdata);
|
||||||
void * const userdata);
|
typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza, void *userdata);
|
||||||
typedef void (*parser_end_callback)(char *name, void * const userdata);
|
|
||||||
typedef void (*parser_stanza_callback)(xmpp_stanza_t *stanza,
|
|
||||||
void * const userdata);
|
|
||||||
|
|
||||||
|
|
||||||
parser_t *parser_new(xmpp_ctx_t *ctx,
|
parser_t *parser_new(xmpp_ctx_t *ctx,
|
||||||
parser_start_callback startcb,
|
parser_start_callback startcb,
|
||||||
parser_end_callback endcb,
|
parser_end_callback endcb,
|
||||||
parser_stanza_callback stanzacb,
|
parser_stanza_callback stanzacb,
|
||||||
void *userdata);
|
void *userdata);
|
||||||
void parser_free(parser_t * const parser);
|
void parser_free(parser_t *parser);
|
||||||
|
char *parser_attr_name(xmpp_ctx_t *ctx, char *nsname);
|
||||||
int parser_reset(parser_t *parser);
|
int parser_reset(parser_t *parser);
|
||||||
int parser_feed(parser_t *parser, char *chunk, int len);
|
int parser_feed(parser_t *parser, char *chunk, int len);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* parser.c
|
/* parser.c
|
||||||
** strophe XMPP client library -- xml parser handlers and utility functions
|
** strophe XMPP client library -- xml parser handlers and utility functions
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -22,10 +20,16 @@
|
|||||||
|
|
||||||
#include <expat.h>
|
#include <expat.h>
|
||||||
|
|
||||||
#include <strophe.h>
|
#include "strophe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
|
/* Allocate inner text by this number bytes more. Expat splits string
|
||||||
|
* "new\nline" into 3 strings: "new" "\n" "line". Expecting this pattern,
|
||||||
|
* we can leave few bytes in the inner_text for "\n". It should reduce
|
||||||
|
* number of re-allocations in 2 times for multi-line texts. */
|
||||||
|
#define INNER_TEXT_PADDING 2
|
||||||
|
|
||||||
struct _parser_t {
|
struct _parser_t {
|
||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
XML_Parser expat;
|
XML_Parser expat;
|
||||||
@@ -35,65 +39,173 @@ struct _parser_t {
|
|||||||
void *userdata;
|
void *userdata;
|
||||||
int depth;
|
int depth;
|
||||||
xmpp_stanza_t *stanza;
|
xmpp_stanza_t *stanza;
|
||||||
|
char *inner_text;
|
||||||
|
/* number of allocated bytes */
|
||||||
|
int inner_text_size;
|
||||||
|
/* excluding terminal '\0' */
|
||||||
|
int inner_text_used;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Use the Unit Separator to delimit namespace and name in our XML */
|
||||||
|
const XML_Char namespace_sep = '\x1F';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cached strophe ctx. It is used for memory suite.
|
||||||
|
* Note, expat doesn't support userdata in memory suite, therefore,
|
||||||
|
* we can support only one strophe context. If user creates more than one
|
||||||
|
* context, this module will fallback to default library allocator for all
|
||||||
|
* contexts other than mem_ctx.
|
||||||
|
*/
|
||||||
|
static xmpp_ctx_t *mem_ctx = NULL;
|
||||||
|
|
||||||
|
static void *parser_mem_malloc(size_t size)
|
||||||
|
{
|
||||||
|
if (mem_ctx != NULL)
|
||||||
|
return strophe_alloc(mem_ctx, size);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *parser_mem_realloc(void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
if (mem_ctx != NULL)
|
||||||
|
return strophe_realloc(mem_ctx, ptr, size);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parser_mem_free(void *ptr)
|
||||||
|
{
|
||||||
|
if (mem_ctx != NULL)
|
||||||
|
strophe_free(mem_ctx, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const XML_Memory_Handling_Suite parser_mem_suite = {
|
||||||
|
.malloc_fcn = &parser_mem_malloc,
|
||||||
|
.realloc_fcn = &parser_mem_realloc,
|
||||||
|
.free_fcn = &parser_mem_free,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* return allocated string with the name from a delimited
|
||||||
|
* namespace/name string */
|
||||||
|
static char *_xml_name(xmpp_ctx_t *ctx, const char *nsname)
|
||||||
|
{
|
||||||
|
char *result = NULL;
|
||||||
|
const char *c;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
c = strchr(nsname, namespace_sep);
|
||||||
|
if (c == NULL)
|
||||||
|
return strophe_strdup(ctx, nsname);
|
||||||
|
|
||||||
|
c++;
|
||||||
|
len = strlen(c);
|
||||||
|
result = strophe_alloc(ctx, len + 1);
|
||||||
|
if (result != NULL) {
|
||||||
|
memcpy(result, c, len);
|
||||||
|
result[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return allocated string with the namespace from a delimited string */
|
||||||
|
static char *_xml_namespace(xmpp_ctx_t *ctx, const char *nsname)
|
||||||
|
{
|
||||||
|
char *result = NULL;
|
||||||
|
const char *c;
|
||||||
|
|
||||||
|
c = strchr(nsname, namespace_sep);
|
||||||
|
if (c != NULL) {
|
||||||
|
result = strophe_alloc(ctx, (c - nsname) + 1);
|
||||||
|
if (result != NULL) {
|
||||||
|
memcpy(result, nsname, (c - nsname));
|
||||||
|
result[c - nsname] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
|
static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
|
||||||
{
|
{
|
||||||
|
char *attr;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!attrs) return;
|
if (!attrs)
|
||||||
|
return;
|
||||||
|
|
||||||
for (i = 0; attrs[i]; i += 2) {
|
for (i = 0; attrs[i]; i += 2) {
|
||||||
xmpp_stanza_set_attribute(stanza, attrs[i], attrs[i+1]);
|
/* namespaced attributes aren't used in xmpp, discard namespace */
|
||||||
|
attr = _xml_name(stanza->ctx, attrs[i]);
|
||||||
|
xmpp_stanza_set_attribute(stanza, attr, attrs[i + 1]);
|
||||||
|
strophe_free(stanza->ctx, attr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _start_element(void *userdata,
|
static void complete_inner_text(parser_t *parser)
|
||||||
const XML_Char *name,
|
{
|
||||||
const XML_Char **attrs)
|
xmpp_stanza_t *stanza;
|
||||||
|
|
||||||
|
if (parser->inner_text) {
|
||||||
|
/* create and populate stanza */
|
||||||
|
stanza = xmpp_stanza_new(parser->ctx);
|
||||||
|
/* FIXME: disconnect on allocation error */
|
||||||
|
if (stanza) {
|
||||||
|
xmpp_stanza_set_text(stanza, parser->inner_text);
|
||||||
|
xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
|
||||||
|
}
|
||||||
|
strophe_free(parser->ctx, parser->inner_text);
|
||||||
|
parser->inner_text = NULL;
|
||||||
|
parser->inner_text_size = 0;
|
||||||
|
parser->inner_text_used = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_start_element(void *userdata, const XML_Char *nsname, const XML_Char **attrs)
|
||||||
{
|
{
|
||||||
parser_t *parser = (parser_t *)userdata;
|
parser_t *parser = (parser_t *)userdata;
|
||||||
xmpp_stanza_t *child;
|
xmpp_stanza_t *child;
|
||||||
|
char *ns, *name;
|
||||||
|
|
||||||
|
ns = _xml_namespace(parser->ctx, nsname);
|
||||||
|
name = _xml_name(parser->ctx, nsname);
|
||||||
|
|
||||||
if (parser->depth == 0) {
|
if (parser->depth == 0) {
|
||||||
/* notify the owner */
|
/* notify the owner */
|
||||||
if (parser->startcb)
|
if (parser->startcb)
|
||||||
parser->startcb((char *)name, (char **)attrs,
|
parser->startcb(name, (char **)attrs, parser->userdata);
|
||||||
parser->userdata);
|
|
||||||
} else {
|
} else {
|
||||||
/* build stanzas at depth 1 */
|
/* build stanzas at depth 1 */
|
||||||
if (!parser->stanza && parser->depth != 1) {
|
if (!parser->stanza && parser->depth != 1) {
|
||||||
/* something terrible happened */
|
/* something terrible happened */
|
||||||
/* FIXME: shutdown disconnect */
|
/* FIXME: shutdown disconnect */
|
||||||
xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
|
strophe_error(parser->ctx, "parser",
|
||||||
} else if (!parser->stanza) {
|
"oops, where did our stanza go?");
|
||||||
/* starting a new toplevel stanza */
|
} else {
|
||||||
parser->stanza = xmpp_stanza_new(parser->ctx);
|
child = xmpp_stanza_new(parser->ctx);
|
||||||
if (!parser->stanza) {
|
if (!child) {
|
||||||
/* FIXME: can't allocate, disconnect */
|
/* FIXME: can't allocate, disconnect */
|
||||||
}
|
}
|
||||||
xmpp_stanza_set_name(parser->stanza, name);
|
xmpp_stanza_set_name(child, name);
|
||||||
_set_attributes(parser->stanza, attrs);
|
_set_attributes(child, attrs);
|
||||||
} else {
|
if (ns)
|
||||||
/* starting a child of parser->stanza */
|
xmpp_stanza_set_ns(child, ns);
|
||||||
child = xmpp_stanza_new(parser->ctx);
|
|
||||||
if (!child) {
|
|
||||||
/* FIXME: can't allocate, disconnect */
|
|
||||||
}
|
|
||||||
xmpp_stanza_set_name(child, name);
|
|
||||||
_set_attributes(child, attrs);
|
|
||||||
|
|
||||||
/* add child to parent */
|
if (parser->stanza != NULL) {
|
||||||
xmpp_stanza_add_child(parser->stanza, child);
|
complete_inner_text(parser);
|
||||||
|
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
|
||||||
/* the child is owned by the toplevel stanza now */
|
}
|
||||||
xmpp_stanza_release(child);
|
parser->stanza = child;
|
||||||
|
}
|
||||||
/* make child the current stanza */
|
|
||||||
parser->stanza = child;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ns)
|
||||||
|
strophe_free(parser->ctx, ns);
|
||||||
|
if (name)
|
||||||
|
strophe_free(parser->ctx, name);
|
||||||
|
|
||||||
parser->depth++;
|
parser->depth++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,36 +220,46 @@ static void _end_element(void *userdata, const XML_Char *name)
|
|||||||
if (parser->endcb)
|
if (parser->endcb)
|
||||||
parser->endcb((char *)name, parser->userdata);
|
parser->endcb((char *)name, parser->userdata);
|
||||||
} else {
|
} else {
|
||||||
if (parser->stanza->parent) {
|
complete_inner_text(parser);
|
||||||
/* we're finishing a child stanza, so set current to the parent */
|
if (parser->stanza->parent) {
|
||||||
parser->stanza = parser->stanza->parent;
|
/* we're finishing a child stanza, so set current to the parent */
|
||||||
} else {
|
parser->stanza = parser->stanza->parent;
|
||||||
|
} else {
|
||||||
if (parser->stanzacb)
|
if (parser->stanzacb)
|
||||||
parser->stanzacb(parser->stanza,
|
parser->stanzacb(parser->stanza, parser->userdata);
|
||||||
parser->userdata);
|
xmpp_stanza_release(parser->stanza);
|
||||||
xmpp_stanza_release(parser->stanza);
|
parser->stanza = NULL;
|
||||||
parser->stanza = NULL;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _characters(void *userdata, const XML_Char *s, int len)
|
static void _characters(void *userdata, const XML_Char *s, int len)
|
||||||
{
|
{
|
||||||
parser_t *parser = (parser_t *)userdata;
|
parser_t *parser = (parser_t *)userdata;
|
||||||
xmpp_stanza_t *stanza;
|
char *p;
|
||||||
|
|
||||||
if (parser->depth < 2) return;
|
if (parser->depth < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
/* create and populate stanza */
|
/* Join all parts to a single resulting string. Stanza is created in
|
||||||
stanza = xmpp_stanza_new(parser->ctx);
|
* _start_element() and _end_element(). */
|
||||||
if (!stanza) {
|
if (parser->inner_text_used + len >= parser->inner_text_size) {
|
||||||
/* FIXME: allocation error, disconnect */
|
parser->inner_text_size =
|
||||||
return;
|
parser->inner_text_used + len + 1 + INNER_TEXT_PADDING;
|
||||||
|
p = strophe_realloc(parser->ctx, parser->inner_text,
|
||||||
|
parser->inner_text_size);
|
||||||
|
if (p == NULL) {
|
||||||
|
strophe_free(parser->ctx, parser->inner_text);
|
||||||
|
parser->inner_text = NULL;
|
||||||
|
parser->inner_text_used = 0;
|
||||||
|
parser->inner_text_size = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parser->inner_text = p;
|
||||||
|
parser->inner_text[parser->inner_text_used] = '\0';
|
||||||
}
|
}
|
||||||
xmpp_stanza_set_text_with_size(stanza, s, len);
|
parser->inner_text_used += len;
|
||||||
|
strncat(parser->inner_text, s, len);
|
||||||
xmpp_stanza_add_child(parser->stanza, stanza);
|
|
||||||
xmpp_stanza_release(stanza);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parser_t *parser_new(xmpp_ctx_t *ctx,
|
parser_t *parser_new(xmpp_ctx_t *ctx,
|
||||||
@@ -148,7 +270,7 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
|
|||||||
{
|
{
|
||||||
parser_t *parser;
|
parser_t *parser;
|
||||||
|
|
||||||
parser = xmpp_alloc(ctx, sizeof(parser_t));
|
parser = strophe_alloc(ctx, sizeof(parser_t));
|
||||||
if (parser != NULL) {
|
if (parser != NULL) {
|
||||||
parser->ctx = ctx;
|
parser->ctx = ctx;
|
||||||
parser->expat = NULL;
|
parser->expat = NULL;
|
||||||
@@ -158,6 +280,9 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
|
|||||||
parser->userdata = userdata;
|
parser->userdata = userdata;
|
||||||
parser->depth = 0;
|
parser->depth = 0;
|
||||||
parser->stanza = NULL;
|
parser->stanza = NULL;
|
||||||
|
parser->inner_text = NULL;
|
||||||
|
parser->inner_text_size = 0;
|
||||||
|
parser->inner_text_used = 0;
|
||||||
|
|
||||||
parser_reset(parser);
|
parser_reset(parser);
|
||||||
}
|
}
|
||||||
@@ -165,29 +290,73 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
|
|||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *parser_attr_name(xmpp_ctx_t *ctx, char *nsname)
|
||||||
|
{
|
||||||
|
return _xml_name(ctx, nsname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _free_parent_stanza(xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
xmpp_stanza_t *parent;
|
||||||
|
|
||||||
|
for (parent = stanza; parent->parent != NULL; parent = parent->parent)
|
||||||
|
;
|
||||||
|
xmpp_stanza_release(parent);
|
||||||
|
}
|
||||||
|
|
||||||
/* free a parser */
|
/* free a parser */
|
||||||
void parser_free(parser_t *parser)
|
void parser_free(parser_t *parser)
|
||||||
{
|
{
|
||||||
if (parser->expat)
|
if (parser->expat)
|
||||||
XML_ParserFree(parser->expat);
|
XML_ParserFree(parser->expat);
|
||||||
|
|
||||||
xmpp_free(parser->ctx, parser);
|
if (parser->stanza) {
|
||||||
|
_free_parent_stanza(parser->stanza);
|
||||||
|
parser->stanza = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser->inner_text) {
|
||||||
|
strophe_free(parser->ctx, parser->inner_text);
|
||||||
|
parser->inner_text = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
strophe_free(parser->ctx, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* shuts down and restarts XML parser. true on success */
|
/* shuts down and restarts XML parser. true on success */
|
||||||
int parser_reset(parser_t *parser)
|
int parser_reset(parser_t *parser)
|
||||||
{
|
{
|
||||||
if (parser->expat)
|
XML_Bool ret;
|
||||||
XML_ParserFree(parser->expat);
|
const XML_Memory_Handling_Suite *mem = NULL;
|
||||||
|
|
||||||
if (parser->stanza)
|
if (parser->expat) {
|
||||||
xmpp_stanza_release(parser->stanza);
|
ret = XML_ParserReset(parser->expat, NULL);
|
||||||
|
if (ret != XML_TRUE) {
|
||||||
|
XML_ParserFree(parser->expat);
|
||||||
|
parser->expat = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mem_ctx == NULL)
|
||||||
|
mem_ctx = parser->ctx;
|
||||||
|
if (parser->ctx == mem_ctx)
|
||||||
|
mem = &parser_mem_suite;
|
||||||
|
parser->expat = XML_ParserCreate_MM(NULL, mem, &namespace_sep);
|
||||||
|
}
|
||||||
|
|
||||||
parser->expat = XML_ParserCreate(NULL);
|
if (parser->stanza) {
|
||||||
if (!parser->expat) return 0;
|
_free_parent_stanza(parser->stanza);
|
||||||
|
parser->stanza = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser->inner_text) {
|
||||||
|
strophe_free(parser->ctx, parser->inner_text);
|
||||||
|
parser->inner_text = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parser->expat)
|
||||||
|
return 0;
|
||||||
|
|
||||||
parser->depth = 0;
|
parser->depth = 0;
|
||||||
parser->stanza = NULL;
|
|
||||||
|
|
||||||
XML_SetUserData(parser->expat, parser);
|
XML_SetUserData(parser->expat, parser);
|
||||||
XML_SetElementHandler(parser->expat, _start_element, _end_element);
|
XML_SetElementHandler(parser->expat, _start_element, _end_element);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* parser.c
|
/* parser.c
|
||||||
** strophe XMPP client library -- xml parser handlers and utility functions
|
** strophe XMPP client library -- xml parser handlers and utility functions
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -23,7 +21,7 @@
|
|||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
#include <strophe.h>
|
#include "strophe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
@@ -39,69 +37,154 @@ struct _parser_t {
|
|||||||
xmpp_stanza_t *stanza;
|
xmpp_stanza_t *stanza;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void _set_attributes(xmpp_stanza_t *stanza, const xmlChar **attrs)
|
static void
|
||||||
|
_set_attributes(xmpp_stanza_t *stanza, int nattrs, const xmlChar **attrs)
|
||||||
{
|
{
|
||||||
int i;
|
int i, len;
|
||||||
|
char *value;
|
||||||
|
|
||||||
if (!attrs) return;
|
if (!attrs)
|
||||||
|
return;
|
||||||
|
|
||||||
for (i = 0; attrs[i]; i += 2) {
|
/* SAX2 uses array of localname/prefix/uri/value_begin/value_end */
|
||||||
xmpp_stanza_set_attribute(stanza, (const char *)attrs[i], (const char *)attrs[i+1]);
|
for (i = 0; i < nattrs * 5; i += 5) {
|
||||||
|
len = attrs[i + 4] - attrs[i + 3];
|
||||||
|
value = strophe_alloc(stanza->ctx, len + 1);
|
||||||
|
if (value) {
|
||||||
|
memcpy(value, attrs[i + 3], len);
|
||||||
|
value[len] = '\0';
|
||||||
|
xmpp_stanza_set_attribute(stanza, (const char *)attrs[i], value);
|
||||||
|
strophe_free(stanza->ctx, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SAX2 gives us the attrs in an incredibly inconvenient array,
|
||||||
|
* convert it to what the start callback is expecting */
|
||||||
|
static char **
|
||||||
|
_convert_attrs(parser_t *parser, int nattrs, const xmlChar **attrs)
|
||||||
|
{
|
||||||
|
int c, i, o, len;
|
||||||
|
char *value;
|
||||||
|
char **ret;
|
||||||
|
|
||||||
|
if (!attrs)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ret = strophe_alloc(parser->ctx, (nattrs + 1) * 2 * sizeof(char *));
|
||||||
|
if (!ret)
|
||||||
|
return NULL;
|
||||||
|
memset(ret, 0, (nattrs + 1) * 2 * sizeof(char *));
|
||||||
|
|
||||||
|
for (c = 0; c < nattrs; c++) {
|
||||||
|
i = c * 5;
|
||||||
|
o = c * 2;
|
||||||
|
|
||||||
|
len = attrs[i + 4] - attrs[i + 3];
|
||||||
|
value = strophe_alloc(parser->ctx, len + 1);
|
||||||
|
if (value) {
|
||||||
|
memcpy(value, attrs[i + 3], len);
|
||||||
|
value[len] = '\0';
|
||||||
|
ret[o] = strophe_strdup(parser->ctx, (char *)attrs[i]);
|
||||||
|
ret[o + 1] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _free_cbattrs(parser_t *parser, char **attrs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!attrs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; attrs[i]; i += 2) {
|
||||||
|
if (attrs[i])
|
||||||
|
strophe_free(parser->ctx, attrs[i]);
|
||||||
|
if (attrs[i + 1])
|
||||||
|
strophe_free(parser->ctx, attrs[i + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
strophe_free(parser->ctx, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
static void _start_element(void *userdata,
|
static void _start_element(void *userdata,
|
||||||
const xmlChar *name, const xmlChar **attrs)
|
const xmlChar *name,
|
||||||
|
const xmlChar *prefix,
|
||||||
|
const xmlChar *uri,
|
||||||
|
int nnamespaces,
|
||||||
|
const xmlChar **namespaces,
|
||||||
|
int nattrs,
|
||||||
|
int ndefaulted,
|
||||||
|
const xmlChar **attrs)
|
||||||
{
|
{
|
||||||
parser_t *parser = (parser_t *)userdata;
|
parser_t *parser = (parser_t *)userdata;
|
||||||
xmpp_stanza_t *child;
|
xmpp_stanza_t *child;
|
||||||
|
char **cbattrs;
|
||||||
|
|
||||||
|
UNUSED(prefix);
|
||||||
|
UNUSED(nnamespaces);
|
||||||
|
UNUSED(namespaces);
|
||||||
|
UNUSED(ndefaulted);
|
||||||
|
|
||||||
if (parser->depth == 0) {
|
if (parser->depth == 0) {
|
||||||
/* notify the owner */
|
/* notify the owner */
|
||||||
if (parser->startcb)
|
if (parser->startcb) {
|
||||||
parser->startcb((char *)name, (char **)attrs,
|
cbattrs = _convert_attrs(parser, nattrs, attrs);
|
||||||
parser->userdata);
|
parser->startcb((char *)name, cbattrs, parser->userdata);
|
||||||
|
_free_cbattrs(parser, cbattrs);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* build stanzas at depth 1 */
|
/* build stanzas at depth 1 */
|
||||||
if (!parser->stanza && parser->depth != 1) {
|
if (!parser->stanza && parser->depth != 1) {
|
||||||
/* something terrible happened */
|
/* something terrible happened */
|
||||||
/* FIXME: we should probably trigger a disconnect */
|
/* FIXME: we should probably trigger a disconnect */
|
||||||
xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
|
strophe_error(parser->ctx, "parser",
|
||||||
} else if (!parser->stanza) {
|
"oops, where did our stanza go?");
|
||||||
/* starting a new toplevel stanza */
|
} else if (!parser->stanza) {
|
||||||
parser->stanza = xmpp_stanza_new(parser->ctx);
|
/* starting a new toplevel stanza */
|
||||||
if (!parser->stanza) {
|
parser->stanza = xmpp_stanza_new(parser->ctx);
|
||||||
/* FIXME: can't allocate, disconnect */
|
if (!parser->stanza) {
|
||||||
}
|
/* FIXME: can't allocate, disconnect */
|
||||||
xmpp_stanza_set_name(parser->stanza, (char *)name);
|
}
|
||||||
_set_attributes(parser->stanza, attrs);
|
xmpp_stanza_set_name(parser->stanza, (char *)name);
|
||||||
} else {
|
_set_attributes(parser->stanza, nattrs, attrs);
|
||||||
/* starting a child of conn->stanza */
|
if (uri)
|
||||||
child = xmpp_stanza_new(parser->ctx);
|
xmpp_stanza_set_ns(parser->stanza, (char *)uri);
|
||||||
if (!child) {
|
} else {
|
||||||
/* FIXME: can't allocate, disconnect */
|
/* starting a child of conn->stanza */
|
||||||
}
|
child = xmpp_stanza_new(parser->ctx);
|
||||||
xmpp_stanza_set_name(child, (char *)name);
|
if (!child) {
|
||||||
_set_attributes(child, attrs);
|
/* FIXME: can't allocate, disconnect */
|
||||||
|
}
|
||||||
|
xmpp_stanza_set_name(child, (char *)name);
|
||||||
|
_set_attributes(child, nattrs, attrs);
|
||||||
|
if (uri)
|
||||||
|
xmpp_stanza_set_ns(child, (char *)uri);
|
||||||
|
|
||||||
/* add child to parent */
|
/* add child to parent */
|
||||||
xmpp_stanza_add_child(parser->stanza, child);
|
xmpp_stanza_add_child_ex(parser->stanza, child, 0);
|
||||||
|
|
||||||
/* the child is owned by the toplevel stanza now */
|
/* make child the current stanza */
|
||||||
xmpp_stanza_release(child);
|
parser->stanza = child;
|
||||||
|
}
|
||||||
/* make child the current stanza */
|
|
||||||
parser->stanza = child;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parser->depth++;
|
parser->depth++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _end_element(void *userdata, const xmlChar *name)
|
static void _end_element(void *userdata,
|
||||||
|
const xmlChar *name,
|
||||||
|
const xmlChar *prefix,
|
||||||
|
const xmlChar *uri)
|
||||||
{
|
{
|
||||||
parser_t *parser = (parser_t *)userdata;
|
parser_t *parser = (parser_t *)userdata;
|
||||||
|
|
||||||
|
UNUSED(prefix);
|
||||||
|
UNUSED(uri);
|
||||||
|
|
||||||
parser->depth--;
|
parser->depth--;
|
||||||
|
|
||||||
if (parser->depth == 0) {
|
if (parser->depth == 0) {
|
||||||
@@ -109,16 +192,15 @@ static void _end_element(void *userdata, const xmlChar *name)
|
|||||||
if (parser->endcb)
|
if (parser->endcb)
|
||||||
parser->endcb((char *)name, parser->userdata);
|
parser->endcb((char *)name, parser->userdata);
|
||||||
} else {
|
} else {
|
||||||
if (parser->stanza->parent) {
|
if (parser->stanza->parent) {
|
||||||
/* we're finishing a child stanza, so set current to the parent */
|
/* we're finishing a child stanza, so set current to the parent */
|
||||||
parser->stanza = parser->stanza->parent;
|
parser->stanza = parser->stanza->parent;
|
||||||
} else {
|
} else {
|
||||||
if (parser->stanzacb)
|
if (parser->stanzacb)
|
||||||
parser->stanzacb(parser->stanza,
|
parser->stanzacb(parser->stanza, parser->userdata);
|
||||||
parser->userdata);
|
|
||||||
xmpp_stanza_release(parser->stanza);
|
xmpp_stanza_release(parser->stanza);
|
||||||
parser->stanza = NULL;
|
parser->stanza = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,18 +210,18 @@ static void _characters(void *userdata, const xmlChar *chr, int len)
|
|||||||
xmpp_stanza_t *stanza;
|
xmpp_stanza_t *stanza;
|
||||||
|
|
||||||
/* skip unimportant whitespace, etc */
|
/* skip unimportant whitespace, etc */
|
||||||
if (parser->depth < 2) return;
|
if (parser->depth < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
/* create and populate stanza */
|
/* create and populate stanza */
|
||||||
stanza = xmpp_stanza_new(parser->ctx);
|
stanza = xmpp_stanza_new(parser->ctx);
|
||||||
if (!stanza) {
|
if (!stanza) {
|
||||||
/* FIXME: allocation error, disconnect */
|
/* FIXME: allocation error, disconnect */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
xmpp_stanza_set_text_with_size(stanza, (char *)chr, len);
|
xmpp_stanza_set_text_with_size(stanza, (char *)chr, len);
|
||||||
|
|
||||||
xmpp_stanza_add_child(parser->stanza, stanza);
|
xmpp_stanza_add_child_ex(parser->stanza, stanza, 0);
|
||||||
xmpp_stanza_release(stanza);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create a new parser */
|
/* create a new parser */
|
||||||
@@ -151,13 +233,14 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
|
|||||||
{
|
{
|
||||||
parser_t *parser;
|
parser_t *parser;
|
||||||
|
|
||||||
parser = xmpp_alloc(ctx, sizeof(parser_t));
|
parser = strophe_alloc(ctx, sizeof(parser_t));
|
||||||
if (parser != NULL) {
|
if (parser != NULL) {
|
||||||
parser->ctx = ctx;
|
parser->ctx = ctx;
|
||||||
parser->xmlctx = NULL;
|
parser->xmlctx = NULL;
|
||||||
memset(&parser->handlers, 0, sizeof(xmlSAXHandler));
|
memset(&parser->handlers, 0, sizeof(xmlSAXHandler));
|
||||||
parser->handlers.startElement = _start_element;
|
parser->handlers.initialized = XML_SAX2_MAGIC;
|
||||||
parser->handlers.endElement = _end_element;
|
parser->handlers.startElementNs = _start_element;
|
||||||
|
parser->handlers.endElementNs = _end_element;
|
||||||
parser->handlers.characters = _characters;
|
parser->handlers.characters = _characters;
|
||||||
parser->startcb = startcb;
|
parser->startcb = startcb;
|
||||||
parser->endcb = endcb;
|
parser->endcb = endcb;
|
||||||
@@ -172,12 +255,28 @@ parser_t *parser_new(xmpp_ctx_t *ctx,
|
|||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *parser_attr_name(xmpp_ctx_t *ctx, char *nsname)
|
||||||
|
{
|
||||||
|
return strophe_strdup(ctx, nsname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _free_parent_stanza(xmpp_stanza_t *stanza)
|
||||||
|
{
|
||||||
|
xmpp_stanza_t *parent;
|
||||||
|
|
||||||
|
for (parent = stanza; parent->parent != NULL; parent = parent->parent)
|
||||||
|
;
|
||||||
|
xmpp_stanza_release(parent);
|
||||||
|
}
|
||||||
|
|
||||||
/* free a parser */
|
/* free a parser */
|
||||||
void parser_free(parser_t *parser)
|
void parser_free(parser_t *parser)
|
||||||
{
|
{
|
||||||
if (parser->xmlctx)
|
if (parser->xmlctx)
|
||||||
xmlFreeParserCtxt(parser->xmlctx);
|
xmlFreeParserCtxt(parser->xmlctx);
|
||||||
xmpp_free(parser->ctx, parser);
|
if (parser->stanza)
|
||||||
|
_free_parent_stanza(parser->stanza);
|
||||||
|
strophe_free(parser->ctx, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* shuts down and restarts XML parser. true on success */
|
/* shuts down and restarts XML parser. true on success */
|
||||||
@@ -185,28 +284,22 @@ int parser_reset(parser_t *parser)
|
|||||||
{
|
{
|
||||||
if (parser->xmlctx)
|
if (parser->xmlctx)
|
||||||
xmlFreeParserCtxt(parser->xmlctx);
|
xmlFreeParserCtxt(parser->xmlctx);
|
||||||
|
|
||||||
if (parser->stanza)
|
if (parser->stanza)
|
||||||
xmpp_stanza_release(parser->stanza);
|
_free_parent_stanza(parser->stanza);
|
||||||
|
|
||||||
parser->xmlctx = xmlCreatePushParserCtxt(&parser->handlers,
|
|
||||||
parser, NULL, 0, NULL);
|
|
||||||
if (!parser->xmlctx) return 0;
|
|
||||||
|
|
||||||
parser->depth = 0;
|
|
||||||
parser->stanza = NULL;
|
parser->stanza = NULL;
|
||||||
|
parser->depth = 0;
|
||||||
|
|
||||||
return 1;
|
parser->xmlctx =
|
||||||
|
xmlCreatePushParserCtxt(&parser->handlers, parser, NULL, 0, NULL);
|
||||||
|
|
||||||
|
return parser->xmlctx ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* feed a chunk of data to the parser */
|
/* feed a chunk of data to the parser */
|
||||||
int parser_feed(parser_t *parser, char *chunk, int len)
|
int parser_feed(parser_t *parser, char *chunk, int len)
|
||||||
{
|
{
|
||||||
/* xmlParseChunk API returns 0 on success which is opposite logic to
|
/* xmlParseChunk API returns 0 on success which is opposite logic to
|
||||||
the status returned by parser_feed */
|
the status returned by parser_feed */
|
||||||
if(!xmlParseChunk(parser->xmlctx, chunk, len, 0)) {
|
return !xmlParseChunk(parser->xmlctx, chunk, len, 0);
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
388
src/rand.c
Normal file
388
src/rand.c
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* rand.c
|
||||||
|
* strophe XMPP client library -- pseudo-random number generator
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* Pseudo-random number generator.
|
||||||
|
*
|
||||||
|
* Implemented Hash_DRBG mechanism according to NIST SP 800-90A.
|
||||||
|
* Hash function is SHA1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup Random Pseudo-random number generator
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h> /* memeset */
|
||||||
|
#include <time.h> /* clock, time */
|
||||||
|
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(DONT_USE_GETRANDOM) && defined(__linux__) && \
|
||||||
|
defined(__GLIBC_PREREQ)
|
||||||
|
#if __GLIBC_PREREQ(2, 25)
|
||||||
|
#define USE_GETRANDOM
|
||||||
|
#include <sys/random.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common.h" /* strophe_alloc, strophe_free */
|
||||||
|
#include "ostypes.h" /* uint8_t, uint32_t, size_t */
|
||||||
|
|
||||||
|
#ifndef USE_GETRANDOM
|
||||||
|
#include "sha1.h"
|
||||||
|
|
||||||
|
#define outlen SHA1_DIGEST_SIZE
|
||||||
|
#define seedlen (440 / 8)
|
||||||
|
#define reseed_interval 0x7fffffff
|
||||||
|
|
||||||
|
/* maximum number of bytes that can be generated per call */
|
||||||
|
#define GENERATE_MAX (outlen * 10)
|
||||||
|
#define ENTROPY_MAX 128
|
||||||
|
#define NONCE_MAX 8
|
||||||
|
|
||||||
|
#define RESEED_NEEDED (-1)
|
||||||
|
|
||||||
|
struct Hash_DRBG_CTX_struc {
|
||||||
|
uint8_t V[seedlen];
|
||||||
|
uint8_t C[seedlen];
|
||||||
|
uint32_t reseed_counter;
|
||||||
|
};
|
||||||
|
typedef struct Hash_DRBG_CTX_struc Hash_DRBG_CTX;
|
||||||
|
|
||||||
|
struct _xmpp_rand_t {
|
||||||
|
int inited;
|
||||||
|
unsigned reseed_count;
|
||||||
|
Hash_DRBG_CTX ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* returns smallest number mupliple of y that not less than x */
|
||||||
|
#define round_up(x, y) (((x) + (y)-1) / (y) * (y))
|
||||||
|
/* returns smallest integer number that not less than x/y */
|
||||||
|
#define div_round_up(x, y) (((x) + (y)-1) / (y))
|
||||||
|
|
||||||
|
/* adds two arrays as numbers in big-endian representation and stores
|
||||||
|
* result in the first one.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
arr_add(uint8_t *arr1, size_t arr1_len, uint8_t *arr2, size_t arr2_len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint32_t acc;
|
||||||
|
uint32_t carry = 0;
|
||||||
|
|
||||||
|
assert(arr1_len >= arr2_len);
|
||||||
|
|
||||||
|
for (i = 1; (i <= arr2_len) || (carry != 0 && i <= arr1_len); ++i) {
|
||||||
|
acc = (uint32_t)arr1[arr1_len - i] + carry;
|
||||||
|
if (i <= arr2_len)
|
||||||
|
acc += (uint32_t)arr2[arr2_len - i];
|
||||||
|
carry = acc >> 8;
|
||||||
|
arr1[arr1_len - i] = (uint8_t)(acc & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stores 32-bit number in big-endian representation */
|
||||||
|
static void store_be32(uint32_t val, uint8_t be[4])
|
||||||
|
{
|
||||||
|
be[0] = (uint8_t)((val >> 24) & 0xff);
|
||||||
|
be[1] = (uint8_t)((val >> 16) & 0xff);
|
||||||
|
be[2] = (uint8_t)((val >> 8) & 0xff);
|
||||||
|
be[3] = (uint8_t)(val & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Hash_df(uint8_t *input_string,
|
||||||
|
size_t input_string_len,
|
||||||
|
uint8_t *output_string,
|
||||||
|
size_t no_of_bytes_to_return)
|
||||||
|
{
|
||||||
|
uint8_t counter;
|
||||||
|
uint8_t temp[round_up(seedlen, outlen)];
|
||||||
|
uint8_t conj[ENTROPY_MAX + NONCE_MAX + seedlen + 6];
|
||||||
|
size_t len;
|
||||||
|
size_t i;
|
||||||
|
size_t offset;
|
||||||
|
|
||||||
|
assert(no_of_bytes_to_return <= sizeof(temp));
|
||||||
|
assert(input_string_len + 5 <= sizeof(conj));
|
||||||
|
|
||||||
|
len = div_round_up(no_of_bytes_to_return, outlen);
|
||||||
|
for (i = 1; i <= len; ++i) {
|
||||||
|
offset = (i - 1) * outlen;
|
||||||
|
counter = (uint8_t)i;
|
||||||
|
conj[0] = counter;
|
||||||
|
store_be32((uint32_t)no_of_bytes_to_return * 8, conj + 1);
|
||||||
|
memcpy(conj + 5, input_string, input_string_len);
|
||||||
|
crypto_SHA1(conj, input_string_len + 5, temp + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(output_string, temp, no_of_bytes_to_return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assume personalization_string is zero length string */
|
||||||
|
static void Hash_DRBG_Instantiate(Hash_DRBG_CTX *ctx,
|
||||||
|
uint8_t *entropy_input,
|
||||||
|
size_t entropy_input_len,
|
||||||
|
uint8_t *nonce,
|
||||||
|
size_t nonce_len)
|
||||||
|
{
|
||||||
|
uint8_t seed_material[ENTROPY_MAX + NONCE_MAX];
|
||||||
|
uint8_t seed0[seedlen + 1];
|
||||||
|
uint8_t *seed = seed0 + 1;
|
||||||
|
|
||||||
|
assert(entropy_input_len <= ENTROPY_MAX);
|
||||||
|
assert(nonce_len <= NONCE_MAX);
|
||||||
|
assert(nonce != NULL || nonce_len == 0);
|
||||||
|
|
||||||
|
memcpy(seed_material, entropy_input, entropy_input_len);
|
||||||
|
if (nonce != NULL)
|
||||||
|
memcpy(seed_material + entropy_input_len, nonce, nonce_len);
|
||||||
|
Hash_df(seed_material, entropy_input_len + nonce_len, seed, seedlen);
|
||||||
|
seed0[0] = 0;
|
||||||
|
|
||||||
|
memcpy(ctx->V, seed, seedlen);
|
||||||
|
Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
|
||||||
|
ctx->reseed_counter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assume additional_input is zero length string */
|
||||||
|
static void Hash_DRBG_Reseed(Hash_DRBG_CTX *ctx,
|
||||||
|
uint8_t *entropy_input,
|
||||||
|
size_t entropy_input_len)
|
||||||
|
{
|
||||||
|
uint8_t seed_material[1 + seedlen + ENTROPY_MAX];
|
||||||
|
uint8_t seed0[seedlen + 1];
|
||||||
|
uint8_t *seed = seed0 + 1;
|
||||||
|
|
||||||
|
assert(entropy_input_len <= ENTROPY_MAX);
|
||||||
|
|
||||||
|
seed_material[0] = 1;
|
||||||
|
memcpy(seed_material + 1, ctx->V, seedlen);
|
||||||
|
memcpy(seed_material + 1 + seedlen, entropy_input, entropy_input_len);
|
||||||
|
Hash_df(seed_material, entropy_input_len + seedlen + 1, seed, seedlen);
|
||||||
|
seed0[0] = 0;
|
||||||
|
|
||||||
|
memcpy(ctx->V, seed, seedlen);
|
||||||
|
Hash_df(seed0, sizeof(seed0), ctx->C, seedlen);
|
||||||
|
ctx->reseed_counter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Hashgen(uint8_t *V, uint8_t *output, size_t requested_number_of_bytes)
|
||||||
|
{
|
||||||
|
uint8_t data[seedlen];
|
||||||
|
uint8_t W[GENERATE_MAX];
|
||||||
|
uint8_t i1 = 1;
|
||||||
|
size_t m;
|
||||||
|
size_t i;
|
||||||
|
size_t offset;
|
||||||
|
|
||||||
|
assert(requested_number_of_bytes <= sizeof(W));
|
||||||
|
|
||||||
|
m = div_round_up(requested_number_of_bytes, outlen);
|
||||||
|
memcpy(data, V, seedlen);
|
||||||
|
for (i = 1; i <= m; ++i) {
|
||||||
|
offset = (i - 1) * outlen;
|
||||||
|
crypto_SHA1(data, seedlen, W + offset);
|
||||||
|
/* increase data by 1 */
|
||||||
|
arr_add(data, sizeof(data), &i1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(output, W, requested_number_of_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assume additional_input is zero length string */
|
||||||
|
static int Hash_DRBG_Generate(Hash_DRBG_CTX *ctx,
|
||||||
|
uint8_t *output,
|
||||||
|
size_t requested_number_of_bytes)
|
||||||
|
{
|
||||||
|
uint8_t H[outlen];
|
||||||
|
uint8_t V3[seedlen + 1];
|
||||||
|
uint8_t reseed_counter[4];
|
||||||
|
|
||||||
|
if (ctx->reseed_counter > reseed_interval || ctx->reseed_counter == 0)
|
||||||
|
return RESEED_NEEDED;
|
||||||
|
|
||||||
|
Hashgen(ctx->V, output, requested_number_of_bytes);
|
||||||
|
|
||||||
|
V3[0] = 3;
|
||||||
|
memcpy(V3 + 1, ctx->V, seedlen);
|
||||||
|
crypto_SHA1(V3, sizeof(V3), H);
|
||||||
|
arr_add(ctx->V, sizeof(ctx->V), ctx->C, sizeof(ctx->C));
|
||||||
|
arr_add(ctx->V, sizeof(ctx->V), H, sizeof(H));
|
||||||
|
store_be32(ctx->reseed_counter, reseed_counter);
|
||||||
|
arr_add(ctx->V, sizeof(ctx->V), reseed_counter, sizeof(reseed_counter));
|
||||||
|
|
||||||
|
++ctx->reseed_counter;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ENTROPY_ACCUMULATE(ptr, last, type, arg) \
|
||||||
|
do { \
|
||||||
|
type __arg = (type)(arg); \
|
||||||
|
if ((char *)ptr + sizeof(__arg) < (char *)last) { \
|
||||||
|
*(type *)ptr = __arg; \
|
||||||
|
ptr = (void *)((char *)ptr + sizeof(__arg)); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static void xmpp_rand_reseed(xmpp_rand_t *rand)
|
||||||
|
{
|
||||||
|
uint8_t entropy[ENTROPY_MAX];
|
||||||
|
uint8_t *ptr = entropy;
|
||||||
|
const uint8_t *last = entropy + sizeof(entropy);
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
/* entropy:
|
||||||
|
* 1. time_stamp()
|
||||||
|
* 2. clock(3)
|
||||||
|
* 3. xmpp_rand_t address to make unique seed within one process
|
||||||
|
* 4. counter to make unique seed within one context
|
||||||
|
* 5. stack address
|
||||||
|
* 6. local ports of every connection in list (getsockname)
|
||||||
|
* 7. other non-constant info that can be retieved from socket
|
||||||
|
*
|
||||||
|
* rand(3) can't be used as it isn't thread-safe.
|
||||||
|
* XXX 6 and 7 are not implemented yet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTROPY_ACCUMULATE(ptr, last, uint64_t, time_stamp());
|
||||||
|
ENTROPY_ACCUMULATE(ptr, last, clock_t, clock());
|
||||||
|
ENTROPY_ACCUMULATE(ptr, last, void *, rand);
|
||||||
|
ENTROPY_ACCUMULATE(ptr, last, unsigned, ++rand->reseed_count);
|
||||||
|
ENTROPY_ACCUMULATE(ptr, last, void *, &entropy);
|
||||||
|
len = ptr - entropy;
|
||||||
|
|
||||||
|
if (rand->inited) {
|
||||||
|
Hash_DRBG_Reseed(&rand->ctx, entropy, len);
|
||||||
|
} else {
|
||||||
|
Hash_DRBG_Instantiate(&rand->ctx, entropy, len, NULL, 0);
|
||||||
|
rand->inited = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
xmpp_rand_t *out = strophe_alloc(ctx, sizeof(*out));
|
||||||
|
if (out != NULL) {
|
||||||
|
memset(out, 0, sizeof(*out));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
|
||||||
|
{
|
||||||
|
strophe_free(ctx, rand);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
size_t gen, tot = 0;
|
||||||
|
while (tot < len) {
|
||||||
|
gen = len - tot;
|
||||||
|
if (gen > GENERATE_MAX)
|
||||||
|
gen = GENERATE_MAX;
|
||||||
|
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output + tot, gen);
|
||||||
|
if (rc == RESEED_NEEDED) {
|
||||||
|
xmpp_rand_reseed(rand);
|
||||||
|
rc = Hash_DRBG_Generate(&rand->ctx, (uint8_t *)output + tot, gen);
|
||||||
|
assert(rc == 0);
|
||||||
|
}
|
||||||
|
tot += gen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static int _read_getrandom(void *p, size_t n)
|
||||||
|
{
|
||||||
|
unsigned char *q = (unsigned char *)p;
|
||||||
|
while (n > 0u) {
|
||||||
|
ssize_t ret = getrandom(q, n, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
q += ret;
|
||||||
|
n -= (size_t)ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _xmpp_rand_t {
|
||||||
|
char nothing;
|
||||||
|
};
|
||||||
|
|
||||||
|
static xmpp_rand_t _xmpp_rand;
|
||||||
|
|
||||||
|
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
UNUSED(ctx);
|
||||||
|
return &_xmpp_rand;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand)
|
||||||
|
{
|
||||||
|
UNUSED(ctx);
|
||||||
|
assert(rand == &_xmpp_rand);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len)
|
||||||
|
{
|
||||||
|
assert(rand == &_xmpp_rand);
|
||||||
|
assert(_read_getrandom(output, len) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int xmpp_rand(xmpp_rand_t *rand)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
xmpp_rand_bytes(rand, (unsigned char *)&result, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rand_byte2hex(unsigned char byte, char *hex)
|
||||||
|
{
|
||||||
|
static const char hex_tbl[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
|
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||||
|
|
||||||
|
hex[0] = hex_tbl[(byte >> 4) & 0x0f];
|
||||||
|
hex[1] = hex_tbl[byte & 0x0f];
|
||||||
|
}
|
||||||
|
|
||||||
|
void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
const size_t rand_len = len / 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't want to use any allocation here, because this function
|
||||||
|
* can't fail. Also we want to avoid VLA.
|
||||||
|
* Current implementation uses half of the output buffer for random buffer
|
||||||
|
* generation and then converts it to HEX representation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (rand_len > 0) {
|
||||||
|
xmpp_rand_bytes(rand, (unsigned char *)output, rand_len);
|
||||||
|
for (i = rand_len; i > 0; --i)
|
||||||
|
rand_byte2hex(output[i - 1], &output[(i - 1) * 2]);
|
||||||
|
}
|
||||||
|
if (len > 0)
|
||||||
|
output[len - 1] = '\0';
|
||||||
|
}
|
||||||
1004
src/resolver.c
Normal file
1004
src/resolver.c
Normal file
File diff suppressed because it is too large
Load Diff
84
src/resolver.h
Normal file
84
src/resolver.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* resolver.h
|
||||||
|
* strophe XMPP client library -- DNS resolver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* DNS resolver.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBSTROPHE_RESOLVER_H__
|
||||||
|
#define __LIBSTROPHE_RESOLVER_H__
|
||||||
|
|
||||||
|
#include "ostypes.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
XMPP_DOMAIN_NOT_FOUND,
|
||||||
|
XMPP_DOMAIN_FOUND,
|
||||||
|
XMPP_DOMAIN_ALTDOMAIN
|
||||||
|
} xmpp_domain_state_t;
|
||||||
|
|
||||||
|
typedef struct resolver_srv_rr_struc {
|
||||||
|
uint16_t priority;
|
||||||
|
uint16_t weight;
|
||||||
|
uint16_t port;
|
||||||
|
char target[MAX_DOMAIN_LEN];
|
||||||
|
struct resolver_srv_rr_struc *next;
|
||||||
|
} resolver_srv_rr_t;
|
||||||
|
|
||||||
|
void resolver_initialize(void);
|
||||||
|
void resolver_shutdown(void);
|
||||||
|
|
||||||
|
resolver_srv_rr_t *resolver_srv_rr_new(xmpp_ctx_t *ctx,
|
||||||
|
const char *host,
|
||||||
|
unsigned short port,
|
||||||
|
unsigned short prio,
|
||||||
|
unsigned short weight);
|
||||||
|
|
||||||
|
/** Perform lookup for RFC1035 message format.
|
||||||
|
* This function allocates all elements.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param buf message in RFC1035 format
|
||||||
|
* @param len length of the message
|
||||||
|
* @param srv_rr_list is the result
|
||||||
|
*
|
||||||
|
* @return XMPP_DOMAIN_FOUND on success or XMPP_DOMAIN_NOT_FOUND on fail
|
||||||
|
*/
|
||||||
|
int resolver_srv_lookup_buf(xmpp_ctx_t *ctx,
|
||||||
|
const unsigned char *buf,
|
||||||
|
size_t len,
|
||||||
|
resolver_srv_rr_t **srv_rr_list);
|
||||||
|
/** Resolve SRV record.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param service service of the SRV record
|
||||||
|
* @param proto protocol of the SRV record
|
||||||
|
* @param domain resolving domain
|
||||||
|
* @param srv_rr_list is the result
|
||||||
|
*
|
||||||
|
* @return XMPP_DOMAIN_FOUND on success or XMPP_DOMAIN_NOT_FOUND on fail
|
||||||
|
*/
|
||||||
|
int resolver_srv_lookup(xmpp_ctx_t *ctx,
|
||||||
|
const char *service,
|
||||||
|
const char *proto,
|
||||||
|
const char *domain,
|
||||||
|
resolver_srv_rr_t **srv_rr_list);
|
||||||
|
|
||||||
|
/** Release a list returned by resolver_srv_lookup() or
|
||||||
|
* resolver_srv_lookup_buf().
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param srv_rr_list a list allocated by lookup functions
|
||||||
|
*/
|
||||||
|
void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list);
|
||||||
|
|
||||||
|
#endif /* __LIBSTROPHE_RESOLVER_H__ */
|
||||||
635
src/sasl.c
635
src/sasl.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* sasl.c
|
/* sasl.c
|
||||||
** strophe XMPP client library -- SASL authentication helpers
|
** strophe XMPP client library -- SASL authentication helpers
|
||||||
**
|
**
|
||||||
@@ -6,42 +7,39 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
* SASL authentication.
|
* SASL authentication.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "strophe.h"
|
#include "strophe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "ostypes.h"
|
||||||
#include "sasl.h"
|
#include "sasl.h"
|
||||||
#include "md5.h"
|
#include "md5.h"
|
||||||
|
#include "scram.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
/* make sure the stdint.h types are available */
|
/* strtok_s() has appeared in visual studio 2005.
|
||||||
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
Use own implementation for older versions. */
|
||||||
typedef signed char int8_t;
|
#ifdef _MSC_VER
|
||||||
typedef short int int16_t;
|
#if (_MSC_VER >= 1400)
|
||||||
typedef int int32_t;
|
#define strtok_r strtok_s
|
||||||
typedef __int64 int64_t;
|
|
||||||
|
|
||||||
typedef unsigned char uint8_t;
|
|
||||||
typedef unsigned short int uint16_t;
|
|
||||||
typedef unsigned int uint32_t;
|
|
||||||
/* no uint64_t */
|
|
||||||
#else
|
#else
|
||||||
#include <stdint.h>
|
#define strtok_r xmpp_strtok_r
|
||||||
#endif
|
#endif
|
||||||
|
#endif /* _MSC_VER */
|
||||||
|
|
||||||
/** generate authentication string for the SASL PLAIN mechanism */
|
/** generate authentication string for the SASL PLAIN mechanism */
|
||||||
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
|
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password)
|
||||||
int idlen, passlen;
|
{
|
||||||
|
size_t idlen, passlen;
|
||||||
|
size_t msglen;
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
char *msg;
|
char *msg;
|
||||||
|
|
||||||
@@ -50,14 +48,15 @@ char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
|
|||||||
|
|
||||||
idlen = strlen(authid);
|
idlen = strlen(authid);
|
||||||
passlen = strlen(password);
|
passlen = strlen(password);
|
||||||
msg = xmpp_alloc(ctx, 2 + idlen + passlen);
|
msglen = 2 + idlen + passlen;
|
||||||
|
msg = strophe_alloc(ctx, msglen);
|
||||||
if (msg != NULL) {
|
if (msg != NULL) {
|
||||||
msg[0] = '\0';
|
msg[0] = '\0';
|
||||||
memcpy(msg+1, authid, idlen);
|
memcpy(msg + 1, authid, idlen);
|
||||||
msg[1+idlen] = '\0';
|
msg[1 + idlen] = '\0';
|
||||||
memcpy(msg+1+idlen+1, password, passlen);
|
memcpy(msg + 1 + idlen + 1, password, passlen);
|
||||||
result = base64_encode(ctx, (unsigned char *)msg, 2 + idlen + passlen);
|
result = xmpp_base64_encode(ctx, (unsigned char *)msg, msglen);
|
||||||
xmpp_free(ctx, msg);
|
strophe_free(ctx, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -66,14 +65,14 @@ char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password) {
|
|||||||
/** helpers for digest auth */
|
/** helpers for digest auth */
|
||||||
|
|
||||||
/* create a new, null-terminated string from a substring */
|
/* create a new, null-terminated string from a substring */
|
||||||
static char *_make_string(xmpp_ctx_t *ctx, const char *s, const unsigned len)
|
static char *_make_string(xmpp_ctx_t *ctx, const char *s, unsigned len)
|
||||||
{
|
{
|
||||||
char *result;
|
char *result;
|
||||||
|
|
||||||
result = xmpp_alloc(ctx, len + 1);
|
result = strophe_alloc(ctx, len + 1);
|
||||||
if (result != NULL) {
|
if (result != NULL) {
|
||||||
memcpy(result, s, len);
|
memcpy(result, s, len);
|
||||||
result[len] = '\0';
|
result[len] = '\0';
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -82,14 +81,14 @@ static char *_make_string(xmpp_ctx_t *ctx, const char *s, const unsigned len)
|
|||||||
static char *_make_quoted(xmpp_ctx_t *ctx, const char *s)
|
static char *_make_quoted(xmpp_ctx_t *ctx, const char *s)
|
||||||
{
|
{
|
||||||
char *result;
|
char *result;
|
||||||
int len = strlen(s);
|
size_t len = strlen(s);
|
||||||
|
|
||||||
result = xmpp_alloc(ctx, len + 3);
|
result = strophe_alloc(ctx, len + 3);
|
||||||
if (result != NULL) {
|
if (result != NULL) {
|
||||||
result[0] = '"';
|
result[0] = '"';
|
||||||
memcpy(result+1, s, len);
|
memcpy(result + 1, s, len);
|
||||||
result[len+1] = '"';
|
result[len + 1] = '"';
|
||||||
result[len+2] = '\0';
|
result[len + 2] = '\0';
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -102,55 +101,60 @@ static hash_t *_parse_digest_challenge(xmpp_ctx_t *ctx, const char *msg)
|
|||||||
char *key, *value;
|
char *key, *value;
|
||||||
unsigned char *s, *t;
|
unsigned char *s, *t;
|
||||||
|
|
||||||
text = base64_decode(ctx, msg, strlen(msg));
|
text = (unsigned char *)xmpp_base64_decode_str(ctx, msg, strlen(msg));
|
||||||
if (text == NULL) {
|
if (text == NULL) {
|
||||||
xmpp_error(ctx, "SASL", "couldn't Base64 decode challenge!");
|
strophe_error(ctx, "SASL", "couldn't Base64 decode challenge!");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = hash_new(ctx, 10, xmpp_free);
|
result = hash_new(ctx, 10, strophe_free);
|
||||||
if (result != NULL) {
|
if (result != NULL) {
|
||||||
s = text;
|
s = text;
|
||||||
while (*s != '\0') {
|
while (*s != '\0') {
|
||||||
/* skip any leading commas and spaces */
|
/* skip any leading commas and spaces */
|
||||||
while ((*s == ',') || (*s == ' ')) s++;
|
while ((*s == ',') || (*s == ' '))
|
||||||
/* accumulate a key ending at '=' */
|
s++;
|
||||||
t = s;
|
/* accumulate a key ending at '=' */
|
||||||
while ((*t != '=') && (*t != '\0')) t++;
|
t = s;
|
||||||
if (*t == '\0') break; /* bad string */
|
while ((*t != '=') && (*t != '\0'))
|
||||||
key = _make_string(ctx, (char *)s, (t-s));
|
t++;
|
||||||
if (key == NULL) break;
|
if (*t == '\0')
|
||||||
|
break; /* bad string */
|
||||||
|
key = _make_string(ctx, (char *)s, (t - s));
|
||||||
|
if (key == NULL)
|
||||||
|
break;
|
||||||
/* advance our start pointer past the key */
|
/* advance our start pointer past the key */
|
||||||
s = t + 1;
|
s = t + 1;
|
||||||
t = s;
|
t = s;
|
||||||
/* if we see quotes, grab the string in between */
|
/* if we see quotes, grab the string in between */
|
||||||
if ((*s == '\'') || (*s == '"')) {
|
if ((*s == '\'') || (*s == '"')) {
|
||||||
t++;
|
t++;
|
||||||
while ((*t != *s) && (*t != '\0'))
|
while ((*t != *s) && (*t != '\0'))
|
||||||
t++;
|
t++;
|
||||||
value = _make_string(ctx, (char *)s+1, (t-s-1));
|
value = _make_string(ctx, (char *)s + 1, (t - s - 1));
|
||||||
if (*t == *s) {
|
if (*t == *s) {
|
||||||
s = t + 1;
|
s = t + 1;
|
||||||
} else {
|
} else {
|
||||||
s = t;
|
s = t;
|
||||||
}
|
}
|
||||||
/* otherwise, accumulate a value ending in ',' or '\0' */
|
/* otherwise, accumulate a value ending in ',' or '\0' */
|
||||||
} else {
|
} else {
|
||||||
while ((*t != ',') && (*t != '\0')) t++;
|
while ((*t != ',') && (*t != '\0'))
|
||||||
value = _make_string(ctx, (char *)s, (t-s));
|
t++;
|
||||||
s = t;
|
value = _make_string(ctx, (char *)s, (t - s));
|
||||||
}
|
s = t;
|
||||||
if (value == NULL) {
|
}
|
||||||
xmpp_free(ctx, key);
|
if (value == NULL) {
|
||||||
break;
|
strophe_free(ctx, key);
|
||||||
}
|
break;
|
||||||
/* TODO: check for collisions per spec */
|
}
|
||||||
hash_add(result, key, value);
|
/* TODO: check for collisions per spec */
|
||||||
/* hash table now owns the value, free the key */
|
hash_add(result, key, value);
|
||||||
xmpp_free(ctx, key);
|
/* hash table now owns the value, free the key */
|
||||||
}
|
strophe_free(ctx, key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
xmpp_free(ctx, text);
|
strophe_free(ctx, text);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -162,89 +166,97 @@ static void _digest_to_hex(const char *digest, char *hex)
|
|||||||
const char hexdigit[] = "0123456789abcdef";
|
const char hexdigit[] = "0123456789abcdef";
|
||||||
|
|
||||||
for (i = 0; i < 16; i++) {
|
for (i = 0; i < 16; i++) {
|
||||||
*hex++ = hexdigit[ (digest[i] >> 4) & 0x0F ];
|
*hex++ = hexdigit[(digest[i] >> 4) & 0x0F];
|
||||||
*hex++ = hexdigit[ digest[i] & 0x0F ];
|
*hex++ = hexdigit[digest[i] & 0x0F];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** append 'key="value"' to a buffer, growing as necessary */
|
/** append 'key="value"' to a buffer, growing as necessary */
|
||||||
static char *_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key,
|
static char *
|
||||||
char *buf, int *len, int quote)
|
_add_key(xmpp_ctx_t *ctx, hash_t *table, const char *key, char *buf, int quote)
|
||||||
{
|
{
|
||||||
int olen,nlen;
|
int olen, nlen;
|
||||||
int keylen, valuelen;
|
int keylen, valuelen;
|
||||||
const char *value, *qvalue;
|
const char *value, *qvalue;
|
||||||
char *c;
|
char *c;
|
||||||
|
|
||||||
/* allocate a zero-length string if necessary */
|
/* allocate a zero-length string if necessary */
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
buf = xmpp_alloc(ctx, 1);
|
buf = strophe_alloc(ctx, 1);
|
||||||
buf[0] = '\0';
|
buf[0] = '\0';
|
||||||
}
|
}
|
||||||
if (buf == NULL) return NULL;
|
if (buf == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/* get current string length */
|
/* get current string length */
|
||||||
olen = strlen(buf);
|
olen = strlen(buf);
|
||||||
value = hash_get(table, key);
|
value = hash_get(table, key);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
xmpp_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
|
strophe_error(ctx, "SASL", "couldn't retrieve value for '%s'", key);
|
||||||
value = "";
|
value = "";
|
||||||
}
|
}
|
||||||
if (quote) {
|
if (quote) {
|
||||||
qvalue = _make_quoted(ctx, value);
|
qvalue = _make_quoted(ctx, value);
|
||||||
} else {
|
} else {
|
||||||
qvalue = value;
|
qvalue = value;
|
||||||
}
|
}
|
||||||
/* added length is key + '=' + value */
|
/* added length is key + '=' + value */
|
||||||
/* (+ ',' if we're not the first entry */
|
/* (+ ',' if we're not the first entry */
|
||||||
keylen = strlen(key);
|
keylen = strlen(key);
|
||||||
valuelen = strlen(qvalue);
|
valuelen = strlen(qvalue);
|
||||||
nlen = (olen ? 1 : 0) + keylen + 1 + valuelen + 1;
|
nlen = (olen ? 1 : 0) + keylen + 1 + valuelen + 1;
|
||||||
buf = xmpp_realloc(ctx, buf, olen+nlen);
|
buf = strophe_realloc(ctx, buf, olen + nlen);
|
||||||
|
|
||||||
if (buf != NULL) {
|
if (buf != NULL) {
|
||||||
c = buf + olen;
|
c = buf + olen;
|
||||||
if (olen) *c++ = ',';
|
if (olen)
|
||||||
memcpy(c, key, keylen); c += keylen;
|
*c++ = ',';
|
||||||
*c++ = '=';
|
memcpy(c, key, keylen);
|
||||||
memcpy(c, qvalue, valuelen); c += valuelen;
|
c += keylen;
|
||||||
*c++ = '\0';
|
*c++ = '=';
|
||||||
|
memcpy(c, qvalue, valuelen);
|
||||||
|
c += valuelen;
|
||||||
|
*c++ = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quote) xmpp_free(ctx, (char *)qvalue);
|
if (quote)
|
||||||
|
strophe_free(ctx, (char *)qvalue);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** generate auth response string for the SASL DIGEST-MD5 mechanism */
|
/** generate auth response string for the SASL DIGEST-MD5 mechanism */
|
||||||
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
char *sasl_digest_md5(xmpp_ctx_t *ctx,
|
||||||
const char *jid, const char *password) {
|
const char *challenge,
|
||||||
|
const char *jid,
|
||||||
|
const char *password)
|
||||||
|
{
|
||||||
hash_t *table;
|
hash_t *table;
|
||||||
char *result = NULL;
|
char *result = NULL;
|
||||||
char *node, *domain, *realm;
|
char *node, *domain, *realm;
|
||||||
char *value;
|
char *value;
|
||||||
char *response;
|
char *response;
|
||||||
int rlen;
|
|
||||||
struct MD5Context MD5;
|
struct MD5Context MD5;
|
||||||
unsigned char digest[16], HA1[16], HA2[16];
|
unsigned char digest[16], HA1[16], HA2[16];
|
||||||
char hex[32];
|
char hex[32];
|
||||||
|
char cnonce[13];
|
||||||
|
|
||||||
/* our digest response is
|
/* our digest response is
|
||||||
Hex( KD( HEX(MD5(A1)),
|
Hex( KD( HEX(MD5(A1)),
|
||||||
nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2))
|
nonce ':' nc ':' cnonce ':' qop ':' HEX(MD5(A2))
|
||||||
))
|
))
|
||||||
|
|
||||||
where KD(k, s) = MD5(k ':' s),
|
where KD(k, s) = MD5(k ':' s),
|
||||||
A1 = MD5( node ':' realm ':' password ) ':' nonce ':' cnonce
|
A1 = MD5( node ':' realm ':' password ) ':' nonce ':' cnonce
|
||||||
A2 = "AUTHENTICATE" ':' "xmpp/" domain
|
A2 = "AUTHENTICATE" ':' "xmpp/" domain
|
||||||
|
|
||||||
If there is an authzid it is ':'-appended to A1 */
|
If there is an authzid it is ':'-appended to A1 */
|
||||||
|
|
||||||
/* parse the challenge */
|
/* parse the challenge */
|
||||||
table = _parse_digest_challenge(ctx, challenge);
|
table = _parse_digest_challenge(ctx, challenge);
|
||||||
if (table == NULL) {
|
if (table == NULL) {
|
||||||
xmpp_error(ctx, "SASL", "couldn't parse digest challenge");
|
strophe_error(ctx, "SASL", "couldn't parse digest challenge");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = xmpp_jid_node(ctx, jid);
|
node = xmpp_jid_node(ctx, jid);
|
||||||
@@ -254,20 +266,21 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
server */
|
server */
|
||||||
realm = hash_get(table, "realm");
|
realm = hash_get(table, "realm");
|
||||||
if (realm == NULL || strlen(realm) == 0) {
|
if (realm == NULL || strlen(realm) == 0) {
|
||||||
hash_add(table, "realm", xmpp_strdup(ctx, domain));
|
hash_add(table, "realm", strophe_strdup(ctx, domain));
|
||||||
realm = hash_get(table, "realm");
|
realm = hash_get(table, "realm");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add our response fields */
|
/* add our response fields */
|
||||||
hash_add(table, "username", xmpp_strdup(ctx, node));
|
hash_add(table, "username", strophe_strdup(ctx, node));
|
||||||
/* TODO: generate a random cnonce */
|
xmpp_rand_nonce(ctx->rand, cnonce, sizeof(cnonce));
|
||||||
hash_add(table, "cnonce", xmpp_strdup(ctx, "00DEADBEEF00"));
|
hash_add(table, "cnonce", strophe_strdup(ctx, cnonce));
|
||||||
hash_add(table, "nc", xmpp_strdup(ctx, "00000001"));
|
hash_add(table, "nc", strophe_strdup(ctx, "00000001"));
|
||||||
hash_add(table, "qop", xmpp_strdup(ctx, "auth"));
|
if (hash_get(table, "qop") == NULL)
|
||||||
value = xmpp_alloc(ctx, 5 + strlen(domain) + 1);
|
hash_add(table, "qop", strophe_strdup(ctx, "auth"));
|
||||||
|
value = strophe_alloc(ctx, 5 + strlen(domain) + 1);
|
||||||
memcpy(value, "xmpp/", 5);
|
memcpy(value, "xmpp/", 5);
|
||||||
memcpy(value+5, domain, strlen(domain));
|
memcpy(value + 5, domain, strlen(domain));
|
||||||
value[5+strlen(domain)] = '\0';
|
value[5 + strlen(domain)] = '\0';
|
||||||
hash_add(table, "digest-uri", value);
|
hash_add(table, "digest-uri", value);
|
||||||
|
|
||||||
/* generate response */
|
/* generate response */
|
||||||
@@ -302,8 +315,8 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
value = hash_get(table, "digest-uri");
|
value = hash_get(table, "digest-uri");
|
||||||
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
MD5Update(&MD5, (unsigned char *)value, strlen(value));
|
||||||
if (strcmp(hash_get(table, "qop"), "auth") != 0) {
|
if (strcmp(hash_get(table, "qop"), "auth") != 0) {
|
||||||
MD5Update(&MD5, (unsigned char *)":00000000000000000000000000000000",
|
MD5Update(&MD5, (unsigned char *)":00000000000000000000000000000000",
|
||||||
33);
|
33);
|
||||||
}
|
}
|
||||||
MD5Final(digest, &MD5);
|
MD5Final(digest, &MD5);
|
||||||
|
|
||||||
@@ -330,7 +343,7 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
MD5Update(&MD5, (unsigned char *)hex, 32);
|
MD5Update(&MD5, (unsigned char *)hex, 32);
|
||||||
MD5Final(digest, &MD5);
|
MD5Final(digest, &MD5);
|
||||||
|
|
||||||
response = xmpp_alloc(ctx, 32+1);
|
response = strophe_alloc(ctx, 32 + 1);
|
||||||
_digest_to_hex((char *)digest, hex);
|
_digest_to_hex((char *)digest, hex);
|
||||||
memcpy(response, hex, 32);
|
memcpy(response, hex, 32);
|
||||||
response[32] = '\0';
|
response[32] = '\0';
|
||||||
@@ -338,278 +351,142 @@ char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
|||||||
|
|
||||||
/* construct reply */
|
/* construct reply */
|
||||||
result = NULL;
|
result = NULL;
|
||||||
rlen = 0;
|
result = _add_key(ctx, table, "username", result, 1);
|
||||||
result = _add_key(ctx, table, "username", result, &rlen, 1);
|
result = _add_key(ctx, table, "realm", result, 1);
|
||||||
result = _add_key(ctx, table, "realm", result, &rlen, 1);
|
result = _add_key(ctx, table, "nonce", result, 1);
|
||||||
result = _add_key(ctx, table, "nonce", result, &rlen, 1);
|
result = _add_key(ctx, table, "cnonce", result, 1);
|
||||||
result = _add_key(ctx, table, "cnonce", result, &rlen, 1);
|
result = _add_key(ctx, table, "nc", result, 0);
|
||||||
result = _add_key(ctx, table, "nc", result, &rlen, 0);
|
result = _add_key(ctx, table, "qop", result, 0);
|
||||||
result = _add_key(ctx, table, "qop", result, &rlen, 0);
|
result = _add_key(ctx, table, "digest-uri", result, 1);
|
||||||
result = _add_key(ctx, table, "digest-uri", result, &rlen, 1);
|
result = _add_key(ctx, table, "response", result, 0);
|
||||||
result = _add_key(ctx, table, "response", result, &rlen, 0);
|
result = _add_key(ctx, table, "charset", result, 0);
|
||||||
result = _add_key(ctx, table, "charset", result, &rlen, 0);
|
|
||||||
|
|
||||||
xmpp_free(ctx, node);
|
strophe_free(ctx, node);
|
||||||
xmpp_free(ctx, domain);
|
strophe_free(ctx, domain);
|
||||||
hash_release(table); /* also frees value strings */
|
hash_release(table); /* also frees value strings */
|
||||||
|
|
||||||
/* reuse response for the base64 encode of our result */
|
/* reuse response for the base64 encode of our result */
|
||||||
response = base64_encode(ctx, (unsigned char *)result, strlen(result));
|
response = xmpp_base64_encode(ctx, (unsigned char *)result, strlen(result));
|
||||||
xmpp_free(ctx, result);
|
strophe_free(ctx, result);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** generate auth response string for the SASL SCRAM mechanism */
|
||||||
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
char *sasl_scram(xmpp_ctx_t *ctx,
|
||||||
|
const struct hash_alg *alg,
|
||||||
/** map of all byte values to the base64 values, or to
|
const char *channel_binding,
|
||||||
'65' which indicates an invalid character. '=' is '64' */
|
const char *challenge,
|
||||||
static const char _base64_invcharmap[256] = {
|
const char *first_bare,
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
const char *jid,
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
const char *password)
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,62, 65,65,65,63,
|
|
||||||
52,53,54,55, 56,57,58,59, 60,61,65,65, 65,64,65,65,
|
|
||||||
65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
|
||||||
15,16,17,18, 19,20,21,22, 23,24,25,65, 65,65,65,65,
|
|
||||||
65,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
|
||||||
41,42,43,44, 45,46,47,48, 49,50,51,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65,
|
|
||||||
65,65,65,65, 65,65,65,65, 65,65,65,65, 65,65,65,65
|
|
||||||
};
|
|
||||||
|
|
||||||
/** map of all 6-bit values to their corresponding byte
|
|
||||||
in the base64 alphabet. Padding char is the value '64' */
|
|
||||||
static const char _base64_charmap[65] = {
|
|
||||||
'A','B','C','D', 'E','F','G','H',
|
|
||||||
'I','J','K','L', 'M','N','O','P',
|
|
||||||
'Q','R','S','T', 'U','V','W','X',
|
|
||||||
'Y','Z','a','b', 'c','d','e','f',
|
|
||||||
'g','h','i','j', 'k','l','m','n',
|
|
||||||
'o','p','q','r', 's','t','u','v',
|
|
||||||
'w','x','y','z', '0','1','2','3',
|
|
||||||
'4','5','6','7', '8','9','+','/',
|
|
||||||
'='
|
|
||||||
};
|
|
||||||
|
|
||||||
int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len)
|
|
||||||
{
|
{
|
||||||
/* encoded steam is 4 bytes for every three, rounded up */
|
uint8_t key[SCRAM_DIGEST_SIZE];
|
||||||
return ((len + 2)/3) << 2;
|
uint8_t sign[SCRAM_DIGEST_SIZE];
|
||||||
}
|
char *r = NULL;
|
||||||
|
char *s = NULL;
|
||||||
|
char *i = NULL;
|
||||||
|
unsigned char *sval;
|
||||||
|
size_t sval_len;
|
||||||
|
long ival;
|
||||||
|
char *tmp;
|
||||||
|
char *ptr;
|
||||||
|
char *saveptr = NULL;
|
||||||
|
char *response;
|
||||||
|
char *auth;
|
||||||
|
char *response_b64;
|
||||||
|
char *sign_b64;
|
||||||
|
char *result = NULL;
|
||||||
|
size_t response_len;
|
||||||
|
size_t auth_len;
|
||||||
|
int l;
|
||||||
|
|
||||||
char *base64_encode(xmpp_ctx_t *ctx,
|
UNUSED(jid);
|
||||||
const unsigned char * const buffer, const unsigned len)
|
|
||||||
{
|
|
||||||
int clen;
|
|
||||||
char *cbuf, *c;
|
|
||||||
uint32_t word, hextet;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
clen = base64_encoded_len(ctx, len);
|
tmp = strophe_strdup(ctx, challenge);
|
||||||
cbuf = xmpp_alloc(ctx, clen + 1);
|
if (!tmp) {
|
||||||
if (cbuf != NULL) {
|
return NULL;
|
||||||
c = cbuf;
|
|
||||||
/* loop over data, turning every 3 bytes into 4 characters */
|
|
||||||
for (i = 0; i < len - 2; i += 3) {
|
|
||||||
word = buffer[i] << 16 | buffer[i+1] << 8 | buffer[i+2];
|
|
||||||
hextet = (word & 0x00FC0000) >> 18;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (word & 0x0003F000) >> 12;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (word & 0x00000FC0) >> 6;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (word & 0x000003F);
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
}
|
|
||||||
/* zero, one or two bytes left */
|
|
||||||
switch (len - i) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
hextet = (buffer[len-1] & 0xFC) >> 2;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (buffer[len-1] & 0x03) << 4;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
*c++ = _base64_charmap[64]; /* pad */
|
|
||||||
*c++ = _base64_charmap[64]; /* pad */
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
hextet = (buffer[len-2] & 0xFC) >> 2;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = ((buffer[len-2] & 0x03) << 4) |
|
|
||||||
((buffer[len-1] & 0xF0) >> 4);
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
hextet = (buffer[len-1] & 0x0F) << 2;
|
|
||||||
*c++ = _base64_charmap[hextet];
|
|
||||||
*c++ = _base64_charmap[64]; /* pad */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* add a terminal null */
|
|
||||||
*c = '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cbuf;
|
ptr = strtok_r(tmp, ",", &saveptr);
|
||||||
}
|
while (ptr) {
|
||||||
|
if (strncmp(ptr, "r=", 2) == 0) {
|
||||||
int base64_decoded_len(xmpp_ctx_t *ctx,
|
r = ptr;
|
||||||
const char * const buffer, const unsigned len)
|
} else if (strncmp(ptr, "s=", 2) == 0) {
|
||||||
{
|
s = ptr + 2;
|
||||||
int nudge;
|
} else if (strncmp(ptr, "i=", 2) == 0) {
|
||||||
int c;
|
i = ptr + 2;
|
||||||
|
}
|
||||||
/* count the padding characters for the remainder */
|
ptr = strtok_r(NULL, ",", &saveptr);
|
||||||
nudge = -1;
|
|
||||||
c = _base64_invcharmap[(int)buffer[len-1]];
|
|
||||||
if (c < 64) nudge = 0;
|
|
||||||
else if (c == 64) {
|
|
||||||
c = _base64_invcharmap[(int)buffer[len-2]];
|
|
||||||
if (c < 64) nudge = 1;
|
|
||||||
else if (c == 64) {
|
|
||||||
c = _base64_invcharmap[(int)buffer[len-3]];
|
|
||||||
if (c < 64) nudge = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nudge < 0) return 0; /* reject bad coding */
|
|
||||||
|
|
||||||
/* decoded steam is 3 bytes for every four */
|
|
||||||
return 3 * (len >> 2) - nudge;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
|
||||||
const char * const buffer, const unsigned len)
|
|
||||||
{
|
|
||||||
int dlen;
|
|
||||||
unsigned char *dbuf, *d;
|
|
||||||
uint32_t word, hextet;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* len must be a multiple of 4 */
|
|
||||||
if (len & 0x03) return NULL;
|
|
||||||
|
|
||||||
dlen = base64_decoded_len(ctx, buffer, len);
|
|
||||||
dbuf = xmpp_alloc(ctx, dlen + 1);
|
|
||||||
if (dbuf != NULL) {
|
|
||||||
d = dbuf;
|
|
||||||
/* loop over each set of 4 characters, decoding 3 bytes */
|
|
||||||
for (i = 0; i < len - 3; i += 4) {
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[i]];
|
|
||||||
if (hextet & 0xC0) break;
|
|
||||||
word = hextet << 18;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[i+1]];
|
|
||||||
if (hextet & 0xC0) break;
|
|
||||||
word |= hextet << 12;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[i+2]];
|
|
||||||
if (hextet & 0xC0) break;
|
|
||||||
word |= hextet << 6;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[i+3]];
|
|
||||||
if (hextet & 0xC0) break;
|
|
||||||
word |= hextet;
|
|
||||||
*d++ = (word & 0x00FF0000) >> 16;
|
|
||||||
*d++ = (word & 0x0000FF00) >> 8;
|
|
||||||
*d++ = (word & 0x000000FF);
|
|
||||||
}
|
|
||||||
if (hextet > 64) goto _base64_decode_error;
|
|
||||||
/* handle the remainder */
|
|
||||||
switch (dlen % 3) {
|
|
||||||
case 0:
|
|
||||||
/* nothing to do */
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* redo the last quartet, checking for correctness */
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word = hextet << 2;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word |= hextet >> 4;
|
|
||||||
*d++ = word & 0xFF;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
|
||||||
if (hextet != 64) goto _base64_decode_error;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
|
||||||
if (hextet != 64) goto _base64_decode_error;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/* redo the last quartet, checking for correctness */
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-4]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word = hextet << 10;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-3]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word |= hextet << 4;
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-2]];
|
|
||||||
if (hextet & 0xC0) goto _base64_decode_error;
|
|
||||||
word |= hextet >> 2;
|
|
||||||
*d++ = (word & 0xFF00) >> 8;
|
|
||||||
*d++ = (word & 0x00FF);
|
|
||||||
hextet = _base64_invcharmap[(int)buffer[len-1]];
|
|
||||||
if (hextet != 64) goto _base64_decode_error;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*d = '\0';
|
|
||||||
return dbuf;
|
|
||||||
|
|
||||||
_base64_decode_error:
|
|
||||||
/* invalid character; abort decoding! */
|
|
||||||
xmpp_free(ctx, dbuf);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** self tests ***/
|
|
||||||
#ifdef TEST
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int test_charmap_identity(void)
|
|
||||||
{
|
|
||||||
int i, v, u;
|
|
||||||
|
|
||||||
for (i = 0; i < 65; i++) {
|
|
||||||
v = _base64_charmap[i];
|
|
||||||
if (v > 255) return 1;
|
|
||||||
u = _base64_invcharmap[v];
|
|
||||||
/* printf("map: %d -> %d -> %d\n", i, v, u); */
|
|
||||||
if (u != i) return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
if (!r || !s || !i) {
|
||||||
}
|
goto out;
|
||||||
|
|
||||||
int test_charmap_range(void)
|
|
||||||
{
|
|
||||||
int i, v;
|
|
||||||
|
|
||||||
for (i = 64; i < 256; i++) {
|
|
||||||
v = _base64_invcharmap[i];
|
|
||||||
if (i < 64) return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
xmpp_base64_decode_bin(ctx, s, strlen(s), &sval, &sval_len);
|
||||||
|
if (!sval) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ival = strtol(i, &saveptr, 10);
|
||||||
|
|
||||||
|
/* "c=<channel_binding>," + r + ",p=" + sign_b64 + '\0' */
|
||||||
|
response_len = 3 + strlen(channel_binding) + strlen(r) + 3 +
|
||||||
|
((alg->digest_size + 2) / 3 * 4) + 1;
|
||||||
|
response = strophe_alloc(ctx, response_len);
|
||||||
|
if (!response) {
|
||||||
|
goto out_sval;
|
||||||
|
}
|
||||||
|
|
||||||
|
auth_len = 3 + response_len + strlen(first_bare) + strlen(challenge);
|
||||||
|
auth = strophe_alloc(ctx, auth_len);
|
||||||
|
if (!auth) {
|
||||||
|
goto out_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
l = strophe_snprintf(response, response_len, "c=%s,%s", channel_binding, r);
|
||||||
|
if (l < 0 || (size_t)l >= response_len) {
|
||||||
|
goto out_auth;
|
||||||
|
}
|
||||||
|
l = strophe_snprintf(auth, auth_len, "%s,%s,%s", first_bare, challenge,
|
||||||
|
response);
|
||||||
|
if (l < 0 || (size_t)l >= auth_len) {
|
||||||
|
goto out_auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCRAM_ClientKey(alg, (uint8_t *)password, strlen(password), (uint8_t *)sval,
|
||||||
|
sval_len, (uint32_t)ival, key);
|
||||||
|
SCRAM_ClientSignature(alg, key, (uint8_t *)auth, strlen(auth), sign);
|
||||||
|
SCRAM_ClientProof(alg, key, sign, sign);
|
||||||
|
|
||||||
|
sign_b64 = xmpp_base64_encode(ctx, sign, alg->digest_size);
|
||||||
|
if (!sign_b64) {
|
||||||
|
goto out_auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for buffer overflow */
|
||||||
|
if (strlen(response) + strlen(sign_b64) + 3 + 1 > response_len) {
|
||||||
|
strophe_free(ctx, sign_b64);
|
||||||
|
goto out_auth;
|
||||||
|
}
|
||||||
|
strcat(response, ",p=");
|
||||||
|
strcat(response, sign_b64);
|
||||||
|
strophe_free(ctx, sign_b64);
|
||||||
|
|
||||||
|
response_b64 =
|
||||||
|
xmpp_base64_encode(ctx, (unsigned char *)response, strlen(response));
|
||||||
|
if (!response_b64) {
|
||||||
|
goto out_auth;
|
||||||
|
}
|
||||||
|
result = response_b64;
|
||||||
|
|
||||||
|
out_auth:
|
||||||
|
strophe_free(ctx, auth);
|
||||||
|
out_response:
|
||||||
|
strophe_free(ctx, response);
|
||||||
|
out_sval:
|
||||||
|
strophe_free(ctx, sval);
|
||||||
|
out:
|
||||||
|
strophe_free(ctx, tmp);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
printf("testing charmap identity...");
|
|
||||||
ret = test_charmap_identity();
|
|
||||||
if (ret) return ret;
|
|
||||||
printf(" ok.\n");
|
|
||||||
|
|
||||||
printf("testing charmap range...");
|
|
||||||
ret = test_charmap_range();
|
|
||||||
if (ret) return ret;
|
|
||||||
printf(" ok.\n");
|
|
||||||
|
|
||||||
printf("no error\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* TEST */
|
|
||||||
|
|||||||
34
src/sasl.h
34
src/sasl.h
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* sasl.h
|
/* sasl.h
|
||||||
** strophe XMPP client library -- SASL authentication helpers
|
** strophe XMPP client library -- SASL authentication helpers
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -20,25 +18,21 @@
|
|||||||
#define __LIBSTROPHE_SASL_H__
|
#define __LIBSTROPHE_SASL_H__
|
||||||
|
|
||||||
#include "strophe.h"
|
#include "strophe.h"
|
||||||
|
#include "scram.h"
|
||||||
|
|
||||||
/** low-level sasl routines */
|
/** low-level sasl routines */
|
||||||
|
|
||||||
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password);
|
char *sasl_plain(xmpp_ctx_t *ctx, const char *authid, const char *password);
|
||||||
char *sasl_digest_md5(xmpp_ctx_t *ctx, const char *challenge,
|
char *sasl_digest_md5(xmpp_ctx_t *ctx,
|
||||||
const char *jid, const char *password);
|
const char *challenge,
|
||||||
|
const char *jid,
|
||||||
|
const char *password);
|
||||||
/** Base64 encoding routines. Implemented according to RFC 3548 */
|
char *sasl_scram(xmpp_ctx_t *ctx,
|
||||||
|
const struct hash_alg *alg,
|
||||||
int base64_encoded_len(xmpp_ctx_t *ctx, const unsigned len);
|
const char *channel_binding,
|
||||||
|
const char *challenge,
|
||||||
char *base64_encode(xmpp_ctx_t *ctx,
|
const char *first_bare,
|
||||||
const unsigned char * const buffer, const unsigned len);
|
const char *jid,
|
||||||
|
const char *password);
|
||||||
int base64_decoded_len(xmpp_ctx_t *ctx,
|
|
||||||
const char * const buffer, const unsigned len);
|
|
||||||
|
|
||||||
unsigned char *base64_decode(xmpp_ctx_t *ctx,
|
|
||||||
const char * const buffer, const unsigned len);
|
|
||||||
|
|
||||||
#endif /* _LIBXMPP_SASL_H__ */
|
#endif /* _LIBXMPP_SASL_H__ */
|
||||||
|
|||||||
241
src/scram.c
Normal file
241
src/scram.c
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* scram.c
|
||||||
|
* strophe XMPP client library
|
||||||
|
*
|
||||||
|
* SCRAM-SHA1 helper functions according to RFC5802
|
||||||
|
* HMAC-SHA1 implementation according to RFC2104
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* SCRAM-SHA1 helper functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "sha1.h"
|
||||||
|
#include "sha256.h"
|
||||||
|
#include "sha512.h"
|
||||||
|
#include "ostypes.h"
|
||||||
|
|
||||||
|
#include "scram.h"
|
||||||
|
|
||||||
|
#define HMAC_BLOCK_SIZE_MAX 128
|
||||||
|
|
||||||
|
static const uint8_t ipad = 0x36;
|
||||||
|
static const uint8_t opad = 0x5C;
|
||||||
|
|
||||||
|
const struct hash_alg scram_sha1 = {
|
||||||
|
"SCRAM-SHA-1",
|
||||||
|
SASL_MASK_SCRAMSHA1,
|
||||||
|
SHA1_DIGEST_SIZE,
|
||||||
|
(void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1,
|
||||||
|
(void (*)(void *))crypto_SHA1_Init,
|
||||||
|
(void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
|
||||||
|
(void (*)(void *, uint8_t *))crypto_SHA1_Final};
|
||||||
|
|
||||||
|
const struct hash_alg scram_sha1_plus = {
|
||||||
|
"SCRAM-SHA-1-PLUS",
|
||||||
|
SASL_MASK_SCRAMSHA1_PLUS,
|
||||||
|
SHA1_DIGEST_SIZE,
|
||||||
|
(void (*)(const uint8_t *, size_t, uint8_t *))crypto_SHA1,
|
||||||
|
(void (*)(void *))crypto_SHA1_Init,
|
||||||
|
(void (*)(void *, const uint8_t *, size_t))crypto_SHA1_Update,
|
||||||
|
(void (*)(void *, uint8_t *))crypto_SHA1_Final};
|
||||||
|
|
||||||
|
const struct hash_alg scram_sha256 = {
|
||||||
|
"SCRAM-SHA-256",
|
||||||
|
SASL_MASK_SCRAMSHA256,
|
||||||
|
SHA256_DIGEST_SIZE,
|
||||||
|
(void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash,
|
||||||
|
(void (*)(void *))sha256_init,
|
||||||
|
(void (*)(void *, const uint8_t *, size_t))sha256_process,
|
||||||
|
(void (*)(void *, uint8_t *))sha256_done};
|
||||||
|
|
||||||
|
const struct hash_alg scram_sha256_plus = {
|
||||||
|
"SCRAM-SHA-256-PLUS",
|
||||||
|
SASL_MASK_SCRAMSHA256_PLUS,
|
||||||
|
SHA256_DIGEST_SIZE,
|
||||||
|
(void (*)(const uint8_t *, size_t, uint8_t *))sha256_hash,
|
||||||
|
(void (*)(void *))sha256_init,
|
||||||
|
(void (*)(void *, const uint8_t *, size_t))sha256_process,
|
||||||
|
(void (*)(void *, uint8_t *))sha256_done};
|
||||||
|
|
||||||
|
const struct hash_alg scram_sha512 = {
|
||||||
|
"SCRAM-SHA-512",
|
||||||
|
SASL_MASK_SCRAMSHA512,
|
||||||
|
SHA512_DIGEST_SIZE,
|
||||||
|
(void (*)(const uint8_t *, size_t, uint8_t *))sha512_hash,
|
||||||
|
(void (*)(void *))sha512_init,
|
||||||
|
(void (*)(void *, const uint8_t *, size_t))sha512_process,
|
||||||
|
(void (*)(void *, uint8_t *))sha512_done};
|
||||||
|
|
||||||
|
const struct hash_alg scram_sha512_plus = {
|
||||||
|
"SCRAM-SHA-512-PLUS",
|
||||||
|
SASL_MASK_SCRAMSHA512_PLUS,
|
||||||
|
SHA512_DIGEST_SIZE,
|
||||||
|
(void (*)(const uint8_t *, size_t, uint8_t *))sha512_hash,
|
||||||
|
(void (*)(void *))sha512_init,
|
||||||
|
(void (*)(void *, const uint8_t *, size_t))sha512_process,
|
||||||
|
(void (*)(void *, uint8_t *))sha512_done};
|
||||||
|
|
||||||
|
/* The order of this list defines the order in which the SCRAM algorithms are
|
||||||
|
* tried if the server supports them.
|
||||||
|
* Their order is derived from
|
||||||
|
* https://datatracker.ietf.org/doc/html/draft-ietf-kitten-password-storage
|
||||||
|
*/
|
||||||
|
const struct hash_alg *scram_algs[] = {
|
||||||
|
/* *1 */
|
||||||
|
&scram_sha512_plus,
|
||||||
|
/* *1 */
|
||||||
|
&scram_sha256_plus,
|
||||||
|
/* *1 */
|
||||||
|
&scram_sha1_plus,
|
||||||
|
/* *1 */
|
||||||
|
&scram_sha512,
|
||||||
|
/* *1 */
|
||||||
|
&scram_sha256,
|
||||||
|
/* *1 */
|
||||||
|
&scram_sha1,
|
||||||
|
};
|
||||||
|
/* *1 - I want to use clang-format here, but by default it will put multiple
|
||||||
|
* elements per line if there's no comment. Currently clang-format also doesn't
|
||||||
|
* have an option to enforce it to behave like that, besides by putting comments
|
||||||
|
* between each element (or I couldn't find a way to do it). In order to prevent
|
||||||
|
* clang-format from re-formatting the array I added the comments above and
|
||||||
|
* wrote this lengthy description.
|
||||||
|
*/
|
||||||
|
const size_t scram_algs_num = sizeof(scram_algs) / sizeof(scram_algs[0]);
|
||||||
|
|
||||||
|
union common_hash_ctx {
|
||||||
|
SHA1_CTX sha1;
|
||||||
|
sha256_context sha256;
|
||||||
|
sha512_context sha512;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void crypto_HMAC(const struct hash_alg *alg,
|
||||||
|
const uint8_t *key,
|
||||||
|
size_t key_len,
|
||||||
|
const uint8_t *text,
|
||||||
|
size_t len,
|
||||||
|
uint8_t *digest)
|
||||||
|
{
|
||||||
|
uint8_t key_pad[HMAC_BLOCK_SIZE_MAX];
|
||||||
|
uint8_t key_ipad[HMAC_BLOCK_SIZE_MAX];
|
||||||
|
uint8_t key_opad[HMAC_BLOCK_SIZE_MAX];
|
||||||
|
uint8_t sha_digest[SCRAM_DIGEST_SIZE];
|
||||||
|
size_t blocksize;
|
||||||
|
size_t i;
|
||||||
|
union common_hash_ctx ctx;
|
||||||
|
|
||||||
|
assert(alg->digest_size <= HMAC_BLOCK_SIZE_MAX);
|
||||||
|
blocksize = alg->digest_size < 48 ? 64 : 128;
|
||||||
|
|
||||||
|
memset(key_pad, 0, blocksize);
|
||||||
|
if (key_len <= blocksize) {
|
||||||
|
memcpy(key_pad, key, key_len);
|
||||||
|
} else {
|
||||||
|
/* according to RFC2104 */
|
||||||
|
alg->hash(key, key_len, key_pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < blocksize; i++) {
|
||||||
|
key_ipad[i] = key_pad[i] ^ ipad;
|
||||||
|
key_opad[i] = key_pad[i] ^ opad;
|
||||||
|
}
|
||||||
|
|
||||||
|
alg->init((void *)&ctx);
|
||||||
|
alg->update((void *)&ctx, key_ipad, blocksize);
|
||||||
|
alg->update((void *)&ctx, text, len);
|
||||||
|
alg->final((void *)&ctx, sha_digest);
|
||||||
|
|
||||||
|
alg->init((void *)&ctx);
|
||||||
|
alg->update((void *)&ctx, key_opad, blocksize);
|
||||||
|
alg->update((void *)&ctx, sha_digest, alg->digest_size);
|
||||||
|
alg->final((void *)&ctx, digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SCRAM_Hi(const struct hash_alg *alg,
|
||||||
|
const uint8_t *text,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *salt,
|
||||||
|
size_t salt_len,
|
||||||
|
uint32_t i,
|
||||||
|
uint8_t *digest)
|
||||||
|
{
|
||||||
|
size_t k;
|
||||||
|
uint32_t j;
|
||||||
|
uint8_t tmp[128];
|
||||||
|
|
||||||
|
static uint8_t int1[] = {0x0, 0x0, 0x0, 0x1};
|
||||||
|
|
||||||
|
/* assume salt + INT(1) isn't longer than sizeof(tmp) */
|
||||||
|
assert(salt_len <= sizeof(tmp) - sizeof(int1));
|
||||||
|
|
||||||
|
memset(digest, 0, alg->digest_size);
|
||||||
|
if (i == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(tmp, salt, salt_len);
|
||||||
|
memcpy(&tmp[salt_len], int1, sizeof(int1));
|
||||||
|
|
||||||
|
/* 'text' for Hi is a 'key' for HMAC */
|
||||||
|
crypto_HMAC(alg, text, len, tmp, salt_len + sizeof(int1), digest);
|
||||||
|
memcpy(tmp, digest, alg->digest_size);
|
||||||
|
|
||||||
|
for (j = 1; j < i; j++) {
|
||||||
|
crypto_HMAC(alg, text, len, tmp, alg->digest_size, tmp);
|
||||||
|
for (k = 0; k < alg->digest_size; k++) {
|
||||||
|
digest[k] ^= tmp[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCRAM_ClientKey(const struct hash_alg *alg,
|
||||||
|
const uint8_t *password,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *salt,
|
||||||
|
size_t salt_len,
|
||||||
|
uint32_t i,
|
||||||
|
uint8_t *key)
|
||||||
|
{
|
||||||
|
uint8_t salted[SCRAM_DIGEST_SIZE];
|
||||||
|
|
||||||
|
/* XXX: Normalize(password) is omitted */
|
||||||
|
|
||||||
|
SCRAM_Hi(alg, password, len, salt, salt_len, i, salted);
|
||||||
|
crypto_HMAC(alg, salted, alg->digest_size, (uint8_t *)"Client Key",
|
||||||
|
strlen("Client Key"), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCRAM_ClientSignature(const struct hash_alg *alg,
|
||||||
|
const uint8_t *ClientKey,
|
||||||
|
const uint8_t *AuthMessage,
|
||||||
|
size_t len,
|
||||||
|
uint8_t *sign)
|
||||||
|
{
|
||||||
|
uint8_t stored[SCRAM_DIGEST_SIZE];
|
||||||
|
|
||||||
|
alg->hash(ClientKey, alg->digest_size, stored);
|
||||||
|
crypto_HMAC(alg, stored, alg->digest_size, AuthMessage, len, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCRAM_ClientProof(const struct hash_alg *alg,
|
||||||
|
const uint8_t *ClientKey,
|
||||||
|
const uint8_t *ClientSignature,
|
||||||
|
uint8_t *proof)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < alg->digest_size; i++) {
|
||||||
|
proof[i] = ClientKey[i] ^ ClientSignature[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/scram.h
Normal file
59
src/scram.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* scram.h
|
||||||
|
* strophe XMPP client library -- SCRAM helper functions
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* SCRAM helper functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBSTROPHE_SCRAM_H__
|
||||||
|
#define __LIBSTROPHE_SCRAM_H__
|
||||||
|
|
||||||
|
/* make sure the stdint.h types are available */
|
||||||
|
#include "ostypes.h"
|
||||||
|
|
||||||
|
/* Maximum possible digest size. Used for buffers allocation. */
|
||||||
|
#include "sha512.h"
|
||||||
|
#define SCRAM_DIGEST_SIZE SHA512_DIGEST_SIZE
|
||||||
|
|
||||||
|
struct hash_alg {
|
||||||
|
const char *scram_name;
|
||||||
|
int mask;
|
||||||
|
size_t digest_size;
|
||||||
|
void (*hash)(const uint8_t *, size_t, uint8_t *);
|
||||||
|
void (*init)(void *);
|
||||||
|
void (*update)(void *, const uint8_t *, size_t);
|
||||||
|
void (*final)(void *, uint8_t *);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const struct hash_alg *scram_algs[];
|
||||||
|
extern const size_t scram_algs_num;
|
||||||
|
|
||||||
|
void SCRAM_ClientKey(const struct hash_alg *alg,
|
||||||
|
const uint8_t *password,
|
||||||
|
size_t len,
|
||||||
|
const uint8_t *salt,
|
||||||
|
size_t salt_len,
|
||||||
|
uint32_t i,
|
||||||
|
uint8_t *key);
|
||||||
|
|
||||||
|
void SCRAM_ClientSignature(const struct hash_alg *alg,
|
||||||
|
const uint8_t *ClientKey,
|
||||||
|
const uint8_t *AuthMessage,
|
||||||
|
size_t len,
|
||||||
|
uint8_t *sign);
|
||||||
|
|
||||||
|
void SCRAM_ClientProof(const struct hash_alg *alg,
|
||||||
|
const uint8_t *ClientKey,
|
||||||
|
const uint8_t *ClientSignature,
|
||||||
|
uint8_t *proof);
|
||||||
|
|
||||||
|
#endif /* __LIBSTROPHE_SCRAM_H__ */
|
||||||
112
src/sha.h
Normal file
112
src/sha.h
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/* public api for steve reid's public domain SHA-1 implementation */
|
||||||
|
/* this file is in the public domain */
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* SHA-1 hash API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBSTROPHE_SHA_H__
|
||||||
|
#define __LIBSTROPHE_SHA_H__
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* make sure the stdint.h types are available */
|
||||||
|
#include "ostypes.h"
|
||||||
|
|
||||||
|
#if defined(__BIG_ENDIAN__) || \
|
||||||
|
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
|
||||||
|
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||||
|
|
||||||
|
#define STORE32H(x, y) \
|
||||||
|
do { \
|
||||||
|
uint32_t __t = (x); \
|
||||||
|
memcpy(y, &__t, 4); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LOAD32H(x, y) \
|
||||||
|
do { \
|
||||||
|
memcpy(&(x), y, 4); \
|
||||||
|
x &= 0xFFFFFFFF; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define STORE64H(x, y) \
|
||||||
|
do { \
|
||||||
|
uint64_t __t = (x); \
|
||||||
|
memcpy(y, &__t, 8); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LOAD64H(x, y) \
|
||||||
|
do { \
|
||||||
|
memcpy(&(x), y, 8); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#elif defined(__LITTLE_ENDIAN__) || \
|
||||||
|
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
|
||||||
|
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||||
|
|
||||||
|
#define STORE32H(x, y) \
|
||||||
|
do { \
|
||||||
|
(y)[0] = (uint8_t)(((x) >> 24) & 255); \
|
||||||
|
(y)[1] = (uint8_t)(((x) >> 16) & 255); \
|
||||||
|
(y)[2] = (uint8_t)(((x) >> 8) & 255); \
|
||||||
|
(y)[3] = (uint8_t)((x)&255); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LOAD32H(x, y) \
|
||||||
|
do { \
|
||||||
|
x = ((uint32_t)((y)[0] & 255) << 24) | \
|
||||||
|
((uint32_t)((y)[1] & 255) << 16) | \
|
||||||
|
((uint32_t)((y)[2] & 255) << 8) | ((uint32_t)((y)[3] & 255)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define STORE64H(x, y) \
|
||||||
|
do { \
|
||||||
|
(y)[0] = (uint8_t)(((x) >> 56) & 255); \
|
||||||
|
(y)[1] = (uint8_t)(((x) >> 48) & 255); \
|
||||||
|
(y)[2] = (uint8_t)(((x) >> 40) & 255); \
|
||||||
|
(y)[3] = (uint8_t)(((x) >> 32) & 255); \
|
||||||
|
(y)[4] = (uint8_t)(((x) >> 24) & 255); \
|
||||||
|
(y)[5] = (uint8_t)(((x) >> 16) & 255); \
|
||||||
|
(y)[6] = (uint8_t)(((x) >> 8) & 255); \
|
||||||
|
(y)[7] = (uint8_t)((x)&255); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LOAD64H(x, y) \
|
||||||
|
do { \
|
||||||
|
x = (((uint64_t)((y)[0] & 255)) << 56) | \
|
||||||
|
(((uint64_t)((y)[1] & 255)) << 48) | \
|
||||||
|
(((uint64_t)((y)[2] & 255)) << 40) | \
|
||||||
|
(((uint64_t)((y)[3] & 255)) << 32) | \
|
||||||
|
(((uint64_t)((y)[4] & 255)) << 24) | \
|
||||||
|
(((uint64_t)((y)[5] & 255)) << 16) | \
|
||||||
|
(((uint64_t)((y)[6] & 255)) << 8) | (((uint64_t)((y)[7] & 255))); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error Unknown endianness not supported
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define CONST64(n) n##ui64
|
||||||
|
#else
|
||||||
|
#define CONST64(n) n##ULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RORc(x, y) \
|
||||||
|
(((((uint32_t)(x)&0xFFFFFFFFUL) >> (uint32_t)((y)&31)) | \
|
||||||
|
((uint32_t)(x) << (uint32_t)((32 - ((y)&31)) & 31))) & \
|
||||||
|
0xFFFFFFFFUL)
|
||||||
|
#define ROR64c(x, y) \
|
||||||
|
(((((x)&CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)(y)&CONST64(63))) | \
|
||||||
|
((x) << (((uint64_t)64 - ((y)&63)) & 63))) & \
|
||||||
|
CONST64(0xFFFFFFFFFFFFFFFF))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __LIBSTROPHE_SHA_H__ */
|
||||||
303
src/sha1.c
303
src/sha1.c
@@ -14,10 +14,10 @@ Still 100% Public Domain
|
|||||||
|
|
||||||
Corrected a problem which generated improper hash values on 16 bit machines
|
Corrected a problem which generated improper hash values on 16 bit machines
|
||||||
Routine SHA1Update changed from
|
Routine SHA1Update changed from
|
||||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
||||||
len)
|
len)
|
||||||
to
|
to
|
||||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
||||||
long len)
|
long len)
|
||||||
|
|
||||||
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
||||||
@@ -70,92 +70,82 @@ use SHA1_ prefix for public api
|
|||||||
move public api to sha1.h
|
move public api to sha1.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
Test Vectors (from FIPS PUB 180-1)
|
|
||||||
"abc"
|
|
||||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
|
||||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
|
||||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
|
||||||
A million repetitions of "a"
|
|
||||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* #define SHA1HANDSOFF */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* make sure the stdint.h types are available */
|
#include "ostypes.h"
|
||||||
#if defined(_MSC_VER) /* Microsoft Visual C++ */
|
|
||||||
typedef signed char int8_t;
|
|
||||||
typedef short int int16_t;
|
|
||||||
typedef int int32_t;
|
|
||||||
typedef __int64 int64_t;
|
|
||||||
|
|
||||||
typedef unsigned char uint8_t;
|
|
||||||
typedef unsigned short int uint16_t;
|
|
||||||
typedef unsigned int uint32_t;
|
|
||||||
/* no uint64_t */
|
|
||||||
#else
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "sha1.h"
|
#include "sha1.h"
|
||||||
|
|
||||||
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
|
static uint32_t host_to_be(uint32_t i);
|
||||||
|
static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
|
||||||
|
|
||||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||||
|
|
||||||
/* blk0() and blk() perform the initial expand. */
|
/* blk0() and blk() perform the initial expand. */
|
||||||
/* I got the idea of expanding during the round function from SSLeay */
|
/* I got the idea of expanding during the round function from SSLeay */
|
||||||
/* FIXME: can we do this in an endian-proof way? */
|
#define blk0(i) (block->l[i] = host_to_be(block->l[i]))
|
||||||
#ifdef WORDS_BIGENDIAN
|
#define blk(i) \
|
||||||
#define blk0(i) block->l[i]
|
(block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \
|
||||||
#else
|
block->l[(i + 2) & 15] ^ block->l[i & 15], \
|
||||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
1))
|
||||||
|(rol(block->l[i],8)&0x00FF00FF))
|
|
||||||
#endif
|
|
||||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
|
||||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
|
||||||
|
|
||||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
#define R0(v, w, x, y, z, i) \
|
||||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
|
||||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
w = rol(w, 30);
|
||||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
#define R1(v, w, x, y, z, i) \
|
||||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R2(v, w, x, y, z, i) \
|
||||||
|
z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R3(v, w, x, y, z, i) \
|
||||||
|
z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R4(v, w, x, y, z, i) \
|
||||||
|
z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
|
||||||
|
static uint32_t host_to_be(uint32_t i)
|
||||||
#ifdef VERBOSE /* SAK */
|
{
|
||||||
void SHAPrintContext(SHA1_CTX *context, char *msg){
|
#define le_to_be(i) ((rol((i), 24) & 0xFF00FF00) | (rol((i), 8) & 0x00FF00FF))
|
||||||
printf("%s (%d,%d) %x %x %x %x %x\n",
|
#if defined(__BIG_ENDIAN__) || \
|
||||||
msg,
|
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
|
||||||
context->count[0], context->count[1],
|
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||||
context->state[0],
|
return i;
|
||||||
context->state[1],
|
#elif defined(__LITTLE_ENDIAN__) || \
|
||||||
context->state[2],
|
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
|
||||||
context->state[3],
|
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||||
context->state[4]);
|
return le_to_be(i);
|
||||||
|
#else /* fallback to run-time check */
|
||||||
|
static const union {
|
||||||
|
uint32_t u;
|
||||||
|
unsigned char c;
|
||||||
|
} check = {1};
|
||||||
|
return check.c ? le_to_be(i) : i;
|
||||||
|
#endif
|
||||||
|
#undef le_to_be
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *(*volatile SHA1_explicit_memset)(void *, int, size_t) = &memset;
|
||||||
|
|
||||||
|
static void SHA1_cleanse(void *p, size_t len)
|
||||||
|
{
|
||||||
|
SHA1_explicit_memset(p, 0, len);
|
||||||
}
|
}
|
||||||
#endif /* VERBOSE */
|
|
||||||
|
|
||||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||||
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
|
static void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
|
||||||
{
|
{
|
||||||
uint32_t a, b, c, d, e;
|
uint32_t a, b, c, d, e;
|
||||||
typedef union {
|
typedef union {
|
||||||
uint8_t c[64];
|
uint8_t c[64];
|
||||||
uint32_t l[16];
|
uint32_t l[16];
|
||||||
} CHAR64LONG16;
|
} CHAR64LONG16;
|
||||||
CHAR64LONG16* block;
|
CHAR64LONG16 *block;
|
||||||
|
|
||||||
#ifdef SHA1HANDSOFF
|
|
||||||
static uint8_t workspace[64];
|
static uint8_t workspace[64];
|
||||||
block = (CHAR64LONG16*)workspace;
|
block = (CHAR64LONG16 *)workspace;
|
||||||
memcpy(block, buffer, 64);
|
memcpy(block, buffer, 64);
|
||||||
#else
|
|
||||||
block = (CHAR64LONG16*)buffer;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Copy context->state[] to working vars */
|
/* Copy context->state[] to working vars */
|
||||||
a = state[0];
|
a = state[0];
|
||||||
@@ -165,6 +155,7 @@ void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
|
|||||||
e = state[4];
|
e = state[4];
|
||||||
|
|
||||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||||
|
/* clang-format off */
|
||||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||||
@@ -185,6 +176,7 @@ void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
|
|||||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
/* Add the working vars back into context.state[] */
|
/* Add the working vars back into context.state[] */
|
||||||
state[0] += a;
|
state[0] += a;
|
||||||
@@ -194,12 +186,15 @@ void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64])
|
|||||||
state[4] += e;
|
state[4] += e;
|
||||||
|
|
||||||
/* Wipe variables */
|
/* Wipe variables */
|
||||||
a = b = c = d = e = 0;
|
SHA1_cleanse(&a, sizeof(a));
|
||||||
|
SHA1_cleanse(&b, sizeof(b));
|
||||||
|
SHA1_cleanse(&c, sizeof(c));
|
||||||
|
SHA1_cleanse(&d, sizeof(d));
|
||||||
|
SHA1_cleanse(&e, sizeof(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* SHA1Init - Initialize new context */
|
/* SHA1Init - Initialize new context */
|
||||||
void SHA1_Init(SHA1_CTX* context)
|
void crypto_SHA1_Init(SHA1_CTX *context)
|
||||||
{
|
{
|
||||||
/* SHA1 initialization constants */
|
/* SHA1 initialization constants */
|
||||||
context->state[0] = 0x67452301;
|
context->state[0] = 0x67452301;
|
||||||
@@ -210,180 +205,62 @@ void SHA1_Init(SHA1_CTX* context)
|
|||||||
context->count[0] = context->count[1] = 0;
|
context->count[0] = context->count[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Run your data through this. */
|
/* Run your data through this. */
|
||||||
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len)
|
void crypto_SHA1_Update(SHA1_CTX *context, const uint8_t *data, size_t len)
|
||||||
{
|
{
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
|
|
||||||
#ifdef VERBOSE
|
|
||||||
SHAPrintContext(context, "before");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
j = (context->count[0] >> 3) & 63;
|
j = (context->count[0] >> 3) & 63;
|
||||||
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
|
if ((context->count[0] += (uint32_t)len << 3) < ((uint32_t)len << 3))
|
||||||
context->count[1] += (len >> 29);
|
context->count[1]++;
|
||||||
|
context->count[1] += (uint32_t)(len >> 29);
|
||||||
if ((j + len) > 63) {
|
if ((j + len) > 63) {
|
||||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
memcpy(&context->buffer[j], data, (i = 64 - j));
|
||||||
SHA1_Transform(context->state, context->buffer);
|
SHA1_Transform(context->state, context->buffer);
|
||||||
for ( ; i + 63 < len; i += 64) {
|
for (; i + 63 < len; i += 64) {
|
||||||
SHA1_Transform(context->state, data + i);
|
SHA1_Transform(context->state, data + i);
|
||||||
}
|
}
|
||||||
j = 0;
|
j = 0;
|
||||||
}
|
} else
|
||||||
else i = 0;
|
i = 0;
|
||||||
memcpy(&context->buffer[j], &data[i], len - i);
|
memcpy(&context->buffer[j], &data[i], len - i);
|
||||||
|
|
||||||
#ifdef VERBOSE
|
|
||||||
SHAPrintContext(context, "after ");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Add padding and return the message digest. */
|
/* Add padding and return the message digest. */
|
||||||
void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE])
|
void crypto_SHA1_Final(SHA1_CTX *context, uint8_t *digest)
|
||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint8_t finalcount[8];
|
uint8_t finalcount[8];
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) {
|
for (i = 0; i < 8; i++) {
|
||||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >>
|
||||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
((3 - (i & 3)) * 8)) &
|
||||||
|
255); /* Endian independent */
|
||||||
}
|
}
|
||||||
SHA1_Update(context, (uint8_t *)"\200", 1);
|
crypto_SHA1_Update(context, (uint8_t *)"\200", 1);
|
||||||
while ((context->count[0] & 504) != 448) {
|
while ((context->count[0] & 504) != 448) {
|
||||||
SHA1_Update(context, (uint8_t *)"\0", 1);
|
crypto_SHA1_Update(context, (uint8_t *)"\0", 1);
|
||||||
}
|
}
|
||||||
SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
|
/* Should cause a SHA1_Transform() */
|
||||||
|
crypto_SHA1_Update(context, finalcount, 8);
|
||||||
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
|
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
|
||||||
digest[i] = (uint8_t)
|
digest[i] =
|
||||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
(uint8_t)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wipe variables */
|
/* Wipe variables */
|
||||||
i = 0;
|
SHA1_cleanse(context->buffer, 64);
|
||||||
memset(context->buffer, 0, 64);
|
SHA1_cleanse(context->state, 20);
|
||||||
memset(context->state, 0, 20);
|
SHA1_cleanse(context->count, 8);
|
||||||
memset(context->count, 0, 8);
|
SHA1_cleanse(finalcount, 8); /* SWR */
|
||||||
memset(finalcount, 0, 8); /* SWR */
|
|
||||||
|
|
||||||
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
|
|
||||||
SHA1_Transform(context->state, context->buffer);
|
SHA1_Transform(context->state, context->buffer);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************/
|
void crypto_SHA1(const uint8_t *data, size_t len, uint8_t *digest)
|
||||||
|
|
||||||
#if 0
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
{
|
||||||
int i, j;
|
SHA1_CTX ctx;
|
||||||
SHA1_CTX context;
|
crypto_SHA1_Init(&ctx);
|
||||||
unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384];
|
crypto_SHA1_Update(&ctx, data, len);
|
||||||
FILE* file;
|
crypto_SHA1_Final(&ctx, digest);
|
||||||
|
|
||||||
if (argc > 2) {
|
|
||||||
puts("Public domain SHA-1 implementation - by Steve Reid <sreid@sea-to-sky.net>");
|
|
||||||
puts("Modified for 16 bit environments 7/98 - by James H. Brown <jbrown@burgoyne.com>"); /* JHB */
|
|
||||||
puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
if (argc < 2) {
|
|
||||||
file = stdin;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!(file = fopen(argv[1], "rb"))) {
|
|
||||||
fputs("Unable to open file.", stderr);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SHA1_Init(&context);
|
|
||||||
while (!feof(file)) { /* note: what if ferror(file) */
|
|
||||||
i = fread(buffer, 1, 16384, file);
|
|
||||||
SHA1_Update(&context, buffer, i);
|
|
||||||
}
|
|
||||||
SHA1_Final(&context, digest);
|
|
||||||
fclose(file);
|
|
||||||
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
|
|
||||||
for (j = 0; j < 4; j++) {
|
|
||||||
printf("%02X", digest[i*4+j]);
|
|
||||||
}
|
|
||||||
putchar(' ');
|
|
||||||
}
|
|
||||||
putchar('\n');
|
|
||||||
return(0); /* JHB */
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/* self test */
|
|
||||||
|
|
||||||
#ifdef TEST
|
|
||||||
|
|
||||||
static char *test_data[] = {
|
|
||||||
"abc",
|
|
||||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
|
||||||
"A million repetitions of 'a'"};
|
|
||||||
static char *test_results[] = {
|
|
||||||
"A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D",
|
|
||||||
"84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1",
|
|
||||||
"34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F"};
|
|
||||||
|
|
||||||
|
|
||||||
void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output)
|
|
||||||
{
|
|
||||||
int i,j;
|
|
||||||
char *c = output;
|
|
||||||
|
|
||||||
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
|
|
||||||
for (j = 0; j < 4; j++) {
|
|
||||||
sprintf(c,"%02X", digest[i*4+j]);
|
|
||||||
c += 2;
|
|
||||||
}
|
|
||||||
sprintf(c, " ");
|
|
||||||
c += 1;
|
|
||||||
}
|
|
||||||
*(c - 1) = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
int k;
|
|
||||||
SHA1_CTX context;
|
|
||||||
uint8_t digest[20];
|
|
||||||
char output[80];
|
|
||||||
|
|
||||||
fprintf(stdout, "verifying SHA-1 implementation... ");
|
|
||||||
|
|
||||||
for (k = 0; k < 2; k++){
|
|
||||||
SHA1_Init(&context);
|
|
||||||
SHA1_Update(&context, (uint8_t*)test_data[k], strlen(test_data[k]));
|
|
||||||
SHA1_Final(&context, digest);
|
|
||||||
digest_to_hex(digest, output);
|
|
||||||
|
|
||||||
if (strcmp(output, test_results[k])) {
|
|
||||||
fprintf(stdout, "FAIL\n");
|
|
||||||
fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[k]);
|
|
||||||
fprintf(stderr,"\t%s returned\n", output);
|
|
||||||
fprintf(stderr,"\t%s is correct\n", test_results[k]);
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* million 'a' vector we feed separately */
|
|
||||||
SHA1_Init(&context);
|
|
||||||
for (k = 0; k < 1000000; k++)
|
|
||||||
SHA1_Update(&context, (uint8_t*)"a", 1);
|
|
||||||
SHA1_Final(&context, digest);
|
|
||||||
digest_to_hex(digest, output);
|
|
||||||
if (strcmp(output, test_results[2])) {
|
|
||||||
fprintf(stdout, "FAIL\n");
|
|
||||||
fprintf(stderr,"* hash of \"%s\" incorrect:\n", test_data[2]);
|
|
||||||
fprintf(stderr,"\t%s returned\n", output);
|
|
||||||
fprintf(stderr,"\t%s is correct\n", test_results[2]);
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
fprintf(stdout, "ok\n");
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
#endif /* TEST */
|
|
||||||
|
|||||||
18
src/sha1.h
18
src/sha1.h
@@ -5,27 +5,31 @@
|
|||||||
* SHA-1 hash API.
|
* SHA-1 hash API.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __SHA1_H
|
#ifndef __LIBSTROPHE_SHA1_H__
|
||||||
#define __SHA1_H
|
#define __LIBSTROPHE_SHA1_H__
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* make sure the stdint.h types are available */
|
||||||
|
#include "ostypes.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t state[5];
|
uint32_t state[5];
|
||||||
uint32_t count[2];
|
uint32_t count[2];
|
||||||
uint8_t buffer[64];
|
uint8_t buffer[64];
|
||||||
} SHA1_CTX;
|
} SHA1_CTX;
|
||||||
|
|
||||||
#define SHA1_DIGEST_SIZE 20
|
#define SHA1_DIGEST_SIZE 20
|
||||||
|
|
||||||
void SHA1_Init(SHA1_CTX* context);
|
void crypto_SHA1_Init(SHA1_CTX *context);
|
||||||
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len);
|
void crypto_SHA1_Update(SHA1_CTX *context, const uint8_t *data, size_t len);
|
||||||
void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]);
|
void crypto_SHA1_Final(SHA1_CTX *context, uint8_t *digest);
|
||||||
|
void crypto_SHA1(const uint8_t *data, size_t len, uint8_t *digest);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __SHA1_H */
|
#endif /* __LIBSTROPHE_SHA1_H__ */
|
||||||
|
|||||||
210
src/sha256.c
Normal file
210
src/sha256.c
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Code originally from LibTomCrypt -- Licensed under the Public Domain/WTFPL2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sha256.h"
|
||||||
|
#include "sha.h"
|
||||||
|
|
||||||
|
/* Various logical functions */
|
||||||
|
#define Ch(x, y, z) (z ^ (x & (y ^ z)))
|
||||||
|
#define Maj(x, y, z) (((x | y) & z) | (x & y))
|
||||||
|
#define S(x, n) RORc((x), (n))
|
||||||
|
#define R(x, n) (((x)&0xFFFFFFFFUL) >> (n))
|
||||||
|
#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
|
||||||
|
#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
|
||||||
|
#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
|
||||||
|
#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
|
||||||
|
|
||||||
|
static void sha256_compress(sha256_context *md, const uint8_t *buf)
|
||||||
|
{
|
||||||
|
uint32_t S[8], W[64], t0, t1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* copy state into S */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
S[i] = md->state[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy the state into 512-bits into W[0..15] */
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
LOAD32H(W[i], buf + (4 * i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fill W[16..63] */
|
||||||
|
for (i = 16; i < 64; i++) {
|
||||||
|
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compress */
|
||||||
|
#define RND(a, b, c, d, e, f, g, h, i, ki) \
|
||||||
|
t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \
|
||||||
|
t1 = Sigma0(a) + Maj(a, b, c); \
|
||||||
|
d += t0; \
|
||||||
|
h = t0 + t1;
|
||||||
|
|
||||||
|
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 0, 0x428a2f98);
|
||||||
|
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 1, 0x71374491);
|
||||||
|
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 2, 0xb5c0fbcf);
|
||||||
|
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 3, 0xe9b5dba5);
|
||||||
|
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 4, 0x3956c25b);
|
||||||
|
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 5, 0x59f111f1);
|
||||||
|
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 6, 0x923f82a4);
|
||||||
|
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 7, 0xab1c5ed5);
|
||||||
|
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 8, 0xd807aa98);
|
||||||
|
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 9, 0x12835b01);
|
||||||
|
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 10, 0x243185be);
|
||||||
|
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 11, 0x550c7dc3);
|
||||||
|
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 12, 0x72be5d74);
|
||||||
|
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 13, 0x80deb1fe);
|
||||||
|
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 14, 0x9bdc06a7);
|
||||||
|
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 15, 0xc19bf174);
|
||||||
|
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 16, 0xe49b69c1);
|
||||||
|
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 17, 0xefbe4786);
|
||||||
|
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 18, 0x0fc19dc6);
|
||||||
|
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 19, 0x240ca1cc);
|
||||||
|
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 20, 0x2de92c6f);
|
||||||
|
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 21, 0x4a7484aa);
|
||||||
|
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 22, 0x5cb0a9dc);
|
||||||
|
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 23, 0x76f988da);
|
||||||
|
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 24, 0x983e5152);
|
||||||
|
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 25, 0xa831c66d);
|
||||||
|
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 26, 0xb00327c8);
|
||||||
|
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 27, 0xbf597fc7);
|
||||||
|
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 28, 0xc6e00bf3);
|
||||||
|
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 29, 0xd5a79147);
|
||||||
|
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 30, 0x06ca6351);
|
||||||
|
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 31, 0x14292967);
|
||||||
|
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 32, 0x27b70a85);
|
||||||
|
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 33, 0x2e1b2138);
|
||||||
|
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 34, 0x4d2c6dfc);
|
||||||
|
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 35, 0x53380d13);
|
||||||
|
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 36, 0x650a7354);
|
||||||
|
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 37, 0x766a0abb);
|
||||||
|
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 38, 0x81c2c92e);
|
||||||
|
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 39, 0x92722c85);
|
||||||
|
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 40, 0xa2bfe8a1);
|
||||||
|
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 41, 0xa81a664b);
|
||||||
|
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 42, 0xc24b8b70);
|
||||||
|
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 43, 0xc76c51a3);
|
||||||
|
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 44, 0xd192e819);
|
||||||
|
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 45, 0xd6990624);
|
||||||
|
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 46, 0xf40e3585);
|
||||||
|
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 47, 0x106aa070);
|
||||||
|
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 48, 0x19a4c116);
|
||||||
|
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 49, 0x1e376c08);
|
||||||
|
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 50, 0x2748774c);
|
||||||
|
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 51, 0x34b0bcb5);
|
||||||
|
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 52, 0x391c0cb3);
|
||||||
|
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 53, 0x4ed8aa4a);
|
||||||
|
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 54, 0x5b9cca4f);
|
||||||
|
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 55, 0x682e6ff3);
|
||||||
|
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], 56, 0x748f82ee);
|
||||||
|
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], 57, 0x78a5636f);
|
||||||
|
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], 58, 0x84c87814);
|
||||||
|
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], 59, 0x8cc70208);
|
||||||
|
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], 60, 0x90befffa);
|
||||||
|
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], 61, 0xa4506ceb);
|
||||||
|
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], 62, 0xbef9a3f7);
|
||||||
|
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], 63, 0xc67178f2);
|
||||||
|
|
||||||
|
#undef RND
|
||||||
|
|
||||||
|
/* feedback */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
md->state[i] = md->state[i] + S[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_init(sha256_context *md)
|
||||||
|
{
|
||||||
|
md->curlen = 0;
|
||||||
|
md->length = 0;
|
||||||
|
md->state[0] = 0x6A09E667UL;
|
||||||
|
md->state[1] = 0xBB67AE85UL;
|
||||||
|
md->state[2] = 0x3C6EF372UL;
|
||||||
|
md->state[3] = 0xA54FF53AUL;
|
||||||
|
md->state[4] = 0x510E527FUL;
|
||||||
|
md->state[5] = 0x9B05688CUL;
|
||||||
|
md->state[6] = 0x1F83D9ABUL;
|
||||||
|
md->state[7] = 0x5BE0CD19UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_process(sha256_context *md, const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
if (md->curlen > sizeof(md->buf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((md->length + inlen) < md->length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (inlen > 0) {
|
||||||
|
if (md->curlen == 0 && inlen >= 64) {
|
||||||
|
sha256_compress(md, in);
|
||||||
|
md->length += 64 * 8;
|
||||||
|
in += 64;
|
||||||
|
inlen -= 64;
|
||||||
|
} else {
|
||||||
|
n = (((inlen) < ((64 - md->curlen))) ? (inlen)
|
||||||
|
: ((64 - md->curlen)));
|
||||||
|
memcpy(md->buf + md->curlen, in, (size_t)n);
|
||||||
|
md->curlen += n;
|
||||||
|
in += n;
|
||||||
|
inlen -= n;
|
||||||
|
if (md->curlen == 64) {
|
||||||
|
sha256_compress(md, md->buf);
|
||||||
|
md->length += 8 * 64;
|
||||||
|
md->curlen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_done(sha256_context *md, uint8_t *out)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (md->curlen >= sizeof(md->buf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* increase the length of the message */
|
||||||
|
md->length += md->curlen * 8;
|
||||||
|
|
||||||
|
/* append the '1' bit */
|
||||||
|
md->buf[md->curlen++] = (uint8_t)0x80;
|
||||||
|
|
||||||
|
/* if the length is currently above 56 bytes we append zeros
|
||||||
|
* then compress. Then we can fall back to padding zeros and length
|
||||||
|
* encoding like normal.
|
||||||
|
*/
|
||||||
|
if (md->curlen > 56) {
|
||||||
|
while (md->curlen < 64) {
|
||||||
|
md->buf[md->curlen++] = (uint8_t)0;
|
||||||
|
}
|
||||||
|
sha256_compress(md, md->buf);
|
||||||
|
md->curlen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pad upto 56 bytes of zeroes */
|
||||||
|
while (md->curlen < 56) {
|
||||||
|
md->buf[md->curlen++] = (uint8_t)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store length */
|
||||||
|
STORE64H(md->length, md->buf + 56);
|
||||||
|
sha256_compress(md, md->buf);
|
||||||
|
|
||||||
|
/* copy output */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
STORE32H(md->state[i], out + (4 * i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_hash(const uint8_t *data, size_t len, uint8_t *digest)
|
||||||
|
{
|
||||||
|
sha256_context md;
|
||||||
|
sha256_init(&md);
|
||||||
|
sha256_process(&md, data, len);
|
||||||
|
sha256_done(&md, digest);
|
||||||
|
}
|
||||||
34
src/sha256.h
Normal file
34
src/sha256.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/* public api for LibTomCrypt SHA-256 implementation */
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* SHA-256 hash API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBSTROPHE_SHA256_H__
|
||||||
|
#define __LIBSTROPHE_SHA256_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* make sure the stdint.h types are available */
|
||||||
|
#include "ostypes.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t length;
|
||||||
|
uint32_t state[8], curlen;
|
||||||
|
uint8_t buf[64];
|
||||||
|
} sha256_context;
|
||||||
|
|
||||||
|
#define SHA256_DIGEST_SIZE 32
|
||||||
|
|
||||||
|
void sha256_init(sha256_context *md);
|
||||||
|
void sha256_process(sha256_context *md, const uint8_t *in, size_t inlen);
|
||||||
|
void sha256_done(sha256_context *md, uint8_t *out);
|
||||||
|
|
||||||
|
void sha256_hash(const uint8_t *data, size_t len, uint8_t *digest);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __LIBSTROPHE_SHA256_H__ */
|
||||||
201
src/sha512.c
Normal file
201
src/sha512.c
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Code originally from LibTomCrypt -- Licensed under the Public Domain/WTFPL2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sha512.h"
|
||||||
|
#include "sha.h"
|
||||||
|
|
||||||
|
/* the K array */
|
||||||
|
static const uint64_t K[80] = {
|
||||||
|
CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
|
||||||
|
CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
|
||||||
|
CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
|
||||||
|
CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
|
||||||
|
CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
|
||||||
|
CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
|
||||||
|
CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
|
||||||
|
CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
|
||||||
|
CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
|
||||||
|
CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
|
||||||
|
CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
|
||||||
|
CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
|
||||||
|
CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
|
||||||
|
CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
|
||||||
|
CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
|
||||||
|
CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
|
||||||
|
CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
|
||||||
|
CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
|
||||||
|
CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
|
||||||
|
CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
|
||||||
|
CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
|
||||||
|
CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
|
||||||
|
CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
|
||||||
|
CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
|
||||||
|
CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
|
||||||
|
CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
|
||||||
|
CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
|
||||||
|
CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
|
||||||
|
CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
|
||||||
|
CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
|
||||||
|
CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
|
||||||
|
CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
|
||||||
|
CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
|
||||||
|
CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
|
||||||
|
CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
|
||||||
|
CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
|
||||||
|
CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
|
||||||
|
CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
|
||||||
|
CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
|
||||||
|
CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)};
|
||||||
|
|
||||||
|
/* Various logical functions */
|
||||||
|
#define Ch(x, y, z) (z ^ (x & (y ^ z)))
|
||||||
|
#define Maj(x, y, z) (((x | y) & z) | (x & y))
|
||||||
|
#define S(x, n) ROR64c(x, n)
|
||||||
|
#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF)) >> ((uint64_t)n))
|
||||||
|
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
|
||||||
|
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
|
||||||
|
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
|
||||||
|
#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
|
||||||
|
|
||||||
|
/* compress 1024-bits */
|
||||||
|
static void sha512_compress(sha512_context *md, const uint8_t *buf)
|
||||||
|
{
|
||||||
|
uint64_t S[8], W[80], t0, t1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* copy state into S */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
S[i] = md->state[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy the state into 1024-bits into W[0..15] */
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
LOAD64H(W[i], buf + (8 * i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fill W[16..79] */
|
||||||
|
for (i = 16; i < 80; i++) {
|
||||||
|
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compress */
|
||||||
|
#define RND(a, b, c, d, e, f, g, h, i) \
|
||||||
|
t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
|
||||||
|
t1 = Sigma0(a) + Maj(a, b, c); \
|
||||||
|
d += t0; \
|
||||||
|
h = t0 + t1;
|
||||||
|
|
||||||
|
for (i = 0; i < 80; i += 8) {
|
||||||
|
RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i + 0);
|
||||||
|
RND(S[7], S[0], S[1], S[2], S[3], S[4], S[5], S[6], i + 1);
|
||||||
|
RND(S[6], S[7], S[0], S[1], S[2], S[3], S[4], S[5], i + 2);
|
||||||
|
RND(S[5], S[6], S[7], S[0], S[1], S[2], S[3], S[4], i + 3);
|
||||||
|
RND(S[4], S[5], S[6], S[7], S[0], S[1], S[2], S[3], i + 4);
|
||||||
|
RND(S[3], S[4], S[5], S[6], S[7], S[0], S[1], S[2], i + 5);
|
||||||
|
RND(S[2], S[3], S[4], S[5], S[6], S[7], S[0], S[1], i + 6);
|
||||||
|
RND(S[1], S[2], S[3], S[4], S[5], S[6], S[7], S[0], i + 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* feedback */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
md->state[i] = md->state[i] + S[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha512_init(sha512_context *md)
|
||||||
|
{
|
||||||
|
md->curlen = 0;
|
||||||
|
md->length = 0;
|
||||||
|
md->state[0] = CONST64(0x6a09e667f3bcc908);
|
||||||
|
md->state[1] = CONST64(0xbb67ae8584caa73b);
|
||||||
|
md->state[2] = CONST64(0x3c6ef372fe94f82b);
|
||||||
|
md->state[3] = CONST64(0xa54ff53a5f1d36f1);
|
||||||
|
md->state[4] = CONST64(0x510e527fade682d1);
|
||||||
|
md->state[5] = CONST64(0x9b05688c2b3e6c1f);
|
||||||
|
md->state[6] = CONST64(0x1f83d9abfb41bd6b);
|
||||||
|
md->state[7] = CONST64(0x5be0cd19137e2179);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha512_process(sha512_context *md, const uint8_t *in, size_t inlen)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
if (md->curlen > sizeof(md->buf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((md->length + inlen) < md->length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (inlen > 0) {
|
||||||
|
if (md->curlen == 0 && inlen >= 128) {
|
||||||
|
sha512_compress(md, in);
|
||||||
|
md->length += 128 * 8;
|
||||||
|
in += 128;
|
||||||
|
inlen -= 128;
|
||||||
|
} else {
|
||||||
|
n = (((inlen) < ((128u - md->curlen))) ? (inlen)
|
||||||
|
: ((128u - md->curlen)));
|
||||||
|
memcpy(md->buf + md->curlen, in, (size_t)n);
|
||||||
|
md->curlen += n;
|
||||||
|
in += n;
|
||||||
|
inlen -= n;
|
||||||
|
if (md->curlen == 128) {
|
||||||
|
sha512_compress(md, md->buf);
|
||||||
|
md->length += 8 * 128;
|
||||||
|
md->curlen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha512_done(sha512_context *md, uint8_t *out)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (md->curlen >= sizeof(md->buf)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* increase the length of the message */
|
||||||
|
md->length += md->curlen * CONST64(8);
|
||||||
|
|
||||||
|
/* append the '1' bit */
|
||||||
|
md->buf[md->curlen++] = (uint8_t)0x80;
|
||||||
|
|
||||||
|
/* if the length is currently above 112 bytes we append zeros
|
||||||
|
* then compress. Then we can fall back to padding zeros and length
|
||||||
|
* encoding like normal.
|
||||||
|
*/
|
||||||
|
if (md->curlen > 112) {
|
||||||
|
while (md->curlen < 128) {
|
||||||
|
md->buf[md->curlen++] = (uint8_t)0;
|
||||||
|
}
|
||||||
|
sha512_compress(md, md->buf);
|
||||||
|
md->curlen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pad upto 120 bytes of zeroes
|
||||||
|
* note: that from 112 to 120 is the 64 MSB of the length. We assume that
|
||||||
|
* you won't hash > 2^64 bits of data... :-)
|
||||||
|
*/
|
||||||
|
while (md->curlen < 120) {
|
||||||
|
md->buf[md->curlen++] = (uint8_t)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store length */
|
||||||
|
STORE64H(md->length, md->buf + 120);
|
||||||
|
sha512_compress(md, md->buf);
|
||||||
|
|
||||||
|
/* copy output */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
STORE64H(md->state[i], out + (8 * i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha512_hash(const uint8_t *data, size_t len, uint8_t *digest)
|
||||||
|
{
|
||||||
|
sha512_context md;
|
||||||
|
sha512_init(&md);
|
||||||
|
sha512_process(&md, data, len);
|
||||||
|
sha512_done(&md, digest);
|
||||||
|
}
|
||||||
34
src/sha512.h
Normal file
34
src/sha512.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/* public api for LibTomCrypt SHA-512 implementation */
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* SHA-512 hash API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBSTROPHE_SHA512_H__
|
||||||
|
#define __LIBSTROPHE_SHA512_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* make sure the stdint.h types are available */
|
||||||
|
#include "ostypes.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint64_t length, state[8];
|
||||||
|
uint8_t curlen;
|
||||||
|
uint8_t buf[128];
|
||||||
|
} sha512_context;
|
||||||
|
|
||||||
|
#define SHA512_DIGEST_SIZE 64
|
||||||
|
|
||||||
|
void sha512_init(sha512_context *cc);
|
||||||
|
void sha512_process(sha512_context *cc, const uint8_t *data, size_t len);
|
||||||
|
void sha512_done(sha512_context *cc, uint8_t *dst);
|
||||||
|
|
||||||
|
void sha512_hash(const uint8_t *data, size_t len, uint8_t *digest);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __LIBSTROPHE_SHA512_H__ */
|
||||||
1210
src/snprintf.c
1210
src/snprintf.c
File diff suppressed because it is too large
Load Diff
34
src/snprintf.h
Normal file
34
src/snprintf.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright Patrick Powell 1995
|
||||||
|
* This code is based on code written by Patrick Powell (papowell@astart.com)
|
||||||
|
* It may be used for any purpose as long as this notice remains intact
|
||||||
|
* on all source code distributions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* Compatibility wrappers for OSes lacking snprintf(3) and/or vsnprintf(3).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LIBSTROPHE_SNPRINTF_H__
|
||||||
|
#define __LIBSTROPHE_SNPRINTF_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#if defined(HAVE_SNPRINTF) || defined(HAVE_VSNPRINTF)
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SNPRINTF
|
||||||
|
#define strophe_snprintf snprintf
|
||||||
|
#else
|
||||||
|
int strophe_snprintf(char *str, size_t count, const char *fmt, ...);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_VSNPRINTF
|
||||||
|
#define strophe_vsnprintf vsnprintf
|
||||||
|
#else
|
||||||
|
int strophe_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __LIBSTROPHE_SNPRINTF_H__ */
|
||||||
1121
src/sock.c
1121
src/sock.c
File diff suppressed because it is too large
Load Diff
46
src/sock.h
46
src/sock.h
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* sock.h
|
/* sock.h
|
||||||
** strophe XMPP client library -- socket abstraction header
|
** strophe XMPP client library -- socket abstraction header
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -23,29 +21,43 @@
|
|||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
typedef int sock_t;
|
typedef int sock_t;
|
||||||
|
#define INVALID_SOCKET (-1)
|
||||||
#else
|
#else
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
#include <mstcpip.h> /* tcp_keepalive */
|
||||||
typedef SOCKET sock_t;
|
typedef SOCKET sock_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern const struct conn_interface sock_intf;
|
||||||
|
|
||||||
|
typedef struct _xmpp_sock_t xmpp_sock_t;
|
||||||
|
|
||||||
void sock_initialize(void);
|
void sock_initialize(void);
|
||||||
void sock_shutdown(void);
|
void sock_shutdown(void);
|
||||||
|
|
||||||
int sock_error(void);
|
int sock_error(struct conn_interface *intf);
|
||||||
|
|
||||||
sock_t sock_connect(const char * const host, const unsigned int port);
|
xmpp_sock_t *sock_new(xmpp_conn_t *conn,
|
||||||
int sock_close(const sock_t sock);
|
const char *domain,
|
||||||
|
const char *host,
|
||||||
|
unsigned short port);
|
||||||
|
void sock_free(xmpp_sock_t *xsock);
|
||||||
|
sock_t sock_connect(xmpp_sock_t *xsock);
|
||||||
|
int sock_close(sock_t sock);
|
||||||
|
|
||||||
int sock_set_blocking(const sock_t sock);
|
int sock_set_blocking(sock_t sock);
|
||||||
int sock_set_nonblocking(const sock_t sock);
|
int sock_set_nonblocking(sock_t sock);
|
||||||
int sock_read(const sock_t sock, void * const buff, const size_t len);
|
int sock_read(struct conn_interface *intf, void *buff, size_t len);
|
||||||
int sock_write(const sock_t sock, const void * const buff, const size_t len);
|
int sock_write(struct conn_interface *intf, const void *buff, size_t len);
|
||||||
int sock_is_recoverable(const int error);
|
int sock_is_recoverable(struct conn_interface *intf, int error);
|
||||||
/* checks for an error after connect, return 0 if connect successful */
|
/* checks for an error after connect, return 0 if connect successful */
|
||||||
int sock_connect_error(const sock_t sock);
|
int sock_connect_error(sock_t sock);
|
||||||
|
int sock_set_keepalive(sock_t sock,
|
||||||
int sock_srv_lookup(const char *service, const char *proto,
|
int timeout,
|
||||||
const char *domain, char *resulttarget,
|
int interval,
|
||||||
int resulttargetlength, int *resultport);
|
int count,
|
||||||
|
unsigned int user_timeout);
|
||||||
|
|
||||||
#endif /* __LIBSTROPHE_SOCK_H__ */
|
#endif /* __LIBSTROPHE_SOCK_H__ */
|
||||||
|
|||||||
1359
src/stanza.c
1359
src/stanza.c
File diff suppressed because it is too large
Load Diff
119
src/thread.c
119
src/thread.c
@@ -1,119 +0,0 @@
|
|||||||
/* thread.c
|
|
||||||
** strophe XMPP client library -- thread abstraction
|
|
||||||
**
|
|
||||||
** Copyright (C) 2005-2009 Collecta, Inc.
|
|
||||||
**
|
|
||||||
** This software is provided AS-IS with no warranty, either express
|
|
||||||
** or implied.
|
|
||||||
**
|
|
||||||
** This software is distributed under license and may not be copied,
|
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @file
|
|
||||||
* Thread absraction.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <pthread.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "strophe.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "thread.h"
|
|
||||||
|
|
||||||
struct _mutex_t {
|
|
||||||
const xmpp_ctx_t *ctx;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
HANDLE mutex;
|
|
||||||
#else
|
|
||||||
pthread_mutex_t *mutex;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
/* mutex functions */
|
|
||||||
|
|
||||||
mutex_t *mutex_create(const xmpp_ctx_t * ctx)
|
|
||||||
{
|
|
||||||
mutex_t *mutex;
|
|
||||||
|
|
||||||
mutex = xmpp_alloc(ctx, sizeof(mutex_t));
|
|
||||||
if (mutex) {
|
|
||||||
mutex->ctx = ctx;
|
|
||||||
#ifdef _WIN32
|
|
||||||
mutex->mutex = CreateMutex(NULL, FALSE, NULL);
|
|
||||||
#else
|
|
||||||
mutex->mutex = xmpp_alloc(ctx, sizeof(pthread_mutex_t));
|
|
||||||
if (mutex->mutex)
|
|
||||||
if (pthread_mutex_init(mutex->mutex, NULL) != 0) {
|
|
||||||
xmpp_free(ctx, mutex->mutex);
|
|
||||||
mutex->mutex = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!mutex->mutex) {
|
|
||||||
xmpp_free(ctx, mutex);
|
|
||||||
mutex = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mutex;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mutex_destroy(mutex_t *mutex)
|
|
||||||
{
|
|
||||||
int ret = 1;
|
|
||||||
const xmpp_ctx_t *ctx;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (mutex->mutex)
|
|
||||||
ret = CloseHandle(mutex->mutex);
|
|
||||||
#else
|
|
||||||
if (mutex->mutex)
|
|
||||||
ret = pthread_mutex_destroy(mutex->mutex) == 0;
|
|
||||||
#endif
|
|
||||||
ctx = mutex->ctx;
|
|
||||||
xmpp_free(ctx, mutex);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mutex_lock(mutex_t *mutex)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
ret = WaitForSingleObject(mutex->mutex, INFINITE) == 0;
|
|
||||||
#else
|
|
||||||
ret = pthread_mutex_lock(mutex->mutex) == 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mutex_trylock(mutex_t *mutex)
|
|
||||||
{
|
|
||||||
/* TODO */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mutex_unlock(mutex_t *mutex)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
ret = ReleaseMutex(mutex->mutex);
|
|
||||||
#else
|
|
||||||
ret = pthread_mutex_unlock(mutex->mutex) == 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
43
src/thread.h
43
src/thread.h
@@ -1,43 +0,0 @@
|
|||||||
/* thread.h
|
|
||||||
** strophe XMPP client library -- thread abstraction header
|
|
||||||
**
|
|
||||||
** Copyright (C) 2005-2009 Collecta, Inc.
|
|
||||||
**
|
|
||||||
** This software is provided AS-IS with no warranty, either express
|
|
||||||
** or implied.
|
|
||||||
**
|
|
||||||
** This software is distributed under license and may not be copied,
|
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @file
|
|
||||||
* Threading abstraction API.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __LIBSTROPHE_THREAD_H__
|
|
||||||
#define __LIBSTROPHE_THREAD_H__
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <pthread.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "strophe.h"
|
|
||||||
|
|
||||||
typedef struct _mutex_t mutex_t;
|
|
||||||
|
|
||||||
/* mutex functions */
|
|
||||||
|
|
||||||
mutex_t *mutex_create(const xmpp_ctx_t *ctx);
|
|
||||||
int mutex_destroy(mutex_t *mutex);
|
|
||||||
int mutex_lock(mutex_t *mutex);
|
|
||||||
int mutex_trylock(mutex_t *mutex);
|
|
||||||
int mutex_unlock(mutex_t *mutex);
|
|
||||||
|
|
||||||
#endif /* __LIBSTROPHE_THREAD_H__ */
|
|
||||||
266
src/tls.c
Normal file
266
src/tls.c
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* tls.c
|
||||||
|
** strophe XMPP client library -- generic TLS functions
|
||||||
|
**
|
||||||
|
** Copyright (C) 2021 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
|
||||||
|
**
|
||||||
|
** This software is provided AS-IS with no warranty, either express
|
||||||
|
** or implied.
|
||||||
|
**
|
||||||
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* Generic TLS functionality.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @defgroup TLS SSL/TLS specific functionality
|
||||||
|
* These functions provide SSL/TLS specific functionality.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "strophe.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
const struct conn_interface tls_intf = {
|
||||||
|
tls_read,
|
||||||
|
tls_write,
|
||||||
|
tls_clear_pending_write,
|
||||||
|
tls_pending,
|
||||||
|
tls_error,
|
||||||
|
tls_is_recoverable,
|
||||||
|
/* init conn */
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _dnsname_t {
|
||||||
|
char **data;
|
||||||
|
size_t cur, max;
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t tlscert_dnsnames_increment = 4;
|
||||||
|
|
||||||
|
/** Get the Strophe context which is assigned to this certificate.
|
||||||
|
*
|
||||||
|
* @param cert a Strophe TLS certificate object
|
||||||
|
*
|
||||||
|
* @return the Strophe context object where this certificate originates from
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
xmpp_ctx_t *xmpp_tlscert_get_ctx(const xmpp_tlscert_t *cert)
|
||||||
|
{
|
||||||
|
return cert->ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the Strophe connection which is assigned to this certificate.
|
||||||
|
*
|
||||||
|
* @param cert a Strophe TLS certificate object
|
||||||
|
*
|
||||||
|
* @return the Strophe connection object where this certificate originates from
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
xmpp_conn_t *xmpp_tlscert_get_conn(const xmpp_tlscert_t *cert)
|
||||||
|
{
|
||||||
|
return cert->conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the complete PEM of this certificate.
|
||||||
|
*
|
||||||
|
* @param cert a Strophe TLS certificate object
|
||||||
|
*
|
||||||
|
* @return a string containing the PEM of this certificate
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
const char *xmpp_tlscert_get_pem(const xmpp_tlscert_t *cert)
|
||||||
|
{
|
||||||
|
return cert->pem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the dnsName entries out of the SubjectAlternativeNames.
|
||||||
|
*
|
||||||
|
* Note: Max. `MAX_NUM_DNSNAMES` are supported.
|
||||||
|
*
|
||||||
|
* @param cert a Strophe TLS certificate object
|
||||||
|
* @param n which dnsName entry
|
||||||
|
*
|
||||||
|
* @return a string with the n'th dnsName
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
const char *xmpp_tlscert_get_dnsname(const xmpp_tlscert_t *cert, size_t n)
|
||||||
|
{
|
||||||
|
if (n >= cert->dnsnames->cur)
|
||||||
|
return NULL;
|
||||||
|
return cert->dnsnames->data[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get various parts of the certificate as String.
|
||||||
|
*
|
||||||
|
* c.f. \ref xmpp_cert_element_t for details.
|
||||||
|
*
|
||||||
|
* @param cert a Strophe TLS certificate object
|
||||||
|
* @param elmnt which part of the certificate
|
||||||
|
*
|
||||||
|
* @return a string with the part of the certificate
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
const char *xmpp_tlscert_get_string(const xmpp_tlscert_t *cert,
|
||||||
|
xmpp_cert_element_t elmnt)
|
||||||
|
{
|
||||||
|
if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX)
|
||||||
|
return NULL;
|
||||||
|
return cert->elements[elmnt];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a descriptive string for each xmpp_cert_element_t.
|
||||||
|
*
|
||||||
|
* c.f. \ref xmpp_cert_element_t for details.
|
||||||
|
*
|
||||||
|
* @param elmnt which element
|
||||||
|
*
|
||||||
|
* @return a string with the description
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt)
|
||||||
|
{
|
||||||
|
static const char *descriptions[] = {
|
||||||
|
"X.509 Version",
|
||||||
|
"SerialNumber",
|
||||||
|
"Subject",
|
||||||
|
"Issuer",
|
||||||
|
"Issued On",
|
||||||
|
"Expires On",
|
||||||
|
"Public Key Algorithm",
|
||||||
|
"Certificate Signature Algorithm",
|
||||||
|
"Fingerprint SHA-1",
|
||||||
|
"Fingerprint SHA-256",
|
||||||
|
};
|
||||||
|
if (elmnt < 0 || elmnt >= XMPP_CERT_ELEMENT_MAX)
|
||||||
|
return NULL;
|
||||||
|
return descriptions[elmnt];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Allocate and initialize a Strophe TLS certificate object.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
*
|
||||||
|
* @return a certificate object or NULL
|
||||||
|
*/
|
||||||
|
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
xmpp_tlscert_t *tlscert = strophe_alloc(ctx, sizeof(*tlscert));
|
||||||
|
if (!tlscert)
|
||||||
|
return NULL;
|
||||||
|
memset(tlscert, 0, sizeof(*tlscert));
|
||||||
|
|
||||||
|
tlscert->dnsnames = strophe_alloc(ctx, sizeof(*tlscert->dnsnames));
|
||||||
|
if (!tlscert->dnsnames) {
|
||||||
|
strophe_free(ctx, tlscert);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(tlscert->dnsnames, 0, sizeof(*tlscert->dnsnames));
|
||||||
|
|
||||||
|
tlscert->ctx = ctx;
|
||||||
|
|
||||||
|
return tlscert;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Free a certificate object.
|
||||||
|
*
|
||||||
|
* @param cert a Strophe TLS certificate object
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
void xmpp_tlscert_free(xmpp_tlscert_t *cert)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
for (n = 0; n < ARRAY_SIZE(cert->elements); ++n) {
|
||||||
|
if (cert->elements[n])
|
||||||
|
strophe_free(cert->ctx, cert->elements[n]);
|
||||||
|
}
|
||||||
|
if (cert->dnsnames->data) {
|
||||||
|
for (n = 0; n < cert->dnsnames->cur; ++n) {
|
||||||
|
if (cert->dnsnames->data[n])
|
||||||
|
strophe_free(cert->ctx, cert->dnsnames->data[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strophe_free(cert->ctx, cert->dnsnames->data);
|
||||||
|
strophe_free(cert->ctx, cert->dnsnames);
|
||||||
|
if (cert->pem)
|
||||||
|
strophe_free(cert->ctx, cert->pem);
|
||||||
|
strophe_free(cert->ctx, cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add a dnsName to the Strophe TLS certificate object.
|
||||||
|
*
|
||||||
|
* @param cert a Strophe TLS certificate object
|
||||||
|
* @param dnsname dnsName that shall be stored
|
||||||
|
*
|
||||||
|
* @return classic Unix style - 0=success, 1=error
|
||||||
|
*/
|
||||||
|
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname)
|
||||||
|
{
|
||||||
|
if ((cert->dnsnames->cur + 1) >= cert->dnsnames->max) {
|
||||||
|
char **dnsnames =
|
||||||
|
strophe_realloc(cert->ctx, cert->dnsnames->data,
|
||||||
|
(cert->dnsnames->max + tlscert_dnsnames_increment) *
|
||||||
|
sizeof(char **));
|
||||||
|
if (!dnsnames)
|
||||||
|
return 1;
|
||||||
|
cert->dnsnames->data = dnsnames;
|
||||||
|
cert->dnsnames->max += tlscert_dnsnames_increment;
|
||||||
|
}
|
||||||
|
cert->dnsnames->data[cert->dnsnames->cur++] =
|
||||||
|
strophe_strdup(cert->ctx, dnsname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tls_caching_password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned char hash[XMPP_SHA1_DIGEST_SIZE];
|
||||||
|
|
||||||
|
const char *fname = conn->tls_client_cert;
|
||||||
|
size_t fname_len = strlen(fname);
|
||||||
|
xmpp_sha1_digest((void *)fname, fname_len, hash);
|
||||||
|
if (fname_len && fname_len == conn->password_cache.fnamelen &&
|
||||||
|
memcmp(hash, conn->password_cache.fname_hash, sizeof(hash)) == 0) {
|
||||||
|
if (conn->password_cache.passlen) {
|
||||||
|
memcpy(pw, conn->password_cache.pass,
|
||||||
|
conn->password_cache.passlen + 1);
|
||||||
|
return conn->password_cache.passlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t max_len = pw_max == 256 ? pw_max : sizeof(conn->password_cache.pass);
|
||||||
|
ret = conn->password_callback(conn->password_cache.pass, max_len, conn,
|
||||||
|
conn->password_callback_userdata);
|
||||||
|
|
||||||
|
if (ret < 0 || ret >= (ssize_t)max_len) {
|
||||||
|
memset(conn->password_cache.pass, 0, sizeof(conn->password_cache.pass));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
conn->password_cache.pass[ret] = '\0';
|
||||||
|
memcpy(pw, conn->password_cache.pass, ret + 1);
|
||||||
|
conn->password_cache.passlen = ret;
|
||||||
|
conn->password_cache.fnamelen = fname_len;
|
||||||
|
memcpy(conn->password_cache.fname_hash, hash, sizeof(hash));
|
||||||
|
return conn->password_cache.passlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tls_clear_password_cache(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
memset(&conn->password_cache, 0, sizeof(conn->password_cache));
|
||||||
|
}
|
||||||
48
src/tls.h
48
src/tls.h
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* tls.h
|
/* tls.h
|
||||||
** strophe XMPP client library -- TLS abstraction header
|
** strophe XMPP client library -- TLS abstraction header
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -24,24 +22,52 @@
|
|||||||
|
|
||||||
typedef struct _tls tls_t;
|
typedef struct _tls tls_t;
|
||||||
|
|
||||||
|
typedef struct _dnsname_t dnsname_t;
|
||||||
|
|
||||||
|
struct _xmpp_tlscert_t {
|
||||||
|
xmpp_ctx_t *ctx;
|
||||||
|
xmpp_conn_t *conn;
|
||||||
|
char *pem;
|
||||||
|
char *elements[XMPP_CERT_ELEMENT_MAX];
|
||||||
|
dnsname_t *dnsnames;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* provided by the real TLS implementation */
|
||||||
|
|
||||||
void tls_initialize(void);
|
void tls_initialize(void);
|
||||||
void tls_shutdown(void);
|
void tls_shutdown(void);
|
||||||
|
|
||||||
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock);
|
tls_t *tls_new(xmpp_conn_t *conn);
|
||||||
void tls_free(tls_t *tls);
|
void tls_free(tls_t *tls);
|
||||||
|
|
||||||
|
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n);
|
||||||
|
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn);
|
||||||
|
|
||||||
|
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn);
|
||||||
int tls_set_credentials(tls_t *tls, const char *cafilename);
|
int tls_set_credentials(tls_t *tls, const char *cafilename);
|
||||||
|
int tls_init_channel_binding(tls_t *tls,
|
||||||
|
const char **binding_prefix,
|
||||||
|
size_t *binding_prefix_len);
|
||||||
|
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size);
|
||||||
|
|
||||||
int tls_start(tls_t *tls);
|
int tls_start(tls_t *tls);
|
||||||
int tls_stop(tls_t *tls);
|
int tls_stop(tls_t *tls);
|
||||||
|
|
||||||
int tls_error(tls_t *tls);
|
int tls_pending(struct conn_interface *intf);
|
||||||
|
int tls_read(struct conn_interface *intf, void *buff, size_t len);
|
||||||
|
int tls_write(struct conn_interface *intf, const void *buff, size_t len);
|
||||||
|
int tls_clear_pending_write(struct conn_interface *intf);
|
||||||
|
|
||||||
int tls_pending(tls_t *tls);
|
int tls_error(struct conn_interface *intf);
|
||||||
int tls_read(tls_t *tls, void * const buff, const size_t len);
|
int tls_is_recoverable(struct conn_interface *intf, int error);
|
||||||
int tls_write(tls_t *tls, const void * const buff, const size_t len);
|
|
||||||
|
|
||||||
int tls_clear_pending_write(tls_t *tls);
|
/* provided by tls.c */
|
||||||
int tls_is_recoverable(int error);
|
extern const struct conn_interface tls_intf;
|
||||||
|
|
||||||
|
xmpp_tlscert_t *tlscert_new(xmpp_ctx_t *ctx);
|
||||||
|
int tlscert_add_dnsname(xmpp_tlscert_t *cert, const char *dnsname);
|
||||||
|
|
||||||
|
int tls_caching_password_callback(char *pw, size_t pw_max, xmpp_conn_t *conn);
|
||||||
|
void tls_clear_password_cache(xmpp_conn_t *conn);
|
||||||
|
|
||||||
#endif /* __LIBSTROPHE_TLS_H__ */
|
#endif /* __LIBSTROPHE_TLS_H__ */
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* tls_dummy.c
|
/* tls_dummy.c
|
||||||
** strophe XMPP client library -- TLS abstraction dummy impl.
|
** strophe XMPP client library -- TLS abstraction dummy impl.
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -36,59 +34,113 @@ void tls_shutdown(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
|
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
|
||||||
{
|
{
|
||||||
|
UNUSED(conn);
|
||||||
|
UNUSED(n);
|
||||||
|
/* always fail */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
UNUSED(conn);
|
||||||
|
/* always fail */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tls_t *tls_new(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
UNUSED(conn);
|
||||||
/* always fail */
|
/* always fail */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tls_free(tls_t *tls)
|
void tls_free(tls_t *tls)
|
||||||
{
|
{
|
||||||
|
UNUSED(tls);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
UNUSED(conn);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int tls_set_credentials(tls_t *tls, const char *cafilename)
|
int tls_set_credentials(tls_t *tls, const char *cafilename)
|
||||||
{
|
{
|
||||||
|
UNUSED(tls);
|
||||||
|
UNUSED(cafilename);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tls_init_channel_binding(tls_t *tls,
|
||||||
|
const char **binding_prefix,
|
||||||
|
size_t *binding_prefix_len)
|
||||||
|
{
|
||||||
|
UNUSED(tls);
|
||||||
|
UNUSED(binding_prefix);
|
||||||
|
UNUSED(binding_prefix_len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
|
||||||
|
{
|
||||||
|
UNUSED(tls);
|
||||||
|
UNUSED(size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int tls_start(tls_t *tls)
|
int tls_start(tls_t *tls)
|
||||||
{
|
{
|
||||||
|
UNUSED(tls);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_stop(tls_t *tls)
|
int tls_stop(tls_t *tls)
|
||||||
{
|
{
|
||||||
|
UNUSED(tls);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_error(tls_t *tls)
|
int tls_error(struct conn_interface *intf)
|
||||||
{
|
{
|
||||||
/* todo: some kind of error polling/dump */
|
UNUSED(intf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_pending(tls_t *tls)
|
int tls_pending(struct conn_interface *intf)
|
||||||
{
|
{
|
||||||
|
UNUSED(intf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_read(tls_t *tls, void * const buff, const size_t len)
|
int tls_read(struct conn_interface *intf, void *buff, size_t len)
|
||||||
{
|
{
|
||||||
|
UNUSED(intf);
|
||||||
|
UNUSED(buff);
|
||||||
|
UNUSED(len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_write(tls_t *tls, const void * const buff, const size_t len)
|
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||||
{
|
{
|
||||||
|
UNUSED(intf);
|
||||||
|
UNUSED(buff);
|
||||||
|
UNUSED(len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_clear_pending_write(tls_t *tls)
|
int tls_clear_pending_write(struct conn_interface *intf)
|
||||||
{
|
{
|
||||||
|
UNUSED(intf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_is_recoverable(int error)
|
int tls_is_recoverable(struct conn_interface *intf, int error)
|
||||||
{
|
{
|
||||||
|
UNUSED(intf);
|
||||||
|
UNUSED(error);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
616
src/tls_gnutls.c
616
src/tls_gnutls.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* tls.c
|
/* tls.c
|
||||||
** strophe XMPP client library -- TLS abstraction header
|
** strophe XMPP client library -- TLS abstraction header
|
||||||
**
|
**
|
||||||
@@ -6,27 +7,45 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
* TLS implementation with GNUTLS
|
* TLS implementation with GNUTLS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
|
#include <gnutls/x509.h>
|
||||||
|
#include <gnutls/x509-ext.h>
|
||||||
|
#include <gnutls/pkcs11.h>
|
||||||
|
#include <gnutls/pkcs12.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "tls.h"
|
#include "tls.h"
|
||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
|
|
||||||
|
#if GNUTLS_VERSION_NUMBER < 0x030702
|
||||||
|
/* This is a very dirty workaround to make this file compile on older GnuTLS
|
||||||
|
* We simply define our own value for the later introduced
|
||||||
|
* `GNUTLS_CB_TLS_EXPORTER` which makes it compile, but it will fail on runtime
|
||||||
|
* when the combination of the following options happen:
|
||||||
|
* * GnuTLS < 3.7.2
|
||||||
|
* * TLS 1.3
|
||||||
|
* * A SCRAM PLUS variant to authenticate
|
||||||
|
* We will think about fixing this once a user pops up who has that problem.
|
||||||
|
*/
|
||||||
|
#define GNUTLS_CB_TLS_EXPORTER 3
|
||||||
|
#endif
|
||||||
|
|
||||||
struct _tls {
|
struct _tls {
|
||||||
xmpp_ctx_t *ctx; /* do we need this? */
|
xmpp_ctx_t *ctx; /* do we need this? */
|
||||||
sock_t sock;
|
xmpp_conn_t *conn;
|
||||||
gnutls_session_t session;
|
gnutls_session_t session;
|
||||||
gnutls_certificate_credentials_t cred;
|
gnutls_certificate_credentials_t cred;
|
||||||
|
gnutls_x509_crt_t client_cert;
|
||||||
|
gnutls_datum_t channel_binding;
|
||||||
|
int lasterror;
|
||||||
};
|
};
|
||||||
|
|
||||||
void tls_initialize(void)
|
void tls_initialize(void)
|
||||||
@@ -45,88 +64,615 @@ void tls_shutdown(void)
|
|||||||
gnutls_global_deinit();
|
gnutls_global_deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
|
static int _tls_password_callback(void *userdata,
|
||||||
|
int attempt,
|
||||||
|
const char *token_url,
|
||||||
|
const char *token_label,
|
||||||
|
unsigned int flags,
|
||||||
|
char *pin,
|
||||||
|
size_t pin_max)
|
||||||
{
|
{
|
||||||
tls_t *tls = xmpp_alloc(ctx, sizeof(tls_t));
|
xmpp_conn_t *conn = userdata;
|
||||||
const int cert_type_priority[3] = { GNUTLS_CRT_X509,
|
UNUSED(attempt);
|
||||||
GNUTLS_CRT_OPENPGP, 0 };
|
UNUSED(token_url);
|
||||||
|
UNUSED(token_label);
|
||||||
|
UNUSED(flags);
|
||||||
|
int ret = tls_caching_password_callback(pin, pin_max, conn);
|
||||||
|
return ret > 0 ? 0 : GNUTLS_E_PKCS11_PIN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gnutls_x509_crt_t _tls_load_cert_x509(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
gnutls_x509_crt_t cert;
|
||||||
|
gnutls_datum_t data;
|
||||||
|
int res;
|
||||||
|
if (gnutls_x509_crt_init(&cert) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (gnutls_load_file(conn->tls_client_cert, &data) < 0)
|
||||||
|
goto error_out;
|
||||||
|
res = gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_PEM);
|
||||||
|
gnutls_free(data.data);
|
||||||
|
if (res < 0)
|
||||||
|
goto error_out;
|
||||||
|
return cert;
|
||||||
|
error_out:
|
||||||
|
gnutls_x509_crt_deinit(cert);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gnutls_x509_crt_t _tls_load_cert_p12(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
gnutls_pkcs12_t p12;
|
||||||
|
gnutls_x509_crt_t *cert = NULL;
|
||||||
|
gnutls_datum_t data;
|
||||||
|
gnutls_x509_privkey_t key;
|
||||||
|
unsigned int cert_num = 0, retries = 0;
|
||||||
|
int err = -1;
|
||||||
|
if (gnutls_pkcs12_init(&p12) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (gnutls_load_file(conn->tls_client_cert, &data) < 0)
|
||||||
|
goto error_out;
|
||||||
|
if (gnutls_pkcs12_import(p12, &data, GNUTLS_X509_FMT_DER, 0) < 0)
|
||||||
|
goto error_out2;
|
||||||
|
|
||||||
|
/* First try to open file with no pass */
|
||||||
|
if ((err = gnutls_pkcs12_simple_parse(p12, NULL, &key, &cert, &cert_num,
|
||||||
|
NULL, NULL, NULL, 0)) == 0) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* Now let's try to open file with an empty pass */
|
||||||
|
if ((err = gnutls_pkcs12_simple_parse(p12, "", &key, &cert, &cert_num, NULL,
|
||||||
|
NULL, NULL, 0)) == 0) {
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn->password_callback) {
|
||||||
|
strophe_error(conn->ctx, "tls", "No password callback set");
|
||||||
|
goto error_out2;
|
||||||
|
}
|
||||||
|
/* ... and only now ask the user for a password */
|
||||||
|
while (retries++ < conn->password_retries) {
|
||||||
|
char pass[GNUTLS_PKCS11_MAX_PIN_LEN];
|
||||||
|
int passlen =
|
||||||
|
_tls_password_callback(conn, 0, NULL, NULL, 0, pass, sizeof(pass));
|
||||||
|
if (passlen < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
err = gnutls_pkcs12_simple_parse(p12, pass, &key, &cert, &cert_num,
|
||||||
|
NULL, NULL, NULL, 0);
|
||||||
|
memset(pass, 0, sizeof(pass));
|
||||||
|
if (err == 0)
|
||||||
|
break;
|
||||||
|
tls_clear_password_cache(conn);
|
||||||
|
if (err != GNUTLS_E_DECRYPTION_FAILED &&
|
||||||
|
err != GNUTLS_E_MAC_VERIFY_FAILED) {
|
||||||
|
strophe_error(conn->ctx, "tls", "could not read P12 file");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
strophe_debug(conn->ctx, "tls", "wrong password?");
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
gnutls_pkcs12_deinit(p12);
|
||||||
|
gnutls_free(data.data);
|
||||||
|
if (err < 0)
|
||||||
|
goto error_out;
|
||||||
|
gnutls_x509_privkey_deinit(key);
|
||||||
|
if (cert_num > 1) {
|
||||||
|
strophe_error(conn->ctx, "tls", "Can't handle stack of %u certs",
|
||||||
|
cert_num);
|
||||||
|
goto error_out;
|
||||||
|
}
|
||||||
|
gnutls_x509_crt_t ret = *cert;
|
||||||
|
gnutls_free(cert);
|
||||||
|
return ret;
|
||||||
|
error_out2:
|
||||||
|
gnutls_free(data.data);
|
||||||
|
error_out:
|
||||||
|
tls_clear_password_cache(conn);
|
||||||
|
if (cert) {
|
||||||
|
for (unsigned int n = 0; n < cert_num; ++n) {
|
||||||
|
gnutls_x509_crt_deinit(cert[n]);
|
||||||
|
}
|
||||||
|
gnutls_free(cert);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gnutls_x509_crt_t _tls_load_cert(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
if (conn->tls && conn->tls->client_cert)
|
||||||
|
return conn->tls->client_cert;
|
||||||
|
if (conn->tls_client_cert && !conn->tls_client_key) {
|
||||||
|
return _tls_load_cert_p12(conn);
|
||||||
|
}
|
||||||
|
return _tls_load_cert_x509(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tls_free_cert(xmpp_conn_t *conn, gnutls_x509_crt_t cert)
|
||||||
|
{
|
||||||
|
if (conn->tls && conn->tls->client_cert == cert)
|
||||||
|
return;
|
||||||
|
gnutls_x509_crt_deinit(cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _tls_get_id_on_xmppaddr(xmpp_conn_t *conn,
|
||||||
|
gnutls_x509_crt_t cert,
|
||||||
|
unsigned int n,
|
||||||
|
char **ret)
|
||||||
|
{
|
||||||
|
gnutls_datum_t san;
|
||||||
|
size_t name_len, oid_len;
|
||||||
|
char oid[128], name[128];
|
||||||
|
name_len = oid_len = 128;
|
||||||
|
int res =
|
||||||
|
gnutls_x509_crt_get_subject_alt_name(cert, n, name, &name_len, NULL);
|
||||||
|
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
|
||||||
|
/* This is the happy flow path with fixed GnuTLS.
|
||||||
|
* While implementing this I stumbled over an issue in GnuTLS
|
||||||
|
* which lead to
|
||||||
|
* https://gitlab.com/gnutls/gnutls/-/merge_requests/1397
|
||||||
|
*/
|
||||||
|
if (ret) {
|
||||||
|
*ret = strophe_strdup(conn->ctx, name);
|
||||||
|
}
|
||||||
|
return GNUTLS_SAN_OTHERNAME_XMPP;
|
||||||
|
}
|
||||||
|
if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
|
||||||
|
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
|
||||||
|
if (res != GNUTLS_SAN_OTHERNAME)
|
||||||
|
return GNUTLS_E_X509_UNKNOWN_SAN;
|
||||||
|
res = gnutls_x509_crt_get_subject_alt_othername_oid(cert, n, oid, &oid_len);
|
||||||
|
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
|
||||||
|
gnutls_datum_t xmpp_addr;
|
||||||
|
san.data = (unsigned char *)name;
|
||||||
|
san.size = name_len;
|
||||||
|
res = gnutls_x509_othername_to_virtual(oid, &san, NULL, &xmpp_addr);
|
||||||
|
if (res < 0) {
|
||||||
|
gnutls_free(xmpp_addr.data);
|
||||||
|
return GNUTLS_E_MEMORY_ERROR;
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
*ret = strophe_strdup(conn->ctx, (char *)xmpp_addr.data);
|
||||||
|
}
|
||||||
|
gnutls_free(xmpp_addr.data);
|
||||||
|
return GNUTLS_SAN_OTHERNAME_XMPP;
|
||||||
|
}
|
||||||
|
return GNUTLS_E_X509_UNKNOWN_SAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _tls_id_on_xmppaddr(xmpp_conn_t *conn,
|
||||||
|
gnutls_x509_crt_t cert,
|
||||||
|
unsigned int n,
|
||||||
|
char **ret)
|
||||||
|
{
|
||||||
|
int res = GNUTLS_E_SUCCESS;
|
||||||
|
unsigned int i, j;
|
||||||
|
for (i = j = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; ++i) {
|
||||||
|
res = _tls_get_id_on_xmppaddr(conn, cert, i, NULL);
|
||||||
|
if (res == GNUTLS_SAN_OTHERNAME_XMPP) {
|
||||||
|
if (j == n) {
|
||||||
|
res = _tls_get_id_on_xmppaddr(conn, cert, i, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search through the SubjectAlternativeNames and return the next
|
||||||
|
* id-on-xmppAddr element starting from `n`.
|
||||||
|
*/
|
||||||
|
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
|
||||||
|
{
|
||||||
|
char *ret = NULL;
|
||||||
|
gnutls_x509_crt_t cert = _tls_load_cert(conn);
|
||||||
|
if (cert == NULL)
|
||||||
|
return NULL;
|
||||||
|
_tls_id_on_xmppaddr(conn, cert, n, &ret);
|
||||||
|
_tls_free_cert(conn, cert);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
unsigned int ret = 0, n;
|
||||||
|
int res = GNUTLS_E_SUCCESS;
|
||||||
|
gnutls_x509_crt_t cert = _tls_load_cert(conn);
|
||||||
|
if (cert == NULL)
|
||||||
|
return 0;
|
||||||
|
for (n = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; ++n) {
|
||||||
|
res = _tls_id_on_xmppaddr(conn, cert, n, NULL);
|
||||||
|
if (res == GNUTLS_SAN_OTHERNAME_XMPP)
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
_tls_free_cert(conn, cert);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xmpp_tlscert_t *_x509_to_tlscert(xmpp_ctx_t *ctx, gnutls_x509_crt_t cert)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
char buf[512], smallbuf[64];
|
||||||
|
size_t size, m;
|
||||||
|
unsigned int algo, n;
|
||||||
|
gnutls_datum_t data;
|
||||||
|
time_t time_val;
|
||||||
|
xmpp_tlscert_t *tlscert = tlscert_new(ctx);
|
||||||
|
|
||||||
|
gnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_PEM, &data);
|
||||||
|
tlscert->pem = strophe_alloc(ctx, data.size + 1);
|
||||||
|
memcpy(tlscert->pem, data.data, data.size);
|
||||||
|
tlscert->pem[data.size] = '\0';
|
||||||
|
gnutls_free(data.data);
|
||||||
|
|
||||||
|
size = sizeof(buf);
|
||||||
|
gnutls_x509_crt_get_dn(cert, buf, &size);
|
||||||
|
tlscert->elements[XMPP_CERT_SUBJECT] = strophe_strdup(ctx, buf);
|
||||||
|
size = sizeof(buf);
|
||||||
|
gnutls_x509_crt_get_issuer_dn(cert, buf, &size);
|
||||||
|
tlscert->elements[XMPP_CERT_ISSUER] = strophe_strdup(ctx, buf);
|
||||||
|
|
||||||
|
time_val = gnutls_x509_crt_get_activation_time(cert);
|
||||||
|
tlscert->elements[XMPP_CERT_NOTBEFORE] =
|
||||||
|
strophe_strdup(ctx, ctime(&time_val));
|
||||||
|
tlscert->elements[XMPP_CERT_NOTBEFORE]
|
||||||
|
[strlen(tlscert->elements[XMPP_CERT_NOTBEFORE]) - 1] =
|
||||||
|
'\0';
|
||||||
|
time_val = gnutls_x509_crt_get_expiration_time(cert);
|
||||||
|
tlscert->elements[XMPP_CERT_NOTAFTER] =
|
||||||
|
strophe_strdup(ctx, ctime(&time_val));
|
||||||
|
tlscert->elements[XMPP_CERT_NOTAFTER]
|
||||||
|
[strlen(tlscert->elements[XMPP_CERT_NOTAFTER]) - 1] = '\0';
|
||||||
|
|
||||||
|
size = sizeof(smallbuf);
|
||||||
|
gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, smallbuf, &size);
|
||||||
|
hex_encode(buf, smallbuf, size);
|
||||||
|
tlscert->elements[XMPP_CERT_FINGERPRINT_SHA1] = strophe_strdup(ctx, buf);
|
||||||
|
size = sizeof(smallbuf);
|
||||||
|
gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, smallbuf, &size);
|
||||||
|
hex_encode(buf, smallbuf, size);
|
||||||
|
tlscert->elements[XMPP_CERT_FINGERPRINT_SHA256] = strophe_strdup(ctx, buf);
|
||||||
|
|
||||||
|
strophe_snprintf(buf, sizeof(buf), "%d", gnutls_x509_crt_get_version(cert));
|
||||||
|
tlscert->elements[XMPP_CERT_VERSION] = strophe_strdup(ctx, buf);
|
||||||
|
|
||||||
|
algo = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
|
||||||
|
tlscert->elements[XMPP_CERT_KEYALG] =
|
||||||
|
strophe_strdup(ctx, gnutls_pk_algorithm_get_name(algo));
|
||||||
|
algo = gnutls_x509_crt_get_signature_algorithm(cert);
|
||||||
|
tlscert->elements[XMPP_CERT_SIGALG] =
|
||||||
|
strophe_strdup(ctx, gnutls_sign_get_name(algo));
|
||||||
|
|
||||||
|
size = sizeof(smallbuf);
|
||||||
|
gnutls_x509_crt_get_serial(cert, smallbuf, &size);
|
||||||
|
hex_encode(buf, smallbuf, size);
|
||||||
|
tlscert->elements[XMPP_CERT_SERIALNUMBER] = strophe_strdup(ctx, buf);
|
||||||
|
|
||||||
|
for (n = 0, m = 0, res = 0; res != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
|
||||||
|
++n) {
|
||||||
|
size = sizeof(buf);
|
||||||
|
res = gnutls_x509_crt_get_subject_alt_name(cert, n, buf, &size, NULL);
|
||||||
|
if (res == GNUTLS_SAN_DNSNAME) {
|
||||||
|
if (tlscert_add_dnsname(tlscert, buf))
|
||||||
|
strophe_debug(ctx, "tls", "Can't store dnsName(%zu): %s", m,
|
||||||
|
buf);
|
||||||
|
m++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlscert;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _tls_verify(gnutls_session_t session)
|
||||||
|
{
|
||||||
|
tls_t *tls = gnutls_session_get_ptr(session);
|
||||||
|
const gnutls_datum_t *cert_list;
|
||||||
|
gnutls_certificate_type_t type;
|
||||||
|
gnutls_datum_t out;
|
||||||
|
unsigned int cert_list_size = 0, status;
|
||||||
|
gnutls_x509_crt_t cert;
|
||||||
|
|
||||||
|
if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (gnutls_certificate_verify_peers2(session, &status) < 0) {
|
||||||
|
strophe_error(tls->ctx, "tls", "Verify peers failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
type = gnutls_certificate_type_get(session);
|
||||||
|
if (gnutls_certificate_verification_status_print(status, type, &out, 0) <
|
||||||
|
0) {
|
||||||
|
strophe_error(tls->ctx, "tls", "Status print failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return early if the Certificate is trusted
|
||||||
|
* OR if we trust all Certificates */
|
||||||
|
if (status == 0 || tls->conn->tls_trust) {
|
||||||
|
gnutls_free(out.data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tls->conn->certfail_handler) {
|
||||||
|
strophe_error(tls->ctx, "tls",
|
||||||
|
"No certfail handler set, canceling connection attempt");
|
||||||
|
gnutls_free(out.data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
|
||||||
|
|
||||||
|
/* OpenSSL displays the certificate chain in reverse order than GnuTLS.
|
||||||
|
* To show consistent behavior to the user, traverse the list from the
|
||||||
|
* end.
|
||||||
|
*/
|
||||||
|
while (cert_list_size--) {
|
||||||
|
gnutls_x509_crt_init(&cert);
|
||||||
|
|
||||||
|
gnutls_x509_crt_import(cert, &cert_list[cert_list_size],
|
||||||
|
GNUTLS_X509_FMT_DER);
|
||||||
|
|
||||||
|
xmpp_tlscert_t *tlscert = _x509_to_tlscert(tls->ctx, cert);
|
||||||
|
|
||||||
|
if (!tlscert) {
|
||||||
|
gnutls_x509_crt_deinit(cert);
|
||||||
|
gnutls_free(out.data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tls->conn->certfail_handler(tlscert, (char *)out.data) == 0) {
|
||||||
|
xmpp_tlscert_free(tlscert);
|
||||||
|
gnutls_x509_crt_deinit(cert);
|
||||||
|
gnutls_free(out.data);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
xmpp_tlscert_free(tlscert);
|
||||||
|
gnutls_x509_crt_deinit(cert);
|
||||||
|
}
|
||||||
|
gnutls_free(out.data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tls_t *tls_new(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
tls_t *tls = strophe_alloc(conn->ctx, sizeof(tls_t));
|
||||||
|
|
||||||
if (tls) {
|
if (tls) {
|
||||||
tls->ctx = ctx;
|
memset(tls, 0, sizeof(*tls));
|
||||||
tls->sock = sock;
|
tls->ctx = conn->ctx;
|
||||||
gnutls_init(&tls->session, GNUTLS_CLIENT);
|
tls->conn = conn;
|
||||||
|
gnutls_init(&tls->session, GNUTLS_CLIENT);
|
||||||
|
|
||||||
gnutls_certificate_allocate_credentials(&tls->cred);
|
gnutls_certificate_allocate_credentials(&tls->cred);
|
||||||
|
tls_set_credentials(tls, NULL);
|
||||||
|
|
||||||
gnutls_set_default_priority(tls->session);
|
if (conn->password_callback)
|
||||||
gnutls_certificate_type_set_priority(tls->session, cert_type_priority);
|
gnutls_certificate_set_pin_function(tls->cred,
|
||||||
|
_tls_password_callback, conn);
|
||||||
|
|
||||||
/* fixme: this may require setting a callback on win32? */
|
if (conn->tls_client_cert && conn->tls_client_key) {
|
||||||
gnutls_transport_set_ptr(tls->session,
|
unsigned int retries = 0;
|
||||||
(gnutls_transport_ptr_t)sock);
|
tls->client_cert = _tls_load_cert(conn);
|
||||||
|
if (!tls->client_cert) {
|
||||||
|
strophe_error(tls->ctx, "tls",
|
||||||
|
"could not read client certificate");
|
||||||
|
goto error_out;
|
||||||
|
}
|
||||||
|
while (retries++ < conn->password_retries) {
|
||||||
|
int err = gnutls_certificate_set_x509_key_file(
|
||||||
|
tls->cred, conn->tls_client_cert, conn->tls_client_key,
|
||||||
|
GNUTLS_X509_FMT_PEM);
|
||||||
|
if (err == 0)
|
||||||
|
break;
|
||||||
|
tls_clear_password_cache(conn);
|
||||||
|
if (err != GNUTLS_E_DECRYPTION_FAILED) {
|
||||||
|
strophe_error(tls->ctx, "tls",
|
||||||
|
"could not read private key");
|
||||||
|
goto error_out;
|
||||||
|
}
|
||||||
|
strophe_debug(tls->ctx, "tls", "wrong password?");
|
||||||
|
}
|
||||||
|
} else if (conn->tls_client_cert) {
|
||||||
|
unsigned int retries = 0;
|
||||||
|
|
||||||
|
while (retries++ < conn->password_retries) {
|
||||||
|
char pass[GNUTLS_PKCS11_MAX_PIN_LEN];
|
||||||
|
pass[0] = '\0';
|
||||||
|
int passlen = _tls_password_callback(conn, 0, NULL, NULL, 0,
|
||||||
|
pass, sizeof(pass));
|
||||||
|
if (passlen < 0)
|
||||||
|
continue;
|
||||||
|
int err = gnutls_certificate_set_x509_simple_pkcs12_file(
|
||||||
|
tls->cred, conn->tls_client_cert, GNUTLS_X509_FMT_DER,
|
||||||
|
pass);
|
||||||
|
memset(pass, 0, sizeof(pass));
|
||||||
|
if (err == 0)
|
||||||
|
break;
|
||||||
|
tls_clear_password_cache(conn);
|
||||||
|
if (err != GNUTLS_E_DECRYPTION_FAILED &&
|
||||||
|
err != GNUTLS_E_MAC_VERIFY_FAILED) {
|
||||||
|
strophe_error(tls->ctx, "tls", "could not read P12 file");
|
||||||
|
goto error_out;
|
||||||
|
}
|
||||||
|
strophe_debug(tls->ctx, "tls", "wrong password?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gnutls_certificate_set_verify_function(tls->cred, _tls_verify);
|
||||||
|
|
||||||
|
gnutls_set_default_priority(tls->session);
|
||||||
|
gnutls_session_set_ptr(tls->session, tls);
|
||||||
|
|
||||||
|
/* fixme: this may require setting a callback on win32? */
|
||||||
|
gnutls_transport_set_int(tls->session, conn->sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tls;
|
return tls;
|
||||||
|
error_out:
|
||||||
|
if (tls->client_cert)
|
||||||
|
gnutls_x509_crt_deinit(tls->client_cert);
|
||||||
|
gnutls_certificate_free_credentials(tls->cred);
|
||||||
|
gnutls_deinit(tls->session);
|
||||||
|
strophe_free(tls->ctx, tls);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tls_free(tls_t *tls)
|
void tls_free(tls_t *tls)
|
||||||
{
|
{
|
||||||
|
gnutls_free(tls->channel_binding.data);
|
||||||
|
gnutls_x509_crt_deinit(tls->client_cert);
|
||||||
gnutls_deinit(tls->session);
|
gnutls_deinit(tls->session);
|
||||||
gnutls_certificate_free_credentials(tls->cred);
|
gnutls_certificate_free_credentials(tls->cred);
|
||||||
xmpp_free(tls->ctx, tls);
|
strophe_free(tls->ctx, tls);
|
||||||
|
}
|
||||||
|
|
||||||
|
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
xmpp_tlscert_t *tlscert = NULL;
|
||||||
|
if (conn && conn->tls && conn->tls->session) {
|
||||||
|
unsigned int list_size = 0;
|
||||||
|
const gnutls_datum_t *der_cert =
|
||||||
|
gnutls_certificate_get_peers(conn->tls->session, &list_size);
|
||||||
|
if (der_cert && list_size) {
|
||||||
|
gnutls_x509_crt_t cert;
|
||||||
|
if (gnutls_x509_crt_init(&cert) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (gnutls_x509_crt_import(cert, der_cert, GNUTLS_X509_FMT_DER) ==
|
||||||
|
0)
|
||||||
|
tlscert = _x509_to_tlscert(conn->ctx, cert);
|
||||||
|
gnutls_x509_crt_deinit(cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tlscert;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_set_credentials(tls_t *tls, const char *cafilename)
|
int tls_set_credentials(tls_t *tls, const char *cafilename)
|
||||||
{
|
{
|
||||||
int err;
|
UNUSED(cafilename);
|
||||||
|
|
||||||
/* set trusted credentials -- takes a .pem filename */
|
/* set trusted credentials -- takes a .pem filename */
|
||||||
err = gnutls_certificate_set_x509_trust_file(tls->cred,
|
int err = gnutls_certificate_set_x509_system_trust(tls->cred);
|
||||||
cafilename, GNUTLS_X509_FMT_PEM);
|
if (err >= 0 && tls->conn->tls_cafile)
|
||||||
if (err < 0) return err;
|
err = gnutls_certificate_set_x509_trust_file(
|
||||||
|
tls->cred, tls->conn->tls_cafile, GNUTLS_X509_FMT_PEM);
|
||||||
|
if (err >= 0 && tls->conn->tls_capath)
|
||||||
|
err = gnutls_certificate_set_x509_trust_dir(
|
||||||
|
tls->cred, tls->conn->tls_capath, GNUTLS_X509_FMT_PEM);
|
||||||
|
if (err >= 0) {
|
||||||
|
err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE,
|
||||||
|
tls->cred);
|
||||||
|
}
|
||||||
|
tls->lasterror = err;
|
||||||
|
|
||||||
err = gnutls_credentials_set(tls->session, GNUTLS_CRD_CERTIFICATE,
|
return err == GNUTLS_E_SUCCESS;
|
||||||
tls->cred);
|
}
|
||||||
|
|
||||||
return err;
|
int tls_init_channel_binding(tls_t *tls,
|
||||||
|
const char **binding_prefix,
|
||||||
|
size_t *binding_prefix_len)
|
||||||
|
{
|
||||||
|
gnutls_channel_binding_t binding_type;
|
||||||
|
gnutls_protocol_t tls_version = gnutls_protocol_get_version(tls->session);
|
||||||
|
|
||||||
|
switch (tls_version) {
|
||||||
|
case GNUTLS_SSL3:
|
||||||
|
case GNUTLS_TLS1_0:
|
||||||
|
case GNUTLS_TLS1_1:
|
||||||
|
case GNUTLS_TLS1_2:
|
||||||
|
*binding_prefix = "tls-unique";
|
||||||
|
*binding_prefix_len = strlen("tls-unique");
|
||||||
|
binding_type = GNUTLS_CB_TLS_UNIQUE;
|
||||||
|
break;
|
||||||
|
case GNUTLS_TLS1_3:
|
||||||
|
*binding_prefix = "tls-exporter";
|
||||||
|
*binding_prefix_len = strlen("tls-exporter");
|
||||||
|
binding_type = GNUTLS_CB_TLS_EXPORTER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strophe_error(tls->ctx, "tls", "Unsupported TLS Version: %s",
|
||||||
|
gnutls_protocol_get_name(tls_version));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tls->channel_binding.data) {
|
||||||
|
gnutls_free(tls->channel_binding.data);
|
||||||
|
tls->channel_binding.data = NULL;
|
||||||
|
}
|
||||||
|
int ret = gnutls_session_channel_binding(tls->session, binding_type,
|
||||||
|
&tls->channel_binding);
|
||||||
|
if (ret) {
|
||||||
|
strophe_error(tls->ctx, "tls", "could not get channel binding: %s",
|
||||||
|
gnutls_strerror(ret));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
|
||||||
|
{
|
||||||
|
if (!tls->channel_binding.data || !tls->channel_binding.size) {
|
||||||
|
strophe_error(tls->ctx, "tls", "No channel binding data available");
|
||||||
|
}
|
||||||
|
*size = tls->channel_binding.size;
|
||||||
|
return tls->channel_binding.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_start(tls_t *tls)
|
int tls_start(tls_t *tls)
|
||||||
{
|
{
|
||||||
return gnutls_handshake(tls->session);
|
sock_set_blocking(tls->conn->sock);
|
||||||
|
tls->lasterror = gnutls_handshake(tls->session);
|
||||||
|
sock_set_nonblocking(tls->conn->sock);
|
||||||
|
|
||||||
|
return tls->lasterror == GNUTLS_E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_stop(tls_t *tls)
|
int tls_stop(tls_t *tls)
|
||||||
{
|
{
|
||||||
return gnutls_bye(tls->session, GNUTLS_SHUT_RDWR);
|
tls->lasterror = gnutls_bye(tls->session, GNUTLS_SHUT_RDWR);
|
||||||
|
return tls->lasterror == GNUTLS_E_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_error(tls_t *tls)
|
int tls_error(struct conn_interface *intf)
|
||||||
{
|
{
|
||||||
/* todo: some kind of error polling/dump */
|
return intf->conn->tls->lasterror;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_pending(tls_t *tls)
|
int tls_is_recoverable(struct conn_interface *intf, int error)
|
||||||
{
|
{
|
||||||
return gnutls_record_check_pending (tls->session);
|
UNUSED(intf);
|
||||||
|
return !gnutls_error_is_fatal(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_read(tls_t *tls, void * const buff, const size_t len)
|
int tls_pending(struct conn_interface *intf)
|
||||||
|
{
|
||||||
|
return gnutls_record_check_pending(intf->conn->tls->session);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tls_read(struct conn_interface *intf, void *buff, size_t len)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
tls_t *tls = intf->conn->tls;
|
||||||
|
|
||||||
ret = gnutls_record_recv(tls->session, buff, len);
|
ret = gnutls_record_recv(tls->session, buff, len);
|
||||||
|
tls->lasterror = ret < 0 ? ret : 0;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_write(tls_t *tls, const void * const buff, const size_t len)
|
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
tls_t *tls = intf->conn->tls;
|
||||||
|
|
||||||
ret = gnutls_record_send(tls->session, buff, len);
|
ret = gnutls_record_send(tls->session, buff, len);
|
||||||
|
tls->lasterror = ret < 0 ? ret : 0;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tls_clear_pending_write(struct conn_interface *intf)
|
||||||
|
{
|
||||||
|
UNUSED(intf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
1225
src/tls_openssl.c
1225
src/tls_openssl.c
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* tls_schannel.c
|
/* tls_schannel.c
|
||||||
** strophe XMPP client library -- TLS abstraction schannel impl.
|
** strophe XMPP client library -- TLS abstraction schannel impl.
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -26,6 +24,7 @@
|
|||||||
|
|
||||||
struct _tls {
|
struct _tls {
|
||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
|
xmpp_conn_t *conn;
|
||||||
sock_t sock;
|
sock_t sock;
|
||||||
|
|
||||||
HANDLE hsec32;
|
HANDLE hsec32;
|
||||||
@@ -63,8 +62,25 @@ void tls_shutdown(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
|
char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n)
|
||||||
{
|
{
|
||||||
|
UNUSED(n);
|
||||||
|
/* always fail */
|
||||||
|
strophe_error(conn->ctx, "tls", "Client-Authentication not implemented");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
/* always fail */
|
||||||
|
strophe_error(conn->ctx, "tls", "Client-Authentication not implemented");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tls_t *tls_new(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
xmpp_ctx_t *ctx = conn->ctx;
|
||||||
|
sock_t sock = conn->sock;
|
||||||
tls_t *tls;
|
tls_t *tls;
|
||||||
PSecurityFunctionTable (*pInitSecurityInterface)(void);
|
PSecurityFunctionTable (*pInitSecurityInterface)(void);
|
||||||
SCHANNEL_CRED scred;
|
SCHANNEL_CRED scred;
|
||||||
@@ -84,89 +100,95 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
|
|||||||
|
|
||||||
/* no TLS support on win9x/me, despite what anyone says */
|
/* no TLS support on win9x/me, despite what anyone says */
|
||||||
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
|
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls = xmpp_alloc(ctx, sizeof(*tls));
|
tls = strophe_alloc(ctx, sizeof(*tls));
|
||||||
|
|
||||||
if (!tls) {
|
if (!tls) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(tls, 0, sizeof(*tls));
|
memset(tls, 0, sizeof(*tls));
|
||||||
tls->ctx = ctx;
|
tls->ctx = ctx;
|
||||||
|
tls->conn = conn;
|
||||||
tls->sock = sock;
|
tls->sock = sock;
|
||||||
|
|
||||||
if (!(tls->hsec32 = LoadLibrary ("secur32.dll"))) {
|
if (!(tls->hsec32 = LoadLibrary("secur32.dll"))) {
|
||||||
tls_free(tls);
|
tls_free(tls);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(pInitSecurityInterface =
|
if (!(pInitSecurityInterface =
|
||||||
(void *)GetProcAddress(tls->hsec32, "InitSecurityInterfaceA"))) {
|
(void *)GetProcAddress(tls->hsec32, "InitSecurityInterfaceA"))) {
|
||||||
tls_free(tls);
|
tls_free(tls);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls->sft = pInitSecurityInterface();
|
tls->sft = pInitSecurityInterface();
|
||||||
|
|
||||||
if (!tls->sft) {
|
if (!tls->sft) {
|
||||||
tls_free(tls);
|
tls_free(tls);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = tls->sft->QuerySecurityPackageInfo(UNISP_NAME, &(tls->spi));
|
ret = tls->sft->QuerySecurityPackageInfo(UNISP_NAME, &(tls->spi));
|
||||||
|
|
||||||
if (ret != SEC_E_OK)
|
if (ret != SEC_E_OK) {
|
||||||
{
|
tls_free(tls);
|
||||||
tls_free(tls);
|
return NULL;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xmpp_debug(ctx, "TLSS", "QuerySecurityPackageInfo() success");
|
strophe_debug(ctx, "TLSS", "QuerySecurityPackageInfo() success");
|
||||||
|
|
||||||
memset(&scred, 0, sizeof(scred));
|
memset(&scred, 0, sizeof(scred));
|
||||||
scred.dwVersion = SCHANNEL_CRED_VERSION;
|
scred.dwVersion = SCHANNEL_CRED_VERSION;
|
||||||
/*scred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;*/
|
/*scred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;*/
|
||||||
|
/* Remote server closes connection with forced RC4.
|
||||||
|
The below lines are commented to leave default system configuration */
|
||||||
|
#if 0
|
||||||
/* Something down the line doesn't like AES, so force it to RC4 */
|
/* Something down the line doesn't like AES, so force it to RC4 */
|
||||||
algs[0] = CALG_RC4;
|
algs[0] = CALG_RC4;
|
||||||
scred.cSupportedAlgs = 1;
|
scred.cSupportedAlgs = 1;
|
||||||
scred.palgSupportedAlgs = algs;
|
scred.palgSupportedAlgs = algs;
|
||||||
|
#else
|
||||||
|
(void)algs;
|
||||||
|
#endif
|
||||||
|
|
||||||
ret = tls->sft->AcquireCredentialsHandleA(NULL, UNISP_NAME,
|
ret = tls->sft->AcquireCredentialsHandleA(
|
||||||
SECPKG_CRED_OUTBOUND, NULL, &scred, NULL, NULL, &(tls->hcred), NULL);
|
NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &scred, NULL, NULL,
|
||||||
|
&(tls->hcred), NULL);
|
||||||
|
|
||||||
if (ret != SEC_E_OK)
|
if (ret != SEC_E_OK) {
|
||||||
{
|
tls_free(tls);
|
||||||
tls_free(tls);
|
return NULL;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xmpp_debug(ctx, "TLSS", "AcquireCredentialsHandle() success");
|
strophe_debug(ctx, "TLSS", "AcquireCredentialsHandle() success");
|
||||||
|
|
||||||
tls->init = 1;
|
tls->init = 1;
|
||||||
|
|
||||||
/* This bunch of queries should trip up wine until someone fixes
|
/* This bunch of queries should trip up wine until someone fixes
|
||||||
* schannel support there */
|
* schannel support there */
|
||||||
ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_ALGS, &spc_sa);
|
ret = tls->sft->QueryCredentialsAttributes(
|
||||||
if (ret != SEC_E_OK)
|
&(tls->hcred), SECPKG_ATTR_SUPPORTED_ALGS, &spc_sa);
|
||||||
{
|
if (ret != SEC_E_OK) {
|
||||||
tls_free(tls);
|
tls_free(tls);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_CIPHER_STRENGTHS, &spc_cs);
|
ret = tls->sft->QueryCredentialsAttributes(
|
||||||
if (ret != SEC_E_OK)
|
&(tls->hcred), SECPKG_ATTR_CIPHER_STRENGTHS, &spc_cs);
|
||||||
{
|
if (ret != SEC_E_OK) {
|
||||||
tls_free(tls);
|
tls_free(tls);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = tls->sft->QueryCredentialsAttributes(&(tls->hcred), SECPKG_ATTR_SUPPORTED_PROTOCOLS, &spc_sp);
|
ret = tls->sft->QueryCredentialsAttributes(
|
||||||
if (ret != SEC_E_OK)
|
&(tls->hcred), SECPKG_ATTR_SUPPORTED_PROTOCOLS, &spc_sp);
|
||||||
{
|
if (ret != SEC_E_OK) {
|
||||||
tls_free(tls);
|
tls_free(tls);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tls;
|
return tls;
|
||||||
@@ -175,37 +197,63 @@ tls_t *tls_new(xmpp_ctx_t *ctx, sock_t sock)
|
|||||||
void tls_free(tls_t *tls)
|
void tls_free(tls_t *tls)
|
||||||
{
|
{
|
||||||
if (tls->recvbuffer) {
|
if (tls->recvbuffer) {
|
||||||
xmpp_free(tls->ctx, tls->recvbuffer);
|
strophe_free(tls->ctx, tls->recvbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tls->readybuffer) {
|
if (tls->readybuffer) {
|
||||||
xmpp_free(tls->ctx, tls->readybuffer);
|
strophe_free(tls->ctx, tls->readybuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tls->sendbuffer) {
|
if (tls->sendbuffer) {
|
||||||
xmpp_free(tls->ctx, tls->sendbuffer);
|
strophe_free(tls->ctx, tls->sendbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tls->init) {
|
if (tls->init) {
|
||||||
tls->sft->FreeCredentialsHandle(&(tls->hcred));
|
tls->sft->FreeCredentialsHandle(&(tls->hcred));
|
||||||
}
|
}
|
||||||
|
|
||||||
tls->sft = NULL;
|
tls->sft = NULL;
|
||||||
|
|
||||||
if (tls->hsec32) {
|
if (tls->hsec32) {
|
||||||
FreeLibrary(tls->hsec32);
|
FreeLibrary(tls->hsec32);
|
||||||
tls->hsec32 = NULL;
|
tls->hsec32 = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
xmpp_free(tls->ctx, tls);
|
strophe_free(tls->ctx, tls);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xmpp_tlscert_t *tls_peer_cert(xmpp_conn_t *conn)
|
||||||
|
{
|
||||||
|
/* always fail */
|
||||||
|
strophe_error(conn->ctx, "tls", "tls_peer_cert() not implemented");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int tls_set_credentials(tls_t *tls, const char *cafilename)
|
int tls_set_credentials(tls_t *tls, const char *cafilename)
|
||||||
{
|
{
|
||||||
|
UNUSED(tls);
|
||||||
|
UNUSED(cafilename);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tls_init_channel_binding(tls_t *tls,
|
||||||
|
const char **binding_prefix,
|
||||||
|
size_t *binding_prefix_len)
|
||||||
|
{
|
||||||
|
UNUSED(tls);
|
||||||
|
UNUSED(binding_prefix);
|
||||||
|
UNUSED(binding_prefix_len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *tls_get_channel_binding_data(tls_t *tls, size_t *size)
|
||||||
|
{
|
||||||
|
UNUSED(tls);
|
||||||
|
UNUSED(size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int tls_start(tls_t *tls)
|
int tls_start(tls_t *tls)
|
||||||
{
|
{
|
||||||
ULONG ctxtreq = 0, ctxtattr = 0;
|
ULONG ctxtreq = 0, ctxtattr = 0;
|
||||||
@@ -213,29 +261,17 @@ int tls_start(tls_t *tls)
|
|||||||
SecBuffer sbin[2], sbout[1];
|
SecBuffer sbin[2], sbout[1];
|
||||||
SECURITY_STATUS ret;
|
SECURITY_STATUS ret;
|
||||||
int sent;
|
int sent;
|
||||||
char *name = NULL;
|
char *name;
|
||||||
|
struct conn_interface *intf;
|
||||||
|
|
||||||
/* search the ctx's conns for our sock, and use the domain there as our
|
/* use the domain there as our name */
|
||||||
* name */
|
name = tls->conn->domain;
|
||||||
{
|
intf = tls->conn->intf;
|
||||||
xmpp_connlist_t *listentry = tls->ctx->connlist;
|
|
||||||
|
|
||||||
while (listentry) {
|
ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
|
||||||
xmpp_conn_t *conn = listentry->conn;
|
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
|
||||||
|
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM |
|
||||||
if (conn->sock == tls->sock) {
|
ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_INTEGRITY;
|
||||||
name = strdup(conn->domain);
|
|
||||||
listentry = NULL;
|
|
||||||
} else {
|
|
||||||
listentry = listentry->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctxtreq = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT
|
|
||||||
| ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR
|
|
||||||
| ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM
|
|
||||||
| ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_INTEGRITY;
|
|
||||||
|
|
||||||
memset(&(sbout[0]), 0, sizeof(sbout[0]));
|
memset(&(sbout[0]), 0, sizeof(sbout[0]));
|
||||||
sbout[0].BufferType = SECBUFFER_TOKEN;
|
sbout[0].BufferType = SECBUFFER_TOKEN;
|
||||||
@@ -247,7 +283,7 @@ int tls_start(tls_t *tls)
|
|||||||
|
|
||||||
memset(&(sbin[0]), 0, sizeof(sbin[0]));
|
memset(&(sbin[0]), 0, sizeof(sbin[0]));
|
||||||
sbin[0].BufferType = SECBUFFER_TOKEN;
|
sbin[0].BufferType = SECBUFFER_TOKEN;
|
||||||
sbin[0].pvBuffer = xmpp_alloc(tls->ctx, tls->spi->cbMaxToken);
|
sbin[0].pvBuffer = strophe_alloc(tls->ctx, tls->spi->cbMaxToken);
|
||||||
sbin[0].cbBuffer = tls->spi->cbMaxToken;
|
sbin[0].cbBuffer = tls->spi->cbMaxToken;
|
||||||
|
|
||||||
memset(&(sbin[1]), 0, sizeof(sbin[1]));
|
memset(&(sbin[1]), 0, sizeof(sbin[1]));
|
||||||
@@ -258,202 +294,208 @@ int tls_start(tls_t *tls)
|
|||||||
sbdin.cBuffers = 2;
|
sbdin.cBuffers = 2;
|
||||||
sbdin.pBuffers = sbin;
|
sbdin.pBuffers = sbin;
|
||||||
|
|
||||||
ret = tls->sft->InitializeSecurityContextA(&(tls->hcred), NULL, name, ctxtreq, 0, 0,
|
ret = tls->sft->InitializeSecurityContextA(
|
||||||
NULL, 0, &(tls->hctxt), &sbdout,
|
&(tls->hcred), NULL, name, ctxtreq, 0, 0, NULL, 0, &(tls->hctxt),
|
||||||
&ctxtattr, NULL);
|
&sbdout, &ctxtattr, NULL);
|
||||||
|
|
||||||
while (ret == SEC_I_CONTINUE_NEEDED
|
unsigned char *p = sbin[0].pvBuffer;
|
||||||
|| ret == SEC_I_INCOMPLETE_CREDENTIALS) {
|
int len = 0;
|
||||||
unsigned char *p = sbin[0].pvBuffer;
|
|
||||||
int len = 0, inbytes = 0;
|
|
||||||
|
|
||||||
if (sbdout.pBuffers[0].cbBuffer) {
|
while (ret == SEC_I_CONTINUE_NEEDED ||
|
||||||
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
|
ret == SEC_I_INCOMPLETE_CREDENTIALS ||
|
||||||
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
|
ret == SEC_E_INCOMPLETE_MESSAGE) {
|
||||||
|
int inbytes = 0;
|
||||||
|
|
||||||
sent = sock_write(tls->sock, writebuff, writelen);
|
if (ret != SEC_E_INCOMPLETE_MESSAGE) {
|
||||||
if (sent == -1) {
|
len = 0;
|
||||||
tls->lasterror = sock_error();
|
p = sbin[0].pvBuffer;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
writebuff += sent;
|
|
||||||
writelen -= sent;
|
|
||||||
}
|
|
||||||
tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer);
|
|
||||||
sbdout.pBuffers[0].pvBuffer = NULL;
|
|
||||||
sbdout.pBuffers[0].cbBuffer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* poll for a bit until the remote server stops sending data, ie it
|
if (sbdout.pBuffers[0].cbBuffer) {
|
||||||
* finishes sending the token */
|
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
|
||||||
inbytes = 1;
|
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
|
||||||
{
|
|
||||||
fd_set fds;
|
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
tv.tv_sec = 2;
|
sent = sock_write(intf, writebuff, writelen);
|
||||||
tv.tv_usec = 0;
|
if (sent == -1) {
|
||||||
|
tls->lasterror = sock_error(intf);
|
||||||
|
} else {
|
||||||
|
writebuff += sent;
|
||||||
|
writelen -= sent;
|
||||||
|
}
|
||||||
|
tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer);
|
||||||
|
sbdout.pBuffers[0].pvBuffer = NULL;
|
||||||
|
sbdout.pBuffers[0].cbBuffer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
FD_ZERO(&fds);
|
/* poll for a bit until the remote server stops sending data, ie it
|
||||||
FD_SET(tls->sock, &fds);
|
* finishes sending the token */
|
||||||
|
inbytes = 1;
|
||||||
|
{
|
||||||
|
fd_set fds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
select(tls->sock, &fds, NULL, NULL, &tv);
|
tv.tv_sec = 2;
|
||||||
}
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
while (inbytes > 0) {
|
FD_ZERO(&fds);
|
||||||
fd_set fds;
|
FD_SET(tls->sock, &fds);
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
tv.tv_sec = 0;
|
select(tls->sock, &fds, NULL, NULL, &tv);
|
||||||
tv.tv_usec = 1000;
|
}
|
||||||
|
|
||||||
FD_ZERO(&fds);
|
while (inbytes > 0) {
|
||||||
FD_SET(tls->sock, &fds);
|
fd_set fds;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
select(tls->sock, &fds, NULL, NULL, &tv);
|
tv.tv_sec = 0;
|
||||||
|
tv.tv_usec = 1000;
|
||||||
|
|
||||||
inbytes = sock_read(tls->sock, p, tls->spi->cbMaxToken - len);
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(tls->sock, &fds);
|
||||||
|
|
||||||
if (inbytes > 0) {
|
select(tls->sock, &fds, NULL, NULL, &tv);
|
||||||
len += inbytes;
|
|
||||||
p += inbytes;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tls->lasterror = sock_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
inbytes = sock_read(intf, p, tls->spi->cbMaxToken - len);
|
||||||
|
|
||||||
sbin[0].cbBuffer = len;
|
if (inbytes > 0) {
|
||||||
|
len += inbytes;
|
||||||
|
p += inbytes;
|
||||||
|
} else {
|
||||||
|
tls->lasterror = sock_error(intf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = tls->sft->InitializeSecurityContextA(&(tls->hcred), &(tls->hctxt), name,
|
sbin[0].cbBuffer = len;
|
||||||
ctxtreq, 0, 0, &sbdin, 0,
|
|
||||||
&(tls->hctxt), &sbdout,
|
ret = tls->sft->InitializeSecurityContextA(
|
||||||
&ctxtattr, NULL);
|
&(tls->hcred), &(tls->hctxt), name, ctxtreq, 0, 0, &sbdin, 0,
|
||||||
|
&(tls->hctxt), &sbdout, &ctxtattr, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == SEC_E_OK) {
|
if (ret == SEC_E_OK) {
|
||||||
if (sbdout.pBuffers[0].cbBuffer) {
|
if (sbdout.pBuffers[0].cbBuffer) {
|
||||||
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
|
unsigned char *writebuff = sbdout.pBuffers[0].pvBuffer;
|
||||||
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
|
unsigned int writelen = sbdout.pBuffers[0].cbBuffer;
|
||||||
sent = sock_write(tls->sock, writebuff, writelen);
|
sent = sock_write(intf, writebuff, writelen);
|
||||||
if (sent == -1) {
|
if (sent == -1) {
|
||||||
tls->lasterror = sock_error();
|
tls->lasterror = sock_error(intf);
|
||||||
}
|
} else {
|
||||||
else
|
writebuff += sent;
|
||||||
{
|
writelen -= sent;
|
||||||
writebuff += sent;
|
}
|
||||||
writelen -= sent;
|
tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer);
|
||||||
}
|
sbdout.pBuffers[0].pvBuffer = NULL;
|
||||||
tls->sft->FreeContextBuffer(sbdout.pBuffers[0].pvBuffer);
|
sbdout.pBuffers[0].cbBuffer = 0;
|
||||||
sbdout.pBuffers[0].pvBuffer = NULL;
|
}
|
||||||
sbdout.pBuffers[0].cbBuffer = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xmpp_free(tls->ctx, sbin[0].pvBuffer);
|
strophe_free(tls->ctx, sbin[0].pvBuffer);
|
||||||
|
|
||||||
if (ret != SEC_E_OK) {
|
if (ret != SEC_E_OK) {
|
||||||
tls->lasterror = ret;
|
tls->lasterror = ret;
|
||||||
return 0;
|
strophe_error(tls->ctx, "TLSS", "Schannel error 0x%lx",
|
||||||
|
(unsigned long)ret);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls->sft->QueryContextAttributes(&(tls->hctxt), SECPKG_ATTR_STREAM_SIZES,
|
tls->sft->QueryContextAttributes(&(tls->hctxt), SECPKG_ATTR_STREAM_SIZES,
|
||||||
&(tls->spcss));
|
&(tls->spcss));
|
||||||
|
|
||||||
tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage
|
tls->recvbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
|
||||||
+ tls->spcss.cbTrailer;
|
tls->spcss.cbTrailer;
|
||||||
tls->recvbuffer = xmpp_alloc(tls->ctx, tls->recvbuffermaxlen);
|
tls->recvbuffer = strophe_alloc(tls->ctx, tls->recvbuffermaxlen);
|
||||||
tls->recvbufferpos = 0;
|
tls->recvbufferpos = 0;
|
||||||
|
|
||||||
tls->sendbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage
|
tls->sendbuffermaxlen = tls->spcss.cbHeader + tls->spcss.cbMaximumMessage +
|
||||||
+ tls->spcss.cbTrailer;
|
tls->spcss.cbTrailer;
|
||||||
tls->sendbuffer = xmpp_alloc(tls->ctx, tls->sendbuffermaxlen);
|
tls->sendbuffer = strophe_alloc(tls->ctx, tls->sendbuffermaxlen);
|
||||||
tls->sendbufferpos = 0;
|
tls->sendbufferpos = 0;
|
||||||
tls->sendbufferlen = 0;
|
tls->sendbufferlen = 0;
|
||||||
|
|
||||||
tls->readybuffer = xmpp_alloc(tls->ctx, tls->spcss.cbMaximumMessage);
|
tls->readybuffer = strophe_alloc(tls->ctx, tls->spcss.cbMaximumMessage);
|
||||||
tls->readybufferpos = 0;
|
tls->readybufferpos = 0;
|
||||||
tls->readybufferlen = 0;
|
tls->readybufferlen = 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_stop(tls_t *tls)
|
int tls_stop(tls_t *tls)
|
||||||
{
|
{
|
||||||
|
UNUSED(tls);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_error(tls_t *tls)
|
int tls_error(struct conn_interface *intf)
|
||||||
{
|
{
|
||||||
return tls->lasterror;
|
return intf->conn->tls->lasterror;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_is_recoverable(int error)
|
int tls_is_recoverable(struct conn_interface *intf, int error)
|
||||||
{
|
{
|
||||||
return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE
|
UNUSED(intf);
|
||||||
|| error == WSAEWOULDBLOCK || error == WSAEMSGSIZE
|
return (error == SEC_E_OK || error == SEC_E_INCOMPLETE_MESSAGE ||
|
||||||
|| error == WSAEINPROGRESS);
|
error == WSAEWOULDBLOCK || error == WSAEMSGSIZE ||
|
||||||
|
error == WSAEINPROGRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_pending(tls_t *tls) {
|
int tls_pending(struct conn_interface *intf)
|
||||||
// There are 3 cases:
|
{
|
||||||
// - there is data in ready buffer, so it is by default pending
|
tls_t *tls = intf->conn->tls;
|
||||||
// - there is data in recv buffer. If it is not decrypted yet, means it
|
// There are 3 cases:
|
||||||
// was incomplete. This should be processed again only if there is data
|
// - there is data in ready buffer, so it is by default pending
|
||||||
// on the physical connection
|
// - there is data in recv buffer. If it is not decrypted yet, means it
|
||||||
// - there is data on the physical connection. This case is treated
|
// was incomplete. This should be processed again only if there is data
|
||||||
// outside the tls (in event.c)
|
// on the physical connection
|
||||||
|
// - there is data on the physical connection. This case is treated
|
||||||
|
// outside the tls (in event.c)
|
||||||
|
|
||||||
if (tls->readybufferpos < tls->readybufferlen) {
|
if (tls->readybufferpos < tls->readybufferlen) {
|
||||||
return tls->readybufferlen - tls->readybufferpos;
|
return tls->readybufferlen - tls->readybufferpos;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_read(tls_t *tls, void * const buff, const size_t len)
|
int tls_read(struct conn_interface *intf, void *buff, size_t len)
|
||||||
{
|
{
|
||||||
int bytes;
|
int bytes;
|
||||||
|
tls_t *tls = intf->conn->tls;
|
||||||
|
|
||||||
/* first, if we've got some ready data, put that in the buffer */
|
/* first, if we've got some ready data, put that in the buffer */
|
||||||
if (tls->readybufferpos < tls->readybufferlen)
|
if (tls->readybufferpos < tls->readybufferlen) {
|
||||||
{
|
if (len < tls->readybufferlen - tls->readybufferpos) {
|
||||||
if (len < tls->readybufferlen - tls->readybufferpos) {
|
bytes = len;
|
||||||
bytes = len;
|
} else {
|
||||||
} else {
|
bytes = tls->readybufferlen - tls->readybufferpos;
|
||||||
bytes = tls->readybufferlen - tls->readybufferpos;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(buff, tls->readybuffer + tls->readybufferpos, bytes);
|
memcpy(buff, tls->readybuffer + tls->readybufferpos, bytes);
|
||||||
|
|
||||||
if (len < tls->readybufferlen - tls->readybufferpos) {
|
if (len < tls->readybufferlen - tls->readybufferpos) {
|
||||||
tls->readybufferpos += bytes;
|
tls->readybufferpos += bytes;
|
||||||
return bytes;
|
return bytes;
|
||||||
} else {
|
} else {
|
||||||
unsigned char *newbuff = buff;
|
unsigned char *newbuff = buff;
|
||||||
int read;
|
int read;
|
||||||
tls->readybufferpos += bytes;
|
tls->readybufferpos += bytes;
|
||||||
newbuff += bytes;
|
newbuff += bytes;
|
||||||
read = tls_read(tls, newbuff, len - bytes);
|
read = tls_read(tls, newbuff, len - bytes);
|
||||||
|
|
||||||
if (read == -1) {
|
if (read == -1) {
|
||||||
if (tls_is_recoverable(tls->lasterror)) {
|
if (tls_is_recoverable(intf, tls->lasterror)) {
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes + read;
|
return bytes + read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* next, top up our recv buffer */
|
/* next, top up our recv buffer */
|
||||||
bytes = sock_read(tls->sock, tls->recvbuffer + tls->recvbufferpos,
|
bytes = sock_read(intf, tls->recvbuffer + tls->recvbufferpos,
|
||||||
tls->recvbuffermaxlen - tls->recvbufferpos);
|
tls->recvbuffermaxlen - tls->recvbufferpos);
|
||||||
|
|
||||||
if (bytes == 0) {
|
if (bytes == 0) {
|
||||||
tls->lasterror = WSAECONNRESET;
|
tls->lasterror = WSAECONNRESET;
|
||||||
@@ -461,76 +503,75 @@ int tls_read(tls_t *tls, void * const buff, const size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bytes == -1) {
|
if (bytes == -1) {
|
||||||
if (!tls_is_recoverable(sock_error())) {
|
if (!tls_is_recoverable(intf, sock_error(intf))) {
|
||||||
tls->lasterror = sock_error();
|
tls->lasterror = sock_error(intf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes > 0) {
|
if (bytes > 0) {
|
||||||
tls->recvbufferpos += bytes;
|
tls->recvbufferpos += bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* next, try to decrypt the recv buffer */
|
/* next, try to decrypt the recv buffer */
|
||||||
if (tls->recvbufferpos > 0) {
|
if (tls->recvbufferpos > 0) {
|
||||||
SecBufferDesc sbddec;
|
SecBufferDesc sbddec;
|
||||||
SecBuffer sbdec[4];
|
SecBuffer sbdec[4];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
memset(&sbddec, 0, sizeof(sbddec));
|
memset(&sbddec, 0, sizeof(sbddec));
|
||||||
sbddec.ulVersion = SECBUFFER_VERSION;
|
sbddec.ulVersion = SECBUFFER_VERSION;
|
||||||
sbddec.cBuffers = 4;
|
sbddec.cBuffers = 4;
|
||||||
sbddec.pBuffers = sbdec;
|
sbddec.pBuffers = sbdec;
|
||||||
|
|
||||||
memset(&(sbdec[0]), 0, sizeof(sbdec[0]));
|
memset(&(sbdec[0]), 0, sizeof(sbdec[0]));
|
||||||
sbdec[0].BufferType = SECBUFFER_DATA;
|
sbdec[0].BufferType = SECBUFFER_DATA;
|
||||||
sbdec[0].pvBuffer = tls->recvbuffer;
|
sbdec[0].pvBuffer = tls->recvbuffer;
|
||||||
sbdec[0].cbBuffer = tls->recvbufferpos;
|
sbdec[0].cbBuffer = tls->recvbufferpos;
|
||||||
|
|
||||||
memset(&(sbdec[1]), 0, sizeof(sbdec[1]));
|
memset(&(sbdec[1]), 0, sizeof(sbdec[1]));
|
||||||
sbdec[1].BufferType = SECBUFFER_EMPTY;
|
sbdec[1].BufferType = SECBUFFER_EMPTY;
|
||||||
|
|
||||||
memset(&(sbdec[2]), 0, sizeof(sbdec[2]));
|
memset(&(sbdec[2]), 0, sizeof(sbdec[2]));
|
||||||
sbdec[2].BufferType = SECBUFFER_EMPTY;
|
sbdec[2].BufferType = SECBUFFER_EMPTY;
|
||||||
|
|
||||||
memset(&(sbdec[3]), 0, sizeof(sbdec[3]));
|
memset(&(sbdec[3]), 0, sizeof(sbdec[3]));
|
||||||
sbdec[3].BufferType = SECBUFFER_EMPTY;
|
sbdec[3].BufferType = SECBUFFER_EMPTY;
|
||||||
|
|
||||||
ret = tls->sft->DecryptMessage(&(tls->hctxt), &sbddec, 0, NULL);
|
ret = tls->sft->DecryptMessage(&(tls->hctxt), &sbddec, 0, NULL);
|
||||||
|
|
||||||
if (ret == SEC_E_OK) {
|
if (ret == SEC_E_OK) {
|
||||||
memcpy(tls->readybuffer, sbdec[1].pvBuffer, sbdec[1].cbBuffer);
|
memcpy(tls->readybuffer, sbdec[1].pvBuffer, sbdec[1].cbBuffer);
|
||||||
tls->readybufferpos = 0;
|
tls->readybufferpos = 0;
|
||||||
tls->readybufferlen = sbdec[1].cbBuffer;
|
tls->readybufferlen = sbdec[1].cbBuffer;
|
||||||
/* have we got some data left over? If so, copy it to the start
|
/* have we got some data left over? If so, copy it to the start
|
||||||
* of the recv buffer */
|
* of the recv buffer */
|
||||||
if (sbdec[3].BufferType == SECBUFFER_EXTRA) {
|
if (sbdec[3].BufferType == SECBUFFER_EXTRA) {
|
||||||
memcpy(tls->recvbuffer, sbdec[3].pvBuffer, sbdec[3].cbBuffer);
|
memcpy(tls->recvbuffer, sbdec[3].pvBuffer, sbdec[3].cbBuffer);
|
||||||
tls->recvbufferpos = sbdec[3].cbBuffer;
|
tls->recvbufferpos = sbdec[3].cbBuffer;
|
||||||
} else {
|
} else {
|
||||||
tls->recvbufferpos = 0;
|
tls->recvbufferpos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tls_read(tls, buff, len);
|
return tls_read(tls, buff, len);
|
||||||
} else if (ret == SEC_E_INCOMPLETE_MESSAGE) {
|
} else if (ret == SEC_E_INCOMPLETE_MESSAGE) {
|
||||||
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
|
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
|
||||||
return -1;
|
return -1;
|
||||||
} else if (ret == SEC_I_RENEGOTIATE) {
|
} else if (ret == SEC_I_RENEGOTIATE) {
|
||||||
ret = tls_start(tls);
|
ret = tls_start(tls);
|
||||||
if (!ret)
|
if (!ret) {
|
||||||
{
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* fake an incomplete message so we're called again */
|
/* fake an incomplete message so we're called again */
|
||||||
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
|
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* something bad happened, so we bail */
|
/* something bad happened, so we bail */
|
||||||
tls->lasterror = ret;
|
tls->lasterror = ret;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
|
tls->lasterror = SEC_E_INCOMPLETE_MESSAGE;
|
||||||
@@ -538,41 +579,41 @@ int tls_read(tls_t *tls, void * const buff, const size_t len)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_clear_pending_write(tls_t *tls)
|
int tls_clear_pending_write(struct conn_interface *intf)
|
||||||
{
|
{
|
||||||
if (tls->sendbufferpos < tls->sendbufferlen)
|
tls_t *tls = intf->conn->tls;
|
||||||
{
|
if (tls->sendbufferpos < tls->sendbufferlen) {
|
||||||
int bytes;
|
int bytes;
|
||||||
|
|
||||||
bytes = sock_write(tls->sock, tls->sendbuffer + tls->sendbufferpos,
|
bytes = sock_write(intf, tls->sendbuffer + tls->sendbufferpos,
|
||||||
tls->sendbufferlen - tls->sendbufferpos);
|
tls->sendbufferlen - tls->sendbufferpos);
|
||||||
|
|
||||||
if (bytes == -1) {
|
if (bytes == -1) {
|
||||||
tls->lasterror = sock_error();
|
tls->lasterror = sock_error(intf);
|
||||||
return -1;
|
return -1;
|
||||||
} else if (bytes > 0) {
|
} else if (bytes > 0) {
|
||||||
tls->sendbufferpos += bytes;
|
tls->sendbufferpos += bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tls->sendbufferpos < tls->sendbufferlen) {
|
if (tls->sendbufferpos < tls->sendbufferlen) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_write(tls_t *tls, const void * const buff, const size_t len)
|
int tls_write(struct conn_interface *intf, const void *buff, size_t len)
|
||||||
{
|
{
|
||||||
SecBufferDesc sbdenc;
|
SecBufferDesc sbdenc;
|
||||||
SecBuffer sbenc[4];
|
SecBuffer sbenc[4];
|
||||||
unsigned char *sendbuffer;
|
|
||||||
const unsigned char *p = buff;
|
const unsigned char *p = buff;
|
||||||
int sent = 0, ret, remain = len;
|
int sent = 0, ret, remain = len;
|
||||||
|
tls_t *tls = intf->conn->tls;
|
||||||
|
|
||||||
ret = tls_clear_pending_write(tls);
|
ret = tls_clear_pending_write(tls);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls->sendbufferpos = 0;
|
tls->sendbufferpos = 0;
|
||||||
@@ -600,51 +641,50 @@ int tls_write(tls_t *tls, const void * const buff, const size_t len)
|
|||||||
|
|
||||||
sbenc[1].pvBuffer = tls->sendbuffer + tls->spcss.cbHeader;
|
sbenc[1].pvBuffer = tls->sendbuffer + tls->spcss.cbHeader;
|
||||||
|
|
||||||
while (remain > 0)
|
while (remain > 0) {
|
||||||
{
|
if (remain > tls->spcss.cbMaximumMessage) {
|
||||||
if (remain > tls->spcss.cbMaximumMessage) {
|
sbenc[1].cbBuffer = tls->spcss.cbMaximumMessage;
|
||||||
sbenc[1].cbBuffer = tls->spcss.cbMaximumMessage;
|
} else {
|
||||||
} else {
|
sbenc[1].cbBuffer = remain;
|
||||||
sbenc[1].cbBuffer = remain;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sbenc[2].pvBuffer = (unsigned char *)sbenc[1].pvBuffer
|
sbenc[2].pvBuffer =
|
||||||
+ sbenc[1].cbBuffer;
|
(unsigned char *)sbenc[1].pvBuffer + sbenc[1].cbBuffer;
|
||||||
sbenc[2].cbBuffer = tls->spcss.cbTrailer;
|
sbenc[2].cbBuffer = tls->spcss.cbTrailer;
|
||||||
|
|
||||||
memcpy(sbenc[1].pvBuffer, p, sbenc[1].cbBuffer);
|
memcpy(sbenc[1].pvBuffer, p, sbenc[1].cbBuffer);
|
||||||
p += tls->spcss.cbMaximumMessage;
|
p += tls->spcss.cbMaximumMessage;
|
||||||
|
|
||||||
tls->sendbufferlen = sbenc[0].cbBuffer + sbenc[1].cbBuffer
|
tls->sendbufferlen =
|
||||||
+ sbenc[2].cbBuffer;
|
sbenc[0].cbBuffer + sbenc[1].cbBuffer + sbenc[2].cbBuffer;
|
||||||
|
|
||||||
ret = tls->sft->EncryptMessage(&(tls->hctxt), 0, &sbdenc, 0);
|
ret = tls->sft->EncryptMessage(&(tls->hctxt), 0, &sbdenc, 0);
|
||||||
|
|
||||||
if (ret != SEC_E_OK) {
|
if (ret != SEC_E_OK) {
|
||||||
tls->lasterror = ret;
|
tls->lasterror = ret;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tls->sendbufferpos = 0;
|
tls->sendbufferpos = 0;
|
||||||
|
|
||||||
ret = tls_clear_pending_write(tls);
|
ret = tls_clear_pending_write(tls);
|
||||||
|
|
||||||
if (ret == -1 && !tls_is_recoverable(tls_error(tls))) {
|
if (ret == -1 && !tls_is_recoverable(intf, tls_error(tls))) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remain > tls->spcss.cbMaximumMessage) {
|
if (remain > tls->spcss.cbMaximumMessage) {
|
||||||
sent += tls->spcss.cbMaximumMessage;
|
sent += tls->spcss.cbMaximumMessage;
|
||||||
remain -= tls->spcss.cbMaximumMessage;
|
remain -= tls->spcss.cbMaximumMessage;
|
||||||
} else {
|
} else {
|
||||||
sent += remain;
|
sent += remain;
|
||||||
remain = 0;
|
remain = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0 || (ret == -1 && tls_is_recoverable(tls_error(tls)))) {
|
|
||||||
return sent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (ret == 0 ||
|
||||||
|
(ret == -1 && tls_is_recoverable(intf, tls_error(tls)))) {
|
||||||
|
return sent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sent;
|
return sent;
|
||||||
|
|||||||
107
src/util.c
107
src/util.c
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* util.c
|
/* util.c
|
||||||
** strophe XMPP client library -- various utility functions
|
** strophe XMPP client library -- various utility functions
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -18,17 +16,18 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#else
|
#else
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "strophe.h"
|
#include "strophe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "ostypes.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
/** implement our own strdup that uses the ctx allocator */
|
/** implement our own strdup that uses the ctx allocator */
|
||||||
@@ -39,25 +38,67 @@
|
|||||||
* @param ctx a Strophe context object
|
* @param ctx a Strophe context object
|
||||||
* @param s a string
|
* @param s a string
|
||||||
*
|
*
|
||||||
* @return a new allocates string with the same data as s or NULL on error
|
* @return a newly allocated string with the same data as s or NULL on error
|
||||||
*/
|
*/
|
||||||
char *xmpp_strdup(const xmpp_ctx_t * const ctx, const char * const s)
|
char *strophe_strdup(const xmpp_ctx_t *ctx, const char *s)
|
||||||
{
|
{
|
||||||
size_t len;
|
return strophe_strndup(ctx, s, SIZE_MAX);
|
||||||
char *copy;
|
}
|
||||||
|
|
||||||
len = strlen(s);
|
/** Duplicate a string with a maximum length.
|
||||||
copy = xmpp_alloc(ctx, len + 1);
|
* This function replaces the standard strndup library call with a version
|
||||||
|
* that uses the Strophe context object's allocator.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param s a string
|
||||||
|
* @param len the maximum length of the string to copy
|
||||||
|
*
|
||||||
|
* @return a newly allocated string that contains at most `len` symbols
|
||||||
|
* of the original string or NULL on error
|
||||||
|
*/
|
||||||
|
char *strophe_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len)
|
||||||
|
{
|
||||||
|
char *copy;
|
||||||
|
size_t l;
|
||||||
|
|
||||||
|
l = strlen(s);
|
||||||
|
if (l > len)
|
||||||
|
l = len;
|
||||||
|
|
||||||
|
copy = strophe_alloc(ctx, l + 1);
|
||||||
if (!copy) {
|
if (!copy) {
|
||||||
xmpp_error(ctx, "xmpp", "failed to allocate required memory");
|
strophe_error(ctx, "xmpp", "failed to allocate required memory");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(copy, s, len + 1);
|
memcpy(copy, s, l);
|
||||||
|
copy[l] = '\0';
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** strtok_r(3) implementation.
|
||||||
|
* This function has appeared in POSIX.1-2001, but not in C standard.
|
||||||
|
* For example, visual studio older than 2005 doesn't provide strtok_r()
|
||||||
|
* nor strtok_s().
|
||||||
|
*/
|
||||||
|
char *strophe_strtok_r(char *s, const char *delim, char **saveptr)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
s = s ? s : *saveptr;
|
||||||
|
len = strspn(s, delim);
|
||||||
|
s += len;
|
||||||
|
if (*s == '\0')
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
len = strcspn(s, delim);
|
||||||
|
*saveptr = s[len] == '\0' ? &s[len] : &s[len + 1];
|
||||||
|
s[len] = '\0';
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
/** Return an integer based time stamp.
|
/** Return an integer based time stamp.
|
||||||
* This function uses gettimeofday or timeGetTime (on Win32 platforms) to
|
* This function uses gettimeofday or timeGetTime (on Win32 platforms) to
|
||||||
* compute an integer based time stamp. This is used internally by the
|
* compute an integer based time stamp. This is used internally by the
|
||||||
@@ -67,8 +108,24 @@ char *xmpp_strdup(const xmpp_ctx_t * const ctx, const char * const s)
|
|||||||
*/
|
*/
|
||||||
uint64_t time_stamp(void)
|
uint64_t time_stamp(void)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#if defined(_WIN32) || defined(_XBOX_ONE)
|
||||||
return timeGetTime();
|
|
||||||
|
#ifndef __GNUC__
|
||||||
|
#define EPOCHFILETIME (116444736000000000i64)
|
||||||
|
#else
|
||||||
|
#define EPOCHFILETIME (116444736000000000LL)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FILETIME ft;
|
||||||
|
LARGE_INTEGER li;
|
||||||
|
__int64 t;
|
||||||
|
|
||||||
|
GetSystemTimeAsFileTime(&ft);
|
||||||
|
li.LowPart = ft.dwLowDateTime;
|
||||||
|
li.HighPart = ft.dwHighDateTime;
|
||||||
|
t = li.QuadPart; /* In 100-nanosecond intervals */
|
||||||
|
t -= EPOCHFILETIME; /* Offset to the Epoch time */
|
||||||
|
return (uint64_t)(t / 10000); /* Convert to milliseconds */
|
||||||
#else
|
#else
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
@@ -100,8 +157,24 @@ uint64_t time_elapsed(uint64_t t1, uint64_t t2)
|
|||||||
*
|
*
|
||||||
* @param conn a Strophe connection object
|
* @param conn a Strophe connection object
|
||||||
*/
|
*/
|
||||||
void disconnect_mem_error(xmpp_conn_t * const conn)
|
void disconnect_mem_error(xmpp_conn_t *conn)
|
||||||
{
|
{
|
||||||
xmpp_error(conn->ctx, "xmpp", "Memory allocation error");
|
strophe_error(conn->ctx, "xmpp", "Memory allocation error");
|
||||||
xmpp_disconnect(conn);
|
xmpp_disconnect(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int string_to_ul(const char *s, unsigned long *ul)
|
||||||
|
{
|
||||||
|
char *endptr;
|
||||||
|
*ul = strtoul(s, &endptr, 10);
|
||||||
|
return *endptr != '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void hex_encode(char *writebuf, void *readbuf, size_t len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
sprintf(writebuf, "%02x", ((unsigned char *)readbuf)[i]);
|
||||||
|
writebuf += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
24
src/util.h
24
src/util.h
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* util.h
|
/* util.h
|
||||||
** strophe XMPP client library -- various utility functions
|
** strophe XMPP client library -- various utility functions
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -19,14 +17,24 @@
|
|||||||
#ifndef __LIBSTROPHE_UTIL_H__
|
#ifndef __LIBSTROPHE_UTIL_H__
|
||||||
#define __LIBSTROPHE_UTIL_H__
|
#define __LIBSTROPHE_UTIL_H__
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include <stdint.h>
|
|
||||||
#else
|
|
||||||
#include "ostypes.h"
|
#include "ostypes.h"
|
||||||
#endif
|
|
||||||
|
#ifndef ARRAY_SIZE
|
||||||
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
#endif /* ARRAY_SIZE */
|
||||||
|
|
||||||
|
/* TODO evaluate x and y only once */
|
||||||
|
#define xmpp_min(x, y) ((x) < (y) ? (x) : (y))
|
||||||
|
|
||||||
|
/* string functions */
|
||||||
|
char *strophe_strtok_r(char *s, const char *delim, char **saveptr);
|
||||||
|
|
||||||
/* timing functions */
|
/* timing functions */
|
||||||
uint64_t time_stamp(void);
|
uint64_t time_stamp(void);
|
||||||
uint64_t time_elapsed(uint64_t t1, uint64_t t2);
|
uint64_t time_elapsed(uint64_t t1, uint64_t t2);
|
||||||
|
|
||||||
|
/* misc functions */
|
||||||
|
int string_to_ul(const char *s, unsigned long *ul);
|
||||||
|
void hex_encode(char *writebuf, void *readbuf, size_t len);
|
||||||
|
|
||||||
#endif /* __LIBSTROPHE_UTIL_H__ */
|
#endif /* __LIBSTROPHE_UTIL_H__ */
|
||||||
|
|||||||
73
src/uuid.c
Normal file
73
src/uuid.c
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* uuid.c
|
||||||
|
* strophe XMPP client library -- UUID generation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* Generation of UUID version 4 according to RFC4122.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "strophe.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
/** @def XMPP_UUID_LEN
|
||||||
|
* UUID length in string representation excluding '\0'.
|
||||||
|
*/
|
||||||
|
#define XMPP_UUID_LEN 36
|
||||||
|
|
||||||
|
/** Generate UUID version 4 in pre-allocated buffer.
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
* @param uuid pre-allocated buffer of size (XMPP_UUID_LEN + 1)
|
||||||
|
*/
|
||||||
|
static void crypto_uuid_gen(xmpp_ctx_t *ctx, char *uuid)
|
||||||
|
{
|
||||||
|
unsigned char buf[16];
|
||||||
|
int i = 0; /* uuid iterator */
|
||||||
|
int j = 0; /* buf iterator */
|
||||||
|
|
||||||
|
static const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
|
||||||
|
xmpp_rand_bytes(ctx->rand, buf, sizeof(buf));
|
||||||
|
buf[8] &= 0x3f;
|
||||||
|
buf[8] |= 0x80;
|
||||||
|
buf[6] &= 0x0f;
|
||||||
|
buf[6] |= 0x40;
|
||||||
|
while (i < XMPP_UUID_LEN) {
|
||||||
|
if (i == 8 || i == 13 || i == 18 || i == 23)
|
||||||
|
uuid[i++] = '-';
|
||||||
|
else {
|
||||||
|
uuid[i++] = hex[buf[j] >> 4];
|
||||||
|
uuid[i++] = hex[buf[j] & 0x0f];
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uuid[XMPP_UUID_LEN] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate UUID version 4.
|
||||||
|
* This function allocates memory for the resulting string and must be freed
|
||||||
|
* with xmpp_free().
|
||||||
|
*
|
||||||
|
* @param ctx a Strophe context object
|
||||||
|
*
|
||||||
|
* @return ASCIIZ string
|
||||||
|
*/
|
||||||
|
char *xmpp_uuid_gen(xmpp_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
char *uuid;
|
||||||
|
|
||||||
|
uuid = strophe_alloc(ctx, XMPP_UUID_LEN + 1);
|
||||||
|
if (uuid != NULL) {
|
||||||
|
crypto_uuid_gen(ctx, uuid);
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
751
strophe.h
751
strophe.h
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* strophe.h
|
/* strophe.h
|
||||||
** strophe XMPP client library C API
|
** strophe XMPP client library C API
|
||||||
**
|
**
|
||||||
@@ -6,10 +7,7 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express or
|
** This software is provided AS-IS with no warranty, either express or
|
||||||
** implied.
|
** implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This software is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @file
|
/** @file
|
||||||
@@ -19,12 +17,12 @@
|
|||||||
#ifndef __LIBSTROPHE_STROPHE_H__
|
#ifndef __LIBSTROPHE_STROPHE_H__
|
||||||
#define __LIBSTROPHE_STROPHE_H__
|
#define __LIBSTROPHE_STROPHE_H__
|
||||||
|
|
||||||
|
#include <stddef.h> /* size_t */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/* namespace defines */
|
/* namespace defines */
|
||||||
/** @def XMPP_NS_CLIENT
|
/** @def XMPP_NS_CLIENT
|
||||||
* Namespace definition for 'jabber:client'.
|
* Namespace definition for 'jabber:client'.
|
||||||
@@ -42,6 +40,10 @@ extern "C" {
|
|||||||
* Namespace definition for 'urn:ietf:params:xml:ns:xmpp-streams'.
|
* Namespace definition for 'urn:ietf:params:xml:ns:xmpp-streams'.
|
||||||
*/
|
*/
|
||||||
#define XMPP_NS_STREAMS_IETF "urn:ietf:params:xml:ns:xmpp-streams"
|
#define XMPP_NS_STREAMS_IETF "urn:ietf:params:xml:ns:xmpp-streams"
|
||||||
|
/** @def XMPP_NS_STANZAS_IETF
|
||||||
|
* Namespace definition for 'urn:ietf:params:xml:ns:xmpp-stanzas'.
|
||||||
|
*/
|
||||||
|
#define XMPP_NS_STANZAS_IETF "urn:ietf:params:xml:ns:xmpp-stanzas"
|
||||||
/** @def XMPP_NS_TLS
|
/** @def XMPP_NS_TLS
|
||||||
* Namespace definition for 'url:ietf:params:xml:ns:xmpp-tls'.
|
* Namespace definition for 'url:ietf:params:xml:ns:xmpp-tls'.
|
||||||
*/
|
*/
|
||||||
@@ -74,6 +76,22 @@ extern "C" {
|
|||||||
* Namespace definition for 'jabber:iq:roster'.
|
* Namespace definition for 'jabber:iq:roster'.
|
||||||
*/
|
*/
|
||||||
#define XMPP_NS_ROSTER "jabber:iq:roster"
|
#define XMPP_NS_ROSTER "jabber:iq:roster"
|
||||||
|
/** @def XMPP_NS_REGISTER
|
||||||
|
* Namespace definition for 'jabber:iq:register'.
|
||||||
|
*/
|
||||||
|
#define XMPP_NS_REGISTER "jabber:iq:register"
|
||||||
|
/** @def XMPP_NS_SM
|
||||||
|
* Namespace definition for Stream Management.
|
||||||
|
*/
|
||||||
|
#define XMPP_NS_SM "urn:xmpp:sm:3"
|
||||||
|
/** @def XMPP_NS_COMPRESSION
|
||||||
|
* Namespace definition for Stream Compression.
|
||||||
|
*/
|
||||||
|
#define XMPP_NS_COMPRESSION "http://jabber.org/protocol/compress"
|
||||||
|
/** @def XMPP_NS_FEATURE_COMPRESSION
|
||||||
|
* Namespace definition for Stream Compression.
|
||||||
|
*/
|
||||||
|
#define XMPP_NS_FEATURE_COMPRESSION "http://jabber.org/features/compress"
|
||||||
|
|
||||||
/* error defines */
|
/* error defines */
|
||||||
/** @def XMPP_EOK
|
/** @def XMPP_EOK
|
||||||
@@ -117,14 +135,22 @@ typedef struct _xmpp_log_t xmpp_log_t;
|
|||||||
/* opaque run time context containing the above hooks */
|
/* opaque run time context containing the above hooks */
|
||||||
typedef struct _xmpp_ctx_t xmpp_ctx_t;
|
typedef struct _xmpp_ctx_t xmpp_ctx_t;
|
||||||
|
|
||||||
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t * const mem,
|
typedef struct _xmpp_tlscert_t xmpp_tlscert_t;
|
||||||
const xmpp_log_t * const log);
|
|
||||||
void xmpp_ctx_free(xmpp_ctx_t * const ctx);
|
xmpp_ctx_t *xmpp_ctx_new(const xmpp_mem_t *mem, const xmpp_log_t *log);
|
||||||
|
void xmpp_ctx_free(xmpp_ctx_t *ctx);
|
||||||
|
|
||||||
|
/* set the verbosity level of the ctx */
|
||||||
|
void xmpp_ctx_set_verbosity(xmpp_ctx_t *ctx, int level);
|
||||||
|
|
||||||
|
/* free some blocks returned by other APIs, for example the
|
||||||
|
buffer you get from xmpp_stanza_to_text */
|
||||||
|
void xmpp_free(const xmpp_ctx_t *ctx, void *p);
|
||||||
|
|
||||||
struct _xmpp_mem_t {
|
struct _xmpp_mem_t {
|
||||||
void *(*alloc)(const size_t size, void * const userdata);
|
void *(*alloc)(size_t size, void *userdata);
|
||||||
void (*free)(void *p, void * const userdata);
|
void (*free)(void *p, void *userdata);
|
||||||
void *(*realloc)(void *p, const size_t size, void * const userdata);
|
void *(*realloc)(void *p, size_t size, void *userdata);
|
||||||
void *userdata;
|
void *userdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -135,21 +161,16 @@ typedef enum {
|
|||||||
XMPP_LEVEL_ERROR
|
XMPP_LEVEL_ERROR
|
||||||
} xmpp_log_level_t;
|
} xmpp_log_level_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum { XMPP_UNKNOWN, XMPP_CLIENT, XMPP_COMPONENT } xmpp_conn_type_t;
|
||||||
XMPP_UNKNOWN,
|
|
||||||
XMPP_CLIENT,
|
|
||||||
XMPP_COMPONENT
|
|
||||||
} xmpp_conn_type_t;
|
|
||||||
|
|
||||||
typedef void (*xmpp_log_handler)(void * const userdata,
|
typedef void (*xmpp_log_handler)(void *userdata,
|
||||||
const xmpp_log_level_t level,
|
xmpp_log_level_t level,
|
||||||
const char * const area,
|
const char *area,
|
||||||
const char * const msg);
|
const char *msg);
|
||||||
|
|
||||||
struct _xmpp_log_t {
|
struct _xmpp_log_t {
|
||||||
xmpp_log_handler handler;
|
xmpp_log_handler handler;
|
||||||
void *userdata;
|
void *userdata;
|
||||||
/* mutex_t lock; */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* return a default logger filtering at a given level */
|
/* return a default logger filtering at a given level */
|
||||||
@@ -160,10 +181,38 @@ xmpp_log_t *xmpp_get_default_logger(xmpp_log_level_t level);
|
|||||||
/* opaque connection object */
|
/* opaque connection object */
|
||||||
typedef struct _xmpp_conn_t xmpp_conn_t;
|
typedef struct _xmpp_conn_t xmpp_conn_t;
|
||||||
typedef struct _xmpp_stanza_t xmpp_stanza_t;
|
typedef struct _xmpp_stanza_t xmpp_stanza_t;
|
||||||
|
typedef struct _xmpp_sm_t xmpp_sm_state_t;
|
||||||
|
|
||||||
|
/* connection flags */
|
||||||
|
#define XMPP_CONN_FLAG_DISABLE_TLS (1UL << 0)
|
||||||
|
#define XMPP_CONN_FLAG_MANDATORY_TLS (1UL << 1)
|
||||||
|
#define XMPP_CONN_FLAG_LEGACY_SSL (1UL << 2)
|
||||||
|
/** @def XMPP_CONN_FLAG_TRUST_TLS
|
||||||
|
* Trust server's certificate even if it is invalid.
|
||||||
|
*/
|
||||||
|
#define XMPP_CONN_FLAG_TRUST_TLS (1UL << 3)
|
||||||
|
/** @def XMPP_CONN_FLAG_LEGACY_AUTH
|
||||||
|
* Enable legacy authentication support.
|
||||||
|
*/
|
||||||
|
#define XMPP_CONN_FLAG_LEGACY_AUTH (1UL << 4)
|
||||||
|
/** @def XMPP_CONN_FLAG_DISABLE_SM
|
||||||
|
* Disable Stream-Management XEP-0198.
|
||||||
|
*/
|
||||||
|
#define XMPP_CONN_FLAG_DISABLE_SM (1UL << 5)
|
||||||
|
/** @def XMPP_CONN_FLAG_ENABLE_COMPRESSION
|
||||||
|
* Enable Stream-Compression XEP-0138.
|
||||||
|
*/
|
||||||
|
#define XMPP_CONN_FLAG_ENABLE_COMPRESSION (1UL << 6)
|
||||||
|
/** @def XMPP_CONN_FLAG_COMPRESSION_DONT_RESET
|
||||||
|
* Don't reset the compressed stream after each stanza.
|
||||||
|
* Only enable this flag if you know what you're doing.
|
||||||
|
*/
|
||||||
|
#define XMPP_CONN_FLAG_COMPRESSION_DONT_RESET (1UL << 7)
|
||||||
|
|
||||||
/* connect callback */
|
/* connect callback */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
XMPP_CONN_CONNECT,
|
XMPP_CONN_CONNECT,
|
||||||
|
XMPP_CONN_RAW_CONNECT,
|
||||||
XMPP_CONN_DISCONNECT,
|
XMPP_CONN_DISCONNECT,
|
||||||
XMPP_CONN_FAIL
|
XMPP_CONN_FAIL
|
||||||
} xmpp_conn_event_t;
|
} xmpp_conn_event_t;
|
||||||
@@ -195,177 +244,591 @@ typedef enum {
|
|||||||
XMPP_SE_XML_NOT_WELL_FORMED
|
XMPP_SE_XML_NOT_WELL_FORMED
|
||||||
} xmpp_error_type_t;
|
} xmpp_error_type_t;
|
||||||
|
|
||||||
|
/** Certificate Elements
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
XMPP_CERT_VERSION, /**< X.509 Version */
|
||||||
|
XMPP_CERT_SERIALNUMBER, /**< SerialNumber */
|
||||||
|
XMPP_CERT_SUBJECT, /**< Subject */
|
||||||
|
XMPP_CERT_ISSUER, /**< Issuer */
|
||||||
|
XMPP_CERT_NOTBEFORE, /**< Issued on */
|
||||||
|
XMPP_CERT_NOTAFTER, /**< Expires on */
|
||||||
|
XMPP_CERT_KEYALG, /**< Public Key Algorithm */
|
||||||
|
XMPP_CERT_SIGALG, /**< Certificate Signature Algorithm */
|
||||||
|
XMPP_CERT_FINGERPRINT_SHA1, /**< Fingerprint SHA-1 */
|
||||||
|
XMPP_CERT_FINGERPRINT_SHA256, /**< Fingerprint SHA-256 */
|
||||||
|
XMPP_CERT_ELEMENT_MAX /**< Last element of the enum */
|
||||||
|
} xmpp_cert_element_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
xmpp_error_type_t type;
|
xmpp_error_type_t type;
|
||||||
char *text;
|
char *text;
|
||||||
xmpp_stanza_t *stanza;
|
xmpp_stanza_t *stanza;
|
||||||
} xmpp_stream_error_t;
|
} xmpp_stream_error_t;
|
||||||
|
|
||||||
typedef void (*xmpp_conn_handler)(xmpp_conn_t * const conn,
|
typedef void (*xmpp_conn_handler)(xmpp_conn_t *conn,
|
||||||
const xmpp_conn_event_t event,
|
xmpp_conn_event_t event,
|
||||||
const int error,
|
int error,
|
||||||
xmpp_stream_error_t * const stream_error,
|
xmpp_stream_error_t *stream_error,
|
||||||
void * const userdata);
|
void *userdata);
|
||||||
|
|
||||||
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t * const ctx);
|
/** The Handler function which will be called when the TLS stack can't
|
||||||
xmpp_conn_t * xmpp_conn_clone(xmpp_conn_t * const conn);
|
* verify the authenticity of a Certificate that gets presented by
|
||||||
int xmpp_conn_release(xmpp_conn_t * const conn);
|
* the server we're trying to connect to.
|
||||||
|
*
|
||||||
|
* When this function is called and details of the `cert` have to be
|
||||||
|
* kept, please copy them yourself. The `cert` object will be free'd
|
||||||
|
* automatically when this function returns.
|
||||||
|
*
|
||||||
|
* NB: `errormsg` is specific per certificate on OpenSSL and the same
|
||||||
|
* for all certificates on GnuTLS.
|
||||||
|
*
|
||||||
|
* @param cert a Strophe certificate object
|
||||||
|
* @param errormsg The error that caused this.
|
||||||
|
*
|
||||||
|
* @return 0 if the connection attempt should be terminated,
|
||||||
|
* 1 if the connection should be established.
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
typedef int (*xmpp_certfail_handler)(const xmpp_tlscert_t *cert,
|
||||||
|
const char *const errormsg);
|
||||||
|
|
||||||
const char *xmpp_conn_get_jid(const xmpp_conn_t * const conn);
|
/** The Handler function which will be called when the TLS stack can't
|
||||||
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t * const conn);
|
* decrypt a password protected key file.
|
||||||
void xmpp_conn_set_jid(xmpp_conn_t * const conn, const char * const jid);
|
*
|
||||||
const char *xmpp_conn_get_pass(const xmpp_conn_t * const conn);
|
* When this callback is called it shall write a NULL-terminated
|
||||||
void xmpp_conn_set_pass(xmpp_conn_t * const conn, const char * const pass);
|
* string of maximum length `pw_max - 1` to `pw`.
|
||||||
xmpp_ctx_t* xmpp_conn_get_context(xmpp_conn_t * const conn);
|
*
|
||||||
void xmpp_conn_disable_tls(xmpp_conn_t * const conn);
|
* This is currently only supported for GnuTLS and OpenSSL.
|
||||||
|
*
|
||||||
|
* On 2022-02-02 the following maximum lengths are valid:
|
||||||
|
* ```
|
||||||
|
* include/gnutls/pkcs11.h: #define GNUTLS_PKCS11_MAX_PIN_LEN 256
|
||||||
|
* include/openssl/pem.h: #define PEM_BUFSIZE 1024
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* We expect the buffer to be NULL-terminated, therefore the usable lengths
|
||||||
|
* are:
|
||||||
|
*
|
||||||
|
* * 255 for GnuTLS
|
||||||
|
* * 1023 for OpenSSL
|
||||||
|
*
|
||||||
|
* Useful API's inside this callback are e.g.
|
||||||
|
*
|
||||||
|
* \ref xmpp_conn_get_keyfile
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param pw The buffer where the password shall be stored.
|
||||||
|
* @param pw_max The maximum length of the password.
|
||||||
|
* @param conn The Strophe connection object this callback originates from.
|
||||||
|
* @param userdata The userdata pointer as supplied when setting this callback.
|
||||||
|
*
|
||||||
|
* @return -1 on error, else the number of bytes written to `pw` w/o
|
||||||
|
* terminating NUL byte
|
||||||
|
*
|
||||||
|
* @ingroup TLS
|
||||||
|
*/
|
||||||
|
typedef int (*xmpp_password_callback)(char *pw,
|
||||||
|
size_t pw_max,
|
||||||
|
xmpp_conn_t *conn,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
int xmpp_connect_client(xmpp_conn_t * const conn,
|
/** The function which will be called when Strophe creates a new socket.
|
||||||
const char * const altdomain,
|
*
|
||||||
unsigned short altport,
|
* The `sock` argument is a pointer that is dependent on the architecture
|
||||||
xmpp_conn_handler callback,
|
* Strophe is compiled for.
|
||||||
void * const userdata);
|
*
|
||||||
|
* For POSIX compatible systems usage shall be:
|
||||||
|
* ```
|
||||||
|
* int soc = *((int*)sock);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* On Windows usage shall be:
|
||||||
|
* ```
|
||||||
|
* SOCKET soc = *((SOCKET*)sock);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* This function will be called for each socket that is created.
|
||||||
|
*
|
||||||
|
* `examples/bot.c` uses a libstrophe supplied callback function that sets
|
||||||
|
* basic keepalive parameters (`xmpp_sockopt_cb_keepalive()`).
|
||||||
|
*
|
||||||
|
* `examples/complex.c` implements a custom function that could be useful
|
||||||
|
* for an application.
|
||||||
|
*
|
||||||
|
* @param conn The Strophe connection object this callback originates from.
|
||||||
|
* @param sock A pointer to the underlying file descriptor.
|
||||||
|
*
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*
|
||||||
|
* @ingroup Connections
|
||||||
|
*/
|
||||||
|
typedef int (*xmpp_sockopt_callback)(xmpp_conn_t *conn, void *sock);
|
||||||
|
|
||||||
/*
|
/* an example callback that sets basic keepalive parameters */
|
||||||
int xmpp_connect_component(conn, name)
|
int xmpp_sockopt_cb_keepalive(xmpp_conn_t *conn, void *sock);
|
||||||
*/
|
|
||||||
void xmpp_disconnect(xmpp_conn_t * const conn);
|
|
||||||
|
|
||||||
void xmpp_send(xmpp_conn_t * const conn,
|
xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx);
|
||||||
xmpp_stanza_t * const stanza);
|
xmpp_conn_t *xmpp_conn_clone(xmpp_conn_t *conn);
|
||||||
|
int xmpp_conn_release(xmpp_conn_t *conn);
|
||||||
|
|
||||||
void xmpp_send_raw_string(xmpp_conn_t * const conn,
|
/** Connection settings
|
||||||
const char * const fmt, ...);
|
*/
|
||||||
void xmpp_send_raw(xmpp_conn_t * const conn,
|
typedef enum xmpp_conn_setting_t {
|
||||||
const char * const data, const size_t len);
|
/* String values */
|
||||||
|
XMPP_SETTING_JID = 0x0, /**< JID */
|
||||||
|
XMPP_SETTING_PASS, /**< Password */
|
||||||
|
XMPP_SETTING_CAFILE, /**< CAfile */
|
||||||
|
XMPP_SETTING_CAPATH, /**< CApath */
|
||||||
|
XMPP_SETTING_CLIENT_CERT, /**< Client Certificate */
|
||||||
|
XMPP_SETTING_CLIENT_KEY, /**< Key of Client Certificate */
|
||||||
|
|
||||||
|
/* Int values */
|
||||||
|
XMPP_SETTING_PASSWORD_RETRIES = 0x40, /**< Number of retry attempts to
|
||||||
|
decrypt a private key file. */
|
||||||
|
XMPP_SETTING_COMPRESSION_LEVEL, /**< Compression level. */
|
||||||
|
|
||||||
|
/* Pointer values */
|
||||||
|
XMPP_SETTING_PASSWORD_CALLBACK_USERDATA = 0x80, /**< Userdata for password
|
||||||
|
callback function */
|
||||||
|
|
||||||
|
/* Functionpointer values */
|
||||||
|
XMPP_SETTING_PASSWORD_CALLBACK = 0xC0, /**< Callback function to retrieve
|
||||||
|
password for key file. */
|
||||||
|
XMPP_SETTING_CERTFAIL_HANDLER, /**< Handler function when certificate chain
|
||||||
|
can't be verified */
|
||||||
|
XMPP_SETTING_SOCKOPT_CALLBACK, /**< Callback function when a new socket is
|
||||||
|
created. */
|
||||||
|
} xmpp_conn_setting_t;
|
||||||
|
|
||||||
|
void xmpp_conn_set_int(xmpp_conn_t *conn,
|
||||||
|
xmpp_conn_setting_t setting,
|
||||||
|
int value);
|
||||||
|
void xmpp_conn_set_string(xmpp_conn_t *conn,
|
||||||
|
xmpp_conn_setting_t setting,
|
||||||
|
const char *value);
|
||||||
|
void xmpp_conn_set_pointer(xmpp_conn_t *conn,
|
||||||
|
xmpp_conn_setting_t setting,
|
||||||
|
void *value);
|
||||||
|
/* Provide a wrapper macro, so users don't have to cast their function pointer
|
||||||
|
* type manually. */
|
||||||
|
typedef int (*xmpp_conn_pfn_t)();
|
||||||
|
#define xmpp_conn_set_functionpointer(c, s, p) \
|
||||||
|
do { \
|
||||||
|
xmpp_conn_set_functionpointer_impl(c, s, (xmpp_conn_pfn_t)p); \
|
||||||
|
} while (0)
|
||||||
|
void xmpp_conn_set_functionpointer_impl(xmpp_conn_t *conn,
|
||||||
|
xmpp_conn_setting_t setting,
|
||||||
|
xmpp_conn_pfn_t pfn);
|
||||||
|
|
||||||
|
long xmpp_conn_get_flags(const xmpp_conn_t *conn);
|
||||||
|
int xmpp_conn_set_flags(xmpp_conn_t *conn, long flags);
|
||||||
|
const char *xmpp_conn_get_jid(const xmpp_conn_t *conn);
|
||||||
|
const char *xmpp_conn_get_bound_jid(const xmpp_conn_t *conn);
|
||||||
|
xmpp_tlscert_t *xmpp_conn_get_peer_cert(xmpp_conn_t *const conn);
|
||||||
|
const char *xmpp_conn_get_keyfile(const xmpp_conn_t *conn);
|
||||||
|
unsigned int xmpp_conn_cert_xmppaddr_num(xmpp_conn_t *conn);
|
||||||
|
char *xmpp_conn_cert_xmppaddr(xmpp_conn_t *conn, unsigned int n);
|
||||||
|
const char *xmpp_conn_get_pass(const xmpp_conn_t *conn);
|
||||||
|
xmpp_ctx_t *xmpp_conn_get_context(xmpp_conn_t *conn);
|
||||||
|
int xmpp_conn_is_secured(xmpp_conn_t *conn);
|
||||||
|
int xmpp_conn_is_connecting(xmpp_conn_t *conn);
|
||||||
|
int xmpp_conn_is_connected(xmpp_conn_t *conn);
|
||||||
|
int xmpp_conn_is_disconnected(xmpp_conn_t *conn);
|
||||||
|
int xmpp_conn_send_queue_len(const xmpp_conn_t *conn);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
XMPP_QUEUE_OLDEST = -1,
|
||||||
|
XMPP_QUEUE_YOUNGEST = -2,
|
||||||
|
} xmpp_queue_element_t;
|
||||||
|
char *xmpp_conn_send_queue_drop_element(xmpp_conn_t *conn,
|
||||||
|
xmpp_queue_element_t which);
|
||||||
|
|
||||||
|
xmpp_sm_state_t *xmpp_conn_get_sm_state(xmpp_conn_t *conn);
|
||||||
|
int xmpp_conn_set_sm_state(xmpp_conn_t *conn, xmpp_sm_state_t *sm_state);
|
||||||
|
|
||||||
|
void xmpp_free_sm_state(xmpp_sm_state_t *sm_state);
|
||||||
|
|
||||||
|
int xmpp_connect_client(xmpp_conn_t *conn,
|
||||||
|
const char *altdomain,
|
||||||
|
unsigned short altport,
|
||||||
|
xmpp_conn_handler callback,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
int xmpp_connect_component(xmpp_conn_t *conn,
|
||||||
|
const char *server,
|
||||||
|
unsigned short port,
|
||||||
|
xmpp_conn_handler callback,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
int xmpp_connect_raw(xmpp_conn_t *conn,
|
||||||
|
const char *altdomain,
|
||||||
|
unsigned short altport,
|
||||||
|
xmpp_conn_handler callback,
|
||||||
|
void *userdata);
|
||||||
|
int xmpp_conn_open_stream_default(xmpp_conn_t *conn);
|
||||||
|
int xmpp_conn_open_stream(xmpp_conn_t *conn,
|
||||||
|
char **attributes,
|
||||||
|
size_t attributes_len);
|
||||||
|
int xmpp_conn_tls_start(xmpp_conn_t *conn);
|
||||||
|
|
||||||
|
void xmpp_disconnect(xmpp_conn_t *conn);
|
||||||
|
|
||||||
|
void xmpp_send(xmpp_conn_t *conn, xmpp_stanza_t *stanza);
|
||||||
|
void xmpp_send_error(xmpp_conn_t *conn, xmpp_error_type_t type, char *text);
|
||||||
|
|
||||||
|
void xmpp_send_raw_string(xmpp_conn_t *conn, const char *fmt, ...);
|
||||||
|
void xmpp_send_raw(xmpp_conn_t *conn, const char *data, size_t len);
|
||||||
|
|
||||||
/* handlers */
|
/* handlers */
|
||||||
|
|
||||||
/* if the handle returns false it is removed */
|
/* if the handler returns false it is removed */
|
||||||
typedef int (*xmpp_timed_handler)(xmpp_conn_t * const conn,
|
typedef int (*xmpp_timed_handler)(xmpp_conn_t *conn, void *userdata);
|
||||||
void * const userdata);
|
|
||||||
|
|
||||||
void xmpp_timed_handler_add(xmpp_conn_t * const conn,
|
|
||||||
xmpp_timed_handler handler,
|
|
||||||
const unsigned long period,
|
|
||||||
void * const userdata);
|
|
||||||
void xmpp_timed_handler_delete(xmpp_conn_t * const conn,
|
|
||||||
xmpp_timed_handler handler);
|
|
||||||
|
|
||||||
|
void xmpp_timed_handler_add(xmpp_conn_t *conn,
|
||||||
|
xmpp_timed_handler handler,
|
||||||
|
unsigned long period,
|
||||||
|
void *userdata);
|
||||||
|
void xmpp_timed_handler_delete(xmpp_conn_t *conn, xmpp_timed_handler handler);
|
||||||
|
|
||||||
/* if the handler returns false it is removed */
|
/* if the handler returns false it is removed */
|
||||||
typedef int (*xmpp_handler)(xmpp_conn_t * const conn,
|
typedef int (*xmpp_global_timed_handler)(xmpp_ctx_t *ctx, void *userdata);
|
||||||
xmpp_stanza_t * const stanza,
|
|
||||||
void * const userdata);
|
|
||||||
|
|
||||||
void xmpp_handler_add(xmpp_conn_t * const conn,
|
void xmpp_global_timed_handler_add(xmpp_ctx_t *ctx,
|
||||||
xmpp_handler handler,
|
xmpp_global_timed_handler handler,
|
||||||
const char * const ns,
|
unsigned long period,
|
||||||
const char * const name,
|
void *userdata);
|
||||||
const char * const type,
|
void xmpp_global_timed_handler_delete(xmpp_ctx_t *ctx,
|
||||||
void * const userdata);
|
xmpp_global_timed_handler handler);
|
||||||
void xmpp_handler_delete(xmpp_conn_t * const conn,
|
|
||||||
xmpp_handler handler);
|
|
||||||
|
|
||||||
void xmpp_id_handler_add(xmpp_conn_t * const conn,
|
/* if the handler returns false it is removed */
|
||||||
xmpp_handler handler,
|
typedef int (*xmpp_handler)(xmpp_conn_t *conn,
|
||||||
const char * const id,
|
xmpp_stanza_t *stanza,
|
||||||
void * const userdata);
|
void *userdata);
|
||||||
void xmpp_id_handler_delete(xmpp_conn_t * const conn,
|
|
||||||
xmpp_handler handler,
|
void xmpp_handler_add(xmpp_conn_t *conn,
|
||||||
const char * const id);
|
xmpp_handler handler,
|
||||||
|
const char *ns,
|
||||||
|
const char *name,
|
||||||
|
const char *type,
|
||||||
|
void *userdata);
|
||||||
|
void xmpp_handler_delete(xmpp_conn_t *conn, xmpp_handler handler);
|
||||||
|
|
||||||
|
void xmpp_id_handler_add(xmpp_conn_t *conn,
|
||||||
|
xmpp_handler handler,
|
||||||
|
const char *id,
|
||||||
|
void *userdata);
|
||||||
|
void xmpp_id_handler_delete(xmpp_conn_t *conn,
|
||||||
|
xmpp_handler handler,
|
||||||
|
const char *id);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
|
void xmpp_register_stanza_handler(conn, stanza, xmlns, type, handler)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** stanzas **/
|
/* stanzas */
|
||||||
|
|
||||||
/** allocate an initialize a blank stanza */
|
/* allocate and initialize a blank stanza */
|
||||||
xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx);
|
xmpp_stanza_t *xmpp_stanza_new(xmpp_ctx_t *ctx);
|
||||||
|
xmpp_stanza_t *xmpp_stanza_new_from_string(xmpp_ctx_t *ctx, const char *str);
|
||||||
|
|
||||||
/** clone a stanza */
|
/* clone a stanza */
|
||||||
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t * const stanza);
|
xmpp_stanza_t *xmpp_stanza_clone(xmpp_stanza_t *stanza);
|
||||||
|
|
||||||
/** copies a stanza and all children */
|
/* copies a stanza and all children */
|
||||||
xmpp_stanza_t * xmpp_stanza_copy(const xmpp_stanza_t * const stanza);
|
xmpp_stanza_t *xmpp_stanza_copy(const xmpp_stanza_t *stanza);
|
||||||
|
|
||||||
/** free a stanza object and it's contents */
|
/* free a stanza object and it's contents */
|
||||||
int xmpp_stanza_release(xmpp_stanza_t * const stanza);
|
int xmpp_stanza_release(xmpp_stanza_t *stanza);
|
||||||
|
|
||||||
/** free some blocks returned by other APIs, for example the
|
xmpp_ctx_t *xmpp_stanza_get_context(const xmpp_stanza_t *stanza);
|
||||||
buffer you get from xmpp_stanza_to_text **/
|
|
||||||
void xmpp_free(const xmpp_ctx_t * const ctx, void *p);
|
|
||||||
|
|
||||||
int xmpp_stanza_is_text(xmpp_stanza_t * const stanza);
|
int xmpp_stanza_is_text(xmpp_stanza_t *stanza);
|
||||||
int xmpp_stanza_is_tag(xmpp_stanza_t * const stanza);
|
int xmpp_stanza_is_tag(xmpp_stanza_t *stanza);
|
||||||
|
|
||||||
/** marshall a stanza into text for transmission or display **/
|
/* marshall a stanza into text for transmission or display */
|
||||||
int xmpp_stanza_to_text(xmpp_stanza_t *stanza,
|
int xmpp_stanza_to_text(xmpp_stanza_t *stanza, char **buf, size_t *buflen);
|
||||||
char ** const buf, size_t * const buflen);
|
|
||||||
|
|
||||||
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t * const stanza);
|
xmpp_stanza_t *xmpp_stanza_get_children(xmpp_stanza_t *stanza);
|
||||||
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t * const stanza,
|
xmpp_stanza_t *xmpp_stanza_get_child_by_name(xmpp_stanza_t *stanza,
|
||||||
const char * const name);
|
const char *name);
|
||||||
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t * const stanza,
|
xmpp_stanza_t *xmpp_stanza_get_child_by_ns(xmpp_stanza_t *stanza,
|
||||||
const char * const ns);
|
const char *ns);
|
||||||
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t * const stanza);
|
xmpp_stanza_t *xmpp_stanza_get_child_by_name_and_ns(xmpp_stanza_t *stanza,
|
||||||
char *xmpp_stanza_get_attribute(xmpp_stanza_t * const stanza,
|
const char *name,
|
||||||
const char * const name);
|
const char *ns);
|
||||||
char * xmpp_stanza_get_ns(xmpp_stanza_t * const stanza);
|
/* helper macro for names with a namespace */
|
||||||
|
#define XMPP_STANZA_NAME_IN_NS(name, ns) name "[@ns='" ns "']"
|
||||||
|
xmpp_stanza_t *xmpp_stanza_get_child_by_path(xmpp_stanza_t *stanza, ...);
|
||||||
|
xmpp_stanza_t *xmpp_stanza_get_next(xmpp_stanza_t *stanza);
|
||||||
|
int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);
|
||||||
|
int xmpp_stanza_add_child_ex(xmpp_stanza_t *stanza,
|
||||||
|
xmpp_stanza_t *child,
|
||||||
|
int do_clone);
|
||||||
|
|
||||||
|
const char *xmpp_stanza_get_attribute(xmpp_stanza_t *stanza, const char *name);
|
||||||
|
int xmpp_stanza_get_attribute_count(xmpp_stanza_t *stanza);
|
||||||
|
int xmpp_stanza_get_attributes(xmpp_stanza_t *stanza,
|
||||||
|
const char **attr,
|
||||||
|
int attrlen);
|
||||||
/* concatenate all child text nodes. this function
|
/* concatenate all child text nodes. this function
|
||||||
* returns a string that must be freed by the caller */
|
* returns a string that must be freed by the caller */
|
||||||
|
char *xmpp_stanza_get_text(xmpp_stanza_t *stanza);
|
||||||
char *xmpp_stanza_get_text(xmpp_stanza_t * const stanza);
|
const char *xmpp_stanza_get_text_ptr(xmpp_stanza_t *stanza);
|
||||||
char *xmpp_stanza_get_text_ptr(xmpp_stanza_t * const stanza);
|
const char *xmpp_stanza_get_name(xmpp_stanza_t *stanza);
|
||||||
char *xmpp_stanza_get_name(xmpp_stanza_t * const stanza);
|
|
||||||
|
|
||||||
int xmpp_stanza_add_child(xmpp_stanza_t *stanza, xmpp_stanza_t *child);
|
|
||||||
int xmpp_stanza_set_ns(xmpp_stanza_t * const stanza, const char * const ns);
|
|
||||||
/* set_attribute adds/replaces attributes */
|
/* set_attribute adds/replaces attributes */
|
||||||
int xmpp_stanza_set_attribute(xmpp_stanza_t * const stanza,
|
int xmpp_stanza_set_attribute(xmpp_stanza_t *stanza,
|
||||||
const char * const key,
|
const char *key,
|
||||||
const char * const value);
|
const char *value);
|
||||||
int xmpp_stanza_set_name(xmpp_stanza_t *stanza,
|
int xmpp_stanza_set_name(xmpp_stanza_t *stanza, const char *name);
|
||||||
const char * const name);
|
int xmpp_stanza_set_text(xmpp_stanza_t *stanza, const char *text);
|
||||||
int xmpp_stanza_set_text(xmpp_stanza_t *stanza,
|
|
||||||
const char * const text);
|
|
||||||
int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
int xmpp_stanza_set_text_with_size(xmpp_stanza_t *stanza,
|
||||||
const char * const text,
|
const char *text,
|
||||||
const size_t size);
|
size_t size);
|
||||||
|
int xmpp_stanza_del_attribute(xmpp_stanza_t *stanza, const char *name);
|
||||||
|
|
||||||
/* common stanza helpers */
|
/* common stanza helpers */
|
||||||
char *xmpp_stanza_get_type(xmpp_stanza_t * const stanza);
|
const char *xmpp_stanza_get_ns(xmpp_stanza_t *stanza);
|
||||||
char *xmpp_stanza_get_id(xmpp_stanza_t * const stanza);
|
const char *xmpp_stanza_get_type(xmpp_stanza_t *stanza);
|
||||||
int xmpp_stanza_set_id(xmpp_stanza_t * const stanza,
|
const char *xmpp_stanza_get_id(xmpp_stanza_t *stanza);
|
||||||
const char * const id);
|
const char *xmpp_stanza_get_to(xmpp_stanza_t *stanza);
|
||||||
int xmpp_stanza_set_type(xmpp_stanza_t * const stanza,
|
const char *xmpp_stanza_get_from(xmpp_stanza_t *stanza);
|
||||||
const char * const type);
|
int xmpp_stanza_set_ns(xmpp_stanza_t *stanza, const char *ns);
|
||||||
|
int xmpp_stanza_set_id(xmpp_stanza_t *stanza, const char *id);
|
||||||
/* unimplemented
|
int xmpp_stanza_set_type(xmpp_stanza_t *stanza, const char *type);
|
||||||
int xmpp_stanza_set_to();
|
int xmpp_stanza_set_to(xmpp_stanza_t *stanza, const char *to);
|
||||||
int xmpp_stanza_set_from();
|
int xmpp_stanza_set_from(xmpp_stanza_t *stanza, const char *from);
|
||||||
*/
|
|
||||||
|
|
||||||
/* allocate and initialize a stanza in reply to another */
|
/* allocate and initialize a stanza in reply to another */
|
||||||
/* unimplemented
|
xmpp_stanza_t *xmpp_stanza_reply(xmpp_stanza_t *stanza);
|
||||||
xmpp_stanza_t *xmpp_stanza_reply(const xmpp_stanza_t *stanza);
|
xmpp_stanza_t *xmpp_stanza_reply_error(xmpp_stanza_t *stanza,
|
||||||
*/
|
const char *error_type,
|
||||||
|
const char *condition,
|
||||||
|
const char *text);
|
||||||
|
|
||||||
/* stanza subclasses */
|
/* stanza subclasses */
|
||||||
/* unimplemented
|
xmpp_stanza_t *xmpp_message_new(xmpp_ctx_t *ctx,
|
||||||
void xmpp_message_new();
|
const char *type,
|
||||||
void xmpp_message_get_body();
|
const char *to,
|
||||||
void xmpp_message_set_body();
|
const char *id);
|
||||||
|
char *xmpp_message_get_body(xmpp_stanza_t *msg);
|
||||||
|
int xmpp_message_set_body(xmpp_stanza_t *msg, const char *text);
|
||||||
|
|
||||||
void xmpp_iq_new();
|
xmpp_stanza_t *xmpp_iq_new(xmpp_ctx_t *ctx, const char *type, const char *id);
|
||||||
void xmpp_presence_new();
|
xmpp_stanza_t *xmpp_presence_new(xmpp_ctx_t *ctx);
|
||||||
*/
|
xmpp_stanza_t *
|
||||||
|
xmpp_error_new(xmpp_ctx_t *ctx, xmpp_error_type_t type, const char *text);
|
||||||
|
|
||||||
/** event loop **/
|
/* jid */
|
||||||
void xmpp_run_once(xmpp_ctx_t *ctx, const unsigned long timeout);
|
|
||||||
|
/* these return new strings that must be xmpp_free()'d */
|
||||||
|
char *xmpp_jid_new(xmpp_ctx_t *ctx,
|
||||||
|
const char *node,
|
||||||
|
const char *domain,
|
||||||
|
const char *resource);
|
||||||
|
char *xmpp_jid_bare(xmpp_ctx_t *ctx, const char *jid);
|
||||||
|
char *xmpp_jid_node(xmpp_ctx_t *ctx, const char *jid);
|
||||||
|
char *xmpp_jid_domain(xmpp_ctx_t *ctx, const char *jid);
|
||||||
|
char *xmpp_jid_resource(xmpp_ctx_t *ctx, const char *jid);
|
||||||
|
|
||||||
|
/* event loop */
|
||||||
|
|
||||||
|
void xmpp_run_once(xmpp_ctx_t *ctx, unsigned long timeout);
|
||||||
void xmpp_run(xmpp_ctx_t *ctx);
|
void xmpp_run(xmpp_ctx_t *ctx);
|
||||||
void xmpp_stop(xmpp_ctx_t *ctx);
|
void xmpp_stop(xmpp_ctx_t *ctx);
|
||||||
|
void xmpp_ctx_set_timeout(xmpp_ctx_t *ctx, unsigned long timeout);
|
||||||
|
|
||||||
|
/* TLS certificates */
|
||||||
|
|
||||||
|
xmpp_ctx_t *xmpp_tlscert_get_ctx(const xmpp_tlscert_t *cert);
|
||||||
|
xmpp_conn_t *xmpp_tlscert_get_conn(const xmpp_tlscert_t *cert);
|
||||||
|
const char *xmpp_tlscert_get_pem(const xmpp_tlscert_t *cert);
|
||||||
|
const char *xmpp_tlscert_get_dnsname(const xmpp_tlscert_t *cert, size_t n);
|
||||||
|
const char *xmpp_tlscert_get_string(const xmpp_tlscert_t *cert,
|
||||||
|
xmpp_cert_element_t elmnt);
|
||||||
|
const char *xmpp_tlscert_get_description(xmpp_cert_element_t elmnt);
|
||||||
|
void xmpp_tlscert_free(xmpp_tlscert_t *cert);
|
||||||
|
|
||||||
|
/* UUID */
|
||||||
|
|
||||||
|
char *xmpp_uuid_gen(xmpp_ctx_t *ctx);
|
||||||
|
|
||||||
|
/* SHA1 */
|
||||||
|
|
||||||
|
/** @def XMPP_SHA1_DIGEST_SIZE
|
||||||
|
* Size of the SHA1 message digest.
|
||||||
|
*/
|
||||||
|
#define XMPP_SHA1_DIGEST_SIZE 20
|
||||||
|
|
||||||
|
typedef struct _xmpp_sha1_t xmpp_sha1_t;
|
||||||
|
|
||||||
|
char *xmpp_sha1(xmpp_ctx_t *ctx, const unsigned char *data, size_t len);
|
||||||
|
void xmpp_sha1_digest(const unsigned char *data,
|
||||||
|
size_t len,
|
||||||
|
unsigned char *digest);
|
||||||
|
|
||||||
|
xmpp_sha1_t *xmpp_sha1_new(xmpp_ctx_t *ctx);
|
||||||
|
void xmpp_sha1_free(xmpp_sha1_t *sha1);
|
||||||
|
void xmpp_sha1_update(xmpp_sha1_t *sha1, const unsigned char *data, size_t len);
|
||||||
|
void xmpp_sha1_final(xmpp_sha1_t *sha1);
|
||||||
|
char *xmpp_sha1_to_string(xmpp_sha1_t *sha1, char *s, size_t slen);
|
||||||
|
char *xmpp_sha1_to_string_alloc(xmpp_sha1_t *sha1);
|
||||||
|
void xmpp_sha1_to_digest(xmpp_sha1_t *sha1, unsigned char *digest);
|
||||||
|
|
||||||
|
/* Base64 */
|
||||||
|
|
||||||
|
char *
|
||||||
|
xmpp_base64_encode(xmpp_ctx_t *ctx, const unsigned char *data, size_t len);
|
||||||
|
char *xmpp_base64_decode_str(xmpp_ctx_t *ctx, const char *base64, size_t len);
|
||||||
|
void xmpp_base64_decode_bin(xmpp_ctx_t *ctx,
|
||||||
|
const char *base64,
|
||||||
|
size_t len,
|
||||||
|
unsigned char **out,
|
||||||
|
size_t *outlen);
|
||||||
|
|
||||||
|
/* RNG */
|
||||||
|
|
||||||
|
typedef struct _xmpp_rand_t xmpp_rand_t;
|
||||||
|
|
||||||
|
/** Create new xmpp_rand_t object.
|
||||||
|
*
|
||||||
|
* @param ctx A Strophe context object
|
||||||
|
*
|
||||||
|
* @ingroup Random
|
||||||
|
*/
|
||||||
|
xmpp_rand_t *xmpp_rand_new(xmpp_ctx_t *ctx);
|
||||||
|
|
||||||
|
/** Destroy an xmpp_rand_t object.
|
||||||
|
*
|
||||||
|
* @param ctx A Strophe context object
|
||||||
|
* @param rand A xmpp_rand_t object
|
||||||
|
*
|
||||||
|
* @ingroup Random
|
||||||
|
*/
|
||||||
|
void xmpp_rand_free(xmpp_ctx_t *ctx, xmpp_rand_t *rand);
|
||||||
|
|
||||||
|
/** Generate random integer.
|
||||||
|
* Analogue of rand(3).
|
||||||
|
*
|
||||||
|
* @ingroup Random
|
||||||
|
*/
|
||||||
|
int xmpp_rand(xmpp_rand_t *rand);
|
||||||
|
|
||||||
|
/** Generate random bytes.
|
||||||
|
* Generates len bytes and stores them to the output buffer.
|
||||||
|
*
|
||||||
|
* @param rand A xmpp_rand_t object
|
||||||
|
* @param output A buffer where a len random bytes will be placed.
|
||||||
|
* @param len Number of bytes reserved for the output..
|
||||||
|
*
|
||||||
|
* @ingroup Random
|
||||||
|
*/
|
||||||
|
void xmpp_rand_bytes(xmpp_rand_t *rand, unsigned char *output, size_t len);
|
||||||
|
|
||||||
|
/** Generate a nonce that is printable randomized string.
|
||||||
|
* This function doesn't allocate memory and doesn't fail.
|
||||||
|
*
|
||||||
|
* @param rand A xmpp_rand_t object
|
||||||
|
* @param output A buffer where a NULL-terminated string will be placed.
|
||||||
|
* The string will contain len-1 printable symbols.
|
||||||
|
* @param len Number of bytes reserved for the output string, including
|
||||||
|
* end of line '\0'.
|
||||||
|
*
|
||||||
|
* @ingroup Random
|
||||||
|
*/
|
||||||
|
void xmpp_rand_nonce(xmpp_rand_t *rand, char *output, size_t len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Formerly "private but exported" functions made public for now to announce
|
||||||
|
* deprecation */
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#if !defined(XMPP_DEPRECATED)
|
||||||
|
/**
|
||||||
|
* XMPP_DEPRECATED(x) macro to show a compiler warning for deprecated API
|
||||||
|
* functions
|
||||||
|
*
|
||||||
|
* @param x The function that can be used as a replacement or 'internal' if
|
||||||
|
* there is no replacement
|
||||||
|
*/
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 405)
|
||||||
|
#define XMPP_DEPRECATED(x) __attribute__((deprecated("replaced by " #x)))
|
||||||
|
#elif (__GNUC__ * 100 + __GNUC_MINOR__ >= 300)
|
||||||
|
#define XMPP_DEPRECATED(x) __attribute__((deprecated))
|
||||||
|
#endif
|
||||||
|
#elif defined(_MSC_VER) && _MSC_VER >= 1500
|
||||||
|
#define XMPP_DEPRECATED(x) __declspec(deprecated("replaced by " #x))
|
||||||
|
#else
|
||||||
|
#define XMPP_DEPRECATED(x)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
XMPP_DEPRECATED(internal) void *xmpp_alloc(const xmpp_ctx_t *ctx, size_t size);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
void *xmpp_realloc(const xmpp_ctx_t *ctx, void *p, size_t size);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
char *xmpp_strdup(const xmpp_ctx_t *ctx, const char *s);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
char *xmpp_strndup(const xmpp_ctx_t *ctx, const char *s, size_t len);
|
||||||
|
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
char *xmpp_strtok_r(char *s, const char *delim, char **saveptr);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
int xmpp_snprintf(char *str, size_t count, const char *fmt, ...);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
int xmpp_vsnprintf(char *str, size_t count, const char *fmt, va_list arg);
|
||||||
|
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
void xmpp_log(const xmpp_ctx_t *ctx,
|
||||||
|
xmpp_log_level_t level,
|
||||||
|
const char *area,
|
||||||
|
const char *fmt,
|
||||||
|
va_list ap);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
void xmpp_error(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
void xmpp_warn(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
void xmpp_info(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
void xmpp_debug(const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||||
|
XMPP_DEPRECATED(internal)
|
||||||
|
void xmpp_debug_verbose(
|
||||||
|
int level, const xmpp_ctx_t *ctx, const char *area, const char *fmt, ...);
|
||||||
|
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_sockopt_callback)
|
||||||
|
void xmpp_conn_set_keepalive(xmpp_conn_t *conn, int timeout, int interval);
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_flags)
|
||||||
|
void xmpp_conn_disable_tls(xmpp_conn_t *conn);
|
||||||
|
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_string)
|
||||||
|
void xmpp_conn_set_jid(xmpp_conn_t *conn, const char *jid);
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_string)
|
||||||
|
void xmpp_conn_set_pass(xmpp_conn_t *conn, const char *pass);
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_string)
|
||||||
|
void xmpp_conn_set_cafile(xmpp_conn_t *const conn, const char *path);
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_string)
|
||||||
|
void xmpp_conn_set_capath(xmpp_conn_t *const conn, const char *path);
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_string)
|
||||||
|
void xmpp_conn_set_client_cert(xmpp_conn_t *conn,
|
||||||
|
const char *cert,
|
||||||
|
const char *key);
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_int)
|
||||||
|
void xmpp_conn_set_password_retries(xmpp_conn_t *conn, unsigned int retries);
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_functionpointer)
|
||||||
|
void xmpp_conn_set_password_callback(xmpp_conn_t *conn,
|
||||||
|
xmpp_password_callback cb,
|
||||||
|
void *userdata);
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_functionpointer)
|
||||||
|
void xmpp_conn_set_certfail_handler(xmpp_conn_t *const conn,
|
||||||
|
xmpp_certfail_handler hndl);
|
||||||
|
XMPP_DEPRECATED(xmpp_conn_set_functionpointer)
|
||||||
|
void xmpp_conn_set_sockopt_callback(xmpp_conn_t *conn,
|
||||||
|
xmpp_sockopt_callback callback);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
136
strophepp.h
136
strophepp.h
@@ -1,136 +0,0 @@
|
|||||||
/* strophepp.h
|
|
||||||
** strophe XMPP client library C++ API
|
|
||||||
**
|
|
||||||
** Copyright (C) 2005-2009 Collecta, Inc.
|
|
||||||
**
|
|
||||||
** This software is provided AS-IS with no warranty, either express or
|
|
||||||
** implied.
|
|
||||||
**
|
|
||||||
** This software is distributed under license and may not be copied,
|
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @file
|
|
||||||
* Strophe public C++ API definitions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __LIBSTROPHE_STROPHEPP_H__
|
|
||||||
#define __LIBSTROPHE_STROPHEPP_H__
|
|
||||||
|
|
||||||
#include "strophe.h"
|
|
||||||
|
|
||||||
namespace XMPP {
|
|
||||||
class Context {
|
|
||||||
private:
|
|
||||||
xmpp_mem_t m_mem;
|
|
||||||
xmpp_log_t m_log;
|
|
||||||
xmpp_ctx_t *m_ctx;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Context();
|
|
||||||
virtual ~Context();
|
|
||||||
|
|
||||||
virtual void *alloc(const size_t size);
|
|
||||||
virtual void *realloc(void *p, const size_t size);
|
|
||||||
virtual void free(void *p);
|
|
||||||
virtual void log(const xmpp_log_level_t level,
|
|
||||||
const char * const area,
|
|
||||||
const char * const msg);
|
|
||||||
|
|
||||||
xmpp_ctx_t *getContext();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void *callAlloc(const size_t size, void * const userdata);
|
|
||||||
static void *callRealloc(void *p, const size_t size,
|
|
||||||
void * const userdata);
|
|
||||||
static void callFree(void *p, void * const userdata);
|
|
||||||
static void callLog(void * const userdata,
|
|
||||||
const xmpp_log_level_t level,
|
|
||||||
const char * const area,
|
|
||||||
const char * const msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Stanza {
|
|
||||||
private:
|
|
||||||
Context *m_ctx;
|
|
||||||
xmpp_stanza_t *m_stanza;
|
|
||||||
|
|
||||||
void *operator new(size_t size, Context *ctx);
|
|
||||||
void operator delete(void *p);
|
|
||||||
Stanza(Context *ctx);
|
|
||||||
virtual ~Stanza();
|
|
||||||
|
|
||||||
public:
|
|
||||||
static Stanza *create(Context *ctx);
|
|
||||||
void release();
|
|
||||||
Stanza *clone();
|
|
||||||
Stanza *copy();
|
|
||||||
|
|
||||||
int toText(const char ** const buf, size_t * const buflen);
|
|
||||||
Stanza *getChildren();
|
|
||||||
Stanza *getChildByName(const char * const name);
|
|
||||||
Stanza *getNext();
|
|
||||||
char *getAttribute(const char * const name);
|
|
||||||
char *getNamespace();
|
|
||||||
char *getText();
|
|
||||||
char *getName();
|
|
||||||
void addChild(Stanza *child);
|
|
||||||
void setNamespace(const char * const ns);
|
|
||||||
void setAttribute(const char * const key, const char * const value);
|
|
||||||
void setName(const char * const name);
|
|
||||||
void setText(const char * const text);
|
|
||||||
void setText(const char * const text, const size_t size);
|
|
||||||
char *getType();
|
|
||||||
char *getId();
|
|
||||||
char *getTo();
|
|
||||||
char *getFrom();
|
|
||||||
void setType(const char * const type);
|
|
||||||
void setId(const char * const id);
|
|
||||||
void setTo(const char * const to);
|
|
||||||
void setFrom(const char * const from);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Connection {
|
|
||||||
private:
|
|
||||||
Context *m_ctx;
|
|
||||||
xmpp_conn_t *conn;
|
|
||||||
|
|
||||||
void *operator new(size_t size, Context *ctx);
|
|
||||||
Connection(Context *ctx);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static Connection *create(Context *ctx);
|
|
||||||
virtual ~Connection();
|
|
||||||
Connection *clone();
|
|
||||||
void operator delete(void *p);
|
|
||||||
|
|
||||||
const char *getJID();
|
|
||||||
void setJID(const char * const jid);
|
|
||||||
const char *getPass();
|
|
||||||
void setPass(const char * const pass);
|
|
||||||
bool connectClient(const char * const domain,
|
|
||||||
xmpp_conn_handler callback,
|
|
||||||
void * const userdata);
|
|
||||||
void disconnect();
|
|
||||||
void send(Stanza *stanza);
|
|
||||||
|
|
||||||
void addTimedHandler(xmpp_timed_handler handler,
|
|
||||||
const unsigned long perdio,
|
|
||||||
void * const userdata);
|
|
||||||
void deleteTimedHandler(xmpp_timed_handler handler);
|
|
||||||
void addHandler(xmpp_handler handler,
|
|
||||||
const char * const ns,
|
|
||||||
const char * const name,
|
|
||||||
const char * const type,
|
|
||||||
void * const userdata);
|
|
||||||
void deleteHandler(xmpp_handler handler);
|
|
||||||
void addIdHandler(xmpp_handler handler,
|
|
||||||
const char * const id,
|
|
||||||
void * const userdata);
|
|
||||||
void deleteIdHandler(xmpp_handler handler);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* __LIBSTROPHE_STROPHEPP_H__ */
|
|
||||||
13
testbuild.sh
Executable file
13
testbuild.sh
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
logfile="../../testbuild.log"
|
||||||
|
|
||||||
|
err_out() {
|
||||||
|
tail $logfile
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
./bootstrap.sh
|
||||||
|
./configure >> $logfile || err_out
|
||||||
|
make -j$(( `nproc` * 2 + 1 )) >> $logfile || err_out
|
||||||
|
make check >> $logfile || err_out
|
||||||
BIN
tests/cert.emptypass.pfx
Normal file
BIN
tests/cert.emptypass.pfx
Normal file
Binary file not shown.
BIN
tests/cert.nopass.pfx
Normal file
BIN
tests/cert.nopass.pfx
Normal file
Binary file not shown.
19
tests/cert.pem
Normal file
19
tests/cert.pem
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDIDCCAqagAwIBAgIIddFxfQ2VcRIwCgYIKoZIzj0EAwQwSzFJMEcGA1UEAwxA
|
||||||
|
dmVyeS5sb25nLnVzZXJuYW1lQHNvLnRoZS5hc24xLmxlbmd0aC5pcy5hLnZhbGlk
|
||||||
|
LmFzY2lpLmNoYXJhY3RlcjAgFw0yMzA4MDkxMzI3MDBaGA8yMTI0MDgwOTEzMjcw
|
||||||
|
MFowSzFJMEcGA1UEAwxAdmVyeS5sb25nLnVzZXJuYW1lQHNvLnRoZS5hc24xLmxl
|
||||||
|
bmd0aC5pcy5hLnZhbGlkLmFzY2lpLmNoYXJhY3RlcjB2MBAGByqGSM49AgEGBSuB
|
||||||
|
BAAiA2IABHfN9a5xAEPYWz+0O6Jfe95WGUeuq6Eca5po4UGcJgs78L6d4KT+FR/a
|
||||||
|
yDSi/BmS0ZAksXbshkSjQA6XmAwWkNInJF/buAwh/hFVAOXwx3eKtguPskxg1xYj
|
||||||
|
0WZ3WrY9oKOCAVMwggFPMAkGA1UdEwQCMAAwHQYDVR0OBBYEFNeonZqrLTGCm3Vk
|
||||||
|
+X3TtMfDQVf4MGQGA1UdIwRdMFuhT6RNMEsxSTBHBgNVBAMMQHZlcnkubG9uZy51
|
||||||
|
c2VybmFtZUBzby50aGUuYXNuMS5sZW5ndGguaXMuYS52YWxpZC5hc2NpaS5jaGFy
|
||||||
|
YWN0ZXKCCHXRcX0NlXESMAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcD
|
||||||
|
AjCBmgYDVR0RBIGSMIGPoE4GCCsGAQUFBwgFoEIMQHZlcnkubG9uZy51c2VybmFt
|
||||||
|
ZUBzby50aGUuYXNuMS5sZW5ndGguaXMuYS52YWxpZC5hc2NpaS5jaGFyYWN0ZXKB
|
||||||
|
DnlldEBub3RoZXIuc2Fugg53d3cuc3Ryb3BoZS5pbaAdBggrBgEFBQcIBaARDA9z
|
||||||
|
ZWNvbmRAeG1wcC5qaWQwCgYIKoZIzj0EAwQDaAAwZQIxAJTzJY/q/zFws0FPPfVI
|
||||||
|
VXc+kaGdxwzM1FKlVMwxl+PzXAoeL0Z7FtGSkaCWyaokaAIwLaYxuQhgisetYxbU
|
||||||
|
qTyv0MdS2uUy6NNcVNztWCMS350TuuVm3f9wmwOjfaeoHft3
|
||||||
|
-----END CERTIFICATE-----
|
||||||
BIN
tests/cert.pfx
Normal file
BIN
tests/cert.pfx
Normal file
Binary file not shown.
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* check_parser.h
|
/* check_parser.h
|
||||||
** strophe XMPP client library -- parser tests
|
** strophe XMPP client library -- parser tests
|
||||||
**
|
**
|
||||||
@@ -6,70 +7,84 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express or
|
** This software is provided AS-IS with no warranty, either express or
|
||||||
** implied.
|
** implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <check.h>
|
#include "strophe.h"
|
||||||
|
|
||||||
#include <strophe.h>
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
#include "test.h"
|
#include "test.h"
|
||||||
|
|
||||||
START_TEST(create_destroy)
|
#define fail_unless(expr) \
|
||||||
|
do { \
|
||||||
|
int result = (expr); \
|
||||||
|
if (!result) { \
|
||||||
|
printf("%s:%d: Assertion failed: %s\n", __FILE__, __LINE__, \
|
||||||
|
#expr); \
|
||||||
|
exit(1); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static void create_destroy(void)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
parser_t *parser;
|
parser_t *parser;
|
||||||
|
|
||||||
ctx = xmpp_ctx_new(NULL, NULL);
|
ctx = xmpp_ctx_new(NULL, NULL);
|
||||||
parser = parser_new(ctx, NULL, NULL, NULL, NULL);
|
parser = parser_new(ctx, NULL, NULL, NULL, NULL);
|
||||||
fail_unless(parser != NULL, "Parser creation failed.");
|
fail_unless(parser != NULL);
|
||||||
parser_free(parser);
|
parser_free(parser);
|
||||||
xmpp_ctx_free(ctx);
|
xmpp_ctx_free(ctx);
|
||||||
}
|
}
|
||||||
END_TEST
|
|
||||||
|
|
||||||
int cbtest_got_start = 0;
|
int cbtest_got_start = 0;
|
||||||
void cbtest_handle_start(char *name, char **attrs, void *userdata)
|
void cbtest_handle_start(char *name, char **attrs, void *userdata)
|
||||||
{
|
{
|
||||||
if (strcmp(name, "stream:stream") == 0)
|
(void)attrs;
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
|
if (strcmp(name, "stream") == 0)
|
||||||
cbtest_got_start = 1;
|
cbtest_got_start = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cbtest_got_end = 0;
|
int cbtest_got_end = 0;
|
||||||
void cbtest_handle_end(char *name, void *userdata)
|
void cbtest_handle_end(char *name, void *userdata)
|
||||||
{
|
{
|
||||||
if (strcmp(name, "stream:stream") == 0)
|
(void)userdata;
|
||||||
|
|
||||||
|
if (strcmp(name, "stream") == 0)
|
||||||
cbtest_got_end = 1;
|
cbtest_got_end = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cbtest_got_stanza = 0;
|
int cbtest_got_stanza = 0;
|
||||||
void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata)
|
void cbtest_handle_stanza(xmpp_stanza_t *stanza, void *userdata)
|
||||||
{
|
{
|
||||||
|
(void)userdata;
|
||||||
|
|
||||||
if (strcmp(xmpp_stanza_get_name(stanza), "message") == 0)
|
if (strcmp(xmpp_stanza_get_name(stanza), "message") == 0)
|
||||||
cbtest_got_stanza = 1;
|
cbtest_got_stanza = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
START_TEST(callbacks)
|
static void callbacks(void)
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
parser_t *parser;
|
parser_t *parser;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ctx = xmpp_ctx_new(NULL, NULL);
|
ctx = xmpp_ctx_new(NULL, NULL);
|
||||||
parser = parser_new(ctx,
|
parser = parser_new(ctx, cbtest_handle_start, cbtest_handle_end,
|
||||||
cbtest_handle_start,
|
|
||||||
cbtest_handle_end,
|
|
||||||
cbtest_handle_stanza, NULL);
|
cbtest_handle_stanza, NULL);
|
||||||
|
|
||||||
ret = parser_feed(parser, "<stream:stream>", 15);
|
ret = parser_feed(parser, "<stream>", 8);
|
||||||
|
fail_unless(ret != 0);
|
||||||
ret = parser_feed(parser, "<message/>", 10);
|
ret = parser_feed(parser, "<message/>", 10);
|
||||||
parser_feed(parser, "</stream:stream>", 16);
|
fail_unless(ret != 0);
|
||||||
|
ret = parser_feed(parser, "</stream>", 9);
|
||||||
|
fail_unless(ret != 0);
|
||||||
|
|
||||||
fail_unless(cbtest_got_start == 1);
|
fail_unless(cbtest_got_start == 1);
|
||||||
fail_unless(cbtest_got_end == 1);
|
fail_unless(cbtest_got_end == 1);
|
||||||
@@ -78,16 +93,18 @@ START_TEST(callbacks)
|
|||||||
parser_free(parser);
|
parser_free(parser);
|
||||||
xmpp_ctx_free(ctx);
|
xmpp_ctx_free(ctx);
|
||||||
}
|
}
|
||||||
END_TEST
|
|
||||||
|
|
||||||
Suite *parser_suite(void)
|
int main()
|
||||||
{
|
{
|
||||||
Suite *s = suite_create("Parser");
|
printf("XML parser tests.\n");
|
||||||
TCase *tc_core = tcase_create("Core");
|
|
||||||
tcase_add_test(tc_core, create_destroy);
|
|
||||||
tcase_add_test(tc_core, callbacks);
|
|
||||||
suite_add_tcase(s, tc_core);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_MAIN
|
printf("create-destroy: ");
|
||||||
|
create_destroy();
|
||||||
|
printf("ok\n");
|
||||||
|
|
||||||
|
printf("callbacks: ");
|
||||||
|
callbacks();
|
||||||
|
printf("ok\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
6
tests/key.pem
Normal file
6
tests/key.pem
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MIGkAgEBBDBZ90AXrC5n1ZxFGq4WpqMgvH7qw2YddaUzzPFtf9qDRDrRe3xW6nTr
|
||||||
|
Iywpxdh26GWgBwYFK4EEACKhZANiAAR3zfWucQBD2Fs/tDuiX3veVhlHrquhHGua
|
||||||
|
aOFBnCYLO/C+neCk/hUf2sg0ovwZktGQJLF27IZEo0AOl5gMFpDSJyRf27gMIf4R
|
||||||
|
VQDl8Md3irYLj7JMYNcWI9Fmd1q2PaA=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
9
tests/key_encrypted.pem
Normal file
9
tests/key_encrypted.pem
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-256-CBC,D1537075EE9323A68A08B79257D87D53
|
||||||
|
|
||||||
|
faIortcMN9tCcmwidAJlFDOS68OJlAH1Bzjh2sqhZDhFJEU/w3bOihTDk1cV0yg1
|
||||||
|
JHSxolcX7aYLaBfhSdbnUDaeNhyvfF97O6lMHXbiSdkWBMKrOjSwpW5BcemQgbpI
|
||||||
|
gjCi7KYWb2Z3CqcELR9624u4vRgytJQliqLW1jjnGMELcuq7HaoB2wP0XmXp9/BV
|
||||||
|
QMQU504S6V8JbogFZeNGCOq+EW5C8tEBCz82y1PRUr0=
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
91
tests/res_query_dump.c
Normal file
91
tests/res_query_dump.c
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* res_query_dump.c
|
||||||
|
* Simple program to dump res_query(3) response
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Linux and OSX:
|
||||||
|
* gcc -o res_query_dump tests/res_query_dump.c -lresolv
|
||||||
|
* *BSD:
|
||||||
|
* gcc -o res_query_dump tests/res_query_dump.c
|
||||||
|
* QNX:
|
||||||
|
* gcc -o res_query_dump tests/res_query_dump.c -lsocket
|
||||||
|
* Solaris:
|
||||||
|
* gcc -o res_query_dump tests/res_query_dump.c -lresolv -lsocket -lnsl
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/nameser.h>
|
||||||
|
#include <resolv.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef T_SRV
|
||||||
|
#define T_SRV 33
|
||||||
|
#endif /* T_SRV */
|
||||||
|
#ifndef C_IN
|
||||||
|
#define C_IN 1
|
||||||
|
#endif /* C_IN */
|
||||||
|
|
||||||
|
#define STEP 10
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
unsigned char buf[65536];
|
||||||
|
char fulldomain[2048];
|
||||||
|
char *service = "xmpp-client";
|
||||||
|
char *proto = "tcp";
|
||||||
|
char *domain = NULL;
|
||||||
|
int len;
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "%s: argument missed\n", argc > 0 ? argv[0] : "$0");
|
||||||
|
fprintf(stderr, "Usage: %s <domain>\n", argc > 0 ? argv[0] : "$0");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain = argv[1];
|
||||||
|
snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", service, proto,
|
||||||
|
domain);
|
||||||
|
errno = 0;
|
||||||
|
len = res_query(fulldomain, C_IN, T_SRV, buf, sizeof(buf));
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
fprintf(stderr, "res_query(): Error occurred (errno=%d)\n", errno);
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
fprintf(stderr, "res_query(): Empty result\n");
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
|
printf("/* res_query(\"%s\", C_IN, T_SRV, ...) */\n", fulldomain);
|
||||||
|
printf("static const unsigned char data[] = {\n");
|
||||||
|
for (i = 0; i < len; i += STEP) {
|
||||||
|
printf(" ");
|
||||||
|
for (j = i; j < len && j < i + STEP; ++j) {
|
||||||
|
printf(" 0x%02x,", buf[j]);
|
||||||
|
}
|
||||||
|
for (j = len; j < i + STEP; ++j) {
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
printf(" // ");
|
||||||
|
for (j = i; j < len && j < i + STEP; ++j) {
|
||||||
|
printf("%c", isprint(buf[j]) ? buf[j] : '.');
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf("};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return len <= 0;
|
||||||
|
}
|
||||||
55
tests/test.c
Normal file
55
tests/test.c
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* test.c
|
||||||
|
* strophe XMPP client library -- common routines for tests
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Dmitry Podgorny <pasis.ua@gmail.com>
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS with no warranty, either express
|
||||||
|
* or implied.
|
||||||
|
*
|
||||||
|
* This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
static uint8_t char_to_bin(char c)
|
||||||
|
{
|
||||||
|
return c <= '9' ? (uint8_t)(c - '0')
|
||||||
|
: c <= 'Z' ? (uint8_t)(c - 'A' + 10)
|
||||||
|
: (uint8_t)(c - 'a' + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_hex_to_bin(const char *hex, uint8_t *bin, size_t *bin_len)
|
||||||
|
{
|
||||||
|
size_t len = strlen(hex);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
assert(len % 2 == 0);
|
||||||
|
|
||||||
|
for (i = 0; i < len / 2; ++i) {
|
||||||
|
bin[i] = char_to_bin(hex[i * 2]) * 16 + char_to_bin(hex[i * 2 + 1]);
|
||||||
|
}
|
||||||
|
*bin_len = len / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *test_bin_to_hex(const uint8_t *bin, size_t len)
|
||||||
|
{
|
||||||
|
static char buf[2048];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
static const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
|
||||||
|
assert(ARRAY_SIZE(buf) > len * 2);
|
||||||
|
|
||||||
|
for (i = 0; i < len; ++i) {
|
||||||
|
buf[i * 2] = hex[(bin[i] >> 4) & 0x0f];
|
||||||
|
buf[i * 2 + 1] = hex[bin[i] & 0x0f];
|
||||||
|
}
|
||||||
|
buf[len * 2] = '\0';
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
89
tests/test.h
89
tests/test.h
@@ -1,16 +1,85 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
|
/* test.h
|
||||||
|
** libstrophe XMPP client library -- common routines for tests
|
||||||
|
**
|
||||||
|
** Copyright (C) 2005-2009 Collecta, Inc.
|
||||||
|
**
|
||||||
|
** This software is provided AS-IS with no warranty, either express
|
||||||
|
** or implied.
|
||||||
|
**
|
||||||
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef __LIBSTROPHE_TEST_H__
|
#ifndef __LIBSTROPHE_TEST_H__
|
||||||
#define __LIBSTROPHE_TEST_H__
|
#define __LIBSTROPHE_TEST_H__
|
||||||
|
|
||||||
#define TEST_MAIN \
|
#include <stddef.h>
|
||||||
int main(int argc, char **argv) {\
|
#include <stdlib.h>
|
||||||
int num_failed;\
|
#include <stdio.h>
|
||||||
Suite *s = parser_suite();\
|
#include <string.h>
|
||||||
SRunner *sr = srunner_create(s);\
|
#include "ostypes.h"
|
||||||
srunner_run_all(sr, CK_NORMAL);\
|
|
||||||
num_failed = srunner_ntests_failed(sr);\
|
|
||||||
srunner_free(sr);\
|
|
||||||
return (num_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;\
|
|
||||||
}\
|
|
||||||
|
|
||||||
|
#define TEST_MAIN \
|
||||||
|
int main(int argc, char **argv) \
|
||||||
|
{ \
|
||||||
|
int num_failed; \
|
||||||
|
Suite *s = parser_suite(); \
|
||||||
|
SRunner *sr = srunner_create(s); \
|
||||||
|
srunner_run_all(sr, CK_NORMAL); \
|
||||||
|
num_failed = srunner_ntests_failed(sr); \
|
||||||
|
srunner_free(sr); \
|
||||||
|
return (num_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef ARRAY_SIZE
|
||||||
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define COMPARE(should, is) \
|
||||||
|
do { \
|
||||||
|
const char *__should = should; \
|
||||||
|
const char *__is = is; \
|
||||||
|
if ((__should == NULL) && (__is == NULL)) { \
|
||||||
|
/* noop */ \
|
||||||
|
} else if (!__should || !__is || strcmp(__should, __is) != 0) { \
|
||||||
|
printf("Error: %s\n" \
|
||||||
|
"Expected: %s\n" \
|
||||||
|
"Got: %s\n", \
|
||||||
|
#should " != " #is, __should, __is); \
|
||||||
|
exit(1); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define COMPARE_BUF(should, should_len, is, is_len) \
|
||||||
|
do { \
|
||||||
|
const uint8_t *__should = (uint8_t *)(should); \
|
||||||
|
const uint8_t *__is = (uint8_t *)(is); \
|
||||||
|
size_t __should_len = should_len; \
|
||||||
|
size_t __is_len = is_len; \
|
||||||
|
if (__should_len != __is_len || \
|
||||||
|
memcmp(__should, __is, __should_len) != 0) { \
|
||||||
|
printf("Error: %s\n", #should " != " #is); \
|
||||||
|
printf("Expected: 0x%s\n", \
|
||||||
|
test_bin_to_hex(__should, __should_len)); \
|
||||||
|
printf("Got: 0x%s\n", test_bin_to_hex(__is, __is_len)); \
|
||||||
|
exit(1); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ENSURE_EQ(should, is) \
|
||||||
|
do { \
|
||||||
|
int __should = should; \
|
||||||
|
int __is = is; \
|
||||||
|
if (__should != __is) { \
|
||||||
|
printf("Error: %s\n" \
|
||||||
|
"Expected: %d\n" \
|
||||||
|
"Got: %d\n", \
|
||||||
|
#should " != " #is, __is, __should); \
|
||||||
|
exit(1); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
void test_hex_to_bin(const char *hex, uint8_t *bin, size_t *bin_len);
|
||||||
|
const char *test_bin_to_hex(const uint8_t *bin, size_t len);
|
||||||
|
|
||||||
#endif /* __LIBSTROPHE_TEST_H__ */
|
#endif /* __LIBSTROPHE_TEST_H__ */
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
||||||
/* test_base64.c
|
/* test_base64.c
|
||||||
** libstrophe XMPP client library -- test routines for the base64 codec
|
** libstrophe XMPP client library -- test routines for the base64 codec
|
||||||
**
|
**
|
||||||
@@ -6,146 +7,158 @@
|
|||||||
** This software is provided AS-IS with no warranty, either express
|
** This software is provided AS-IS with no warranty, either express
|
||||||
** or implied.
|
** or implied.
|
||||||
**
|
**
|
||||||
** This software is distributed under license and may not be copied,
|
** This program is dual licensed under the MIT or GPLv3 licenses.
|
||||||
** modified or distributed except as expressly authorized under the
|
|
||||||
** terms of the license contained in the file LICENSE.txt in this
|
|
||||||
** distribution.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "strophe.h"
|
#include "strophe.h"
|
||||||
#include "common.h"
|
|
||||||
#include "sasl.h"
|
|
||||||
|
|
||||||
static const unsigned char text_1[] = "How now brown cow?";
|
#include "test.h"
|
||||||
static const char base64_1[] = "SG93IG5vdyBicm93biBjb3c/";
|
|
||||||
static const unsigned char text_2[] = {0x14,0xfb,0x9c,0x03,0xd9,0x7e,0x00};
|
|
||||||
static const char base64_2[] = "FPucA9l+";
|
|
||||||
static const unsigned char text_3[] =
|
|
||||||
"From rest and sleep, which but thy pictures be, "
|
|
||||||
"Much pleasure; then from thee much more must flow, "
|
|
||||||
"And soonest our best men with thee do go, "
|
|
||||||
"Rest of their bones, and soul's delivery.";
|
|
||||||
static const char base64_3[] =
|
|
||||||
"RnJvbSByZXN0IGFuZCBzbGVlcCwgd2hpY2ggYnV0IHRoeSBwaWN0dXJl"
|
|
||||||
"cyBiZSwgTXVjaCBwbGVhc3VyZTsgdGhlbiBmcm9tIHRoZWUgbXVjaCBt"
|
|
||||||
"b3JlIG11c3QgZmxvdywgQW5kIHNvb25lc3Qgb3VyIGJlc3QgbWVuIHdp"
|
|
||||||
"dGggdGhlZSBkbyBnbywgUmVzdCBvZiB0aGVpciBib25lcywgYW5kIHNv"
|
|
||||||
"dWwncyBkZWxpdmVyeS4=";
|
|
||||||
static const unsigned char text_4[] =
|
|
||||||
{0xd6, 0x2f, 0x27, 0x49, 0x7e, 0xdd, 0xf3, 0xd5,
|
|
||||||
0x41, 0xbc, 0x1b, 0xe9, 0xdf, 0xe9, 0xb3, 0x08, 0x00};
|
|
||||||
static const char base64_4[] = "1i8nSX7d89VBvBvp3+mzCA==";
|
|
||||||
static const char text_5[] =
|
|
||||||
"realm=\"chesspark.com\",nonce=\"b243c0d663257a9149999cef2f83a22116559e93\",qop=\"auth\",charset=utf-8,algorithm=md5-sess";
|
|
||||||
static const char base64_5[] =
|
|
||||||
"cmVhbG09ImNoZXNzcGFyay5jb20iLG5vbmNlPSJiMjQzYzBkNjYzMjU3"
|
|
||||||
"YTkxNDk5OTljZWYyZjgzYTIyMTE2NTU5ZTkzIixxb3A9ImF1dGgiLGNo"
|
|
||||||
"YXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz";
|
|
||||||
|
|
||||||
int test_encode(xmpp_ctx_t *ctx)
|
static const unsigned char test_2_raw[] = {0x14, 0xfb, 0x9c, 0x03,
|
||||||
{
|
0xd9, 0x7e, 0x00};
|
||||||
char *result;
|
static const unsigned char test_4_raw[] = {0xd6, 0x2f, 0x27, 0x49, 0x7e, 0xdd,
|
||||||
result = base64_encode(ctx, text_1, strlen(text_1));
|
0xf3, 0xd5, 0x41, 0xbc, 0x1b, 0xe9,
|
||||||
if (result == NULL) return 2;
|
0xdf, 0xe9, 0xb3, 0x08, 0x00};
|
||||||
if (strlen(result) != strlen(base64_1)) return 1;
|
|
||||||
if (strncmp(base64_1, result, strlen(base64_1))) return 1;
|
|
||||||
xmpp_free(ctx,result);
|
|
||||||
|
|
||||||
result = base64_encode(ctx, text_2, strlen(text_2));
|
static const struct {
|
||||||
if (result == NULL) return 2;
|
char *raw;
|
||||||
if (strlen(result) != strlen(base64_2)) return 1;
|
char *base64;
|
||||||
if (strncmp(base64_2, result, strlen(base64_2))) return 1;
|
} tests[] = {
|
||||||
xmpp_free(ctx,result);
|
{
|
||||||
|
.raw = "How now brown cow?",
|
||||||
|
.base64 = "SG93IG5vdyBicm93biBjb3c/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.raw = (char *)test_2_raw,
|
||||||
|
.base64 = "FPucA9l+",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.raw = "From rest and sleep, which but thy pictures be, "
|
||||||
|
"Much pleasure; then from thee much more must flow, "
|
||||||
|
"And soonest our best men with thee do go, "
|
||||||
|
"Rest of their bones, and soul's delivery.",
|
||||||
|
.base64 = "RnJvbSByZXN0IGFuZCBzbGVlcCwgd2hpY2ggYnV0IHRoeSBwaWN0dXJl"
|
||||||
|
"cyBiZSwgTXVjaCBwbGVhc3VyZTsgdGhlbiBmcm9tIHRoZWUgbXVjaCBt"
|
||||||
|
"b3JlIG11c3QgZmxvdywgQW5kIHNvb25lc3Qgb3VyIGJlc3QgbWVuIHdp"
|
||||||
|
"dGggdGhlZSBkbyBnbywgUmVzdCBvZiB0aGVpciBib25lcywgYW5kIHNv"
|
||||||
|
"dWwncyBkZWxpdmVyeS4=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.raw = (char *)test_4_raw,
|
||||||
|
.base64 = "1i8nSX7d89VBvBvp3+mzCA==",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.raw = "realm=\"chesspark.com\",nonce=\"b243c0d663257a9149999cef2f83"
|
||||||
|
"a22116559e93\",qop=\"auth\",charset=utf-8,algorithm=md5-sess",
|
||||||
|
.base64 = "cmVhbG09ImNoZXNzcGFyay5jb20iLG5vbmNlPSJiMjQzYzBkNjYzMjU3"
|
||||||
|
"YTkxNDk5OTljZWYyZjgzYTIyMTE2NTU5ZTkzIixxb3A9ImF1dGgiLGNo"
|
||||||
|
"YXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz",
|
||||||
|
},
|
||||||
|
|
||||||
result = base64_encode(ctx, text_3, strlen(text_3));
|
/* RFC4648 test vectors */
|
||||||
if (result == NULL) return 2;
|
{
|
||||||
if (strlen(result) != strlen(base64_3)) return 1;
|
.raw = "",
|
||||||
if (strncmp(base64_3, result, strlen(base64_3))) return 1;
|
.base64 = "",
|
||||||
xmpp_free(ctx,result);
|
},
|
||||||
|
{
|
||||||
|
.raw = "f",
|
||||||
|
.base64 = "Zg==",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.raw = "fo",
|
||||||
|
.base64 = "Zm8=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.raw = "foo",
|
||||||
|
.base64 = "Zm9v",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.raw = "foob",
|
||||||
|
.base64 = "Zm9vYg==",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.raw = "fooba",
|
||||||
|
.base64 = "Zm9vYmE=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.raw = "foobar",
|
||||||
|
.base64 = "Zm9vYmFy",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
result = base64_encode(ctx, text_4, strlen(text_4));
|
static const unsigned char bin_data[] = {
|
||||||
if (result == NULL) return 2;
|
0xda, 0xa8, 0x81, 0x80, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
|
||||||
if (strlen(result) != strlen(base64_4)) return 1;
|
0x0c, 0x5f, 0x78, 0x6d, 0x70, 0x70, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e,
|
||||||
if (strncmp(base64_4, result, strlen(base64_4))) return 1;
|
0x74, 0x04, 0x5f, 0x74, 0x63, 0x70, 0x05, 0x67, 0x6d, 0x61, 0x69, 0x6c,
|
||||||
xmpp_free(ctx,result);
|
0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x21, 0x00, 0x01, 0xc0, 0x0c, 0x00,
|
||||||
|
0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00,
|
||||||
|
0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x31, 0x04, 0x78, 0x6d, 0x70,
|
||||||
|
0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
|
||||||
|
0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02,
|
||||||
|
0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61, 0x6c,
|
||||||
|
0x74, 0x34, 0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f,
|
||||||
|
0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00,
|
||||||
|
0x21, 0x00, 0x01, 0x00, 0x00, 0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00,
|
||||||
|
0x00, 0x14, 0x66, 0x04, 0x61, 0x6c, 0x74, 0x32, 0x04, 0x78, 0x6d, 0x70,
|
||||||
|
0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
|
||||||
|
0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x02,
|
||||||
|
0x43, 0x00, 0x19, 0x00, 0x05, 0x00, 0x00, 0x14, 0x66, 0x04, 0x78, 0x6d,
|
||||||
|
0x70, 0x70, 0x01, 0x6c, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
|
||||||
|
0x63, 0x6f, 0x6d, 0x00, 0xc0, 0x0c, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00,
|
||||||
|
0x02, 0x43, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x00, 0x14, 0x66, 0x04, 0x61,
|
||||||
|
0x6c, 0x74, 0x33, 0x04, 0x78, 0x6d, 0x70, 0x70, 0x01, 0x6c, 0x06, 0x67,
|
||||||
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
result = base64_encode(ctx, text_5, strlen(text_5));
|
int main()
|
||||||
if (result == NULL) return 2;
|
|
||||||
if (strlen(result) != strlen(base64_5)) return 1;
|
|
||||||
if (strncmp(base64_5, result, strlen(base64_5))) return 1;
|
|
||||||
xmpp_free(ctx,result);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_decode(xmpp_ctx_t *ctx)
|
|
||||||
{
|
|
||||||
unsigned char *result;
|
|
||||||
|
|
||||||
result = base64_decode(ctx, base64_1, strlen(base64_1));
|
|
||||||
if (result == NULL) return 2;
|
|
||||||
if (strlen(result) != strlen(text_1)) return 1;
|
|
||||||
if (strncmp(text_1, result, strlen(text_1))) return 1;
|
|
||||||
xmpp_free(ctx,result);
|
|
||||||
|
|
||||||
result = base64_decode(ctx, base64_2, strlen(base64_2));
|
|
||||||
if (result == NULL) return 2;
|
|
||||||
if (strlen(result) != strlen(text_2)) return 1;
|
|
||||||
if (strncmp(text_2, result, strlen(text_2))) return 1;
|
|
||||||
xmpp_free(ctx,result);
|
|
||||||
|
|
||||||
result = base64_decode(ctx, base64_3, strlen(base64_3));
|
|
||||||
if (result == NULL) return 2;
|
|
||||||
if (strlen(result) != strlen(text_3)) return 1;
|
|
||||||
if (strncmp(text_3, result, strlen(text_3))) return 1;
|
|
||||||
xmpp_free(ctx,result);
|
|
||||||
|
|
||||||
result = base64_decode(ctx, base64_4, strlen(base64_4));
|
|
||||||
if (result == NULL) return 2;
|
|
||||||
if (strlen(result) != strlen(text_4)) return 1;
|
|
||||||
if (strncmp(text_4, result, strlen(text_4))) return 1;
|
|
||||||
xmpp_free(ctx,result);
|
|
||||||
|
|
||||||
result = base64_decode(ctx, base64_5, strlen(base64_5));
|
|
||||||
if (result == NULL) return 2;
|
|
||||||
if (strlen(result) != strlen(text_5)) return 1;
|
|
||||||
if (strncmp(text_5, result, strlen(text_5))) return 1;
|
|
||||||
xmpp_free(ctx,result);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
{
|
||||||
xmpp_ctx_t *ctx;
|
xmpp_ctx_t *ctx;
|
||||||
int ret;
|
unsigned char *udec;
|
||||||
|
char *dec;
|
||||||
|
char *enc;
|
||||||
|
size_t len, i;
|
||||||
|
|
||||||
|
printf("BASE64 tests.\n");
|
||||||
|
|
||||||
printf("allocating context... ");
|
|
||||||
ctx = xmpp_ctx_new(NULL, NULL);
|
ctx = xmpp_ctx_new(NULL, NULL);
|
||||||
if (ctx == NULL) printf("failed to create context\n");
|
if (ctx == NULL) {
|
||||||
if (ctx == NULL) return -1;
|
fprintf(stderr, "failed to create context\n");
|
||||||
printf("ok.\n");
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
printf("testing encode... ");
|
for (i = 0; i < ARRAY_SIZE(tests); ++i) {
|
||||||
ret = test_encode(ctx);
|
printf("Test #%d: ", (int)i + 1);
|
||||||
if (ret) printf("base64_encode failed!\n");
|
enc = xmpp_base64_encode(ctx, (unsigned char *)tests[i].raw,
|
||||||
if (ret) return ret;
|
strlen(tests[i].raw));
|
||||||
printf("ok.\n");
|
assert(enc != NULL);
|
||||||
|
COMPARE(tests[i].base64, enc);
|
||||||
|
xmpp_free(ctx, enc);
|
||||||
|
|
||||||
printf("testing decode... ");
|
dec = xmpp_base64_decode_str(ctx, tests[i].base64,
|
||||||
ret = test_decode(ctx);
|
strlen(tests[i].base64));
|
||||||
if (ret) printf("base64_decode failed!\n");
|
assert(dec != NULL);
|
||||||
if (ret) return ret;
|
COMPARE_BUF(tests[i].raw, strlen(tests[i].raw), dec, strlen(dec));
|
||||||
printf("ok.\n");
|
xmpp_free(ctx, dec);
|
||||||
|
printf("ok\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Test with binary data: ");
|
||||||
|
enc = xmpp_base64_encode(ctx, bin_data, sizeof(bin_data));
|
||||||
|
assert(enc != NULL);
|
||||||
|
xmpp_base64_decode_bin(ctx, enc, strlen(enc), &udec, &len);
|
||||||
|
assert(udec != NULL);
|
||||||
|
assert(len != 0);
|
||||||
|
assert(len == sizeof(bin_data));
|
||||||
|
COMPARE_BUF(bin_data, sizeof(bin_data), udec, len);
|
||||||
|
xmpp_free(ctx, udec);
|
||||||
|
xmpp_free(ctx, enc);
|
||||||
|
printf("ok\n");
|
||||||
|
|
||||||
printf("freeing context... ");
|
|
||||||
xmpp_ctx_free(ctx);
|
xmpp_ctx_free(ctx);
|
||||||
printf("ok.\n");
|
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user