Compare commits
613 Commits
Author | SHA1 | Date |
---|---|---|
jacob.eva | 2d2ea29a20 | |
Badlop | 7ec5336a4a | |
Badlop | f4ea11b0b6 | |
Badlop | 32451219ed | |
Badlop | b0b62f509c | |
Badlop | 0bccf45c0e | |
Badlop | dcc59aef75 | |
Badlop | 323b53d125 | |
Badlop | b607d97331 | |
Paweł Chmielowski | 8a91598b04 | |
Badlop | 4106ae484c | |
Badlop | 0db49856b8 | |
Badlop | 232fc5e31f | |
Badlop | 26e7ce3003 | |
Badlop | bc43e7e750 | |
Badlop | 1469e7c76f | |
Badlop | a37f44ed20 | |
Badlop | f2497c3146 | |
badlop | 7e11adc32a | |
Roman Hargrave | 1a54cf7e52 | |
Roman Hargrave | 961060a284 | |
Roman Hargrave | 5c7ec06224 | |
Roman Hargrave | 7c58c7aa0a | |
Badlop | d536707e4f | |
Roman Hargrave | 83690375cb | |
Roman Hargrave | a91e9fa276 | |
badlop | bad48b0d6a | |
Mujtaba Roohani | eea2a5d257 | |
Mujtaba Roohani | 10ffbc08df | |
Mujtaba Roohani | af928e35cb | |
Badlop | 037c3749f3 | |
Badlop | 35d05f2925 | |
Badlop | 6adfc2cec5 | |
Badlop | 90d9ef3a5d | |
Badlop | f48adc2ea0 | |
Badlop | 3293ba1cfe | |
Badlop | 5b1402f033 | |
Badlop | 12e31364f6 | |
Badlop | f3e03b9729 | |
Badlop | 2bf5a362ce | |
Badlop | b71cbeb48a | |
Badlop | 241df9ac97 | |
Badlop | 8eb58060f8 | |
Badlop | b3c8d25104 | |
Badlop | 04f4489427 | |
Badlop | 7d4e1ad933 | |
Badlop | d2557111af | |
Badlop | 813a93faff | |
Badlop | fff94c83e7 | |
Badlop | 6be46dacc9 | |
Badlop | b96efd7df2 | |
Badlop | 6062f6a614 | |
Badlop | c7e8208879 | |
Badlop | 2ee0a514a5 | |
Badlop | e76b0877d6 | |
Badlop | 8499366bcc | |
Badlop | cc4702882f | |
Badlop | 10b1a83bbf | |
Badlop | dd4d5e698a | |
Badlop | 8fd6fabdbb | |
Badlop | 6a4618d3d3 | |
Badlop | f672262950 | |
Badlop | 9ac123ff56 | |
Badlop | 2612f1ead1 | |
Badlop | 7e2fe1b789 | |
Badlop | 3d523cec45 | |
Badlop | 03cd4de6b0 | |
Badlop | 3b116414db | |
Mickaël Rémond | c2efdc1950 | |
Badlop | a41df9c914 | |
Badlop | 0da3afbeeb | |
Badlop | c9084fae66 | |
Badlop | 4aa09c552c | |
Badlop | f72aadd88d | |
Badlop | 622233eaf6 | |
Badlop | 97d796cb00 | |
Badlop | a95e657a21 | |
Badlop | 664531f1ef | |
Badlop | 80bae05721 | |
Badlop | 5b44231a37 | |
Badlop | 2e60ec7079 | |
Badlop | 1096159be1 | |
Badlop | 37e44168f6 | |
Badlop | eac6bb36de | |
Badlop | dcf8189cb0 | |
Badlop | 65a37864ff | |
Badlop | 4be0998c6e | |
Badlop | f2b3151f0b | |
Badlop | f51ac06ecd | |
Badlop | c031269003 | |
Badlop | f8df7b0a60 | |
badlop | bf08312336 | |
badlop | 3374984b28 | |
Badlop | 00e3fa97fc | |
Badlop | 2aea4659cc | |
Badlop | 4cde5d84f2 | |
Badlop | 5e6eb2738a | |
Badlop | a6b3ceccbf | |
Badlop | f46182b238 | |
badlop | dd4c264b33 | |
Sonny Piers | d7d054cf05 | |
Badlop | cfea844cec | |
Badlop | 21d78a74c1 | |
Badlop | 05a3c87239 | |
Badlop | 68c8116102 | |
Badlop | 5fdcb0a6e3 | |
Badlop | 6220d58e43 | |
Badlop | 2fcf924747 | |
Badlop | 9d51b80051 | |
Badlop | f32bb792bc | |
Badlop | 0ab2007a3a | |
Evgeny Khramtsov | 75129ec0bc | |
Holger Weiss | 20fb74c363 | |
Badlop | 20ba631f3e | |
Badlop | b7222c8828 | |
Badlop | b8c42a4766 | |
Badlop | 857d350a71 | |
Badlop | 4753268b5f | |
Badlop | a4465fbeaa | |
Holger Weiss | f2faca6dc9 | |
Holger Weiss | b410f64f33 | |
Holger Weiss | 7c1b83941c | |
Holger Weiss | 2a7ae832a8 | |
Holger Weiss | 4af4244975 | |
Evgeny Khramtsov | 9404980f0d | |
Badlop | 0db6868aa6 | |
Mickaël Rémond | fc2dfb0f1d | |
Holger Weiss | 48f5cc3b88 | |
Holger Weiss | 6bf9a45c93 | |
Holger Weiss | 4cb537c693 | |
Holger Weiss | 5f612b7dab | |
Holger Weiss | 694b45474c | |
Badlop | b07766ab93 | |
Holger Weiss | 689f526cea | |
Holger Weiss | 34bf1a7d6c | |
Holger Weiss | 0bf94ba446 | |
Holger Weiss | 92fadcdf4e | |
Holger Weiss | fe121057f2 | |
Holger Weiss | cb0a14c89a | |
Holger Weiss | 4476c95f14 | |
Holger Weiss | a0a9aaa1e0 | |
Holger Weiss | 4e15dcbd7d | |
Holger Weiss | 6fc4fccfbb | |
Holger Weiss | 362c866862 | |
Holger Weiss | e26ea15d7f | |
Licaon_Kter | 86f90153c4 | |
Holger Weiss | c1591030b0 | |
Holger Weiss | 4787e890af | |
Holger Weiss | 05a5f002a3 | |
Holger Weiss | e6249c41e2 | |
Holger Weiss | 6a877d7018 | |
Holger Weiss | b5daceaac2 | |
Holger Weiss | f459a7e57c | |
Holger Weiss | 8f63b2cbae | |
Holger Weiss | 83117240d2 | |
Holger Weiss | d60fa5e9b3 | |
Badlop | 009d25f0bd | |
Badlop | bae1cdc52e | |
Badlop | d1066f6644 | |
Badlop | ebfaf70df4 | |
Badlop | eb39a4f3af | |
Badlop | 2be66bb69f | |
Badlop | 5c94639bf2 | |
Holger Weiss | cfad261cf4 | |
Badlop | 774cc46481 | |
Badlop | 18be432fdf | |
Badlop | 1fcf7b5cb1 | |
Badlop | a43bc6f794 | |
Badlop | 1a6284efa9 | |
Badlop | 95ef06b768 | |
Holger Weiss | 954fb94017 | |
Badlop | 4ebf99a1d1 | |
Badlop | 97de583107 | |
Badlop | 8978f38c22 | |
Badlop | 2627182cdb | |
Badlop | b130567e16 | |
Badlop | f20d6239db | |
Holger Weiss | b7b6af8315 | |
Badlop | 7191ddd6f3 | |
Badlop | e138359c7f | |
Evgeny Khramtsov | 562fb1640d | |
ytkang | 8045f4c60e | |
Paweł Chmielowski | 017ce87198 | |
Badlop | 06dbe80c65 | |
Evgeniy Khramtsov | acc32abf17 | |
Evgeniy Khramtsov | de4e42c20d | |
Evgeniy Khramtsov | 65575478a3 | |
Badlop | 721ca125ea | |
Holger Weiss | cd54f70943 | |
Holger Weiss | fa5c601325 | |
Holger Weiss | d37bb1cd2d | |
Holger Weiss | 435d0bcc3d | |
Holger Weiss | 9c3943b703 | |
Holger Weiss | 450f2eb5f4 | |
Holger Weiss | ef7c92233d | |
Holger Weiss | 79a804ac3b | |
Holger Weiss | 7d2a7aa418 | |
Holger Weiss | 89c5bc0546 | |
Holger Weiss | d783cc7cca | |
Holger Weiss | d84204bdb8 | |
Badlop | 494ba6f941 | |
Badlop | 9fe5f383dc | |
Badlop | fc40a38780 | |
Badlop | 9a14b7f7f7 | |
Badlop | 74e068d91c | |
Badlop | aa6519209e | |
Badlop | 4d3acc1637 | |
Badlop | e99866d835 | |
Badlop | babc6213f9 | |
Badlop | 156331c123 | |
Evgeniy Khramtsov | 6a4b9a08d5 | |
Badlop | 6780874893 | |
badlop | 91b9bd9973 | |
Tom Quackenbush | 8e1263c328 | |
Badlop | ec3c13cade | |
Badlop | 18d1a5d60d | |
Badlop | 2a95a53285 | |
Badlop | ce89412feb | |
Badlop | 00e76aec2c | |
Badlop | bd57bdb3ae | |
Badlop | 5359c2a0dd | |
Evgeny Khramtsov | 008a8bc70a | |
Abdulrhman A Alkhodiry | 3d6901efe2 | |
Badlop | 511f30a751 | |
Badlop | 36644dc78c | |
Badlop | 1f992d161a | |
Badlop | e93af70b57 | |
Badlop | fc901d9dea | |
Evgeniy Khramtsov | 3dc4494ee1 | |
badlop | da878bd75c | |
Badlop | df79a848f6 | |
Tom Quackenbush | 3faa47df45 | |
Tom Quackenbush | 78bb06c6aa | |
Tom Quackenbush | 707321e0e0 | |
Tom Quackenbush | a190156d0a | |
Tom Quackenbush | 83b32e7e4f | |
Tom Quackenbush | 5662e1c530 | |
Badlop | 4013a68554 | |
Evgeny Khramtsov | b63aa9c326 | |
Jonathan Gueron | ebd2671963 | |
Badlop | 65f4267fc1 | |
Tom Quackenbush | 29b4b5ae30 | |
Tom Quackenbush | 06c96ad778 | |
Tom Quackenbush | 9359ce3b75 | |
Tom Quackenbush | 610c0e72eb | |
Tom Quackenbush | 265ff3dc70 | |
Badlop | 41371cf7fe | |
Badlop | 065ab08592 | |
badlop | d3f0218dc4 | |
xmppjingle | 0aa5516e04 | |
badlop | d7513f5a00 | |
Tom Quackenbush | 612b2b78e9 | |
xmppjingle | ed3853ccf9 | |
xmppjingle | 098a810843 | |
xmppjingle | e2ebd1a405 | |
badlop | b8ed8af10e | |
badlop | f52b98c068 | |
Badlop | f806442b9c | |
Tom Quackenbush | 779024b9c5 | |
Tom Quackenbush | 3a03dde3c0 | |
Mark Tran | 9eefebadc2 | |
Tom Quackenbush | 3a3a3f1db1 | |
Tom Quackenbush | 1b86f7ccaa | |
badlop | bb8802a70a | |
Tom Quackenbush | 6c22e0fa0c | |
Badlop | d7ce81cd22 | |
Badlop | 0b5ad7a994 | |
Holger Weiss | 04d2234e0a | |
Badlop | 7429577b7a | |
Badlop | 9374ed0ffc | |
Badlop | b45fd21340 | |
Badlop | 5fc2b410a2 | |
Paweł Chmielowski | 7f75aa0372 | |
Holger Weiss | 32fd4c4d9c | |
Holger Weiss | 02759c6aae | |
Holger Weiss | 39cf5a0fd7 | |
Holger Weiss | 512255d98b | |
Holger Weiss | c5daf56385 | |
Holger Weiss | 04cc600772 | |
Holger Weiss | 4165428780 | |
Holger Weiss | b7414f8837 | |
Holger Weiss | c9039a5ad2 | |
badlop | 8c1df80794 | |
Holger Weiss | 44ffde7eda | |
Andrew B | e2939a0353 | |
Mickaël Rémond | e9a014a6cb | |
Badlop | 8c86849e86 | |
Holger Weiss | d73b648cf3 | |
Matthias Rieber | 4deb2fd2c3 | |
Holger Weiss | 1696107f43 | |
Holger Weiss | d4d8b15413 | |
Holger Weiss | 99d89ce59d | |
Holger Weiss | ce64f79b63 | |
Holger Weiss | ddf70d60df | |
Holger Weiss | f2f8fe25f7 | |
Holger Weiss | b6f5273228 | |
Holger Weiss | ea4964dccd | |
Holger Weiss | 0a24936e92 | |
Holger Weiss | 0cfd521b7a | |
Holger Weiss | 8c470c6a01 | |
Holger Weiss | 73e945fd1d | |
Holger Weiss | 1ee63657ac | |
Holger Weiss | 9cd4e405c8 | |
Holger Weiss | 5359525d3c | |
Holger Weiss | d46ee127e7 | |
Holger Weiss | 0b4e0e720e | |
Holger Weiss | 08f7291a24 | |
Holger Weiss | d29795fb24 | |
Holger Weiss | f6b66cd130 | |
Badlop | 0d2fa84c8a | |
Paweł Chmielowski | ecf8db140d | |
Kévin Dunglas | 5ccefb0e9e | |
Leon-SFS | 55f08f9b43 | |
Holger Weiss | 195daf9fa1 | |
Holger Weiss | 567e19728c | |
Holger Weiss | 1ee52e2e42 | |
Holger Weiss | c173ae9f36 | |
Holger Weiss | 4b0b66cfbf | |
Holger Weiss | 9337b3ea39 | |
Holger Weiss | dd34fc63c8 | |
Holger Weiss | 1ff5cbc467 | |
Holger Weiss | 4936e82bd1 | |
Holger Weiss | eeba72cd4c | |
Holger Weiss | 655f195e9d | |
Holger Weiss | 3652dd1796 | |
Holger Weiss | b88e766571 | |
Holger Weiss | d4d5200c5b | |
Holger Weiss | e5eef02b25 | |
Holger Weiss | c37387984a | |
Holger Weiss | 511f0fa03d | |
Holger Weiss | 157fd94c32 | |
Holger Weiss | 7c61eef522 | |
Holger Weiss | 8a849069ec | |
Holger Weiss | 14c3b13a11 | |
Badlop | 5964ad412e | |
Holger Weiss | 6dc35e8fcb | |
badlop | 9aa4cd41fe | |
Badlop | 0451576fcb | |
Badlop | 0cade69713 | |
Holger Weiss | a1fe316565 | |
Holger Weiss | f58db83523 | |
Holger Weiss | a36515d74d | |
Holger Weiss | de035cefff | |
Michael Ansel | e2a85ef9ad | |
Badlop | ce4c11f5fe | |
Badlop | 2cfe253b9b | |
Badlop | e5d66210f0 | |
Holger Weiss | 6ca6f021cc | |
Holger Weiss | 645d29a264 | |
Holger Weiss | 5142174b98 | |
Holger Weiss | a6c7dc9b8a | |
Holger Weiss | f3bf402e7a | |
Badlop | 31f19239d7 | |
Holger Weiss | c12e225e88 | |
Badlop | 3e4f4baeca | |
Holger Weiss | 96adf84c6f | |
Holger Weiss | 9eb68b4ef3 | |
Holger Weiss | 10092e3bd5 | |
Holger Weiss | d45ec798ee | |
Holger Weiss | 12274a2adc | |
Holger Weiss | 19044ccb85 | |
Holger Weiss | 06def469a4 | |
Holger Weiss | 81e8404207 | |
Holger Weiss | 11fc485a9c | |
Holger Weiss | f7beaa3ef1 | |
Holger Weiss | 958b428ff0 | |
Holger Weiss | a00090086e | |
Holger Weiss | 374c1b6dfc | |
Holger Weiss | 13e8c857ce | |
Holger Weiss | 6db50940a1 | |
Holger Weiss | f4950cdc83 | |
Holger Weiss | b9ece5c953 | |
Holger Weiss | a17a554a8c | |
Holger Weiss | 216e244b6a | |
Holger Weiss | 034a156066 | |
Holger Weiss | 69ec20b3b9 | |
Holger Weiss | 6316b0f097 | |
Holger Weiss | 26e45fa0b0 | |
Badlop | 21ffae911c | |
Holger Weiss | 941ed7c21a | |
Badlop | 5d6f6b820f | |
Badlop | 7d0f6caa33 | |
Badlop | 25a63c6ed1 | |
Badlop | 4e5fac304a | |
Holger Weiss | 7cba0f88e8 | |
Holger Weiss | 1c79c86168 | |
Holger Weiss | 638019ec58 | |
Holger Weiss | fb98a62bce | |
Badlop | 680d541efa | |
Holger Weiss | 16edbd5e7a | |
badlop | 2a953da2fb | |
Badlop | 6d655af0c4 | |
Badlop | db91c97614 | |
Badlop | ff61b1a2e1 | |
Holger Weiss | 33ec5968d7 | |
Holger Weiss | 32043a80eb | |
Holger Weiss | 94a439726b | |
Holger Weiss | 00267eeb71 | |
Holger Weiss | 746d198cc3 | |
Holger Weiss | 5363043402 | |
Holger Weiss | 564691c7a6 | |
Holger Weiss | 932987447d | |
Holger Weiss | 76c6d17d4b | |
Badlop | 89e0a70e44 | |
Badlop | 98ab39f962 | |
badlop | be350628c7 | |
Santiago26 | 2852dee33f | |
Christophe Romain | 9eca69d092 | |
Christophe Romain | e5336f02db | |
Badlop | 97834d2483 | |
Badlop | c8dca7f0a5 | |
Holger Weiss | eb52291ca1 | |
Holger Weiss | 73b984f193 | |
Christophe Romain | 362a896e6a | |
Christophe Romain | 6f7b5ccba0 | |
Badlop | fa47490da4 | |
Badlop | c098a763b0 | |
Holger Weiss | 176d2e68ca | |
Badlop | 5ab060ef72 | |
Badlop | 65007779f7 | |
Badlop | 6e150caa5e | |
Paweł Chmielowski | 5a32309eaa | |
Badlop | c30e7d7f7b | |
Badlop | 9379f73feb | |
Mickaël Rémond | cbea31ab68 | |
Christophe Romain | e2f860fd9f | |
Badlop | bdfe82ce79 | |
Christophe Romain | a20187ed70 | |
Christophe Romain | 7870f77392 | |
Christophe Romain | 9330892787 | |
Badlop | f4fb0d7c7a | |
Badlop | e87dee1a00 | |
Badlop | e4c5172b22 | |
Christophe Romain | 416e203e66 | |
Mickaël Rémond | e209c00f26 | |
Holger Weiss | 7ca2ae6947 | |
Christophe Romain | f5677e7033 | |
Christophe Romain | 4c68f4421b | |
Holger Weiss | 94c690661b | |
Holger Weiss | bc7bff10bd | |
badlop | ac36d6c6dc | |
Krzysztof Grochocki | 0b4a282f24 | |
Mickaël Rémond | af33df5594 | |
Badlop | ff3cddee70 | |
Holger Weiss | 15ddd00ad4 | |
Holger Weiss | 8b1eead24b | |
Holger Weiss | 48966d43a9 | |
Holger Weiss | 6b4d521bb1 | |
badlop | f749c91e36 | |
Mickaël Rémond | 87cb689298 | |
Ben Langfeld | 66b9d491b9 | |
Ben Langfeld | 7baf2edbc2 | |
Badlop | 53ca8bafba | |
badlop | c3310aa417 | |
Mickaël Rémond | ba2790eca8 | |
HAMANO Tsukasa | 44aacf2ee4 | |
Holger Weiss | d1fe26dd17 | |
Badlop | e9f5cb2c14 | |
Badlop | 537706e9da | |
Mickaël Rémond | 0b88690212 | |
Christophe Romain | d40808bd57 | |
Christophe Romain | f7afc783d1 | |
badlop | 53c20de211 | |
badlop | 0785f8c767 | |
Holger Weiss | 24dd1428c5 | |
Holger Weiss | 6d38be2a75 | |
Holger Weiss | 4db159b4fa | |
Holger Weiss | 7e9acda1c7 | |
Holger Weiss | 185fd7bb92 | |
Badlop | 8e157279ca | |
HAMANO Tsukasa | 92914e09db | |
Holger Weiss | 43add0866e | |
Holger Weiss | 741a1de374 | |
Holger Weiss | d6181eefed | |
Holger Weiss | 8208d5d172 | |
Holger Weiss | 214339814f | |
Holger Weiss | f445f6aa31 | |
Holger Weiss | 4b82d830c4 | |
Holger Weiss | 0ba0639ec5 | |
Holger Weiss | bf99a0a97a | |
colm | 0bcbce87a7 | |
Holger Weiss | d430788865 | |
Holger Weiss | c825f6deea | |
badlop | 289f2f2b1d | |
Zach Goldberg | 566b78849e | |
Zach Goldberg | aecd0e7deb | |
Holger Weiss | d662bbe783 | |
Holger Weiss | 671655eaee | |
Badlop | 078ba3ace6 | |
badlop | 4fafcbf93d | |
Mickaël Rémond | bd0dc5ab9d | |
Mickaël Rémond | a16f199473 | |
Mickaël Rémond | ab85f6a6e4 | |
Mickaël Rémond | f41fb570d6 | |
Mickaël Rémond | ce8f97b55f | |
Mickaël Rémond | b9357194a3 | |
HAMANO Tsukasa | feac3b1d61 | |
Holger Weiss | f0b54e1e1a | |
Holger Weiss | de67fa6adc | |
Holger Weiß | 76d4143d45 | |
Holger Weiss | d3b4bc817d | |
Holger Weiss | 1f8631e7cf | |
Badlop | 6499187102 | |
Badlop | 45824cf82b | |
Badlop | cadc841927 | |
Badlop | 4b117b5fe5 | |
Badlop | 350d4d3884 | |
Badlop | 2b7bbd9535 | |
Badlop | edb67df064 | |
Badlop | 44f709ba38 | |
badlop | b7a7d9ae16 | |
HAMANO Tsukasa | 6a4e88d8d7 | |
badlop | fb643dfb10 | |
Holger Weiss | 84044c290b | |
Holger Weiss | 44464c0f8d | |
Badlop | bdac50591a | |
Badlop | 5eafb8188a | |
Badlop | 4f3a724927 | |
Badlop | 2589d6ec6f | |
Badlop | 632c05e700 | |
badlop | b8120ee64e | |
badlop | 503ce9e3ba | |
badlop | 9eca3305ba | |
Tim Stewart | 41e5fd5f0e | |
Holger Weiss | 0dfb0b9c6b | |
Ben Klang | b897489dab | |
badlop | 8b3a3b47a4 | |
Holger Weiss | 34b28d4689 | |
Holger Weiss | ddae2cd8dd | |
Holger Weiss | bb18e9f04b | |
badlop | a1eb77b91a | |
Holger Weiss | 26f332a8bc | |
badlop | ccf95a6fc8 | |
Holger Weiss | 07d5e77cc6 | |
badlop | 750170f27d | |
Mathias Ertl | b710783208 | |
Mathias Ertl | 142ab9d477 | |
Mathias Ertl | 7aa2808cda | |
Mathias Ertl | 9bf84ad5f7 | |
Mathias Ertl | ba56d73077 | |
Mathias Ertl | de820d073f | |
Mathias Ertl | c62daaeeef | |
Badlop | 198f14a7c0 | |
badlop | ba64c794c7 | |
Holger Weiss | e23bf94c57 | |
badlop | 73b7c2e81a | |
badlop | 474bb63209 | |
badlop | ec2537352b | |
badlop | 72701df745 | |
Holger Weiss | d47310d239 | |
Holger Weiss | 4320c9c898 | |
Holger Weiss | b019764868 | |
badlop | a10d580021 | |
Holger Weiss | 79151528d9 | |
Holger Weiss | 471b1e41e3 | |
Holger Weiss | 97b4735da0 | |
Holger Weiss | dc30f205fe | |
Badlop | c1debbd48b | |
Badlop | ab0220ae1d | |
badlop | 40459ec650 | |
badlop | 3c7f583a0e | |
pyromania | 9c2a2c44e1 | |
pyromania | f4963a26e7 | |
Sonny Scroggin | 8945540dc9 | |
Sonny Scroggin | 3a61f008ac | |
badlop | ea1025879d | |
badlop | 52878eaff2 | |
Holger Weiss | 33dfbf1ef6 | |
Holger Weiss | 3ba48bac80 | |
Holger Weiss | 3f68bb65b9 | |
badlop | cf60eef409 | |
badlop | b49f5f9adf | |
Antonio Murdaca | 1a17e97873 | |
Badlop | 09775d41db | |
Sonny Scroggin | b77645eecc | |
Badlop | f7c243ab4f | |
badlop | 341c8531be | |
Badlop | a8fba1ca30 | |
Sonny Scroggin | 73c553be75 | |
badlop | c8dfced84c | |
Badlop | 911eb5b199 | |
Sonny Scroggin | 73e6b7ed22 | |
Sonny Scroggin | 9a30d2d022 | |
Sonny Scroggin | b194fd5c99 | |
badlop | 1ae6491e75 | |
Rahul Gautam | bcf5fb4d64 | |
badlop | 66d414d3a4 | |
Badlop | b3d422af15 | |
Sonny Scroggin | 017cc725bb | |
Sonny Scroggin | 84d50e974e | |
Badlop | 2492bc46d0 | |
Badlop | 036076d373 | |
Badlop | 18c9d87206 | |
Badlop | d98d55e1f7 | |
Badlop | 429f3e2ced | |
Badlop | 977f043a6a | |
badlop | 9180e814bc | |
Sonny Scroggin | 2d20f1847c | |
Badlop | 525e071422 | |
Badlop | 4f1aa9a0a4 | |
Badlop | a5ae331263 | |
Badlop | 9c2bee6980 | |
Badlop | 13a2e2d973 | |
Badlop | 90a72ea9fd | |
Badlop | 9dcb9b2364 | |
Badlop | dc4a7583c0 | |
badlop | 587f495af5 | |
Paul Donohue | da98b215a9 | |
Badlop | c331365b97 | |
badlop | 203c316300 | |
Paul Donohue | b87effe882 | |
Badlop | a380488235 | |
Badlop | 6d7c241a6f |
|
@ -0,0 +1,162 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '**.spec'
|
||||||
|
- '**.txt'
|
||||||
|
- '*/conf/*.yml'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
- '**.spec'
|
||||||
|
- '**.txt'
|
||||||
|
- '*/conf/*.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
tests:
|
||||||
|
name: Tests
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
otp: ['21.3', '25.3']
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: erlang:${{ matrix.otp }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout ejabberd
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: processone/ejabberd
|
||||||
|
|
||||||
|
- name: Checkout ejabberd-contrib
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: .ejabberd-modules/sources/ejabberd-contrib
|
||||||
|
|
||||||
|
- name: Get a compatible Rebar3
|
||||||
|
if: matrix.otp <= '21.3'
|
||||||
|
run: |
|
||||||
|
rm rebar3
|
||||||
|
wget https://github.com/processone/ejabberd/raw/21.12/rebar3
|
||||||
|
chmod +x rebar3
|
||||||
|
|
||||||
|
- name: Prepare libraries
|
||||||
|
run: |
|
||||||
|
apt-get -qq update
|
||||||
|
apt-get -y purge libgd3 nginx
|
||||||
|
apt-get -qq install libexpat1-dev libgd-dev libpam0g-dev \
|
||||||
|
libsqlite3-dev libwebp-dev libyaml-dev
|
||||||
|
|
||||||
|
- name: Prepare rebar
|
||||||
|
id: rebar
|
||||||
|
run: |
|
||||||
|
echo '{xref_ignores, [{eldap_filter_yecc, return_error, 2},
|
||||||
|
{fusco_lib, split_credentials, 1},
|
||||||
|
{http_uri, encode, 1},
|
||||||
|
{http_uri, decode, 1}
|
||||||
|
]}.' >>rebar.config
|
||||||
|
echo '{xref_checks, [deprecated_function_calls, deprecated_functions,
|
||||||
|
locals_not_used, undefined_function_calls, undefined_functions]}.
|
||||||
|
% Disabled: exports_not_used,' >>rebar.config
|
||||||
|
echo '{dialyzer, [{get_warnings, true}, {plt_extra_apps, [cache_tab,
|
||||||
|
eimp, epam, esip, ezlib, fast_tls, fast_xml, fast_yaml,
|
||||||
|
mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix,
|
||||||
|
sqlite3, stringprep, stun, xmpp, yconf]} ]}.' >>rebar.config
|
||||||
|
echo '{ct_extra_params, "-verbosity 20"}.' >>rebar.config
|
||||||
|
|
||||||
|
- name: Remove syntax_tools from release
|
||||||
|
run: sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script
|
||||||
|
|
||||||
|
- name: Cache rebar
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/rebar3/
|
||||||
|
key: ${{matrix.otp}}-${{hashFiles('rebar.config')}}
|
||||||
|
|
||||||
|
- name: Compile
|
||||||
|
run: |
|
||||||
|
./autogen.sh
|
||||||
|
./configure --with-rebar=./rebar3 \
|
||||||
|
--prefix=/tmp/ejabberd \
|
||||||
|
--enable-all \
|
||||||
|
--disable-elixir \
|
||||||
|
--disable-mssql \
|
||||||
|
--disable-odbc
|
||||||
|
make update
|
||||||
|
make
|
||||||
|
|
||||||
|
- name: Start ejabberd
|
||||||
|
run: |
|
||||||
|
echo "CONTRIB_MODULES_PATH=`pwd`/.ejabberd-modules" >> ejabberdctl.cfg.example
|
||||||
|
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||||
|
make dev
|
||||||
|
$CTL start
|
||||||
|
$CTL started
|
||||||
|
|
||||||
|
- name: Enable mod_muc_log
|
||||||
|
run: |
|
||||||
|
echo ' mod_muc_log: {}' >>.ejabberd-modules/sources/ejabberd-contrib/mod_muc_log_http/conf/mod_muc_log_http.yml
|
||||||
|
|
||||||
|
- name: Get list of available modules
|
||||||
|
run: |
|
||||||
|
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||||
|
$CTL modules_available | awk '{print $1}' | grep -v mod_captcha_rust >modules_available.txt
|
||||||
|
|
||||||
|
- name: Install modules
|
||||||
|
run: |
|
||||||
|
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||||
|
for i in `cat modules_available.txt` ; do
|
||||||
|
echo "Installing $i"
|
||||||
|
$CTL module_install $i
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Copy modules
|
||||||
|
run: |
|
||||||
|
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||||
|
for i in `cat modules_available.txt` ; do
|
||||||
|
echo "Copying from $i"
|
||||||
|
find .ejabberd-modules/sources/ejabberd-contrib/ -wholename "*/ejabberd-contrib/$i/include/*.hrl" -exec 'cp' '{}' 'include/' ';'
|
||||||
|
find .ejabberd-modules/sources/ejabberd-contrib/ -wholename "*/ejabberd-contrib/$i/src/*.erl" -exec 'cp' '{}' 'src/' ';'
|
||||||
|
find .ejabberd-modules/sources/ejabberd-contrib/ -wholename "*/ejabberd-contrib/$i/deps/*/ebin/*.beam" -exec 'cp' '{}' '_build/default/lib/ejabberd/ebin/' ';'
|
||||||
|
find .ejabberd-modules/sources/ejabberd-contrib/ -wholename "*/ejabberd-contrib/$i/deps/*/include/*.hrl" -exec 'cp' '{}' 'include/' ';'
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Uninstall modules
|
||||||
|
run: |
|
||||||
|
CTL=_build/dev/rel/ejabberd/bin/ejabberdctl
|
||||||
|
for i in `cat modules_available.txt` ; do
|
||||||
|
echo "Uninstalling $i"
|
||||||
|
$CTL module_uninstall $i
|
||||||
|
done
|
||||||
|
|
||||||
|
# This doesn't work right now, because epmd is in another path
|
||||||
|
# - run: ./ejabberdctl stop && ./ejabberdctl stopped
|
||||||
|
|
||||||
|
- run: make
|
||||||
|
- run: make hooks
|
||||||
|
- run: make options
|
||||||
|
- run: make xref
|
||||||
|
|
||||||
|
- name: Run Dialyzer
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
rm -rf _build/default/lib/ejabberd/ebin/fusco*
|
||||||
|
rm -rf _build/default/lib/ejabberd/ebin/observer_cli*
|
||||||
|
make dialyzer # Too many errors... first fix them, then enable this
|
||||||
|
|
||||||
|
- name: View logs dir
|
||||||
|
if: always()
|
||||||
|
run: ls -la _build/dev/rel/ejabberd/logs
|
||||||
|
- name: View ejabberd.log
|
||||||
|
if: always()
|
||||||
|
run: cat _build/dev/rel/ejabberd/logs/ejabberd.log
|
||||||
|
- name: View error.log
|
||||||
|
if: always()
|
||||||
|
run: cat _build/dev/rel/ejabberd/logs/error.log
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
*.beam
|
|
@ -0,0 +1,85 @@
|
||||||
|
ejabberd-contrib
|
||||||
|
================
|
||||||
|
|
||||||
|
This is a collaborative development area for ejabberd module developers
|
||||||
|
and users.
|
||||||
|
|
||||||
|
Those modules are not officially supported by ProcessOne.
|
||||||
|
|
||||||
|
For users
|
||||||
|
---------
|
||||||
|
|
||||||
|
To use an ejabberd module coming from this repository:
|
||||||
|
|
||||||
|
- You need to have ejabberd installed.
|
||||||
|
|
||||||
|
- If you have not already done it, run `ejabberdctl modules_update_specs`
|
||||||
|
to retrieve the list of available modules.
|
||||||
|
|
||||||
|
- Run `ejabberdctl module_install <module>` to get the source code and to
|
||||||
|
compile and install the `beam` file into ejabberd's module search path.
|
||||||
|
This path is either `~/.ejabberd-modules` or defined by the
|
||||||
|
`CONTRIB_MODULES_PATH` setting in `ejabberdctl.cfg`.
|
||||||
|
|
||||||
|
- Edit the configuration file provided in the `conf` directory of the
|
||||||
|
installed module and update it to your needs. Or, if you prefer so,
|
||||||
|
configure it in your main ejabberd configuration file.
|
||||||
|
|
||||||
|
- Run `ejabberdctl module_uninstall <module>` to remove a module from
|
||||||
|
ejabberd.
|
||||||
|
|
||||||
|
|
||||||
|
For developers
|
||||||
|
--------------
|
||||||
|
|
||||||
|
The following organization has been set up for the development:
|
||||||
|
|
||||||
|
- Development and compilation of modules is done by ejabberd. You need
|
||||||
|
ejabberd installed. Use `ejabberdctl module_check <module>` to ensure it
|
||||||
|
compiles correctly before committing your work. The sources of your
|
||||||
|
module must be located in `$CONTRIB_MODULES_PATH/sources/<module>`.
|
||||||
|
|
||||||
|
- Compilation can by done manually (if you know what you are doing) so you
|
||||||
|
don't need ejabberd running:
|
||||||
|
```
|
||||||
|
cd /path/of/module
|
||||||
|
mkdir ebin
|
||||||
|
/path/of/ejabberd's/erlc \
|
||||||
|
-o ebin \
|
||||||
|
-I include -I /path/of/ejabberd/lib/ejabberd-XX.YY/include \
|
||||||
|
-DLAGER -DNO_EXT_LIB \
|
||||||
|
src/*erl
|
||||||
|
```
|
||||||
|
|
||||||
|
- The module directory structure is usually the following:
|
||||||
|
* `README.txt`: Module description.
|
||||||
|
* `COPYING`: License for the module.
|
||||||
|
* `doc/`: Documentation directory.
|
||||||
|
* `src/`: Erlang source directory.
|
||||||
|
* `lib/`: Elixir source directory.
|
||||||
|
* `priv/msgs/`: Directory with translation files (pot, po and msg).
|
||||||
|
* `conf/<module>.yml`: Configuration for your module.
|
||||||
|
* `<module>.spec`: Yaml description file for your module.
|
||||||
|
|
||||||
|
- Module developers should note in the `README.txt` file whether the
|
||||||
|
module has requirements or known incompatibilities with other modules.
|
||||||
|
|
||||||
|
- If your module project contains several erlang modules, you should export a
|
||||||
|
function pre_uninstall/0 in the main one listing the other ones.
|
||||||
|
See mod_statsdx as an example.
|
||||||
|
|
||||||
|
|
||||||
|
Broken modules
|
||||||
|
--------------
|
||||||
|
|
||||||
|
This is the list of modules that are known to be broken with latest ejabberd master branch.
|
||||||
|
|
||||||
|
If you feel they are worth it, your help to fix them is welcome:
|
||||||
|
|
||||||
|
- atom_pubsub: "Provides access to all PEP nodes via an AtomPub interface."
|
||||||
|
- ircd: "This is an IRC server frontend to ejabberd."
|
||||||
|
- mod_archive: "Message Archiving (XEP-0136)."
|
||||||
|
- mod_irc: "IRC transport."
|
||||||
|
- mod_mam_mnesia: This feature got included in ejabberd 15.06
|
||||||
|
- mod_openid: "Transform the Jabber Server in an openid provider."
|
||||||
|
- mod_profile: "User Profile (XEP-0154) in Mnesia table."
|
60
README.txt
60
README.txt
|
@ -1,60 +0,0 @@
|
||||||
ejabberd-modules is a collaborative development area for ejabberd
|
|
||||||
modules developers and users.
|
|
||||||
|
|
||||||
|
|
||||||
For users
|
|
||||||
=========
|
|
||||||
|
|
||||||
You need to have Erlang installed.
|
|
||||||
|
|
||||||
To use an ejabberd module coming from this repository:
|
|
||||||
|
|
||||||
- Read the module specific README to see if special steps are needed
|
|
||||||
to deploy it.
|
|
||||||
|
|
||||||
- Run "./build.sh" or "build.bat" in the root (usually trunk
|
|
||||||
directory) of the wanted module.
|
|
||||||
|
|
||||||
- Copy generated .beam files from the ebin directory to the directory
|
|
||||||
where your ejabberd .beam files are.
|
|
||||||
|
|
||||||
- Use the configuration file examples provided in the conf dir to
|
|
||||||
update your ejabberd.cfg configuration file.
|
|
||||||
|
|
||||||
If during compilation of a module you get an error like:
|
|
||||||
{"init terminating in do_boot",{undef,[{make,all,[]},...
|
|
||||||
it means Erlang couldn't find its file make.beam
|
|
||||||
In Debian and other distributions you can try to install packages like:
|
|
||||||
erlang-dev erlang-nox erlang-tools
|
|
||||||
|
|
||||||
|
|
||||||
For developers
|
|
||||||
==============
|
|
||||||
|
|
||||||
The following organisation has been set-up for the development:
|
|
||||||
|
|
||||||
- Each module has its own SVN structure (trunk/branches/tags) to allow
|
|
||||||
independent versioning.
|
|
||||||
|
|
||||||
- Development and compilation of module should be possible without
|
|
||||||
ejabberd SVN, as long as developers check-out the ejabberd-dev
|
|
||||||
module. This module contains include file to make compilation
|
|
||||||
possible.
|
|
||||||
|
|
||||||
- The module directory structure is usually the following:
|
|
||||||
README.txt: Module description
|
|
||||||
LICENSE.txt: License for the module
|
|
||||||
Emakefile: Erlang makefile to build the module (preferred way, if no
|
|
||||||
dependencies on C code, as build will thus works on Windows)
|
|
||||||
doc/: Documentation dir
|
|
||||||
src/: Source directory
|
|
||||||
src/msgs/: Directory with translation files (pot, po and msg).
|
|
||||||
ebin/: empty (Target directory for the build).
|
|
||||||
conf/: Directory containing example configuration for your module.
|
|
||||||
build.sh: *nix build script.
|
|
||||||
build.bat: Windows build script.
|
|
||||||
|
|
||||||
- Module developers should put in the README if the module has
|
|
||||||
requirements or known incompatibilities with other modules (for
|
|
||||||
example, by modifying the same main ejabberd modules).
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
As a special exception, the authors give permission to link this program
|
As a special exception, the authors give permission to link this program
|
||||||
with the OpenSSL library and distribute the resulting binary.
|
with the OpenSSL library and distribute the resulting binary.
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Version 2, June 1991
|
Version 2, June 1991
|
||||||
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
Preamble
|
Preamble
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
The licenses for most software are designed to take away your
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
@ -18,7 +18,7 @@ software--to make sure the software is free for all its users. This
|
||||||
General Public License applies to most of the Free Software
|
General Public License applies to most of the Free Software
|
||||||
Foundation's software and to any other program whose authors commit to
|
Foundation's software and to any other program whose authors commit to
|
||||||
using it. (Some other Free Software Foundation software is covered by
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
the GNU Library General Public License instead.) You can apply it to
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
your programs, too.
|
your programs, too.
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
@ -58,8 +58,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
The precise terms and conditions for copying, distribution and
|
||||||
modification follow.
|
modification follow.
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
0. This License applies to any program or other work which contains
|
0. This License applies to any program or other work which contains
|
||||||
|
@ -113,7 +113,7 @@ above, provided that you also meet all of these conditions:
|
||||||
License. (Exception: if the Program itself is interactive but
|
License. (Exception: if the Program itself is interactive but
|
||||||
does not normally print such an announcement, your work based on
|
does not normally print such an announcement, your work based on
|
||||||
the Program is not required to print an announcement.)
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
These requirements apply to the modified work as a whole. If
|
||||||
identifiable sections of that work are not derived from the Program,
|
identifiable sections of that work are not derived from the Program,
|
||||||
and can be reasonably considered independent and separate works in
|
and can be reasonably considered independent and separate works in
|
||||||
|
@ -171,7 +171,7 @@ access to copy from a designated place, then offering equivalent
|
||||||
access to copy the source code from the same place counts as
|
access to copy the source code from the same place counts as
|
||||||
distribution of the source code, even though third parties are not
|
distribution of the source code, even though third parties are not
|
||||||
compelled to copy the source along with the object code.
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
4. You may not copy, modify, sublicense, or distribute the Program
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
except as expressly provided under this License. Any attempt
|
except as expressly provided under this License. Any attempt
|
||||||
otherwise to copy, modify, sublicense or distribute the Program is
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
@ -228,7 +228,7 @@ impose that choice.
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
This section is intended to make thoroughly clear what is believed to
|
||||||
be a consequence of the rest of this License.
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
8. If the distribution and/or use of the Program is restricted in
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
original copyright holder who places the Program under this License
|
original copyright holder who places the Program under this License
|
||||||
|
@ -258,7 +258,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||||
of preserving the free status of all derivatives of our free software and
|
of preserving the free status of all derivatives of our free software and
|
||||||
of promoting the sharing and reuse of software generally.
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
NO WARRANTY
|
NO WARRANTY
|
||||||
|
|
||||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
@ -280,9 +280,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
POSSIBILITY OF SUCH DAMAGES.
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
How to Apply These Terms to Your New Programs
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
If you develop a new program, and you want it to be of the greatest
|
If you develop a new program, and you want it to be of the greatest
|
||||||
possible use to the public, the best way to achieve this is to make it
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
@ -306,17 +306,16 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License along
|
||||||
along with this program; if not, write to the Free Software
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
If the program is interactive, make it output a short notice like this
|
If the program is interactive, make it output a short notice like this
|
||||||
when it starts in an interactive mode:
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
Gnomovision version 69, Copyright (C) year name of author
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
under certain conditions; type `show c' for details.
|
under certain conditions; type `show c' for details.
|
||||||
|
@ -339,5 +338,5 @@ necessary. Here is a sample; alter the names:
|
||||||
This General Public License does not permit incorporating your program into
|
This General Public License does not permit incorporating your program into
|
||||||
proprietary programs. If your program is a subroutine library, you may
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
consider it more useful to permit linking proprietary applications with the
|
consider it more useful to permit linking proprietary applications with the
|
||||||
library. If this is what you want to do, use the GNU Library General
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
Public License instead of this License.
|
Public License instead of this License.
|
|
@ -1,4 +0,0 @@
|
||||||
{'../ejabberd-dev/src/gen_mod', [{outdir, "../ejabberd-dev/ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
||||||
{'src/atom_microblog', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
||||||
{'src/atom_pubsub', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
||||||
{'src/mod_couch', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
|
@ -1,9 +1,20 @@
|
||||||
|
|
||||||
|
|
||||||
|
***************
|
||||||
|
PLEASE NOTE
|
||||||
|
***************
|
||||||
|
|
||||||
|
This module does NOT work
|
||||||
|
with ejabberd 13 or newer.
|
||||||
|
|
||||||
|
***************
|
||||||
|
|
||||||
|
|
||||||
atom_pubsub - the Atom PubSub tunnel
|
atom_pubsub - the Atom PubSub tunnel
|
||||||
|
|
||||||
Author: Eric Cestari <eric@ohmforce.com> http://www.cestari.info/
|
Author: Eric Cestari <eric@ohmforce.com> http://www.cestari.info/
|
||||||
Licensed under the same terms as ejabberd (GPL 2)
|
Licensed under the same terms as ejabberd (GPL 2)
|
||||||
Requires: ejabberd trunk SVN r1561 or newer; or ejabberd 2.0.3 or newer
|
Requires: ejabberd 2.0.3 or newer.
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
|
@ -22,28 +33,6 @@ http://www.cestari.info/2008/6/19/atom-pubsub-module-for-ejabberd
|
||||||
http://www.cestari.info/2008/9/12/atom_pubsub-dead-long-live-atom_microblog
|
http://www.cestari.info/2008/9/12/atom_pubsub-dead-long-live-atom_microblog
|
||||||
|
|
||||||
|
|
||||||
INSTALL
|
|
||||||
-------
|
|
||||||
|
|
||||||
1. Compile the files executing: ./build.sh
|
|
||||||
|
|
||||||
2. Copy beam files from ebin/ into your ejabberd installation.
|
|
||||||
|
|
||||||
3. Edit ejabberd.cfg and add a request handler in ejabberd_http listener:
|
|
||||||
{listen, [
|
|
||||||
...
|
|
||||||
{8080, ejabberd_http, [
|
|
||||||
http_poll,
|
|
||||||
web_admin,
|
|
||||||
{request_handlers, [{["pep"], atom_microblog}, % Make sure it's "pep", or change it in atom_microblog.erl
|
|
||||||
{["pubsub"], atom_pubsub}]} % Make sure it's "pubsub", or change it in atom_pubsub.erl
|
|
||||||
|
|
||||||
]}
|
|
||||||
]}.
|
|
||||||
|
|
||||||
4. Restart ejabberd.
|
|
||||||
|
|
||||||
|
|
||||||
USAGE
|
USAGE
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
author: "Eric Cestari <eric at ohmforce.com>"
|
||||||
|
category: "pubsub"
|
||||||
|
summary: "Provides access to all PEP nodes via an AtomPub interface"
|
||||||
|
home: "https://github.com/processone/ejabberd-contrib/tree/master/atom_pubsub"
|
||||||
|
url: "git@github.com:processone/ejabberd-contrib.git"
|
|
@ -1 +0,0 @@
|
||||||
erl -pa ../../ejabberd-dev/trunk/ebin -pa ebin -make
|
|
|
@ -1,2 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
erl -pa ../ejabberd-dev/ebin -pz ebin -make
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
listen:
|
||||||
|
-
|
||||||
|
port: 8080
|
||||||
|
module: ejabberd_http
|
||||||
|
request_handlers:
|
||||||
|
"pep": atom_microblog
|
||||||
|
"pubsub": atom_pubsub
|
|
@ -7,8 +7,9 @@
|
||||||
-author('eric@ohmforce.com').
|
-author('eric@ohmforce.com').
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
-include("mod_pubsub/pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("web/ejabberd_http.hrl").
|
-include("ejabberd_http.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
-export([process/2]).
|
-export([process/2]).
|
||||||
|
|
||||||
process([Domain,User|_]=LocalPath, #request{auth = Auth} = Request)->
|
process([Domain,User|_]=LocalPath, #request{auth = Auth} = Request)->
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
-author('eric@ohmforce.com').
|
-author('eric@ohmforce.com').
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
-include("mod_pubsub/pubsub.hrl").
|
-include("pubsub.hrl").
|
||||||
-include("web/ejabberd_http.hrl").
|
-include("ejabberd_http.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
-export([process/2]).
|
-export([process/2]).
|
||||||
|
|
||||||
process([Domain,User|_]=LocalPath, #request{auth = Auth} = Request)->
|
process([Domain,User|_]=LocalPath, #request{auth = Auth} = Request)->
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
|
|
||||||
Copyright (c) 2006, Claes Wikstrom, klacke@hyber.org
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
* Neither the name of "bfile" nor the names of its contributors may be
|
|
||||||
used to endorse or promote products derived from this software without
|
|
||||||
specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,23 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
all:
|
|
||||||
(cd config;$(MAKE))
|
|
||||||
(cd src;$(MAKE))
|
|
||||||
-(cd c_src;$(MAKE) -k)
|
|
||||||
$(MAKE) appfile
|
|
||||||
|
|
||||||
clean:
|
|
||||||
(cd src;$(MAKE) clean)
|
|
||||||
(cd c_src;$(MAKE) clean)
|
|
||||||
(cd config; $(MAKE) clean)
|
|
||||||
|
|
||||||
|
|
||||||
install: all
|
|
||||||
(cd c_src; $(MAKE) install)
|
|
||||||
|
|
||||||
conf_clean:
|
|
||||||
(cd config; $(MAKE) clean)
|
|
||||||
|
|
||||||
appfile:
|
|
||||||
(cd src;$(MAKE) ../ebin/bfile.app)
|
|
||||||
|
|
28
bfile/README
28
bfile/README
|
@ -1,28 +0,0 @@
|
||||||
An interface to fast FILE I/O
|
|
||||||
|
|
||||||
It's based on an old and hacked version
|
|
||||||
of the BSD FILE*
|
|
||||||
|
|
||||||
To install, type make; make install
|
|
||||||
and it shuld install itself as an app in your
|
|
||||||
erlang dir.
|
|
||||||
|
|
||||||
See the source src/bfile.erl for API
|
|
||||||
|
|
||||||
Here's an example shell session:
|
|
||||||
|
|
||||||
|
|
||||||
2> bfile:load_driver().
|
|
||||||
ok
|
|
||||||
4> {ok, Fd} = bfile:fopen("Makefile", "r").
|
|
||||||
{ok,{bfile,#Port<0.98>}}
|
|
||||||
5> bfile:fgets(Fd).
|
|
||||||
{line,<<10>>}
|
|
||||||
6> bfile:fgets(Fd).
|
|
||||||
{line,<<10>>}
|
|
||||||
7> bfile:fgets(Fd).
|
|
||||||
{line,<<97,108,108,58,32,10>>}
|
|
||||||
14> bfile:fread(Fd, 10000).
|
|
||||||
{ok,<<10,10,105,110,115,116,97,108,108,58,32,97,108,108,10,9,40,99,100,32,99,95,115,114,99,59,32,...>>}
|
|
||||||
15> bfile:fread(Fd, 10000).
|
|
||||||
eof
|
|
|
@ -1,490 +0,0 @@
|
||||||
/* Interface to stdio buffered FILE io */
|
|
||||||
/* author: klacke@kaja.klacke.net */
|
|
||||||
/* Created : 22 Nov 1999 by Claes Wikstrom <klacke@kaja.klacke.net> */
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define USE_STDIO
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
#define USE_STDIO
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "erl_driver.h"
|
|
||||||
#ifndef ERL_DRV_NIL
|
|
||||||
#include "erl_driver_compat.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_STDIO
|
|
||||||
# include <stdio.h>
|
|
||||||
#else
|
|
||||||
|
|
||||||
# define malloc(s) driver_alloc(s)
|
|
||||||
# define realloc(p, s) driver_realloc(p, s)
|
|
||||||
# define free(p) driver_free(p)
|
|
||||||
|
|
||||||
# define BINTERFACE static
|
|
||||||
# include "bbio.c"
|
|
||||||
# define FILE bFILE
|
|
||||||
# define clearerr bclearerr
|
|
||||||
# define fclose bfclose
|
|
||||||
# define feof bfeof
|
|
||||||
# define ferror bferror
|
|
||||||
# define fflush bfflush
|
|
||||||
# define fgets bfgets
|
|
||||||
# define fileno bfileno
|
|
||||||
# define fopen bfopen
|
|
||||||
# define fread bfread
|
|
||||||
# define fseek bfseek
|
|
||||||
# define ftell bftell
|
|
||||||
# define fwrite bfwrite
|
|
||||||
# define getc bgetc
|
|
||||||
# define ungetc bungetc
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
|
|
||||||
(((unsigned char*) (s))[1] << 16) | \
|
|
||||||
(((unsigned char*) (s))[2] << 8) | \
|
|
||||||
(((unsigned char*) (s))[3]))
|
|
||||||
|
|
||||||
#define put_int32(i, s) {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \
|
|
||||||
((char*)(s))[1] = (char)((i) >> 16) & 0xff; \
|
|
||||||
((char*)(s))[2] = (char)((i) >> 8) & 0xff; \
|
|
||||||
((char*)(s))[3] = (char)((i) & 0xff);}
|
|
||||||
/* op codes */
|
|
||||||
|
|
||||||
#define XX_OPEN 'o'
|
|
||||||
#define XX_CLOSE 'c'
|
|
||||||
#define XX_READ 'r'
|
|
||||||
#define XX_WRITE 'w'
|
|
||||||
#define XX_SEEK 's'
|
|
||||||
#define XX_TELL 't'
|
|
||||||
#define XX_TRUNCATE 'T'
|
|
||||||
#define XX_FLUSH 'f'
|
|
||||||
#define XX_OEOF 'e'
|
|
||||||
#define XX_ERROR 'E'
|
|
||||||
#define XX_GETC 'g'
|
|
||||||
#define XX_GETS 'G'
|
|
||||||
#define XX_GETS2 '2'
|
|
||||||
#define XX_SET_LINEBUF_SIZE 'S'
|
|
||||||
#define XX_UNGETC 'u'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* return codes */
|
|
||||||
#define XX_VALUE 'v'
|
|
||||||
#define XX_FLINE 'L'
|
|
||||||
#define XX_OK 'o'
|
|
||||||
#define XX_I32 'O'
|
|
||||||
#define XX_NOLINE 'N'
|
|
||||||
#define XX_FERROR 'E'
|
|
||||||
#define XX_REOF 'x'
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
#define XX_EINVAL WSAEINVAL
|
|
||||||
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define XX_EINVAL EINVAL
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static ErlDrvData FILE_start(ErlDrvPort port, char *buf);
|
|
||||||
static void FILE_stop(ErlDrvData drv_data);
|
|
||||||
|
|
||||||
static ErlDrvEntry FILE_driver_entry;
|
|
||||||
|
|
||||||
typedef struct _desc {
|
|
||||||
ErlDrvPort port;
|
|
||||||
FILE *fp;
|
|
||||||
int linebuf_size;
|
|
||||||
} Desc;
|
|
||||||
|
|
||||||
|
|
||||||
static ErlDrvData FILE_start(ErlDrvPort port, char *buf)
|
|
||||||
{
|
|
||||||
Desc *d = (Desc*) driver_alloc(sizeof (Desc));
|
|
||||||
|
|
||||||
if (d == NULL)
|
|
||||||
return (ErlDrvData) -1;
|
|
||||||
d->fp = NULL;
|
|
||||||
d->port = port;
|
|
||||||
d->linebuf_size = 255; /* default line size */
|
|
||||||
|
|
||||||
set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
|
|
||||||
|
|
||||||
return (ErlDrvData) d;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void FILE_stop(ErlDrvData drv_data)
|
|
||||||
{
|
|
||||||
Desc *d = (Desc*) drv_data;
|
|
||||||
if (d->fp)
|
|
||||||
fclose(d->fp);
|
|
||||||
driver_free(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static char *driver_error(ErlDrvPort port, int err)
|
|
||||||
{
|
|
||||||
char response[256]; /* Response buffer. */
|
|
||||||
char* s;
|
|
||||||
char* t;
|
|
||||||
ErlDrvBinary* bin;
|
|
||||||
|
|
||||||
bin = driver_alloc_binary(1);
|
|
||||||
bin->orig_bytes[0] = XX_FERROR;
|
|
||||||
|
|
||||||
response[0] = XX_FERROR;
|
|
||||||
for (s = erl_errno_id(err), t = bin->orig_bytes + 1; *s; s++, t++)
|
|
||||||
*t = tolower(*s);
|
|
||||||
return (char *)bin;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *driver_ret32(ErlDrvPort port, unsigned int r)
|
|
||||||
{
|
|
||||||
char ch = XX_I32;
|
|
||||||
ErlDrvBinary* bin;
|
|
||||||
|
|
||||||
bin = driver_alloc_binary(1);
|
|
||||||
bin->orig_bytes[0] = ch;
|
|
||||||
put_int32(r, bin->orig_bytes + 1);
|
|
||||||
return (char *)bin;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *driver_ok(ErlDrvPort port)
|
|
||||||
{
|
|
||||||
char ch = XX_OK;
|
|
||||||
ErlDrvBinary* bin;
|
|
||||||
bin = driver_alloc_binary(1);
|
|
||||||
bin->orig_bytes[0] = ch;
|
|
||||||
return (char *)bin;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *driver_eof(ErlDrvPort port)
|
|
||||||
{
|
|
||||||
char ch = XX_REOF;
|
|
||||||
ErlDrvBinary* bin;
|
|
||||||
bin = driver_alloc_binary(1);
|
|
||||||
bin->orig_bytes[0] = ch;
|
|
||||||
return (char *)bin;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int FILE_control(ErlDrvData drv_data,
|
|
||||||
unsigned int command,
|
|
||||||
char *buf, int len,
|
|
||||||
char **rbuf, int rlen)
|
|
||||||
{
|
|
||||||
Desc *desc = (Desc*) drv_data;
|
|
||||||
ErlDrvBinary* bin;
|
|
||||||
|
|
||||||
switch (command) {
|
|
||||||
|
|
||||||
case XX_OPEN: {
|
|
||||||
char file[BUFSIZ]; /* should be FILENAME_MAX */
|
|
||||||
char flags[4]; /* at most someething like rb+ */
|
|
||||||
char* src;
|
|
||||||
char* dst;
|
|
||||||
char* src_end;
|
|
||||||
char* dst_end;
|
|
||||||
|
|
||||||
if (desc->fp != NULL) {
|
|
||||||
*rbuf = driver_error(desc->port, XX_EINVAL);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* play it safe ? */
|
|
||||||
src = buf;
|
|
||||||
src_end = buf + len;
|
|
||||||
|
|
||||||
/* get file name */
|
|
||||||
dst = file;
|
|
||||||
dst_end = dst + BUFSIZ; /* make room for a '\0' */
|
|
||||||
while((src < src_end) && (dst < dst_end) && (*src != '\0'))
|
|
||||||
*dst++ = *src++;
|
|
||||||
if ((src == src_end) || (dst == dst_end)) {
|
|
||||||
driver_error(desc->port, XX_EINVAL);
|
|
||||||
}
|
|
||||||
*dst = *src++;
|
|
||||||
/* get flags */
|
|
||||||
dst = flags;
|
|
||||||
dst_end = dst + 4;
|
|
||||||
while((src < src_end) && (dst < dst_end) && (*src != '\0'))
|
|
||||||
*dst++ = *src++;
|
|
||||||
if (dst == dst_end) {
|
|
||||||
*rbuf = driver_error(desc->port, XX_EINVAL);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*dst = '\0';
|
|
||||||
|
|
||||||
if (src + 1 != src_end) {
|
|
||||||
*rbuf = driver_error(desc->port, XX_EINVAL);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((desc->fp = fopen(file, flags))==NULL) {
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*rbuf = driver_ok(desc->port);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case XX_WRITE: {
|
|
||||||
if (fwrite(buf, 1, len, desc->fp) != len) {
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*rbuf = driver_ok(desc->port);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case XX_READ: {
|
|
||||||
char ch = XX_VALUE;
|
|
||||||
int rval;
|
|
||||||
int sz = get_int32(buf);
|
|
||||||
|
|
||||||
if ((bin = driver_alloc_binary(sz + 1)) == NULL) {
|
|
||||||
*rbuf = driver_error(desc->port, -1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bin->orig_bytes[0] = ch;
|
|
||||||
if ((rval = fread(bin->orig_bytes + 1, 1, sz, desc->fp)) != sz) {
|
|
||||||
if (feof(desc->fp)) {
|
|
||||||
if (rval == 0) {
|
|
||||||
driver_free_binary(bin);
|
|
||||||
*rbuf = driver_eof(desc->port);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
bin = driver_realloc_binary(bin, rval + 1);
|
|
||||||
*rbuf = (char *)bin;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
driver_free_binary(bin);
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*rbuf = (char *)bin;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
case XX_SEEK: {
|
|
||||||
int offs = get_int32(buf);
|
|
||||||
int w = (int) buf[4];
|
|
||||||
int whence;
|
|
||||||
switch (w) {
|
|
||||||
case 1: whence = SEEK_SET; break;
|
|
||||||
case 2: whence = SEEK_CUR; break;
|
|
||||||
case 3: whence = SEEK_END; break;
|
|
||||||
}
|
|
||||||
if ((w = fseek(desc->fp, offs, whence)) != 0) {
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*rbuf = driver_ok(desc->port);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
case XX_TELL: {
|
|
||||||
int offs;
|
|
||||||
if ((offs = ftell(desc->fp)) == -1) {
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*rbuf = driver_ret32(desc->port, offs);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case XX_TRUNCATE: {
|
|
||||||
int fno;
|
|
||||||
int offs;
|
|
||||||
/* is this really safe? */
|
|
||||||
if (fflush(desc->fp) != 0) {
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if ((offs = ftell(desc->fp)) == -1) {
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
fno = fileno(desc->fp);
|
|
||||||
#ifdef WIN32
|
|
||||||
if (SetEndOfFile((HANDLE)fno) != 0) {
|
|
||||||
*rbuf = driver_error(desc->port, GetLastError());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (ftruncate(fno, offs) == -1) {
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
*rbuf = driver_ok(desc->port);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
case XX_FLUSH:
|
|
||||||
if (fflush(desc->fp) != 0)
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
else
|
|
||||||
*rbuf = driver_ok(desc->port);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XX_OEOF:
|
|
||||||
if (feof(desc->fp))
|
|
||||||
*rbuf = driver_ret32(desc->port, 1);
|
|
||||||
else
|
|
||||||
*rbuf = driver_ret32(desc->port, 0);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XX_ERROR:
|
|
||||||
if (ferror(desc->fp))
|
|
||||||
*rbuf = driver_ret32(desc->port, 1);
|
|
||||||
else
|
|
||||||
*rbuf = driver_ret32(desc->port,0);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case XX_GETC: {
|
|
||||||
int ch;
|
|
||||||
if ((ch = getc(desc->fp)) == EOF) {
|
|
||||||
if (feof(desc->fp)) {
|
|
||||||
*rbuf = driver_eof(desc->port);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*rbuf = driver_ret32(desc->port, ch);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case XX_SET_LINEBUF_SIZE: {
|
|
||||||
int sz = get_int32(buf);
|
|
||||||
desc->linebuf_size = sz;
|
|
||||||
*rbuf = driver_ok(desc->port);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case XX_GETS:
|
|
||||||
case XX_GETS2: {
|
|
||||||
int rval;
|
|
||||||
long cpos1, cpos2;
|
|
||||||
char header;
|
|
||||||
|
|
||||||
if ((bin = driver_alloc_binary(desc->linebuf_size + 1)) == NULL) {
|
|
||||||
*rbuf = driver_error(desc->port, -1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((cpos1 = ftell(desc->fp)) == -1) {
|
|
||||||
driver_free_binary(bin);
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((fgets(bin->orig_bytes + 1, desc->linebuf_size,
|
|
||||||
desc->fp)) == NULL) {
|
|
||||||
driver_free_binary(bin);
|
|
||||||
if (feof(desc->fp)) {
|
|
||||||
*rbuf = driver_eof(desc->port);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if ((cpos2 = ftell(desc->fp)) == -1) {
|
|
||||||
driver_free_binary(bin);
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
rval = cpos2 - cpos1;
|
|
||||||
|
|
||||||
if (bin->orig_bytes[rval] == '\n' &&
|
|
||||||
bin->orig_bytes[rval + 1] == 0) {
|
|
||||||
header = XX_FLINE;
|
|
||||||
/* GETS keep newline, GETS2 remove newline */
|
|
||||||
rval = rval - (command == XX_GETS ? 0 : 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
header = XX_NOLINE;
|
|
||||||
bin->orig_bytes[0] = header;
|
|
||||||
bin = driver_realloc_binary(bin, rval + 1);
|
|
||||||
*rbuf = (char *)bin;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
case XX_UNGETC: {
|
|
||||||
int ch = buf[0];
|
|
||||||
if (ungetc(ch, desc->fp) == EOF)
|
|
||||||
*rbuf = driver_error(desc->port, errno);
|
|
||||||
else
|
|
||||||
*rbuf = driver_ok(desc->port);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "Unknown opcode %c\n\r", command);
|
|
||||||
#endif
|
|
||||||
*rbuf = driver_error(desc->port, XX_EINVAL);
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FILE_finish()
|
|
||||||
{
|
|
||||||
#ifndef USE_STDIO
|
|
||||||
/*
|
|
||||||
* Make sure any remaining buffers are flushed (this is done on exit() by
|
|
||||||
* the normal stdio).
|
|
||||||
*/
|
|
||||||
bbio_cleanup();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize and return a driver entry struct
|
|
||||||
*/
|
|
||||||
|
|
||||||
DRIVER_INIT(FILE_drv)
|
|
||||||
{
|
|
||||||
FILE_driver_entry.init = NULL; /* Not used */
|
|
||||||
FILE_driver_entry.start = FILE_start;
|
|
||||||
FILE_driver_entry.stop = FILE_stop;
|
|
||||||
FILE_driver_entry.output = NULL;
|
|
||||||
FILE_driver_entry.ready_input = NULL;
|
|
||||||
FILE_driver_entry.ready_output = NULL;
|
|
||||||
FILE_driver_entry.driver_name = "FILE_drv";
|
|
||||||
FILE_driver_entry.finish = FILE_finish;
|
|
||||||
FILE_driver_entry.outputv = NULL;
|
|
||||||
FILE_driver_entry.control = FILE_control;
|
|
||||||
return &FILE_driver_entry;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
|
|
||||||
include ../config/include.mk
|
|
||||||
|
|
||||||
## don't build this under win32 at all
|
|
||||||
ifdef WIN32
|
|
||||||
|
|
||||||
PRIV_FILES =
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
PRIV_FILES=../priv/FILE_drv.so
|
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
CFLAGS += -I$(ERL_C_INCLUDE_DIR) -I../config -I.
|
|
||||||
|
|
||||||
#
|
|
||||||
# Targets
|
|
||||||
#
|
|
||||||
|
|
||||||
all: $(PRIV_FILES)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
-rm -f $(PRIV_FILES) FILE_drv.o
|
|
||||||
|
|
||||||
|
|
||||||
install:
|
|
||||||
install -d $(ERLDIR)/lib/bfile
|
|
||||||
cp -r `pwd` $(ERLDIR)/lib/bfile
|
|
||||||
cp -r `pwd`/../ebin $(ERLDIR)/lib/bfile
|
|
||||||
cp -r `pwd`/../priv $(ERLDIR)/lib/bfile
|
|
||||||
|
|
||||||
|
|
||||||
../priv/FILE_drv.so: FILE_drv.o
|
|
||||||
$(LD_SHARED) -o $@ FILE_drv.o $(LIBS)
|
|
||||||
|
|
||||||
|
|
||||||
FILE_drv.o: FILE_drv.c
|
|
||||||
$(CC) -o $@ -c -fpic $(CFLAGS) -DDYNAMIC_DRIVER FILE_drv.c
|
|
||||||
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
MK_INCLUDE=include.mk
|
|
||||||
CONFIG_H=config.h
|
|
||||||
|
|
||||||
all: config.status $(MK_INCLUDE) $(CONFIG_H)
|
|
||||||
|
|
||||||
config.status: configure
|
|
||||||
./configure
|
|
||||||
|
|
||||||
$(CONFIG_H) $(MK_INCLUDE): config.status include.mk.in config.h.in
|
|
||||||
./config.status
|
|
||||||
|
|
||||||
configure: configure.in
|
|
||||||
autoheader
|
|
||||||
autoconf
|
|
||||||
|
|
||||||
clean:
|
|
||||||
-rm -f config.cache config.log config.status configure \
|
|
||||||
$(MK_INCLUDE) $(CONFIG_H)
|
|
||||||
-rm -rf autom4te.cache
|
|
||||||
|
|
||||||
|
|
|
@ -1,269 +0,0 @@
|
||||||
#ifndef _CONFIG_H_
|
|
||||||
#define _CONFIG_H_
|
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
|
|
||||||
@TOP@
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file contains prototypes for config.h.in which autoheader
|
|
||||||
* could not figure by itself. I.e. if you write your own test which
|
|
||||||
* defines a macro you will probably have to put it here, but if you
|
|
||||||
* use any standard test AC_* it will be detected by autoheader.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#undef WIN32
|
|
||||||
|
|
||||||
/* Define to a string defining your target. ($target in configure.in) */
|
|
||||||
#undef CPU_VENDOR_OS
|
|
||||||
|
|
||||||
|
|
||||||
/* Define to the full path of the ifconfig program */
|
|
||||||
#undef IFCONFIG
|
|
||||||
|
|
||||||
/* Define to the full path of the route program */
|
|
||||||
#undef ROUTE
|
|
||||||
|
|
||||||
/* Define to the full path of the arp program */
|
|
||||||
#undef ARP
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These four below should definately be done away with!!
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Define if your target OS is a BSD derivative */
|
|
||||||
#undef BSD
|
|
||||||
|
|
||||||
/* Define if your target OS is Linux */
|
|
||||||
#undef LINUX
|
|
||||||
|
|
||||||
/* Define if your target OS is SunOS 5.x */
|
|
||||||
#undef SOLARIS
|
|
||||||
|
|
||||||
/* Define if your target OS is BSD/OS 4.x */
|
|
||||||
#undef BSDI
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* What are these???
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* ? */
|
|
||||||
#undef USE_IFALIAS
|
|
||||||
|
|
||||||
/* Define if you wish to use the bpf interface */
|
|
||||||
#undef USE_BPF
|
|
||||||
|
|
||||||
/* Define if you wish to use the dlpi interface */
|
|
||||||
#undef USE_DLPI
|
|
||||||
|
|
||||||
/* ? */
|
|
||||||
#undef USE_SOCKET
|
|
||||||
|
|
||||||
|
|
||||||
/* Define if prototypes for malloc can be found in stdlib.h */
|
|
||||||
#undef STDLIB_MALLOC
|
|
||||||
|
|
||||||
/* Define if your include files defines a prototype for sys_errlist[] */
|
|
||||||
#undef HAVE_SYS_ERRLIST
|
|
||||||
|
|
||||||
/* This isn't used anywhere (that I could find) so I don't know what it means*/
|
|
||||||
#undef IFCONFIG_REQUIRES_DOWN_ADDRESS
|
|
||||||
|
|
||||||
/* Define if your OS have broken cmsg fields in the msghdr struct (Linux) */
|
|
||||||
#undef BROKEN_CMSG_FIELDS
|
|
||||||
|
|
||||||
/* Define if sockaddr structure has sa_len member */
|
|
||||||
#undef SA_LEN_IN_SOCKADDR
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Provide a common way to refer to ints with specific size. (Is this
|
|
||||||
* used everywhere?) The names used are {u_,}int{8,16,32,64}_t unless
|
|
||||||
* they are defined in sys/types.h they are defined in ints.h using
|
|
||||||
* the BIT macros. e.g. if int8_t isn't defined in sys/types.h int8_t
|
|
||||||
* is typedef:ed to BIT8.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Define to a basic signed type that is 8 bits in size */
|
|
||||||
#undef BIT8
|
|
||||||
|
|
||||||
/* Define to a basic signed type that is 16 bits in size */
|
|
||||||
#undef BIT16
|
|
||||||
|
|
||||||
/* Define to a basic signed type that is 32 bits in size */
|
|
||||||
#undef BIT32
|
|
||||||
|
|
||||||
/* Define to a basic signed type that is 64 bits in size */
|
|
||||||
#undef BIT64
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_int8_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_u_int8_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_int16_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_u_int16_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_int32_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_u_int32_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_int64_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_u_int64_t
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef ETHER_HEADER_USES_ETHER_ADDR
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_DLIOCRAW
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ETHERADDRL
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ETHER_ADDR_LEN
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_MSGHDR_MSG_CONTROL
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_SIOCGARP
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_SIOCGIFCONF
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_SIOCGIFHWADDR
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_arpreq
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_caddr_t
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ether_header
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ethhdr
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ifnet
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_in_addr
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_sockaddr
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_sockaddr_dl
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_sockaddr_in
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_HAVE_sockaddr_dl
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* I have yet to figure out what all this need_* stuff is for, shouldn't
|
|
||||||
* it suffice with using have_* ???
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_LINUX_SOCKIOS_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NETINET_IF_ETHER_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NETINET_IN_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NET_ETHERNET_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NET_IF_ARP_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NET_IF_DL_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NET_IF_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_BITYPES_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_DLPI_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_ETHERNET_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_SOCKETIO_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_SOCKET_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_SOCKIO_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_TYPES_H
|
|
||||||
|
|
||||||
/* if we have the openssl with engine support */
|
|
||||||
#undef HAVE_SSL_ENGINE
|
|
||||||
/* ... with Rainbow patches */
|
|
||||||
#undef HAVE_RAINBOW_PATCHES
|
|
||||||
/* ... with Rainbow's libswift */
|
|
||||||
#undef HAVE_SWIFT
|
|
||||||
/* ... with one of our HW checks */
|
|
||||||
#undef HAVE_LOCAL_ENGINE_SETUP
|
|
||||||
#undef HAVE_SSL_HW_CHECK
|
|
||||||
/* ... with patch to get cfg password from card */
|
|
||||||
#undef HAVE_ENGINE_GET_PASSWORD
|
|
||||||
/* ... with our buffer patches */
|
|
||||||
#undef HAVE_SSL_BUFFER_CB
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef ISD_SYSTEM_VSN
|
|
||||||
|
|
||||||
/* eventpoll */
|
|
||||||
#undef HAVE_KPOLL
|
|
||||||
|
|
||||||
/* Have/use netfilter (a.k.a. iptables) */
|
|
||||||
#undef HAVE_NETFILTER
|
|
||||||
|
|
||||||
/* Non-standard support for "non-local connect()" in Linux 2.4 */
|
|
||||||
#undef HAVE_NONLOCAL_CONNECT
|
|
||||||
|
|
||||||
/* atomic asmebler op in asm/atomic.h ? */
|
|
||||||
#undef HAVE_ATOMIC_OPS
|
|
||||||
|
|
||||||
|
|
||||||
@BOTTOM@
|
|
||||||
|
|
||||||
#endif /* WIN32 */
|
|
||||||
#endif /* _CONFIG_H_ */
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
dnl ----------------------------------------------------------------------
|
|
||||||
dnl
|
|
||||||
dnl BT_MSG_CONTROL checks for msg_control member in msghdr and that
|
|
||||||
dnl the cmsg fields aren't broken...
|
|
||||||
dnl
|
|
||||||
|
|
||||||
AC_DEFUN(BT_MSG_CONTROL,
|
|
||||||
[
|
|
||||||
AC_CACHE_CHECK([for msg_control member in msghdr],
|
|
||||||
bt_cv_have_msghdr_msg_control,
|
|
||||||
[AC_TRY_COMPILE([#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>],
|
|
||||||
[struct msghdr msg;
|
|
||||||
msg.msg_control;],
|
|
||||||
bt_cv_have_msghdr_msg_control=yes, bt_cv_have_msghdr_msg_control=no)])
|
|
||||||
if test $bt_cv_have_msghdr_msg_control = yes; then
|
|
||||||
AC_DEFINE(HAVE_MSGHDR_MSG_CONTROL)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test $bt_cv_have_msghdr_msg_control = yes; then
|
|
||||||
AC_MSG_CHECKING(for broken CMSG_FIELDS)
|
|
||||||
case "$target_os" in
|
|
||||||
linux*)
|
|
||||||
AC_DEFINE(BROKEN_CMSG_FIELDS)
|
|
||||||
AC_MSG_RESULT(yes)
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
AC_MSG_RESULT(no)
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
])
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,319 +0,0 @@
|
||||||
/* config.h.in. Generated from configure.in by autoheader. */
|
|
||||||
#ifndef _CONFIG_H_
|
|
||||||
#define _CONFIG_H_
|
|
||||||
|
|
||||||
#ifndef WIN32
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file contains prototypes for config.h.in which autoheader
|
|
||||||
* could not figure by itself. I.e. if you write your own test which
|
|
||||||
* defines a macro you will probably have to put it here, but if you
|
|
||||||
* use any standard test AC_* it will be detected by autoheader.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#undef WIN32
|
|
||||||
|
|
||||||
/* Define to a string defining your target. ($target in configure.in) */
|
|
||||||
#undef CPU_VENDOR_OS
|
|
||||||
|
|
||||||
|
|
||||||
/* Define to the full path of the ifconfig program */
|
|
||||||
#undef IFCONFIG
|
|
||||||
|
|
||||||
/* Define to the full path of the route program */
|
|
||||||
#undef ROUTE
|
|
||||||
|
|
||||||
/* Define to the full path of the arp program */
|
|
||||||
#undef ARP
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These four below should definately be done away with!!
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Define if your target OS is a BSD derivative */
|
|
||||||
#undef BSD
|
|
||||||
|
|
||||||
/* Define if your target OS is Linux */
|
|
||||||
#undef LINUX
|
|
||||||
|
|
||||||
/* Define if your target OS is SunOS 5.x */
|
|
||||||
#undef SOLARIS
|
|
||||||
|
|
||||||
/* Define if your target OS is BSD/OS 4.x */
|
|
||||||
#undef BSDI
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* What are these???
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* ? */
|
|
||||||
#undef USE_IFALIAS
|
|
||||||
|
|
||||||
/* Define if you wish to use the bpf interface */
|
|
||||||
#undef USE_BPF
|
|
||||||
|
|
||||||
/* Define if you wish to use the dlpi interface */
|
|
||||||
#undef USE_DLPI
|
|
||||||
|
|
||||||
/* ? */
|
|
||||||
#undef USE_SOCKET
|
|
||||||
|
|
||||||
|
|
||||||
/* Define if prototypes for malloc can be found in stdlib.h */
|
|
||||||
#undef STDLIB_MALLOC
|
|
||||||
|
|
||||||
/* Define if your include files defines a prototype for sys_errlist[] */
|
|
||||||
#undef HAVE_SYS_ERRLIST
|
|
||||||
|
|
||||||
/* This isn't used anywhere (that I could find) so I don't know what it means*/
|
|
||||||
#undef IFCONFIG_REQUIRES_DOWN_ADDRESS
|
|
||||||
|
|
||||||
/* Define if your OS have broken cmsg fields in the msghdr struct (Linux) */
|
|
||||||
#undef BROKEN_CMSG_FIELDS
|
|
||||||
|
|
||||||
/* Define if sockaddr structure has sa_len member */
|
|
||||||
#undef SA_LEN_IN_SOCKADDR
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Provide a common way to refer to ints with specific size. (Is this
|
|
||||||
* used everywhere?) The names used are {u_,}int{8,16,32,64}_t unless
|
|
||||||
* they are defined in sys/types.h they are defined in ints.h using
|
|
||||||
* the BIT macros. e.g. if int8_t isn't defined in sys/types.h int8_t
|
|
||||||
* is typedef:ed to BIT8.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Define to a basic signed type that is 8 bits in size */
|
|
||||||
#undef BIT8
|
|
||||||
|
|
||||||
/* Define to a basic signed type that is 16 bits in size */
|
|
||||||
#undef BIT16
|
|
||||||
|
|
||||||
/* Define to a basic signed type that is 32 bits in size */
|
|
||||||
#undef BIT32
|
|
||||||
|
|
||||||
/* Define to a basic signed type that is 64 bits in size */
|
|
||||||
#undef BIT64
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_int8_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_u_int8_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_int16_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_u_int16_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_int32_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_u_int32_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_int64_t
|
|
||||||
|
|
||||||
/* Define if sys/types.h defines this type */
|
|
||||||
#undef HAVE_u_int64_t
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef ETHER_HEADER_USES_ETHER_ADDR
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_DLIOCRAW
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ETHERADDRL
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ETHER_ADDR_LEN
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_MSGHDR_MSG_CONTROL
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_SIOCGARP
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_SIOCGIFCONF
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_SIOCGIFHWADDR
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_arpreq
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_caddr_t
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ether_header
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ethhdr
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_ifnet
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_in_addr
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_sockaddr
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_sockaddr_dl
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef HAVE_sockaddr_in
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_HAVE_sockaddr_dl
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* I have yet to figure out what all this need_* stuff is for, shouldn't
|
|
||||||
* it suffice with using have_* ???
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_LINUX_SOCKIOS_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NETINET_IF_ETHER_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NETINET_IN_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NET_ETHERNET_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NET_IF_ARP_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NET_IF_DL_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_NET_IF_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_BITYPES_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_DLPI_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_ETHERNET_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_SOCKETIO_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_SOCKET_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_SOCKIO_H
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef NEED_SYS_TYPES_H
|
|
||||||
|
|
||||||
/* if we have the openssl with engine support */
|
|
||||||
#undef HAVE_SSL_ENGINE
|
|
||||||
/* ... with Rainbow patches */
|
|
||||||
#undef HAVE_RAINBOW_PATCHES
|
|
||||||
/* ... with Rainbow's libswift */
|
|
||||||
#undef HAVE_SWIFT
|
|
||||||
/* ... with one of our HW checks */
|
|
||||||
#undef HAVE_LOCAL_ENGINE_SETUP
|
|
||||||
#undef HAVE_SSL_HW_CHECK
|
|
||||||
/* ... with patch to get cfg password from card */
|
|
||||||
#undef HAVE_ENGINE_GET_PASSWORD
|
|
||||||
/* ... with our buffer patches */
|
|
||||||
#undef HAVE_SSL_BUFFER_CB
|
|
||||||
|
|
||||||
/* */
|
|
||||||
#undef ISD_SYSTEM_VSN
|
|
||||||
|
|
||||||
/* eventpoll */
|
|
||||||
#undef HAVE_KPOLL
|
|
||||||
|
|
||||||
/* Have/use netfilter (a.k.a. iptables) */
|
|
||||||
#undef HAVE_NETFILTER
|
|
||||||
|
|
||||||
/* Non-standard support for "non-local connect()" in Linux 2.4 */
|
|
||||||
#undef HAVE_NONLOCAL_CONNECT
|
|
||||||
|
|
||||||
/* atomic asmebler op in asm/atomic.h ? */
|
|
||||||
#undef HAVE_ATOMIC_OPS
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Description */
|
|
||||||
#undef DARWIN
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
|
||||||
#undef HAVE_INTTYPES_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <malloc.h> header file. */
|
|
||||||
#undef HAVE_MALLOC_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <memory.h> header file. */
|
|
||||||
#undef HAVE_MEMORY_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <stdint.h> header file. */
|
|
||||||
#undef HAVE_STDINT_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
|
||||||
#undef HAVE_STDLIB_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <strings.h> header file. */
|
|
||||||
#undef HAVE_STRINGS_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <string.h> header file. */
|
|
||||||
#undef HAVE_STRING_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
|
||||||
#undef HAVE_SYS_STAT_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
|
||||||
#undef HAVE_SYS_TYPES_H
|
|
||||||
|
|
||||||
/* Define to 1 if you have the <unistd.h> header file. */
|
|
||||||
#undef HAVE_UNISTD_H
|
|
||||||
|
|
||||||
/* Define to the address where bug reports for this package should be sent. */
|
|
||||||
#undef PACKAGE_BUGREPORT
|
|
||||||
|
|
||||||
/* Define to the full name of this package. */
|
|
||||||
#undef PACKAGE_NAME
|
|
||||||
|
|
||||||
/* Define to the full name and version of this package. */
|
|
||||||
#undef PACKAGE_STRING
|
|
||||||
|
|
||||||
/* Define to the one symbol short name of this package. */
|
|
||||||
#undef PACKAGE_TARNAME
|
|
||||||
|
|
||||||
/* Define to the version of this package. */
|
|
||||||
#undef PACKAGE_VERSION
|
|
||||||
|
|
||||||
/* Define to 1 if you have the ANSI C header files. */
|
|
||||||
#undef STDC_HEADERS
|
|
||||||
|
|
||||||
#endif /* WIN32 */
|
|
||||||
#endif /* _CONFIG_H_ */
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,88 +0,0 @@
|
||||||
AC_INIT
|
|
||||||
|
|
||||||
dnl work out who the cpu, vendor and OS are
|
|
||||||
AC_CANONICAL_SYSTEM
|
|
||||||
AC_DEFINE_UNQUOTED(CPU_VENDOR_OS, "$target")
|
|
||||||
|
|
||||||
dnl Programs
|
|
||||||
|
|
||||||
AC_PROG_CC
|
|
||||||
AC_PATH_PROG(ERL, erl)
|
|
||||||
AC_PATH_PROG(ERLC, erlc)
|
|
||||||
ERLBINDIR=`dirname $ERL` ; ERLBINDIR=`dirname $ERLBINDIR`/lib/erlang/bin
|
|
||||||
|
|
||||||
ERLDIR=`awk -F= '/ROOTDIR=/ { print [$]2; exit; }' $ERL`
|
|
||||||
AC_SUBST(ERL)
|
|
||||||
AC_SUBST(ERLC)
|
|
||||||
AC_SUBST(ERLBINDIR)
|
|
||||||
AC_SUBST(ERLDIR)
|
|
||||||
|
|
||||||
if test ! -d "$ERLDIR" ; then
|
|
||||||
AC_MSG_ERROR([Broken Erlang installation, $ERLDIR does not exist!])
|
|
||||||
fi
|
|
||||||
|
|
||||||
dnl C header files
|
|
||||||
|
|
||||||
AC_CONFIG_HEADER(config.h:config.h.in)
|
|
||||||
|
|
||||||
AC_CHECK_HEADERS(malloc.h)
|
|
||||||
|
|
||||||
BT_MSG_CONTROL
|
|
||||||
|
|
||||||
case "$target_os" in
|
|
||||||
*cygwin*)
|
|
||||||
:
|
|
||||||
dnl fix this later
|
|
||||||
;;
|
|
||||||
linux*)
|
|
||||||
AC_DEFINE(LINUX)
|
|
||||||
LD_SHARED="ld -shared"
|
|
||||||
;;
|
|
||||||
*bsd*)
|
|
||||||
AC_DEFINE(BSD)
|
|
||||||
LD_SHARED="ld -Bshareable"
|
|
||||||
;;
|
|
||||||
*solaris*)
|
|
||||||
AC_DEFINE(SOLARIS)
|
|
||||||
LD_SHARED="ld -G"
|
|
||||||
;;
|
|
||||||
*darwin*)
|
|
||||||
AC_DEFINE([DARWIN], [], [Description])
|
|
||||||
LD_SHARED="cc -bundle -flat_namespace -undefined suppress"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
LD_SHARED="ld -shared"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
AC_SUBST(LD_SHARED)
|
|
||||||
|
|
||||||
|
|
||||||
dnl libnsl and libsocket tests borrowed from ethereal's autoconf scheme.
|
|
||||||
dnl # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT,
|
|
||||||
dnl # to get the SysV transport functions.
|
|
||||||
dnl # chad@anasazi.com says the Pyramid MIS-ES running DC/OSx (SVR4)
|
|
||||||
dnl # needs -lnsl.
|
|
||||||
dnl # The nsl library prevents programs from opening the X display
|
|
||||||
dnl # on Irix 5.2, according to dickey@clark.net.
|
|
||||||
AC_CHECK_FUNC(gethostbyname, ,
|
|
||||||
AC_CHECK_LIB(nsl, gethostbyname, NSL_LIBS="-lnsl"))
|
|
||||||
AC_SUBST(NSL_LIBS)
|
|
||||||
dnl # lieder@skyler.mavd.honeywell.com says without -lsocket,
|
|
||||||
dnl # socket/setsockopt and other routines are undefined under SCO ODT
|
|
||||||
dnl # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary
|
|
||||||
dnl # on later versions), says simon@lia.di.epfl.ch: it contains
|
|
||||||
dnl # gethostby* variants that don't use the nameserver (or something).
|
|
||||||
dnl # -lsocket must be given before -lnsl if both are needed.
|
|
||||||
dnl # We assume that if connect needs -lnsl, so does gethostbyname.
|
|
||||||
AC_CHECK_FUNC(connect, ,
|
|
||||||
AC_CHECK_LIB(socket, connect, SOCKET_LIBS="-lsocket",
|
|
||||||
AC_MSG_ERROR(Function 'socket' not found.), $NSL_LIBS))
|
|
||||||
AC_SUBST(SOCKET_LIBS)
|
|
||||||
|
|
||||||
dnl
|
|
||||||
dnl End.
|
|
||||||
|
|
||||||
AC_OUTPUT(include.mk)
|
|
||||||
|
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
## -*- makefile -*-
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
## C
|
|
||||||
|
|
||||||
CC := @CC@
|
|
||||||
CFLAGS := @CFLAGS@ @DEFS@
|
|
||||||
|
|
||||||
LD_SHARED := @LD_SHARED@
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
## Erlang
|
|
||||||
|
|
||||||
ERL = @ERL@
|
|
||||||
ERLC = @ERLC@
|
|
||||||
ERLDIR = @ERLDIR@
|
|
||||||
|
|
||||||
ERL_C_INCLUDE_DIR := $(ERLDIR)/usr/include
|
|
||||||
|
|
||||||
|
|
||||||
ERLC_FLAGS := -W
|
|
||||||
|
|
||||||
ifndef no_debug_info
|
|
||||||
ERLC_FLAGS += +debug_info
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef debug
|
|
||||||
ERLC_FLAGS += -Ddebug
|
|
||||||
endif
|
|
||||||
|
|
||||||
EBIN_DIR := ../ebin
|
|
||||||
DOC_DIR := ../doc
|
|
||||||
EMULATOR := beam
|
|
||||||
|
|
||||||
ERL_SOURCES := $(wildcard *.erl)
|
|
||||||
ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl)
|
|
||||||
ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR))
|
|
||||||
ERL_DOCUMENTS := $(ERL_SOURCES:%.erl=$(DOC_DIR)/%.html)
|
|
||||||
|
|
||||||
# Hmm, don't know if you are supposed to like this better... ;-)
|
|
||||||
APPSCRIPT = '$$vsn=shift; $$mods=""; while(@ARGV){ $$_=shift; s/^([A-Z].*)$$/\'\''$$1\'\''/; $$mods.=", " if $$mods; $$mods .= $$_; } while(<>) { s/%VSN%/$$vsn/; s/%MODULES%/$$mods/; print; }'
|
|
||||||
|
|
||||||
|
|
||||||
../ebin/%.app: %.app.src ../vsn.mk Makefile
|
|
||||||
perl -e $(APPSCRIPT) "$(VSN)" $(MODULES) < $< > $@
|
|
||||||
|
|
||||||
../ebin/%.appup: %.appup
|
|
||||||
cp $< $@
|
|
||||||
|
|
||||||
|
|
||||||
$(EBIN_DIR)/%.$(EMULATOR): %.erl
|
|
||||||
$(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $<
|
|
||||||
|
|
||||||
# generate documentation with edoc:
|
|
||||||
# this is still not the proper way to do it, but it works
|
|
||||||
# (see the wumpus application for an example)
|
|
||||||
|
|
||||||
$(DOC_DIR)/%.html: %.erl
|
|
||||||
${ERL} -noshell \
|
|
||||||
-pa ../../syntax_tools/ebin \
|
|
||||||
-pa ../../edoc/ebin \
|
|
||||||
-pa ../../xmerl/ebin \
|
|
||||||
-pa ../../ucs/ebin \
|
|
||||||
-run edoc file $< -run init stop
|
|
||||||
mv *.html $(DOC_DIR)
|
|
||||||
|
|
||||||
# C linking items
|
|
||||||
|
|
||||||
NSL_LIBS = @NSL_LIBS@
|
|
||||||
SOCKET_LIBS = @SOCKET_LIBS@
|
|
||||||
|
|
||||||
# Miscellaneous
|
|
||||||
|
|
||||||
GROFF = @GROFF@
|
|
||||||
PS2PDF = @PS2PDF@
|
|
|
@ -1,250 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# install - install a program, script, or datafile
|
|
||||||
# This comes from X11R5 (mit/util/scripts/install.sh).
|
|
||||||
#
|
|
||||||
# Copyright 1991 by the Massachusetts Institute of Technology
|
|
||||||
#
|
|
||||||
# Permission to use, copy, modify, distribute, and sell this software and its
|
|
||||||
# documentation for any purpose is hereby granted without fee, provided that
|
|
||||||
# the above copyright notice appear in all copies and that both that
|
|
||||||
# copyright notice and this permission notice appear in supporting
|
|
||||||
# documentation, and that the name of M.I.T. not be used in advertising or
|
|
||||||
# publicity pertaining to distribution of the software without specific,
|
|
||||||
# written prior permission. M.I.T. makes no representations about the
|
|
||||||
# suitability of this software for any purpose. It is provided "as is"
|
|
||||||
# without express or implied warranty.
|
|
||||||
#
|
|
||||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
|
||||||
# `make' implicit rules from creating a file called install from it
|
|
||||||
# when there is no Makefile.
|
|
||||||
#
|
|
||||||
# This script is compatible with the BSD install script, but was written
|
|
||||||
# from scratch. It can only install one file at a time, a restriction
|
|
||||||
# shared with many OS's install programs.
|
|
||||||
|
|
||||||
|
|
||||||
# set DOITPROG to echo to test this script
|
|
||||||
|
|
||||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
|
||||||
doit="${DOITPROG-}"
|
|
||||||
|
|
||||||
|
|
||||||
# put in absolute paths if you don't have them in your path; or use env. vars.
|
|
||||||
|
|
||||||
mvprog="${MVPROG-mv}"
|
|
||||||
cpprog="${CPPROG-cp}"
|
|
||||||
chmodprog="${CHMODPROG-chmod}"
|
|
||||||
chownprog="${CHOWNPROG-chown}"
|
|
||||||
chgrpprog="${CHGRPPROG-chgrp}"
|
|
||||||
stripprog="${STRIPPROG-strip}"
|
|
||||||
rmprog="${RMPROG-rm}"
|
|
||||||
mkdirprog="${MKDIRPROG-mkdir}"
|
|
||||||
|
|
||||||
transformbasename=""
|
|
||||||
transform_arg=""
|
|
||||||
instcmd="$mvprog"
|
|
||||||
chmodcmd="$chmodprog 0755"
|
|
||||||
chowncmd=""
|
|
||||||
chgrpcmd=""
|
|
||||||
stripcmd=""
|
|
||||||
rmcmd="$rmprog -f"
|
|
||||||
mvcmd="$mvprog"
|
|
||||||
src=""
|
|
||||||
dst=""
|
|
||||||
dir_arg=""
|
|
||||||
|
|
||||||
while [ x"$1" != x ]; do
|
|
||||||
case $1 in
|
|
||||||
-c) instcmd="$cpprog"
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-d) dir_arg=true
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-m) chmodcmd="$chmodprog $2"
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-o) chowncmd="$chownprog $2"
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-g) chgrpcmd="$chgrpprog $2"
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-s) stripcmd="$stripprog"
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
*) if [ x"$src" = x ]
|
|
||||||
then
|
|
||||||
src=$1
|
|
||||||
else
|
|
||||||
# this colon is to work around a 386BSD /bin/sh bug
|
|
||||||
:
|
|
||||||
dst=$1
|
|
||||||
fi
|
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ x"$src" = x ]
|
|
||||||
then
|
|
||||||
echo "install: no input file specified"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ x"$dir_arg" != x ]; then
|
|
||||||
dst=$src
|
|
||||||
src=""
|
|
||||||
|
|
||||||
if [ -d $dst ]; then
|
|
||||||
instcmd=:
|
|
||||||
else
|
|
||||||
instcmd=mkdir
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
|
|
||||||
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
|
|
||||||
# might cause directories to be created, which would be especially bad
|
|
||||||
# if $src (and thus $dsttmp) contains '*'.
|
|
||||||
|
|
||||||
if [ -f $src -o -d $src ]
|
|
||||||
then
|
|
||||||
true
|
|
||||||
else
|
|
||||||
echo "install: $src does not exist"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ x"$dst" = x ]
|
|
||||||
then
|
|
||||||
echo "install: no destination specified"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If destination is a directory, append the input filename; if your system
|
|
||||||
# does not like double slashes in filenames, you may need to add some logic
|
|
||||||
|
|
||||||
if [ -d $dst ]
|
|
||||||
then
|
|
||||||
dst="$dst"/`basename $src`
|
|
||||||
else
|
|
||||||
true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
## this sed command emulates the dirname command
|
|
||||||
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
|
|
||||||
|
|
||||||
# Make sure that the destination directory exists.
|
|
||||||
# this part is taken from Noah Friedman's mkinstalldirs script
|
|
||||||
|
|
||||||
# Skip lots of stat calls in the usual case.
|
|
||||||
if [ ! -d "$dstdir" ]; then
|
|
||||||
defaultIFS='
|
|
||||||
'
|
|
||||||
IFS="${IFS-${defaultIFS}}"
|
|
||||||
|
|
||||||
oIFS="${IFS}"
|
|
||||||
# Some sh's can't handle IFS=/ for some reason.
|
|
||||||
IFS='%'
|
|
||||||
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
|
|
||||||
IFS="${oIFS}"
|
|
||||||
|
|
||||||
pathcomp=''
|
|
||||||
|
|
||||||
while [ $# -ne 0 ] ; do
|
|
||||||
pathcomp="${pathcomp}${1}"
|
|
||||||
shift
|
|
||||||
|
|
||||||
if [ ! -d "${pathcomp}" ] ;
|
|
||||||
then
|
|
||||||
$mkdirprog "${pathcomp}"
|
|
||||||
else
|
|
||||||
true
|
|
||||||
fi
|
|
||||||
|
|
||||||
pathcomp="${pathcomp}/"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ x"$dir_arg" != x ]
|
|
||||||
then
|
|
||||||
$doit $instcmd $dst &&
|
|
||||||
|
|
||||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
|
|
||||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
|
|
||||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
|
|
||||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
|
|
||||||
else
|
|
||||||
|
|
||||||
# If we're going to rename the final executable, determine the name now.
|
|
||||||
|
|
||||||
if [ x"$transformarg" = x ]
|
|
||||||
then
|
|
||||||
dstfile=`basename $dst`
|
|
||||||
else
|
|
||||||
dstfile=`basename $dst $transformbasename |
|
|
||||||
sed $transformarg`$transformbasename
|
|
||||||
fi
|
|
||||||
|
|
||||||
# don't allow the sed command to completely eliminate the filename
|
|
||||||
|
|
||||||
if [ x"$dstfile" = x ]
|
|
||||||
then
|
|
||||||
dstfile=`basename $dst`
|
|
||||||
else
|
|
||||||
true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Make a temp file name in the proper directory.
|
|
||||||
|
|
||||||
dsttmp=$dstdir/#inst.$$#
|
|
||||||
|
|
||||||
# Move or copy the file name to the temp name
|
|
||||||
|
|
||||||
$doit $instcmd $src $dsttmp &&
|
|
||||||
|
|
||||||
trap "rm -f ${dsttmp}" 0 &&
|
|
||||||
|
|
||||||
# and set any options; do chmod last to preserve setuid bits
|
|
||||||
|
|
||||||
# If any of these fail, we abort the whole thing. If we want to
|
|
||||||
# ignore errors from any of these, just make sure not to ignore
|
|
||||||
# errors from the above "$doit $instcmd $src $dsttmp" command.
|
|
||||||
|
|
||||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
|
|
||||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
|
|
||||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
|
|
||||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
|
|
||||||
|
|
||||||
# Now rename the file to the real destination.
|
|
||||||
|
|
||||||
$doit $rmcmd -f $dstdir/$dstfile &&
|
|
||||||
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
|
||||||
|
|
||||||
fi &&
|
|
||||||
|
|
||||||
|
|
||||||
exit 0
|
|
|
@ -1,25 +0,0 @@
|
||||||
|
|
||||||
include ../config/include.mk
|
|
||||||
|
|
||||||
include ../vsn.mk
|
|
||||||
VSN=$(FD_SERVER_VSN)
|
|
||||||
|
|
||||||
ifeq ($(TYPE),debug)
|
|
||||||
DEBUG_FLAGS = -Ddebug
|
|
||||||
else
|
|
||||||
DEBUG_FLAGS =
|
|
||||||
endif
|
|
||||||
|
|
||||||
MODULES=bfile
|
|
||||||
|
|
||||||
EBIN = ../ebin
|
|
||||||
EBIN_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(EBIN)/bfile.app
|
|
||||||
|
|
||||||
|
|
||||||
all: $(EBIN_FILES)
|
|
||||||
|
|
||||||
debug:
|
|
||||||
$(MAKE) DEBUG=-DDEBUG
|
|
||||||
clean:
|
|
||||||
rm -rf $(EBIN_FILES)
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
{application,bfile,
|
|
||||||
[{description,"Fast FILE interface"},
|
|
||||||
{vsn,"%VSN%"},
|
|
||||||
{modules,[%MODULES%]},
|
|
||||||
{applications,[kernel,stdlib]},
|
|
||||||
{env, []},
|
|
||||||
{mod,{bfile,[]}}]}.
|
|
|
@ -1,224 +0,0 @@
|
||||||
%%%
|
|
||||||
%%% File : bfile.erl
|
|
||||||
%%% Author : Claes Wikstrom <klacke@kaja.klacke.net>
|
|
||||||
%%% Purpose : Interface to stdio buffered FILE io
|
|
||||||
%%% Created : 22 Nov 1999 by Claes Wikstrom <klacke@kaja.klacke.net>
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(bfile).
|
|
||||||
-vsn("$Revision: 1.1 $ ").
|
|
||||||
-author('klacke@kaja.klacke.net').
|
|
||||||
|
|
||||||
-export([
|
|
||||||
load_driver/0,
|
|
||||||
fopen/2,
|
|
||||||
fclose/1,
|
|
||||||
fread/2,
|
|
||||||
fwrite/2,
|
|
||||||
feof/1,
|
|
||||||
ferror/1,
|
|
||||||
set_linebuf_size/2,
|
|
||||||
fseek/3,
|
|
||||||
ftell/1,
|
|
||||||
ftruncate/1,
|
|
||||||
fflush/1,
|
|
||||||
frewind/1,
|
|
||||||
fgetc/1,
|
|
||||||
fungetc/2,
|
|
||||||
fgets/1,
|
|
||||||
gets/1,
|
|
||||||
pwrite/3,
|
|
||||||
pread/3
|
|
||||||
]).
|
|
||||||
|
|
||||||
|
|
||||||
%% Opcodes
|
|
||||||
-define(OPEN, $o).
|
|
||||||
-define(CLOSE, $c).
|
|
||||||
-define(READ, $r).
|
|
||||||
-define(WRITE, $w).
|
|
||||||
-define(SEEK, $s).
|
|
||||||
-define(TELL, $t).
|
|
||||||
-define(TRUNCATE, $T).
|
|
||||||
-define(FLUSH, $f).
|
|
||||||
-define(OEOF, $e).
|
|
||||||
-define(ERROR, $E).
|
|
||||||
-define(GETC, $g).
|
|
||||||
-define(GETS, $G).
|
|
||||||
-define(GETS2, $2).
|
|
||||||
-define(SET_LINEBUF_SIZE, $S).
|
|
||||||
-define(UNGETC, $u).
|
|
||||||
|
|
||||||
|
|
||||||
%% ret codes
|
|
||||||
-define(VALUE, $v).
|
|
||||||
-define(FLINE, $L).
|
|
||||||
-define(OK, $o).
|
|
||||||
-define(I32, $O).
|
|
||||||
-define(NOLINE,$N).
|
|
||||||
-define(FERROR, $E).
|
|
||||||
-define(REOF, $x).
|
|
||||||
|
|
||||||
-define(int32(X),
|
|
||||||
[((X) bsr 24) band 16#ff, ((X) bsr 16) band 16#ff,
|
|
||||||
((X) bsr 8) band 16#ff, (X) band 16#ff]).
|
|
||||||
%% Bytes to unsigned
|
|
||||||
-define(u32(X3,X2,X1,X0),
|
|
||||||
(((X3) bsl 24) bor ((X2) bsl 16) bor ((X1) bsl 8) bor (X0))).
|
|
||||||
%% Bytes to signed
|
|
||||||
-define(i32(X3,X2,X1,X0),
|
|
||||||
(?u32(X3,X2,X1,X0) -
|
|
||||||
(if (X3) > 127 -> 16#100000000; true -> 0 end))).
|
|
||||||
|
|
||||||
load_driver() ->
|
|
||||||
Dir = filename:join([filename:dirname(code:which(bfile)),"..", "priv"]),
|
|
||||||
erl_ddll:load_driver(Dir, "FILE_drv").
|
|
||||||
|
|
||||||
|
|
||||||
%% Flags = "r" | "w" | ... etc, see fopen(3)
|
|
||||||
%% Ret: {ok, Fd} | {error, Reason}
|
|
||||||
|
|
||||||
fopen(Fname, Flags) ->
|
|
||||||
P = open_port({spawn, 'FILE_drv'}, [binary]),
|
|
||||||
Res = erlang_port_control(P, ?OPEN, [Fname, 0, Flags, 0]),
|
|
||||||
case decode(Res) of
|
|
||||||
ok ->
|
|
||||||
{ok, {bfile, P}};
|
|
||||||
Err ->
|
|
||||||
unlink(P),
|
|
||||||
exit(P, die),
|
|
||||||
Err
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% void()
|
|
||||||
fclose({bfile, Fd}) ->
|
|
||||||
unlink(Fd),
|
|
||||||
catch erlang:port_close(Fd).
|
|
||||||
|
|
||||||
%% {ok, #Bin} | {error, Reason} | eof
|
|
||||||
fread({bfile, Fd}, Sz) ->
|
|
||||||
Res = erlang_port_control(Fd, ?READ, ?int32(Sz)),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% ok | {error, Reason}
|
|
||||||
fwrite({bfile, Fd}, IoList) ->
|
|
||||||
Res = erlang_port_control(Fd, ?WRITE, IoList),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% ok | {error, Reason}
|
|
||||||
pwrite(BFd, Pos, IoList) ->
|
|
||||||
case fseek(BFd, Pos, seek_set) of
|
|
||||||
ok ->
|
|
||||||
fwrite(BFd, IoList);
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% {ok, #Bin} | {error, Reason} | eof
|
|
||||||
pread(BFd, Pos, Sz) ->
|
|
||||||
case fseek(BFd, Pos, seek_set) of
|
|
||||||
ok ->
|
|
||||||
fread(BFd, Sz);
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% bool
|
|
||||||
feof({bfile, Fd}) ->
|
|
||||||
Res = erlang_port_control(Fd, ?OEOF, []),
|
|
||||||
bool(decode(Res)).
|
|
||||||
|
|
||||||
%% bool
|
|
||||||
ferror({bfile, Fd}) ->
|
|
||||||
Res = erlang_port_control(Fd, ?ERROR, []),
|
|
||||||
bool(decode(Res)).
|
|
||||||
|
|
||||||
|
|
||||||
%% void()
|
|
||||||
set_linebuf_size({bfile, Fd}, Sz) ->
|
|
||||||
Res = erlang_port_control(Fd, ?SET_LINEBUF_SIZE, ?int32(Sz)),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% Whence == seek_set | seek_cur || seek_end
|
|
||||||
%% ok | {error, Reason}
|
|
||||||
fseek({bfile, Fd}, Offs, Whence) ->
|
|
||||||
Res = erlang_port_control(Fd, ?SEEK, [?int32(Offs), whence_enc(Whence)]),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% {ok, Int} | {error, Reason}
|
|
||||||
ftell({bfile, Fd}) ->
|
|
||||||
Res = erlang_port_control(Fd, ?TELL, []),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% ok | {error, Reason}
|
|
||||||
ftruncate({bfile, Fd}) ->
|
|
||||||
Res = erlang_port_control(Fd, ?TRUNCATE, []),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% ok | {error, Reason}
|
|
||||||
fflush({bfile, Fd}) ->
|
|
||||||
Res = erlang_port_control(Fd, ?FLUSH, []),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% ok | {error, Reason}
|
|
||||||
frewind(BFd) ->
|
|
||||||
fseek(BFd, 0, seek_set).
|
|
||||||
|
|
||||||
%% {ok, Char} | {error, Reason} | eof
|
|
||||||
fgetc({bfile, Fd}) ->
|
|
||||||
Res = erlang_port_control(Fd, ?GETC, []),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% ok | {error, Reason}
|
|
||||||
fungetc({bfile, Fd}, Char) ->
|
|
||||||
Res = erlang_port_control(Fd, ?UNGETC, [Char]),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% {line, #Bin} | {noline, #Bin} | {error, Reason} | eof
|
|
||||||
%% including newline
|
|
||||||
fgets({bfile, Fd}) ->
|
|
||||||
Res = erlang_port_control(Fd, ?GETS, []),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
%% {line, #Bin} | {noline, #Bin} | {error, Reason} | eof
|
|
||||||
%% not including newline
|
|
||||||
gets({bfile, Fd}) ->
|
|
||||||
Res = erlang_port_control(Fd, ?GETS2, []),
|
|
||||||
decode(Res).
|
|
||||||
|
|
||||||
|
|
||||||
whence_enc(seek_set) ->
|
|
||||||
1;
|
|
||||||
whence_enc(seek_cur) ->
|
|
||||||
2;
|
|
||||||
whence_enc(seek_end) ->
|
|
||||||
3.
|
|
||||||
|
|
||||||
|
|
||||||
bool({ok, 1}) ->
|
|
||||||
true;
|
|
||||||
bool({ok, 0}) ->
|
|
||||||
false.
|
|
||||||
|
|
||||||
|
|
||||||
decode(Res) ->
|
|
||||||
case Res of
|
|
||||||
<<?VALUE, Bin/binary>> ->
|
|
||||||
{ok, Bin};
|
|
||||||
<<?FLINE, Bin/binary>> ->
|
|
||||||
{line, Bin};
|
|
||||||
<<?OK>> ->
|
|
||||||
ok;
|
|
||||||
<<?I32, X1, X2, X3, X4>> ->
|
|
||||||
{ok, ?i32(X1, X2, X3, X4)};
|
|
||||||
<<?NOLINE, Bin/binary>> ->
|
|
||||||
{noline, Bin};
|
|
||||||
<<?FERROR, Err/binary>> ->
|
|
||||||
{error, list_to_atom(binary_to_list(Err))};
|
|
||||||
<<?REOF>> ->
|
|
||||||
eof
|
|
||||||
end.
|
|
||||||
|
|
||||||
erlang_port_control(P, C, Data) ->
|
|
||||||
erlang:port_control(P, C, Data).
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
-module(read).
|
|
||||||
-export([start/1, start/2]).
|
|
||||||
|
|
||||||
scan_file(F, Readsize, Total) ->
|
|
||||||
Rd = bfile:fread(F, Readsize),
|
|
||||||
case Rd of
|
|
||||||
{ok, Bin} -> scan_file(F, Readsize, size(Bin)+Total);
|
|
||||||
eof -> Total
|
|
||||||
end.
|
|
||||||
scan_file(F, Readsize) -> scan_file(F, Readsize, 0).
|
|
||||||
|
|
||||||
start(File, Readsize) ->
|
|
||||||
bfile:load_driver(),
|
|
||||||
{ok, F} = bfile:fopen(File, "r"),
|
|
||||||
T = scan_file(F, Readsize),
|
|
||||||
io:format("read ~p bytes~n", [T]),
|
|
||||||
bfile:fclose(F).
|
|
||||||
start(File) ->
|
|
||||||
start(File, 512*1024).
|
|
|
@ -1,18 +0,0 @@
|
||||||
-module(readold).
|
|
||||||
-export([start/1, start/2]).
|
|
||||||
|
|
||||||
scan_file(F, Readsize, Total) ->
|
|
||||||
Rd = file:read(F, Readsize),
|
|
||||||
case Rd of
|
|
||||||
{ok, Bin} -> scan_file(F, Readsize, size(Bin)+Total);
|
|
||||||
eof -> Total
|
|
||||||
end.
|
|
||||||
scan_file(F, Readsize) -> scan_file(F, Readsize, 0).
|
|
||||||
|
|
||||||
start(File, Readsize) ->
|
|
||||||
{ok, F} = file:open(File, [raw, binary, read]),
|
|
||||||
T = scan_file(F, Readsize),
|
|
||||||
io:format("read ~p bytes~n", [T]),
|
|
||||||
file:close(F).
|
|
||||||
start(File) ->
|
|
||||||
start(File, 512*1024).
|
|
|
@ -1,17 +0,0 @@
|
||||||
-module(write).
|
|
||||||
-export([start/2, start/3]).
|
|
||||||
|
|
||||||
dump_file(F, Data, 0) ->
|
|
||||||
ok;
|
|
||||||
dump_file(F, Data, N) ->
|
|
||||||
bfile:fwrite(F, Data),
|
|
||||||
dump_file(F, Data, N - 1).
|
|
||||||
|
|
||||||
start(File, Data, N) ->
|
|
||||||
bfile:load_driver(),
|
|
||||||
{ok, F} = bfile:fopen(File, "w"),
|
|
||||||
dump_file(F, Data, N),
|
|
||||||
bfile:fclose(F).
|
|
||||||
start(File, N) ->
|
|
||||||
Data = list_to_binary(lists:duplicate(1000000, 10)),
|
|
||||||
start(File, Data, N).
|
|
|
@ -1,16 +0,0 @@
|
||||||
-module(writeold).
|
|
||||||
-export([start/2, start/3]).
|
|
||||||
|
|
||||||
dump_file(F, Data, 0) ->
|
|
||||||
ok;
|
|
||||||
dump_file(F, Data, N) ->
|
|
||||||
file:write(F, Data),
|
|
||||||
dump_file(F, Data, N - 1).
|
|
||||||
|
|
||||||
start(File, Data, N) ->
|
|
||||||
{ok, F} = file:open(File, [raw, binary, write]),
|
|
||||||
dump_file(F, Data, N),
|
|
||||||
file:close(F).
|
|
||||||
start(File, N) ->
|
|
||||||
Data = list_to_binary(lists:duplicate(1000000, 10)),
|
|
||||||
start(File, Data, N).
|
|
|
@ -1 +0,0 @@
|
||||||
BFILE_VSN=1.0
|
|
381
dns/src/dns.erl
381
dns/src/dns.erl
|
@ -1,381 +0,0 @@
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
%% @copyright Process One 2008-2009
|
|
||||||
%% @author Geoff Cant <geoff.cant@process-one.net>
|
|
||||||
%% @author Geoff Cant <nem@erlang.geek.nz>
|
|
||||||
%% @version {@vsn}, {@date} {@time}
|
|
||||||
%% @doc DNS Lookup and utility functions.
|
|
||||||
%%
|
|
||||||
%% Provides a programmer-friendly API for a number of undocumented OTP
|
|
||||||
%% dns lookup, resolution, caching and configuration functions.
|
|
||||||
%%
|
|
||||||
%% Also provides utility functions for performing lookups while
|
|
||||||
%% bypassing local resolver caching, finding closest parent zones,
|
|
||||||
%% parsing `resolv.conf' files, simplifying #dns_rec{} answers and more.
|
|
||||||
%% @end
|
|
||||||
%%%-------------------------------------------------------------------
|
|
||||||
-module(dns).
|
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([lookup/2
|
|
||||||
,lookup/3
|
|
||||||
,lookup/4
|
|
||||||
,lookup/5
|
|
||||||
,lookup/7
|
|
||||||
,lookup_cache/3
|
|
||||||
,cache_lookup/1
|
|
||||||
,simplify/1]).
|
|
||||||
|
|
||||||
-export([find_soa/1
|
|
||||||
,nameservers/1
|
|
||||||
,nameservers/2
|
|
||||||
,nameserver_address/1
|
|
||||||
,direct_lookup/3
|
|
||||||
,info/2
|
|
||||||
,to_proplist/1
|
|
||||||
,parse_resolv/1
|
|
||||||
,domain_exists/1
|
|
||||||
,expand_options/1
|
|
||||||
,resolvers/0
|
|
||||||
]).
|
|
||||||
|
|
||||||
-include_lib("kernel/src/inet_dns.hrl").
|
|
||||||
-include_lib("kernel/include/inet.hrl").
|
|
||||||
|
|
||||||
%% @type query_class() = in.
|
|
||||||
%% An atom with the name of a DNS query class. `in' is the only class
|
|
||||||
%% used (and probably tested) in the OTP lookup code.
|
|
||||||
|
|
||||||
%% @type query_type() = a | cname | soa | mx | ns | srv | any.
|
|
||||||
%% An atom with the name of a DNS query type. This list is not
|
|
||||||
%% exhaustive.
|
|
||||||
|
|
||||||
%% @type simple_rr() = {Name::string(), query_type(), RRdata::term(), [simple_rr_info()]}.
|
|
||||||
%% A simplified version of #dns_rr{} resource records.
|
|
||||||
|
|
||||||
%% @type simple_rr_info() = {ttl, TimeToLive::integer()} | {class, query_class()}.
|
|
||||||
|
|
||||||
%% @type ip_address() = {integer(),integer(),integer(),integer()}.
|
|
||||||
|
|
||||||
%% @type address() = ip_address() | {ip_address(), port_no()} |
|
|
||||||
%% string() | #dns_rr{}.
|
|
||||||
|
|
||||||
%% @type port_no() = integer().
|
|
||||||
|
|
||||||
%% @type resolver() = {ip_address(), port_no()}.
|
|
||||||
|
|
||||||
%% @type query_options() = [query_option()].
|
|
||||||
%% A proplist of configuration options for lookup behaviour.
|
|
||||||
|
|
||||||
%% @type query_option() = {read_cache, bool()} |
|
|
||||||
%% {write_cache, bool()} |
|
|
||||||
%% {timeout, TimeOut::integer()} |
|
|
||||||
%% {servers, [address()]} |
|
|
||||||
%% {class, query_class()} |
|
|
||||||
%% defaults | read_cached.
|
|
||||||
%% <ul>
|
|
||||||
%% <li>`read_cache' - if `true' (default: `false'), return results from the `inet_db' cache
|
|
||||||
%% before making a query over the network.</li>
|
|
||||||
%% <li>`write_cache' - if `true' (default: `false'), write successful query result RRs to
|
|
||||||
%% the `inet_db' cache</li>
|
|
||||||
%% <li>`timeout' - number of milliseconds (default: 5000) to wait for a response from a
|
|
||||||
%% nameserver.</li>
|
|
||||||
%% <li>`servers' - a list of nameservers to query for the RRs. Defaults
|
|
||||||
%% to `inet_db:res_option(nameserver)' which is usually the nameservers
|
|
||||||
%% specified in '/etc/resolv.conf'</li>
|
|
||||||
%% <li>`class' - record class to query for (default: `in').</li>
|
|
||||||
%% <li>`defaults' - shorthand for
|
|
||||||
%% <code>
|
|
||||||
%% [{servers, dns:resolvers()},
|
|
||||||
%% {read_cache, false}, {write_cache, false},
|
|
||||||
%% {timeout, timer:seconds(5)}, {class, in}]
|
|
||||||
%% </code>
|
|
||||||
%% </li>
|
|
||||||
%% <li>`read_cached' - shorthand for
|
|
||||||
%% <code>
|
|
||||||
%% [{servers, dns:resolvers()},
|
|
||||||
%% {read_cache, true}, {write_cache, false},
|
|
||||||
%% {timeout, timer:seconds(5)}, {class, in}]
|
|
||||||
%% </code>
|
|
||||||
%% </li>
|
|
||||||
%% </ul>
|
|
||||||
|
|
||||||
%%====================================================================
|
|
||||||
%% RR lookup API
|
|
||||||
%%====================================================================
|
|
||||||
|
|
||||||
%% @spec lookup(Name::string(), query_type()) -> #dns_res{}
|
|
||||||
%% @doc Query for a DNS record of Type for Name. Uses reasonable default
|
|
||||||
%% options for class (`in') timeouts (5s), caching (no caching) and
|
|
||||||
%% nameservers (`inet_db' defaults).
|
|
||||||
%% @end
|
|
||||||
%% @equiv lookup(Name, Type, [defaults])
|
|
||||||
lookup(Name, Type)
|
|
||||||
when is_list(Name), is_atom(Type) ->
|
|
||||||
lookup(Name, Type, [defaults]).
|
|
||||||
|
|
||||||
%% @spec lookup(Name::string(), query_type(), query_options()) -> #dns_res{}
|
|
||||||
%% @doc Query for a DNS record of Type for Name. Takes a variety of
|
|
||||||
%% query options.
|
|
||||||
%% @end
|
|
||||||
lookup(Name, Type, Options)
|
|
||||||
when is_list(Name), is_atom(Type),
|
|
||||||
is_list(Options) ->
|
|
||||||
lookup2(Name, Type, expand_options(Options)).
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
lookup2(Name, Type, Options) ->
|
|
||||||
Timeout = proplists:get_value(timeout, Options, timer:seconds(5)),
|
|
||||||
Servers = [nameserver_address(R)
|
|
||||||
|| R <- proplists:get_value(servers, Options, resolvers())],
|
|
||||||
Class = proplists:get_value(class, Options, in),
|
|
||||||
ReadCache = proplists:get_value(read_cache, Options, false),
|
|
||||||
WriteCache = proplists:get_value(write_cache, Options, false),
|
|
||||||
lookup(Name, Class, Type, Servers, Timeout, ReadCache, WriteCache).
|
|
||||||
|
|
||||||
%% @spec lookup(Name::string(), query_type(),
|
|
||||||
%% NameServers, Timeout::integer()) -> #dns_res{}
|
|
||||||
%% where NameServers = [resolver()]
|
|
||||||
%% @doc Query the given Nameservers for a DNS record of Type (class `in') for
|
|
||||||
%% Name. Takes a Timeout in milliseconds. Doesn't read or write the
|
|
||||||
%% `inet_db' RR cache.
|
|
||||||
%% @equiv lookup(Name, in, Type, Servers, Timeout, false, false)
|
|
||||||
lookup(Name, Type, Servers, Timeout)
|
|
||||||
when is_list(Name), is_atom(Type),
|
|
||||||
is_list(Servers), is_integer(Timeout) ->
|
|
||||||
lookup(Name, in, Type, Servers, Timeout).
|
|
||||||
|
|
||||||
%% @spec lookup(Name::string(), query_class(), query_type(),
|
|
||||||
%% [resolver()], Timeout::integer()) -> #dns_res{}
|
|
||||||
%% @doc Query the given Nameservers for a DNS record of Class, Type for
|
|
||||||
%% Name. Takes a Timeout in milliseconds. Doesn't read or write the
|
|
||||||
%% `inet_db' RR cache.
|
|
||||||
%% @equiv lookup(Name, Class, Type, Servers, Timeout, false, false)
|
|
||||||
lookup(Name, Class, Type, Servers, Timeout)
|
|
||||||
when is_list(Name), is_atom(Class), is_atom(Type),
|
|
||||||
is_list(Servers), is_integer(Timeout) ->
|
|
||||||
lookup(Name, Class, Type, Servers, Timeout, false, false).
|
|
||||||
|
|
||||||
%% @spec lookup(Name::string(), class(), query_type(),
|
|
||||||
%% Servers::[address()], Timeout::integer(),
|
|
||||||
%% ReadCache::bool(), WriteCache::bool()) -> #dns_res{}
|
|
||||||
%% @doc Query the given Nameservers for a DNS record of Class, Type for
|
|
||||||
%% Name. Takes a Timeout in milliseconds. Returns results from the
|
|
||||||
%% `inet_db' RR cache if available and ReadCache is `true'. Writes
|
|
||||||
%% successful query answers to the cache if WriteCache is `true'.
|
|
||||||
%% @end
|
|
||||||
%% Query inet_db cache.
|
|
||||||
lookup(Name, Class = in, Type, Servers, Timeout,
|
|
||||||
_ReadCache = true, WriteCache) ->
|
|
||||||
case lookup_cache(Class, Name, Type) of
|
|
||||||
{ok, Answer} -> {ok, Answer};
|
|
||||||
{error, nxdomain} ->
|
|
||||||
lookup(Name, Class, Type, Servers, Timeout, false, WriteCache)
|
|
||||||
end;
|
|
||||||
%% Perform DNS lookup and write results to inet_db cache
|
|
||||||
lookup(Name, Class, Type, Servers, Timeout,
|
|
||||||
_ReadCache, _WriteCache = true) ->
|
|
||||||
case lookup(Name, Class, Type, Servers, Timeout, false, false) of
|
|
||||||
{ok, Rec} -> cache_lookup(Rec), Rec;
|
|
||||||
Else -> Else
|
|
||||||
end;
|
|
||||||
%% Lookup via DNS
|
|
||||||
lookup(Name, Class, Type, Servers, Timeout,
|
|
||||||
_ReadCache = false, _WriteCache = false) ->
|
|
||||||
inet_res:nnslookup(Name, Class, Type, Servers, Timeout).
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
%% Process options list
|
|
||||||
expand_options(Opts) ->
|
|
||||||
proplists:expand(option_expansions(), Opts).
|
|
||||||
%% @private
|
|
||||||
option_expansions() ->
|
|
||||||
[{defaults, [{servers, resolvers()},
|
|
||||||
{timeout, timer:seconds(5)},
|
|
||||||
{read_cache, false},
|
|
||||||
{write_cache, false}]}
|
|
||||||
,{read_cached, [{servers, resolvers()},
|
|
||||||
{timeout, timer:seconds(5)},
|
|
||||||
{read_cache, true},
|
|
||||||
{write_cache, false}]}
|
|
||||||
].
|
|
||||||
|
|
||||||
%% @spec simplify(Result) -> {error, Reason::term()} | [simple_rr()]
|
|
||||||
%% where Result = {ok, #dns_rec{}} | {error, Reason::term()}
|
|
||||||
%% @doc Simplify the records returned from lookup to fixed-format
|
|
||||||
%% tuples instead of records.
|
|
||||||
%% @end
|
|
||||||
simplify({ok, #dns_rec{anlist=A}}) ->
|
|
||||||
{ok, [{Name, Type, Data, [{ttl, TTL},{class,Class}]}
|
|
||||||
|| #dns_rr{domain=Name,class=Class,
|
|
||||||
type=Type,data=Data,
|
|
||||||
ttl=TTL} <- A]};
|
|
||||||
simplify(E) -> E.
|
|
||||||
|
|
||||||
%%====================================================================
|
|
||||||
%% Cache API
|
|
||||||
%%====================================================================
|
|
||||||
|
|
||||||
%% @spec lookup_cache(query_class(), Name::string(), query_type()) ->
|
|
||||||
%% {ok, #dns_rec{}} | {error, Reason::term()}
|
|
||||||
%% @doc Query `inet_db' RR cache. Converts the `inet_db' `#hostent{}' respone
|
|
||||||
%% into a fake `#dns_rec{}'.
|
|
||||||
lookup_cache(Class, Name, Type) ->
|
|
||||||
case inet_db:getbyname(Name, Type) of
|
|
||||||
{ok, #hostent{h_addr_list=Answers}} ->
|
|
||||||
%% Convert hostents to fake dns records
|
|
||||||
RRs = [#dns_rr{domain=Name, class=Class,
|
|
||||||
type=Type, ttl=cached,
|
|
||||||
data=D}
|
|
||||||
|| D <- Answers],
|
|
||||||
{ok, #dns_rec{header=cached, anlist=RRs}};
|
|
||||||
Else -> Else
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec cache_lookup(#dns_rec{}) -> ok
|
|
||||||
%% @doc Store dns answer resource records in inet_db cache
|
|
||||||
cache_lookup(#dns_rec{anlist=RRs}) ->
|
|
||||||
lists:foreach(fun inet_db:add_rr/1, RRs),
|
|
||||||
ok.
|
|
||||||
|
|
||||||
%%====================================================================
|
|
||||||
%% Utility API
|
|
||||||
%%====================================================================
|
|
||||||
|
|
||||||
%% @spec resolvers() -> [resolver()]
|
|
||||||
%% @doc Returns a list of name servers that can be used as recursive
|
|
||||||
%% resolvers. Resolvers taken from the `inet_db' `namserver' setting.
|
|
||||||
resolvers() ->
|
|
||||||
inet_db:res_option(nameserver).
|
|
||||||
|
|
||||||
%% @spec find_soa(Name::string()) -> {ok, {SoaName::string(), #dns_rr{}}} |
|
|
||||||
%% {error, Reason::term()}
|
|
||||||
%% @doc Recursively climb the ancestry of a domain name to find the
|
|
||||||
%% most specific SOA. (Closest domain delegation/authority)
|
|
||||||
%% Given `foo.bar.baz.com' would try `foo.bar.baz.com' then `bar.baz.com'
|
|
||||||
%% then `baz.com' and so on.
|
|
||||||
find_soa(Name) ->
|
|
||||||
Components = string:tokens(Name, "."),
|
|
||||||
find_soa2(Components).
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
find_soa2([]) ->
|
|
||||||
{error, nxdomain};
|
|
||||||
find_soa2(Components) ->
|
|
||||||
Name = string:join(Components,"."),
|
|
||||||
case lookup(Name, soa) of
|
|
||||||
{ok, #dns_rec{anlist=[RR = #dns_rr{domain=Name,type=soa}]}} ->
|
|
||||||
{ok, {Name, RR}};
|
|
||||||
{error, nxdomain} ->
|
|
||||||
find_soa2(tl(Components));
|
|
||||||
Else -> Else
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec domain_exists(DomainName::string()) -> bool()
|
|
||||||
%% @doc Returns `true' if a name exists and has an SOA record.
|
|
||||||
domain_exists(Name) ->
|
|
||||||
case lookup(Name, soa) of
|
|
||||||
{ok, #dns_rec{anlist=[#dns_rr{domain=Name,type=soa}]}} ->
|
|
||||||
true;
|
|
||||||
{error, nxdomain} ->
|
|
||||||
false;
|
|
||||||
Else -> erlang:error(Else)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec parse_resolv(FileName::string()) -> [ip_address()]
|
|
||||||
%% @doc Returns a list of nameservers from a POSIX style `resolv.conf' file.
|
|
||||||
parse_resolv(File) ->
|
|
||||||
{ok, Rs} = inet_parse:resolv(File),
|
|
||||||
[NS || {nameserver,NS} <- Rs].
|
|
||||||
|
|
||||||
%% @spec nameservers(Domain::string()) -> [resolver()]
|
|
||||||
%% @equiv nameservers(Domain, [read_cached])
|
|
||||||
nameservers(Domain) -> nameservers(Domain, [read_cached]).
|
|
||||||
|
|
||||||
%% @spec nameservers(Domain::string(), query_options()) -> [resolver()]
|
|
||||||
%% @doc Retrieve the names and addresses of the authoritative
|
|
||||||
%% nameservers for Domain. Will perform one NS lookup if the
|
|
||||||
%% resolving nameserver returns the address records for the queried
|
|
||||||
%% records, otherwise will perform 1 + N lookups (N = number of NS
|
|
||||||
%% records for Domain). Takes a list of query option that will be used
|
|
||||||
%% for NS lookups.
|
|
||||||
nameservers(Domain, Options) ->
|
|
||||||
case lookup(Domain, ns, Options) of
|
|
||||||
E = {error, _} -> E;
|
|
||||||
{ok, #dns_rec{anlist=RRs, arlist=AR}} ->
|
|
||||||
Names = [Name
|
|
||||||
|| #dns_rr{data=Name,type=ns,domain=D} <- RRs,
|
|
||||||
D =:= Domain],
|
|
||||||
Additional = [Addr || #dns_rr{data=Addr,type=a,domain=Name} <- AR,
|
|
||||||
lists:member(Name, Names)],
|
|
||||||
if length(Additional) > 0 ->
|
|
||||||
[nameserver_address(Addr) || Addr <- Additional];
|
|
||||||
true ->
|
|
||||||
[nameserver_address(RR) || RR <- RRs]
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec nameserver_address(address()) -> resolver() | error
|
|
||||||
%% @doc
|
|
||||||
%% Convert a variety of nameserver address specifications into a
|
|
||||||
%% host-port tuple for use with `gen_udp' and `inet_res'.
|
|
||||||
%% @end
|
|
||||||
%% @todo Should take/respect query options instead of calling inet:getaddr/2.
|
|
||||||
nameserver_address(IP = {_, _, _, _}) -> {IP,53};
|
|
||||||
nameserver_address(Addr = {{_, _, _, _}, _}) -> Addr;
|
|
||||||
nameserver_address(Name) when is_list(Name) ->
|
|
||||||
case inet:getaddr(Name, inet) of
|
|
||||||
{ok, IP} -> {IP, 53};
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
nameserver_address(#dns_rr{data=Name,type=ns}) ->
|
|
||||||
nameserver_address(Name).
|
|
||||||
|
|
||||||
%% @spec direct_lookup(Name::string(), query_type(), Domain::string())
|
|
||||||
%% -> #dns_res{}
|
|
||||||
%% @doc Lookup the given Name/Type against the authoritative NSs for
|
|
||||||
%% Domain. This will bypass nxdomain caching and should result in
|
|
||||||
%% quicker recognition of changed/added records.
|
|
||||||
%% This is mainly intended for checking if people have setup a set of
|
|
||||||
%% DNS records correctly (say for a white-label hosting service).
|
|
||||||
direct_lookup(Name, Type, Domain) ->
|
|
||||||
Servers = [{A,53} || {_NS, A} <- nameservers(Domain)],
|
|
||||||
lookup(Name, Type, [{servers, Servers}]).
|
|
||||||
|
|
||||||
%%====================================================================
|
|
||||||
%% inet record manipulation and access API
|
|
||||||
%%====================================================================
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
info(Field, Rec) ->
|
|
||||||
Fields = fields(Rec),
|
|
||||||
FieldIdx = lists:zip(Fields, lists:seq(2,length(Fields)+1)),
|
|
||||||
element(proplists:get_value(Field,FieldIdx), Rec).
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
fields(#dns_rec{}) -> fields(dns_rec);
|
|
||||||
fields(dns_rec) -> record_info(fields, dns_rec);
|
|
||||||
fields(#dns_rr{}) -> fields(dns_rr);
|
|
||||||
fields(dns_rr) -> record_info(fields, dns_rr).
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
to_proplist(R) ->
|
|
||||||
Keys = fields(R),
|
|
||||||
Values = tl(tuple_to_list(R)),
|
|
||||||
lists:zip(Keys,Values).
|
|
||||||
|
|
||||||
%%====================================================================
|
|
||||||
%% Unit Tests
|
|
||||||
%%====================================================================
|
|
||||||
%% @todo Add more unit tests.
|
|
||||||
|
|
||||||
%% @private
|
|
||||||
nameserver_address_test_() ->
|
|
||||||
T = [{{1,2,3,4}, {{1,2,3,4},53}},
|
|
||||||
{{{1,2,3,4},53}, {{1,2,3,4},53}},
|
|
||||||
{#dns_rr{data={1,2,3,4},type=ns}, {{1,2,3,4},53}},
|
|
||||||
{"localhost", {{127,0,0,1},53}},
|
|
||||||
{#dns_rr{data="localhost",type=ns}, {{127,0,0,1},53}}],
|
|
||||||
[ ?_assertMatch(C when C =:= B, nameserver_address(A))
|
|
||||||
|| {A,B} <- T].
|
|
|
@ -1,5 +0,0 @@
|
||||||
ejabberd-dev is an helper module containing include files from the ejabberd
|
|
||||||
project.
|
|
||||||
|
|
||||||
This should allow to develop or build ejabberd modules without a complete
|
|
||||||
development version of ejabberd (for example with a binary version of ejabberd).
|
|
|
@ -1,36 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-record(adhoc_request, {lang,
|
|
||||||
node,
|
|
||||||
sessionid,
|
|
||||||
action,
|
|
||||||
xdata,
|
|
||||||
others}).
|
|
||||||
|
|
||||||
-record(adhoc_response, {lang,
|
|
||||||
node,
|
|
||||||
sessionid,
|
|
||||||
status,
|
|
||||||
defaultaction = "",
|
|
||||||
actions = [],
|
|
||||||
notes = [],
|
|
||||||
elements = []}).
|
|
|
@ -1,64 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
%% This macro returns a string of the ejabberd version running, e.g. "2.3.4"
|
|
||||||
%% If the ejabberd application description isn't loaded, returns atom: undefined
|
|
||||||
-define(VERSION, element(2, application:get_key(ejabberd,vsn))).
|
|
||||||
|
|
||||||
-define(MYHOSTS, ejabberd_config:get_global_option(hosts)).
|
|
||||||
-define(MYNAME, hd(ejabberd_config:get_global_option(hosts))).
|
|
||||||
-define(MYLANG, ejabberd_config:get_global_option(language)).
|
|
||||||
|
|
||||||
-define(MSGS_DIR, "msgs").
|
|
||||||
-define(CONFIG_PATH, "ejabberd.cfg").
|
|
||||||
-define(LOG_PATH, "ejabberd.log").
|
|
||||||
|
|
||||||
-define(EJABBERD_URI, "http://www.process-one.net/en/ejabberd/").
|
|
||||||
|
|
||||||
-define(S2STIMEOUT, 600000).
|
|
||||||
|
|
||||||
%%-define(DBGFSM, true).
|
|
||||||
|
|
||||||
-record(scram, {storedkey, serverkey, salt, iterationcount}).
|
|
||||||
-define(SCRAM_DEFAULT_ITERATION_COUNT, 4096).
|
|
||||||
|
|
||||||
%% ---------------------------------
|
|
||||||
%% Logging mechanism
|
|
||||||
|
|
||||||
%% Print in standard output
|
|
||||||
-define(PRINT(Format, Args),
|
|
||||||
io:format(Format, Args)).
|
|
||||||
|
|
||||||
-define(DEBUG(Format, Args),
|
|
||||||
ejabberd_logger:debug_msg(?MODULE,?LINE,Format, Args)).
|
|
||||||
|
|
||||||
-define(INFO_MSG(Format, Args),
|
|
||||||
ejabberd_logger:info_msg(?MODULE,?LINE,Format, Args)).
|
|
||||||
|
|
||||||
-define(WARNING_MSG(Format, Args),
|
|
||||||
ejabberd_logger:warning_msg(?MODULE,?LINE,Format, Args)).
|
|
||||||
|
|
||||||
-define(ERROR_MSG(Format, Args),
|
|
||||||
ejabberd_logger:error_msg(?MODULE,?LINE,Format, Args)).
|
|
||||||
|
|
||||||
-define(CRITICAL_MSG(Format, Args),
|
|
||||||
ejabberd_logger:critical_msg(?MODULE,?LINE,Format, Args)).
|
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-record(ejabberd_commands, {name, tags = [],
|
|
||||||
desc = "", longdesc = "",
|
|
||||||
module, function,
|
|
||||||
args = [], result = rescode}).
|
|
||||||
|
|
||||||
%% @type ejabberd_commands() = #ejabberd_commands{
|
|
||||||
%% name = atom(),
|
|
||||||
%% tags = [atom()],
|
|
||||||
%% desc = string(),
|
|
||||||
%% longdesc = string(),
|
|
||||||
%% module = atom(),
|
|
||||||
%% function = atom(),
|
|
||||||
%% args = [aterm()],
|
|
||||||
%% result = rterm()
|
|
||||||
%% }.
|
|
||||||
%% desc: Description of the command
|
|
||||||
%% args: Describe the accepted arguments.
|
|
||||||
%% This way the function that calls the command can format the
|
|
||||||
%% arguments before calling.
|
|
||||||
|
|
||||||
%% @type atype() = integer | string | {tuple, [aterm()]} | {list, aterm()}.
|
|
||||||
%% Allowed types for arguments are integer, string, tuple and list.
|
|
||||||
|
|
||||||
%% @type rtype() = integer | string | atom | {tuple, [rterm()]} | {list, rterm()} | rescode | restuple.
|
|
||||||
%% A rtype is either an atom or a tuple with two elements.
|
|
||||||
|
|
||||||
%% @type aterm() = {Name::atom(), Type::atype()}.
|
|
||||||
%% An argument term is a tuple with the term name and the term type.
|
|
||||||
|
|
||||||
%% @type rterm() = {Name::atom(), Type::rtype()}.
|
|
||||||
%% A result term is a tuple with the term name and the term type.
|
|
|
@ -1,28 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-record(config, {key, value}).
|
|
||||||
-record(local_config, {key, value}).
|
|
||||||
-record(state, {opts = [],
|
|
||||||
hosts = [],
|
|
||||||
override_local = false,
|
|
||||||
override_global = false,
|
|
||||||
override_acls = false}).
|
|
|
@ -1,25 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-define(STATUS_SUCCESS, 0).
|
|
||||||
-define(STATUS_ERROR, 1).
|
|
||||||
-define(STATUS_USAGE, 2).
|
|
||||||
-define(STATUS_BADRPC, 3).
|
|
|
@ -1,336 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-define(NS_DISCO_ITEMS, "http://jabber.org/protocol/disco#items").
|
|
||||||
-define(NS_DISCO_INFO, "http://jabber.org/protocol/disco#info").
|
|
||||||
-define(NS_VCARD, "vcard-temp").
|
|
||||||
-define(NS_VCARD_UPDATE, "vcard-temp:x:update").
|
|
||||||
-define(NS_AUTH, "jabber:iq:auth").
|
|
||||||
-define(NS_AUTH_ERROR, "jabber:iq:auth:error").
|
|
||||||
-define(NS_REGISTER, "jabber:iq:register").
|
|
||||||
-define(NS_SEARCH, "jabber:iq:search").
|
|
||||||
-define(NS_ROSTER, "jabber:iq:roster").
|
|
||||||
-define(NS_ROSTER_VER, "urn:xmpp:features:rosterver").
|
|
||||||
-define(NS_PRIVACY, "jabber:iq:privacy").
|
|
||||||
-define(NS_BLOCKING, "urn:xmpp:blocking").
|
|
||||||
-define(NS_PRIVATE, "jabber:iq:private").
|
|
||||||
-define(NS_VERSION, "jabber:iq:version").
|
|
||||||
-define(NS_TIME90, "jabber:iq:time"). % TODO: Remove once XEP-0090 is Obsolete
|
|
||||||
-define(NS_TIME, "urn:xmpp:time").
|
|
||||||
-define(NS_LAST, "jabber:iq:last").
|
|
||||||
-define(NS_XDATA, "jabber:x:data").
|
|
||||||
-define(NS_IQDATA, "jabber:iq:data").
|
|
||||||
-define(NS_DELAY91, "jabber:x:delay"). % TODO: Remove once XEP-0091 is Obsolete
|
|
||||||
-define(NS_DELAY, "urn:xmpp:delay").
|
|
||||||
-define(NS_EXPIRE, "jabber:x:expire").
|
|
||||||
-define(NS_EVENT, "jabber:x:event").
|
|
||||||
-define(NS_CHATSTATES, "http://jabber.org/protocol/chatstates").
|
|
||||||
-define(NS_XCONFERENCE, "jabber:x:conference").
|
|
||||||
-define(NS_STATS, "http://jabber.org/protocol/stats").
|
|
||||||
-define(NS_MUC, "http://jabber.org/protocol/muc").
|
|
||||||
-define(NS_MUC_USER, "http://jabber.org/protocol/muc#user").
|
|
||||||
-define(NS_MUC_ADMIN, "http://jabber.org/protocol/muc#admin").
|
|
||||||
-define(NS_MUC_OWNER, "http://jabber.org/protocol/muc#owner").
|
|
||||||
-define(NS_MUC_UNIQUE, "http://jabber.org/protocol/muc#unique").
|
|
||||||
-define(NS_PUBSUB, "http://jabber.org/protocol/pubsub").
|
|
||||||
-define(NS_PUBSUB_EVENT, "http://jabber.org/protocol/pubsub#event").
|
|
||||||
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
|
|
||||||
-define(NS_PUBSUB_NMI, "http://jabber.org/protocol/pubsub#node-meta-info").
|
|
||||||
-define(NS_PUBSUB_ERRORS,"http://jabber.org/protocol/pubsub#errors").
|
|
||||||
-define(NS_PUBSUB_NODE_CONFIG, "http://jabber.org/protocol/pubsub#node_config").
|
|
||||||
-define(NS_PUBSUB_SUB_OPTIONS, "http://jabber.org/protocol/pubsub#subscribe_options").
|
|
||||||
-define(NS_PUBSUB_SUB_AUTH, "http://jabber.org/protocol/pubsub#subscribe_authorization").
|
|
||||||
-define(NS_PUBSUB_GET_PENDING, "http://jabber.org/protocol/pubsub#get-pending").
|
|
||||||
-define(NS_COMMANDS, "http://jabber.org/protocol/commands").
|
|
||||||
-define(NS_BYTESTREAMS, "http://jabber.org/protocol/bytestreams").
|
|
||||||
-define(NS_ADMIN, "http://jabber.org/protocol/admin").
|
|
||||||
-define(NS_SERVERINFO, "http://jabber.org/network/serverinfo").
|
|
||||||
|
|
||||||
-define(NS_RSM, "http://jabber.org/protocol/rsm").
|
|
||||||
-define(NS_EJABBERD_CONFIG, "ejabberd:config").
|
|
||||||
|
|
||||||
-define(NS_STREAM, "http://etherx.jabber.org/streams").
|
|
||||||
|
|
||||||
-define(NS_STANZAS, "urn:ietf:params:xml:ns:xmpp-stanzas").
|
|
||||||
-define(NS_STREAMS, "urn:ietf:params:xml:ns:xmpp-streams").
|
|
||||||
|
|
||||||
-define(NS_TLS, "urn:ietf:params:xml:ns:xmpp-tls").
|
|
||||||
-define(NS_SASL, "urn:ietf:params:xml:ns:xmpp-sasl").
|
|
||||||
-define(NS_SESSION, "urn:ietf:params:xml:ns:xmpp-session").
|
|
||||||
-define(NS_BIND, "urn:ietf:params:xml:ns:xmpp-bind").
|
|
||||||
|
|
||||||
-define(NS_FEATURE_IQAUTH, "http://jabber.org/features/iq-auth").
|
|
||||||
-define(NS_FEATURE_IQREGISTER, "http://jabber.org/features/iq-register").
|
|
||||||
-define(NS_FEATURE_COMPRESS, "http://jabber.org/features/compress").
|
|
||||||
-define(NS_FEATURE_MSGOFFLINE, "msgoffline").
|
|
||||||
|
|
||||||
-define(NS_COMPRESS, "http://jabber.org/protocol/compress").
|
|
||||||
|
|
||||||
-define(NS_CAPS, "http://jabber.org/protocol/caps").
|
|
||||||
-define(NS_SHIM, "http://jabber.org/protocol/shim").
|
|
||||||
-define(NS_ADDRESS, "http://jabber.org/protocol/address").
|
|
||||||
|
|
||||||
%% CAPTCHA related NSes.
|
|
||||||
-define(NS_OOB, "jabber:x:oob").
|
|
||||||
-define(NS_CAPTCHA, "urn:xmpp:captcha").
|
|
||||||
-define(NS_MEDIA, "urn:xmpp:media-element").
|
|
||||||
-define(NS_BOB, "urn:xmpp:bob").
|
|
||||||
|
|
||||||
% TODO: remove "code" attribute (currently it used for backward-compatibility)
|
|
||||||
-define(STANZA_ERROR(Code, Type, Condition),
|
|
||||||
{xmlelement, "error",
|
|
||||||
[{"code", Code}, {"type", Type}],
|
|
||||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}).
|
|
||||||
|
|
||||||
-define(ERR_BAD_FORMAT,
|
|
||||||
?STANZA_ERROR("406", "modify", "bad-format")).
|
|
||||||
-define(ERR_BAD_REQUEST,
|
|
||||||
?STANZA_ERROR("400", "modify", "bad-request")).
|
|
||||||
-define(ERR_CONFLICT,
|
|
||||||
?STANZA_ERROR("409", "cancel", "conflict")).
|
|
||||||
-define(ERR_FEATURE_NOT_IMPLEMENTED,
|
|
||||||
?STANZA_ERROR("501", "cancel", "feature-not-implemented")).
|
|
||||||
-define(ERR_FORBIDDEN,
|
|
||||||
?STANZA_ERROR("403", "auth", "forbidden")).
|
|
||||||
-define(ERR_GONE,
|
|
||||||
?STANZA_ERROR("302", "modify", "gone")).
|
|
||||||
-define(ERR_INTERNAL_SERVER_ERROR,
|
|
||||||
?STANZA_ERROR("500", "wait", "internal-server-error")).
|
|
||||||
-define(ERR_ITEM_NOT_FOUND,
|
|
||||||
?STANZA_ERROR("404", "cancel", "item-not-found")).
|
|
||||||
-define(ERR_JID_MALFORMED,
|
|
||||||
?STANZA_ERROR("400", "modify", "jid-malformed")).
|
|
||||||
-define(ERR_NOT_ACCEPTABLE,
|
|
||||||
?STANZA_ERROR("406", "modify", "not-acceptable")).
|
|
||||||
-define(ERR_NOT_ALLOWED,
|
|
||||||
?STANZA_ERROR("405", "cancel", "not-allowed")).
|
|
||||||
-define(ERR_NOT_AUTHORIZED,
|
|
||||||
?STANZA_ERROR("401", "auth", "not-authorized")).
|
|
||||||
-define(ERR_PAYMENT_REQUIRED,
|
|
||||||
?STANZA_ERROR("402", "auth", "payment-required")).
|
|
||||||
-define(ERR_RECIPIENT_UNAVAILABLE,
|
|
||||||
?STANZA_ERROR("404", "wait", "recipient-unavailable")).
|
|
||||||
-define(ERR_REDIRECT,
|
|
||||||
?STANZA_ERROR("302", "modify", "redirect")).
|
|
||||||
-define(ERR_REGISTRATION_REQUIRED,
|
|
||||||
?STANZA_ERROR("407", "auth", "registration-required")).
|
|
||||||
-define(ERR_REMOTE_SERVER_NOT_FOUND,
|
|
||||||
?STANZA_ERROR("404", "cancel", "remote-server-not-found")).
|
|
||||||
-define(ERR_REMOTE_SERVER_TIMEOUT,
|
|
||||||
?STANZA_ERROR("504", "wait", "remote-server-timeout")).
|
|
||||||
-define(ERR_RESOURCE_CONSTRAINT,
|
|
||||||
?STANZA_ERROR("500", "wait", "resource-constraint")).
|
|
||||||
-define(ERR_SERVICE_UNAVAILABLE,
|
|
||||||
?STANZA_ERROR("503", "cancel", "service-unavailable")).
|
|
||||||
-define(ERR_SUBSCRIPTION_REQUIRED,
|
|
||||||
?STANZA_ERROR("407", "auth", "subscription-required")).
|
|
||||||
-define(ERR_UNEXPECTED_REQUEST,
|
|
||||||
?STANZA_ERROR("400", "wait", "unexpected-request")).
|
|
||||||
-define(ERR_UNEXPECTED_REQUEST_CANCEL,
|
|
||||||
?STANZA_ERROR("401", "cancel", "unexpected-request")).
|
|
||||||
%-define(ERR_,
|
|
||||||
% ?STANZA_ERROR("", "", "")).
|
|
||||||
|
|
||||||
-define(STANZA_ERRORT(Code, Type, Condition, Lang, Text),
|
|
||||||
{xmlelement, "error",
|
|
||||||
[{"code", Code}, {"type", Type}],
|
|
||||||
[{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []},
|
|
||||||
{xmlelement, "text", [{"xmlns", ?NS_STANZAS}],
|
|
||||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
|
||||||
|
|
||||||
-define(ERRT_BAD_FORMAT(Lang, Text),
|
|
||||||
?STANZA_ERRORT("406", "modify", "bad-format", Lang, Text)).
|
|
||||||
-define(ERRT_BAD_REQUEST(Lang, Text),
|
|
||||||
?STANZA_ERRORT("400", "modify", "bad-request", Lang, Text)).
|
|
||||||
-define(ERRT_CONFLICT(Lang, Text),
|
|
||||||
?STANZA_ERRORT("409", "cancel", "conflict", Lang, Text)).
|
|
||||||
-define(ERRT_FEATURE_NOT_IMPLEMENTED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("501", "cancel", "feature-not-implemented", Lang, Text)).
|
|
||||||
-define(ERRT_FORBIDDEN(Lang, Text),
|
|
||||||
?STANZA_ERRORT("403", "auth", "forbidden", Lang, Text)).
|
|
||||||
-define(ERRT_GONE(Lang, Text),
|
|
||||||
?STANZA_ERRORT("302", "modify", "gone", Lang, Text)).
|
|
||||||
-define(ERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
|
||||||
?STANZA_ERRORT("500", "wait", "internal-server-error", Lang, Text)).
|
|
||||||
-define(ERRT_ITEM_NOT_FOUND(Lang, Text),
|
|
||||||
?STANZA_ERRORT("404", "cancel", "item-not-found", Lang, Text)).
|
|
||||||
-define(ERRT_JID_MALFORMED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("400", "modify", "jid-malformed", Lang, Text)).
|
|
||||||
-define(ERRT_NOT_ACCEPTABLE(Lang, Text),
|
|
||||||
?STANZA_ERRORT("406", "modify", "not-acceptable", Lang, Text)).
|
|
||||||
-define(ERRT_NOT_ALLOWED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("405", "cancel", "not-allowed", Lang, Text)).
|
|
||||||
-define(ERRT_NOT_AUTHORIZED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("401", "auth", "not-authorized", Lang, Text)).
|
|
||||||
-define(ERRT_PAYMENT_REQUIRED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("402", "auth", "payment-required", Lang, Text)).
|
|
||||||
-define(ERRT_RECIPIENT_UNAVAILABLE(Lang, Text),
|
|
||||||
?STANZA_ERRORT("404", "wait", "recipient-unavailable", Lang, Text)).
|
|
||||||
-define(ERRT_REDIRECT(Lang, Text),
|
|
||||||
?STANZA_ERRORT("302", "modify", "redirect", Lang, Text)).
|
|
||||||
-define(ERRT_REGISTRATION_REQUIRED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("407", "auth", "registration-required", Lang, Text)).
|
|
||||||
-define(ERRT_REMOTE_SERVER_NOT_FOUND(Lang, Text),
|
|
||||||
?STANZA_ERRORT("404", "cancel", "remote-server-not-found", Lang, Text)).
|
|
||||||
-define(ERRT_REMOTE_SERVER_TIMEOUT(Lang, Text),
|
|
||||||
?STANZA_ERRORT("504", "wait", "remote-server-timeout", Lang, Text)).
|
|
||||||
-define(ERRT_RESOURCE_CONSTRAINT(Lang, Text),
|
|
||||||
?STANZA_ERRORT("500", "wait", "resource-constraint", Lang, Text)).
|
|
||||||
-define(ERRT_SERVICE_UNAVAILABLE(Lang, Text),
|
|
||||||
?STANZA_ERRORT("503", "cancel", "service-unavailable", Lang, Text)).
|
|
||||||
-define(ERRT_SUBSCRIPTION_REQUIRED(Lang, Text),
|
|
||||||
?STANZA_ERRORT("407", "auth", "subscription-required", Lang, Text)).
|
|
||||||
-define(ERRT_UNEXPECTED_REQUEST(Lang, Text),
|
|
||||||
?STANZA_ERRORT("400", "wait", "unexpected-request", Lang, Text)).
|
|
||||||
|
|
||||||
% Auth stanza errors
|
|
||||||
-define(ERR_AUTH_NO_RESOURCE_PROVIDED(Lang),
|
|
||||||
?ERRT_NOT_ACCEPTABLE(Lang, "No resource provided")).
|
|
||||||
-define(ERR_AUTH_BAD_RESOURCE_FORMAT(Lang),
|
|
||||||
?ERRT_NOT_ACCEPTABLE(Lang, "Illegal resource format")).
|
|
||||||
-define(ERR_AUTH_RESOURCE_CONFLICT(Lang),
|
|
||||||
?ERRT_CONFLICT(Lang, "Resource conflict")).
|
|
||||||
|
|
||||||
|
|
||||||
-define(STREAM_ERROR(Condition),
|
|
||||||
{xmlelement, "stream:error",
|
|
||||||
[],
|
|
||||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []}]}).
|
|
||||||
|
|
||||||
-define(SERR_BAD_FORMAT,
|
|
||||||
?STREAM_ERROR("bad-format")).
|
|
||||||
-define(SERR_BAD_NAMESPACE_PREFIX,
|
|
||||||
?STREAM_ERROR("bad-namespace-prefix")).
|
|
||||||
-define(SERR_CONFLICT,
|
|
||||||
?STREAM_ERROR("conflict")).
|
|
||||||
-define(SERR_CONNECTION_TIMEOUT,
|
|
||||||
?STREAM_ERROR("connection-timeout")).
|
|
||||||
-define(SERR_HOST_GONE,
|
|
||||||
?STREAM_ERROR("host-gone")).
|
|
||||||
-define(SERR_HOST_UNKNOWN,
|
|
||||||
?STREAM_ERROR("host-unknown")).
|
|
||||||
-define(SERR_IMPROPER_ADDRESSING,
|
|
||||||
?STREAM_ERROR("improper-addressing")).
|
|
||||||
-define(SERR_INTERNAL_SERVER_ERROR,
|
|
||||||
?STREAM_ERROR("internal-server-error")).
|
|
||||||
-define(SERR_INVALID_FROM,
|
|
||||||
?STREAM_ERROR("invalid-from")).
|
|
||||||
-define(SERR_INVALID_ID,
|
|
||||||
?STREAM_ERROR("invalid-id")).
|
|
||||||
-define(SERR_INVALID_NAMESPACE,
|
|
||||||
?STREAM_ERROR("invalid-namespace")).
|
|
||||||
-define(SERR_INVALID_XML,
|
|
||||||
?STREAM_ERROR("invalid-xml")).
|
|
||||||
-define(SERR_NOT_AUTHORIZED,
|
|
||||||
?STREAM_ERROR("not-authorized")).
|
|
||||||
-define(SERR_POLICY_VIOLATION,
|
|
||||||
?STREAM_ERROR("policy-violation")).
|
|
||||||
-define(SERR_REMOTE_CONNECTION_FAILED,
|
|
||||||
?STREAM_ERROR("remote-connection-failed")).
|
|
||||||
-define(SERR_RESOURSE_CONSTRAINT,
|
|
||||||
?STREAM_ERROR("resource-constraint")).
|
|
||||||
-define(SERR_RESTRICTED_XML,
|
|
||||||
?STREAM_ERROR("restricted-xml")).
|
|
||||||
% TODO: include hostname or IP
|
|
||||||
-define(SERR_SEE_OTHER_HOST,
|
|
||||||
?STREAM_ERROR("see-other-host")).
|
|
||||||
-define(SERR_SYSTEM_SHUTDOWN,
|
|
||||||
?STREAM_ERROR("system-shutdown")).
|
|
||||||
-define(SERR_UNSUPPORTED_ENCODING,
|
|
||||||
?STREAM_ERROR("unsupported-encoding")).
|
|
||||||
-define(SERR_UNSUPPORTED_STANZA_TYPE,
|
|
||||||
?STREAM_ERROR("unsupported-stanza-type")).
|
|
||||||
-define(SERR_UNSUPPORTED_VERSION,
|
|
||||||
?STREAM_ERROR("unsupported-version")).
|
|
||||||
-define(SERR_XML_NOT_WELL_FORMED,
|
|
||||||
?STREAM_ERROR("xml-not-well-formed")).
|
|
||||||
%-define(SERR_,
|
|
||||||
% ?STREAM_ERROR("")).
|
|
||||||
|
|
||||||
-define(STREAM_ERRORT(Condition, Lang, Text),
|
|
||||||
{xmlelement, "stream:error",
|
|
||||||
[],
|
|
||||||
[{xmlelement, Condition, [{"xmlns", ?NS_STREAMS}], []},
|
|
||||||
{xmlelement, "text", [{"xml:lang", Lang}, {"xmlns", ?NS_STREAMS}],
|
|
||||||
[{xmlcdata, translate:translate(Lang, Text)}]}]}).
|
|
||||||
|
|
||||||
-define(SERRT_BAD_FORMAT(Lang, Text),
|
|
||||||
?STREAM_ERRORT("bad-format", Lang, Text)).
|
|
||||||
-define(SERRT_BAD_NAMESPACE_PREFIX(Lang, Text),
|
|
||||||
?STREAM_ERRORT("bad-namespace-prefix", Lang, Text)).
|
|
||||||
-define(SERRT_CONFLICT(Lang, Text),
|
|
||||||
?STREAM_ERRORT("conflict", Lang, Text)).
|
|
||||||
-define(SERRT_CONNECTION_TIMEOUT(Lang, Text),
|
|
||||||
?STREAM_ERRORT("connection-timeout", Lang, Text)).
|
|
||||||
-define(SERRT_HOST_GONE(Lang, Text),
|
|
||||||
?STREAM_ERRORT("host-gone", Lang, Text)).
|
|
||||||
-define(SERRT_HOST_UNKNOWN(Lang, Text),
|
|
||||||
?STREAM_ERRORT("host-unknown", Lang, Text)).
|
|
||||||
-define(SERRT_IMPROPER_ADDRESSING(Lang, Text),
|
|
||||||
?STREAM_ERRORT("improper-addressing", Lang, Text)).
|
|
||||||
-define(SERRT_INTERNAL_SERVER_ERROR(Lang, Text),
|
|
||||||
?STREAM_ERRORT("internal-server-error", Lang, Text)).
|
|
||||||
-define(SERRT_INVALID_FROM(Lang, Text),
|
|
||||||
?STREAM_ERRORT("invalid-from", Lang, Text)).
|
|
||||||
-define(SERRT_INVALID_ID(Lang, Text),
|
|
||||||
?STREAM_ERRORT("invalid-id", Lang, Text)).
|
|
||||||
-define(SERRT_INVALID_NAMESPACE(Lang, Text),
|
|
||||||
?STREAM_ERRORT("invalid-namespace", Lang, Text)).
|
|
||||||
-define(SERRT_INVALID_XML(Lang, Text),
|
|
||||||
?STREAM_ERRORT("invalid-xml", Lang, Text)).
|
|
||||||
-define(SERRT_NOT_AUTHORIZED(Lang, Text),
|
|
||||||
?STREAM_ERRORT("not-authorized", Lang, Text)).
|
|
||||||
-define(SERRT_POLICY_VIOLATION(Lang, Text),
|
|
||||||
?STREAM_ERRORT("policy-violation", Lang, Text)).
|
|
||||||
-define(SERRT_REMOTE_CONNECTION_FAILED(Lang, Text),
|
|
||||||
?STREAM_ERRORT("remote-connection-failed", Lang, Text)).
|
|
||||||
-define(SERRT_RESOURSE_CONSTRAINT(Lang, Text),
|
|
||||||
?STREAM_ERRORT("resource-constraint", Lang, Text)).
|
|
||||||
-define(SERRT_RESTRICTED_XML(Lang, Text),
|
|
||||||
?STREAM_ERRORT("restricted-xml", Lang, Text)).
|
|
||||||
% TODO: include hostname or IP
|
|
||||||
-define(SERRT_SEE_OTHER_HOST(Lang, Text),
|
|
||||||
?STREAM_ERRORT("see-other-host", Lang, Text)).
|
|
||||||
-define(SERRT_SYSTEM_SHUTDOWN(Lang, Text),
|
|
||||||
?STREAM_ERRORT("system-shutdown", Lang, Text)).
|
|
||||||
-define(SERRT_UNSUPPORTED_ENCODING(Lang, Text),
|
|
||||||
?STREAM_ERRORT("unsupported-encoding", Lang, Text)).
|
|
||||||
-define(SERRT_UNSUPPORTED_STANZA_TYPE(Lang, Text),
|
|
||||||
?STREAM_ERRORT("unsupported-stanza-type", Lang, Text)).
|
|
||||||
-define(SERRT_UNSUPPORTED_VERSION(Lang, Text),
|
|
||||||
?STREAM_ERRORT("unsupported-version", Lang, Text)).
|
|
||||||
-define(SERRT_XML_NOT_WELL_FORMED(Lang, Text),
|
|
||||||
?STREAM_ERRORT("xml-not-well-formed", Lang, Text)).
|
|
||||||
%-define(SERRT_(Lang, Text),
|
|
||||||
% ?STREAM_ERRORT("", Lang, Text)).
|
|
||||||
|
|
||||||
|
|
||||||
-record(jid, {user, server, resource,
|
|
||||||
luser, lserver, lresource}).
|
|
||||||
|
|
||||||
-record(iq, {id = "",
|
|
||||||
type,
|
|
||||||
xmlns = "",
|
|
||||||
lang = "",
|
|
||||||
sub_el}).
|
|
||||||
|
|
||||||
-record(rsm_in, {max, direction, id, index}).
|
|
||||||
-record(rsm_out, {count, index, first, last}).
|
|
|
@ -1,90 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-define(MAX_USERS_DEFAULT, 200).
|
|
||||||
|
|
||||||
-define(SETS, gb_sets).
|
|
||||||
-define(DICT, dict).
|
|
||||||
|
|
||||||
-record(lqueue, {queue, len, max}).
|
|
||||||
|
|
||||||
-record(config, {title = "",
|
|
||||||
description = "",
|
|
||||||
allow_change_subj = true,
|
|
||||||
allow_query_users = true,
|
|
||||||
allow_private_messages = true,
|
|
||||||
allow_private_messages_from_visitors = anyone,
|
|
||||||
allow_visitor_status = true,
|
|
||||||
allow_visitor_nickchange = true,
|
|
||||||
public = true,
|
|
||||||
public_list = true,
|
|
||||||
persistent = false,
|
|
||||||
moderated = true,
|
|
||||||
captcha_protected = false,
|
|
||||||
members_by_default = true,
|
|
||||||
members_only = false,
|
|
||||||
allow_user_invites = false,
|
|
||||||
password_protected = false,
|
|
||||||
password = "",
|
|
||||||
anonymous = true,
|
|
||||||
allow_voice_requests = true,
|
|
||||||
voice_request_min_interval = 1800,
|
|
||||||
max_users = ?MAX_USERS_DEFAULT,
|
|
||||||
logging = false,
|
|
||||||
captcha_whitelist = ?SETS:empty()
|
|
||||||
}).
|
|
||||||
|
|
||||||
-record(user, {jid,
|
|
||||||
nick,
|
|
||||||
role,
|
|
||||||
last_presence}).
|
|
||||||
|
|
||||||
-record(activity, {message_time = 0,
|
|
||||||
presence_time = 0,
|
|
||||||
message_shaper,
|
|
||||||
presence_shaper,
|
|
||||||
message,
|
|
||||||
presence}).
|
|
||||||
|
|
||||||
-record(state, {room,
|
|
||||||
host,
|
|
||||||
server_host,
|
|
||||||
mod,
|
|
||||||
access,
|
|
||||||
jid,
|
|
||||||
config = #config{},
|
|
||||||
users = ?DICT:new(),
|
|
||||||
last_voice_request_time = treap:empty(),
|
|
||||||
robots = ?DICT:new(),
|
|
||||||
nicks = ?DICT:new(),
|
|
||||||
affiliations = ?DICT:new(),
|
|
||||||
history,
|
|
||||||
subject = "",
|
|
||||||
subject_author = "",
|
|
||||||
just_created = false,
|
|
||||||
activity = treap:empty(),
|
|
||||||
room_shaper,
|
|
||||||
room_queue = queue:new()}).
|
|
||||||
|
|
||||||
-record(muc_online_users, {us,
|
|
||||||
resource,
|
|
||||||
room,
|
|
||||||
host}).
|
|
|
@ -1,193 +0,0 @@
|
||||||
%%% ====================================================================
|
|
||||||
%%% ``The contents of this file are subject to the Erlang Public License,
|
|
||||||
%%% Version 1.1, (the "License"); you may not use this file except in
|
|
||||||
%%% compliance with the License. You should have received a copy of the
|
|
||||||
%%% Erlang Public License along with this software. If not, it can be
|
|
||||||
%%% retrieved via the world wide web at http://www.erlang.org/.
|
|
||||||
%%%
|
|
||||||
%%% Software distributed under the License is distributed on an "AS IS"
|
|
||||||
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
|
||||||
%%% the License for the specific language governing rights and limitations
|
|
||||||
%%% under the License.
|
|
||||||
%%%
|
|
||||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
|
||||||
%%% Portions created by ProcessOne are Copyright 2006-2011, ProcessOne
|
|
||||||
%%% All Rights Reserved.''
|
|
||||||
%%% This software is copyright 2006-2011, ProcessOne.
|
|
||||||
%%%
|
|
||||||
%%%
|
|
||||||
%%% copyright 2006-2011 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This file contains pubsub types definition.
|
|
||||||
%%% ====================================================================
|
|
||||||
|
|
||||||
%% -------------------------------
|
|
||||||
%% Pubsub constants
|
|
||||||
-define(ERR_EXTENDED(E,C), mod_pubsub:extended_error(E,C)).
|
|
||||||
|
|
||||||
%% The actual limit can be configured with mod_pubsub's option max_items_node
|
|
||||||
-define(MAXITEMS, 10).
|
|
||||||
|
|
||||||
%% this is currently a hard limit.
|
|
||||||
%% Would be nice to have it configurable.
|
|
||||||
-define(MAX_PAYLOAD_SIZE, 60000).
|
|
||||||
|
|
||||||
%% -------------------------------
|
|
||||||
%% Pubsub types
|
|
||||||
|
|
||||||
%% @type hostPubsub() = string().
|
|
||||||
%% <p><tt>hostPubsub</tt> is the name of the PubSub service. For example, it can be
|
|
||||||
%% <tt>"pubsub.localhost"</tt>.</p>
|
|
||||||
|
|
||||||
%% @type hostPEP() = {User, Server, Resource}
|
|
||||||
%% User = string()
|
|
||||||
%% Server = string()
|
|
||||||
%% Resource = [].
|
|
||||||
%% <p>For example, it can be :
|
|
||||||
%% ```{"bob", "example.org", []}'''.</p>
|
|
||||||
|
|
||||||
%% @type host() = hostPubsub() | hostPEP().
|
|
||||||
|
|
||||||
%% @type nodeId() = binary().
|
|
||||||
%% <p>A node is defined by a list of its ancestors. The last element is the name
|
|
||||||
%% of the current node. For example:
|
|
||||||
%% ```<<"/home/localhost/user">>'''</p>
|
|
||||||
|
|
||||||
%% @type nodeIdx() = integer().
|
|
||||||
|
|
||||||
%% @type itemId() = string().
|
|
||||||
|
|
||||||
%% @type subId() = string().
|
|
||||||
|
|
||||||
%% @type payload() = [#xmlelement{} | #xmlcdata{}].
|
|
||||||
|
|
||||||
%% @type stanzaError() = #xmlelement{}.
|
|
||||||
%% Example:
|
|
||||||
%% ```{xmlelement, "error",
|
|
||||||
%% [{"code", Code}, {"type", Type}],
|
|
||||||
%% [{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}'''
|
|
||||||
%% @type pubsubIQResponse() = #xmlelement{}.
|
|
||||||
%% Example:
|
|
||||||
%% ```{xmlelement, "pubsub",
|
|
||||||
%% [{"xmlns", ?NS_PUBSUB_EVENT}],
|
|
||||||
%% [{xmlelement, "affiliations", [],
|
|
||||||
%% []}]}'''
|
|
||||||
|
|
||||||
%% @type nodeOption() = {Option, Value}
|
|
||||||
%% Option = atom()
|
|
||||||
%% Value = term().
|
|
||||||
%% Example:
|
|
||||||
%% ```{deliver_payloads, true}'''
|
|
||||||
|
|
||||||
%% @type nodeType() = string().
|
|
||||||
%% <p>The <tt>nodeType</tt> is a string containing the name of the PubSub
|
|
||||||
%% plugin to use to manage a given node. For example, it can be
|
|
||||||
%% <tt>"flat"</tt>, <tt>"hometree"</tt> or <tt>"blog"</tt>.</p>
|
|
||||||
|
|
||||||
%% @type jid() = {jid, User, Server, Resource, LUser, LServer, LResource}
|
|
||||||
%% User = string()
|
|
||||||
%% Server = string()
|
|
||||||
%% Resource = string()
|
|
||||||
%% LUser = string()
|
|
||||||
%% LServer = string()
|
|
||||||
%% LResource = string().
|
|
||||||
|
|
||||||
%% @type ljid() = {User, Server, Resource}
|
|
||||||
%% User = string()
|
|
||||||
%% Server = string()
|
|
||||||
%% Resource = string().
|
|
||||||
|
|
||||||
%% @type affiliation() = 'none' | 'owner' | 'publisher' | 'publish-only' | 'member' | 'outcast'.
|
|
||||||
|
|
||||||
%% @type subscription() = 'none' | 'pending' | 'unconfigured' | 'subscribed'.
|
|
||||||
|
|
||||||
%% @type accessModel() = 'open' | 'presence' | 'roster' | 'authorize' | 'whitelist'.
|
|
||||||
|
|
||||||
%% @type pubsubIndex() = {pubsub_index, Index, Last, Free}
|
|
||||||
%% Index = atom()
|
|
||||||
%% Last = integer()
|
|
||||||
%% Free = [integer()].
|
|
||||||
%% internal pubsub index table
|
|
||||||
-record(pubsub_index,
|
|
||||||
{
|
|
||||||
index,
|
|
||||||
last,
|
|
||||||
free
|
|
||||||
}).
|
|
||||||
|
|
||||||
%% @type pubsubNode() = {pubsub_node, NodeId, Id, Parents, Type, Owners, Options}
|
|
||||||
%% NodeId = {host() | ljid(), nodeId()}
|
|
||||||
%% Id = nodeIdx()
|
|
||||||
%% Parents = [nodeId()]
|
|
||||||
%% Type = nodeType()
|
|
||||||
%% Owners = [ljid()]
|
|
||||||
%% Options = [nodeOption()].
|
|
||||||
%% <p>This is the format of the <tt>nodes</tt> table. The type of the table
|
|
||||||
%% is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
|
|
||||||
%% <p>The <tt>Parents</tt> and <tt>type</tt> fields are indexed.</p>
|
|
||||||
%% <tt>id</tt> can be anything you want.
|
|
||||||
-record(pubsub_node,
|
|
||||||
{
|
|
||||||
nodeid,
|
|
||||||
id,
|
|
||||||
parents = [],
|
|
||||||
type = "flat",
|
|
||||||
owners = [],
|
|
||||||
options = []
|
|
||||||
}).
|
|
||||||
|
|
||||||
%% @type pubsubState() = {pubsub_state, StateId, Items, Affiliation, Subscriptions}
|
|
||||||
%% StateId = {ljid(), nodeIdx()}
|
|
||||||
%% Items = [itemId()]
|
|
||||||
%% Affiliation = affiliation()
|
|
||||||
%% Subscriptions = [{subscription(), subId()}].
|
|
||||||
%% <p>This is the format of the <tt>affiliations</tt> table. The type of the
|
|
||||||
%% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
|
|
||||||
-record(pubsub_state,
|
|
||||||
{
|
|
||||||
stateid,
|
|
||||||
items = [],
|
|
||||||
affiliation = 'none',
|
|
||||||
subscriptions = []
|
|
||||||
}).
|
|
||||||
|
|
||||||
%% @type pubsubItem() = {pubsub_item, ItemId, Creation, Modification, Payload}
|
|
||||||
%% ItemId = {itemId(), nodeIdx()}
|
|
||||||
%% Creation = {now(), ljid()}
|
|
||||||
%% Modification = {now(), ljid()}
|
|
||||||
%% Payload = payload().
|
|
||||||
%% <p>This is the format of the <tt>published items</tt> table. The type of the
|
|
||||||
%% table is: <tt>set</tt>,<tt>disc</tt>,<tt>fragmented</tt>.</p>
|
|
||||||
-record(pubsub_item,
|
|
||||||
{
|
|
||||||
itemid,
|
|
||||||
creation = {'unknown','unknown'},
|
|
||||||
modification = {'unknown','unknown'},
|
|
||||||
payload = []
|
|
||||||
}).
|
|
||||||
|
|
||||||
%% @type pubsubSubscription() = {pubsub_subscription, SubId, Options}
|
|
||||||
%% SubId = subId()
|
|
||||||
%% Options = [nodeOption()].
|
|
||||||
%% <p>This is the format of the <tt>subscriptions</tt> table. The type of the
|
|
||||||
%% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
|
|
||||||
-record(pubsub_subscription,
|
|
||||||
{
|
|
||||||
subid,
|
|
||||||
options
|
|
||||||
}).
|
|
||||||
|
|
||||||
%% @type pubsubLastItem() = {pubsub_last_item, NodeId, ItemId, Creation, Payload}
|
|
||||||
%% NodeId = nodeIdx()
|
|
||||||
%% ItemId = itemId()
|
|
||||||
%% Creation = {now(),ljid()}
|
|
||||||
%% Payload = payload().
|
|
||||||
%% <p>This is the format of the <tt>last items</tt> table. it stores last item payload
|
|
||||||
%% for every node</p>
|
|
||||||
-record(pubsub_last_item,
|
|
||||||
{
|
|
||||||
nodeid,
|
|
||||||
itemid,
|
|
||||||
creation,
|
|
||||||
payload
|
|
||||||
}).
|
|
|
@ -1,34 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-record(request, {method,
|
|
||||||
path,
|
|
||||||
q = [],
|
|
||||||
us,
|
|
||||||
auth,
|
|
||||||
lang = "",
|
|
||||||
data = "",
|
|
||||||
ip,
|
|
||||||
host, % string()
|
|
||||||
port, % integer()
|
|
||||||
tp, % transfer protocol = http | https
|
|
||||||
headers
|
|
||||||
}).
|
|
|
@ -1,76 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2012 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-define(X(Name), {xmlelement, Name, [], []}).
|
|
||||||
-define(XA(Name, Attrs), {xmlelement, Name, Attrs, []}).
|
|
||||||
-define(XE(Name, Els), {xmlelement, Name, [], Els}).
|
|
||||||
-define(XAE(Name, Attrs, Els), {xmlelement, Name, Attrs, Els}).
|
|
||||||
-define(C(Text), {xmlcdata, Text}).
|
|
||||||
-define(XC(Name, Text), ?XE(Name, [?C(Text)])).
|
|
||||||
-define(XAC(Name, Attrs, Text), ?XAE(Name, Attrs, [?C(Text)])).
|
|
||||||
|
|
||||||
-define(T(Text), translate:translate(Lang, Text)).
|
|
||||||
-define(CT(Text), ?C(?T(Text))).
|
|
||||||
-define(XCT(Name, Text), ?XC(Name, ?T(Text))).
|
|
||||||
-define(XACT(Name, Attrs, Text), ?XAC(Name, Attrs, ?T(Text))).
|
|
||||||
|
|
||||||
-define(LI(Els), ?XE("li", Els)).
|
|
||||||
-define(A(URL, Els), ?XAE("a", [{"href", URL}], Els)).
|
|
||||||
-define(AC(URL, Text), ?A(URL, [?C(Text)])).
|
|
||||||
-define(ACT(URL, Text), ?AC(URL, ?T(Text))).
|
|
||||||
-define(P, ?X("p")).
|
|
||||||
-define(BR, ?X("br")).
|
|
||||||
-define(INPUT(Type, Name, Value),
|
|
||||||
?XA("input", [{"type", Type},
|
|
||||||
{"name", Name},
|
|
||||||
{"value", Value}])).
|
|
||||||
-define(INPUTT(Type, Name, Value), ?INPUT(Type, Name, ?T(Value))).
|
|
||||||
-define(INPUTS(Type, Name, Value, Size),
|
|
||||||
?XA("input", [{"type", Type},
|
|
||||||
{"name", Name},
|
|
||||||
{"value", Value},
|
|
||||||
{"size", Size}])).
|
|
||||||
-define(INPUTST(Type, Name, Value, Size), ?INPUT(Type, Name, ?T(Value), Size)).
|
|
||||||
-define(ACLINPUT(Text), ?XE("td", [?INPUT("text", "value" ++ ID, Text)])).
|
|
||||||
|
|
||||||
-define(TEXTAREA(Name, Rows, Cols, Value),
|
|
||||||
?XAC("textarea", [{"name", Name},
|
|
||||||
{"rows", Rows},
|
|
||||||
{"cols", Cols}],
|
|
||||||
Value)).
|
|
||||||
|
|
||||||
%% Build an xmlelement for result
|
|
||||||
-define(XRES(Text), ?XAC("p", [{"class", "result"}], Text)).
|
|
||||||
-define(XREST(Text), ?XRES(?T(Text))).
|
|
||||||
|
|
||||||
%% Guide Link
|
|
||||||
-define(GL(Ref, Title),
|
|
||||||
?XAE("div",
|
|
||||||
[{"class", "guidelink"}],
|
|
||||||
[?XAE("a",
|
|
||||||
[{"href", "/admin/doc/guide.html#"++ Ref},
|
|
||||||
{"target", "_blank"}],
|
|
||||||
[?C("[Guide: " ++ Title ++ "]")])
|
|
||||||
])).
|
|
||||||
|
|
||||||
|
|
||||||
%% h1 with a Guide Link
|
|
||||||
-define(H1GL(Name, Ref, Title), [?XC("h1", Name), ?GL(Ref, Title)]).
|
|
|
@ -1,236 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%% File : gen_mod.erl
|
|
||||||
%%% Author : Alexey Shchepin <alexey@process-one.net>
|
|
||||||
%%% Purpose :
|
|
||||||
%%% Created : 24 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
|
||||||
%%%
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2008 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(gen_mod).
|
|
||||||
-author('alexey@process-one.net').
|
|
||||||
|
|
||||||
-export([start/0,
|
|
||||||
start_module/3,
|
|
||||||
stop_module/2,
|
|
||||||
stop_module_keep_config/2,
|
|
||||||
get_opt/2,
|
|
||||||
get_opt/3,
|
|
||||||
get_opt_host/3,
|
|
||||||
get_module_opt/4,
|
|
||||||
get_module_opt_host/3,
|
|
||||||
loaded_modules/1,
|
|
||||||
loaded_modules_with_opts/1,
|
|
||||||
get_hosts/2,
|
|
||||||
get_module_proc/2,
|
|
||||||
is_loaded/2]).
|
|
||||||
|
|
||||||
-export([behaviour_info/1]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
|
||||||
|
|
||||||
-record(ejabberd_module, {module_host, opts}).
|
|
||||||
|
|
||||||
behaviour_info(callbacks) ->
|
|
||||||
[{start, 2},
|
|
||||||
{stop, 1}];
|
|
||||||
behaviour_info(_Other) ->
|
|
||||||
undefined.
|
|
||||||
|
|
||||||
start() ->
|
|
||||||
ets:new(ejabberd_modules, [named_table,
|
|
||||||
public,
|
|
||||||
{keypos, #ejabberd_module.module_host}]),
|
|
||||||
ok.
|
|
||||||
|
|
||||||
|
|
||||||
start_module(Host, Module, Opts) ->
|
|
||||||
set_module_opts_mnesia(Host, Module, Opts),
|
|
||||||
ets:insert(ejabberd_modules,
|
|
||||||
#ejabberd_module{module_host = {Module, Host},
|
|
||||||
opts = Opts}),
|
|
||||||
case catch Module:start(Host, Opts) of
|
|
||||||
{'EXIT', Reason} ->
|
|
||||||
del_module_mnesia(Host, Module),
|
|
||||||
ets:delete(ejabberd_modules, {Module, Host}),
|
|
||||||
?ERROR_MSG("~p", [Reason]);
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @doc Stop the module in a host, and forget its configuration.
|
|
||||||
stop_module(Host, Module) ->
|
|
||||||
case stop_module_keep_config(Host, Module) of
|
|
||||||
error ->
|
|
||||||
error;
|
|
||||||
ok ->
|
|
||||||
del_module_mnesia(Host, Module)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @doc Stop the module in a host, but keep its configuration.
|
|
||||||
%% As the module configuration is kept in the Mnesia local_config table,
|
|
||||||
%% when ejabberd is restarted the module will be started again.
|
|
||||||
%% This function is useful when ejabberd is being stopped
|
|
||||||
%% and it stops all modules.
|
|
||||||
stop_module_keep_config(Host, Module) ->
|
|
||||||
case catch Module:stop(Host) of
|
|
||||||
{'EXIT', Reason} ->
|
|
||||||
?ERROR_MSG("~p", [Reason]),
|
|
||||||
error;
|
|
||||||
{wait, ProcList} when is_list(ProcList) ->
|
|
||||||
lists:foreach(fun wait_for_process/1, ProcList),
|
|
||||||
ets:delete(ejabberd_modules, {Module, Host}),
|
|
||||||
ok;
|
|
||||||
{wait, Process} ->
|
|
||||||
wait_for_process(Process),
|
|
||||||
ets:delete(ejabberd_modules, {Module, Host}),
|
|
||||||
ok;
|
|
||||||
_ ->
|
|
||||||
ets:delete(ejabberd_modules, {Module, Host}),
|
|
||||||
ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
wait_for_process(Process) ->
|
|
||||||
MonitorReference = erlang:monitor(process, Process),
|
|
||||||
wait_for_stop(Process, MonitorReference).
|
|
||||||
|
|
||||||
wait_for_stop(Process, MonitorReference) ->
|
|
||||||
receive
|
|
||||||
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
|
|
||||||
ok
|
|
||||||
after 5000 ->
|
|
||||||
catch exit(whereis(Process), kill),
|
|
||||||
wait_for_stop1(MonitorReference)
|
|
||||||
end.
|
|
||||||
|
|
||||||
wait_for_stop1(MonitorReference) ->
|
|
||||||
receive
|
|
||||||
{'DOWN', MonitorReference, _Type, _Object, _Info} ->
|
|
||||||
ok
|
|
||||||
after 5000 ->
|
|
||||||
ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_opt(Opt, Opts) ->
|
|
||||||
case lists:keysearch(Opt, 1, Opts) of
|
|
||||||
false ->
|
|
||||||
% TODO: replace with more appropriate function
|
|
||||||
throw({undefined_option, Opt});
|
|
||||||
{value, {_, Val}} ->
|
|
||||||
Val
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_opt(Opt, Opts, Default) ->
|
|
||||||
case lists:keysearch(Opt, 1, Opts) of
|
|
||||||
false ->
|
|
||||||
Default;
|
|
||||||
{value, {_, Val}} ->
|
|
||||||
Val
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_module_opt(global, Module, Opt, Default) ->
|
|
||||||
Hosts = ?MYHOSTS,
|
|
||||||
[Value | Values] = lists:map(
|
|
||||||
fun(Host) ->
|
|
||||||
get_module_opt(Host, Module, Opt, Default)
|
|
||||||
end,
|
|
||||||
Hosts),
|
|
||||||
Same_all = lists:all(
|
|
||||||
fun(Other_value) ->
|
|
||||||
Other_value == Value
|
|
||||||
end,
|
|
||||||
Values),
|
|
||||||
case Same_all of
|
|
||||||
true -> Value;
|
|
||||||
false -> Default
|
|
||||||
end;
|
|
||||||
|
|
||||||
get_module_opt(Host, Module, Opt, Default) ->
|
|
||||||
OptsList = ets:lookup(ejabberd_modules, {Module, Host}),
|
|
||||||
case OptsList of
|
|
||||||
[] ->
|
|
||||||
Default;
|
|
||||||
[#ejabberd_module{opts = Opts} | _] ->
|
|
||||||
get_opt(Opt, Opts, Default)
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_module_opt_host(Host, Module, Default) ->
|
|
||||||
Val = get_module_opt(Host, Module, host, Default),
|
|
||||||
element(2, regexp:gsub(Val, "@HOST@", Host)).
|
|
||||||
|
|
||||||
get_opt_host(Host, Opts, Default) ->
|
|
||||||
Val = get_opt(host, Opts, Default),
|
|
||||||
element(2, regexp:gsub(Val, "@HOST@", Host)).
|
|
||||||
|
|
||||||
loaded_modules(Host) ->
|
|
||||||
ets:select(ejabberd_modules,
|
|
||||||
[{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
|
|
||||||
[],
|
|
||||||
['$1']}]).
|
|
||||||
|
|
||||||
loaded_modules_with_opts(Host) ->
|
|
||||||
ets:select(ejabberd_modules,
|
|
||||||
[{#ejabberd_module{_ = '_', module_host = {'$1', Host},
|
|
||||||
opts = '$2'},
|
|
||||||
[],
|
|
||||||
[{{'$1', '$2'}}]}]).
|
|
||||||
|
|
||||||
set_module_opts_mnesia(Host, Module, Opts) ->
|
|
||||||
Modules = case ejabberd_config:get_local_option({modules, Host}) of
|
|
||||||
undefined ->
|
|
||||||
[];
|
|
||||||
Ls ->
|
|
||||||
Ls
|
|
||||||
end,
|
|
||||||
Modules1 = lists:keydelete(Module, 1, Modules),
|
|
||||||
Modules2 = [{Module, Opts} | Modules1],
|
|
||||||
ejabberd_config:add_local_option({modules, Host}, Modules2).
|
|
||||||
|
|
||||||
del_module_mnesia(Host, Module) ->
|
|
||||||
Modules = case ejabberd_config:get_local_option({modules, Host}) of
|
|
||||||
undefined ->
|
|
||||||
[];
|
|
||||||
Ls ->
|
|
||||||
Ls
|
|
||||||
end,
|
|
||||||
Modules1 = lists:keydelete(Module, 1, Modules),
|
|
||||||
ejabberd_config:add_local_option({modules, Host}, Modules1).
|
|
||||||
|
|
||||||
get_hosts(Opts, Prefix) ->
|
|
||||||
case catch gen_mod:get_opt(hosts, Opts) of
|
|
||||||
{'EXIT', _Error1} ->
|
|
||||||
case catch gen_mod:get_opt(host, Opts) of
|
|
||||||
{'EXIT', _Error2} ->
|
|
||||||
[Prefix ++ Host || Host <- ?MYHOSTS];
|
|
||||||
Host ->
|
|
||||||
[Host]
|
|
||||||
end;
|
|
||||||
Hosts ->
|
|
||||||
Hosts
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_module_proc(Host, {frontend, Base}) ->
|
|
||||||
get_module_proc("frontend_" ++ Host, Base);
|
|
||||||
get_module_proc(Host, Base) ->
|
|
||||||
list_to_atom(atom_to_list(Base) ++ "_" ++ Host).
|
|
||||||
|
|
||||||
is_loaded(Host, Module) ->
|
|
||||||
ets:member(ejabberd_modules, {Module, Host}).
|
|
||||||
|
|
Binary file not shown.
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
IBClasses = (
|
|
||||||
{
|
|
||||||
CLASS = NSPreferencePane;
|
|
||||||
LANGUAGE = ObjC;
|
|
||||||
OUTLETS = {
|
|
||||||
"_firstKeyView" = id;
|
|
||||||
"_initialKeyView" = id;
|
|
||||||
"_lastKeyView" = id;
|
|
||||||
"_window" = id;
|
|
||||||
};
|
|
||||||
SUPERCLASS = NSObject;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ACTIONS = {automaticStartAction = id; startStopAction = id; };
|
|
||||||
CLASS = ejabberdController;
|
|
||||||
LANGUAGE = ObjC;
|
|
||||||
OUTLETS = {
|
|
||||||
actionProgress = NSProgressIndicator;
|
|
||||||
automaticBox = NSButton;
|
|
||||||
imagestarted = NSImageView;
|
|
||||||
imagestopped = NSImageView;
|
|
||||||
startStopButton = NSButton;
|
|
||||||
status = NSTextField;
|
|
||||||
};
|
|
||||||
SUPERCLASS = NSObject;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
IBVersion = 1;
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>IBDocumentLocation</key>
|
|
||||||
<string>32 22 356 240 0 0 1680 1028 </string>
|
|
||||||
<key>IBFramework Version</key>
|
|
||||||
<string>446.1</string>
|
|
||||||
<key>IBOldestOS</key>
|
|
||||||
<integer>2</integer>
|
|
||||||
<key>IBSystem Version</key>
|
|
||||||
<string>8S2167</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
Binary file not shown.
|
@ -1,30 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>English</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>ejabberd</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.apple.prefpanel</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>BNDL</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>process-one</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>2.0.3</string>
|
|
||||||
<key>NSMainNibFile</key>
|
|
||||||
<string>ejabberdPref</string>
|
|
||||||
<key>NSPrefPaneIconFile</key>
|
|
||||||
<string>ejabberdPref.tiff</string>
|
|
||||||
<key>NSPrefPaneIconLabel</key>
|
|
||||||
<string>ejabberd</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>ejabberdPref</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
|
@ -1,18 +0,0 @@
|
||||||
ejabberd's prefPane Licence
|
|
||||||
|
|
||||||
Except as described below, all of the source code to prefPane is
|
|
||||||
available under the Mozilla Public Licence (MPL).
|
|
||||||
|
|
||||||
The complete text of the Mozilla Public License can be found in
|
|
||||||
the MPL file in the same directory as this file or consulted at
|
|
||||||
http://www.mozilla.org/MPL/MPL-1.1.html.
|
|
||||||
|
|
||||||
Exceptions
|
|
||||||
|
|
||||||
The following portions are not available under the above
|
|
||||||
terms:
|
|
||||||
|
|
||||||
* Image files containing the trademarks and logos of
|
|
||||||
ProcessOne, which may not be reproduced without
|
|
||||||
permission. (Copyright ©2001-2009 ProcessOne. All Rights
|
|
||||||
Reserved.)
|
|
|
@ -1,470 +0,0 @@
|
||||||
MOZILLA PUBLIC LICENSE
|
|
||||||
Version 1.1
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
1.0.1. "Commercial Use" means distribution or otherwise making the
|
|
||||||
Covered Code available to a third party.
|
|
||||||
|
|
||||||
1.1. "Contributor" means each entity that creates or contributes to
|
|
||||||
the creation of Modifications.
|
|
||||||
|
|
||||||
1.2. "Contributor Version" means the combination of the Original
|
|
||||||
Code, prior Modifications used by a Contributor, and the Modifications
|
|
||||||
made by that particular Contributor.
|
|
||||||
|
|
||||||
1.3. "Covered Code" means the Original Code or Modifications or the
|
|
||||||
combination of the Original Code and Modifications, in each case
|
|
||||||
including portions thereof.
|
|
||||||
|
|
||||||
1.4. "Electronic Distribution Mechanism" means a mechanism generally
|
|
||||||
accepted in the software development community for the electronic
|
|
||||||
transfer of data.
|
|
||||||
|
|
||||||
1.5. "Executable" means Covered Code in any form other than Source
|
|
||||||
Code.
|
|
||||||
|
|
||||||
1.6. "Initial Developer" means the individual or entity identified
|
|
||||||
as the Initial Developer in the Source Code notice required by Exhibit
|
|
||||||
A.
|
|
||||||
|
|
||||||
1.7. "Larger Work" means a work which combines Covered Code or
|
|
||||||
portions thereof with code not governed by the terms of this License.
|
|
||||||
|
|
||||||
1.8. "License" means this document.
|
|
||||||
|
|
||||||
1.8.1. "Licensable" means having the right to grant, to the maximum
|
|
||||||
extent possible, whether at the time of the initial grant or
|
|
||||||
subsequently acquired, any and all of the rights conveyed herein.
|
|
||||||
|
|
||||||
1.9. "Modifications" means any addition to or deletion from the
|
|
||||||
substance or structure of either the Original Code or any previous
|
|
||||||
Modifications. When Covered Code is released as a series of files, a
|
|
||||||
Modification is:
|
|
||||||
A. Any addition to or deletion from the contents of a file
|
|
||||||
containing Original Code or previous Modifications.
|
|
||||||
|
|
||||||
B. Any new file that contains any part of the Original Code or
|
|
||||||
previous Modifications.
|
|
||||||
|
|
||||||
1.10. "Original Code" means Source Code of computer software code
|
|
||||||
which is described in the Source Code notice required by Exhibit A as
|
|
||||||
Original Code, and which, at the time of its release under this
|
|
||||||
License is not already Covered Code governed by this License.
|
|
||||||
|
|
||||||
1.10.1. "Patent Claims" means any patent claim(s), now owned or
|
|
||||||
hereafter acquired, including without limitation, method, process,
|
|
||||||
and apparatus claims, in any patent Licensable by grantor.
|
|
||||||
|
|
||||||
1.11. "Source Code" means the preferred form of the Covered Code for
|
|
||||||
making modifications to it, including all modules it contains, plus
|
|
||||||
any associated interface definition files, scripts used to control
|
|
||||||
compilation and installation of an Executable, or source code
|
|
||||||
differential comparisons against either the Original Code or another
|
|
||||||
well known, available Covered Code of the Contributor's choice. The
|
|
||||||
Source Code can be in a compressed or archival form, provided the
|
|
||||||
appropriate decompression or de-archiving software is widely available
|
|
||||||
for no charge.
|
|
||||||
|
|
||||||
1.12. "You" (or "Your") means an individual or a legal entity
|
|
||||||
exercising rights under, and complying with all of the terms of, this
|
|
||||||
License or a future version of this License issued under Section 6.1.
|
|
||||||
For legal entities, "You" includes any entity which controls, is
|
|
||||||
controlled by, or is under common control with You. For purposes of
|
|
||||||
this definition, "control" means (a) the power, direct or indirect,
|
|
||||||
to cause the direction or management of such entity, whether by
|
|
||||||
contract or otherwise, or (b) ownership of more than fifty percent
|
|
||||||
(50%) of the outstanding shares or beneficial ownership of such
|
|
||||||
entity.
|
|
||||||
|
|
||||||
2. Source Code License.
|
|
||||||
|
|
||||||
2.1. The Initial Developer Grant.
|
|
||||||
The Initial Developer hereby grants You a world-wide, royalty-free,
|
|
||||||
non-exclusive license, subject to third party intellectual property
|
|
||||||
claims:
|
|
||||||
(a) under intellectual property rights (other than patent or
|
|
||||||
trademark) Licensable by Initial Developer to use, reproduce,
|
|
||||||
modify, display, perform, sublicense and distribute the Original
|
|
||||||
Code (or portions thereof) with or without Modifications, and/or
|
|
||||||
as part of a Larger Work; and
|
|
||||||
|
|
||||||
(b) under Patents Claims infringed by the making, using or
|
|
||||||
selling of Original Code, to make, have made, use, practice,
|
|
||||||
sell, and offer for sale, and/or otherwise dispose of the
|
|
||||||
Original Code (or portions thereof).
|
|
||||||
|
|
||||||
(c) the licenses granted in this Section 2.1(a) and (b) are
|
|
||||||
effective on the date Initial Developer first distributes
|
|
||||||
Original Code under the terms of this License.
|
|
||||||
|
|
||||||
(d) Notwithstanding Section 2.1(b) above, no patent license is
|
|
||||||
granted: 1) for code that You delete from the Original Code; 2)
|
|
||||||
separate from the Original Code; or 3) for infringements caused
|
|
||||||
by: i) the modification of the Original Code or ii) the
|
|
||||||
combination of the Original Code with other software or devices.
|
|
||||||
|
|
||||||
2.2. Contributor Grant.
|
|
||||||
Subject to third party intellectual property claims, each Contributor
|
|
||||||
hereby grants You a world-wide, royalty-free, non-exclusive license
|
|
||||||
|
|
||||||
(a) under intellectual property rights (other than patent or
|
|
||||||
trademark) Licensable by Contributor, to use, reproduce, modify,
|
|
||||||
display, perform, sublicense and distribute the Modifications
|
|
||||||
created by such Contributor (or portions thereof) either on an
|
|
||||||
unmodified basis, with other Modifications, as Covered Code
|
|
||||||
and/or as part of a Larger Work; and
|
|
||||||
|
|
||||||
(b) under Patent Claims infringed by the making, using, or
|
|
||||||
selling of Modifications made by that Contributor either alone
|
|
||||||
and/or in combination with its Contributor Version (or portions
|
|
||||||
of such combination), to make, use, sell, offer for sale, have
|
|
||||||
made, and/or otherwise dispose of: 1) Modifications made by that
|
|
||||||
Contributor (or portions thereof); and 2) the combination of
|
|
||||||
Modifications made by that Contributor with its Contributor
|
|
||||||
Version (or portions of such combination).
|
|
||||||
|
|
||||||
(c) the licenses granted in Sections 2.2(a) and 2.2(b) are
|
|
||||||
effective on the date Contributor first makes Commercial Use of
|
|
||||||
the Covered Code.
|
|
||||||
|
|
||||||
(d) Notwithstanding Section 2.2(b) above, no patent license is
|
|
||||||
granted: 1) for any code that Contributor has deleted from the
|
|
||||||
Contributor Version; 2) separate from the Contributor Version;
|
|
||||||
3) for infringements caused by: i) third party modifications of
|
|
||||||
Contributor Version or ii) the combination of Modifications made
|
|
||||||
by that Contributor with other software (except as part of the
|
|
||||||
Contributor Version) or other devices; or 4) under Patent Claims
|
|
||||||
infringed by Covered Code in the absence of Modifications made by
|
|
||||||
that Contributor.
|
|
||||||
|
|
||||||
3. Distribution Obligations.
|
|
||||||
|
|
||||||
3.1. Application of License.
|
|
||||||
The Modifications which You create or to which You contribute are
|
|
||||||
governed by the terms of this License, including without limitation
|
|
||||||
Section 2.2. The Source Code version of Covered Code may be
|
|
||||||
distributed only under the terms of this License or a future version
|
|
||||||
of this License released under Section 6.1, and You must include a
|
|
||||||
copy of this License with every copy of the Source Code You
|
|
||||||
distribute. You may not offer or impose any terms on any Source Code
|
|
||||||
version that alters or restricts the applicable version of this
|
|
||||||
License or the recipients' rights hereunder. However, You may include
|
|
||||||
an additional document offering the additional rights described in
|
|
||||||
Section 3.5.
|
|
||||||
|
|
||||||
3.2. Availability of Source Code.
|
|
||||||
Any Modification which You create or to which You contribute must be
|
|
||||||
made available in Source Code form under the terms of this License
|
|
||||||
either on the same media as an Executable version or via an accepted
|
|
||||||
Electronic Distribution Mechanism to anyone to whom you made an
|
|
||||||
Executable version available; and if made available via Electronic
|
|
||||||
Distribution Mechanism, must remain available for at least twelve (12)
|
|
||||||
months after the date it initially became available, or at least six
|
|
||||||
(6) months after a subsequent version of that particular Modification
|
|
||||||
has been made available to such recipients. You are responsible for
|
|
||||||
ensuring that the Source Code version remains available even if the
|
|
||||||
Electronic Distribution Mechanism is maintained by a third party.
|
|
||||||
|
|
||||||
3.3. Description of Modifications.
|
|
||||||
You must cause all Covered Code to which You contribute to contain a
|
|
||||||
file documenting the changes You made to create that Covered Code and
|
|
||||||
the date of any change. You must include a prominent statement that
|
|
||||||
the Modification is derived, directly or indirectly, from Original
|
|
||||||
Code provided by the Initial Developer and including the name of the
|
|
||||||
Initial Developer in (a) the Source Code, and (b) in any notice in an
|
|
||||||
Executable version or related documentation in which You describe the
|
|
||||||
origin or ownership of the Covered Code.
|
|
||||||
|
|
||||||
3.4. Intellectual Property Matters
|
|
||||||
(a) Third Party Claims.
|
|
||||||
If Contributor has knowledge that a license under a third party's
|
|
||||||
intellectual property rights is required to exercise the rights
|
|
||||||
granted by such Contributor under Sections 2.1 or 2.2,
|
|
||||||
Contributor must include a text file with the Source Code
|
|
||||||
distribution titled "LEGAL" which describes the claim and the
|
|
||||||
party making the claim in sufficient detail that a recipient will
|
|
||||||
know whom to contact. If Contributor obtains such knowledge after
|
|
||||||
the Modification is made available as described in Section 3.2,
|
|
||||||
Contributor shall promptly modify the LEGAL file in all copies
|
|
||||||
Contributor makes available thereafter and shall take other steps
|
|
||||||
(such as notifying appropriate mailing lists or newsgroups)
|
|
||||||
reasonably calculated to inform those who received the Covered
|
|
||||||
Code that new knowledge has been obtained.
|
|
||||||
|
|
||||||
(b) Contributor APIs.
|
|
||||||
If Contributor's Modifications include an application programming
|
|
||||||
interface and Contributor has knowledge of patent licenses which
|
|
||||||
are reasonably necessary to implement that API, Contributor must
|
|
||||||
also include this information in the LEGAL file.
|
|
||||||
|
|
||||||
(c) Representations.
|
|
||||||
Contributor represents that, except as disclosed pursuant to
|
|
||||||
Section 3.4(a) above, Contributor believes that Contributor's
|
|
||||||
Modifications are Contributor's original creation(s) and/or
|
|
||||||
Contributor has sufficient rights to grant the rights conveyed by
|
|
||||||
this License.
|
|
||||||
|
|
||||||
3.5. Required Notices.
|
|
||||||
You must duplicate the notice in Exhibit A in each file of the Source
|
|
||||||
Code. If it is not possible to put such notice in a particular Source
|
|
||||||
Code file due to its structure, then You must include such notice in a
|
|
||||||
location (such as a relevant directory) where a user would be likely
|
|
||||||
to look for such a notice. If You created one or more Modification(s)
|
|
||||||
You may add your name as a Contributor to the notice described in
|
|
||||||
Exhibit A. You must also duplicate this License in any documentation
|
|
||||||
for the Source Code where You describe recipients' rights or ownership
|
|
||||||
rights relating to Covered Code. You may choose to offer, and to
|
|
||||||
charge a fee for, warranty, support, indemnity or liability
|
|
||||||
obligations to one or more recipients of Covered Code. However, You
|
|
||||||
may do so only on Your own behalf, and not on behalf of the Initial
|
|
||||||
Developer or any Contributor. You must make it absolutely clear than
|
|
||||||
any such warranty, support, indemnity or liability obligation is
|
|
||||||
offered by You alone, and You hereby agree to indemnify the Initial
|
|
||||||
Developer and every Contributor for any liability incurred by the
|
|
||||||
Initial Developer or such Contributor as a result of warranty,
|
|
||||||
support, indemnity or liability terms You offer.
|
|
||||||
|
|
||||||
3.6. Distribution of Executable Versions.
|
|
||||||
You may distribute Covered Code in Executable form only if the
|
|
||||||
requirements of Section 3.1-3.5 have been met for that Covered Code,
|
|
||||||
and if You include a notice stating that the Source Code version of
|
|
||||||
the Covered Code is available under the terms of this License,
|
|
||||||
including a description of how and where You have fulfilled the
|
|
||||||
obligations of Section 3.2. The notice must be conspicuously included
|
|
||||||
in any notice in an Executable version, related documentation or
|
|
||||||
collateral in which You describe recipients' rights relating to the
|
|
||||||
Covered Code. You may distribute the Executable version of Covered
|
|
||||||
Code or ownership rights under a license of Your choice, which may
|
|
||||||
contain terms different from this License, provided that You are in
|
|
||||||
compliance with the terms of this License and that the license for the
|
|
||||||
Executable version does not attempt to limit or alter the recipient's
|
|
||||||
rights in the Source Code version from the rights set forth in this
|
|
||||||
License. If You distribute the Executable version under a different
|
|
||||||
license You must make it absolutely clear that any terms which differ
|
|
||||||
from this License are offered by You alone, not by the Initial
|
|
||||||
Developer or any Contributor. You hereby agree to indemnify the
|
|
||||||
Initial Developer and every Contributor for any liability incurred by
|
|
||||||
the Initial Developer or such Contributor as a result of any such
|
|
||||||
terms You offer.
|
|
||||||
|
|
||||||
3.7. Larger Works.
|
|
||||||
You may create a Larger Work by combining Covered Code with other code
|
|
||||||
not governed by the terms of this License and distribute the Larger
|
|
||||||
Work as a single product. In such a case, You must make sure the
|
|
||||||
requirements of this License are fulfilled for the Covered Code.
|
|
||||||
|
|
||||||
4. Inability to Comply Due to Statute or Regulation.
|
|
||||||
|
|
||||||
If it is impossible for You to comply with any of the terms of this
|
|
||||||
License with respect to some or all of the Covered Code due to
|
|
||||||
statute, judicial order, or regulation then You must: (a) comply with
|
|
||||||
the terms of this License to the maximum extent possible; and (b)
|
|
||||||
describe the limitations and the code they affect. Such description
|
|
||||||
must be included in the LEGAL file described in Section 3.4 and must
|
|
||||||
be included with all distributions of the Source Code. Except to the
|
|
||||||
extent prohibited by statute or regulation, such description must be
|
|
||||||
sufficiently detailed for a recipient of ordinary skill to be able to
|
|
||||||
understand it.
|
|
||||||
|
|
||||||
5. Application of this License.
|
|
||||||
|
|
||||||
This License applies to code to which the Initial Developer has
|
|
||||||
attached the notice in Exhibit A and to related Covered Code.
|
|
||||||
|
|
||||||
6. Versions of the License.
|
|
||||||
|
|
||||||
6.1. New Versions.
|
|
||||||
Netscape Communications Corporation ("Netscape") may publish revised
|
|
||||||
and/or new versions of the License from time to time. Each version
|
|
||||||
will be given a distinguishing version number.
|
|
||||||
|
|
||||||
6.2. Effect of New Versions.
|
|
||||||
Once Covered Code has been published under a particular version of the
|
|
||||||
License, You may always continue to use it under the terms of that
|
|
||||||
version. You may also choose to use such Covered Code under the terms
|
|
||||||
of any subsequent version of the License published by Netscape. No one
|
|
||||||
other than Netscape has the right to modify the terms applicable to
|
|
||||||
Covered Code created under this License.
|
|
||||||
|
|
||||||
6.3. Derivative Works.
|
|
||||||
If You create or use a modified version of this License (which you may
|
|
||||||
only do in order to apply it to code which is not already Covered Code
|
|
||||||
governed by this License), You must (a) rename Your license so that
|
|
||||||
the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
|
|
||||||
"MPL", "NPL" or any confusingly similar phrase do not appear in your
|
|
||||||
license (except to note that your license differs from this License)
|
|
||||||
and (b) otherwise make it clear that Your version of the license
|
|
||||||
contains terms which differ from the Mozilla Public License and
|
|
||||||
Netscape Public License. (Filling in the name of the Initial
|
|
||||||
Developer, Original Code or Contributor in the notice described in
|
|
||||||
Exhibit A shall not of themselves be deemed to be modifications of
|
|
||||||
this License.)
|
|
||||||
|
|
||||||
7. DISCLAIMER OF WARRANTY.
|
|
||||||
|
|
||||||
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
|
||||||
WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
|
|
||||||
DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
|
|
||||||
THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
|
|
||||||
IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
|
|
||||||
YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
|
|
||||||
COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
|
|
||||||
OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
|
|
||||||
ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
|
|
||||||
|
|
||||||
8. TERMINATION.
|
|
||||||
|
|
||||||
8.1. This License and the rights granted hereunder will terminate
|
|
||||||
automatically if You fail to comply with terms herein and fail to cure
|
|
||||||
such breach within 30 days of becoming aware of the breach. All
|
|
||||||
sublicenses to the Covered Code which are properly granted shall
|
|
||||||
survive any termination of this License. Provisions which, by their
|
|
||||||
nature, must remain in effect beyond the termination of this License
|
|
||||||
shall survive.
|
|
||||||
|
|
||||||
8.2. If You initiate litigation by asserting a patent infringement
|
|
||||||
claim (excluding declatory judgment actions) against Initial Developer
|
|
||||||
or a Contributor (the Initial Developer or Contributor against whom
|
|
||||||
You file such action is referred to as "Participant") alleging that:
|
|
||||||
|
|
||||||
(a) such Participant's Contributor Version directly or indirectly
|
|
||||||
infringes any patent, then any and all rights granted by such
|
|
||||||
Participant to You under Sections 2.1 and/or 2.2 of this License
|
|
||||||
shall, upon 60 days notice from Participant terminate prospectively,
|
|
||||||
unless if within 60 days after receipt of notice You either: (i)
|
|
||||||
agree in writing to pay Participant a mutually agreeable reasonable
|
|
||||||
royalty for Your past and future use of Modifications made by such
|
|
||||||
Participant, or (ii) withdraw Your litigation claim with respect to
|
|
||||||
the Contributor Version against such Participant. If within 60 days
|
|
||||||
of notice, a reasonable royalty and payment arrangement are not
|
|
||||||
mutually agreed upon in writing by the parties or the litigation claim
|
|
||||||
is not withdrawn, the rights granted by Participant to You under
|
|
||||||
Sections 2.1 and/or 2.2 automatically terminate at the expiration of
|
|
||||||
the 60 day notice period specified above.
|
|
||||||
|
|
||||||
(b) any software, hardware, or device, other than such Participant's
|
|
||||||
Contributor Version, directly or indirectly infringes any patent, then
|
|
||||||
any rights granted to You by such Participant under Sections 2.1(b)
|
|
||||||
and 2.2(b) are revoked effective as of the date You first made, used,
|
|
||||||
sold, distributed, or had made, Modifications made by that
|
|
||||||
Participant.
|
|
||||||
|
|
||||||
8.3. If You assert a patent infringement claim against Participant
|
|
||||||
alleging that such Participant's Contributor Version directly or
|
|
||||||
indirectly infringes any patent where such claim is resolved (such as
|
|
||||||
by license or settlement) prior to the initiation of patent
|
|
||||||
infringement litigation, then the reasonable value of the licenses
|
|
||||||
granted by such Participant under Sections 2.1 or 2.2 shall be taken
|
|
||||||
into account in determining the amount or value of any payment or
|
|
||||||
license.
|
|
||||||
|
|
||||||
8.4. In the event of termination under Sections 8.1 or 8.2 above,
|
|
||||||
all end user license agreements (excluding distributors and resellers)
|
|
||||||
which have been validly granted by You or any distributor hereunder
|
|
||||||
prior to termination shall survive termination.
|
|
||||||
|
|
||||||
9. LIMITATION OF LIABILITY.
|
|
||||||
|
|
||||||
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
|
|
||||||
(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
|
|
||||||
DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
|
|
||||||
OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
|
|
||||||
ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
|
|
||||||
CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
|
|
||||||
WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
|
|
||||||
COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
|
|
||||||
INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
|
|
||||||
LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
|
|
||||||
RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
|
|
||||||
PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
|
|
||||||
EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
|
|
||||||
THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
|
|
||||||
|
|
||||||
10. U.S. GOVERNMENT END USERS.
|
|
||||||
|
|
||||||
The Covered Code is a "commercial item," as that term is defined in
|
|
||||||
48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
|
|
||||||
software" and "commercial computer software documentation," as such
|
|
||||||
terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
|
|
||||||
C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
|
|
||||||
all U.S. Government End Users acquire Covered Code with only those
|
|
||||||
rights set forth herein.
|
|
||||||
|
|
||||||
11. MISCELLANEOUS.
|
|
||||||
|
|
||||||
This License represents the complete agreement concerning subject
|
|
||||||
matter hereof. If any provision of this License is held to be
|
|
||||||
unenforceable, such provision shall be reformed only to the extent
|
|
||||||
necessary to make it enforceable. This License shall be governed by
|
|
||||||
California law provisions (except to the extent applicable law, if
|
|
||||||
any, provides otherwise), excluding its conflict-of-law provisions.
|
|
||||||
With respect to disputes in which at least one party is a citizen of,
|
|
||||||
or an entity chartered or registered to do business in the United
|
|
||||||
States of America, any litigation relating to this License shall be
|
|
||||||
subject to the jurisdiction of the Federal Courts of the Northern
|
|
||||||
District of California, with venue lying in Santa Clara County,
|
|
||||||
California, with the losing party responsible for costs, including
|
|
||||||
without limitation, court costs and reasonable attorneys' fees and
|
|
||||||
expenses. The application of the United Nations Convention on
|
|
||||||
Contracts for the International Sale of Goods is expressly excluded.
|
|
||||||
Any law or regulation which provides that the language of a contract
|
|
||||||
shall be construed against the drafter shall not apply to this
|
|
||||||
License.
|
|
||||||
|
|
||||||
12. RESPONSIBILITY FOR CLAIMS.
|
|
||||||
|
|
||||||
As between Initial Developer and the Contributors, each party is
|
|
||||||
responsible for claims and damages arising, directly or indirectly,
|
|
||||||
out of its utilization of rights under this License and You agree to
|
|
||||||
work with Initial Developer and Contributors to distribute such
|
|
||||||
responsibility on an equitable basis. Nothing herein is intended or
|
|
||||||
shall be deemed to constitute any admission of liability.
|
|
||||||
|
|
||||||
13. MULTIPLE-LICENSED CODE.
|
|
||||||
|
|
||||||
Initial Developer may designate portions of the Covered Code as
|
|
||||||
"Multiple-Licensed". "Multiple-Licensed" means that the Initial
|
|
||||||
Developer permits you to utilize portions of the Covered Code under
|
|
||||||
Your choice of the NPL or the alternative licenses, if any, specified
|
|
||||||
by the Initial Developer in the file described in Exhibit A.
|
|
||||||
|
|
||||||
EXHIBIT A -Mozilla Public License.
|
|
||||||
|
|
||||||
``The contents of this file are subject to the Mozilla Public License
|
|
||||||
Version 1.1 (the "License"); you may not use this file except in
|
|
||||||
compliance with the License. You may obtain a copy of the License at
|
|
||||||
http://www.mozilla.org/MPL/
|
|
||||||
|
|
||||||
Software distributed under the License is distributed on an "AS IS"
|
|
||||||
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
|
||||||
License for the specific language governing rights and limitations
|
|
||||||
under the License.
|
|
||||||
|
|
||||||
The Original Code is ______________________________________.
|
|
||||||
|
|
||||||
The Initial Developer of the Original Code is ________________________.
|
|
||||||
Portions created by ______________________ are Copyright (C) ______
|
|
||||||
_______________________. All Rights Reserved.
|
|
||||||
|
|
||||||
Contributor(s): ______________________________________.
|
|
||||||
|
|
||||||
Alternatively, the contents of this file may be used under the terms
|
|
||||||
of the _____ license (the "[___] License"), in which case the
|
|
||||||
provisions of [______] License are applicable instead of those
|
|
||||||
above. If you wish to allow use of your version of this file only
|
|
||||||
under the terms of the [____] License and not to allow others to use
|
|
||||||
your version of this file under the MPL, indicate your decision by
|
|
||||||
deleting the provisions above and replace them with the notice and
|
|
||||||
other provisions required by the [___] License. If you do not delete
|
|
||||||
the provisions above, a recipient may use your version of this file
|
|
||||||
under either the MPL or the [___] License."
|
|
||||||
|
|
||||||
[NOTE: The text of this Exhibit A may differ slightly from the text of
|
|
||||||
the notices in the Source Code files of the Original Code. You should
|
|
||||||
use the text of this Exhibit A rather than the text found in the
|
|
||||||
Original Code Source Code for Your Modifications.]
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
/Applications/ejabberd-2.0.3
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
|
|
||||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>Label</key>
|
|
||||||
<string>ejabberd.etc.rc.command</string>
|
|
||||||
<key>ProgramArguments</key>
|
|
||||||
<array>
|
|
||||||
<string>/Applications/ejabberd-2.0.3/bin/ejabberdctl</string>
|
|
||||||
<string>start</string>
|
|
||||||
</array>
|
|
||||||
<key>RunAtLoad</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
|
@ -1,439 +0,0 @@
|
||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 42;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
837D1CC80B635DA9003F99C1 /* ejabberd.plist in Resources */ = {isa = PBXBuildFile; fileRef = 837D1CC70B635DA9003F99C1 /* ejabberd.plist */; };
|
|
||||||
837F6DD209F777FF00914317 /* ejabberdController.h in Headers */ = {isa = PBXBuildFile; fileRef = 837F6DD009F777FF00914317 /* ejabberdController.h */; };
|
|
||||||
837F6DD309F777FF00914317 /* ejabberdController.m in Sources */ = {isa = PBXBuildFile; fileRef = 837F6DD109F777FF00914317 /* ejabberdController.m */; };
|
|
||||||
837F6DDD09F7E55000914317 /* logo_ejabberd.png in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DDB09F7E55000914317 /* logo_ejabberd.png */; };
|
|
||||||
837F6DDF09F7E6FD00914317 /* logo_ejabberd.png in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DDE09F7E6FD00914317 /* logo_ejabberd.png */; };
|
|
||||||
837F6DE409F7E8BF00914317 /* instance_started.png in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DE209F7E8BF00914317 /* instance_started.png */; };
|
|
||||||
837F6DE509F7E8BF00914317 /* instance_stopped.png in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DE309F7E8BF00914317 /* instance_stopped.png */; };
|
|
||||||
837F6DEE09F8D41200914317 /* config.txt in Resources */ = {isa = PBXBuildFile; fileRef = 837F6DED09F8D41200914317 /* config.txt */; };
|
|
||||||
8D202CEA0486D31800D8A456 /* ejabberd_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 32DBCFA20370C41700C91783 /* ejabberd_Prefix.pch */; };
|
|
||||||
8D202CEB0486D31800D8A456 /* ejabberdPref.h in Headers */ = {isa = PBXBuildFile; fileRef = F506C03C013D9D7901CA16C8 /* ejabberdPref.h */; };
|
|
||||||
8D202CED0486D31800D8A456 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; };
|
|
||||||
8D202CEE0486D31800D8A456 /* ejabberdPref.tiff in Resources */ = {isa = PBXBuildFile; fileRef = F506C040013D9D8001CA16C8 /* ejabberdPref.tiff */; };
|
|
||||||
8D202CEF0486D31800D8A456 /* ejabberdPref.nib in Resources */ = {isa = PBXBuildFile; fileRef = F506C042013D9D8C01CA16C8 /* ejabberdPref.nib */; };
|
|
||||||
8D202CF10486D31800D8A456 /* ejabberdPref.m in Sources */ = {isa = PBXBuildFile; fileRef = F506C03D013D9D7901CA16C8 /* ejabberdPref.m */; };
|
|
||||||
8D202CF30486D31800D8A456 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; };
|
|
||||||
8D202CF40486D31800D8A456 /* PreferencePanes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F506C035013D953901CA16C8 /* PreferencePanes.framework */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
|
|
||||||
089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
|
||||||
089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
|
|
||||||
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
|
|
||||||
32DBCFA20370C41700C91783 /* ejabberd_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ejabberd_Prefix.pch; sourceTree = "<group>"; };
|
|
||||||
837D1CC70B635DA9003F99C1 /* ejabberd.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = ejabberd.plist; sourceTree = "<group>"; };
|
|
||||||
837F6DD009F777FF00914317 /* ejabberdController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ejabberdController.h; sourceTree = "<group>"; };
|
|
||||||
837F6DD109F777FF00914317 /* ejabberdController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ejabberdController.m; sourceTree = "<group>"; };
|
|
||||||
837F6DDB09F7E55000914317 /* logo_ejabberd.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_ejabberd.png; sourceTree = "<group>"; };
|
|
||||||
837F6DDE09F7E6FD00914317 /* logo_ejabberd.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_ejabberd.png; sourceTree = "<group>"; };
|
|
||||||
837F6DE209F7E8BF00914317 /* instance_started.png */ = {isa = PBXFileReference; explicitFileType = image.png; path = instance_started.png; sourceTree = "<group>"; };
|
|
||||||
837F6DE309F7E8BF00914317 /* instance_stopped.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = instance_stopped.png; sourceTree = "<group>"; };
|
|
||||||
837F6DED09F8D41200914317 /* config.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = config.txt; sourceTree = "<group>"; };
|
|
||||||
8D202CF70486D31800D8A456 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
|
||||||
8D202CF80486D31800D8A456 /* ejabberd.prefPane */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ejabberd.prefPane; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
F506C035013D953901CA16C8 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = "<absolute>"; };
|
|
||||||
F506C03C013D9D7901CA16C8 /* ejabberdPref.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ejabberdPref.h; sourceTree = "<group>"; };
|
|
||||||
F506C03D013D9D7901CA16C8 /* ejabberdPref.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ejabberdPref.m; sourceTree = "<group>"; };
|
|
||||||
F506C040013D9D8001CA16C8 /* ejabberdPref.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = ejabberdPref.tiff; sourceTree = "<group>"; };
|
|
||||||
F506C043013D9D8C01CA16C8 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/ejabberdPref.nib; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
8D202CF20486D31800D8A456 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
8D202CF30486D31800D8A456 /* Cocoa.framework in Frameworks */,
|
|
||||||
8D202CF40486D31800D8A456 /* PreferencePanes.framework in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
089C166AFE841209C02AAC07 /* ejabberd */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
08FB77AFFE84173DC02AAC07 /* Classes */,
|
|
||||||
32DBCFA10370C40200C91783 /* Other Sources */,
|
|
||||||
089C167CFE841241C02AAC07 /* Resources */,
|
|
||||||
089C1671FE841209C02AAC07 /* Frameworks and Libraries */,
|
|
||||||
19C28FB8FE9D52D311CA2CBB /* Products */,
|
|
||||||
);
|
|
||||||
name = ejabberd;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */,
|
|
||||||
1058C7AEFEA557BF11CA2CBB /* Other Frameworks */,
|
|
||||||
);
|
|
||||||
name = "Frameworks and Libraries";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
089C167CFE841241C02AAC07 /* Resources */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
837F6DE209F7E8BF00914317 /* instance_started.png */,
|
|
||||||
837F6DE309F7E8BF00914317 /* instance_stopped.png */,
|
|
||||||
8D202CF70486D31800D8A456 /* Info.plist */,
|
|
||||||
089C167DFE841241C02AAC07 /* InfoPlist.strings */,
|
|
||||||
F506C040013D9D8001CA16C8 /* ejabberdPref.tiff */,
|
|
||||||
F506C042013D9D8C01CA16C8 /* ejabberdPref.nib */,
|
|
||||||
837F6DDB09F7E55000914317 /* logo_ejabberd.png */,
|
|
||||||
837F6DDE09F7E6FD00914317 /* logo_ejabberd.png */,
|
|
||||||
837F6DED09F8D41200914317 /* config.txt */,
|
|
||||||
);
|
|
||||||
name = Resources;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
08FB77AFFE84173DC02AAC07 /* Classes */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
F506C03C013D9D7901CA16C8 /* ejabberdPref.h */,
|
|
||||||
F506C03D013D9D7901CA16C8 /* ejabberdPref.m */,
|
|
||||||
);
|
|
||||||
name = Classes;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */,
|
|
||||||
F506C035013D953901CA16C8 /* PreferencePanes.framework */,
|
|
||||||
);
|
|
||||||
name = "Linked Frameworks";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
089C1672FE841209C02AAC07 /* Foundation.framework */,
|
|
||||||
089C167FFE841241C02AAC07 /* AppKit.framework */,
|
|
||||||
);
|
|
||||||
name = "Other Frameworks";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
19C28FB8FE9D52D311CA2CBB /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
8D202CF80486D31800D8A456 /* ejabberd.prefPane */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
32DBCFA10370C40200C91783 /* Other Sources */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
837D1CC70B635DA9003F99C1 /* ejabberd.plist */,
|
|
||||||
837F6DD009F777FF00914317 /* ejabberdController.h */,
|
|
||||||
837F6DD109F777FF00914317 /* ejabberdController.m */,
|
|
||||||
32DBCFA20370C41700C91783 /* ejabberd_Prefix.pch */,
|
|
||||||
);
|
|
||||||
name = "Other Sources";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXHeadersBuildPhase section */
|
|
||||||
8D202CE90486D31800D8A456 /* Headers */ = {
|
|
||||||
isa = PBXHeadersBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
8D202CEA0486D31800D8A456 /* ejabberd_Prefix.pch in Headers */,
|
|
||||||
8D202CEB0486D31800D8A456 /* ejabberdPref.h in Headers */,
|
|
||||||
837F6DD209F777FF00914317 /* ejabberdController.h in Headers */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXHeadersBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
8D202CE80486D31800D8A456 /* ejabberd */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = 83231E620B57EBEA006434E8 /* Build configuration list for PBXNativeTarget "ejabberd" */;
|
|
||||||
buildPhases = (
|
|
||||||
8D202CE90486D31800D8A456 /* Headers */,
|
|
||||||
8D202CEC0486D31800D8A456 /* Resources */,
|
|
||||||
8D202CF00486D31800D8A456 /* Sources */,
|
|
||||||
8D202CF20486D31800D8A456 /* Frameworks */,
|
|
||||||
8D202CF50486D31800D8A456 /* Rez */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = ejabberd;
|
|
||||||
productInstallPath = "$(HOME)/Library/PreferencePanes";
|
|
||||||
productName = ejabberd;
|
|
||||||
productReference = 8D202CF80486D31800D8A456 /* ejabberd.prefPane */;
|
|
||||||
productType = "com.apple.product-type.bundle";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
089C1669FE841209C02AAC07 /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
buildConfigurationList = 83231E660B57EBEA006434E8 /* Build configuration list for PBXProject "ejabberd" */;
|
|
||||||
hasScannedForEncodings = 1;
|
|
||||||
mainGroup = 089C166AFE841209C02AAC07 /* ejabberd */;
|
|
||||||
projectDirPath = "";
|
|
||||||
targets = (
|
|
||||||
8D202CE80486D31800D8A456 /* ejabberd */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
|
||||||
8D202CEC0486D31800D8A456 /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
8D202CED0486D31800D8A456 /* InfoPlist.strings in Resources */,
|
|
||||||
8D202CEE0486D31800D8A456 /* ejabberdPref.tiff in Resources */,
|
|
||||||
8D202CEF0486D31800D8A456 /* ejabberdPref.nib in Resources */,
|
|
||||||
837F6DDD09F7E55000914317 /* logo_ejabberd.png in Resources */,
|
|
||||||
837F6DDF09F7E6FD00914317 /* logo_ejabberd.png in Resources */,
|
|
||||||
837F6DE409F7E8BF00914317 /* instance_started.png in Resources */,
|
|
||||||
837F6DE509F7E8BF00914317 /* instance_stopped.png in Resources */,
|
|
||||||
837F6DEE09F8D41200914317 /* config.txt in Resources */,
|
|
||||||
837D1CC80B635DA9003F99C1 /* ejabberd.plist in Resources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXResourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXRezBuildPhase section */
|
|
||||||
8D202CF50486D31800D8A456 /* Rez */ = {
|
|
||||||
isa = PBXRezBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXRezBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
8D202CF00486D31800D8A456 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
8D202CF10486D31800D8A456 /* ejabberdPref.m in Sources */,
|
|
||||||
837F6DD309F777FF00914317 /* ejabberdController.m in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
|
||||||
089C167DFE841241C02AAC07 /* InfoPlist.strings */ = {
|
|
||||||
isa = PBXVariantGroup;
|
|
||||||
children = (
|
|
||||||
089C167EFE841241C02AAC07 /* English */,
|
|
||||||
);
|
|
||||||
name = InfoPlist.strings;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
F506C042013D9D8C01CA16C8 /* ejabberdPref.nib */ = {
|
|
||||||
isa = PBXVariantGroup;
|
|
||||||
children = (
|
|
||||||
F506C043013D9D8C01CA16C8 /* English */,
|
|
||||||
);
|
|
||||||
name = ejabberdPref.nib;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXVariantGroup section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
83231E630B57EBEA006434E8 /* Development */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ARCHS = (
|
|
||||||
ppc,
|
|
||||||
i386,
|
|
||||||
);
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUGGING_SYMBOLS = YES;
|
|
||||||
FRAMEWORK_SEARCH_PATHS = "";
|
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
|
||||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
|
||||||
GCC_ENABLE_TRIGRAPHS = NO;
|
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
|
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
|
||||||
GCC_PREFIX_HEADER = ejabberd_Prefix.pch;
|
|
||||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
|
|
||||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
|
|
||||||
GCC_WARN_UNKNOWN_PRAGMAS = NO;
|
|
||||||
HEADER_SEARCH_PATHS = "";
|
|
||||||
INFOPLIST_FILE = Info.plist;
|
|
||||||
INSTALL_PATH = "$(HOME)/Library/PreferencePanes";
|
|
||||||
LIBRARY_SEARCH_PATHS = "";
|
|
||||||
LIBRARY_STYLE = Bundle;
|
|
||||||
OPTIMIZATION_CFLAGS = "-O0";
|
|
||||||
OTHER_CFLAGS = "";
|
|
||||||
OTHER_LDFLAGS = (
|
|
||||||
"-bundle",
|
|
||||||
"-twolevel_namespace",
|
|
||||||
);
|
|
||||||
OTHER_REZFLAGS = "";
|
|
||||||
PRODUCT_NAME = ejabberd;
|
|
||||||
SECTORDER_FLAGS = "";
|
|
||||||
WARNING_CFLAGS = (
|
|
||||||
"-Wmost",
|
|
||||||
"-Wno-four-char-constants",
|
|
||||||
"-Wno-unknown-pragmas",
|
|
||||||
);
|
|
||||||
WRAPPER_EXTENSION = prefPane;
|
|
||||||
ZERO_LINK = YES;
|
|
||||||
};
|
|
||||||
name = Development;
|
|
||||||
};
|
|
||||||
83231E640B57EBEA006434E8 /* Deployment */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ARCHS = (
|
|
||||||
ppc,
|
|
||||||
i386,
|
|
||||||
);
|
|
||||||
COPY_PHASE_STRIP = YES;
|
|
||||||
FRAMEWORK_SEARCH_PATHS = "";
|
|
||||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
|
||||||
GCC_ENABLE_TRIGRAPHS = NO;
|
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
|
||||||
GCC_PREFIX_HEADER = ejabberd_Prefix.pch;
|
|
||||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
|
|
||||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
|
|
||||||
GCC_WARN_UNKNOWN_PRAGMAS = NO;
|
|
||||||
HEADER_SEARCH_PATHS = "";
|
|
||||||
INFOPLIST_FILE = Info.plist;
|
|
||||||
INSTALL_PATH = "$(HOME)/Library/PreferencePanes";
|
|
||||||
LIBRARY_SEARCH_PATHS = "";
|
|
||||||
LIBRARY_STYLE = Bundle;
|
|
||||||
OTHER_CFLAGS = "";
|
|
||||||
OTHER_LDFLAGS = (
|
|
||||||
"-bundle",
|
|
||||||
"-twolevel_namespace",
|
|
||||||
);
|
|
||||||
OTHER_REZFLAGS = "";
|
|
||||||
PRODUCT_NAME = ejabberd;
|
|
||||||
SECTORDER_FLAGS = "";
|
|
||||||
WARNING_CFLAGS = (
|
|
||||||
"-Wmost",
|
|
||||||
"-Wno-four-char-constants",
|
|
||||||
"-Wno-unknown-pragmas",
|
|
||||||
);
|
|
||||||
WRAPPER_EXTENSION = prefPane;
|
|
||||||
ZERO_LINK = NO;
|
|
||||||
};
|
|
||||||
name = Deployment;
|
|
||||||
};
|
|
||||||
83231E650B57EBEA006434E8 /* Default */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ARCHS = (
|
|
||||||
ppc,
|
|
||||||
i386,
|
|
||||||
);
|
|
||||||
FRAMEWORK_SEARCH_PATHS = "";
|
|
||||||
GCC_ENABLE_TRIGRAPHS = NO;
|
|
||||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
|
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
|
||||||
GCC_PREFIX_HEADER = ejabberd_Prefix.pch;
|
|
||||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
|
|
||||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = NO;
|
|
||||||
GCC_WARN_UNKNOWN_PRAGMAS = NO;
|
|
||||||
HEADER_SEARCH_PATHS = "";
|
|
||||||
INFOPLIST_FILE = Info.plist;
|
|
||||||
INSTALL_PATH = "$(HOME)/Library/PreferencePanes";
|
|
||||||
LIBRARY_SEARCH_PATHS = "";
|
|
||||||
LIBRARY_STYLE = Bundle;
|
|
||||||
OTHER_CFLAGS = "";
|
|
||||||
OTHER_LDFLAGS = (
|
|
||||||
"-bundle",
|
|
||||||
"-twolevel_namespace",
|
|
||||||
);
|
|
||||||
OTHER_REZFLAGS = "";
|
|
||||||
PRODUCT_NAME = ejabberd;
|
|
||||||
SECTORDER_FLAGS = "";
|
|
||||||
WARNING_CFLAGS = (
|
|
||||||
"-Wmost",
|
|
||||||
"-Wno-four-char-constants",
|
|
||||||
"-Wno-unknown-pragmas",
|
|
||||||
);
|
|
||||||
WRAPPER_EXTENSION = prefPane;
|
|
||||||
};
|
|
||||||
name = Default;
|
|
||||||
};
|
|
||||||
83231E670B57EBEA006434E8 /* Development */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ARCHS = (
|
|
||||||
ppc,
|
|
||||||
i386,
|
|
||||||
);
|
|
||||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
|
||||||
};
|
|
||||||
name = Development;
|
|
||||||
};
|
|
||||||
83231E680B57EBEA006434E8 /* Deployment */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ARCHS = (
|
|
||||||
ppc,
|
|
||||||
i386,
|
|
||||||
);
|
|
||||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
|
||||||
};
|
|
||||||
name = Deployment;
|
|
||||||
};
|
|
||||||
83231E690B57EBEA006434E8 /* Default */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ARCHS = (
|
|
||||||
ppc,
|
|
||||||
i386,
|
|
||||||
);
|
|
||||||
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
|
|
||||||
};
|
|
||||||
name = Default;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
83231E620B57EBEA006434E8 /* Build configuration list for PBXNativeTarget "ejabberd" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
83231E630B57EBEA006434E8 /* Development */,
|
|
||||||
83231E640B57EBEA006434E8 /* Deployment */,
|
|
||||||
83231E650B57EBEA006434E8 /* Default */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Deployment;
|
|
||||||
};
|
|
||||||
83231E660B57EBEA006434E8 /* Build configuration list for PBXProject "ejabberd" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
83231E670B57EBEA006434E8 /* Development */,
|
|
||||||
83231E680B57EBEA006434E8 /* Deployment */,
|
|
||||||
83231E690B57EBEA006434E8 /* Default */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Deployment;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = 089C1669FE841209C02AAC07 /* Project object */;
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// ejabberdController.h
|
|
||||||
// ejabberd preference pane
|
|
||||||
//
|
|
||||||
// Created on Wed Apr 19 2006.
|
|
||||||
// Copyright (c) 2006-2009 ProcessOne.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
|
||||||
|
|
||||||
@interface ejabberdController : NSObject
|
|
||||||
{
|
|
||||||
IBOutlet NSTextField *status;
|
|
||||||
//IBOutlet NSImageView *image;
|
|
||||||
IBOutlet NSImageView *imagestarted;
|
|
||||||
IBOutlet NSImageView *imagestopped;
|
|
||||||
IBOutlet NSProgressIndicator *actionProgress;
|
|
||||||
IBOutlet NSButton *startStopButton;
|
|
||||||
IBOutlet NSButton *automaticBox;
|
|
||||||
BOOL started;
|
|
||||||
char startscript[128];
|
|
||||||
char stopscript[128];
|
|
||||||
char statusscript[128];
|
|
||||||
char waitstartedscript[128];
|
|
||||||
char waitstoppedscript[128];
|
|
||||||
}
|
|
||||||
- (void)setStarted:(BOOL)flag;
|
|
||||||
- (BOOL)isStarted;
|
|
||||||
- (void)getRunningStatus;
|
|
||||||
- (void)waitRunningStatus;
|
|
||||||
- (void)updateRunningStatus;
|
|
||||||
- (IBAction)startStopAction:(id)sender;
|
|
||||||
- (IBAction)automaticStartAction:(id)sender;
|
|
||||||
@end
|
|
|
@ -1,125 +0,0 @@
|
||||||
//
|
|
||||||
// ejabberdController.m
|
|
||||||
// ejabberd preference pane
|
|
||||||
//
|
|
||||||
// Created on Wed Apr 19 2006.
|
|
||||||
// Copyright (c) 2006-2009 ProcessOne.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
#import "ejabberdController.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
@implementation ejabberdController
|
|
||||||
|
|
||||||
- (id) init
|
|
||||||
{
|
|
||||||
if((self = [super init])) {
|
|
||||||
[self performSelector:@selector(getRunningStatus) withObject:self afterDelay:0.5];
|
|
||||||
/*
|
|
||||||
[automaticBox setState:1];
|
|
||||||
[automaticBox setNeedsDisplay];
|
|
||||||
NSDictionary *defaultDefaults = [[NSDictionary alloc] initWithContentsOfFile:
|
|
||||||
[bundle pathForResource:@"ejabberdPath" ofType:@"plist"]];
|
|
||||||
[[EjabberdPreferences preferences] registerDefaults:defaultDefaults];
|
|
||||||
[defaultDefaults release];
|
|
||||||
*/
|
|
||||||
//TODO: This is very dirty code. need rewrite by MacOS/ObjC coder.
|
|
||||||
FILE *file;
|
|
||||||
char conf[255];
|
|
||||||
strcpy(conf, getenv("HOME"));
|
|
||||||
strcat(conf, "/Library/PreferencePanes/ejabberd.prefPane/Contents/Resources/config.txt");
|
|
||||||
file = fopen(conf,"r");
|
|
||||||
char path[255], *ptr;
|
|
||||||
memset(path, 0, 128);
|
|
||||||
fgets(path, 127, file);
|
|
||||||
for(ptr=path; *ptr; ptr++)
|
|
||||||
{
|
|
||||||
if(*ptr==10) *ptr=0;
|
|
||||||
if(*ptr==13) *ptr=0;
|
|
||||||
}
|
|
||||||
fclose(file);
|
|
||||||
sprintf(startscript, "%s/bin/ejabberdctl start", path);
|
|
||||||
sprintf(stopscript, "%s/bin/ejabberdctl stop", path);
|
|
||||||
sprintf(statusscript, "%s/bin/ejabberdctl status", path);
|
|
||||||
sprintf(waitstartedscript, "%s/bin/ejabberdctl started", path);
|
|
||||||
sprintf(waitstoppedscript, "%s/bin/ejabberdctl stopped", path);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) setStarted:(BOOL)flag
|
|
||||||
{
|
|
||||||
started = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL) isStarted
|
|
||||||
{
|
|
||||||
return started;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) getRunningStatus
|
|
||||||
{
|
|
||||||
[actionProgress startAnimation:self];
|
|
||||||
started = (system(statusscript) == 0);
|
|
||||||
[self updateRunningStatus];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) waitRunningStatus
|
|
||||||
{
|
|
||||||
[actionProgress startAnimation:self];
|
|
||||||
system(started?waitstartedscript:waitstoppedscript);
|
|
||||||
[self getRunningStatus];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) updateRunningStatus
|
|
||||||
{
|
|
||||||
//[startStopButton setTitle:isStarted
|
|
||||||
// ? NSLocalizedStringFromTableInBundle(@"Stop ejabberd",nil,bundle,@"")
|
|
||||||
// : NSLocalizedStringFromTableInBundle(@"Start ejabberd",nil,bundle,@"")];
|
|
||||||
if(started)
|
|
||||||
{
|
|
||||||
[startStopButton setTitle:@"Stop ejabberd"];
|
|
||||||
[status setStringValue:@"ejabberd is started."];
|
|
||||||
[imagestarted setHidden:NO];
|
|
||||||
[imagestopped setHidden:YES];
|
|
||||||
} else {
|
|
||||||
[startStopButton setTitle:@"Start ejabberd"];
|
|
||||||
[status setStringValue:@"ejabberd is stopped."];
|
|
||||||
[imagestarted setHidden:YES];
|
|
||||||
[imagestopped setHidden:NO];
|
|
||||||
}
|
|
||||||
[actionProgress stopAnimation:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(IBAction)startStopAction:(id)sender
|
|
||||||
{
|
|
||||||
[actionProgress startAnimation:self];
|
|
||||||
if(started)
|
|
||||||
{
|
|
||||||
[status setStringValue:@"Stopping ejabberd..."];
|
|
||||||
started = !(system(stopscript) == 0);
|
|
||||||
} else {
|
|
||||||
[status setStringValue:@"Starting ejabberd..."];
|
|
||||||
started = (system(startscript) == 0);
|
|
||||||
}
|
|
||||||
[self performSelector:@selector(waitRunningStatus) withObject:self afterDelay:0.5];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(IBAction)automaticStartAction:(id)sender
|
|
||||||
{
|
|
||||||
//TODO: implement autostart
|
|
||||||
if([automaticBox state])
|
|
||||||
{
|
|
||||||
system("mkdir -p ~/Library/LaunchDaemons");
|
|
||||||
system("cp /Library/PreferencePanes/ejabberd.prefPane/Contents/Resources/ejabberd.plist ~/Library/LaunchDaemons");
|
|
||||||
system("launchctl load ~/Library/LaunchDaemons/ejabberd.plist");
|
|
||||||
} else {
|
|
||||||
system("launchctl unload ~/Library/LaunchDaemons/ejabberd.plist");
|
|
||||||
system("rm ~/Library/LaunchDaemons/ejabberd.plist");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,19 +0,0 @@
|
||||||
//
|
|
||||||
// ejabberdPref.h
|
|
||||||
// ejabberd preference pane
|
|
||||||
//
|
|
||||||
// Created on Wed Apr 19 2006.
|
|
||||||
// Copyright (c) 2006-2009 ProcessOne.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <PreferencePanes/PreferencePanes.h>
|
|
||||||
|
|
||||||
|
|
||||||
@interface ejabberdPref : NSPreferencePane
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) mainViewDidLoad;
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,19 +0,0 @@
|
||||||
//
|
|
||||||
// ejabberdPref.m
|
|
||||||
// ejabberd preference pane
|
|
||||||
//
|
|
||||||
// Created on Wed Apr 19 2006.
|
|
||||||
// Copyright (c) 2006-2009 ProcessOne.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "ejabberdPref.h"
|
|
||||||
|
|
||||||
|
|
||||||
@implementation ejabberdPref
|
|
||||||
|
|
||||||
- (void) mainViewDidLoad
|
|
||||||
{
|
|
||||||
//ejabberdPref->ejabberdController
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
Binary file not shown.
|
@ -1,8 +0,0 @@
|
||||||
//
|
|
||||||
// Prefix header for all source files of the 'ejabberd' target in the 'ejabberd' project.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifdef __OBJC__
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <AppKit/AppKit.h>
|
|
||||||
#endif
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.4 KiB |
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>BuildVersion</key>
|
|
||||||
<string>54</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>ProjectName</key>
|
|
||||||
<string>PreferencePaneTemplate</string>
|
|
||||||
<key>SourceVersion</key>
|
|
||||||
<string>120000</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
|
@ -0,0 +1,342 @@
|
||||||
|
As a special exception, the authors give permission to link this program
|
||||||
|
with the OpenSSL library and distribute the resulting binary.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
|
@ -0,0 +1,210 @@
|
||||||
|
# HTTP authentication module
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The purpose of this module is to connect with an external REST API and
|
||||||
|
delegate the authentication operations to it whenever possible. The
|
||||||
|
component must implement the API described in one of the next sections
|
||||||
|
for `ejabberd_auth_http` to work out of the box.
|
||||||
|
|
||||||
|
Thanks to ejabberd design, the user base does not have to be local and
|
||||||
|
this approach allows you to avoid user base duplication, without
|
||||||
|
having to grant access to your main backend database.
|
||||||
|
|
||||||
|
The module can be especially useful for users maintaining their own,
|
||||||
|
central user database, which is shared with other services. It fits
|
||||||
|
perfectly when client application uses custom authentication token and
|
||||||
|
ejabberd has to validate it externally.
|
||||||
|
|
||||||
|
This module requires ejabberd 20.02 or higher.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### How to enable
|
||||||
|
|
||||||
|
The simplest way is to just replace default `auth_method` option in
|
||||||
|
`ejabberd.yml` with `auth_method: http`.
|
||||||
|
|
||||||
|
However, enabling the module **is not enough!** Please follow
|
||||||
|
instructions below.
|
||||||
|
|
||||||
|
### Configuration options
|
||||||
|
|
||||||
|
`ejabberd_auth_http` requires some parameters to function
|
||||||
|
properly. The following options can be set in `auth_opts` in
|
||||||
|
`ejabberd.yml`:
|
||||||
|
|
||||||
|
* `host` (mandatory, `string`) - consists of protocol, hostname (or IP) and port (optional). Examples:
|
||||||
|
* `host: "http://localhost:12000"`
|
||||||
|
* `host: "https://10.20.30.40"`
|
||||||
|
* `connection_pool_size` (optional, `integer`, default: 10) - the
|
||||||
|
number of connections open to auth service
|
||||||
|
* `connection_opts` (optional, default: `[]`) - extra options for
|
||||||
|
hackers: http://erlang.org/doc/man/gen_tcp.html#type-connect_option
|
||||||
|
|
||||||
|
* `basic_auth` (optional, default: `""`) - HTTP Basic Authentication
|
||||||
|
in format `"username:password"`; auth service doesn't have to
|
||||||
|
require authentication for HTTP auth to work
|
||||||
|
|
||||||
|
* `path_prefix` (optional, default: `"/"`) - a path prefix to be
|
||||||
|
inserted between `host` and method name; must be terminated with `/`
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
```
|
||||||
|
auth_method: http
|
||||||
|
auth_opts:
|
||||||
|
host: "http://localhost:12000"
|
||||||
|
connection_pool_size: 10
|
||||||
|
connection_opts: []
|
||||||
|
basic_auth: ""
|
||||||
|
path_prefix: "/"
|
||||||
|
```
|
||||||
|
|
||||||
|
## SCRAM support
|
||||||
|
|
||||||
|
`ejabberd_auth_http` can use the SCRAM method. When SCRAM is enabled,
|
||||||
|
the passwords sent to the auth service are serialised and the same
|
||||||
|
serialised format is expected when fetching a password from the
|
||||||
|
component.
|
||||||
|
|
||||||
|
It is transparent when ejabberd is responsible for all DB operations
|
||||||
|
such as password setting, account creation etc.
|
||||||
|
|
||||||
|
The service CAN perform the (de)serialisation of SCRAM-encoded
|
||||||
|
passwords, using a format that will be described in near future in
|
||||||
|
separate document.
|
||||||
|
|
||||||
|
## Authentication service API
|
||||||
|
|
||||||
|
All GET requests include the following URL-encoded query string:
|
||||||
|
`?user=<username>&server=<domain>&pass=<password>`.
|
||||||
|
|
||||||
|
All POST requests have the following URL-encoded string in the request
|
||||||
|
body: `user=<username>&server=<domain>&pass=<password>`.
|
||||||
|
|
||||||
|
If certain method does not need a password, the value of `pass` is
|
||||||
|
**undefined**, so it shouldn't be used.
|
||||||
|
|
||||||
|
For the best integration, the return code range should not exceed the
|
||||||
|
list below:
|
||||||
|
|
||||||
|
* 500 - internal server error
|
||||||
|
* 409 - conflict
|
||||||
|
* 404 - not found
|
||||||
|
* 403 - not allowed
|
||||||
|
* 401 - not authorised
|
||||||
|
* 400 - other error, should be sent in response body
|
||||||
|
* 204 - success, no return data
|
||||||
|
* 201 - created
|
||||||
|
* 200 - success, return value in response body
|
||||||
|
|
||||||
|
Whenever the specification says "anything else", service should use
|
||||||
|
one of the codes from the list above.
|
||||||
|
|
||||||
|
Some requests consider multiple return codes a "success". It is up to
|
||||||
|
the server-side developer to pick one of the codes.
|
||||||
|
|
||||||
|
### `check_password`
|
||||||
|
|
||||||
|
* **Method:** GET
|
||||||
|
* **Type:** mandatory when SCRAM is not used
|
||||||
|
* **Return values:**
|
||||||
|
* 200, `true` or `false` in body
|
||||||
|
* anything else - will be treated as `false`
|
||||||
|
* **Description:** Must respond if the password is valid for user.
|
||||||
|
|
||||||
|
### `get_password`
|
||||||
|
|
||||||
|
* **Method:** GET
|
||||||
|
* **Type:** mandatory when SCRAM or DIGEST SASL mechanism is used
|
||||||
|
* **Return values:**
|
||||||
|
* 200, password in the body
|
||||||
|
* anything else - `get_password` will fail
|
||||||
|
* **Description:** Must return the user's password in plaintext or in the SCRAM serialised form.
|
||||||
|
|
||||||
|
### `user_exists`
|
||||||
|
|
||||||
|
* **Method:** GET
|
||||||
|
* **Type:** mandatory
|
||||||
|
* **Return values:**
|
||||||
|
* 200, `true` or `false` in body
|
||||||
|
* anything else - will be treated as `false`
|
||||||
|
* **Description:** Must return the information whether the user exists in DB.
|
||||||
|
|
||||||
|
### `set_password`
|
||||||
|
|
||||||
|
* **Method:** POST
|
||||||
|
* **Type:** mandatory when `mod_register` is enabled
|
||||||
|
* **Return values:**
|
||||||
|
* 200 or 201 or 204 - success
|
||||||
|
* anything else - will be treated as `false`
|
||||||
|
* **Description:** Must set user's password in the internal database
|
||||||
|
to a provided value. The value should not be transformed (except for
|
||||||
|
URL-decoding) before writing into DB.
|
||||||
|
|
||||||
|
### `remove_user`
|
||||||
|
|
||||||
|
* **Method:** POST
|
||||||
|
* **Type:** mandatory when `mod_register` is enabled
|
||||||
|
* **Return values:**
|
||||||
|
* 200 or 201 or 204 - success
|
||||||
|
* 404 - user does not exist
|
||||||
|
* 403 - not allowed for some reason
|
||||||
|
* 40X - will be treated as `bad request`
|
||||||
|
* **Description:** Removes a user account.
|
||||||
|
|
||||||
|
### `remove_user_validate`
|
||||||
|
|
||||||
|
* **Method:** POST
|
||||||
|
* **Type:** mandatory when `mod_register` is enabled
|
||||||
|
* **Return values:**
|
||||||
|
* 200 or 201 or 204 - success
|
||||||
|
* 404 - user does not exist
|
||||||
|
* 403 - invalid user password or not allowed for other reason
|
||||||
|
* 40X - will be treated as `bad request`
|
||||||
|
* **Description:** Removes a user account only if provided password is valid.
|
||||||
|
|
||||||
|
### `register`
|
||||||
|
|
||||||
|
* **Method:** POST
|
||||||
|
* **Type:** mandatory when `mod_register` is enabled
|
||||||
|
* **Return values:**
|
||||||
|
* 201 - success
|
||||||
|
* 409 - user already exists
|
||||||
|
* anything else - will be treated as failure
|
||||||
|
* **Description:** Creates a user account.
|
||||||
|
|
||||||
|
## Authentication service API recipes
|
||||||
|
|
||||||
|
Below are some examples of the auth service APIs and ejabberd-side
|
||||||
|
configuration along with use cases.
|
||||||
|
|
||||||
|
### System using common, custom auth token
|
||||||
|
|
||||||
|
An Auth token is provided as a password.
|
||||||
|
|
||||||
|
* **Service implements:** `check_password`, `user_exists`
|
||||||
|
* **ejabberd config:** `auth_password format`: `plain`, `mod_register` disabled
|
||||||
|
* **Client side:** MUST NOT use `DIGEST-MD5` mechanism; use `PLAIN`
|
||||||
|
|
||||||
|
### Central database of plaintext passwords
|
||||||
|
|
||||||
|
* **Service implements:** `check_password`, `get_password`, `user_exists`
|
||||||
|
* **ejabberd config:** `auth_password_format`: `plain`, `mod_register` disabled
|
||||||
|
* **Client side:** May use any available auth method
|
||||||
|
|
||||||
|
### Central database able to process SCRAM
|
||||||
|
|
||||||
|
* **Service implements:** `get_password`, `user_exists`
|
||||||
|
* **ejabberd config:** `auth_password_format`: `scram`, `mod_register` disabled
|
||||||
|
* **Client side:** May use any available auth method
|
||||||
|
|
||||||
|
### All-included
|
||||||
|
|
||||||
|
* **Service implements:** all methods
|
||||||
|
* **ejabberd config:** `auth_password_format`: `scram` (recommended) or `plain`, `mod_register` enabled
|
||||||
|
* **Client side:** May use any available auth method
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If the service supports multiple passwords per user you need to disable authentication caching with `auth_use_cache: false`. See [https://github.com/processone/ejabberd-contrib/issues/288](https://github.com/processone/ejabberd-contrib/issues/288).
|
|
@ -0,0 +1,5 @@
|
||||||
|
author: "Piotr Nosek <piotr.nosek at erlang-solutions.com>"
|
||||||
|
category: "auth"
|
||||||
|
summary: "Authentication via HTTP request"
|
||||||
|
home: "https://github.com/processone/ejabberd-contrib/tree/master/"
|
||||||
|
url: "git@github.com:processone/ejabberd-contrib.git"
|
|
@ -0,0 +1,4 @@
|
||||||
|
{deps, [
|
||||||
|
{cuesport, ".*", {git, "https://github.com/goj/cuesport"}},
|
||||||
|
{fusco, ".*", {git, "https://github.com/esl/fusco"}}
|
||||||
|
]}.
|
|
@ -0,0 +1,290 @@
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : ejabberd_auth_http.erl
|
||||||
|
%%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com>
|
||||||
|
%%% Purpose : Authentication via HTTP request
|
||||||
|
%%% Created : 23 Sep 2013 by Piotr Nosek <piotr.nosek@erlang-solutions.com>
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(ejabberd_auth_http).
|
||||||
|
-author('piotr.nosek@erlang-solutions.com').
|
||||||
|
|
||||||
|
-behaviour(ejabberd_auth).
|
||||||
|
|
||||||
|
%% External exports
|
||||||
|
-export([start/1,
|
||||||
|
set_password/3,
|
||||||
|
check_password/4,
|
||||||
|
check_password/6,
|
||||||
|
try_register/3,
|
||||||
|
get_password/2,
|
||||||
|
get_password_s/2,
|
||||||
|
user_exists/2,
|
||||||
|
remove_user/2,
|
||||||
|
remove_user/3,
|
||||||
|
plain_password_required/1,
|
||||||
|
store_type/1,
|
||||||
|
login/2,
|
||||||
|
get_password/3,
|
||||||
|
stop/1]).
|
||||||
|
|
||||||
|
-include_lib("xmpp/include/scram.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% API
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-spec start(binary()) -> ok.
|
||||||
|
start(Host) ->
|
||||||
|
AuthOpts = ejabberd_config:get_option({auth_opts, Host}),
|
||||||
|
{_, AuthHost} = lists:keyfind(host, 1, AuthOpts),
|
||||||
|
PoolSize = proplists:get_value(connection_pool_size, AuthOpts, 10),
|
||||||
|
Opts = proplists:get_value(connection_opts, AuthOpts, []),
|
||||||
|
ChildMods = [fusco],
|
||||||
|
ChildMFA = {fusco, start_link, [binary_to_list(AuthHost), Opts]},
|
||||||
|
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||||
|
ChildSpec = {Proc, {cuesport, start_link,
|
||||||
|
[pool_name(Host), PoolSize, ChildMods, ChildMFA]},
|
||||||
|
transient, 2000, supervisor, [cuesport | ChildMods]},
|
||||||
|
supervisor:start_child(ejabberd_backend_sup, ChildSpec),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
-spec plain_password_required(binary()) -> false.
|
||||||
|
plain_password_required(_Server) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
-spec store_type(binary()) -> external.
|
||||||
|
store_type(_) ->
|
||||||
|
external.
|
||||||
|
|
||||||
|
-spec check_password(binary(), binary(), binary(), binary()) -> {ets_cache:tag(), boolean()}.
|
||||||
|
check_password(LUser, _AuthzId, LServer, Password) ->
|
||||||
|
case scram2:enabled(LServer) of
|
||||||
|
false ->
|
||||||
|
case make_req(get, <<"check_password">>, LUser, LServer, Password) of
|
||||||
|
{ok, <<"true">>} -> {cache, true};
|
||||||
|
_ -> {nocache, false}
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
case verify_scram_password(LUser, LServer, Password) of
|
||||||
|
{ok, true} -> {cache, true};
|
||||||
|
_ -> {nocache, false}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec check_password(binary(), binary(), binary(), binary(), binary(), fun()) -> boolean().
|
||||||
|
check_password(LUser, _AuthzId, LServer, Password, Digest, DigestGen) ->
|
||||||
|
case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
|
||||||
|
{error, _} ->
|
||||||
|
false;
|
||||||
|
{ok, GotPasswd} ->
|
||||||
|
case scram2:enabled(LServer) of
|
||||||
|
true ->
|
||||||
|
case scram2:deserialize(GotPasswd) of
|
||||||
|
{ok, #scram{} = Scram} ->
|
||||||
|
scram2:check_digest(Scram, Digest, DigestGen, Password);
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
check_digest(Digest, DigestGen, Password, GotPasswd)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec check_digest(binary(), fun(), binary(), binary()) -> boolean().
|
||||||
|
check_digest(Digest, DigestGen, Password, Passwd) ->
|
||||||
|
DigRes = if
|
||||||
|
Digest /= <<>> ->
|
||||||
|
Digest == DigestGen(Passwd);
|
||||||
|
true ->
|
||||||
|
false
|
||||||
|
end,
|
||||||
|
if DigRes ->
|
||||||
|
true;
|
||||||
|
true ->
|
||||||
|
(Passwd == Password) and (Password /= <<>>)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec set_password(binary(), binary(), binary()) -> {ets_cache:tag(), {ok, binary()} | {error, not_allowed}}.
|
||||||
|
set_password(LUser, LServer, Password) ->
|
||||||
|
PasswordFinal = case scram2:enabled(LServer) of
|
||||||
|
true -> scram2:serialize(scram2:password_to_scram(
|
||||||
|
Password, scram2:iterations(LServer)));
|
||||||
|
false -> Password
|
||||||
|
end,
|
||||||
|
case make_req(post, <<"set_password">>, LUser, LServer, PasswordFinal) of
|
||||||
|
{error, _Error} -> {nocache, {error, not_allowed}};
|
||||||
|
{ok, _} -> {cache, {ok, Password}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec try_register(binary(), binary(), binary()) -> {ets_cache:tag(), {ok, binary()} | {error, exists | not_allowed}}.
|
||||||
|
try_register(LUser, LServer, Password) ->
|
||||||
|
PasswordFinal = case scram2:enabled(LServer) of
|
||||||
|
true -> scram2:serialize(scram2:password_to_scram(
|
||||||
|
Password, scram2:iterations(LServer)));
|
||||||
|
false -> Password
|
||||||
|
end,
|
||||||
|
case make_req(post, <<"register">>, LUser, LServer, PasswordFinal) of
|
||||||
|
{ok, <<"created">>} -> {cache, {ok, Password}};
|
||||||
|
{error, conflict} -> {nocache, {error, exists}};
|
||||||
|
_Error -> {nocache, {error, not_allowed}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec get_password(binary(), binary()) -> {cache, error}.
|
||||||
|
get_password(_, _) ->
|
||||||
|
{cache, error}.
|
||||||
|
|
||||||
|
-spec get_password_s(binary(), binary()) -> {cache, error}.
|
||||||
|
get_password_s(_User, _Server) ->
|
||||||
|
{cache, error}.
|
||||||
|
|
||||||
|
-spec user_exists(binary(), binary()) -> {ets_cache:tag(), boolean()}.
|
||||||
|
user_exists(LUser, LServer) ->
|
||||||
|
case make_req(get, <<"user_exists">>, LUser, LServer, <<"">>) of
|
||||||
|
{ok, <<"true">>} -> {cache, true};
|
||||||
|
_ -> {nocache, false}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec remove_user(binary(), binary()) -> ok | {error, db_failure | not_allowed}.
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
remove_user_req(LUser, LServer, <<"">>, <<"remove_user">>).
|
||||||
|
|
||||||
|
-spec remove_user(binary(), binary(), binary()) -> ok | {error, db_failure | not_allowed}.
|
||||||
|
remove_user(LUser, LServer, Password) ->
|
||||||
|
case scram2:enabled(LServer) of
|
||||||
|
false ->
|
||||||
|
remove_user_req(LUser, LServer, Password, <<"remove_user_validate">>);
|
||||||
|
true ->
|
||||||
|
case verify_scram_password(LUser, LServer, Password) of
|
||||||
|
{ok, false} ->
|
||||||
|
{error, not_allowed};
|
||||||
|
{ok, true} ->
|
||||||
|
remove_user_req(LUser, LServer, <<"">>, <<"remove_user">>);
|
||||||
|
{error, _Error} ->
|
||||||
|
{error, db_failure}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec remove_user_req(binary(), binary(), binary(), binary()) ->
|
||||||
|
ok | {error, not_allowed | db_failure}.
|
||||||
|
remove_user_req(LUser, LServer, Password, Method) ->
|
||||||
|
case make_req(post, Method, LUser, LServer, Password) of
|
||||||
|
{error, not_allowed} -> {error, not_allowed};
|
||||||
|
{error, not_found} -> {error, db_failure};
|
||||||
|
{error, _} -> {error, db_failure};
|
||||||
|
_ -> ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% Request maker
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-ifdef(OTP_BELOW_25).
|
||||||
|
-dialyzer({no_missing_calls, [uri_quote/1]}).
|
||||||
|
uri_quote(URL) ->
|
||||||
|
http_uri:encode(URL).
|
||||||
|
-else.
|
||||||
|
uri_quote(URL) ->
|
||||||
|
uri_string:quote(URL). % Available since OTP 25.0
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-spec make_req(post | get, binary(), binary(), binary(), binary()) ->
|
||||||
|
{ok, Body :: binary()} | {error, term()}.
|
||||||
|
make_req(_, _, LUser, LServer, _) when LUser == error orelse LServer == error ->
|
||||||
|
{error, {prep_failed, LUser, LServer}};
|
||||||
|
make_req(Method, Path, LUser, LServer, Password) ->
|
||||||
|
AuthOpts = ejabberd_config:get_option({auth_opts, LServer}),
|
||||||
|
BasicAuth = case lists:keyfind(basic_auth, 1, AuthOpts) of
|
||||||
|
{_, BasicAuth0} -> BasicAuth0;
|
||||||
|
_ -> ""
|
||||||
|
end,
|
||||||
|
PathPrefix = case lists:keyfind(path_prefix, 1, AuthOpts) of
|
||||||
|
{_, Prefix} -> Prefix;
|
||||||
|
false -> <<"/">>
|
||||||
|
end,
|
||||||
|
BasicAuth64 = base64:encode(BasicAuth),
|
||||||
|
LUserE = list_to_binary(uri_quote(binary_to_list(LUser))),
|
||||||
|
LServerE = list_to_binary(uri_quote(binary_to_list(LServer))),
|
||||||
|
PasswordE = list_to_binary(uri_quote(binary_to_list(Password))),
|
||||||
|
Query = <<"user=", LUserE/binary, "&server=", LServerE/binary, "&pass=", PasswordE/binary>>,
|
||||||
|
Header = [{<<"Authorization">>, <<"Basic ", BasicAuth64/binary>>}],
|
||||||
|
ContentType = {<<"Content-Type">>, <<"application/x-www-form-urlencoded">>},
|
||||||
|
Connection = cuesport:get_worker(existing_pool_name(LServer)),
|
||||||
|
|
||||||
|
?DEBUG("Making request '~s' for user ~s@~s...", [Path, LUser, LServer]),
|
||||||
|
{Url, MethodStr, Headers, Query2} =
|
||||||
|
case Method of
|
||||||
|
get -> {<<PathPrefix/binary, Path/binary, "?", Query/binary>>,
|
||||||
|
"GET",
|
||||||
|
Header,
|
||||||
|
""};
|
||||||
|
post -> {<<PathPrefix/binary, Path/binary>>,
|
||||||
|
"POST",
|
||||||
|
[ContentType|Header],
|
||||||
|
Query}
|
||||||
|
end,
|
||||||
|
http_request(Connection, Url, MethodStr, Headers, Query2, 0).
|
||||||
|
|
||||||
|
http_request(Connection, Url, MethodStr, Headers, Query, RedirectCounter) ->
|
||||||
|
{ok, {{Code, _Reason}, RespHeaders, RespBody, _, _}} =
|
||||||
|
fusco:request(Connection, Url, MethodStr, Headers, Query, 2, 5000),
|
||||||
|
?DEBUG("Request result: ~s: ~p", [Code, RespBody]),
|
||||||
|
case Code of
|
||||||
|
<<"409">> -> {error, conflict};
|
||||||
|
<<"404">> -> {error, not_found};
|
||||||
|
<<"401">> -> {error, not_authorized};
|
||||||
|
<<"403">> -> {error, not_allowed};
|
||||||
|
<<"400">> -> {error, RespBody};
|
||||||
|
<<"503">> -> {error, RespBody};
|
||||||
|
<<"204">> -> {ok, <<"">>};
|
||||||
|
<<"201">> -> {ok, <<"created">>};
|
||||||
|
<<"200">> -> {ok, RespBody};
|
||||||
|
R when (R==<<"301">>) or (R==<<"307">>) or (R==<<"308">>) ->
|
||||||
|
handle_redirect(RespHeaders, Connection, MethodStr, Headers, Query, RedirectCounter+1);
|
||||||
|
_ -> {error, RespBody}
|
||||||
|
end.
|
||||||
|
|
||||||
|
handle_redirect(RespHeaders, Connection, MethodStr, Headers, Query, RedirectCounter)
|
||||||
|
when RedirectCounter < 5 ->
|
||||||
|
{_, Location} = lists:keyfind(<<"location">>, 1, RespHeaders),
|
||||||
|
http_request(Connection, Location, MethodStr, Headers, Query, RedirectCounter);
|
||||||
|
handle_redirect(_, _, _, _, _, _) ->
|
||||||
|
{error, redirect_loop}.
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% Other internal functions
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
-spec pool_name(binary()) -> atom().
|
||||||
|
pool_name(Host) ->
|
||||||
|
list_to_atom("ejabberd_auth_http_" ++ binary_to_list(Host)).
|
||||||
|
|
||||||
|
-spec existing_pool_name(binary()) -> atom().
|
||||||
|
existing_pool_name(Host) ->
|
||||||
|
list_to_existing_atom("ejabberd_auth_http_" ++ binary_to_list(Host)).
|
||||||
|
|
||||||
|
-spec verify_scram_password(binary(), binary(), binary()) ->
|
||||||
|
{ok, boolean()} | {error, bad_request | not_exists}.
|
||||||
|
verify_scram_password(LUser, LServer, Password) ->
|
||||||
|
case make_req(get, <<"get_password">>, LUser, LServer, <<"">>) of
|
||||||
|
{ok, RawPassword} ->
|
||||||
|
case scram2:deserialize(RawPassword) of
|
||||||
|
{ok, #scram{} = ScramRecord} ->
|
||||||
|
{ok, scram2:check_password(Password, ScramRecord)};
|
||||||
|
_ ->
|
||||||
|
{error, bad_request}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{error, not_exists}
|
||||||
|
end.
|
||||||
|
|
||||||
|
login(_User, _Server) ->
|
||||||
|
erlang:error(not_implemented).
|
||||||
|
|
||||||
|
get_password(_User, _Server, _DefaultValue) ->
|
||||||
|
erlang:error(not_implemented).
|
||||||
|
|
||||||
|
stop(Host) ->
|
||||||
|
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||||
|
supervisor:terminate_child(ejabberd_backend_sup, Proc),
|
||||||
|
supervisor:delete_child(ejabberd_backend_sup, Proc).
|
|
@ -0,0 +1,192 @@
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : scram.erl
|
||||||
|
%%% Author : Stephen Röttger <stephen.roettger@googlemail.com>
|
||||||
|
%%% Purpose : SCRAM (RFC 5802)
|
||||||
|
%%% Created : 7 Aug 2011 by Stephen Röttger <stephen.roettger@googlemail.com>
|
||||||
|
%%%
|
||||||
|
%%%
|
||||||
|
%%% ejabberd, Copyright (C) 2002-2020 ProcessOne
|
||||||
|
%%%
|
||||||
|
%%% This program is free software; you can redistribute it and/or
|
||||||
|
%%% modify it under the terms of the GNU General Public License as
|
||||||
|
%%% published by the Free Software Foundation; either version 2 of the
|
||||||
|
%%% License, or (at your option) any later version.
|
||||||
|
%%%
|
||||||
|
%%% This program is distributed in the hope that it will be useful,
|
||||||
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
%%% General Public License for more details.
|
||||||
|
%%%
|
||||||
|
%%% You should have received a copy of the GNU General Public License
|
||||||
|
%%% along with this program; if not, write to the Free Software
|
||||||
|
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||||
|
%%% 02111-1307 USA
|
||||||
|
%%%
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(scram2).
|
||||||
|
|
||||||
|
-author('stephen.roettger@googlemail.com').
|
||||||
|
|
||||||
|
-include_lib("xmpp/include/scram.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%% External exports
|
||||||
|
%% ejabberd doesn't implement SASLPREP, so we use the similar RESOURCEPREP instead
|
||||||
|
-export([ % Core SCRAM functions
|
||||||
|
salted_password/3,
|
||||||
|
stored_key/1,
|
||||||
|
server_key/1,
|
||||||
|
server_signature/2,
|
||||||
|
client_signature/2,
|
||||||
|
client_key/1,
|
||||||
|
client_key/2]).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
enabled/1,
|
||||||
|
iterations/0,
|
||||||
|
iterations/1,
|
||||||
|
password_to_scram/1,
|
||||||
|
password_to_scram/2,
|
||||||
|
check_password/2,
|
||||||
|
check_digest/4
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export([serialize/1, deserialize/1]).
|
||||||
|
|
||||||
|
-export([scram_to_tuple/1]).
|
||||||
|
|
||||||
|
-define(SALT_LENGTH, 16).
|
||||||
|
-define(SCRAM_SERIAL_PREFIX, "==SCRAM==,").
|
||||||
|
|
||||||
|
-spec salted_password(binary(), binary(), non_neg_integer()) -> binary().
|
||||||
|
salted_password(Password, Salt, IterationCount) ->
|
||||||
|
hi(jid:resourceprep(Password), Salt, IterationCount).
|
||||||
|
|
||||||
|
-spec client_key(binary()) -> binary().
|
||||||
|
client_key(SaltedPassword) ->
|
||||||
|
crypto_hmac(sha, SaltedPassword, <<"Client Key">>).
|
||||||
|
|
||||||
|
-spec stored_key(binary()) -> binary().
|
||||||
|
stored_key(ClientKey) -> crypto:hash(sha, ClientKey).
|
||||||
|
|
||||||
|
-spec server_key(binary()) -> binary().
|
||||||
|
server_key(SaltedPassword) ->
|
||||||
|
crypto_hmac(sha, SaltedPassword, <<"Server Key">>).
|
||||||
|
|
||||||
|
-spec client_signature(binary(), binary()) -> binary().
|
||||||
|
client_signature(StoredKey, AuthMessage) ->
|
||||||
|
crypto_hmac(sha, StoredKey, AuthMessage).
|
||||||
|
|
||||||
|
-spec client_key(binary(), binary()) -> binary().
|
||||||
|
client_key(ClientProof, ClientSignature) ->
|
||||||
|
list_to_binary(lists:zipwith(fun (X, Y) -> X bxor Y end,
|
||||||
|
binary_to_list(ClientProof),
|
||||||
|
binary_to_list(ClientSignature))).
|
||||||
|
|
||||||
|
-spec server_signature(binary(), binary()) -> binary().
|
||||||
|
server_signature(ServerKey, AuthMessage) ->
|
||||||
|
crypto_hmac(sha, ServerKey, AuthMessage).
|
||||||
|
|
||||||
|
hi(Password, Salt, IterationCount) ->
|
||||||
|
U1 = crypto_hmac(sha, Password, <<Salt/binary, 0, 0, 0, 1>>),
|
||||||
|
list_to_binary(lists:zipwith(fun (X, Y) -> X bxor Y end,
|
||||||
|
binary_to_list(U1),
|
||||||
|
binary_to_list(hi_round(Password, U1,
|
||||||
|
IterationCount - 1)))).
|
||||||
|
|
||||||
|
hi_round(Password, UPrev, 1) ->
|
||||||
|
crypto_hmac(sha, Password, UPrev);
|
||||||
|
hi_round(Password, UPrev, IterationCount) ->
|
||||||
|
U = crypto_hmac(sha, Password, UPrev),
|
||||||
|
list_to_binary(lists:zipwith(fun (X, Y) -> X bxor Y end,
|
||||||
|
binary_to_list(U),
|
||||||
|
binary_to_list(hi_round(Password, U,
|
||||||
|
IterationCount - 1)))).
|
||||||
|
|
||||||
|
|
||||||
|
enabled(Host) ->
|
||||||
|
case ejabberd_config:get_option({auth_opts, Host}) of
|
||||||
|
undefined ->
|
||||||
|
false;
|
||||||
|
AuthOpts ->
|
||||||
|
{password_format, scram} == lists:keyfind(password_format, 1, AuthOpts)
|
||||||
|
end.
|
||||||
|
|
||||||
|
iterations() -> ?SCRAM_DEFAULT_ITERATION_COUNT.
|
||||||
|
|
||||||
|
iterations(Host) ->
|
||||||
|
case ejabberd_config:get_option({auth_opts, Host}) of
|
||||||
|
undefined ->
|
||||||
|
iterations();
|
||||||
|
AuthOpts ->
|
||||||
|
case lists:keyfind(scram_iterations, 1, AuthOpts) of
|
||||||
|
false -> iterations();
|
||||||
|
{_, Iterations} -> Iterations
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
password_to_scram(Password) ->
|
||||||
|
password_to_scram(Password, ?SCRAM_DEFAULT_ITERATION_COUNT).
|
||||||
|
|
||||||
|
password_to_scram(#scram{} = Password, _) ->
|
||||||
|
Password;
|
||||||
|
password_to_scram(Password, IterationCount) ->
|
||||||
|
Salt = p1_rand:bytes(?SALT_LENGTH),
|
||||||
|
SaltedPassword = salted_password(Password, Salt, IterationCount),
|
||||||
|
StoredKey = stored_key(scram2:client_key(SaltedPassword)),
|
||||||
|
ServerKey = server_key(SaltedPassword),
|
||||||
|
#scram{storedkey = base64:encode(StoredKey),
|
||||||
|
serverkey = base64:encode(ServerKey),
|
||||||
|
salt = base64:encode(Salt),
|
||||||
|
iterationcount = IterationCount}.
|
||||||
|
|
||||||
|
check_password(Password, Scram) ->
|
||||||
|
IterationCount = Scram#scram.iterationcount,
|
||||||
|
Salt = base64:decode(Scram#scram.salt),
|
||||||
|
SaltedPassword = salted_password(Password, Salt, IterationCount),
|
||||||
|
StoredKey = stored_key(client_key(SaltedPassword)),
|
||||||
|
(base64:decode(Scram#scram.storedkey) == StoredKey).
|
||||||
|
|
||||||
|
serialize(#scram{storedkey = StoredKey, serverkey = ServerKey,
|
||||||
|
salt = Salt, iterationcount = IterationCount})->
|
||||||
|
IterationCountBin = integer_to_binary(IterationCount),
|
||||||
|
<< <<?SCRAM_SERIAL_PREFIX>>/binary,
|
||||||
|
StoredKey/binary,$,,ServerKey/binary,
|
||||||
|
$,,Salt/binary,$,,IterationCountBin/binary>>.
|
||||||
|
|
||||||
|
deserialize(<<?SCRAM_SERIAL_PREFIX, Serialized/binary>>) ->
|
||||||
|
case catch binary:split(Serialized, <<",">>, [global]) of
|
||||||
|
[StoredKey, ServerKey,Salt,IterationCount] ->
|
||||||
|
{ok, #scram{storedkey = StoredKey,
|
||||||
|
serverkey = ServerKey,
|
||||||
|
salt = Salt,
|
||||||
|
iterationcount = binary_to_integer(IterationCount)}};
|
||||||
|
_ ->
|
||||||
|
?WARNING_MSG("Incorrect serialized SCRAM: ~p, ~p", [Serialized]),
|
||||||
|
{error, incorrect_scram}
|
||||||
|
end;
|
||||||
|
deserialize(Bin) ->
|
||||||
|
?WARNING_MSG("Corrupted serialized SCRAM: ~p, ~p", [Bin]),
|
||||||
|
{error, corrupted_scram}.
|
||||||
|
|
||||||
|
-spec scram_to_tuple(scram()) -> {binary(), binary(), binary(), non_neg_integer()}.
|
||||||
|
scram_to_tuple(Scram) ->
|
||||||
|
{base64:decode(Scram#scram.storedkey),
|
||||||
|
base64:decode(Scram#scram.serverkey),
|
||||||
|
base64:decode(Scram#scram.salt),
|
||||||
|
Scram#scram.iterationcount}.
|
||||||
|
|
||||||
|
-spec check_digest(scram(), binary(), fun(), binary()) -> boolean().
|
||||||
|
check_digest(#scram{storedkey = StoredKey}, Digest, DigestGen, Password) ->
|
||||||
|
Passwd = base64:decode(StoredKey),
|
||||||
|
DigRes = if Digest /= <<"">> ->
|
||||||
|
Digest == DigestGen(Passwd);
|
||||||
|
true -> false
|
||||||
|
end,
|
||||||
|
if DigRes -> true;
|
||||||
|
true -> (Passwd == Password) and (Password /= <<"">>)
|
||||||
|
end.
|
||||||
|
|
||||||
|
crypto_hmac(sha, Key, Data) ->
|
||||||
|
misc:crypto_hmac(sha, Key, Data).
|
|
@ -0,0 +1,342 @@
|
||||||
|
As a special exception, the authors give permission to link this program
|
||||||
|
with the OpenSSL library and distribute the resulting binary.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
|
@ -0,0 +1,41 @@
|
||||||
|
ejabberd observer_cli plugins
|
||||||
|
=============================
|
||||||
|
|
||||||
|
[observer_cli][oc] is an erlang tool to
|
||||||
|
visualize Erlang node statistics on the command line.
|
||||||
|
|
||||||
|
This directory contains several plugins
|
||||||
|
specifically written to view statistics of [ejabberd][ej].
|
||||||
|
|
||||||
|
To install this, just run:
|
||||||
|
```bash
|
||||||
|
ejabberdctl module_install ejabberd_observer_cli
|
||||||
|
```
|
||||||
|
It will automatically download, compile and install the required dependencies:
|
||||||
|
[observer_cli][oc], [recon][recon],
|
||||||
|
and the additional plugin [os_stats][os].
|
||||||
|
|
||||||
|
Then, start an erlang shell attached to your ejabberd node, for example:
|
||||||
|
```bash
|
||||||
|
ejabberdctl debug
|
||||||
|
```
|
||||||
|
in that erlang shell execute:
|
||||||
|
```erlang
|
||||||
|
ejabberd_observer_cli:start().
|
||||||
|
```
|
||||||
|
|
||||||
|
If using Elixir (for example when started with `ejabberdctl iexdebug`, run:
|
||||||
|
```elixir
|
||||||
|
:ejabberd_observer_cli.start()
|
||||||
|
```
|
||||||
|
|
||||||
|
To sort columns or change between the different pages,
|
||||||
|
type the corresponding letter and hit Enter.
|
||||||
|
For example, to view MUC statistics, type: `M and then Enter`.
|
||||||
|
|
||||||
|
There is no configuration required.
|
||||||
|
|
||||||
|
[oc]: https://github.com/zhongwencool/observer_cli
|
||||||
|
[os]: https://github.com/zhongwencool/os_stats
|
||||||
|
[recon]: https://github.com/ferd/recon
|
||||||
|
[ej]: https://www.ejabberd.im/
|
|
@ -0,0 +1,5 @@
|
||||||
|
author: "Badlop <badlop at process-one.net>"
|
||||||
|
category: "stats"
|
||||||
|
summary: "Observer CLI plugins for ejabberd"
|
||||||
|
home: "https://github.com/processone/ejabberd-contrib/tree/master/"
|
||||||
|
url: "git@github.com:processone/ejabberd-contrib.git"
|
|
@ -0,0 +1,5 @@
|
||||||
|
{deps, [
|
||||||
|
{observer_cli, ".*", {git, "https://github.com/zhongwencool/observer_cli"}},
|
||||||
|
{os_stats, ".*", {git, "https://github.com/zhongwencool/os_stats"}},
|
||||||
|
{recon, ".*", {git, "https://github.com/ferd/recon"}}
|
||||||
|
]}.
|
|
@ -0,0 +1,44 @@
|
||||||
|
-module(ejabberd_observer_cli).
|
||||||
|
|
||||||
|
-export([start/0, mod_status/0]).
|
||||||
|
|
||||||
|
start() ->
|
||||||
|
application:set_env(observer_cli, plugins, plugins(), [{persistent, true}]),
|
||||||
|
observer_cli:start_plugin().
|
||||||
|
|
||||||
|
mod_status() ->
|
||||||
|
"In an erlang shell run: ejabberd_observer_cli:start().".
|
||||||
|
|
||||||
|
plugins() ->
|
||||||
|
[
|
||||||
|
#{
|
||||||
|
module => ejabberd_observer_cli_vhosts,
|
||||||
|
title => "VHosts",
|
||||||
|
interval => 1600,
|
||||||
|
shortcut => "V",
|
||||||
|
sort_column => 2
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
module => ejabberd_observer_cli_users,
|
||||||
|
title => "Users",
|
||||||
|
interval => 1600,
|
||||||
|
shortcut => "U",
|
||||||
|
sort_column => 2
|
||||||
|
},
|
||||||
|
%% #{module => ejabberd_observer_cli_userstophost, title => "Users Top Vhost",
|
||||||
|
%% interval => 1600, shortcut => "T", sort_column => 2},
|
||||||
|
#{
|
||||||
|
module => ejabberd_observer_cli_muc,
|
||||||
|
title => "MUC",
|
||||||
|
interval => 1600,
|
||||||
|
shortcut => "M",
|
||||||
|
sort_column => 2
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
module => os_stats_plug,
|
||||||
|
title => "OS",
|
||||||
|
interval => 2000,
|
||||||
|
shortcut => "O",
|
||||||
|
sort_column => 2
|
||||||
|
}
|
||||||
|
].
|
|
@ -0,0 +1,48 @@
|
||||||
|
-module(ejabberd_observer_cli_muc).
|
||||||
|
|
||||||
|
%% observer_cli_plugin Callback API
|
||||||
|
-export([attributes/1, sheet_header/0, sheet_body/1]).
|
||||||
|
|
||||||
|
attributes(PrevState) ->
|
||||||
|
OnlineRoomsNumber = lists:foldl(
|
||||||
|
fun(Host, Acc) ->
|
||||||
|
Acc + mod_muc:count_online_rooms(Host)
|
||||||
|
end,
|
||||||
|
0,
|
||||||
|
mod_muc_admin:find_hosts(global)
|
||||||
|
),
|
||||||
|
|
||||||
|
Attrs = [
|
||||||
|
[
|
||||||
|
#{content => "MUC Rooms", width => 25},
|
||||||
|
#{content => OnlineRoomsNumber, width => 8}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
NewState = PrevState,
|
||||||
|
{Attrs, NewState}.
|
||||||
|
|
||||||
|
sheet_header() ->
|
||||||
|
[
|
||||||
|
#{title => "Room Name", width => 20, shortcut => "n"},
|
||||||
|
#{title => "MUC Service", width => 30, shortcut => "s"},
|
||||||
|
#{title => "Occupants", width => 12, shortcut => "o"},
|
||||||
|
#{title => "Subscribers", width => 13, shortcut => "r"}
|
||||||
|
].
|
||||||
|
|
||||||
|
sheet_body(PrevState) ->
|
||||||
|
Body = [
|
||||||
|
begin
|
||||||
|
{Name, Service, _} = jid:split(jid:decode(RoomStr)),
|
||||||
|
OccupantsNumber = mod_muc_admin:get_room_occupants_number(Name, Service),
|
||||||
|
SubsNumber = length(mod_muc_admin:get_subscribers(Name, Service)),
|
||||||
|
[
|
||||||
|
Name,
|
||||||
|
Service,
|
||||||
|
OccupantsNumber,
|
||||||
|
SubsNumber
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|| RoomStr <- mod_muc_admin:muc_online_rooms(global)
|
||||||
|
],
|
||||||
|
NewState = PrevState,
|
||||||
|
{Body, NewState}.
|
|
@ -0,0 +1,95 @@
|
||||||
|
-module(ejabberd_observer_cli_users).
|
||||||
|
|
||||||
|
%% observer_cli_plugin Callback API
|
||||||
|
-export([attributes/1, sheet_header/0, sheet_body/1]).
|
||||||
|
|
||||||
|
attributes(PrevState) ->
|
||||||
|
Hosts = length(ejabberd_option:hosts()),
|
||||||
|
RegisteredUsers =
|
||||||
|
lists:foldl(
|
||||||
|
fun(Host, Sum) ->
|
||||||
|
ejabberd_auth:count_users(Host) + Sum
|
||||||
|
end,
|
||||||
|
0,
|
||||||
|
ejabberd_option:hosts()
|
||||||
|
),
|
||||||
|
Sessions = length(ejabberd_sm:dirty_get_sessions_list()),
|
||||||
|
SessionsThisNode = length(ejabberd_sm:dirty_get_my_sessions_list()),
|
||||||
|
|
||||||
|
Attrs = [
|
||||||
|
[
|
||||||
|
#{content => "Virtual Hosts", width => 18},
|
||||||
|
#{content => Hosts, width => 8},
|
||||||
|
#{content => "Sessions Total", width => 18},
|
||||||
|
#{content => Sessions, width => 8}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
#{content => "Accounts Total", width => 18},
|
||||||
|
#{content => RegisteredUsers, width => 8},
|
||||||
|
#{content => "Sessions This Node", width => 18},
|
||||||
|
#{content => SessionsThisNode, width => 8}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
NewState = PrevState,
|
||||||
|
{Attrs, NewState}.
|
||||||
|
|
||||||
|
sheet_header() ->
|
||||||
|
[
|
||||||
|
#{title => "Username", width => 25, shortcut => "u"},
|
||||||
|
#{title => "Host", width => 25, shortcut => "h"},
|
||||||
|
#{title => "Sessions", width => 11, shortcut => "s"},
|
||||||
|
#{title => "Roster", width => 9, shortcut => "r"},
|
||||||
|
#{title => "Offline", width => 10, shortcut => "o"},
|
||||||
|
#{title => "Last Activity", width => 20, shortcut => "l"}
|
||||||
|
].
|
||||||
|
|
||||||
|
sheet_body(PrevState) ->
|
||||||
|
Body = [
|
||||||
|
begin
|
||||||
|
[
|
||||||
|
Username,
|
||||||
|
Host,
|
||||||
|
length(ejabberd_sm:get_user_resources(Username, Host)),
|
||||||
|
length(mod_roster:get_roster(Username, Host)),
|
||||||
|
mod_offline:count_offline_messages(Username, Host),
|
||||||
|
get_last_activity(Username, Host)
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|| {Username, Host} <- lists:sort(ejabberd_auth:get_users())
|
||||||
|
],
|
||||||
|
NewState = PrevState,
|
||||||
|
{Body, NewState}.
|
||||||
|
|
||||||
|
%% Code copied from ejabberd_web_admin.erl
|
||||||
|
get_last_activity(User, Server) ->
|
||||||
|
case ejabberd_sm:get_user_resources(User, Server) of
|
||||||
|
[] ->
|
||||||
|
case get_last_info(User, Server) of
|
||||||
|
not_found ->
|
||||||
|
"Never";
|
||||||
|
{ok, Shift, _Status} ->
|
||||||
|
TimeStamp = {Shift div 1000000, Shift rem 1000000, 0},
|
||||||
|
{{Year, Month, Day}, {Hour, Minute, Second}} =
|
||||||
|
calendar:now_to_local_time(TimeStamp),
|
||||||
|
(io_lib:format(
|
||||||
|
"~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
|
||||||
|
[
|
||||||
|
Year,
|
||||||
|
Month,
|
||||||
|
Day,
|
||||||
|
Hour,
|
||||||
|
Minute,
|
||||||
|
Second
|
||||||
|
]
|
||||||
|
))
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
"Online"
|
||||||
|
end.
|
||||||
|
get_last_info(User, Server) ->
|
||||||
|
case gen_mod:is_loaded(Server, mod_last) of
|
||||||
|
true ->
|
||||||
|
mod_last:get_last_info(User, Server);
|
||||||
|
false ->
|
||||||
|
not_found
|
||||||
|
end.
|
|
@ -0,0 +1,61 @@
|
||||||
|
-module(ejabberd_observer_cli_userstophost).
|
||||||
|
|
||||||
|
%% observer_cli_plugin Callback API
|
||||||
|
-export([attributes/1, sheet_header/0, sheet_body/1]).
|
||||||
|
|
||||||
|
get_top_host() ->
|
||||||
|
lists:foldl(
|
||||||
|
fun(Host, {HostRelativeMax, CountRelativeMax}) ->
|
||||||
|
case ejabberd_auth:count_users(Host) of
|
||||||
|
Count when Count > CountRelativeMax ->
|
||||||
|
{Host, Count};
|
||||||
|
_ ->
|
||||||
|
{HostRelativeMax, CountRelativeMax}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
{unknown, -1},
|
||||||
|
ejabberd_option:hosts()
|
||||||
|
).
|
||||||
|
|
||||||
|
attributes(PrevState) ->
|
||||||
|
{Host, _} = get_top_host(),
|
||||||
|
RegisteredUsers = ejabberd_auth:count_users(Host),
|
||||||
|
Sessions = length(ejabberd_sm:dirty_get_sessions_list()),
|
||||||
|
SessionsThisNode = length(ejabberd_sm:dirty_get_my_sessions_list()),
|
||||||
|
|
||||||
|
Attrs = [
|
||||||
|
[
|
||||||
|
#{content => "Virtual Host", width => 12},
|
||||||
|
#{content => Host, width => 14},
|
||||||
|
#{content => "Sessions Total", width => 18},
|
||||||
|
#{content => Sessions, width => 8}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
#{content => "Accounts Total", width => 12},
|
||||||
|
#{content => RegisteredUsers, width => 14},
|
||||||
|
#{content => "Sessions This Node", width => 18},
|
||||||
|
#{content => SessionsThisNode, width => 8}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
NewState = PrevState,
|
||||||
|
{Attrs, NewState}.
|
||||||
|
|
||||||
|
sheet_header() ->
|
||||||
|
[
|
||||||
|
#{title => "Username", width => 25, shortcut => "u"},
|
||||||
|
#{title => "Resources", width => 15, shortcut => "r"}
|
||||||
|
].
|
||||||
|
|
||||||
|
sheet_body(PrevState) ->
|
||||||
|
{Host, _} = get_top_host(),
|
||||||
|
Body = [
|
||||||
|
begin
|
||||||
|
[
|
||||||
|
Username,
|
||||||
|
length(ejabberd_sm:get_user_resources(Username, Host))
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|| {Username, _} <- lists:reverse(lists:sort(ejabberd_auth:get_users(Host)))
|
||||||
|
],
|
||||||
|
NewState = PrevState,
|
||||||
|
{Body, NewState}.
|
|
@ -0,0 +1,78 @@
|
||||||
|
-module(ejabberd_observer_cli_vhosts).
|
||||||
|
|
||||||
|
%% observer_cli_plugin Callback API
|
||||||
|
-export([attributes/1, sheet_header/0, sheet_body/1]).
|
||||||
|
|
||||||
|
attributes(PrevState) ->
|
||||||
|
Hosts = length(ejabberd_option:hosts()),
|
||||||
|
RegisteredUsers =
|
||||||
|
lists:foldl(
|
||||||
|
fun(Host, Sum) ->
|
||||||
|
ejabberd_auth:count_users(Host) + Sum
|
||||||
|
end,
|
||||||
|
0,
|
||||||
|
ejabberd_option:hosts()
|
||||||
|
),
|
||||||
|
Sessions = length(ejabberd_sm:dirty_get_sessions_list()),
|
||||||
|
SessionsThisNode = length(ejabberd_sm:dirty_get_my_sessions_list()),
|
||||||
|
|
||||||
|
OnlineRoomsNumber = lists:foldl(
|
||||||
|
fun(Host, Acc) ->
|
||||||
|
Acc + mod_muc:count_online_rooms(Host)
|
||||||
|
end,
|
||||||
|
0,
|
||||||
|
mod_muc_admin:find_hosts(global)
|
||||||
|
),
|
||||||
|
|
||||||
|
Attrs = [
|
||||||
|
[
|
||||||
|
#{content => "Virtual Hosts", width => 25},
|
||||||
|
#{content => Hosts, width => 8},
|
||||||
|
#{content => "Sessions Total", width => 25},
|
||||||
|
#{content => Sessions, width => 8}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
#{content => "Accounts Total", width => 25},
|
||||||
|
#{content => RegisteredUsers, width => 8},
|
||||||
|
#{content => "Sessions This Node", width => 25},
|
||||||
|
#{content => SessionsThisNode, width => 8}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
#{content => "MUC Rooms", width => 25},
|
||||||
|
#{content => OnlineRoomsNumber, width => 8}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
NewState = PrevState,
|
||||||
|
{Attrs, NewState}.
|
||||||
|
|
||||||
|
sheet_header() ->
|
||||||
|
[
|
||||||
|
#{title => "Virtual Host", width => 38, shortcut => "v"},
|
||||||
|
#{title => "Accounts", width => 11, shortcut => "a"},
|
||||||
|
#{title => "Sessions", width => 11, shortcut => "s"},
|
||||||
|
#{title => "Rooms", width => 8, shortcut => "r"}
|
||||||
|
].
|
||||||
|
|
||||||
|
sheet_body(PrevState) ->
|
||||||
|
Body = [
|
||||||
|
begin
|
||||||
|
RegisteredUsers = ejabberd_auth:count_users(Host),
|
||||||
|
Sessions = ejabberd_sm:get_vh_session_number(Host),
|
||||||
|
OnlineRoomsNumber = lists:foldl(
|
||||||
|
fun(Host1, Acc) ->
|
||||||
|
Acc + mod_muc:count_online_rooms(Host1)
|
||||||
|
end,
|
||||||
|
0,
|
||||||
|
mod_muc_admin:find_hosts(Host)
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Host,
|
||||||
|
RegisteredUsers,
|
||||||
|
Sessions,
|
||||||
|
OnlineRoomsNumber
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|| Host <- lists:reverse(lists:sort(ejabberd_option:hosts()))
|
||||||
|
],
|
||||||
|
NewState = PrevState,
|
||||||
|
{Body, NewState}.
|
|
@ -1,130 +0,0 @@
|
||||||
2009-09-11 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/ejabberd_xmlrpc.erl: Update to work with ejabberd 2.1.0
|
|
||||||
|
|
||||||
2009-04-28 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/ejabberd_xmlrpc.erl: Improve handling of errors in arguments
|
|
||||||
|
|
||||||
2009-04-17 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/ejabberd_xmlrpc.erl: Change access_commands to use the new
|
|
||||||
AccessCommands feature of ejabberd. Syntax is similar (EJAB-910)
|
|
||||||
|
|
||||||
2009-03-02 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/ejabberd_xmlrpc.erl: Replace the listener option 'access'
|
|
||||||
with the much more powerful 'access_commands', that allows to
|
|
||||||
grant selective permission to execute commands with certain
|
|
||||||
arguments
|
|
||||||
* README.txt: Likewise
|
|
||||||
|
|
||||||
2009-02-24 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/ejabberd_xmlrpc.erl: Update to work with recent ejabberd
|
|
||||||
trunk SVN (thanks to Artem Yurov)
|
|
||||||
* README.txt: Likewise
|
|
||||||
|
|
||||||
2008-10-21 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* README.txt: Updated PHP example
|
|
||||||
|
|
||||||
2008-10-12 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/ejabberd_xmlrpc.erl: Major rewrite of mod_xmlrpc to be an
|
|
||||||
ejabberd independent listener, and to be a frontend of ejabberd
|
|
||||||
commands (EJAB-694)
|
|
||||||
|
|
||||||
2008-08-31 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* README.txt: Added Java example client and fixed the PHP
|
|
||||||
example (thanks to Calder)
|
|
||||||
|
|
||||||
2008-07-24 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/mod_xmlrpc.erl: Fixed typo in get_roster
|
|
||||||
|
|
||||||
2008-07-08 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/mod_xmlrpc.erl: Detect if mod_roster or mod_roster_odbc is
|
|
||||||
enabled. New call send_message (thanks to Darren Ferguson)
|
|
||||||
* README.txt: Likewise
|
|
||||||
|
|
||||||
2008-05-20 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/mod_xmlrpc.erl: New call get_roster; works only with
|
|
||||||
mod_roster_odbc
|
|
||||||
* README.txt: Likewise
|
|
||||||
|
|
||||||
2008-05-19 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/mod_xmlrpc.erl: New function check_account (thanks to
|
|
||||||
Zbyszek Żółkiewski)
|
|
||||||
* README.txt: Likewise
|
|
||||||
|
|
||||||
2008-05-18 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* README.txt: Added example client in PHP (thanks to Zbyszek
|
|
||||||
Żółkiewski)
|
|
||||||
|
|
||||||
* src/mod_xmlrpc.erl: Convert from DOS to Unix line format
|
|
||||||
* README.txt: Likewise
|
|
||||||
* ChangeLog: Likewise
|
|
||||||
|
|
||||||
2008-05-16 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/mod_xmlrpc.erl: New calls muc_room_change_option and
|
|
||||||
muc_room_set_affiliation. Improved calls add_rosteritem and
|
|
||||||
delete_rosteritem: they push the changed roster item to connected
|
|
||||||
resources (thanks to Darren Ferguson). New call check_password.
|
|
||||||
* README.txt: Likewise
|
|
||||||
|
|
||||||
2008-05-06 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/mod_xmlrpc.erl: Added new calls delete_account,
|
|
||||||
delete_rosteritem, create_muc_room and destroy_muc_room (thanks to
|
|
||||||
Darren Ferguson)
|
|
||||||
* README.txt: Likewise
|
|
||||||
|
|
||||||
2008-02-29 Badlop <badlop@process-one.net>
|
|
||||||
|
|
||||||
* src/mod_xmlrpc.erl: Added module options: maxsessions, timeout
|
|
||||||
* README.txt: Likewise
|
|
||||||
|
|
||||||
2007-08-24 Badlop <badlop@ono.com>
|
|
||||||
|
|
||||||
* README.txt: Removed requirement of Xmerl 0.20, since it is
|
|
||||||
included in Erlang/OTP. Instead, require updated XMLRPC-Erlang.
|
|
||||||
|
|
||||||
2007-08-21 Badlop <badlop@ono.com>
|
|
||||||
|
|
||||||
* README.txt: New Ruby example (thanks to Diddek). Patched xmlrpc
|
|
||||||
Erlang library for Ruby compatibility (thanks to Cstar).
|
|
||||||
|
|
||||||
* mod_xmlrpc/*: Initial commit.
|
|
||||||
|
|
||||||
Old Changelog:
|
|
||||||
|
|
||||||
0.2.4 - 7 November 2006
|
|
||||||
* Fixed a bug that required clients to provide attributes in a fixed order
|
|
||||||
|
|
||||||
0.2.3 - 22 September 2006
|
|
||||||
* New feature: bind to a specific IP address, requires the patched XML-RPC-Erlang-1.13-IP (thanks to Zeank)
|
|
||||||
|
|
||||||
0.2.2 - 15 August 2006
|
|
||||||
* Fixed small bug on resource_num (thanks to Flipkick)
|
|
||||||
|
|
||||||
0.2.1 - 20 July 2006
|
|
||||||
* Fixed small bug on add_rosteritem (thanks to Leonexis)
|
|
||||||
|
|
||||||
0.2.0 - 16 April 2006
|
|
||||||
* Added two new calls: num_resources and resouce_num
|
|
||||||
* Added support for vhosts
|
|
||||||
|
|
||||||
0.1.2 - 28 December 2005
|
|
||||||
* Now compatible with ejabberd 1.0.0
|
|
||||||
* The XMLRPC server is started only once, not once for every virtual host
|
|
||||||
* Added comments for handlers. Every available handler must be explained
|
|
||||||
|
|
||||||
0.1.0 - 4 April 2005
|
|
||||||
* Initial version
|
|
|
@ -1,2 +0,0 @@
|
||||||
{'../ejabberd-dev/src/gen_mod', [{outdir, "../ejabberd-dev/ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
||||||
{'src/ejabberd_xmlrpc', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
|
@ -1,499 +0,0 @@
|
||||||
|
|
||||||
ejabberd_xmlrpc - XML-RPC server
|
|
||||||
|
|
||||||
Homepage: http://www.ejabberd.im/ejabberd_xmlrpc
|
|
||||||
Author: Badlop
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
-----------
|
|
||||||
|
|
||||||
ejabberd_xmlrpc is an ejabberd listener that starts a XML-RPC server
|
|
||||||
and waits for external calls.
|
|
||||||
|
|
||||||
ejabberd_xmlrpc implements some example calls that can be used to test
|
|
||||||
during the development of a new XML-RPC client. But most
|
|
||||||
importantly, ejabberd_xmlrpc is also a frontend to execute ejabberd
|
|
||||||
commands. This way a XML-RPC client can execute any ejabberd command.
|
|
||||||
|
|
||||||
This allows external programs written in any language like websites or
|
|
||||||
administrative tools to communicate with ejabberd to get information
|
|
||||||
or to make changes without the need to know ejabberd internals. One
|
|
||||||
example usage is a corporate site in PHP that creates a Jabber user
|
|
||||||
every time a new user is created on the website.
|
|
||||||
|
|
||||||
Some benefits of interfacing with the Jabber server by XML-RPC instead
|
|
||||||
of modifying directly the database are:
|
|
||||||
- external programs are more simple and easy to develop and debug
|
|
||||||
- can communicate with a server in a different machine, and even on Internet
|
|
||||||
|
|
||||||
|
|
||||||
REQUIREMENTS
|
|
||||||
------------
|
|
||||||
|
|
||||||
ejabberd 2.1.0 or higher
|
|
||||||
XMLRPC-Erlang 1.13 with IP, Ruby and Xmerl 1.x patches
|
|
||||||
Optional: mod_admin_extra implements many ejabberd commands for general server administration
|
|
||||||
Optional: mod_muc_admin implements ejabberd commands for MUC administration
|
|
||||||
|
|
||||||
|
|
||||||
CONFIGURE EJABBERD
|
|
||||||
------------------
|
|
||||||
|
|
||||||
1. You need to get and install XMLRPC-Erlang.
|
|
||||||
You can download XMLRPC-Erlang binary files from
|
|
||||||
http://www.ejabberd.im/ejabberd_xmlrpc
|
|
||||||
or compile it yourself:
|
|
||||||
wget http://www.ejabberd.im/files/contributions/xmlrpc-1.13-ipr2.tgz
|
|
||||||
tar -xzvf xmlrpc-1.13-ipr2.tgz
|
|
||||||
cd xmlrpc-1.13/src
|
|
||||||
make
|
|
||||||
cd ../../
|
|
||||||
Then you can copy the *.beam files to ejabberd ebin directory,
|
|
||||||
or add an option like this to the ejabberd start script:
|
|
||||||
$ erl -pa '/home/jabber/xmlrpc-1.13/ebin' ...
|
|
||||||
|
|
||||||
2. Configure ejabberd to start this listener at startup:
|
|
||||||
edit ejabberd.cfg and add on the 'listen' section:
|
|
||||||
{listen, [
|
|
||||||
{4560, ejabberd_xmlrpc, []},
|
|
||||||
...
|
|
||||||
]}.
|
|
||||||
|
|
||||||
3. Start ejabberd.
|
|
||||||
|
|
||||||
4. Verify that ejabberd is listening in that port:
|
|
||||||
$ netstat -n -l | grep 4560
|
|
||||||
tcp 0 0 0.0.0.0:4560 0.0.0.0:* LISTEN
|
|
||||||
|
|
||||||
5. If there is any problem, check ejabberd.log and sasl.log files
|
|
||||||
|
|
||||||
|
|
||||||
CONFIGURE
|
|
||||||
---------
|
|
||||||
|
|
||||||
The listener allow several configurable options:
|
|
||||||
|
|
||||||
{maxsessions, Integer}
|
|
||||||
Number of concurrent connections allowed.
|
|
||||||
Default: 10
|
|
||||||
|
|
||||||
{timeout, Integer}
|
|
||||||
Timeout of the connections, expressed in milliseconds.
|
|
||||||
Default: 5000
|
|
||||||
|
|
||||||
{access_commands, AccessCommands}
|
|
||||||
This option allows to define a list of access restrictions.
|
|
||||||
If this option is present, then XML-RPC calls must include as
|
|
||||||
first argument a struct with a user, server and password of an
|
|
||||||
account in ejabberd that has privileges in Access.
|
|
||||||
If the option is not present, such struct must not be provided.
|
|
||||||
The default value is to not define any restriction: []
|
|
||||||
When one or several access restrictions are defined and the
|
|
||||||
XML-RPC call provides authentication for an account, each
|
|
||||||
restriction is verified until one matches completely:
|
|
||||||
the account matches the Access rule,
|
|
||||||
the command name is listed in CommandNames,
|
|
||||||
and the provided arguments do not contradict Arguments.
|
|
||||||
There is more information about AccessCommands in the ejabberd Guide.
|
|
||||||
|
|
||||||
|
|
||||||
Example configuration: XML-RPC calls can execute any command, with any
|
|
||||||
argument, and no authentication information must be provided:
|
|
||||||
{listen, [
|
|
||||||
{4560, ejabberd_xmlrpc, [{maxsessions, 10}, {timeout, 5000}]},
|
|
||||||
...
|
|
||||||
]}.
|
|
||||||
|
|
||||||
In this case authentication information must be provided, but it is
|
|
||||||
enough that the account exists and the password is valid to execute
|
|
||||||
any command:
|
|
||||||
{listen, [
|
|
||||||
{4560, ejabberd_xmlrpc, [{maxsessions, 10}, {timeout, 5000},
|
|
||||||
{access_commands, [{all, all, []}]}]},
|
|
||||||
...
|
|
||||||
]}.
|
|
||||||
|
|
||||||
In this example the local Jabber account xmlrpc-robot@jabber.example.org
|
|
||||||
can execute any command with no argument restriction:
|
|
||||||
{acl, xmlrpcbot, {user, "xmlrpc-robot", "jabber.example.org"}}.
|
|
||||||
{access, xmlrpcaccess, [{allow, xmlrpcbot}]}.
|
|
||||||
{listen, [
|
|
||||||
{4560, ejabberd_xmlrpc, [{maxsessions, 10}, {timeout, 5000},
|
|
||||||
{access_commands, [{xmlrpcaccess, all, []}]}]},
|
|
||||||
...
|
|
||||||
]}.
|
|
||||||
|
|
||||||
Finally, in this complex example the listener only listens in port
|
|
||||||
4560 of IP address 127.0.0.1, and several access restrictions are
|
|
||||||
defined (the corresponding ACL and ACCESS are not shown):
|
|
||||||
{listen, [
|
|
||||||
{{4560, "127.0.0.1"}, ejabberd_xmlrpc, [
|
|
||||||
{access_commands, [
|
|
||||||
%% This bot can execute any command:
|
|
||||||
{xmlrpc_bot, all, []},
|
|
||||||
%% This bot can execute any command,
|
|
||||||
%% but if a 'host' argument is provided, it must be "example.org":
|
|
||||||
{xmlrpc_bot_all_example, all, [{host, "example.org"}]},
|
|
||||||
%% This bot can only execute the command 'dump'. No argument restriction:
|
|
||||||
{xmlrpc_bot_backups, [dump], []}
|
|
||||||
%% This bot can only execute the command 'register',
|
|
||||||
%% and if argument 'host' is provided, it must be "example.org":
|
|
||||||
{xmlrpc_bot_reg_example, [register], [{host, "example.org"}]},
|
|
||||||
%% This bot can execute the commands 'register' and 'unregister',
|
|
||||||
%% if argument host is provided, it must be "test.org":
|
|
||||||
{xmlrpc_bot_reg_test, [register, unregister], [{host, "test.org"}]}
|
|
||||||
]}
|
|
||||||
]},
|
|
||||||
...
|
|
||||||
]}.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
-----
|
|
||||||
|
|
||||||
You can send calls to http://host:4560/
|
|
||||||
|
|
||||||
Call: Arguments: Returns:
|
|
||||||
|
|
||||||
-- debug
|
|
||||||
echothis String String
|
|
||||||
echothisnew struct[{sentence, String}] struct[{repeated, String}]
|
|
||||||
multhis struct[{a, Integer}, {b, Integer}] Integer
|
|
||||||
multhisnew struct[{a, Integer}, {b, Integer}] struct[{mu, Integer}]
|
|
||||||
|
|
||||||
-- statistics
|
|
||||||
tellme_title String String
|
|
||||||
tellme_value String String
|
|
||||||
tellme String struct[{title, String}, {value. String}]
|
|
||||||
|
|
||||||
|
|
||||||
With ejabberd_xmlrpc you can execute any ejabberd command with a XML-RPC call.
|
|
||||||
|
|
||||||
1. Get a list of available ejabberd commands, for example:
|
|
||||||
$ ejabberdctl help
|
|
||||||
Available commands in this ejabberd node:
|
|
||||||
connected_users List all established sessions
|
|
||||||
connected_users_number Get the number of established sessions
|
|
||||||
delete_expired_messages Delete expired offline messages from database
|
|
||||||
delete_old_messages days Delete offline messages older than DAYS
|
|
||||||
dump file Dump the database to text file
|
|
||||||
register user host password Register a user
|
|
||||||
registered_users host List all registered users in HOST
|
|
||||||
reopen_log Reopen the log files
|
|
||||||
restart Restart ejabberd
|
|
||||||
restore file Restore the database from backup file
|
|
||||||
status Get ejabberd status
|
|
||||||
stop Stop ejabberd
|
|
||||||
unregister user host Unregister a user
|
|
||||||
user_resources user host List user's connected resources
|
|
||||||
|
|
||||||
2. When you found the command you want to call, get some additional
|
|
||||||
help of the arguments and result:
|
|
||||||
$ ejabberdctl help user_resources
|
|
||||||
Command Name: user_resources
|
|
||||||
Arguments: user::string
|
|
||||||
host::string
|
|
||||||
Returns: resources::[ resource::string ]
|
|
||||||
Tags: session
|
|
||||||
Description: List user's connected resources
|
|
||||||
|
|
||||||
3. You can try to execute the command in the shell for the account testuser@localhost:
|
|
||||||
$ ejabberdctl user_resources testuser localhost
|
|
||||||
Home
|
|
||||||
Psi
|
|
||||||
|
|
||||||
4. Now implement the proper XML-RPC call in your XML-RPC client.
|
|
||||||
This example will use the Erlang library:
|
|
||||||
$ erl
|
|
||||||
1> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, user_resources, [{struct, [{user, "testuser"}, {host, "localhost"}]}]}).
|
|
||||||
{ok,{response,[{struct,[{resources,{array,[{struct,[{resource,"Home"}]},
|
|
||||||
{struct,[{resource,"Psi"}]}]}}]}]}}
|
|
||||||
|
|
||||||
5. Note: if ejabberd_xmlrpc has the option 'access_commands'
|
|
||||||
configured with some access restriction (see the example
|
|
||||||
configurations provided above), the XML-RPC must include first an
|
|
||||||
argument providing information of a valid account. For example:
|
|
||||||
1> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, user_resources, [
|
|
||||||
{struct, [{user, "adminuser"}, {server, "localhost"}, {password, "aeiou"}]},
|
|
||||||
{struct, [{user, "testuser"}, {host, "localhost"}]} ]}).
|
|
||||||
|
|
||||||
|
|
||||||
Arguments in XML-RPC calls can be provided in any order;
|
|
||||||
This module will sort the arguments before calling the ejabberd command.
|
|
||||||
|
|
||||||
If auth is provided in the call when ejabberd_xmlrpc does not require it,
|
|
||||||
the call will return the error: -112 Unknown call
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
EXAMPLE IN PHP
|
|
||||||
--------------
|
|
||||||
|
|
||||||
This is an XML-RPC client in PHP, thanks to Zbyszek Żółkiewski and Calder.
|
|
||||||
It requires "allow_url_fopen = On" in your php.ini.
|
|
||||||
|
|
||||||
-------
|
|
||||||
<?
|
|
||||||
$param=array("user"=>"testuser", "host"=>"localhost");
|
|
||||||
$request = xmlrpc_encode_request('user_resources', $param, (array('encoding' => 'utf-8')));
|
|
||||||
|
|
||||||
$context = stream_context_create(array('http' => array(
|
|
||||||
'method' => "POST",
|
|
||||||
'header' => "User-Agent: XMLRPC::Client mod_xmlrpc\r\n" .
|
|
||||||
"Content-Type: text/xml\r\n" .
|
|
||||||
"Content-Length: ".strlen($request),
|
|
||||||
'content' => $request
|
|
||||||
)));
|
|
||||||
|
|
||||||
$file = file_get_contents("http://127.0.0.1:4560/RPC2", false, $context);
|
|
||||||
|
|
||||||
$response = xmlrpc_decode($file);
|
|
||||||
|
|
||||||
if (xmlrpc_is_fault($response)) {
|
|
||||||
trigger_error("xmlrpc: $response[faultString] ($response[faultCode])");
|
|
||||||
} else {
|
|
||||||
print_r($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
-------
|
|
||||||
|
|
||||||
The response, following the example would be like this:
|
|
||||||
-------
|
|
||||||
$ php5 call.php
|
|
||||||
Array
|
|
||||||
(
|
|
||||||
[resources] => Array
|
|
||||||
(
|
|
||||||
[0] => Array
|
|
||||||
(
|
|
||||||
[resource] => Home
|
|
||||||
)
|
|
||||||
[1] => Array
|
|
||||||
(
|
|
||||||
[resource] => Psi
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
-------
|
|
||||||
|
|
||||||
If you configured the option access_commands, you have to provide authentication
|
|
||||||
information by replacing the first lime with something like this:
|
|
||||||
-------
|
|
||||||
$param_auth=array("user"=>"analloweduser", "server"=>"localhost", "password"=>"MyPasS997");
|
|
||||||
$param_comm=array("user"=>"testuser", "host"=>"localhost");
|
|
||||||
$param=array($param_auth, $param_comm);
|
|
||||||
-------
|
|
||||||
|
|
||||||
|
|
||||||
**** WARNING: all the remaining text was written for mod_xmlrpc and
|
|
||||||
is NOT valid for ejabberd_xmlrpc ****
|
|
||||||
|
|
||||||
|
|
||||||
TEST
|
|
||||||
----
|
|
||||||
|
|
||||||
- You can easily try the XML-RPC server starting a new Erlang Virtual Machine
|
|
||||||
and making calls to ejabberd's XML-RPC:
|
|
||||||
|
|
||||||
1. Start Erlang with this option:
|
|
||||||
$ erl -pa '/home/jabber/xmlrpc-1.13/ebin'
|
|
||||||
|
|
||||||
2. Now on the Erlang console, write commands and check the results:
|
|
||||||
|
|
||||||
1> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [800]}).
|
|
||||||
{ok,{response,[800]}}
|
|
||||||
|
|
||||||
2> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, ["blot cloc 557.889 kg"]}).
|
|
||||||
{ok,{response,["blot cloc 557.889 kg"]}}
|
|
||||||
|
|
||||||
3> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, multhis, [{struct,[{a, 83}, {b, 689}]}]}).
|
|
||||||
{ok,{response,[57187]}}
|
|
||||||
|
|
||||||
4> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, register,
|
|
||||||
[{struct, [{user, "ggeo"}, {host, "example.com"}, {password, "gogo11"}]}]}).
|
|
||||||
{ok,{response,[0]}}
|
|
||||||
|
|
||||||
5> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, register,
|
|
||||||
[{struct, [{user, "ggeo"}, {host, "example.com"}, {password, "gogo11"}]}]}).
|
|
||||||
{ok,{response,[409]}}
|
|
||||||
|
|
||||||
6> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, muc_room_change_option,
|
|
||||||
[{struct, [{name, "test"}, {service, "conference.localhost"},
|
|
||||||
{option, "title"}, {value, "Test Room"}]}]}).
|
|
||||||
|
|
||||||
7> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, muc_room_set_affiliation,
|
|
||||||
[{struct, [{name, "test"}, {service, "conference.example.com"},
|
|
||||||
{jid, "ex@example.com"}, {affiliation, "member"}]}]}).
|
|
||||||
|
|
||||||
8> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, muc_room_set_affiliation,
|
|
||||||
[{struct, [{name, "test"}, {service, "conference.example.com"},
|
|
||||||
{jid, "ex@example.com"}, {affiliation, "none"}]}]}).
|
|
||||||
|
|
||||||
|
|
||||||
- Some possible XML-RPC error messages:
|
|
||||||
|
|
||||||
+ Client: connection refused: wrong IP, wrong port, the server is not started...
|
|
||||||
|
|
||||||
2> xmlrpc:call({127, 0, 0, 1}, 44444, "/", {call, echothis, [800]}).
|
|
||||||
{error,econnrefused}
|
|
||||||
|
|
||||||
+ Client: bad value: a800 is a string, so it must be put into ""
|
|
||||||
|
|
||||||
7> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [a800]}).
|
|
||||||
{error,{bad_value,a800}}
|
|
||||||
|
|
||||||
+ Server: unknown call: you sent a call that the server does not implement
|
|
||||||
|
|
||||||
3> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, bububu, [800]}).
|
|
||||||
{ok,{response,{fault,-1,"Unknown call: {call,bububu,[800]}"}}}
|
|
||||||
|
|
||||||
|
|
||||||
EXAMPLE IN PYTHON
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
This is an example XML-RPC client in Python, thanks to Diddek:
|
|
||||||
-------
|
|
||||||
import xmlrpclib
|
|
||||||
|
|
||||||
server_url = 'http://127.0.0.1:4560';
|
|
||||||
server = xmlrpclib.Server(server_url);
|
|
||||||
|
|
||||||
params = {}
|
|
||||||
params["user"] = "ggeo"
|
|
||||||
params["host"] = "localhost"
|
|
||||||
params["password"] = "gogo11"
|
|
||||||
|
|
||||||
result = server.register(params)
|
|
||||||
print result
|
|
||||||
-------
|
|
||||||
|
|
||||||
This Python example shows how to provide authentication in the call, thanks to Muelli:
|
|
||||||
-------
|
|
||||||
import xmlrpclib
|
|
||||||
|
|
||||||
server_url = 'http://127.0.0.1:4560'
|
|
||||||
server = xmlrpclib.ServerProxy(server_url)
|
|
||||||
|
|
||||||
EJABBERD_XMLRPC_LOGIN = {'user': 'adminuser', 'server': 'localhost', 'password': 'aeiou'}
|
|
||||||
|
|
||||||
def ejabberdctl(command, data):
|
|
||||||
fn = getattr(server, command)
|
|
||||||
return fn(EJABBERD_XMLRPC_LOGIN, data)
|
|
||||||
|
|
||||||
result = ejabberdctl('register', {'user':'ggeo', 'host':'localhost', 'password':'gogo11'})
|
|
||||||
print result
|
|
||||||
-------
|
|
||||||
|
|
||||||
|
|
||||||
EXAMPLE IN RUBY
|
|
||||||
---------------
|
|
||||||
|
|
||||||
This is an example XML-RPC client in Ruby, thanks to Diddek:
|
|
||||||
-------
|
|
||||||
require 'xmlrpc/client'
|
|
||||||
|
|
||||||
host = "172.16.29.6:4560"
|
|
||||||
timeout = 3000000
|
|
||||||
client = XMLRPC::Client.new2("http://#{host}", "#{host}", timeout)
|
|
||||||
result = client.call("echothis", "800")
|
|
||||||
puts result
|
|
||||||
-------
|
|
||||||
|
|
||||||
|
|
||||||
EXAMPLE IN JAVA
|
|
||||||
---------------
|
|
||||||
|
|
||||||
This is an XML-RPC client in Java, thanks to Calder.
|
|
||||||
It requires Apache XML-RPC available at http://ws.apache.org/xmlrpc/
|
|
||||||
|
|
||||||
-------
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.xmlrpc.client.XmlRpcClient;
|
|
||||||
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
|
|
||||||
|
|
||||||
public class Test {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
try {
|
|
||||||
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
|
|
||||||
config.setServerURL(new URL("http://127.0.0.1:4560/RPC2"));
|
|
||||||
XmlRpcClient client = new XmlRpcClient();
|
|
||||||
client.setConfig(config);
|
|
||||||
|
|
||||||
/* Command string */
|
|
||||||
String command = "check_password";
|
|
||||||
|
|
||||||
/* Parameters as struct */
|
|
||||||
Map struct = new HashMap();
|
|
||||||
struct.put("user", "test1");
|
|
||||||
struct.put("host", "localhost");
|
|
||||||
struct.put("password", "test");
|
|
||||||
|
|
||||||
Object[] params = new Object[]{struct};
|
|
||||||
Integer result = (Integer) client.execute(command, params);
|
|
||||||
System.out.println(result);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-------
|
|
||||||
|
|
||||||
|
|
||||||
EXAMPLE IN C#
|
|
||||||
-------------
|
|
||||||
|
|
||||||
This is an XML-RPC client in C#, thanks to Mitchel Constantin.
|
|
||||||
|
|
||||||
-------
|
|
||||||
// Copyright: 2010 Weavver, Inc.
|
|
||||||
// Author: Mitchel Constantin <mythicalbox@weavver.com>
|
|
||||||
// License: Public Domain (Limited to this file)
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using CookComputing.XmlRpc;
|
|
||||||
|
|
||||||
namespace Weavver.Vendors.ProcessOne
|
|
||||||
{
|
|
||||||
public struct send_message_chat
|
|
||||||
{
|
|
||||||
public string from;
|
|
||||||
public string to;
|
|
||||||
public string body;
|
|
||||||
}
|
|
||||||
public struct status {}
|
|
||||||
public class ejabberdRPC
|
|
||||||
{
|
|
||||||
[XmlRpcUrl("http://205.134.225.18:4560/RPC2")]
|
|
||||||
public interface IStateName : IXmlRpcProxy
|
|
||||||
{
|
|
||||||
[XmlRpcMethod("send_message_chat")]
|
|
||||||
object SendMessageChat(send_message_chat message);
|
|
||||||
[XmlRpcMethod("status")]
|
|
||||||
object Status(status s);
|
|
||||||
}
|
|
||||||
public CookComputing.XmlRpc.XmlRpcStruct SendMessageChat(send_message_chat message)
|
|
||||||
{
|
|
||||||
IStateName proxy = XmlRpcProxyGen.Create<IStateName>();
|
|
||||||
proxy.KeepAlive = false;
|
|
||||||
return (CookComputing.XmlRpc.XmlRpcStruct) proxy.SendMessageChat(message);
|
|
||||||
}
|
|
||||||
public CookComputing.XmlRpc.XmlRpcStruct Status(status status)
|
|
||||||
{
|
|
||||||
IStateName proxy = XmlRpcProxyGen.Create<IStateName>();
|
|
||||||
proxy.KeepAlive = false;
|
|
||||||
return (CookComputing.XmlRpc.XmlRpcStruct) proxy.Status(status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-------
|
|
|
@ -1 +0,0 @@
|
||||||
erl -pa ../../ejabberd-dev/trunk/ebin -pa ebin -make
|
|
|
@ -1,2 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
erl -pa ../ejabberd-dev/ebin -pz ebin -make
|
|
|
@ -1,467 +0,0 @@
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%% File : ejabberd_xmlrpc.erl
|
|
||||||
%%% Author : Badlop <badlop@process-one.net>
|
|
||||||
%%% Purpose : XML-RPC server that frontends ejabberd commands
|
|
||||||
%%% Created : 21 Aug 2007 by Badlop <badlop@ono.com>
|
|
||||||
%%% Id : $Id: ejabberd_xmlrpc.erl 595 2008-05-20 11:39:31Z badlop $
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
%%% TODO: Implement a command in ejabberdctl 'help COMMAND LANGUAGE' that shows
|
|
||||||
%%% a coding example to call that command in a specific language (python, php).
|
|
||||||
|
|
||||||
%%% TODO: Remove support for plaintext password
|
|
||||||
|
|
||||||
%%% TODO: commands strings should be strings without ~n
|
|
||||||
|
|
||||||
-module(ejabberd_xmlrpc).
|
|
||||||
-author('badlop@process-one.net').
|
|
||||||
|
|
||||||
-export([
|
|
||||||
start_listener/2,
|
|
||||||
handler/2,
|
|
||||||
socket_type/0
|
|
||||||
]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
|
||||||
-include("mod_roster.hrl").
|
|
||||||
-include("jlib.hrl").
|
|
||||||
|
|
||||||
-record(state, {access_commands, auth = noauth, get_auth}).
|
|
||||||
|
|
||||||
|
|
||||||
%% Test:
|
|
||||||
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_integer, [{struct, [{thisinteger, 5}]}]}).
|
|
||||||
%% {ok,{response,[{struct,[{zero,0}]}]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_string, [{struct, [{thisstring, "abcd"}]}]}).
|
|
||||||
%% {ok,{response,[{struct,[{thatstring,"abcd"}]}]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, tell_tuple_3integer, [{struct, [{thisstring, "abcd"}]}]}).
|
|
||||||
%% {ok,{response,
|
|
||||||
%% [{struct,
|
|
||||||
%% [{thattuple,
|
|
||||||
%% {array,
|
|
||||||
%% [{struct,[{first,123}]},
|
|
||||||
%% {struct,[{second,456}]},
|
|
||||||
%% {struct,[{third,789}]}]}}]}]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, pow, [{struct, [{base, 5}, {exponent, 7}]}]}).
|
|
||||||
%% {ok,{response,[{struct,[{pow,78125}]}]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, seq, [{struct, [{from, 3}, {to, 7}]}]}).
|
|
||||||
%% {ok,{response,[{array,[{struct,[{intermediate,3}]},
|
|
||||||
%% {struct,[{intermediate,4}]},
|
|
||||||
%% {struct,[{intermediate,5}]},
|
|
||||||
%% {struct,[{intermediate,6}]},
|
|
||||||
%% {struct,[{intermediate,7}]}]}]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, substrs, [{struct, [{word, "abcd"}]}]}).
|
|
||||||
%% NO:
|
|
||||||
%% {ok,{response,[{array,[{struct,[{miniword,"a"}]},
|
|
||||||
%% {struct,[{miniword,"ab"}]},
|
|
||||||
%% {struct,[{miniword,"abc"}]},
|
|
||||||
%% {struct,[{miniword,"abcd"}]}]}]}}
|
|
||||||
%% {ok,{response,
|
|
||||||
%% [{struct,
|
|
||||||
%% [{substrings,
|
|
||||||
%% {array,
|
|
||||||
%% [{struct,[{miniword,"a"}]},
|
|
||||||
%% {struct,[{miniword,"ab"}]},
|
|
||||||
%% {struct,[{miniword,"abc"}]},
|
|
||||||
%% {struct,[{miniword,"abcd"}]}]}}]}]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, splitjid, [{struct, [{jid, "abcd@localhost/work"}]}]}).
|
|
||||||
%% {ok,{response,
|
|
||||||
%% [{struct,
|
|
||||||
%% [{jidparts,
|
|
||||||
%% {array,
|
|
||||||
%% [{struct,[{user,"abcd"}]},
|
|
||||||
%% {struct,[{server,"localhost"}]},
|
|
||||||
%% {struct,[{resource,"work"}]}]}}]}]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}, {thisinteger, 55}]}]}).
|
|
||||||
%% {ok,{response,
|
|
||||||
%% [{struct,
|
|
||||||
%% [{thistuple,
|
|
||||||
%% {array,
|
|
||||||
%% [{struct,[{thisinteger,55}]},
|
|
||||||
%% {struct,[{thisstring,"abc"}]}]}}]}]}}
|
|
||||||
%%
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_list_integer, [{struct, [{thislist, {array, [{struct, [{thisinteger, 55}, {thisinteger, 4567}]}]}}]}]}).
|
|
||||||
%% {ok,{response,
|
|
||||||
%% [{struct,
|
|
||||||
%% [{thatlist,
|
|
||||||
%% {array,
|
|
||||||
%% [{struct,[{thatinteger,55}]},
|
|
||||||
%% {struct,[{thatinteger,4567}]}]}}]}]}}
|
|
||||||
%%
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_list_string, [{struct, [{thisinteger, 123456}, {thislist, {array, [{struct, [{thisstring, "abc"}, {thisstring, "bobo baba"}]}]}}]}]}).
|
|
||||||
%% {ok,
|
|
||||||
%% {response,
|
|
||||||
%% [{struct,
|
|
||||||
%% [{thistuple,
|
|
||||||
%% {array,
|
|
||||||
%% [{struct,[{thatinteger,123456}]},
|
|
||||||
%% {struct,
|
|
||||||
%% [{thatlist,
|
|
||||||
%% {array,
|
|
||||||
%% [{struct,[{thatstring,"abc"}]},
|
|
||||||
%% {struct,[{thatstring,"bobo baba"}]}]}}]}]}}]}]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisinteger2, 4567}]}]}}]}]}).
|
|
||||||
%% {ok,{response,[{struct,[{zero,0}]}]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_isatils, [{struct,
|
|
||||||
%% [{thisinteger, 123456990},
|
|
||||||
%% {thisstring, "This is ISATILS"},
|
|
||||||
%% {thisatom, "test_isatils"},
|
|
||||||
%% {thistuple, {array, [{struct, [
|
|
||||||
%% {listlen, 2},
|
|
||||||
%% {thislist, {array, [{struct, [
|
|
||||||
%% {contentstring, "word1"},
|
|
||||||
%% {contentstring, "word 2"}
|
|
||||||
%% ]}]}}
|
|
||||||
%% ]}]}}
|
|
||||||
%% ]}]}).
|
|
||||||
%% {ok,{response,
|
|
||||||
%% [{struct,
|
|
||||||
%% [{results,
|
|
||||||
%% {array,
|
|
||||||
%% [{struct,[{thatinteger,123456990}]},
|
|
||||||
%% {struct,[{thatstring,"This is ISATILS"}]},
|
|
||||||
%% {struct,[{thatatom,"test_isatils"}]},
|
|
||||||
%% {struct,
|
|
||||||
%% [{thattuple,
|
|
||||||
%% {array,
|
|
||||||
%% [{struct,[{listlen,123456990}]},
|
|
||||||
%% {struct,[{thatlist,...}]}]}}]}]}}]}]}}
|
|
||||||
|
|
||||||
%% ecommand doesn't exist:
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string2, [{struct, [{thisstring, "abc"}]}]}).
|
|
||||||
%% {ok,{response,{fault,-1, "Unknown call: {call,echo_integer_string2,[{struct,[{thisstring,\"abc\"}]}]}"}}}
|
|
||||||
%%
|
|
||||||
%% Duplicated argument:
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}, {thisinteger, 44}, {thisinteger, 55}]}]}).
|
|
||||||
%% {ok,{response,{fault,-104, "Error -104\nAttribute 'thisinteger' duplicated:\n[{thisstring,\"abc\"},{thisinteger,44},{thisinteger,55}]"}}}
|
|
||||||
%%
|
|
||||||
%% Missing argument:
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echo_integer_string, [{struct, [{thisstring, "abc"}]}]}).
|
|
||||||
%% {ok,{response,{fault,-106, "Error -106\nRequired attribute 'thisinteger' not found:\n[{thisstring,\"abc\"}]"}}}
|
|
||||||
%%
|
|
||||||
%% Duplicated tuple element:
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisinteger1, 66}, {thisinteger2, 4567}]}]}}]}]}).
|
|
||||||
%% {ok,{response,{fault,-104, "Error -104\nAttribute 'thisinteger1' defined multiple times:\n[{thisinteger1,55},{thisinteger1,66},{thisinteger2,4567}]"}}}
|
|
||||||
%%
|
|
||||||
%% Missing element in tuple:
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, take_tuple_2integer, [{struct, [{thistuple, {array, [{struct, [{thisinteger1, 55}, {thisintegerc, 66}, {thisinteger, 4567}]}]}}]}]}).
|
|
||||||
%% {ok,{response,{fault,-106, "Error -106\nRequired attribute 'thisinteger2' not found:\n[{thisintegerc,66},{thisinteger,4567}]"}}}
|
|
||||||
%%
|
|
||||||
%% The ecommand crashed:
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, this_crashes, [{struct, []}]}).
|
|
||||||
%% {ok,{response,{fault,-100, "Error -100\nA problem 'error' occurred executing the command this_crashes with arguments []: badarith"}}}
|
|
||||||
|
|
||||||
|
|
||||||
%% -----------------------------
|
|
||||||
%% Listener interface
|
|
||||||
%% -----------------------------
|
|
||||||
|
|
||||||
start_listener({Port, Ip, tcp = _TranportProtocol}, Opts) ->
|
|
||||||
%% get options
|
|
||||||
MaxSessions = gen_mod:get_opt(maxsessions, Opts, 10),
|
|
||||||
Timeout = gen_mod:get_opt(timeout, Opts, 5000),
|
|
||||||
AccessCommands = gen_mod:get_opt(access_commands, Opts, []),
|
|
||||||
GetAuth = case [ACom || {Ac, _, _} = ACom <- AccessCommands, Ac /= all] of
|
|
||||||
[] -> false;
|
|
||||||
_ -> true
|
|
||||||
end,
|
|
||||||
|
|
||||||
%% start the XML-RPC server
|
|
||||||
Handler = {?MODULE, handler},
|
|
||||||
State = #state{access_commands = AccessCommands, get_auth = GetAuth},
|
|
||||||
xmlrpc:start_link(Ip, Port, MaxSessions, Timeout, Handler, State).
|
|
||||||
|
|
||||||
socket_type() ->
|
|
||||||
independent.
|
|
||||||
|
|
||||||
|
|
||||||
%% -----------------------------
|
|
||||||
%% Access verification
|
|
||||||
%% -----------------------------
|
|
||||||
|
|
||||||
%% @spec (AuthList) -> {User, Server, Password}
|
|
||||||
%% where
|
|
||||||
%% AuthList = [{user, string()}, {server, string()}, {password, string()}]
|
|
||||||
%% It may throw: {error, missing_auth_arguments, Attr}
|
|
||||||
get_auth(AuthList) ->
|
|
||||||
%% Check AuthList contains all the required data
|
|
||||||
[User, Server, Password] =
|
|
||||||
try get_attrs([user, server, password], AuthList) of
|
|
||||||
[U, S, P] -> [U, S, P]
|
|
||||||
catch
|
|
||||||
exit:{attribute_not_found, Attr, _} ->
|
|
||||||
throw({error, missing_auth_arguments, Attr})
|
|
||||||
end,
|
|
||||||
{User, Server, Password}.
|
|
||||||
|
|
||||||
|
|
||||||
%% -----------------------------
|
|
||||||
%% Handlers
|
|
||||||
%% -----------------------------
|
|
||||||
|
|
||||||
%% Call: Arguments: Returns:
|
|
||||||
|
|
||||||
|
|
||||||
%% .............................
|
|
||||||
%% Access verification
|
|
||||||
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [152]}).
|
|
||||||
%% {ok,{response,{fault,-103, "Error -103\nRequired authentication: {call,echothis,[152]}"}}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "ada"}]}, 152]}).
|
|
||||||
%% {ok,{response,{fault,-103,
|
|
||||||
%% "Error -103\nAuthentication non valid: [{user,\"badlop\"},\n
|
|
||||||
%% {server,\"localhost\"},\n
|
|
||||||
%% {password,\"ada\"}]"}}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "ada90ada"}]}, 152]}).
|
|
||||||
%% {ok,{response,[152]}}
|
|
||||||
%%
|
|
||||||
%% xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [{struct, [{user, "badlop"}, {server, "localhost"}, {password, "79C1574A43BC995F2B145A299EF97277"}]}, 152]}).
|
|
||||||
%% {ok,{response,[152]}}
|
|
||||||
|
|
||||||
handler(#state{get_auth = true, auth = noauth} = State, {call, Method, [{struct, AuthList} | Arguments] = AllArgs}) ->
|
|
||||||
try get_auth(AuthList) of
|
|
||||||
Auth ->
|
|
||||||
handler(State#state{get_auth = false, auth = Auth}, {call, Method, Arguments})
|
|
||||||
catch
|
|
||||||
{error, missing_auth_arguments, _Attr} ->
|
|
||||||
handler(State#state{get_auth = false, auth = noauth}, {call, Method, AllArgs})
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
%% .............................
|
|
||||||
%% Debug
|
|
||||||
|
|
||||||
%% echothis String String
|
|
||||||
handler(_State, {call, echothis, [A]}) ->
|
|
||||||
{false, {response, [A]}};
|
|
||||||
|
|
||||||
%% echothisnew struct[{sentence, String}] struct[{repeated, String}]
|
|
||||||
handler(_State, {call, echothisnew, [{struct, [{sentence, A}]}]}) ->
|
|
||||||
{false, {response, [{struct, [{repeated, A}]}]}};
|
|
||||||
|
|
||||||
%% multhis struct[{a, Integer}, {b, Integer}] Integer
|
|
||||||
handler(_State, {call, multhis, [{struct, [{a, A}, {b, B}]}]}) ->
|
|
||||||
{false, {response, [A*B]}};
|
|
||||||
|
|
||||||
%% multhisnew struct[{a, Integer}, {b, Integer}] struct[{mu, Integer}]
|
|
||||||
handler(_State, {call, multhisnew, [{struct, [{a, A}, {b, B}]}]}) ->
|
|
||||||
{false, {response, [{struct, [{mu, A*B}]}]}};
|
|
||||||
|
|
||||||
%% .............................
|
|
||||||
%% Statistics
|
|
||||||
|
|
||||||
%% tellme_title String String
|
|
||||||
handler(_State, {call, tellme_title, [A]}) ->
|
|
||||||
{false, {response, [get_title(A)]}};
|
|
||||||
|
|
||||||
%% tellme_value String String
|
|
||||||
handler(_State, {call, tellme_value, [A]}) ->
|
|
||||||
N = node(),
|
|
||||||
{false, {response, [get_value(N, A)]}};
|
|
||||||
|
|
||||||
%% tellme String struct[{title, String}, {value. String}]
|
|
||||||
handler(_State, {call, tellme, [A]}) ->
|
|
||||||
N = node(),
|
|
||||||
T = {title, get_title(A)},
|
|
||||||
V = {value, get_value(N, A)},
|
|
||||||
R = {struct, [T, V]},
|
|
||||||
{false, {response, [R]}};
|
|
||||||
|
|
||||||
%% .............................
|
|
||||||
%% ejabberd commands
|
|
||||||
|
|
||||||
handler(State, {call, Command, []}) ->
|
|
||||||
%% The XMLRPC request may not contain a struct parameter,
|
|
||||||
%% but our internal functions need such struct, even if it's empty
|
|
||||||
%% So let's add it and do a recursive call:
|
|
||||||
handler(State, {call, Command, [{struct, []}]});
|
|
||||||
|
|
||||||
handler(State, {call, Command, [{struct, AttrL}]} = Payload) ->
|
|
||||||
case ejabberd_commands:get_command_format(Command) of
|
|
||||||
{error, command_unknown} ->
|
|
||||||
build_fault_response(-112, "Unknown call: ~p", [Payload]);
|
|
||||||
{ArgsF, ResultF} ->
|
|
||||||
try_do_command(State#state.access_commands, State#state.auth, Command, AttrL, ArgsF, ResultF)
|
|
||||||
end;
|
|
||||||
|
|
||||||
%% If no other guard matches
|
|
||||||
handler(_State, Payload) ->
|
|
||||||
build_fault_response(-112, "Unknown call: ~p", [Payload]).
|
|
||||||
|
|
||||||
|
|
||||||
%% -----------------------------
|
|
||||||
%% Command
|
|
||||||
%% -----------------------------
|
|
||||||
|
|
||||||
try_do_command(AccessCommands, Auth, Command, AttrL, ArgsF, ResultF) ->
|
|
||||||
try do_command(AccessCommands, Auth, Command, AttrL, ArgsF, ResultF) of
|
|
||||||
{command_result, ResultFormatted} ->
|
|
||||||
{false, {response, [ResultFormatted]}}
|
|
||||||
catch
|
|
||||||
exit:{duplicated_attribute, ExitAt, ExitAtL} ->
|
|
||||||
build_fault_response(-114, "Attribute '~p' duplicated:~n~p", [ExitAt, ExitAtL]);
|
|
||||||
exit:{attribute_not_found, ExitAt, ExitAtL} ->
|
|
||||||
build_fault_response(-116, "Required attribute '~p' not found:~n~p", [ExitAt, ExitAtL]);
|
|
||||||
exit:{additional_unused_args, ExitAtL} ->
|
|
||||||
build_fault_response(-120, "The call provided additional unused arguments:~n~p", [ExitAtL]);
|
|
||||||
throw:Why ->
|
|
||||||
build_fault_response(-118, "A problem '~p' occurred executing the command ~p with arguments~n~p", [Why, Command, AttrL])
|
|
||||||
end.
|
|
||||||
|
|
||||||
build_fault_response(Code, ParseString, ParseArgs) ->
|
|
||||||
FaultString = "Error " ++ integer_to_list(Code) ++ "\n" ++
|
|
||||||
lists:flatten(io_lib:format(ParseString, ParseArgs)),
|
|
||||||
?WARNING_MSG(FaultString, []), %% Show Warning message in ejabberd log file
|
|
||||||
{false, {response, {fault, Code, FaultString}}}.
|
|
||||||
|
|
||||||
do_command(AccessCommands, Auth, Command, AttrL, ArgsF, ResultF) ->
|
|
||||||
ArgsFormatted = format_args(AttrL, ArgsF),
|
|
||||||
Result = ejabberd_commands:execute_command(AccessCommands, Auth, Command, ArgsFormatted),
|
|
||||||
ResultFormatted = format_result(Result, ResultF),
|
|
||||||
{command_result, ResultFormatted}.
|
|
||||||
|
|
||||||
|
|
||||||
%%-----------------------------
|
|
||||||
%% Format arguments
|
|
||||||
%%-----------------------------
|
|
||||||
|
|
||||||
get_attrs(Attribute_names, L) ->
|
|
||||||
[get_attr(A, L) || A <- Attribute_names].
|
|
||||||
|
|
||||||
get_attr(A, L) ->
|
|
||||||
case lists:keysearch(A, 1, L) of
|
|
||||||
{value, {A, Value}} -> Value;
|
|
||||||
false ->
|
|
||||||
%% Report the error and then force a crash
|
|
||||||
exit({attribute_not_found, A, L})
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% Get an element from a list and delete it.
|
|
||||||
%% The element must be defined once and only once,
|
|
||||||
%% otherwise the function crashes on purpose.
|
|
||||||
get_elem_delete(A, L) ->
|
|
||||||
case proplists:get_all_values(A, L) of
|
|
||||||
[Value] ->
|
|
||||||
{Value, proplists:delete(A, L)};
|
|
||||||
[_, _ | _] ->
|
|
||||||
%% Crash reporting the error
|
|
||||||
exit({duplicated_attribute, A, L});
|
|
||||||
[] ->
|
|
||||||
%% Report the error and then force a crash
|
|
||||||
exit({attribute_not_found, A, L})
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
format_args(Args, ArgsFormat) ->
|
|
||||||
{ArgsRemaining, R} =
|
|
||||||
lists:foldl(
|
|
||||||
fun({ArgName, ArgFormat}, {Args1, Res}) ->
|
|
||||||
{ArgValue, Args2} = get_elem_delete(ArgName, Args1),
|
|
||||||
Formatted = format_arg(ArgValue, ArgFormat),
|
|
||||||
{Args2, Res ++ [Formatted]}
|
|
||||||
end,
|
|
||||||
{Args, []},
|
|
||||||
ArgsFormat),
|
|
||||||
case ArgsRemaining of
|
|
||||||
[] -> R;
|
|
||||||
L when is_list(L) ->
|
|
||||||
exit({additional_unused_args, L})
|
|
||||||
end.
|
|
||||||
format_arg({array, [{struct, Elements}]}, {list, {ElementDefName, ElementDefFormat}})
|
|
||||||
when is_list(Elements) ->
|
|
||||||
lists:map(
|
|
||||||
fun({ElementName, ElementValue}) ->
|
|
||||||
true = (ElementDefName == ElementName),
|
|
||||||
format_arg(ElementValue, ElementDefFormat)
|
|
||||||
end,
|
|
||||||
Elements);
|
|
||||||
format_arg({array, [{struct, Elements}]}, {tuple, ElementsDef})
|
|
||||||
when is_list(Elements) ->
|
|
||||||
FormattedList = format_args(Elements, ElementsDef),
|
|
||||||
list_to_tuple(FormattedList);
|
|
||||||
format_arg({array, Elements}, {list, ElementsDef})
|
|
||||||
when is_list(Elements) and is_atom(ElementsDef) ->
|
|
||||||
[format_arg(Element, ElementsDef) || Element <- Elements];
|
|
||||||
format_arg(Arg, integer)
|
|
||||||
when is_integer(Arg) ->
|
|
||||||
Arg;
|
|
||||||
format_arg(Arg, string)
|
|
||||||
when is_list(Arg) ->
|
|
||||||
Arg.
|
|
||||||
|
|
||||||
|
|
||||||
%% -----------------------------
|
|
||||||
%% Result
|
|
||||||
%% -----------------------------
|
|
||||||
|
|
||||||
format_result({error, Error}, _) ->
|
|
||||||
throw({error, Error});
|
|
||||||
|
|
||||||
format_result(String, string) ->
|
|
||||||
lists:flatten(String);
|
|
||||||
|
|
||||||
format_result(Atom, {Name, atom}) ->
|
|
||||||
{struct, [{Name, atom_to_list(Atom)}]};
|
|
||||||
|
|
||||||
format_result(Int, {Name, integer}) ->
|
|
||||||
{struct, [{Name, Int}]};
|
|
||||||
|
|
||||||
format_result(String, {Name, string}) ->
|
|
||||||
{struct, [{Name, lists:flatten(String)}]};
|
|
||||||
|
|
||||||
format_result(Code, {Name, rescode}) ->
|
|
||||||
{struct, [{Name, make_status(Code)}]};
|
|
||||||
|
|
||||||
format_result({Code, Text}, {Name, restuple}) ->
|
|
||||||
{struct, [{Name, make_status(Code)},
|
|
||||||
{text, lists:flatten(Text)}]};
|
|
||||||
|
|
||||||
%% Result is a list of something: [something()]
|
|
||||||
format_result(Elements, {Name, {list, ElementsDef}}) ->
|
|
||||||
FormattedList = lists:map(
|
|
||||||
fun(Element) ->
|
|
||||||
format_result(Element, ElementsDef)
|
|
||||||
end,
|
|
||||||
Elements),
|
|
||||||
{struct, [{Name, {array, FormattedList}}]};
|
|
||||||
|
|
||||||
%% Result is a tuple with several elements: {something1(), something2(), ...}
|
|
||||||
format_result(ElementsTuple, {Name, {tuple, ElementsDef}}) ->
|
|
||||||
ElementsList = tuple_to_list(ElementsTuple),
|
|
||||||
ElementsAndDef = lists:zip(ElementsList, ElementsDef),
|
|
||||||
FormattedList = lists:map(
|
|
||||||
fun({Element, ElementDef}) ->
|
|
||||||
format_result(Element, ElementDef)
|
|
||||||
end,
|
|
||||||
ElementsAndDef),
|
|
||||||
{struct, [{Name, {array, FormattedList}}]}.
|
|
||||||
%% TODO: should be struct instead of array?
|
|
||||||
|
|
||||||
|
|
||||||
make_status(ok) -> 0;
|
|
||||||
make_status(true) -> 0;
|
|
||||||
make_status(false) -> 1;
|
|
||||||
make_status(error) -> 1;
|
|
||||||
make_status(_) -> 1.
|
|
||||||
|
|
||||||
|
|
||||||
%% -----------------------------
|
|
||||||
%% Internal
|
|
||||||
%% -----------------------------
|
|
||||||
|
|
||||||
get_title(A) -> mod_statsdx:get_title(A).
|
|
||||||
get_value(N, A) -> mod_statsdx:get(N, [A]).
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
author: "Gregor Uhlenheuer <kongo2002 at gmail.com>"
|
||||||
|
category: "archive"
|
||||||
|
summary: "Message Archive Management (XEP-0313)"
|
||||||
|
home: "https://github.com/kongo2002/ejabberd-mod-mam/tree/master/"
|
||||||
|
url: "git@github.com:kongo2002/ejabberd-mod-mam.git"
|
|
@ -0,0 +1,5 @@
|
||||||
|
author: "Lavrin"
|
||||||
|
category: "admin"
|
||||||
|
summary: "Easy tracing of connections made to ejabberd"
|
||||||
|
home: "https://github.com/lavrin/ejabberd-trace/tree/master/"
|
||||||
|
url: "git@github.com:lavrin/ejabberd-trace.git"
|
|
@ -0,0 +1,5 @@
|
||||||
|
author: "Rael Max"
|
||||||
|
category: "http"
|
||||||
|
summary: "POST offline messages to a web"
|
||||||
|
home: "https://github.com/raelmax/mod_http_offline/tree/master/"
|
||||||
|
url: "git@github.com:raelmax/mod_http_offline.git"
|
|
@ -0,0 +1,5 @@
|
||||||
|
author: "Jonas Ådahl <jadahl at gmail.com>"
|
||||||
|
category: "http"
|
||||||
|
summary: "RESTful API for ejabberd"
|
||||||
|
home: "https://github.com/jadahl/mod_restful/tree/master/"
|
||||||
|
url: "git@github.com:jadahl/mod_restful.git"
|
|
@ -1,27 +0,0 @@
|
||||||
|
|
||||||
MODULES=`pwd`
|
|
||||||
|
|
||||||
# Put here the path to ejabberd source
|
|
||||||
EJADIR=$MODULES/../git/ejabberd/
|
|
||||||
|
|
||||||
# TODO: Support extraction of multiple modules
|
|
||||||
#MODULE=mod_webpresence
|
|
||||||
MODULE=mod_register_web
|
|
||||||
|
|
||||||
RUNDIR=$MODULES/$MODULE/trunk/
|
|
||||||
PREPARESCRIPT=$EJADIR/contrib/extract_translations/prepare-translation.sh
|
|
||||||
|
|
||||||
# 1. Create the directory $MODULE/msgs/
|
|
||||||
|
|
||||||
# 2. Create the $MODULE.pot
|
|
||||||
#$PREPARESCRIPT -rundir $RUNDIR -ejadir $EJADIR -project $MODULE -src2pot
|
|
||||||
|
|
||||||
# 3. Create a language
|
|
||||||
# cp $MODULE.pot $LANG.$MODULE.po
|
|
||||||
# echo "" > $LANG.$MODULE.msg
|
|
||||||
|
|
||||||
# 3.b Convert msg to po. But it doesn't work! :(
|
|
||||||
#$PREPARESCRIPT -rundir $RUNDIR -ejadir $EJADIR -project $MODULE -srcmsg2po we
|
|
||||||
|
|
||||||
# 4. Update strings
|
|
||||||
$PREPARESCRIPT -rundir $RUNDIR -ejadir $EJADIR -project $MODULE -updateall
|
|
|
@ -1 +0,0 @@
|
||||||
{'src/ejabberd_ircd', [{outdir, "ebin"},{i,"../ejabberd-dev/include"}]}.
|
|
|
@ -1,4 +1,15 @@
|
||||||
|
|
||||||
|
|
||||||
|
***************
|
||||||
|
PLEASE NOTE
|
||||||
|
***************
|
||||||
|
|
||||||
|
This module does NOT work
|
||||||
|
with ejabberd 13 or newer.
|
||||||
|
|
||||||
|
***************
|
||||||
|
|
||||||
|
|
||||||
ircd - IRC-to-XMPP interface
|
ircd - IRC-to-XMPP interface
|
||||||
|
|
||||||
Author:
|
Author:
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
erl -pa ../../ejabberd-dev/trunk/ebin -pa ebin -make
|
|
|
@ -1,2 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
erl -pa ../ejabberd-dev/ebin -pz ebin -make
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
author: "Magnus Henoch <henoch at dtek.chalmers.se>"
|
||||||
|
category: "listener"
|
||||||
|
summary: "IRC server frontend to ejabberd"
|
||||||
|
home: "https://github.com/processone/ejabberd-contrib/tree/master/"
|
||||||
|
url: "git@github.com:processone/ejabberd-contrib.git"
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
-define(DICT, dict).
|
-define(DICT, dict).
|
||||||
|
|
||||||
|
@ -83,7 +84,7 @@ socket_type() ->
|
||||||
%% {stop, StopReason}
|
%% {stop, StopReason}
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
init([{SockMod, Socket}, Opts]) ->
|
init([{SockMod, Socket}, Opts]) ->
|
||||||
iconv:start(),
|
%iconv:start(),
|
||||||
Access = case lists:keysearch(access, 1, Opts) of
|
Access = case lists:keysearch(access, 1, Opts) of
|
||||||
{value, {_, A}} -> A;
|
{value, {_, A}} -> A;
|
||||||
_ -> all
|
_ -> all
|
||||||
|
@ -133,7 +134,8 @@ init([{SockMod, Socket}, Opts]) ->
|
||||||
}}.
|
}}.
|
||||||
|
|
||||||
handle_info({tcp, _Socket, Line}, StateName, StateData) ->
|
handle_info({tcp, _Socket, Line}, StateName, StateData) ->
|
||||||
DecodedLine = iconv:convert(StateData#state.encoding, "utf-8", Line),
|
%DecodedLine = iconv:convert(StateData#state.encoding, "utf-8", Line),
|
||||||
|
DecodedLine = Line,
|
||||||
Parsed = parse_line(DecodedLine),
|
Parsed = parse_line(DecodedLine),
|
||||||
?MODULE:StateName({line, Parsed}, StateData);
|
?MODULE:StateName({line, Parsed}, StateData);
|
||||||
handle_info({tcp_closed, _}, _StateName, StateData) ->
|
handle_info({tcp_closed, _}, _StateName, StateData) ->
|
||||||
|
@ -162,8 +164,8 @@ terminate(_Reason, _StateName, #state{socket = Socket, sockmod = SockMod,
|
||||||
none ->
|
none ->
|
||||||
ok;
|
ok;
|
||||||
_ ->
|
_ ->
|
||||||
Packet = {xmlelement, "presence",
|
Packet = {xmlel, <<"presence">>,
|
||||||
[{"type", "unavailable"}], []},
|
[{<<"type">>, <<"unavailable">>}], []},
|
||||||
FromJID = user_jid(State),
|
FromJID = user_jid(State),
|
||||||
?DICT:map(fun(ChannelJID, _ChannelData) ->
|
?DICT:map(fun(ChannelJID, _ChannelData) ->
|
||||||
ejabberd_router:route(FromJID, ChannelJID, Packet)
|
ejabberd_router:route(FromJID, ChannelJID, Packet)
|
||||||
|
@ -185,8 +187,10 @@ wait_for_nick({line, #line{command = "NICK", params = Params}}, State) ->
|
||||||
Nick = hd(Params),
|
Nick = hd(Params),
|
||||||
Pass = State#state.pass,
|
Pass = State#state.pass,
|
||||||
Server = State#state.host,
|
Server = State#state.host,
|
||||||
|
?DEBUG("user=~p server=~p", [Nick, Server]),
|
||||||
|
|
||||||
JID = jlib:make_jid(Nick, Server, "irc"),
|
JID = jlib:make_jid(list_to_binary(Nick), Server, <<"irc">>),
|
||||||
|
?DEBUG("JID=~p", [JID]),
|
||||||
case JID of
|
case JID of
|
||||||
error ->
|
error ->
|
||||||
?DEBUG("invalid nick '~p'", [Nick]),
|
?DEBUG("invalid nick '~p'", [Nick]),
|
||||||
|
@ -199,7 +203,7 @@ wait_for_nick({line, #line{command = "NICK", params = Params}}, State) ->
|
||||||
send_reply('ERR_NICKCOLLISION', [Nick, "Nickname collision"], State),
|
send_reply('ERR_NICKCOLLISION', [Nick, "Nickname collision"], State),
|
||||||
{next_state, wait_for_nick, State};
|
{next_state, wait_for_nick, State};
|
||||||
allow ->
|
allow ->
|
||||||
case ejabberd_auth:check_password(Nick, Server, Pass) of
|
case ejabberd_auth:check_password(list_to_binary(Nick), Server, list_to_binary(Pass)) of
|
||||||
false ->
|
false ->
|
||||||
?DEBUG("auth failed for '~p'", [Nick]),
|
?DEBUG("auth failed for '~p'", [Nick]),
|
||||||
send_reply('ERR_NICKCOLLISION', [Nick, "Authentication failed"], State),
|
send_reply('ERR_NICKCOLLISION', [Nick, "Authentication failed"], State),
|
||||||
|
@ -208,15 +212,15 @@ wait_for_nick({line, #line{command = "NICK", params = Params}}, State) ->
|
||||||
?DEBUG("good nickname '~p'", [Nick]),
|
?DEBUG("good nickname '~p'", [Nick]),
|
||||||
SID = {now(), self()},
|
SID = {now(), self()},
|
||||||
ejabberd_sm:open_session(
|
ejabberd_sm:open_session(
|
||||||
SID, Nick, Server, "irc", peerip(gen_tcp, State#state.socket)),
|
SID, list_to_binary(Nick), Server, <<"irc">>, peerip(gen_tcp, State#state.socket)),
|
||||||
ejabberd_sm:set_presence(SID, Nick, Server, "irc",
|
ejabberd_sm:set_presence(SID, list_to_binary(Nick), Server, <<"irc">>,
|
||||||
3, "undefined",
|
3, "undefined",
|
||||||
[{'ip', peerip(gen_tcp, State#state.socket)}, {'conn','c2s'}, {'state',"+"}]),
|
[{'ip', peerip(gen_tcp, State#state.socket)}, {'conn','c2s'}, {'state',"+"}]),
|
||||||
send_text_command("", "001", [Nick, "IRC interface of ejabberd server "++Server], State),
|
send_text_command("", "001", [Nick, "IRC interface of ejabberd server "++Server], State),
|
||||||
send_reply('RPL_MOTDSTART', [Nick, "- "++Server++" Message of the day - "], State),
|
send_reply('RPL_MOTDSTART', [Nick, "- "++binary_to_list(Server)++" Message of the day - "], State),
|
||||||
send_reply('RPL_MOTD', [Nick, "- This is the IRC interface of the ejabberd server "++Server++"."], State),
|
send_reply('RPL_MOTD', [Nick, "- This is the IRC interface of the ejabberd server "++binary_to_list(Server)++"."], State),
|
||||||
send_reply('RPL_MOTD', [Nick, "- Your full JID is "++Nick++"@"++Server++"/irc."], State),
|
send_reply('RPL_MOTD', [Nick, "- Your full JID is "++Nick++"@"++binary_to_list(Server)++"/irc."], State),
|
||||||
send_reply('RPL_MOTD', [Nick, "- Channel #whatever corresponds to MUC room whatever@"++State#state.muc_host++"."], State),
|
send_reply('RPL_MOTD', [Nick, "- Channel #whatever corresponds to MUC room whatever@"++binary_to_list(State#state.muc_host)++"."], State),
|
||||||
send_reply('RPL_MOTD', [Nick, "- This IRC interface is quite immature. You will probably find bugs."], State),
|
send_reply('RPL_MOTD', [Nick, "- This IRC interface is quite immature. You will probably find bugs."], State),
|
||||||
send_reply('RPL_MOTD', [Nick, "- Have a good time!"], State),
|
send_reply('RPL_MOTD', [Nick, "- Have a good time!"], State),
|
||||||
send_reply('RPL_ENDOFMOTD', [Nick, "End of /MOTD command"], State),
|
send_reply('RPL_ENDOFMOTD', [Nick, "End of /MOTD command"], State),
|
||||||
|
@ -243,6 +247,7 @@ wait_for_cmd({line, #line{command = "USER", params = [_Username, _Hostname, _Ser
|
||||||
%% Yeah, like we care.
|
%% Yeah, like we care.
|
||||||
{next_state, wait_for_cmd, State};
|
{next_state, wait_for_cmd, State};
|
||||||
wait_for_cmd({line, #line{command = "JOIN", params = Params}}, State) ->
|
wait_for_cmd({line, #line{command = "JOIN", params = Params}}, State) ->
|
||||||
|
?DEBUG("received JOIN ~p", [Params]),
|
||||||
{ChannelsString, KeysString} =
|
{ChannelsString, KeysString} =
|
||||||
case Params of
|
case Params of
|
||||||
[C, K] ->
|
[C, K] ->
|
||||||
|
@ -252,7 +257,9 @@ wait_for_cmd({line, #line{command = "JOIN", params = Params}}, State) ->
|
||||||
end,
|
end,
|
||||||
Channels = string:tokens(ChannelsString, ","),
|
Channels = string:tokens(ChannelsString, ","),
|
||||||
Keys = string:tokens(KeysString, ","),
|
Keys = string:tokens(KeysString, ","),
|
||||||
|
?DEBUG("joining channels ~p", [Channels]),
|
||||||
NewState = join_channels(Channels, Keys, State),
|
NewState = join_channels(Channels, Keys, State),
|
||||||
|
?DEBUG("joined channels ~p", [Channels]),
|
||||||
{next_state, wait_for_cmd, NewState};
|
{next_state, wait_for_cmd, NewState};
|
||||||
|
|
||||||
%% USERHOST command
|
%% USERHOST command
|
||||||
|
@ -293,18 +300,18 @@ wait_for_cmd({line, #line{command = "PRIVMSG", params = [To, Text]}}, State) ->
|
||||||
fun(Rcpt) ->
|
fun(Rcpt) ->
|
||||||
case Rcpt of
|
case Rcpt of
|
||||||
[$# | Roomname] ->
|
[$# | Roomname] ->
|
||||||
Packet = {xmlelement, "message",
|
Packet = {xmlel, <<"message">>,
|
||||||
[{"type", "groupchat"}],
|
[{<<"type">>, <<"groupchat">>}],
|
||||||
[{xmlelement, "body", [],
|
[{xmlel, <<"body">>, [],
|
||||||
filter_cdata(translate_action(Text))}]},
|
filter_cdata(translate_action(Text))}]},
|
||||||
ToJID = channel_to_jid(Roomname, State),
|
ToJID = channel_to_jid(Roomname, State),
|
||||||
ejabberd_router:route(FromJID, ToJID, Packet);
|
ejabberd_router:route(FromJID, ToJID, Packet);
|
||||||
_ ->
|
_ ->
|
||||||
case string:tokens(Rcpt, "#") of
|
case string:tokens(Rcpt, "#") of
|
||||||
[Nick, Channel] ->
|
[Nick, Channel] ->
|
||||||
Packet = {xmlelement, "message",
|
Packet = {xmlel, <<"message">>,
|
||||||
[{"type", "chat"}],
|
[{<<"type">>, <<"chat">>}],
|
||||||
[{xmlelement, "body", [],
|
[{xmlel, <<"body">>, [],
|
||||||
filter_cdata(translate_action(Text))}]},
|
filter_cdata(translate_action(Text))}]},
|
||||||
ToJID = channel_nick_to_jid(Nick, Channel, State),
|
ToJID = channel_nick_to_jid(Nick, Channel, State),
|
||||||
ejabberd_router:route(FromJID, ToJID, Packet);
|
ejabberd_router:route(FromJID, ToJID, Packet);
|
||||||
|
@ -356,9 +363,9 @@ wait_for_cmd({line, #line{command = "TOPIC", params = Params}}, State) ->
|
||||||
end;
|
end;
|
||||||
[Channel, NewTopic] ->
|
[Channel, NewTopic] ->
|
||||||
Packet =
|
Packet =
|
||||||
{xmlelement, "message",
|
{xmlel, <<"message">>,
|
||||||
[{"type", "groupchat"}],
|
[{<<"type">>, <<"groupchat">>}],
|
||||||
[{xmlelement, "subject", [], filter_cdata(NewTopic)}]},
|
[{xmlel, <<"subject">>, [], filter_cdata(NewTopic)}]},
|
||||||
FromJID = user_jid(State),
|
FromJID = user_jid(State),
|
||||||
ToJID = channel_to_jid(Channel, State),
|
ToJID = channel_to_jid(Channel, State),
|
||||||
ejabberd_router:route(FromJID, ToJID, Packet)
|
ejabberd_router:route(FromJID, ToJID, Packet)
|
||||||
|
@ -410,10 +417,11 @@ wait_for_cmd({line, #line{command = Unknown, params = Params} = Line}, State) ->
|
||||||
Unknown ++ "/" ++ integer_to_list(length(Params))], State),
|
Unknown ++ "/" ++ integer_to_list(length(Params))], State),
|
||||||
{next_state, wait_for_cmd, State};
|
{next_state, wait_for_cmd, State};
|
||||||
|
|
||||||
wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, State) ->
|
wait_for_cmd({route, From, _To, {xmlel, <<"presence">>, Attrs, Els} = El}, State) ->
|
||||||
|
?DEBUG("Received a Presence ~p ~p ~p", [From, _To, El]),
|
||||||
Type = xml:get_attr_s("type", Attrs),
|
Type = xml:get_attr_s("type", Attrs),
|
||||||
FromRoom = jlib:jid_remove_resource(From),
|
FromRoom = jlib:jid_remove_resource(From),
|
||||||
FromNick = From#jid.resource,
|
FromNick = binary_to_list(From#jid.resource),
|
||||||
|
|
||||||
Channel = jid_to_channel(From, State),
|
Channel = jid_to_channel(From, State),
|
||||||
MyNick = State#state.nick,
|
MyNick = State#state.nick,
|
||||||
|
@ -421,24 +429,29 @@ wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, Stat
|
||||||
|
|
||||||
Joining = ?DICT:find(FromRoom, State#state.joining),
|
Joining = ?DICT:find(FromRoom, State#state.joining),
|
||||||
Joined = ?DICT:find(FromRoom, State#state.joined),
|
Joined = ?DICT:find(FromRoom, State#state.joined),
|
||||||
|
?DEBUG("JoinState ~p ~p ~p", [Joining, Joined, Type]),
|
||||||
case {Joining, Joined, Type} of
|
case {Joining, Joined, Type} of
|
||||||
{{ok, BufferedNicks}, _, ""} ->
|
{{ok, BufferedNicks}, _, <<"">>} ->
|
||||||
|
?DEBUG("BufferedNicks ~p", [BufferedNicks]),
|
||||||
case BufferedNicks of
|
case BufferedNicks of
|
||||||
[] ->
|
[] ->
|
||||||
%% If this is the first presence, tell the
|
%% If this is the first presence, tell the
|
||||||
%% client that it's joining.
|
%% client that it's joining.
|
||||||
send_command(make_irc_sender(MyNick, FromRoom, State),
|
?DEBUG("Sending Command ~p ~p", [IRCSender, Channel]),
|
||||||
"JOIN", [Channel], State);
|
send_command(IRCSender, "JOIN", [Channel], State),
|
||||||
|
?DEBUG("Command Sent", []);
|
||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
?DEBUG("Getting NewRole", []),
|
||||||
NewRole = case find_el("x", ?NS_MUC_USER, Els) of
|
NewRole = case find_el("x", ?NS_MUC_USER, Els) of
|
||||||
nothing ->
|
nothing ->
|
||||||
"";
|
"";
|
||||||
XMucEl ->
|
XMucEl ->
|
||||||
xml:get_path_s(XMucEl, [{elem, "item"}, {attr, "role"}])
|
xml:get_path_s(XMucEl, [{elem, "item"}, {attr, "role"}])
|
||||||
end,
|
end,
|
||||||
|
?DEBUG("NewRole ~p", [NewRole]),
|
||||||
NewBufferedNicks = [{FromNick, NewRole} | BufferedNicks],
|
NewBufferedNicks = [{FromNick, NewRole} | BufferedNicks],
|
||||||
?DEBUG("~s is present in ~s. we now have ~p.",
|
?DEBUG("~s is present in ~s. we now have ~p.",
|
||||||
[FromNick, Channel, NewBufferedNicks]),
|
[FromNick, Channel, NewBufferedNicks]),
|
||||||
|
@ -478,14 +491,14 @@ wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, Stat
|
||||||
State#state{joining = NewJoining}
|
State#state{joining = NewJoining}
|
||||||
end,
|
end,
|
||||||
{next_state, wait_for_cmd, NewState};
|
{next_state, wait_for_cmd, NewState};
|
||||||
{{ok, _BufferedNicks}, _, "error"} ->
|
{{ok, _BufferedNicks}, _, <<"error">>} ->
|
||||||
NewState =
|
NewState =
|
||||||
case FromNick of
|
case FromNick of
|
||||||
MyNick ->
|
MyNick ->
|
||||||
%% we couldn't join the room
|
%% we couldn't join the room
|
||||||
{ReplyCode, ErrorDescription} =
|
{ReplyCode, ErrorDescription} =
|
||||||
case xml:get_subtag(El, "error") of
|
case xml:get_subtag(El, "error") of
|
||||||
{xmlelement, _, _, _} = ErrorEl ->
|
{xmlel, _, _, _} = ErrorEl ->
|
||||||
{ErrorName, ErrorText} = parse_error(ErrorEl),
|
{ErrorName, ErrorText} = parse_error(ErrorEl),
|
||||||
{case ErrorName of
|
{case ErrorName of
|
||||||
"forbidden" -> 'ERR_INVITEONLYCHAN';
|
"forbidden" -> 'ERR_INVITEONLYCHAN';
|
||||||
|
@ -510,7 +523,7 @@ wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, Stat
|
||||||
end,
|
end,
|
||||||
{next_state, wait_for_cmd, NewState};
|
{next_state, wait_for_cmd, NewState};
|
||||||
%% Presence in a channel we have already joined
|
%% Presence in a channel we have already joined
|
||||||
{_, {ok, _}, ""} ->
|
{_, {ok, _}, <<"">>} ->
|
||||||
%% Someone enters
|
%% Someone enters
|
||||||
send_command(IRCSender, "JOIN", [Channel], State),
|
send_command(IRCSender, "JOIN", [Channel], State),
|
||||||
{next_state, wait_for_cmd, State};
|
{next_state, wait_for_cmd, State};
|
||||||
|
@ -523,29 +536,37 @@ wait_for_cmd({route, From, _To, {xmlelement, "presence", Attrs, Els} = El}, Stat
|
||||||
{next_state, wait_for_cmd, State}
|
{next_state, wait_for_cmd, State}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State) ->
|
wait_for_cmd({route, From, _To, {xmlel, <<"message">>, Attrs, Els} = El}, State) ->
|
||||||
Type = xml:get_attr_s("type", Attrs),
|
?DEBUG("Got a Message! ~p ~p ~p", [From, _To, El]),
|
||||||
|
Type = xml:get_attr_s(<<"type">>, Attrs),
|
||||||
case Type of
|
case Type of
|
||||||
"groupchat" ->
|
<<"groupchat">> ->
|
||||||
|
?DEBUG("It's a groupchat", []),
|
||||||
ChannelJID = jlib:jid_remove_resource(From),
|
ChannelJID = jlib:jid_remove_resource(From),
|
||||||
case ?DICT:find(ChannelJID, State#state.joined) of
|
case ?DICT:find(ChannelJID, State#state.joined) of
|
||||||
{ok, #channel{} = ChannelData} ->
|
{ok, #channel{} = ChannelData} ->
|
||||||
FromChannel = jid_to_channel(From, State),
|
FromChannel = jid_to_channel(From, State),
|
||||||
FromNick = From#jid.resource,
|
FromNick = binary_to_list(From#jid.resource),
|
||||||
Subject = xml:get_path_s(El, [{elem, "subject"}, cdata]),
|
Subject = xml:get_path_s(El, [{elem, <<"subject">>}, cdata]),
|
||||||
Body = xml:get_path_s(El, [{elem, "body"}, cdata]),
|
Body = xml:get_path_s(El, [{elem, <<"body">>}, cdata]),
|
||||||
XDelay = lists:any(fun({xmlelement, "x", XAttrs, _}) ->
|
?DEBUG("Message Data ~p ~p", [Subject, Body]),
|
||||||
xml:get_attr_s("xmlns", XAttrs) == ?NS_DELAY;
|
XDelay = lists:any(fun({xmlel, <<"x">>, XAttrs, _}) ->
|
||||||
|
xml:get_attr_s(<<"xmlns">>, XAttrs) == ?NS_DELAY;
|
||||||
(_) ->
|
(_) ->
|
||||||
false
|
false
|
||||||
end, Els),
|
end, Els),
|
||||||
|
?DEBUG("XDelay ~p", [XDelay]),
|
||||||
if
|
if
|
||||||
Subject /= "" ->
|
Subject /= <<"">> ->
|
||||||
|
?DEBUG("Cleaning Subject!", []),
|
||||||
CleanSubject = lists:map(fun($\n) ->
|
CleanSubject = lists:map(fun($\n) ->
|
||||||
$\ ;
|
$\ ;
|
||||||
(C) -> C
|
(C) -> C
|
||||||
end, Subject),
|
end, binary_to_list(Subject)),
|
||||||
send_text_command(make_irc_sender(From, State),
|
?DEBUG("CleanSubject ~p", [CleanSubject]),
|
||||||
|
IRCSender = make_irc_sender(From, State),
|
||||||
|
?DEBUG("IRCSender ~p", [IRCSender]),
|
||||||
|
send_text_command(IRCSender,
|
||||||
"TOPIC", [FromChannel, CleanSubject], State),
|
"TOPIC", [FromChannel, CleanSubject], State),
|
||||||
NewChannelData = ChannelData#channel{topic = CleanSubject},
|
NewChannelData = ChannelData#channel{topic = CleanSubject},
|
||||||
NewState = State#state{joined = ?DICT:store(jlib:jid_remove_resource(From), NewChannelData, State#state.joined)},
|
NewState = State#state{joined = ?DICT:store(jlib:jid_remove_resource(From), NewChannelData, State#state.joined)},
|
||||||
|
@ -553,9 +574,11 @@ wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State
|
||||||
not XDelay, FromNick == State#state.nick ->
|
not XDelay, FromNick == State#state.nick ->
|
||||||
%% there is no message echo in IRC.
|
%% there is no message echo in IRC.
|
||||||
%% we let the backlog through, though.
|
%% we let the backlog through, though.
|
||||||
|
?DEBUG("Don't care about it", []),
|
||||||
{next_state, wait_for_cmd, State};
|
{next_state, wait_for_cmd, State};
|
||||||
true ->
|
true ->
|
||||||
BodyLines = string:tokens(Body, "\n"),
|
?DEBUG("Send it to someone!", []),
|
||||||
|
BodyLines = string:tokens(binary_to_list(Body), "\n"),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Line) ->
|
fun(Line) ->
|
||||||
Line1 =
|
Line1 =
|
||||||
|
@ -575,7 +598,7 @@ wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State
|
||||||
[jlib:jid_to_string(ChannelJID)]),
|
[jlib:jid_to_string(ChannelJID)]),
|
||||||
{next_state, wait_for_cmd, State}
|
{next_state, wait_for_cmd, State}
|
||||||
end;
|
end;
|
||||||
"error" ->
|
<<"error">> ->
|
||||||
MucHost = State#state.muc_host,
|
MucHost = State#state.muc_host,
|
||||||
ErrorFrom =
|
ErrorFrom =
|
||||||
case From of
|
case From of
|
||||||
|
@ -594,7 +617,7 @@ wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State
|
||||||
%% I think this should cover all possible combinations of
|
%% I think this should cover all possible combinations of
|
||||||
%% XMPP and non-XMPP error messages...
|
%% XMPP and non-XMPP error messages...
|
||||||
ErrorText =
|
ErrorText =
|
||||||
error_to_string(xml:get_subtag(El, "error")),
|
error_to_string(xml:get_subtag(El, <<"error">>)),
|
||||||
send_text_command("", "NOTICE", [State#state.nick,
|
send_text_command("", "NOTICE", [State#state.nick,
|
||||||
"Message to "++ErrorFrom++" bounced: "++
|
"Message to "++ErrorFrom++" bounced: "++
|
||||||
ErrorText], State),
|
ErrorText], State),
|
||||||
|
@ -603,8 +626,8 @@ wait_for_cmd({route, From, _To, {xmlelement, "message", Attrs, Els} = El}, State
|
||||||
ChannelJID = jlib:jid_remove_resource(From),
|
ChannelJID = jlib:jid_remove_resource(From),
|
||||||
case ?DICT:find(ChannelJID, State#state.joined) of
|
case ?DICT:find(ChannelJID, State#state.joined) of
|
||||||
{ok, #channel{}} ->
|
{ok, #channel{}} ->
|
||||||
FromNick = From#jid.lresource++jid_to_channel(From, State),
|
FromNick = binary_to_list(From#jid.lresource)++jid_to_channel(From, State),
|
||||||
Body = xml:get_path_s(El, [{elem, "body"}, cdata]),
|
Body = xml:get_path_s(El, [{elem, <<"body">>}, cdata]),
|
||||||
BodyLines = string:tokens(Body, "\n"),
|
BodyLines = string:tokens(Body, "\n"),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(Line) ->
|
fun(Line) ->
|
||||||
|
@ -635,30 +658,36 @@ join_channels(Channels, [], State) ->
|
||||||
join_channels([Channel | Channels], [Key | Keys],
|
join_channels([Channel | Channels], [Key | Keys],
|
||||||
#state{nick = Nick} = State) ->
|
#state{nick = Nick} = State) ->
|
||||||
Packet =
|
Packet =
|
||||||
{xmlelement, "presence", [],
|
{xmlel, <<"presence">>, [],
|
||||||
[{xmlelement, "x", [{"xmlns", ?NS_MUC}],
|
[{xmlel, <<"x">>, [{<<"xmlns">>, ?NS_MUC}],
|
||||||
case Key of
|
case Key of
|
||||||
none ->
|
none ->
|
||||||
[];
|
[];
|
||||||
_ ->
|
_ ->
|
||||||
[{xmlelement, "password", [], filter_cdata(Key)}]
|
[{xmlel, <<"password">>, [], filter_cdata(Key)}]
|
||||||
end}]},
|
end}]},
|
||||||
|
?DEBUG("joining channel nick=~p channel=~p state=~p", [Nick, Channel, State]),
|
||||||
From = user_jid(State),
|
From = user_jid(State),
|
||||||
|
?DEBUG("1 ~p", [From]),
|
||||||
To = channel_nick_to_jid(Nick, Channel, State),
|
To = channel_nick_to_jid(Nick, Channel, State),
|
||||||
|
?DEBUG("2 ~p", [To]),
|
||||||
Room = jlib:jid_remove_resource(To),
|
Room = jlib:jid_remove_resource(To),
|
||||||
|
?DEBUG("3 ~p", [Room]),
|
||||||
ejabberd_router:route(From, To, Packet),
|
ejabberd_router:route(From, To, Packet),
|
||||||
|
?DEBUG("4", []),
|
||||||
NewState = State#state{joining = ?DICT:store(Room, [], State#state.joining)},
|
NewState = State#state{joining = ?DICT:store(Room, [], State#state.joining)},
|
||||||
|
?DEBUG("5 ~p", [NewState]),
|
||||||
join_channels(Channels, Keys, NewState).
|
join_channels(Channels, Keys, NewState).
|
||||||
|
|
||||||
part_channels([], State, _Message) ->
|
part_channels([], State, _Message) ->
|
||||||
State;
|
State;
|
||||||
part_channels([Channel | Channels], State, Message) ->
|
part_channels([Channel | Channels], State, Message) ->
|
||||||
Packet =
|
Packet =
|
||||||
{xmlelement, "presence",
|
{xmlel, <<"presence">>,
|
||||||
[{"type", "unavailable"}],
|
[{<<"type">>, <<"unavailable">>}],
|
||||||
case Message of
|
case Message of
|
||||||
nothing -> [];
|
nothing -> [];
|
||||||
_ -> [{xmlelement, "status", [],
|
_ -> [{xmlel, <<"status">>, [],
|
||||||
[{xmlcdata, Message}]}]
|
[{xmlcdata, Message}]}]
|
||||||
end},
|
end},
|
||||||
From = user_jid(State),
|
From = user_jid(State),
|
||||||
|
@ -703,7 +732,8 @@ upcase([C|String]) ->
|
||||||
send_line(Line, #state{sockmod = SockMod, socket = Socket, encoding = Encoding}) ->
|
send_line(Line, #state{sockmod = SockMod, socket = Socket, encoding = Encoding}) ->
|
||||||
?DEBUG("sending ~s", [Line]),
|
?DEBUG("sending ~s", [Line]),
|
||||||
gen_tcp = SockMod,
|
gen_tcp = SockMod,
|
||||||
EncodedLine = iconv:convert("utf-8", Encoding, Line),
|
%EncodedLine = iconv:convert("utf-8", Encoding, Line),
|
||||||
|
EncodedLine = Line,
|
||||||
ok = gen_tcp:send(Socket, [EncodedLine, 13, 10]).
|
ok = gen_tcp:send(Socket, [EncodedLine, 13, 10]).
|
||||||
|
|
||||||
send_command(Sender, Command, Params, State) ->
|
send_command(Sender, Command, Params, State) ->
|
||||||
|
@ -715,9 +745,10 @@ send_text_command(Sender, Command, Params, State) ->
|
||||||
send_command(Sender, Command, Params, State, true).
|
send_command(Sender, Command, Params, State, true).
|
||||||
|
|
||||||
send_command(Sender, Command, Params, State, AlwaysQuote) ->
|
send_command(Sender, Command, Params, State, AlwaysQuote) ->
|
||||||
|
?DEBUG("SendCommand ~p ~p ~p", [Sender, Command, Params]),
|
||||||
Prefix = case Sender of
|
Prefix = case Sender of
|
||||||
"" ->
|
"" ->
|
||||||
[$: | State#state.host];
|
[$: | binary_to_list(State#state.host)];
|
||||||
_ ->
|
_ ->
|
||||||
[$: | Sender]
|
[$: | Sender]
|
||||||
end,
|
end,
|
||||||
|
@ -783,7 +814,7 @@ make_param_string([Param | Params], AlwaysQuote) ->
|
||||||
" " ++ Param ++ make_param_string(Params, AlwaysQuote)
|
" " ++ Param ++ make_param_string(Params, AlwaysQuote)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
find_el(Name, NS, [{xmlelement, N, Attrs, _} = El|Els]) ->
|
find_el(Name, NS, [{xmlel, N, Attrs, _} = El|Els]) ->
|
||||||
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
XMLNS = xml:get_attr_s("xmlns", Attrs),
|
||||||
case {Name, NS} of
|
case {Name, NS} of
|
||||||
{N, XMLNS} ->
|
{N, XMLNS} ->
|
||||||
|
@ -800,7 +831,7 @@ channel_to_jid(Channel, #state{muc_host = MucHost,
|
||||||
channels_to_jids = ChannelsToJids}) ->
|
channels_to_jids = ChannelsToJids}) ->
|
||||||
case ?DICT:find(Channel, ChannelsToJids) of
|
case ?DICT:find(Channel, ChannelsToJids) of
|
||||||
{ok, RoomJID} -> RoomJID;
|
{ok, RoomJID} -> RoomJID;
|
||||||
_ -> jlib:make_jid(Channel, MucHost, "")
|
_ -> jlib:make_jid(list_to_binary(Channel), MucHost, <<"">>)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
channel_nick_to_jid(Nick, [$#|Channel], State) ->
|
channel_nick_to_jid(Nick, [$#|Channel], State) ->
|
||||||
|
@ -808,28 +839,28 @@ channel_nick_to_jid(Nick, [$#|Channel], State) ->
|
||||||
channel_nick_to_jid(Nick, Channel, #state{muc_host = MucHost,
|
channel_nick_to_jid(Nick, Channel, #state{muc_host = MucHost,
|
||||||
channels_to_jids = ChannelsToJids}) ->
|
channels_to_jids = ChannelsToJids}) ->
|
||||||
case ?DICT:find(Channel, ChannelsToJids) of
|
case ?DICT:find(Channel, ChannelsToJids) of
|
||||||
{ok, RoomJID} -> jlib:jid_replace_resource(RoomJID, Nick);
|
{ok, RoomJID} -> jlib:jid_replace_resource(RoomJID, list_to_binary(Nick));
|
||||||
_ -> jlib:make_jid(Channel, MucHost, Nick)
|
_ -> jlib:make_jid(list_to_binary(Channel), MucHost, list_to_binary(Nick))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
jid_to_channel(#jid{user = Room} = RoomJID,
|
jid_to_channel(#jid{user = Room} = RoomJID,
|
||||||
#state{jids_to_channels = JidsToChannels}) ->
|
#state{jids_to_channels = JidsToChannels}) ->
|
||||||
case ?DICT:find(jlib:jid_remove_resource(RoomJID), JidsToChannels) of
|
case ?DICT:find(jlib:jid_remove_resource(RoomJID), JidsToChannels) of
|
||||||
{ok, Channel} -> [$#|Channel];
|
{ok, Channel} -> [$#|binary_to_list(Channel)];
|
||||||
_ -> [$#|Room]
|
_ -> [$#|binary_to_list(Room)]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
make_irc_sender(Nick, #jid{luser = Room} = RoomJID,
|
make_irc_sender(Nick, #jid{luser = Room} = RoomJID,
|
||||||
#state{jids_to_channels = JidsToChannels}) ->
|
#state{jids_to_channels = JidsToChannels}) ->
|
||||||
case ?DICT:find(jlib:jid_remove_resource(RoomJID), JidsToChannels) of
|
case ?DICT:find(jlib:jid_remove_resource(RoomJID), JidsToChannels) of
|
||||||
{ok, Channel} -> Nick++"!"++Nick++"@"++Channel;
|
{ok, Channel} -> Nick++"!"++Nick++"@"++binary_to_list(Channel);
|
||||||
_ -> Nick++"!"++Nick++"@"++Room
|
_ -> Nick++"!"++Nick++"@"++binary_to_list(Room)
|
||||||
end.
|
end.
|
||||||
make_irc_sender(#jid{lresource = Nick} = JID, State) ->
|
make_irc_sender(#jid{lresource = Nick} = JID, State) ->
|
||||||
make_irc_sender(Nick, JID, State).
|
make_irc_sender(binary_to_list(Nick), JID, State).
|
||||||
|
|
||||||
user_jid(#state{nick = Nick, host = Host}) ->
|
user_jid(#state{nick = Nick, host = Host}) ->
|
||||||
jlib:make_jid(Nick, Host, "irc").
|
jlib:make_jid(list_to_binary(Nick), Host, <<"irc">>).
|
||||||
|
|
||||||
filter_cdata(Msg) ->
|
filter_cdata(Msg) ->
|
||||||
[{xmlcdata, filter_message(Msg)}].
|
[{xmlcdata, filter_message(Msg)}].
|
||||||
|
@ -856,25 +887,25 @@ translate_action(Msg) ->
|
||||||
Msg
|
Msg
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_error({xmlelement, "error", _ErrorAttrs, ErrorEls} = ErrorEl) ->
|
parse_error({xmlel, "error", _ErrorAttrs, ErrorEls} = ErrorEl) ->
|
||||||
ErrorTextEl = xml:get_subtag(ErrorEl, "text"),
|
ErrorTextEl = xml:get_subtag(ErrorEl, "text"),
|
||||||
ErrorName =
|
ErrorName =
|
||||||
case ErrorEls -- [ErrorTextEl] of
|
case ErrorEls -- [ErrorTextEl] of
|
||||||
[{xmlelement, ErrorReason, _, _}] ->
|
[{xmlel, ErrorReason, _, _}] ->
|
||||||
ErrorReason;
|
ErrorReason;
|
||||||
_ ->
|
_ ->
|
||||||
"unknown error"
|
"unknown error"
|
||||||
end,
|
end,
|
||||||
ErrorText =
|
ErrorText =
|
||||||
case ErrorTextEl of
|
case ErrorTextEl of
|
||||||
{xmlelement, _, _, _} ->
|
{xmlel, _, _, _} ->
|
||||||
xml:get_tag_cdata(ErrorTextEl);
|
xml:get_tag_cdata(ErrorTextEl);
|
||||||
_ ->
|
_ ->
|
||||||
nothing
|
nothing
|
||||||
end,
|
end,
|
||||||
{ErrorName, ErrorText}.
|
{ErrorName, ErrorText}.
|
||||||
|
|
||||||
error_to_string({xmlelement, "error", _ErrorAttrs, _ErrorEls} = ErrorEl) ->
|
error_to_string({xmlel, "error", _ErrorAttrs, _ErrorEls} = ErrorEl) ->
|
||||||
case parse_error(ErrorEl) of
|
case parse_error(ErrorEl) of
|
||||||
{ErrorName, ErrorText} when is_list(ErrorText) ->
|
{ErrorName, ErrorText} when is_list(ErrorText) ->
|
||||||
ErrorName ++ ": " ++ ErrorText;
|
ErrorName ++ ": " ++ ErrorText;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue