Compare commits
891 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1820b4f63b | |||
| 63aabed320 | |||
| fd7bf7fed3 | |||
| 7a90cda8ff | |||
| 35eeaa5869 | |||
| 024713a441 | |||
| 32fbfe1981 | |||
| 9c5427e0c2 | |||
| 8f5a1c4a2a | |||
| 7d3609d954 | |||
| fc7ba53c37 | |||
| a96d72330d | |||
| 7d626b4f5c | |||
| e903348dd3 | |||
| bee251d928 | |||
| c658907331 | |||
| 3fec782494 | |||
| 52525eb76d | |||
| 8679cfd2f3 | |||
| 25af3fb029 | |||
| 2bceebc39d | |||
| 92532a0d66 | |||
| b673539a2a | |||
| e1aaa1c99d | |||
| aa9eb001d0 | |||
| 101e808124 | |||
| 766b7c65a6 | |||
| 0516a3dc0e | |||
| 3d185c0fb8 | |||
| 7815164216 | |||
| 65f4094804 | |||
| 06450f4a82 | |||
| ce0beb550c | |||
| 96c0483533 | |||
| 1274bcdba9 | |||
| 5dcc97c85a | |||
| 6deceeec2e | |||
| 9edcbadd60 | |||
| f65492e27f | |||
| 1df61a82c8 | |||
| 73509686f1 | |||
| 93e521d65e | |||
| 50511fcff7 | |||
| 5e26190b98 | |||
| f22bd6eb46 | |||
| 0ba6c78ed0 | |||
| 636d68e0a9 | |||
| 7e6d1c24c2 | |||
| 67918b17d3 | |||
| 51fa438340 | |||
| 35a11526f9 | |||
| 0cfec92c14 | |||
| e5f64bc24a | |||
| 5c48ba4609 | |||
| 3ca62a797a | |||
| b66dab1313 | |||
| 58110e4bc1 | |||
| ea96615460 | |||
| b8c26671c4 | |||
| 2e88d001d6 | |||
| 1a0db3de3a | |||
| 250876ea1a | |||
| 66510c1d78 | |||
| d6f1d3df5b | |||
| 72dbb6e7c1 | |||
| b1082a96c9 | |||
| ed17586cf0 | |||
| 26b8dd75f7 | |||
| c3473c2077 | |||
| e216654c52 | |||
| cc3391cc1c | |||
| b8d56a7c11 | |||
| 1bb9e83501 | |||
| de1a66dfbe | |||
| 95613a11ab | |||
| 2cd193f97c | |||
| 33a9d6a3c3 | |||
| fdb863ce70 | |||
| 43fc29873e | |||
| e216a54ead | |||
| d4cdc3a246 | |||
| f6bdc6fdb2 | |||
| 8f25baada6 | |||
| aaef1a14b4 | |||
| 22e8f5fd51 | |||
| 88ab787ba6 | |||
| eb9faffadd | |||
| 3b0eee785f | |||
| 5ef542a638 | |||
| ffdaff3740 | |||
| 56d273477e | |||
| a35b9dd9cc | |||
| c69720a1ab | |||
| a58de70f06 | |||
| 800965a957 | |||
| fcf672c50e | |||
| b66e369a1d | |||
| 9dd03c873c | |||
| e42bb47ce3 | |||
| dc7fa076d7 | |||
| 960cf495c6 | |||
| 0f12804a49 | |||
| 3c7c71cfa6 | |||
| aac190255b | |||
| 85a08a087b | |||
| 1bfb0ab39c | |||
| 4ef1cdec12 | |||
| 0534678028 | |||
| 70606d7f1a | |||
| bb39ecbc08 | |||
| 54e6e1a5fb | |||
| 5bb7a0b0db | |||
| 976a8c9cc9 | |||
| a095477b4c | |||
| 950aca380c | |||
| 985b0a1933 | |||
| a7841ed486 | |||
| a11e833a98 | |||
| 62ee051c6e | |||
| 5424ead01d | |||
| 96d385bf82 | |||
| 8e2258b16a | |||
| f87b46f454 | |||
| 5418b37314 | |||
| 6353a06a5d | |||
| 58b9077b51 | |||
| 10fcfa860a | |||
| 5f2dcc51ce | |||
| 0aa64381ff | |||
| 6c8b037422 | |||
| d63ea000c7 | |||
| 5e148df0a9 | |||
| d8f05acb67 | |||
| d6f4c99243 | |||
| 63b6e0d381 | |||
| 8c1568ff93 | |||
| 7b5895c90d | |||
| 115cb23bd8 | |||
| 62806607bf | |||
| b25b5c2f98 | |||
| 444c385f23 | |||
| ee8bbccb2a | |||
| 66237abd35 | |||
| 0973a8d6c1 | |||
| fbead19c88 | |||
| baf574d6c4 | |||
| e3c801f1f5 | |||
| f773edcb98 | |||
| 2a73068aac | |||
| 5971eb3da0 | |||
| 04fd5567a7 | |||
| 4f8c132b53 | |||
| a98685e0bb | |||
| 12733bd21b | |||
| f6767ed061 | |||
| de10a7a8ce | |||
| 5081a180fa | |||
| a576f3a6d1 | |||
| 3201f8e513 | |||
| 92003fa4dc | |||
| 818d9c8c42 | |||
| cbe6553baa | |||
| 5d3870faa3 | |||
| 2d8ce266bd | |||
| 0042f18c1f | |||
| 0982a9bc3c | |||
| 50327a0cfc | |||
| 5802062746 | |||
| 69de1780a0 | |||
| 6e8895f9e9 | |||
| e93762a720 | |||
| 268065e5c4 | |||
| d7878ef131 | |||
| 908bedeaa6 | |||
| 504860f065 | |||
| 3a96d72a7f | |||
| d88e4d495f | |||
| 66a4e405e0 | |||
| 0a77b9f43e | |||
| 470669fa6b | |||
| af29fb21df | |||
| be50d57ddd | |||
| 35d19b32f4 | |||
| 654d907dcf | |||
| b013c29c7e | |||
| 0ed23980a6 | |||
| a78862e05e | |||
| bcb44ccb6f | |||
| 736a182544 | |||
| 97bb1250ba | |||
| b0b7ac101c | |||
| 3e35d44b0f | |||
| 6691c59a7a | |||
| 1391d5a304 | |||
| 8f595b58a7 | |||
| 5bdc6c0822 | |||
| 1925b94131 | |||
| fc794b680a | |||
| a71065fcda | |||
| 7165196211 | |||
| f782955c06 | |||
| 3f13396d73 | |||
| 061d5f2380 | |||
| 2d17a2850c | |||
| cc58ce6301 | |||
| d3c8fb7705 | |||
| 9fe16a29e1 | |||
| 31a3cc7b10 | |||
| a8dc5f80d1 | |||
| 81d9770d4f | |||
| cdb191bb48 | |||
| 633b68db11 | |||
| e890525788 | |||
| 6b8c61b3a2 | |||
| 4849ac9781 | |||
| cd18d3d8a7 | |||
| a0908ba393 | |||
| 5d7a704ca5 | |||
| cee90a886e | |||
| 6b6d07745d | |||
| bf2a2f291f | |||
| 8368a0850a | |||
| 01a2c9fe12 | |||
| 6aefd24eb3 | |||
| 3241c2506b | |||
| 48d6ae1def | |||
| f2dc8c0442 | |||
| b174e2c9c6 | |||
| fb17c1b99f | |||
| e790e66f47 | |||
| c64e77a08c | |||
| cf53d834e9 | |||
| a2a4a4970e | |||
| 31fa36003f | |||
| cd1c41e448 | |||
| 54cc49bc70 | |||
| fe662c1a0a | |||
| fddd6110e0 | |||
| 2b63d07329 | |||
| b82b93f8f0 | |||
| 7129aebe76 | |||
| 2bcf822637 | |||
| 6658c0d386 | |||
| 0b93cb7ece | |||
| b51e2aa213 | |||
| d18d99e8ec | |||
| 9b8364b6c8 | |||
| 959419419f | |||
| 2988d84cfb | |||
| c0eb85ce53 | |||
| 56a4bf8f3c | |||
| 9cc332d6b3 | |||
| 120682ec8b | |||
| a67b3dc6a6 | |||
| f4c98f635b | |||
| 069bf6dec6 | |||
| a59bef1afe | |||
| d0f3696596 | |||
| c923bb5c10 | |||
| 9a93acc62a | |||
| 8770fc98e1 | |||
| 18433e289f | |||
| 168712ebbd | |||
| 44ae6bcc83 | |||
| 02790b105e | |||
| d88c08e074 | |||
| 9937cb48fd | |||
| 5444475b1d | |||
| 264a40f217 | |||
| a26f90a346 | |||
| ca9d04ba6b | |||
| 4e86a71ab2 | |||
| 3682888655 | |||
| 3adf720bc1 | |||
| d7a999eaf5 | |||
| 64333f69ea | |||
| b8a7720986 | |||
| 7c9415356d | |||
| c97aade33e | |||
| ab751d290a | |||
| f496d22074 | |||
| 86b680a3ad | |||
| 78dba217bf | |||
| 3b14b35252 | |||
| 878c762cdf | |||
| 920f2678ac | |||
| b6182e6fe8 | |||
| 41fe062a8d | |||
| 4c5f97bb9a | |||
| f9c24ab16d | |||
| 72b536b52d | |||
| 598c79ff86 | |||
| da66eb5714 | |||
| c290b4284f | |||
| fcb978248f | |||
| 5774edfe79 | |||
| 5c23187d2c | |||
| 0a7eb33772 | |||
| 9de075029b | |||
| d110cbb6e2 | |||
| 5f1f126613 | |||
| e40baf0bda | |||
| aa7d5df6a0 | |||
| 177d5fec86 | |||
| 0c0d79fd93 | |||
| 87ae2d7996 | |||
| b1acd1183f | |||
| 265aa54bc2 | |||
| 1e82db5655 | |||
| f2ca4cb3cd | |||
| cf784772c9 | |||
| 0567d528c6 | |||
| 36e3f4bc2a | |||
| 2a6c50832e | |||
| d759875db8 | |||
| 1510ee0b3c | |||
| 3729acc5b0 | |||
| 9fa5f37f74 | |||
| 0ddd2e0ebf | |||
| 7827faae4b | |||
| 245fe04289 | |||
| 6876a37e61 | |||
| 00c613b351 | |||
| 81fe380de2 | |||
| 179e8934cf | |||
| 8bfb6fdd4e | |||
| 89f81c89da | |||
| ad948d33c0 | |||
| 6fa55e7c38 | |||
| 2febbe5ffb | |||
| 578ecad93c | |||
| f5b0cd1793 | |||
| 3a0b4ad8da | |||
| 408f9b515e | |||
| f45dc46856 | |||
| b75780b9cd | |||
| 19614678e9 | |||
| 55ea097bce | |||
| 9d9037856c | |||
| 5087e9c2df | |||
| 178a0a3e1b | |||
| 05ef009552 | |||
| 316da00345 | |||
| f449df161a | |||
| 3e4ed83cb3 | |||
| f5f353d90a | |||
| bfde473c3b | |||
| c93bf732db | |||
| 7bcbea2108 | |||
| 997ac58329 | |||
| 085b61eea5 | |||
| 31fd83b2ae | |||
| 4b4c039fde | |||
| 0b3cf26406 | |||
| 12e01a5119 | |||
| ba6c88cb90 | |||
| e5815553cb | |||
| cba6e1b3ab | |||
| 0f864d9466 | |||
| 117f31125d | |||
| 510fde58d8 | |||
| 191fc1b4e8 | |||
| 41de5e78d0 | |||
| 5b6d042de2 | |||
| e30d41e5f0 | |||
| e1f01f0c8a | |||
| e56c6545fa | |||
| cb0627e6b2 | |||
| 6c0419db2b | |||
| 232cb3dd45 | |||
| 430fc8eb62 | |||
| e469b6ffb4 | |||
| a1068df602 | |||
| f819272b88 | |||
| 9fb188afbc | |||
| 70050b52ad | |||
| 44484fa4ae | |||
| 69b52196bd | |||
| f7cb7ef014 | |||
| b9cd5ca07f | |||
| 6017785e10 | |||
| 186fda098c | |||
| b6e1858ce6 | |||
| 7ab4f32c1a | |||
| 7113f2b8ba | |||
| c526c02d92 | |||
| f3ecba0445 | |||
| 06f42bc749 | |||
| 72da5bd062 | |||
| e56a76299c | |||
| c5af421cee | |||
| 91245141e9 | |||
| 2d67ff2249 | |||
| 0ef6973457 | |||
| 1aeaa794e1 | |||
| b932afb0cd | |||
| 9a142eb807 | |||
| 069d28b1ed | |||
| 301b1b7a7a | |||
| 113ed66f49 | |||
| a90c337297 | |||
| 071c1dcb64 | |||
| c4ed1c2764 | |||
| 4b1bdb563e | |||
| 0822108fc8 | |||
| 02064ae12a | |||
| 782caaa678 | |||
| ee849338dd | |||
| 7e2380ab0c | |||
| b0a950b6dd | |||
| dd11ed82d7 | |||
| bdc278399d | |||
| b71c4c0e52 | |||
| d0a185f2d9 | |||
| 0aa22e5149 | |||
| 8b29af629b | |||
| e5aac80cb4 | |||
| 4e014b4c5c | |||
| cebdb87387 | |||
| 8e41bdc25e | |||
| 28f4131d50 | |||
| f6e2840947 | |||
| c3ef56702a | |||
| 489385d565 | |||
| ce3317da7a | |||
| d6a9ef5bc1 | |||
| ac74b7dca1 | |||
| d81b07bd5f | |||
| 95a8f58314 | |||
| f3594ec881 | |||
| 7a186e242d | |||
| b2e16ffb12 | |||
| ac025ee6f2 | |||
| b1b3aba439 | |||
| 719b4d1183 | |||
| a098d1ec62 | |||
| ac548f2e95 | |||
| 6808865068 | |||
| 7a9fa52e91 | |||
| d523357ba8 | |||
| 933351ab4c | |||
| fadcc85553 | |||
| 7decd58aaa | |||
| c1439ddd5b | |||
| 603ec9cb19 | |||
| 485aae8134 | |||
| 039ac46963 | |||
| 4892b01eb4 | |||
| 03e2b0f0bf | |||
| 0124d292b5 | |||
| b8fbac72ac | |||
| e8c0e21f71 | |||
| a85a953e8e | |||
| c950fc05f0 | |||
| 0db99ccb4b | |||
| 6aab450c16 | |||
| c15d230a57 | |||
| f310d7bb12 | |||
| 67d6ca9f10 | |||
| 5b112c86bf | |||
| 57181c0a76 | |||
| 6314a96b05 | |||
| 4294ba6b52 | |||
| 519f3db6b6 | |||
| e7901f9d70 | |||
| 1030f041e3 | |||
| c76253cb0a | |||
| 33e82de475 | |||
| b475df2250 | |||
| 720741b725 | |||
| bc9f178b6d | |||
| 6cdead166b | |||
| 0542c65a07 | |||
| f4081a363b | |||
| a3ea6e7723 | |||
| 7d68112265 | |||
| ed34b04e9b | |||
| 96a690f607 | |||
| 3c4057ff55 | |||
| ff67860cdb | |||
| 87b5e2500b | |||
| 56df6e7ba8 | |||
| c0b346e0b8 | |||
| 635dfa2eae | |||
| 6ffd5ffd0c | |||
| e11bcf9c43 | |||
| adbd174959 | |||
| 1c34f89c19 | |||
| bbfd089b7e | |||
| 1e679e6f84 | |||
| 61cdee97fc | |||
| 7d767fac7f | |||
| 9426c67302 | |||
| a664d6389e | |||
| 7c159e3881 | |||
| 70bfc5b4a8 | |||
| 940ca9311d | |||
| b2de1d7438 | |||
| 1a2dd3680f | |||
| 68cc1e775c | |||
| a1e8d3c3dc | |||
| ae25fa994c | |||
| 009a5007ff | |||
| cb30f7733d | |||
| eec63dbefe | |||
| 218eeae797 | |||
| 8b2d308498 | |||
| 26a5811744 | |||
| 0d4e1abbd1 | |||
| 5b019ab48c | |||
| f2b5153a8c | |||
| 6fd89f5fe0 | |||
| 0b304731c5 | |||
| 63da351038 | |||
| cad02bb1b0 | |||
| f61c933a7a | |||
| 28f66ddd7c | |||
| a1e45ab56c | |||
| f664e39374 | |||
| e7733ce7d9 | |||
| e1ba499bd6 | |||
| fd885d0818 | |||
| 67c9de6461 | |||
| ca0dd09944 | |||
| b703b4f022 | |||
| f30c9d49f7 | |||
| af5457ba09 | |||
| a31154c035 | |||
| b2a2e0f135 | |||
| 85eb8f022c | |||
| 589bc73838 | |||
| 649d3b753b | |||
| c377708da1 | |||
| 326e8da06d | |||
| 44a9ceac0b | |||
| 5c1168c33f | |||
| dd458c637e | |||
| 2f3f6f8b71 | |||
| 11326692f2 | |||
| 9d144bd495 | |||
| a284722fd0 | |||
| c0a89e328a | |||
| 99fd621aed | |||
| 5183f7724d | |||
| 0c57044c59 | |||
| 5e2c105396 | |||
| 0ce3a3665a | |||
| b0e72ece43 | |||
| 8b5e6e4a13 | |||
| 43bfa0efbc | |||
| ca298c7c39 | |||
| 48d8498dbb | |||
| 19bec62eb5 | |||
| 5e5a8bbdcf | |||
| 7c2ccb2cb7 | |||
| 435e5e6263 | |||
| e564f9ae31 | |||
| 68cf45a8b9 | |||
| 94b23482a9 | |||
| 1e968f5487 | |||
| 14e771d871 | |||
| 56ef607904 | |||
| 17fe272dc5 | |||
| d5d906184f | |||
| 3b2c2b5c04 | |||
| 39cfee239d | |||
| 1f02567507 | |||
| 4f5d54f062 | |||
| fc2b71a04e | |||
| 87b964a7f6 | |||
| 4cf83ca3a7 | |||
| 3d9997288d | |||
| 527472f18c | |||
| d2b8569452 | |||
| 3482ee5c75 | |||
| 3029e84faf | |||
| 044e55b98e | |||
| 60a09285b7 | |||
| ba47fd4649 | |||
| 2de2d00f14 | |||
| 7b8c01da12 | |||
| 9021ba01ac | |||
| b8db0a8ed6 | |||
| 2a68591181 | |||
| 94ac777f93 | |||
| 0ea6c1dcf6 | |||
| 2ce6d49a40 | |||
| c7476875e6 | |||
| 22ee16fd9d | |||
| c68ac1d5eb | |||
| a23c76fb82 | |||
| fd4f5c0996 | |||
| b080b8f54d | |||
| 5a8dea1591 | |||
| a849b384f8 | |||
| fedfe5427c | |||
| 1176e1f096 | |||
| 22c25e4413 | |||
| 557820707b | |||
| 0078a3b904 | |||
| 65d352d5de | |||
| 64997d9961 | |||
| ce42b6be01 | |||
| 7606be93d5 | |||
| e2665c5da9 | |||
| 32d548d99b | |||
| 25ddb6c69d | |||
| 0baaad30b1 | |||
| 31491ebe16 | |||
| b10e10efae | |||
| 02f96d0f41 | |||
| 2129a33077 | |||
| 5cfe57ece5 | |||
| 03667d0c73 | |||
| f2acf57412 | |||
| efccee55e3 | |||
| 6ff81946cf | |||
| 08d482b064 | |||
| dd5389df23 | |||
| e01bece552 | |||
| 3d0d9cb354 | |||
| afc7f5aa37 | |||
| f14a966680 | |||
| 6c564775c4 | |||
| 1e55e018e5 | |||
| 9605e2b2a4 | |||
| a910ab8171 | |||
| 32e5781a6a | |||
| f5290a8e44 | |||
| 5fdd1c39fe | |||
| 769975f6d7 | |||
| 1bda40dac7 | |||
| 5ef60bf594 | |||
| 666608544b | |||
| 3dd2a614ac | |||
| 50682b98d6 | |||
| cf87c5664f | |||
| 247ca872f6 | |||
| e7fe4dc474 | |||
| c6b5dd6c4f | |||
| 309bdfbe28 | |||
| ed679279fd | |||
| fad7612cf1 | |||
| df92d96d4f | |||
| 7ef8dfb4e0 | |||
| b30b70db70 | |||
| 08f73a5aeb | |||
| 3811a61573 | |||
| cc14e223c6 | |||
| 7f653cfe76 | |||
| 5cc8e807df | |||
| 1a4f63f058 | |||
| bcc04d93e1 | |||
| 2976c2d921 | |||
| a50247c20d | |||
| fdf69dcd0d | |||
| a3b12fd745 | |||
| 8a7ea85a7e | |||
| a681874f67 | |||
| 1c8edd07a1 | |||
| 1883a98d1c | |||
| 0c2491d9ea | |||
| 23f7075313 | |||
| 23786c95c9 | |||
| 847376924e | |||
| d7e1f6d7b3 | |||
| 34f2a8a4f2 | |||
| 68cf6845e1 | |||
| 294d58a393 | |||
| 732eecac43 | |||
| a3c134c43b | |||
| 309fd56fb4 | |||
| 8618af4e34 | |||
| 32f484a349 | |||
| 92db9ff105 | |||
| 95a4b1b266 | |||
| 2cd70280d2 | |||
| fc7e52df71 | |||
| 56b30ab598 | |||
| 3ac73f9607 | |||
| 18609befa4 | |||
| ae297a4ae6 | |||
| 9432a16893 | |||
| ca1b22bdd4 | |||
| 4d6eb31264 | |||
| e1539e5769 | |||
| 993cbcb133 | |||
| d554827ebc | |||
| 0f11b1be36 | |||
| 28d0a1b9d2 | |||
| 49f1275e20 | |||
| b14843d098 | |||
| 8f692f51d8 | |||
| 560038c808 | |||
| d12210f4e1 | |||
| 9c3ebb7d22 | |||
| b6ddcf3e58 | |||
| cbda2e038e | |||
| 6a4f2a78ff | |||
| 12683b4aaf | |||
| 5d434c1aea | |||
| d364eab74b | |||
| 6f2f1e87c9 | |||
| 3325e69ae6 | |||
| 114ca786ee | |||
| ee8cc1dac2 | |||
| 9ab169bc63 | |||
| 24ef90c556 | |||
| c0e7b298db | |||
| cc63bcc997 | |||
| a2fb493f91 | |||
| ebadcf71c2 | |||
| fbfbb96872 | |||
| 577eeb642f | |||
| 7ffab38b44 | |||
| 2786df651a | |||
| c0b5c6e9d4 | |||
| 3189bb3bb9 | |||
| 404a7c3381 | |||
| 3908a80ac9 | |||
| 1a02a2a51d | |||
| b8e8e4b971 | |||
| 04fdf69737 | |||
| e57de02e0f | |||
| c5e7b4738f | |||
| e6365979bd | |||
| 274303f248 | |||
| 507e756b69 | |||
| 3f91668c46 | |||
| c7ae916afc | |||
| 049a6d97f1 | |||
| 13c6039700 | |||
| de7a143a2c | |||
| 9aff7e52a8 | |||
| d00a634025 | |||
| daab95e3b5 | |||
| 45e77ea483 | |||
| e69937d93a | |||
| f57f267c54 | |||
| b8dcc911a3 | |||
| 995c97671d | |||
| 1c90b19d74 | |||
| 8ced3bdbe9 | |||
| 3cd174cb56 | |||
| e69ddd981f | |||
| b1723c6e2d | |||
| 5a01b5f1fc | |||
| 2929f5b5bc | |||
| 4d4ad922a2 | |||
| 8df68266f2 | |||
| 2b93de6912 | |||
| 909e0eb5dd | |||
| e75dd17e2c | |||
| 41794c57d6 | |||
| 5ffc01db53 | |||
| 717159a98f | |||
| 309962fb8b | |||
| 3765210698 | |||
| 13d5da4da6 | |||
| 75c15d3853 | |||
| ebefd0d8d6 | |||
| 7e9f1a6dc1 | |||
| 132033d01a | |||
| 21d78ed7f4 | |||
| b8f22ff538 | |||
| 534e73f732 | |||
| de81c50199 | |||
| 5d552c8463 | |||
| 78a44e0176 | |||
| 566ac872fe | |||
| 42bede77a1 | |||
| 35506f5470 | |||
| e841a6ec34 | |||
| e9dd3ffe9c | |||
| 56c91d3c58 | |||
| bd060bc1bb | |||
| 49d3b7ec1d | |||
| 5e723bc90e | |||
| bab71f0832 | |||
| 62db030942 | |||
| 2a63d0e95a | |||
| 149cc9654f | |||
| 9d977e484a | |||
| f6236d456d | |||
| ed6bc9081b | |||
| cdafd3254b | |||
| 509776a0d1 | |||
| 2ab72bcd00 | |||
| 0212559ca7 | |||
| 1bdbe54442 | |||
| a5e737157c | |||
| 0a3fcc9ade | |||
| 7621564839 | |||
| 686305bb21 | |||
| c3b62d2f75 | |||
| f56840a682 | |||
| 059a806bb0 | |||
| 3ec68a4ecf | |||
| 3b876875e9 | |||
| d19552f464 | |||
| 4c5460f0bd | |||
| 90acec8a2b | |||
| 305d4c05dc | |||
| 67720c7713 | |||
| fd6f0f94b5 | |||
| a1faecc4c9 | |||
| d97e777c9b | |||
| b693601dd1 | |||
| 4935ac8866 | |||
| ead7e21037 | |||
| 0f67a8f98b | |||
| a915fd161e | |||
| dffcfe74d4 | |||
| 5c48263579 | |||
| 6a3691ef7c | |||
| d701230555 | |||
| e54ba3db5b | |||
| 0ae84a646f | |||
| b01fbfadf3 | |||
| 438dbc8bda | |||
| 8accb8ee0c | |||
| a78c3422cd | |||
| 695d22ef95 | |||
| 98e0123ca4 | |||
| 9cee3760db | |||
| 1de0bb83a0 | |||
| 7566e267a7 | |||
| 15ebb79160 | |||
| 767dba8f3b | |||
| a42bf67957 | |||
| 6f538545b4 | |||
| d4b4f35a0e | |||
| acab2270f1 | |||
| ac6f701033 | |||
| d327119cf7 | |||
| 3112a7187f | |||
| 7100c67be6 | |||
| fa31e3ef23 | |||
| 58969fb854 | |||
| 53209b9ab1 | |||
| ceda073766 | |||
| a4ec064455 | |||
| e7787e2f33 | |||
| 5bcfcf4c5e | |||
| 9fa92092bf | |||
| f2cc81dfea | |||
| 8244c1fa8c | |||
| ed62d705d8 | |||
| da291d804c | |||
| 662b6f1020 | |||
| 27999a122f | |||
| 151668ac10 | |||
| e987b88848 | |||
| c29a48695d | |||
| 5ec972b00f | |||
| 45eb49125b | |||
| 31faa4eb9a | |||
| 522a186a38 | |||
| 4ff8d7918a | |||
| b487ccfb34 | |||
| ca217dc105 | |||
| 8b81b9ecb1 | |||
| cbdc106427 | |||
| bc33a3873d | |||
| abb4446b51 | |||
| a417fbf45b | |||
| 5d06c6acbf | |||
| c0272ae766 | |||
| e258462b6b | |||
| 0bcbd12776 | |||
| eb1d385d4e | |||
| 5cd1cf5096 | |||
| 792e6a7c1c | |||
| d2d3b961eb | |||
| f19d2fdcff | |||
| a093e9d441 | |||
| 9bf1bac7df | |||
| f91f2bc3d2 | |||
| b31ebd2ea0 | |||
| 96e912b09a | |||
| 8275e95a16 | |||
| 1097d31d63 | |||
| c409ed2f2c | |||
| 23858469b7 | |||
| c26d38a893 | |||
| da310a5173 | |||
| 179fcd9521 | |||
| 5d90292849 | |||
| bc802a4049 | |||
| a4a9dd7f03 | |||
| 9a8e197d7e |
@@ -0,0 +1,3 @@
|
||||
.git
|
||||
.win32
|
||||
.examples
|
||||
@@ -29,6 +29,7 @@
|
||||
/doc/version.tex
|
||||
/ebin/
|
||||
/ejabberd.init
|
||||
/ejabberd.service
|
||||
/ejabberdctl.example
|
||||
XmppAddr.hrl
|
||||
/rel/ejabberd/
|
||||
|
||||
+3
-1
@@ -3,6 +3,7 @@ language: erlang
|
||||
otp_release:
|
||||
- 17.5
|
||||
- 18.3
|
||||
- 19.2
|
||||
|
||||
services:
|
||||
- riak
|
||||
@@ -20,7 +21,8 @@ before_install:
|
||||
- sudo apt-key adv --import .travis/mysql_repo_key.asc
|
||||
- sudo add-apt-repository 'deb http://repo.mysql.com/apt/ubuntu/ precise mysql-5.6'
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get -qq -o Dpkg::Options::=--force-confold install mysql-server-5.6
|
||||
- sudo apt-get -qq -o Dpkg::Options::=--force-confold install mysql-server
|
||||
- sudo mysql_upgrade
|
||||
# /END MYSQL 5.6
|
||||
- pip install --user coveralls-merge
|
||||
|
||||
|
||||
+420
-84
@@ -1,5 +1,5 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1.4.9 (SunOS)
|
||||
Version: GnuPG v1.4.5 (GNU/Linux)
|
||||
|
||||
mQGiBD4+owwRBAC14GIfUfCyEDSIePvEW3SAFUdJBtoQHH/nJKZyQT7h9bPlUWC3
|
||||
RODjQReyCITRrdwyrKUGku2FmeVGwn2u2WmDMNABLnpprWPkBdCk96+OmSLN9brZ
|
||||
@@ -10,87 +10,423 @@ K2HtkrPbP72vwsEKMYhhr0eKCbtLGfls9krjJ6sBgACyP/Vb7hiPwxh6rDZ7ITnE
|
||||
kYpXBACmWpP8NJTkamEnPCia2ZoOHODANwpUkP43I7jsDmgtobZX9qnrAXw+uNDI
|
||||
QJEXM6FSbi0LLtZciNlYsafwAPEOMDKpMqAK6IyisNtPvaLd8lH0bPAnWqcyefep
|
||||
rv0sxxqUEMcM3o7wwgfN83POkDasDbs3pjwPhxvhz6//62zQJ7Q2TXlTUUwgUmVs
|
||||
ZWFzZSBFbmdpbmVlcmluZyA8bXlzcWwtYnVpbGRAb3NzLm9yYWNsZS5jb20+iGkE
|
||||
ExECACkCGyMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAIZAQUCUwHUZgUJGmbLywAK
|
||||
CRCMcY07UHLh9V+DAKCjS1gGwgVI/eut+5L+l2v3ybl+ZgCcD7ZoA341HtoroV3U
|
||||
6xRD09fUgeq0O015U1FMIFBhY2thZ2Ugc2lnbmluZyBrZXkgKHd3dy5teXNxbC5j
|
||||
b20pIDxidWlsZEBteXNxbC5jb20+iG8EMBECAC8FAk53Pa0oHSBidWlsZEBteXNx
|
||||
bC5jb20gd2lsbCBzdG9wIHdvcmtpbmcgc29vbgAKCRCMcY07UHLh9bU9AJ9xDK0o
|
||||
xJFL9vTl9OSZC4lX0K9AzwCcCrS9cnJyz79eaRjL0s2r/CcljdyIZQQTEQIAHQUC
|
||||
R6yUtAUJDTBYqAULBwoDBAMVAwIDFgIBAheAABIJEIxxjTtQcuH1B2VHUEcAAQGu
|
||||
kgCffz4GUEjzXkOi71VcwgCxASTgbe0An34LPr1j9fCbrXWXO14msIADfb5piEwE
|
||||
ExECAAwFAj4+o9EFgwlmALsACgkQSVDhKrJykfIk4QCfWbEeKN+3TRspe+5xKj+k
|
||||
QJSammIAnjUz0xFWPlVx0f8o38qNG1bq0cU9iEwEExECAAwFAj5CggMFgwliIokA
|
||||
CgkQtvXNTca6JD+WkQCgiGmnoGjMojynp5ppvMXkyUkfnykAoK79E6h8rwkSDZou
|
||||
iz7nMRisH8uyiEYEEBECAAYFAj+s468ACgkQr8UjSHiDdA/2lgCg21IhIMMABTYd
|
||||
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
|
||||
AcOphrnJ
|
||||
=443I
|
||||
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
|
||||
XDR51P1TEw9mC1btHj+7m7Iq1a5ke5wIC7ENZiilr0yPqeWgL5+LC98dz/L85hqA
|
||||
wIoGeOfMhrlaVbAZEj4yQTAJDA35vZHVsQmp87il0m+fZX04OBLXBzw86EoAAZ7Q
|
||||
EoH4qFcT9k1T363tvNnIm3mEvkQ5WjE1R9uchJa1g7hdlNQlVkjFmPZrJK9fl4z5
|
||||
6Dto89Po4Sge48jDH0pias4HATYHsxW819nz5jZzGcxLnFRRR5iITVZi9qzsHP7N
|
||||
bUh3qxuWCHS9xziXpOcSZY848xXw63Y5jDJfpzupzu/KHj6CzXYJUEEqp9MluoGb
|
||||
/BCCEPzdZ0ovyxFutM/BRcc6DvE6sTDF/UES21ROqfuwtJ6qJYWX+lBIgyCJvj4o
|
||||
RdbzxUleePuzqCzmwrIXtoOKW0Rlj4SCeF9yCwUMBTGW5/nCLmN4dwf1KW2RP2Eg
|
||||
4ERbuUy7QnwRP5UCl+0ISZJyYUISfg8fmPIdQsetUK9Cj+Q5jpB2GXwELXWnIK6h
|
||||
K/6jXp+EGEXSqdIE53vAFe7LwfHiP/D5M71D2h62sdIOmUm3lm7xMOnM5tKlBiV+
|
||||
4jJSUmriCT62zo710+6iLGqmUUYlEll6Ppvo8yuanXkYRCFJpSSP7VP0bBqIZgQT
|
||||
EQIAJgUCTnc9dgIbIwUJEPPzpwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEIxx
|
||||
jTtQcuH1Ut4AoIKjhdf70899d+7JFq3LD7zeeyI0AJ9Z+YyE1HZSnzYi73brScil
|
||||
bIV6sbQ7TXlTUUwgUGFja2FnZSBzaWduaW5nIGtleSAod3d3Lm15c3FsLmNvbSkg
|
||||
PGJ1aWxkQG15c3FsLmNvbT6IbwQwEQIALwUCTnc9rSgdIGJ1aWxkQG15c3FsLmNv
|
||||
bSB3aWxsIHN0b3Agd29ya2luZyBzb29uAAoJEIxxjTtQcuH1tT0An3EMrSjEkUv2
|
||||
9OX05JkLiVfQr0DPAJwKtL1ycnLPv15pGMvSzav8JyWN3IhlBBMRAgAdBQJHrJS0
|
||||
BQkNMFioBQsHCgMEAxUDAgMWAgECF4AAEgkQjHGNO1By4fUHZUdQRwABAa6SAJ9/
|
||||
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
|
||||
roUTAKClYAhZuX2nUNwH4vlEJQHDqYa5yQ==
|
||||
=HfUN
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
FROM debian:jessie-slim
|
||||
MAINTAINER Rafael Römhild <rafael@roemhild.de>
|
||||
|
||||
ENV EJABBERD_BRANCH=17.08 \
|
||||
EJABBERD_USER=ejabberd \
|
||||
EJABBERD_HTTPS=true \
|
||||
EJABBERD_STARTTLS=true \
|
||||
EJABBERD_S2S_SSL=true \
|
||||
EJABBERD_HOME=/opt/ejabberd \
|
||||
EJABBERD_DEBUG_MODE=false \
|
||||
HOME=$EJABBERD_HOME \
|
||||
PATH=$EJABBERD_HOME/bin:/usr/sbin:/usr/bin:/sbin:/bin \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
XMPP_DOMAIN=localhost \
|
||||
# Set default locale for the environment
|
||||
LC_ALL=C.UTF-8 \
|
||||
LANG=en_US.UTF-8 \
|
||||
LANGUAGE=en_US.UTF-8
|
||||
|
||||
# Add ejabberd user and group
|
||||
RUN groupadd -r $EJABBERD_USER \
|
||||
&& useradd -r -m \
|
||||
-g $EJABBERD_USER \
|
||||
-d $EJABBERD_HOME \
|
||||
$EJABBERD_USER
|
||||
|
||||
# Install packages and perform cleanup
|
||||
RUN set -x \
|
||||
&& buildDeps=' \
|
||||
git-core \
|
||||
build-essential \
|
||||
automake \
|
||||
libssl-dev \
|
||||
zlib1g-dev \
|
||||
libexpat-dev \
|
||||
libyaml-dev \
|
||||
libsqlite3-dev \
|
||||
erlang-src erlang-dev \
|
||||
' \
|
||||
&& requiredAptPackages=' \
|
||||
locales \
|
||||
ldnsutils \
|
||||
python2.7 \
|
||||
python-jinja2 \
|
||||
ca-certificates \
|
||||
libyaml-0-2 \
|
||||
erlang-base erlang-snmp erlang-ssl erlang-ssh erlang-webtool \
|
||||
erlang-tools erlang-xmerl erlang-corba erlang-diameter erlang-eldap \
|
||||
erlang-eunit erlang-ic erlang-odbc erlang-os-mon \
|
||||
erlang-parsetools erlang-percept erlang-typer erlang-inets \
|
||||
python-mysqldb \
|
||||
imagemagick \
|
||||
' \
|
||||
&& apt-key adv \
|
||||
--keyserver keys.gnupg.net \
|
||||
--recv-keys 434975BD900CCBE4F7EE1B1ED208507CA14F4FCA \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y $buildDeps $requiredAptPackages --no-install-recommends \
|
||||
&& dpkg-reconfigure locales && \
|
||||
locale-gen C.UTF-8 \
|
||||
&& /usr/sbin/update-locale LANG=C.UTF-8 \
|
||||
&& echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen \
|
||||
&& locale-gen \
|
||||
&& cd /tmp \
|
||||
&& git clone https://github.com/processone/ejabberd.git \
|
||||
--branch $EJABBERD_BRANCH --single-branch --depth=1 \
|
||||
&& cd ejabberd \
|
||||
&& chmod +x ./autogen.sh \
|
||||
&& ./autogen.sh \
|
||||
&& ./configure --enable-user=$EJABBERD_USER \
|
||||
--prefix=/ \
|
||||
--enable-all \
|
||||
--disable-tools \
|
||||
--disable-pam \
|
||||
&& make debug=$EJABBERD_DEBUG_MODE \
|
||||
&& make install \
|
||||
&& mkdir $EJABBERD_HOME/ssl \
|
||||
&& mkdir $EJABBERD_HOME/conf \
|
||||
&& mkdir $EJABBERD_HOME/backup \
|
||||
&& mkdir $EJABBERD_HOME/upload \
|
||||
&& mkdir $EJABBERD_HOME/database \
|
||||
&& mkdir $EJABBERD_HOME/module_source \
|
||||
&& cd $EJABBERD_HOME \
|
||||
&& rm -rf /tmp/ejabberd \
|
||||
&& rm -rf /etc/ejabberd \
|
||||
&& ln -sf $EJABBERD_HOME/conf /etc/ejabberd \
|
||||
&& chown -R $EJABBERD_USER: $EJABBERD_HOME \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get purge -y --auto-remove $buildDeps
|
||||
|
||||
# Wrapper for setting config on disk from environment
|
||||
# allows setting things like XMPP domain at runtime
|
||||
ADD ./docker/run.sh /sbin/run
|
||||
|
||||
# Add run scripts
|
||||
ADD ./docker/scripts $EJABBERD_HOME/scripts
|
||||
ADD https://raw.githubusercontent.com/rankenstein/ejabberd-auth-mysql/master/auth_mysql.py $EJABBERD_HOME/scripts/lib/auth_mysql.py
|
||||
RUN chmod a+rx $EJABBERD_HOME/scripts/lib/auth_mysql.py
|
||||
|
||||
# Add config templates
|
||||
ADD ./docker/conf /opt/ejabberd/conf
|
||||
|
||||
# Continue as user
|
||||
USER $EJABBERD_USER
|
||||
|
||||
# Set workdir to ejabberd root
|
||||
WORKDIR $EJABBERD_HOME
|
||||
|
||||
VOLUME ["$EJABBERD_HOME/database", "$EJABBERD_HOME/ssl", "$EJABBERD_HOME/backup", "$EJABBERD_HOME/upload"]
|
||||
EXPOSE 4560 5222 5269 5280 5443
|
||||
|
||||
CMD ["start"]
|
||||
ENTRYPOINT ["run"]
|
||||
+21
-20
@@ -108,10 +108,6 @@ edoc:
|
||||
$(ERL) -noinput +B -eval \
|
||||
'case edoc:application(ejabberd, ".", []) of ok -> halt(0); error -> halt(1) end.'
|
||||
|
||||
spec:
|
||||
$(ERL) -noinput +B -pa ebin -pa deps/*/ebin -eval \
|
||||
'case fxml_gen:compile("tools/xmpp_codec.spec") of ok -> halt(0); _ -> halt(1) end.'
|
||||
|
||||
JOIN_PATHS=$(if $(wordlist 2,1000,$(1)),$(firstword $(1))/$(call JOIN_PATHS,$(wordlist 2,1000,$(1))),$(1))
|
||||
|
||||
VERSIONED_DEP=$(if $(DEP_$(1)_VERSION),$(DEP_$(1)_VERSION),$(1))
|
||||
@@ -129,11 +125,11 @@ ifeq ($(MAKECMDGOALS),copy-files-sub)
|
||||
|
||||
DEPS:=$(sort $(shell $(REBAR) list-deps|$(SED) -e '/^=/d;s/ .*//'))
|
||||
|
||||
DEPS_FILES=$(call FILES_WILDCARD,$(foreach DEP,$(DEPS),deps/$(DEP)/ebin/*.beam deps/$(DEP)/ebin/*.app deps/$(DEP)/priv/* deps/$(DEP)/priv/lib/* deps/$(DEP)/priv/bin/* deps/$(DEP)/include/*.hrl deps/$(DEP)/lib/*/ebin/*.beam deps/$(DEP)/lib/*/ebin/*.app))
|
||||
DEPS_FILES=$(call FILES_WILDCARD,$(foreach DEP,$(DEPS),deps/$(DEP)/ebin/*.beam deps/$(DEP)/ebin/*.app deps/$(DEP)/priv/* deps/$(DEP)/priv/lib/* deps/$(DEP)/priv/bin/* deps/$(DEP)/include/*.hrl deps/$(DEP)/COPY* deps/$(DEP)/LICENSE* deps/$(DEP)/lib/*/ebin/*.beam deps/$(DEP)/lib/*/ebin/*.app))
|
||||
DEPS_FILES_FILTERED=$(filter-out %/epam deps/elixir/ebin/elixir.app,$(DEPS_FILES))
|
||||
DEPS_DIRS=$(sort deps/ $(foreach DEP,$(DEPS),deps/$(DEP)/) $(dir $(DEPS_FILES)))
|
||||
|
||||
MAIN_FILES=$(filter-out %/configure.beam,$(call FILES_WILDCARD,ebin/*.beam ebin/*.app priv/msgs/*.msg priv/lib/* include/*.hrl))
|
||||
MAIN_FILES=$(filter-out %/configure.beam,$(call FILES_WILDCARD,ebin/*.beam ebin/*.app priv/msgs/*.msg priv/lib/* include/*.hrl COPYING))
|
||||
MAIN_DIRS=$(sort $(dir $(MAIN_FILES)) priv/bin priv/sql)
|
||||
|
||||
define DEP_VERSION_template
|
||||
@@ -152,7 +148,7 @@ $(foreach file,$(DEPS_FILES_FILTERED) $(MAIN_FILES),$(eval $(call COPY_template,
|
||||
$(sort $(call TO_DEST,$(MAIN_DIRS) $(DEPS_DIRS))):
|
||||
$(INSTALL) -d $@
|
||||
|
||||
$(call TO_DEST,deps/p1_pam/priv/bin/epam): $(LIBDIR)/%: deps/p1_pam/priv/bin/epam $(call TO_DEST,deps/p1_pam/priv/bin/)
|
||||
$(call TO_DEST,deps/epam/priv/bin/epam): $(LIBDIR)/%: deps/epam/priv/bin/epam $(call TO_DEST,deps/epam/priv/bin/)
|
||||
$(INSTALL) -m 750 $(O_USER) $< $@
|
||||
|
||||
$(call TO_DEST,priv/sql/lite.sql): sql/lite.sql $(call TO_DEST,priv/sql)
|
||||
@@ -177,15 +173,15 @@ install: all copy-files
|
||||
[ -f $(ETCDIR)/ejabberd.yml ] \
|
||||
&& $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml-new \
|
||||
|| $(INSTALL) -b -m 640 $(G_USER) ejabberd.yml.example $(ETCDIR)/ejabberd.yml
|
||||
$(SED) -e "s*{{rootdir}}*@prefix@*" \
|
||||
-e "s*{{installuser}}*@INSTALLUSER@*" \
|
||||
-e "s*{{bindir}}*@bindir@*" \
|
||||
-e "s*{{libdir}}*@libdir@*" \
|
||||
-e "s*{{sysconfdir}}*@sysconfdir@*" \
|
||||
-e "s*{{localstatedir}}*@localstatedir@*" \
|
||||
-e "s*{{docdir}}*@docdir@*" \
|
||||
-e "s*{{erl}}*@ERL@*" \
|
||||
-e "s*{{epmd}}*@EPMD@*" ejabberdctl.template \
|
||||
$(SED) -e "s*{{rootdir}}*@prefix@*g" \
|
||||
-e "s*{{installuser}}*@INSTALLUSER@*g" \
|
||||
-e "s*{{bindir}}*@bindir@*g" \
|
||||
-e "s*{{libdir}}*@libdir@*g" \
|
||||
-e "s*{{sysconfdir}}*@sysconfdir@*g" \
|
||||
-e "s*{{localstatedir}}*@localstatedir@*g" \
|
||||
-e "s*{{docdir}}*@docdir@*g" \
|
||||
-e "s*{{erl}}*@ERL@*g" \
|
||||
-e "s*{{epmd}}*@EPMD@*g" ejabberdctl.template \
|
||||
> ejabberdctl.example
|
||||
[ -f $(ETCDIR)/ejabberdctl.cfg ] \
|
||||
&& $(INSTALL) -b -m 640 $(G_USER) ejabberdctl.cfg.example $(ETCDIR)/ejabberdctl.cfg-new \
|
||||
@@ -202,11 +198,16 @@ install: all copy-files
|
||||
[ -f deps/elixir/bin/mix ] && $(INSTALL) -m 550 $(G_USER) deps/elixir/bin/mix $(BINDIR)/mix || true
|
||||
#
|
||||
# Init script
|
||||
$(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*" \
|
||||
-e "s*@installuser@*$(INIT_USER)*" ejabberd.init.template \
|
||||
$(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*g" \
|
||||
-e "s*@installuser@*$(INIT_USER)*g" ejabberd.init.template \
|
||||
> ejabberd.init
|
||||
chmod 755 ejabberd.init
|
||||
#
|
||||
# Service script
|
||||
$(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*g" ejabberd.service.template \
|
||||
> ejabberd.service
|
||||
chmod 644 ejabberd.service
|
||||
#
|
||||
# Spool directory
|
||||
$(INSTALL) -d -m 750 $(O_USER) $(SPOOLDIR)
|
||||
$(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT)
|
||||
@@ -294,7 +295,7 @@ dialyzer/erlang.plt:
|
||||
@mkdir -p dialyzer
|
||||
@dialyzer --build_plt --output_plt dialyzer/erlang.plt \
|
||||
-o dialyzer/erlang.log --apps kernel stdlib sasl crypto \
|
||||
public_key ssl mnesia inets odbc tools compiler erts webtool \
|
||||
public_key ssl mnesia inets odbc tools compiler erts \
|
||||
runtime_tools asn1 observer xmerl et gs wx syntax_tools; \
|
||||
status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi
|
||||
|
||||
@@ -338,5 +339,5 @@ quicktest:
|
||||
$(REBAR) skip_deps=true ct suites=elixir
|
||||
|
||||
.PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean rel \
|
||||
install uninstall uninstall-binary uninstall-all translations deps test spec \
|
||||
install uninstall uninstall-binary uninstall-all translations deps test \
|
||||
quicktest erlang_plt deps_plt ejabberd_plt
|
||||
|
||||
@@ -108,7 +108,7 @@ To compile ejabberd you need:
|
||||
- GCC.
|
||||
- Libexpat 1.95 or higher.
|
||||
- Libyaml 0.1.4 or higher.
|
||||
- Erlang/OTP 17.1 or higher.
|
||||
- Erlang/OTP 17.5 or higher.
|
||||
- OpenSSL 1.0.0 or higher, for STARTTLS, SASL and SSL encryption.
|
||||
- Zlib 1.2.3 or higher, for Stream Compression support (XEP-0138). Optional.
|
||||
- PAM library. Optional. For Pluggable Authentication Modules (PAM).
|
||||
@@ -116,11 +116,14 @@ To compile ejabberd you need:
|
||||
needed on systems with GNU Libc.
|
||||
- ImageMagick's Convert program. Optional. For CAPTCHA challenges.
|
||||
|
||||
If your system splits packages in libraries and development headers, you must
|
||||
install the development packages also.
|
||||
|
||||
### 1. Compile and install on *nix systems
|
||||
|
||||
To compile ejabberd, execute the following commands. The first one is only
|
||||
necessary if your source tree didn't come with a `configure` script.
|
||||
necessary if your source tree didn't come with a `configure` script (In this
|
||||
case you need autoconf installed).
|
||||
|
||||
./autogen.sh
|
||||
./configure
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
-- LDAPv3 ASN.1 specification, taken from RFC 2251
|
||||
|
||||
-- Lightweight-Directory-Access-Protocol-V3 DEFINITIONS
|
||||
ELDAPv3 DEFINITIONS
|
||||
IMPLICIT TAGS ::=
|
||||
|
||||
BEGIN
|
||||
|
||||
LDAPMessage ::= SEQUENCE {
|
||||
messageID MessageID,
|
||||
protocolOp CHOICE {
|
||||
bindRequest BindRequest,
|
||||
bindResponse BindResponse,
|
||||
unbindRequest UnbindRequest,
|
||||
searchRequest SearchRequest,
|
||||
searchResEntry SearchResultEntry,
|
||||
searchResDone SearchResultDone,
|
||||
searchResRef SearchResultReference,
|
||||
modifyRequest ModifyRequest,
|
||||
modifyResponse ModifyResponse,
|
||||
addRequest AddRequest,
|
||||
addResponse AddResponse,
|
||||
delRequest DelRequest,
|
||||
delResponse DelResponse,
|
||||
modDNRequest ModifyDNRequest,
|
||||
modDNResponse ModifyDNResponse,
|
||||
compareRequest CompareRequest,
|
||||
compareResponse CompareResponse,
|
||||
abandonRequest AbandonRequest,
|
||||
extendedReq ExtendedRequest,
|
||||
extendedResp ExtendedResponse },
|
||||
controls [0] Controls OPTIONAL }
|
||||
|
||||
MessageID ::= INTEGER (0 .. maxInt)
|
||||
|
||||
maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
|
||||
|
||||
LDAPString ::= OCTET STRING
|
||||
|
||||
LDAPOID ::= OCTET STRING
|
||||
|
||||
LDAPDN ::= LDAPString
|
||||
|
||||
RelativeLDAPDN ::= LDAPString
|
||||
|
||||
AttributeType ::= LDAPString
|
||||
|
||||
AttributeDescription ::= LDAPString
|
||||
|
||||
|
||||
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 44]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
AttributeDescriptionList ::= SEQUENCE OF
|
||||
AttributeDescription
|
||||
|
||||
AttributeValue ::= OCTET STRING
|
||||
|
||||
AttributeValueAssertion ::= SEQUENCE {
|
||||
attributeDesc AttributeDescription,
|
||||
assertionValue AssertionValue }
|
||||
|
||||
AssertionValue ::= OCTET STRING
|
||||
|
||||
Attribute ::= SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
MatchingRuleId ::= LDAPString
|
||||
|
||||
LDAPResult ::= SEQUENCE {
|
||||
resultCode ENUMERATED {
|
||||
success (0),
|
||||
operationsError (1),
|
||||
protocolError (2),
|
||||
timeLimitExceeded (3),
|
||||
sizeLimitExceeded (4),
|
||||
compareFalse (5),
|
||||
compareTrue (6),
|
||||
authMethodNotSupported (7),
|
||||
strongAuthRequired (8),
|
||||
-- 9 reserved --
|
||||
referral (10), -- new
|
||||
adminLimitExceeded (11), -- new
|
||||
unavailableCriticalExtension (12), -- new
|
||||
confidentialityRequired (13), -- new
|
||||
saslBindInProgress (14), -- new
|
||||
noSuchAttribute (16),
|
||||
undefinedAttributeType (17),
|
||||
inappropriateMatching (18),
|
||||
constraintViolation (19),
|
||||
attributeOrValueExists (20),
|
||||
invalidAttributeSyntax (21),
|
||||
-- 22-31 unused --
|
||||
noSuchObject (32),
|
||||
aliasProblem (33),
|
||||
invalidDNSyntax (34),
|
||||
-- 35 reserved for undefined isLeaf --
|
||||
aliasDereferencingProblem (36),
|
||||
-- 37-47 unused --
|
||||
inappropriateAuthentication (48),
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 45]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
invalidCredentials (49),
|
||||
insufficientAccessRights (50),
|
||||
busy (51),
|
||||
unavailable (52),
|
||||
unwillingToPerform (53),
|
||||
loopDetect (54),
|
||||
-- 55-63 unused --
|
||||
namingViolation (64),
|
||||
objectClassViolation (65),
|
||||
notAllowedOnNonLeaf (66),
|
||||
notAllowedOnRDN (67),
|
||||
entryAlreadyExists (68),
|
||||
objectClassModsProhibited (69),
|
||||
-- 70 reserved for CLDAP --
|
||||
affectsMultipleDSAs (71), -- new
|
||||
-- 72-79 unused --
|
||||
other (80) },
|
||||
-- 81-90 reserved for APIs --
|
||||
matchedDN LDAPDN,
|
||||
errorMessage LDAPString,
|
||||
referral [3] Referral OPTIONAL }
|
||||
|
||||
Referral ::= SEQUENCE OF LDAPURL
|
||||
|
||||
LDAPURL ::= LDAPString -- limited to characters permitted in URLs
|
||||
|
||||
Controls ::= SEQUENCE OF Control
|
||||
|
||||
Control ::= SEQUENCE {
|
||||
controlType LDAPOID,
|
||||
criticality BOOLEAN DEFAULT FALSE,
|
||||
controlValue OCTET STRING OPTIONAL }
|
||||
|
||||
BindRequest ::= [APPLICATION 0] SEQUENCE {
|
||||
version INTEGER (1 .. 127),
|
||||
name LDAPDN,
|
||||
authentication AuthenticationChoice }
|
||||
|
||||
AuthenticationChoice ::= CHOICE {
|
||||
simple [0] OCTET STRING,
|
||||
-- 1 and 2 reserved
|
||||
sasl [3] SaslCredentials }
|
||||
|
||||
SaslCredentials ::= SEQUENCE {
|
||||
mechanism LDAPString,
|
||||
credentials OCTET STRING OPTIONAL }
|
||||
|
||||
BindResponse ::= [APPLICATION 1] SEQUENCE {
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 46]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
COMPONENTS OF LDAPResult,
|
||||
serverSaslCreds [7] OCTET STRING OPTIONAL }
|
||||
|
||||
UnbindRequest ::= [APPLICATION 2] NULL
|
||||
|
||||
SearchRequest ::= [APPLICATION 3] SEQUENCE {
|
||||
baseObject LDAPDN,
|
||||
scope ENUMERATED {
|
||||
baseObject (0),
|
||||
singleLevel (1),
|
||||
wholeSubtree (2) },
|
||||
derefAliases ENUMERATED {
|
||||
neverDerefAliases (0),
|
||||
derefInSearching (1),
|
||||
derefFindingBaseObj (2),
|
||||
derefAlways (3) },
|
||||
sizeLimit INTEGER (0 .. maxInt),
|
||||
timeLimit INTEGER (0 .. maxInt),
|
||||
typesOnly BOOLEAN,
|
||||
filter Filter,
|
||||
attributes AttributeDescriptionList }
|
||||
|
||||
Filter ::= CHOICE {
|
||||
and [0] SET OF Filter,
|
||||
or [1] SET OF Filter,
|
||||
not [2] Filter,
|
||||
equalityMatch [3] AttributeValueAssertion,
|
||||
substrings [4] SubstringFilter,
|
||||
greaterOrEqual [5] AttributeValueAssertion,
|
||||
lessOrEqual [6] AttributeValueAssertion,
|
||||
present [7] AttributeDescription,
|
||||
approxMatch [8] AttributeValueAssertion,
|
||||
extensibleMatch [9] MatchingRuleAssertion }
|
||||
|
||||
SubstringFilter ::= SEQUENCE {
|
||||
type AttributeDescription,
|
||||
-- at least one must be present
|
||||
substrings SEQUENCE OF CHOICE {
|
||||
initial [0] LDAPString,
|
||||
any [1] LDAPString,
|
||||
final [2] LDAPString } }
|
||||
|
||||
MatchingRuleAssertion ::= SEQUENCE {
|
||||
matchingRule [1] MatchingRuleId OPTIONAL,
|
||||
type [2] AttributeDescription OPTIONAL,
|
||||
matchValue [3] AssertionValue,
|
||||
dnAttributes [4] BOOLEAN DEFAULT FALSE }
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 47]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
|
||||
objectName LDAPDN,
|
||||
attributes PartialAttributeList }
|
||||
|
||||
PartialAttributeList ::= SEQUENCE OF SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL
|
||||
|
||||
SearchResultDone ::= [APPLICATION 5] LDAPResult
|
||||
|
||||
ModifyRequest ::= [APPLICATION 6] SEQUENCE {
|
||||
object LDAPDN,
|
||||
modification SEQUENCE OF SEQUENCE {
|
||||
operation ENUMERATED {
|
||||
add (0),
|
||||
delete (1),
|
||||
replace (2) },
|
||||
modification AttributeTypeAndValues } }
|
||||
|
||||
AttributeTypeAndValues ::= SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
ModifyResponse ::= [APPLICATION 7] LDAPResult
|
||||
|
||||
AddRequest ::= [APPLICATION 8] SEQUENCE {
|
||||
entry LDAPDN,
|
||||
attributes AttributeList }
|
||||
|
||||
AttributeList ::= SEQUENCE OF SEQUENCE {
|
||||
type AttributeDescription,
|
||||
vals SET OF AttributeValue }
|
||||
|
||||
AddResponse ::= [APPLICATION 9] LDAPResult
|
||||
|
||||
DelRequest ::= [APPLICATION 10] LDAPDN
|
||||
|
||||
DelResponse ::= [APPLICATION 11] LDAPResult
|
||||
|
||||
ModifyDNRequest ::= [APPLICATION 12] SEQUENCE {
|
||||
entry LDAPDN,
|
||||
newrdn RelativeLDAPDN,
|
||||
deleteoldrdn BOOLEAN,
|
||||
newSuperior [0] LDAPDN OPTIONAL }
|
||||
|
||||
ModifyDNResponse ::= [APPLICATION 13] LDAPResult
|
||||
|
||||
-- Wahl, et. al. Standards Track [Page 48]
|
||||
--
|
||||
-- RFC 2251 LDAPv3 December 1997
|
||||
|
||||
|
||||
CompareRequest ::= [APPLICATION 14] SEQUENCE {
|
||||
entry LDAPDN,
|
||||
ava AttributeValueAssertion }
|
||||
|
||||
CompareResponse ::= [APPLICATION 15] LDAPResult
|
||||
|
||||
AbandonRequest ::= [APPLICATION 16] MessageID
|
||||
|
||||
ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
|
||||
requestName [0] LDAPOID,
|
||||
requestValue [1] OCTET STRING OPTIONAL }
|
||||
|
||||
ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
|
||||
COMPONENTS OF LDAPResult,
|
||||
responseName [10] LDAPOID OPTIONAL,
|
||||
response [11] OCTET STRING OPTIONAL }
|
||||
|
||||
passwdModifyOID LDAPOID ::= "1.3.6.1.4.1.4203.1.11.1"
|
||||
|
||||
PasswdModifyRequestValue ::= SEQUENCE {
|
||||
userIdentity [0] OCTET STRING OPTIONAL,
|
||||
oldPasswd [1] OCTET STRING OPTIONAL,
|
||||
newPasswd [2] OCTET STRING OPTIONAL }
|
||||
|
||||
PasswdModifyResponseValue ::= SEQUENCE {
|
||||
genPasswd [0] OCTET STRING OPTIONAL }
|
||||
|
||||
END
|
||||
|
||||
|
||||
+33
-6
@@ -3,8 +3,8 @@
|
||||
|
||||
AC_PREREQ(2.53)
|
||||
AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 0.0` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd])
|
||||
REQUIRE_ERLANG_MIN="6.1 (Erlang/OTP 17.1)"
|
||||
REQUIRE_ERLANG_MAX="9.0.0 (No Max)"
|
||||
REQUIRE_ERLANG_MIN="6.4 (Erlang/OTP 17.5)"
|
||||
REQUIRE_ERLANG_MAX="100.0.0 (No Max)"
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
@@ -66,7 +66,7 @@ if test "x$MAKE" = "x"; then
|
||||
fi
|
||||
|
||||
# Change default prefix
|
||||
AC_PREFIX_DEFAULT(/)
|
||||
AC_PREFIX_DEFAULT(/usr/local)
|
||||
|
||||
AC_ARG_ENABLE(hipe,
|
||||
[AC_HELP_STRING([--enable-hipe], [compile natively with HiPE, not recommended (default: no)])],
|
||||
@@ -101,10 +101,10 @@ AC_ARG_ENABLE(mssql,
|
||||
esac],[db_type=generic])
|
||||
|
||||
AC_ARG_ENABLE(all,
|
||||
[AC_HELP_STRING([--enable-all], [same as --enable-odbc --enable-mysql --enable-pgsql --enable-sqlite --enable-pam --enable-zlib --enable-riak --enable-redis --enable-elixir --enable-iconv --enable-debug --enable-tools (useful for Dialyzer checks, default: no)])],
|
||||
[AC_HELP_STRING([--enable-all], [same as --enable-odbc --enable-mysql --enable-pgsql --enable-sqlite --enable-pam --enable-zlib --enable-riak --enable-redis --enable-elixir --enable-iconv --enable-stun --enable-sip --enable-debug --enable-tools (useful for Dialyzer checks, default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) odbc=true mysql=true pgsql=true sqlite=true pam=true zlib=true riak=true redis=true elixir=true iconv=true debug=true tools=true ;;
|
||||
no) odbc=false mysql=false pgsql=false sqlite=false pam=false zlib=false riak=false redis=false elixir=false iconv=false debug=false tools=false ;;
|
||||
yes) odbc=true mysql=true pgsql=true sqlite=true pam=true zlib=true riak=true redis=true elixir=true iconv=true stun=true sip=true debug=true tools=true ;;
|
||||
no) odbc=false mysql=false pgsql=false sqlite=false pam=false zlib=false riak=false redis=false elixir=false iconv=false stun=false sip=false debug=false tools=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-all) ;;
|
||||
esac],[])
|
||||
|
||||
@@ -212,6 +212,30 @@ AC_ARG_ENABLE(latest_deps,
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-latest-deps) ;;
|
||||
esac],[if test "x$latest_deps" = "x"; then latest_deps=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(system_deps,
|
||||
[AC_HELP_STRING([--enable-system-deps], [makes rebar use localy installed dependences instead of downloading them (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) system_deps=true ;;
|
||||
no) system_deps=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-system-deps) ;;
|
||||
esac],[if test "x$system_deps" = "x"; then system_deps=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(stun,
|
||||
[AC_HELP_STRING([--enable-stun], [enable STUN/TURN support (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) stun=true ;;
|
||||
no) stun=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-stun) ;;
|
||||
esac],[if test "x$stun" = "x"; then stun=false; fi])
|
||||
|
||||
AC_ARG_ENABLE(sip,
|
||||
[AC_HELP_STRING([--enable-sip], [enable SIP support (default: no)])],
|
||||
[case "${enableval}" in
|
||||
yes) sip=true ;;
|
||||
no) sip=false ;;
|
||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-sip) ;;
|
||||
esac],[if test "x$sip" = "x"; then sip=false; fi])
|
||||
|
||||
AC_CONFIG_FILES([Makefile
|
||||
vars.config
|
||||
src/ejabberd.app.src])
|
||||
@@ -253,9 +277,12 @@ AC_SUBST(riak)
|
||||
AC_SUBST(redis)
|
||||
AC_SUBST(elixir)
|
||||
AC_SUBST(iconv)
|
||||
AC_SUBST(stun)
|
||||
AC_SUBST(sip)
|
||||
AC_SUBST(debug)
|
||||
AC_SUBST(tools)
|
||||
AC_SUBST(latest_deps)
|
||||
AC_SUBST(system_deps)
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(CPPFLAGS)
|
||||
AC_SUBST(LDFLAGS)
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
ejabberd container
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Usage](#usage)
|
||||
- [Persistence](#persistence)
|
||||
- [SSL Certificates](#ssl-certificates)
|
||||
- [Base Image](#base-image)
|
||||
- [Ejabberd Configuration](#ejabberd-configuration)
|
||||
- [Served Hostnames](#served-hostnames)
|
||||
- [Authentication](#authentication)
|
||||
- [Admins](#admins)
|
||||
- [Users](#users)
|
||||
- [SSL](#ssl)
|
||||
- [Modules](#modules)
|
||||
- [Logging](#logging)
|
||||
- [Mount Configurations](#mount-configurations)
|
||||
- [Erlang Configuration](#erlang-configuration)
|
||||
- [Maintenance](#maintenance)
|
||||
- [Register Users](#register-users)
|
||||
- [Creating Backups](#creating-backups)
|
||||
- [Restoring Backups](#restoring-backups)
|
||||
- [Debug](#debug)
|
||||
- [Erlang Shell](#erlang-shell)
|
||||
- [System Shell](#system-shell)
|
||||
- [System Commands](#system-commands)
|
||||
- [Exposed Ports](#exposed-ports)
|
||||
|
||||
# Introduction
|
||||
|
||||
Dockerfile to build an [ejabberd](https://www.ejabberd.im/) container image.
|
||||
|
||||
Docker Tag Names are based on ejabberd versions in git [tags][]. The image tag `:latest` is based on the master branch.
|
||||
|
||||
[tags]: https://github.com/rroemhild/ejabberd/tags
|
||||
|
||||
# Quick Start
|
||||
|
||||
You can start of with the following container:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name "ejabberd" \
|
||||
-p 5222:5222 \
|
||||
-p 5269:5269 \
|
||||
-p 5280:5280 \
|
||||
-h 'xmpp.example.de' \
|
||||
-e "XMPP_DOMAIN=example.de" \
|
||||
-e "ERLANG_NODE=nodename" \
|
||||
-e "EJABBERD_ADMINS=admin@example.de admin2@example.de" \
|
||||
-e "EJABBERD_USERS=admin@example.de:password1234 admin2@example.de" \
|
||||
-e "TZ=Europe/Berlin" \
|
||||
rroemhild/ejabberd
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
## Persistence
|
||||
|
||||
For storage of the application data, you can mount volumes at
|
||||
|
||||
* `/opt/ejabberd/ssl`
|
||||
* `/opt/ejabberd/backup`
|
||||
* `/opt/ejabberd/upload`
|
||||
* `/opt/ejabberd/database`
|
||||
|
||||
or use a data container
|
||||
|
||||
```bash
|
||||
docker create --name ejabberd-data rroemhild/ejabberd-data
|
||||
docker run -d --name ejabberd --volumes-from ejabberd-data rroemhild/ejabberd
|
||||
```
|
||||
|
||||
## SSL Certificates
|
||||
|
||||
TLS is enabled by default and the run script will auto-generate two snake-oil certificates during boot if you don't provide your SSL certificates.
|
||||
|
||||
To use your own certificates, there are two options.
|
||||
|
||||
1. Mount the volume `/opt/ejabberd/ssl` to a local directory with the `.pem` files:
|
||||
|
||||
* /tmp/ssl/host.pem (SERVER_HOSTNAME)
|
||||
* /tmp/ssl/xmpp_domain.pem (XMPP_DOMAIN)
|
||||
|
||||
Make sure that the certificate and private key are in one `.pem` file. If one file is missing it will be auto-generated. I.e. you can provide your certificate for your **XMMP_DOMAIN** and use a snake-oil certificate for the `SERVER_HOSTNAME`.
|
||||
|
||||
2. Specify the certificates via environment variables: **EJABBERD_SSLCERT_HOST** and **EJABBERD_SSLCERT_EXAMPLE_COM**. For the
|
||||
domain certificates, make sure you match the domain names given in **XMPP_DOMAIN** and replace dots and dashes with underscore.
|
||||
|
||||
## Base Image
|
||||
|
||||
Build your own ejabberd container image and add your config templates, certificates or [extend](#cluster-example) it for your needs.
|
||||
|
||||
```
|
||||
FROM rroemhild/ejabberd
|
||||
ADD ./ejabberd.yml.tpl /opt/ejabberd/conf/ejabberd.yml.tpl
|
||||
ADD ./ejabberdctl.cfg.tpl /opt/ejabberd/conf/ejabberdctl.cfg.tpl
|
||||
ADD ./example.com.pem /opt/ejabberd/ssl/example.com.pem
|
||||
```
|
||||
|
||||
If you need root privileges switch to `USER root` and go back to `USER ejabberd` when you're done.
|
||||
|
||||
# Ejabberd Configuration
|
||||
|
||||
You can additionally provide extra runtime configuration in a downstream image by replacing the config template `ejabberd.yml.tpl` with one based on this image's template and include extra interpolation of environment variables. The template is parsed by Jinja2 with the runtime environment (equivalent to Python's `os.environ` available as `env`).
|
||||
|
||||
## Served Hostnames
|
||||
|
||||
By default the container will serve the XMPP domain `localhost`. In order to serve a different domain at runtime, provide the **XMPP_DOMAIN** variable with a domain name. You can add more domains separated with whitespace.
|
||||
|
||||
```
|
||||
XMPP_DOMAIN=example.ninja xyz.io test.com
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
Authentication methods can be set with the **EJABBERD_AUTH_METHOD** environment variable. The default authentication mode is `internal`.
|
||||
|
||||
Supported authentication methods:
|
||||
|
||||
* anonymous
|
||||
* internal
|
||||
* external
|
||||
* ldap
|
||||
|
||||
Internal and anonymous authentication example:
|
||||
|
||||
```
|
||||
EJABBERD_AUTH_METHOD=internal anonymous
|
||||
```
|
||||
|
||||
[External authentication](http://docs.ejabberd.im/admin/guide/configuration/#external-script) example:
|
||||
```
|
||||
EJABBERD_AUTH_METHOD=external
|
||||
EJABBERD_EXTAUTH_PROGRAM="/opt/ejabberd/scripts/authenticate-user.sh"
|
||||
EJABBERD_EXTAUTH_INSTANCES=3
|
||||
EJABBERD_EXTAUTH_CACHE=600
|
||||
```
|
||||
**EJABBERD_EXTAUTH_INSTANCES** must be an integer with a minimum value of 1. **EJABBERD_EXTAUTH_CACHE** can be set to "false" or an integer value representing cache time in seconds. Note that caching should not be enabled if internal auth is also enabled.
|
||||
|
||||
### MySQL Authentication
|
||||
|
||||
Set `EJABBERD_AUTH_METHOD=external` and `EJABBERD_EXTAUTH_PROGRAM=/opt/ejabberd/scripts/lib/auth_mysql.py` to enable MySQL authentication. Use the following environment variables to configure the database connection and the layout of the database. Password changing, registration, and unregistration are optional features and are enabled only if the respective queries are provided.
|
||||
|
||||
- **AUTH_MYSQL_HOST**: The MySQL host
|
||||
- **AUTH_MYSQL_USER**: Username to connect to the MySQL host
|
||||
- **AUTH_MYSQL_PASSWORD**: Password to connect to the MySQL host
|
||||
- **AUTH_MYSQL_DATABASE**: Database name where to find the user information
|
||||
- **AUTH_MYSQL_HASHALG**: Format of the password in the database. Default is cleartext. Options are `crypt`, `md5`, `sha1`, `sha224`, `sha256`, `sha384`, `sha512`. `crypt` is recommended, as it is salted. When setting the password, `crypt` uses SHA-512 (prefix `$6$`).
|
||||
- **AUTH_MYSQL_QUERY_GETPASS**: Get the password for a user. Use the placeholders `%(user)s`, `%(host)s`. Example: `SELECT password FROM users WHERE username = CONCAT(%(user)s, '@', %(host)s)`
|
||||
- **AUTH_MYSQL_QUERY_SETPASS**: Update the password for a user. Leave empty to disable. Placeholder `%(password)s` contains the hashed password. Example: `UPDATE users SET password = %(password)s WHERE username = CONCAT(%(user)s, '@', %(host)s)`
|
||||
- **AUTH_MYSQL_QUERY_REGISTER**: Register a new user. Leave empty to disable. Example: `INSERT INTO users ( username, password ) VALUES ( CONCAT(%(user)s, '@', %(host)s), %(password)s )`
|
||||
- **AUTH_MYSQL_QUERY_UNREGISTER**: Removes a user. Leave empty to disable. Example: `DELETE FROM users WHERE username = CONCAT(%(user)s, '@', %(host)s)`
|
||||
|
||||
Note that the MySQL authentication script writes a debug log into the file `/var/log/ejabberd/extauth.log`. To get its content, execute the following command:
|
||||
|
||||
```bash
|
||||
docker exec -ti ejabberd tail -n50 -f /var/log/ejabberd/extauth.log
|
||||
```
|
||||
|
||||
To find out more about the mysql authentication script, check out the [ejabberd-auth-mysql](https://github.com/rankenstein/ejabberd-auth-mysql) repository.
|
||||
|
||||
### LDAP Auth
|
||||
|
||||
Full documentation http://docs.ejabberd.im/admin/guide/configuration/#ldap.
|
||||
|
||||
Connection
|
||||
|
||||
- **EJABBERD_LDAP_SERVERS**: List of IP addresses or DNS names of your LDAP servers. This option is required.
|
||||
- **EJABBERD_LDAP_ENCRYPT**: The value `tls` enables encryption by using LDAP over SSL. The default value is: `none`.
|
||||
- **EJABBERD_LDAP_TLS_VERIFY**: `false|soft|hard` This option specifies whether to verify LDAP server certificate or not when TLS is enabled. The default is `false` which means no checks are performed.
|
||||
- **EJABBERD_LDAP_TLS_CACERTFILE**: Path to file containing PEM encoded CA certificates.
|
||||
- **EJABBERD_LDAP_TLS_DEPTH**: Specifies the maximum verification depth when TLS verification is enabled. The default value is 1.
|
||||
- **EJABBERD_LDAP_PORT**: The default port is `389` if encryption is disabled; and `636` if encryption is enabled.
|
||||
- **EJABBERD_LDAP_ROOTDN**: Bind DN. The default value is "" which means ‘anonymous connection’.
|
||||
- **EJABBERD_LDAP_PASSWORD**: Bind password. The default value is "".
|
||||
- **EJABBERD_LDAP_DEREF_ALIASES**: `never|always|finding|searching`
|
||||
Whether or not to dereference aliases. The default is `never`.
|
||||
|
||||
Authentication
|
||||
|
||||
- **EJABBERD_LDAP_BASE**: LDAP base directory which stores users accounts. This option is required.
|
||||
- **EJABBERD_LDAP_UIDS**: `ldap_uidattr:ldap_uidattr_format` The default attributes are `uid:%u`.
|
||||
- **EJABBERD_LDAP_FILTER**: RFC 4515 LDAP filter. The default Filter value is undefined.
|
||||
- **EJABBERD_LDAP_DN_FILTER**: `{ Filter: FilterAttrs }` This filter is applied on the results returned by the main filter. By default ldap_dn_filter is undefined.
|
||||
|
||||
## Admins
|
||||
|
||||
Set one or more admin user (seperated by whitespace) with the **EJABBERD_ADMINS** environment variable. You can register admin users with the **EJABBERD_USERS** environment variable during container startup, use you favorite XMPP client or the `ejabberdctl` command line utility.
|
||||
|
||||
```
|
||||
EJABBERD_ADMINS=admin@example.ninja
|
||||
```
|
||||
|
||||
## Users
|
||||
|
||||
Automatically register users during container startup. Uses random password if you don't provide a password for the user. Format is `JID:PASSWORD`. Register more users separated with whitespace.
|
||||
|
||||
Register the admin user from **EJABBERD_ADMINS** with a give password:
|
||||
|
||||
```
|
||||
EJABBERD_USERS=admin@example.ninja:password1234
|
||||
```
|
||||
|
||||
Or without a random password printed to stdout (check container logs):
|
||||
|
||||
```
|
||||
EJABBERD_USERS=admin@example.ninja
|
||||
```
|
||||
|
||||
Register more than one user:
|
||||
|
||||
```
|
||||
EJABBERD_USERS=admin@example.ninja:password1234 user1@test.com user1@xyz.io
|
||||
```
|
||||
|
||||
## SSL
|
||||
|
||||
- **EJABBERD_SSLCERT_HOST**: SSL Certificate for the hostname.
|
||||
- **EJABBERD_SSLCERT_EXAMPLE_COM**: SSL Certificates for XMPP domains.
|
||||
- **EJABBERD_STARTTLS**: Set to `false` to disable StartTLS for client to server connections. Default: `true`.
|
||||
- **EJABBERD_S2S_SSL**: Set to `false` to disable SSL in server 2 server connections. Default: `true`.
|
||||
- **EJABBERD_HTTPS**: If your proxy terminates SSL you may want to disable HTTPS on port 5280 and 5443. Default: `true`.
|
||||
- **EJABBERD_PROTOCOL_OPTIONS_TLSV1**: Allow TLSv1 protocol. Default: `false`.
|
||||
- **EJABBERD_PROTOCOL_OPTIONS_TLSV1_1**: Allow TLSv1.1 protocol. Default: `true`.
|
||||
- **EJABBERD_CIPHERS**: Cipher suite. Default: `HIGH:!aNULL:!3DES`.
|
||||
- **EJABBERD_DHPARAM**: Set to `true` to use or generate custom DH parameters. Default: `false`.
|
||||
|
||||
## Modules
|
||||
|
||||
- **EJABBERD_SKIP_MODULES_UPDATE**: If you do not need to update ejabberd modules specs, skip the update task and speedup start. Default: `false`.
|
||||
- **EJABBERD_MOD_MUC_ADMIN**: Activate the mod_muc_admin module. Default: `false`.
|
||||
- **EJABBERD_MOD_ADMIN_EXTRA**: Activate the mod_muc_admin module. Default: `true`.
|
||||
- **EJABBERD_REGISTER_TRUSTED_NETWORK_ONLY**: Only allow user registration from the trusted_network access rule. Default: `true`.
|
||||
- **EJABBERD_MOD_VERSION**: Activate the mod_version module. Default: `true`.
|
||||
|
||||
## Logging
|
||||
|
||||
Use the **EJABBERD_LOGLEVEL** environment variable to set verbosity. Default: `4` (Info).
|
||||
|
||||
```
|
||||
loglevel: Verbosity of log files generated by ejabberd.
|
||||
0: No ejabberd log at all (not recommended)
|
||||
1: Critical
|
||||
2: Error
|
||||
3: Warning
|
||||
4: Info
|
||||
5: Debug
|
||||
```
|
||||
|
||||
## Mount Configurations
|
||||
|
||||
If you prefer to use your own configuration files and avoid passing docker environment variables (```-e```), you can do so by mounting a host directory.
|
||||
Pass in an additional ```-v``` to the ```docker run``` command, like so:
|
||||
```
|
||||
docker run -d \
|
||||
--name "ejabberd" \
|
||||
-p 5222:5222 \
|
||||
-p 5269:5269 \
|
||||
-p 5280:5280 \
|
||||
-h 'xmpp.example.de' \
|
||||
-v /<host_path>/conf:/opt/ejabberd/conf \
|
||||
rroemhild/ejabberd
|
||||
```
|
||||
|
||||
Your ```/<host_path>/conf``` folder should look like so:
|
||||
|
||||
```
|
||||
/<host_path>/conf/
|
||||
├── ejabberdctl.cfg
|
||||
├── ejabberd.yml
|
||||
└── inetrc
|
||||
```
|
||||
|
||||
Example configuration files can be downloaded from the ejabberd [github](https://github.com/rroemhild/ejabberd) page.
|
||||
|
||||
When these files exist in ```/opt/ejabberd/conf```, the run script will ignore the configuration templates.
|
||||
|
||||
## Erlang Configuration
|
||||
|
||||
With the following environment variables you can configure options that are passed by ejabberdctl to the erlang runtime system when starting ejabberd.
|
||||
|
||||
- **POLL**: Set to `false` to disable Kernel polling. Default: `true`.
|
||||
- **SMP**: SMP support `enable`, `auto`, `disable`. Default: `auto`.
|
||||
- **ERL_MAX_PORTS**: Maximum number of simultaneously open Erlang ports. Default: `32000`.
|
||||
- **FIREWALL_WINDOW**: Range of allowed ports to pass through a firewall. Default: `not defined`.
|
||||
- **INET_DIST_INTERFACE**: IP address where this Erlang node listens other nodes. Default: `0.0.0.0`.
|
||||
- **ERL_EPMD_ADDRESS**: IP addresses where epmd listens for connections. Default: `0.0.0.0`.
|
||||
- **ERL_PROCESSES**: Maximum number of Erlang processes. Default: `250000`.
|
||||
- **ERL_MAX_ETS_TABLES**: Maximum number of Erlang processes. Default: `1400`.
|
||||
- **ERLANG_OPTIONS**: Overwrite additional options passed to erlang while starting ejabberd. Default: `-noshell`
|
||||
- **ERLANG_NODE**: Allows to explicitly specify erlang node for ejabberd. Set to `nodename` lets erlang add the hostname. Default: `ejabberd@localhost`.
|
||||
- **EJABBERD_CONFIG_PATH**: ejabberd configuration file. Default: `/opt/ejabberd/conf/ejabberd.yml`.
|
||||
- **CONTRIB_MODULES_PATH**: contributed ejabberd modules path. Default: `/opt/ejabberd/modules`.
|
||||
- **CONTRIB_MODULES_CONF_DIR**: configuration directory for contributed modules. Default: `/opt/ejabberd/modules/conf`.
|
||||
- **ERLANG_COOKIE**: Set erlang cookie. Default is to auto-generated cookie.
|
||||
|
||||
# Maintenance
|
||||
|
||||
The `ejabberdctl` command is in the search path and can be run by:
|
||||
|
||||
```bash
|
||||
docker exec CONTAINER ejabberdctl help
|
||||
```
|
||||
|
||||
## Register Users
|
||||
|
||||
```bash
|
||||
docker exec CONTAINER ejabberdctl register user XMPP_DOMAIN PASSWORD
|
||||
```
|
||||
|
||||
## Creating Backups
|
||||
|
||||
Create a backupfile with ejabberdctl and copy the file from the container to localhost
|
||||
|
||||
```bash
|
||||
docker exec CONTAINER ejabberdctl backup /opt/ejabberd/backup/ejabberd.backup
|
||||
docker cp CONTAINER:/opt/ejabberd/backup/ejabberd.backup /tmp/ejabberd.backup
|
||||
```
|
||||
|
||||
## Restoring Backups
|
||||
|
||||
Copy the backupfile from localhost to the running container and restore with ejabberdctl
|
||||
|
||||
```bash
|
||||
docker cp /tmp/ejabberd.backup CONTAINER:/opt/ejabberd/backup/ejabberd.backup
|
||||
docker exec CONTAINER ejabberdctl restore /opt/ejabberd/backup/ejabberd.backup
|
||||
```
|
||||
|
||||
# Debug
|
||||
|
||||
## Erlang Shell
|
||||
|
||||
Set `-i` and `-t` option and append `live` to get an interactive erlang shell:
|
||||
|
||||
```bash
|
||||
docker run -i -t -P rroemhild/ejabberd live
|
||||
```
|
||||
|
||||
You can terminate the erlang shell with `q().`.
|
||||
|
||||
## System Shell
|
||||
|
||||
```bash
|
||||
docker run -i -t rroemhild/ejabberd shell
|
||||
```
|
||||
|
||||
## System Commands
|
||||
|
||||
```bash
|
||||
docker run -i -t rroemhild/ejabberd env
|
||||
```
|
||||
|
||||
# Exposed Ports
|
||||
|
||||
* 4560 (XMLRPC)
|
||||
* 5222 (Client 2 Server)
|
||||
* 5269 (Server 2 Server)
|
||||
* 5280 (HTTP admin/websocket/http-bind)
|
||||
* 5443 (HTTP Upload)
|
||||
@@ -0,0 +1,437 @@
|
||||
###
|
||||
### ejabberd configuration file
|
||||
###
|
||||
###
|
||||
|
||||
### The parameters used in this configuration file are explained in more detail
|
||||
### in the ejabberd Installation and Operation Guide.
|
||||
### Please consult the Guide in case of doubts, it is included with
|
||||
### your copy of ejabberd, and is also available online at
|
||||
### http://www.process-one.net/en/ejabberd/docs/
|
||||
|
||||
### =======
|
||||
### LOGGING
|
||||
|
||||
loglevel: {{ env['EJABBERD_LOGLEVEL'] or 4 }}
|
||||
log_rotate_size: 10485760
|
||||
log_rotate_count: 0
|
||||
log_rate_limit: 100
|
||||
|
||||
## watchdog_admins:
|
||||
## - "bob@example.com"
|
||||
|
||||
### ================
|
||||
### SERVED HOSTNAMES
|
||||
|
||||
hosts:
|
||||
{%- for xmpp_domain in env['XMPP_DOMAIN'].split() %}
|
||||
- "{{ xmpp_domain }}"
|
||||
{%- endfor %}
|
||||
|
||||
##
|
||||
## route_subdomains: Delegate subdomains to other XMPP servers.
|
||||
## For example, if this ejabberd serves example.org and you want
|
||||
## to allow communication with an XMPP server called im.example.org.
|
||||
##
|
||||
## route_subdomains: s2s
|
||||
|
||||
### ===============
|
||||
### LISTENING PORTS
|
||||
|
||||
listen:
|
||||
-
|
||||
port: 5222
|
||||
module: ejabberd_c2s
|
||||
{%- if env['EJABBERD_STARTTLS'] == "true" %}
|
||||
starttls_required: true
|
||||
{%- endif %}
|
||||
protocol_options:
|
||||
- "no_sslv3"
|
||||
{%- if env.get('EJABBERD_PROTOCOL_OPTIONS_TLSV1', "false") == "false" %}
|
||||
- "no_tlsv1"
|
||||
{%- endif %}
|
||||
{%- if env.get('EJABBERD_PROTOCOL_OPTIONS_TLSV1_1', "true") == "false" %}
|
||||
- "no_tlsv1_1"
|
||||
{%- endif %}
|
||||
max_stanza_size: 65536
|
||||
shaper: c2s_shaper
|
||||
access: c2s
|
||||
ciphers: "{{ env.get('EJABBERD_CIPHERS', 'HIGH:!aNULL:!3DES') }}"
|
||||
{%- if env.get('EJABBERD_DHPARAM', false) == "true" %}
|
||||
dhfile: "/opt/ejabberd/ssl/dh.pem"
|
||||
{%- endif %}
|
||||
-
|
||||
port: 5269
|
||||
module: ejabberd_s2s_in
|
||||
-
|
||||
port: 4560
|
||||
module: ejabberd_xmlrpc
|
||||
access_commands:
|
||||
configure:
|
||||
all: []
|
||||
|
||||
-
|
||||
port: 5280
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
"/websocket": ejabberd_http_ws
|
||||
## "/pub/archive": mod_http_fileserver
|
||||
web_admin: true
|
||||
http_bind: true
|
||||
## register: true
|
||||
captcha: true
|
||||
{%- if env['EJABBERD_HTTPS'] == "true" %}
|
||||
tls: true
|
||||
certfile: "/opt/ejabberd/ssl/host.pem"
|
||||
{% endif %}
|
||||
-
|
||||
port: 5443
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
"": mod_http_upload
|
||||
{%- if env['EJABBERD_HTTPS'] == "true" %}
|
||||
tls: true
|
||||
certfile: "/opt/ejabberd/ssl/host.pem"
|
||||
{% endif %}
|
||||
|
||||
|
||||
### SERVER TO SERVER
|
||||
### ================
|
||||
|
||||
{%- if env['EJABBERD_S2S_SSL'] == "true" %}
|
||||
s2s_use_starttls: required
|
||||
s2s_certfile: "/opt/ejabberd/ssl/host.pem"
|
||||
s2s_protocol_options:
|
||||
- "no_sslv3"
|
||||
{%- if env.get('EJABBERD_PROTOCOL_OPTIONS_TLSV1', "false") == "false" %}
|
||||
- "no_tlsv1"
|
||||
{%- endif %}
|
||||
{%- if env.get('EJABBERD_PROTOCOL_OPTIONS_TLSV1_1', "true") == "false" %}
|
||||
- "no_tlsv1_1"
|
||||
{%- endif %}
|
||||
s2s_ciphers: "{{ env.get('EJABBERD_CIPHERS', 'HIGH:!aNULL:!3DES') }}"
|
||||
{%- if env.get('EJABBERD_DHPARAM', false) == "true" %}
|
||||
s2s_dhfile: "/opt/ejabberd/ssl/dh.pem"
|
||||
{%- endif %}
|
||||
{% endif %}
|
||||
|
||||
### ==============
|
||||
### AUTHENTICATION
|
||||
|
||||
auth_method:
|
||||
{%- for auth_method in env.get('EJABBERD_AUTH_METHOD', 'internal').split() %}
|
||||
- {{ auth_method }}
|
||||
{%- endfor %}
|
||||
|
||||
{%- if 'anonymous' in env.get('EJABBERD_AUTH_METHOD', 'internal').split() %}
|
||||
anonymous_protocol: login_anon
|
||||
allow_multiple_connections: true
|
||||
{%- endif %}
|
||||
|
||||
|
||||
## LDAP authentication
|
||||
|
||||
{%- if 'ldap' in env.get('EJABBERD_AUTH_METHOD', 'internal').split() %}
|
||||
|
||||
ldap_servers:
|
||||
{%- for ldap_server in env.get('EJABBERD_LDAP_SERVERS', 'internal').split() %}
|
||||
- "{{ ldap_server }}"
|
||||
{%- endfor %}
|
||||
|
||||
ldap_encrypt: {{ env.get('EJABBERD_LDAP_ENCRYPT', 'none') }}
|
||||
ldap_tls_verify: {{ env.get('EJABBERD_LDAP_TLS_VERIFY', 'false') }}
|
||||
|
||||
{%- if env['EJABBERD_LDAP_TLS_CACERTFILE'] %}
|
||||
ldap_tls_cacertfile: "{{ env['EJABBERD_LDAP_TLS_CACERTFILE'] }}"
|
||||
{%- endif %}
|
||||
|
||||
ldap_tls_depth: {{ env.get('EJABBERD_LDAP_TLS_DEPTH', 1) }}
|
||||
|
||||
{%- if env['EJABBERD_LDAP_PORT'] %}
|
||||
ldap_port: {{ env['EJABBERD_LDAP_PORT'] }}
|
||||
{%- endif %}
|
||||
|
||||
{%- if env['EJABBERD_LDAP_ROOTDN'] %}
|
||||
ldap_rootdn: "{{ env['EJABBERD_LDAP_ROOTDN'] }}"
|
||||
{%- endif %}
|
||||
|
||||
{%- if env['EJABBERD_LDAP_PASSWORD'] %}
|
||||
ldap_password: "{{ env['EJABBERD_LDAP_PASSWORD'] }}"
|
||||
{%- endif %}
|
||||
|
||||
ldap_deref_aliases: {{ env.get('EJABBERD_LDAP_DEREF_ALIASES', 'never') }}
|
||||
ldap_base: "{{ env['EJABBERD_LDAP_BASE'] }}"
|
||||
|
||||
{%- if env['EJABBERD_LDAP_UIDS'] %}
|
||||
ldap_uids:
|
||||
{%- for ldap_uid in env['EJABBERD_LDAP_UIDS'].split() %}
|
||||
"{{ ldap_uid.split(':')[0] }}": "{{ ldap_uid.split(':')[1] }}"
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if env['EJABBERD_LDAP_FILTER'] %}
|
||||
ldap_filter: "{{ env['EJABBERD_LDAP_FILTER'] }}"
|
||||
{%- endif %}
|
||||
|
||||
{%- if env['EJABBERD_LDAP_DN_FILTER'] %}
|
||||
ldap_dn_filter:
|
||||
{%- for dn_filter in env['EJABBERD_LDAP_DN_FILTER'].split() %}
|
||||
"{{ dn_filter.split(':')[0] }}": ["{{ dn_filter.split(':')[1] }}"]
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
|
||||
{%- endif %}
|
||||
|
||||
{%- if 'external' in env.get('EJABBERD_AUTH_METHOD', 'internal').split() %}
|
||||
{%- if env['EJABBERD_EXTAUTH_PROGRAM'] %}
|
||||
extauth_program: "{{ env['EJABBERD_EXTAUTH_PROGRAM'] }}"
|
||||
{%- endif %}
|
||||
{%- if env['EJABBERD_EXTAUTH_INSTANCES'] %}
|
||||
extauth_instances: {{ env['EJABBERD_EXTAUTH_INSTANCES'] }}
|
||||
{%- endif %}
|
||||
{%- if 'internal' in env.get('EJABBERD_AUTH_METHOD').split() %}
|
||||
extauth_cache: false
|
||||
{%- elif env['EJABBERD_EXTAUTH_CACHE'] %}
|
||||
extauth_cache: {{ env['EJABBERD_EXTAUTH_CACHE'] }}
|
||||
{%- endif %}
|
||||
{% endif %}
|
||||
|
||||
### ===============
|
||||
### TRAFFIC SHAPERS
|
||||
|
||||
shaper:
|
||||
normal: 1000
|
||||
fast: 50000
|
||||
max_fsm_queue: 1000
|
||||
|
||||
### ====================
|
||||
### ACCESS CONTROL LISTS
|
||||
|
||||
acl:
|
||||
admin:
|
||||
user:
|
||||
{%- if env['EJABBERD_ADMINS'] %}
|
||||
{%- for admin in env['EJABBERD_ADMINS'].split() %}
|
||||
- "{{ admin.split('@')[0] }}": "{{ admin.split('@')[1] }}"
|
||||
{%- endfor %}
|
||||
{%- else %}
|
||||
- "admin": "{{ env['XMPP_DOMAIN'].split()[0] }}"
|
||||
{%- endif %}
|
||||
local:
|
||||
user_regexp: ""
|
||||
|
||||
### ============
|
||||
### ACCESS RULES
|
||||
|
||||
access:
|
||||
## Maximum number of simultaneous sessions allowed for a single user:
|
||||
max_user_sessions:
|
||||
all: 10
|
||||
## Maximum number of offline messages that users can have:
|
||||
max_user_offline_messages:
|
||||
admin: 5000
|
||||
all: 100
|
||||
## This rule allows access only for local users:
|
||||
local:
|
||||
local: allow
|
||||
## Only non-blocked users can use c2s connections:
|
||||
c2s:
|
||||
blocked: deny
|
||||
all: allow
|
||||
## For C2S connections, all users except admins use the "normal" shaper
|
||||
c2s_shaper:
|
||||
admin: none
|
||||
all: normal
|
||||
## All S2S connections use the "fast" shaper
|
||||
s2s_shaper:
|
||||
all: fast
|
||||
## Only admins can send announcement messages:
|
||||
announce:
|
||||
admin: allow
|
||||
## Only admins can use the configuration interface:
|
||||
configure:
|
||||
admin: allow
|
||||
## Admins of this server are also admins of the MUC service:
|
||||
muc_admin:
|
||||
admin: allow
|
||||
## Only accounts of the local ejabberd server, or only admins can create rooms, depending on environment variable:
|
||||
muc_create:
|
||||
{%- if env['EJABBERD_MUC_CREATE_ADMIN_ONLY'] == "true" %}
|
||||
admin: allow
|
||||
{% else %}
|
||||
local: allow
|
||||
{% endif %}
|
||||
## All users are allowed to use the MUC service:
|
||||
muc:
|
||||
all: allow
|
||||
## Only accounts on the local ejabberd server can create Pubsub nodes:
|
||||
pubsub_createnode:
|
||||
local: allow
|
||||
## In-band registration allows registration of any possible username.
|
||||
register:
|
||||
{%- if env['EJABBERD_REGISTER_ADMIN_ONLY'] == "true" %}
|
||||
all: deny
|
||||
admin: allow
|
||||
{% else %}
|
||||
all: allow
|
||||
{% endif %}
|
||||
## Only allow to register from localhost
|
||||
trusted_network:
|
||||
loopback: allow
|
||||
soft_upload_quota:
|
||||
all: 400 # MiB
|
||||
hard_upload_quota:
|
||||
all: 500 # MiB
|
||||
|
||||
|
||||
language: "en"
|
||||
|
||||
### =======
|
||||
### MODULES
|
||||
|
||||
modules:
|
||||
mod_adhoc: {}
|
||||
{%- if env['EJABBERD_MOD_ADMIN_EXTRA'] == "true" %}
|
||||
mod_admin_extra: {}
|
||||
{% endif %}
|
||||
mod_announce: # recommends mod_adhoc
|
||||
access: announce
|
||||
mod_blocking: {} # requires mod_privacy
|
||||
mod_caps: {}
|
||||
mod_carboncopy: {}
|
||||
mod_client_state:
|
||||
queue_chat_states: true
|
||||
queue_presence: false
|
||||
mod_configure: {} # requires mod_adhoc
|
||||
mod_disco: {}
|
||||
## mod_echo: {}
|
||||
mod_irc: {}
|
||||
mod_http_bind: {}
|
||||
## mod_http_fileserver:
|
||||
## docroot: "/var/www"
|
||||
## accesslog: "/var/log/ejabberd/access.log"
|
||||
mod_last: {}
|
||||
mod_mam:
|
||||
default: always
|
||||
mod_muc:
|
||||
host: "conference.@HOST@"
|
||||
access: muc
|
||||
access_create: muc_create
|
||||
access_persistent: muc_create
|
||||
access_admin: muc_admin
|
||||
history_size: 50
|
||||
default_room_options:
|
||||
persistent: true
|
||||
{%- if env['EJABBERD_MOD_MUC_ADMIN'] == "true" %}
|
||||
mod_muc_admin: {}
|
||||
{% endif %}
|
||||
## mod_muc_log: {}
|
||||
## mod_multicast: {}
|
||||
mod_offline:
|
||||
access_max_user_messages: max_user_offline_messages
|
||||
mod_ping: {}
|
||||
## mod_pres_counter:
|
||||
## count: 5
|
||||
## interval: 60
|
||||
mod_privacy: {}
|
||||
mod_private: {}
|
||||
## mod_proxy65: {}
|
||||
mod_pubsub:
|
||||
access_createnode: pubsub_createnode
|
||||
## reduces resource comsumption, but XEP incompliant
|
||||
ignore_pep_from_offline: true
|
||||
## XEP compliant, but increases resource comsumption
|
||||
## ignore_pep_from_offline: false
|
||||
last_item_cache: false
|
||||
plugins:
|
||||
- "flat"
|
||||
- "hometree"
|
||||
- "pep" # pep requires mod_caps
|
||||
mod_register:
|
||||
##
|
||||
## Protect In-Band account registrations with CAPTCHA.
|
||||
##
|
||||
## captcha_protected: true
|
||||
|
||||
##
|
||||
## Set the minimum informational entropy for passwords.
|
||||
##
|
||||
## password_strength: 32
|
||||
|
||||
##
|
||||
## After successful registration, the user receives
|
||||
## a message with this subject and body.
|
||||
##
|
||||
welcome_message:
|
||||
subject: "Welcome!"
|
||||
body: |-
|
||||
Hi.
|
||||
Welcome to this XMPP server.
|
||||
|
||||
##
|
||||
## Only clients in the server machine can register accounts
|
||||
##
|
||||
{%- if env['EJABBERD_REGISTER_TRUSTED_NETWORK_ONLY'] == "true" %}
|
||||
ip_access: trusted_network
|
||||
{% endif %}
|
||||
|
||||
access: register
|
||||
mod_roster: {}
|
||||
mod_shared_roster: {}
|
||||
mod_stats: {}
|
||||
mod_time: {}
|
||||
mod_vcard: {}
|
||||
{% if env.get('EJABBERD_MOD_VERSION', true) == "true" %}
|
||||
mod_version: {}
|
||||
{% endif %}
|
||||
mod_http_upload:
|
||||
docroot: "/opt/ejabberd/upload"
|
||||
{%- if env['EJABBERD_HTTPS'] == "true" %}
|
||||
put_url: "https://@HOST@:5443"
|
||||
{%- else %}
|
||||
put_url: "http://@HOST@:5443"
|
||||
{% endif %}
|
||||
mod_http_upload_quota:
|
||||
max_days: 10
|
||||
|
||||
### ============
|
||||
### HOST CONFIG
|
||||
|
||||
host_config:
|
||||
{%- for xmpp_domain in env['XMPP_DOMAIN'].split() %}
|
||||
"{{ xmpp_domain }}":
|
||||
domain_certfile: "/opt/ejabberd/ssl/{{ xmpp_domain }}.pem"
|
||||
{%- endfor %}
|
||||
|
||||
{%- if env['EJABBERD_CONFIGURE_ODBC'] == "true" %}
|
||||
### ====================
|
||||
### ODBC DATABASE CONFIG
|
||||
sql_type: {{ env['EJABBERD_ODBC_TYPE'] }}
|
||||
sql_server: "{{ env['EJABBERD_ODBC_SERVER'] }}"
|
||||
sql_database: "{{ env['EJABBERD_ODBC_DATABASE'] }}"
|
||||
sql_username: "{{ env['EJABBERD_ODBC_USERNAME'] }}"
|
||||
sql_password: "{{ env['EJABBERD_ODBC_PASSWORD'] }}"
|
||||
|
||||
default_db: sql
|
||||
{% endif %}
|
||||
|
||||
{%- if env['EJABBERD_DEFAULT_DB'] is defined %}
|
||||
default_db: {{ env['EJABBERD_DEFAULT_DB'] }}
|
||||
{% endif %}
|
||||
|
||||
### =====================
|
||||
### SESSION MANAGEMENT DB
|
||||
sm_db_type: {{ env['EJABBERD_SESSION_DB'] or "mnesia" }}
|
||||
|
||||
{%- if env['EJABBERD_CONFIGURE_REDIS'] == "true" %}
|
||||
### ====================
|
||||
### REDIS DATABASE CONFIG
|
||||
redis_server: {{ env['EJABBERD_REDIS_SERVER'] or "localhost" }}
|
||||
redis_port: {{ env['EJABBERD_REDIS_PORT'] or 6379 }}
|
||||
{%- if env['EJABBERD_REDIS_PASSWORD'] is defined %}
|
||||
redis_password: {{ env['EJABBERD_REDIS_PASSWORD'] }}
|
||||
{% endif %}
|
||||
redis_db: {{ env['EJABBERD_REDIS_DB'] or 0}}
|
||||
redis_reconnect_timeout: {{ env['EJABBERD_REDIS_RECONNECT_TIMEOUT'] or 1 }}
|
||||
redis_connect_timeout: {{ env['EJABBERD_REDIS_CONNECT_TIMEOUT'] or 1 }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,187 @@
|
||||
#
|
||||
# In this file you can configure options that are passed by ejabberdctl
|
||||
# to the erlang runtime system when starting ejabberd
|
||||
#
|
||||
|
||||
#' POLL: Kernel polling ([true|false])
|
||||
#
|
||||
# The kernel polling option requires support in the kernel.
|
||||
# Additionally, you need to enable this feature while compiling Erlang.
|
||||
#
|
||||
# Default: true
|
||||
#
|
||||
#POLL=true
|
||||
|
||||
#.
|
||||
#' SMP: SMP support ([enable|auto|disable])
|
||||
#
|
||||
# Explanation in Erlang/OTP documentation:
|
||||
# enable: starts the Erlang runtime system with SMP support enabled.
|
||||
# This may fail if no runtime system with SMP support is available.
|
||||
# auto: starts the Erlang runtime system with SMP support enabled if it
|
||||
# is available and more than one logical processor are detected.
|
||||
# disable: starts a runtime system without SMP support.
|
||||
#
|
||||
# Default: auto
|
||||
#
|
||||
#SMP=auto
|
||||
|
||||
#.
|
||||
#' ERL_MAX_PORTS: Maximum number of simultaneously open Erlang ports
|
||||
#
|
||||
# ejabberd consumes two or three ports for every connection, either
|
||||
# from a client or from another Jabber server. So take this into
|
||||
# account when setting this limit.
|
||||
#
|
||||
# Default: 32000
|
||||
# Maximum: 268435456
|
||||
#
|
||||
#ERL_MAX_PORTS=32000
|
||||
|
||||
#.
|
||||
#' FIREWALL_WINDOW: Range of allowed ports to pass through a firewall
|
||||
#
|
||||
# If Ejabberd is configured to run in cluster, and a firewall is blocking ports,
|
||||
# it's possible to make Erlang use a defined range of port (instead of dynamic
|
||||
# ports) for node communication.
|
||||
#
|
||||
# Default: not defined
|
||||
# Example: 4200-4210
|
||||
#
|
||||
#FIREWALL_WINDOW=
|
||||
|
||||
#.
|
||||
#' INET_DIST_INTERFACE: IP address where this Erlang node listens other nodes
|
||||
#
|
||||
# This communication is used by ejabberdctl command line tool,
|
||||
# and in a cluster of several ejabberd nodes.
|
||||
#
|
||||
# Default: 127.0.0.1
|
||||
#
|
||||
#INET_DIST_INTERFACE=127.0.0.1
|
||||
|
||||
#.
|
||||
#' ERL_EPMD_ADDRESS: IP addresses where epmd listens for connections
|
||||
#
|
||||
# IMPORTANT: This option works only in Erlang/OTP R14B03 and newer.
|
||||
#
|
||||
# This environment variable may be set to a comma-separated
|
||||
# list of IP addresses, in which case the epmd daemon
|
||||
# will listen only on the specified address(es) and on the
|
||||
# loopback address (which is implicitly added to the list if it
|
||||
# has not been specified). The default behaviour is to listen on
|
||||
# all available IP addresses.
|
||||
#
|
||||
# Default: 0.0.0.0
|
||||
#
|
||||
#ERL_EPMD_ADDRESS=127.0.0.1
|
||||
|
||||
#.
|
||||
#' ERL_PROCESSES: Maximum number of Erlang processes
|
||||
#
|
||||
# Erlang consumes a lot of lightweight processes. If there is a lot of activity
|
||||
# on ejabberd so that the maximum number of processes is reached, people will
|
||||
# experience greater latency times. As these processes are implemented in
|
||||
# Erlang, and therefore not related to the operating system processes, you do
|
||||
# not have to worry about allowing a huge number of them.
|
||||
#
|
||||
# Default: 250000
|
||||
# Maximum: 268435456
|
||||
#
|
||||
#ERL_PROCESSES=250000
|
||||
|
||||
#.
|
||||
#' ERL_MAX_ETS_TABLES: Maximum number of ETS and Mnesia tables
|
||||
#
|
||||
# The number of concurrent ETS and Mnesia tables is limited. When the limit is
|
||||
# reached, errors will appear in the logs:
|
||||
# ** Too many db tables **
|
||||
# You can safely increase this limit when starting ejabberd. It impacts memory
|
||||
# consumption but the difference will be quite small.
|
||||
#
|
||||
# Default: 1400
|
||||
#
|
||||
#ERL_MAX_ETS_TABLES=1400
|
||||
|
||||
#.
|
||||
#' ERL_OPTIONS: Additional Erlang options
|
||||
#
|
||||
# The next variable allows to specify additional options passed to erlang while
|
||||
# starting ejabberd. Some useful options are -noshell, -detached, -heart. When
|
||||
# ejabberd is started from an init.d script options -noshell and -detached are
|
||||
# added implicitly. See erl(1) for more info.
|
||||
#
|
||||
# It might be useful to add "-pa /usr/local/lib/ejabberd/ebin" if you
|
||||
# want to add local modules in this path.
|
||||
#
|
||||
# Default: ""
|
||||
#
|
||||
ERL_OPTIONS="{{ env['ERL_OPTIONS'] or "-noshell" }}"
|
||||
|
||||
#.
|
||||
#' ERLANG_NODE: Erlang node name
|
||||
#
|
||||
# The next variable allows to explicitly specify erlang node for ejabberd
|
||||
# It can be given in different formats:
|
||||
# ERLANG_NODE=ejabberd
|
||||
# Lets erlang add hostname to the node (ejabberd uses short name in this case)
|
||||
# ERLANG_NODE=ejabberd@hostname
|
||||
# Erlang uses node name as is (so make sure that hostname is a real
|
||||
# machine hostname or you'll not be able to control ejabberd)
|
||||
# ERLANG_NODE=ejabberd@hostname.domainname
|
||||
# The same as previous, but erlang will use long hostname
|
||||
# (see erl (1) manual for details)
|
||||
#
|
||||
# Default: ejabberd@localhost
|
||||
#
|
||||
ERLANG_NODE={{ env['ERLANG_NODE'] or "ejabberd@localhost" }}
|
||||
|
||||
#.
|
||||
#' EJABBERD_PID_PATH: ejabberd PID file
|
||||
#
|
||||
# Indicate the full path to the ejabberd Process identifier (PID) file.
|
||||
# If this variable is defined, ejabberd writes the PID file when starts,
|
||||
# and deletes it when stops.
|
||||
# Remember to create the directory and grant write permission to ejabberd.
|
||||
#
|
||||
# Default: don't write PID file
|
||||
#
|
||||
#EJABBERD_PID_PATH=/var/run/ejabberd/ejabberd.pid
|
||||
|
||||
#.
|
||||
#' EJABBERD_CONFIG_PATH: ejabberd configuration file
|
||||
#
|
||||
# Specify the full path to the ejabberd configuration file. If the file name has
|
||||
# yml or yaml extension, it is parsed as a YAML file; otherwise, Erlang syntax is
|
||||
# expected.
|
||||
#
|
||||
# Default: $ETC_DIR/ejabberd.yml
|
||||
#
|
||||
#EJABBERD_CONFIG_PATH=/etc/ejabberd/ejabberd.yml
|
||||
|
||||
#.
|
||||
#' CONTRIB_MODULES_PATH: contributed ejabberd modules path
|
||||
#
|
||||
# Specify the full path to the contributed ejabberd modules. If the path is not
|
||||
# defined, ejabberd will use ~/.ejabberd-modules in home of user running ejabberd.
|
||||
#
|
||||
# Default: $HOME/.ejabberd-modules
|
||||
#
|
||||
#CONTRIB_MODULES_PATH=/opt/ejabberd-modules
|
||||
|
||||
#.
|
||||
#' EJABBERD_BYPASS_WARNINGS: Bypass LIVE warning
|
||||
#
|
||||
# Default: don't bypass the warning
|
||||
#
|
||||
EJABBERD_BYPASS_WARNINGS=true
|
||||
|
||||
#.
|
||||
#' SPOOL_DIR: Database spool dir
|
||||
#
|
||||
# Specify the full path to the database spool dir used in binary installer for
|
||||
# backwards compatibility.
|
||||
#
|
||||
# Docker: rroemhild/ejabberd
|
||||
#
|
||||
SPOOL_DIR=$EJABBERD_HOME/database/$ERLANG_NODE
|
||||
@@ -0,0 +1,3 @@
|
||||
{lookup,["file","native"]}.
|
||||
{host,{127,0,0,1}, ["localhost","hostalias"]}.
|
||||
{file, resolv, "/etc/resolv.conf"}.
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
# discover hostname
|
||||
readonly nodename=$(get_nodename)
|
||||
|
||||
is_zero ${ERLANG_NODE} \
|
||||
&& export ERLANG_NODE="ejabberd@localhost"
|
||||
|
||||
## backward compatibility
|
||||
# if ERLANG_NODE is true reset it to "ejabberd" and add
|
||||
# hostname to the nodename.
|
||||
# else: export ${ERLANG_NODE} with nodename
|
||||
if (is_true ${ERLANG_NODE}); then
|
||||
export ERLANG_NODE="ejabberd@${nodename}"
|
||||
fi
|
||||
|
||||
|
||||
run_scripts() {
|
||||
local run_script_dir="${EJABBERD_HOME}/scripts/${1}"
|
||||
for script in ${run_script_dir}/*.sh ; do
|
||||
if [ -f ${script} -a -x ${script} ] ; then
|
||||
${script}
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
pre_scripts() {
|
||||
run_scripts "pre"
|
||||
}
|
||||
|
||||
|
||||
post_scripts() {
|
||||
run_scripts "post"
|
||||
}
|
||||
|
||||
stop_scripts() {
|
||||
run_scripts "stop"
|
||||
}
|
||||
|
||||
|
||||
ctl() {
|
||||
local action="$1"
|
||||
${EJABBERDCTL} ${action} >/dev/null
|
||||
}
|
||||
|
||||
|
||||
_trap() {
|
||||
echo "Stopping ejabberd..."
|
||||
stop_scripts
|
||||
if ctl stop ; then
|
||||
local cnt=0
|
||||
sleep 1
|
||||
while ctl status || test $? = 1 ; do
|
||||
cnt=`expr $cnt + 1`
|
||||
if [ $cnt -ge 60 ] ; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Catch signals and shutdown ejabberd
|
||||
trap _trap SIGTERM SIGINT
|
||||
|
||||
## run ejabberd
|
||||
case "$@" in
|
||||
start)
|
||||
pre_scripts
|
||||
tail -n 0 -F ${LOGDIR}/crash.log \
|
||||
${LOGDIR}/error.log \
|
||||
${LOGDIR}/erlang.log &
|
||||
echo "Starting ejabberd..."
|
||||
exec ${EJABBERDCTL} "foreground" &
|
||||
child=$!
|
||||
${EJABBERDCTL} "started"
|
||||
post_scripts
|
||||
wait $child
|
||||
;;
|
||||
live)
|
||||
pre_scripts
|
||||
echo "Starting ejabberd in 'live' mode..."
|
||||
exec ${EJABBERDCTL} "live"
|
||||
;;
|
||||
shell)
|
||||
exec "/bin/bash"
|
||||
;;
|
||||
*)
|
||||
exec $@
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,53 @@
|
||||
readonly HOSTIP=$(hostname -i)
|
||||
readonly HOSTNAME=$(hostname -f)
|
||||
readonly DOMAINNAME=$(hostname -d)
|
||||
|
||||
readonly ERLANGCOOKIEFILE="${EJABBERD_HOME}/.erlang.cookie"
|
||||
readonly EJABBERDCTL="/sbin/ejabberdctl"
|
||||
readonly CONFIGFILE="${EJABBERD_HOME}/conf/ejabberd.yml"
|
||||
readonly CONFIGTEMPLATE="${EJABBERD_HOME}/conf/ejabberd.yml.tpl"
|
||||
readonly CTLCONFIGFILE="${EJABBERD_HOME}/conf/ejabberdctl.cfg"
|
||||
readonly CTLCONFIGTEMPLATE="${EJABBERD_HOME}/conf/ejabberdctl.cfg.tpl"
|
||||
readonly SSLCERTDIR="${EJABBERD_HOME}/ssl"
|
||||
readonly SSLCERTHOST="${SSLCERTDIR}/host.pem"
|
||||
readonly SSLDHPARAM="${SSLCERTDIR}/dh.pem"
|
||||
readonly LOGDIR="/var/log/ejabberd"
|
||||
readonly FIRST_START_DONE_FILE="/${EJABBERD_HOME}/first-start-done"
|
||||
readonly CLUSTER_NODE_FILE="/${EJABBERD_HOME}/cluster-done"
|
||||
|
||||
readonly PYTHON_JINJA2="import os;
|
||||
import sys;
|
||||
import jinja2;
|
||||
sys.stdout.write(
|
||||
jinja2.Template
|
||||
(sys.stdin.read()
|
||||
).render(env=os.environ))"
|
||||
|
||||
# backward compatibility environment variables
|
||||
set +e
|
||||
|
||||
[[ -n $EJABBERD_ADMIN ]] \
|
||||
&& export EJABBERD_ADMINS=${EJABBERD_ADMIN}
|
||||
|
||||
[[ -n $AUTH_METHOD ]] \
|
||||
&& export EJABBERD_AUTH_METHOD=${AUTH_METHOD}
|
||||
|
||||
[[ -n $SKIP_MODULES_UPDATE ]] \
|
||||
&& export EJABBERD_SKIP_MODULES_UPDATE=${SKIP_MODULES_UPDATE}
|
||||
|
||||
[[ -n $ERL_OPTIONS ]] \
|
||||
&& export ERLANG_OPTIONS=${ERL_OPTIONS}
|
||||
|
||||
[[ -n $SSLCERT_HOST ]] \
|
||||
&& export EJABBERD_SSLCERT_HOST=${SSLCERT_HOST}
|
||||
|
||||
[[ -n $SSLCERT_EXAMPLE_COM ]] \
|
||||
&& export EJABBERD_SSLCERT_EXAMPLE_COM=${SSLCERT_EXAMPLE_COM}
|
||||
|
||||
[[ -n $LOGLEVEL ]] \
|
||||
&& export EJABBERD_LOGLEVEL=${LOGLEVEL}
|
||||
|
||||
[[ -n $EJABBERD_WEB_ADMIN_SSL ]] \
|
||||
&& export EJABBERD_HTTPS=${EJABBERD_WEB_ADMIN_SSL}
|
||||
|
||||
set -e
|
||||
@@ -0,0 +1,66 @@
|
||||
is_set() {
|
||||
local var=$1
|
||||
|
||||
[[ -n $var ]]
|
||||
}
|
||||
|
||||
|
||||
is_zero() {
|
||||
local var=$1
|
||||
|
||||
[[ -z $var ]]
|
||||
}
|
||||
|
||||
|
||||
file_exist() {
|
||||
local file=$1
|
||||
|
||||
[[ -e $file ]]
|
||||
}
|
||||
|
||||
|
||||
is_true() {
|
||||
local var=${1,,}
|
||||
local choices=("yes" "1" "y" "true")
|
||||
for ((i=0;i < ${#choices[@]};i++)) {
|
||||
[[ "${choices[i]}" == $var ]] && return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# overwrite this function to get hostname from other sources
|
||||
# like dns or etcd
|
||||
get_nodename() {
|
||||
echo ${HOSTNAME}
|
||||
}
|
||||
|
||||
|
||||
join_cluster() {
|
||||
local cluster_node=$1
|
||||
|
||||
is_zero ${cluster_node} \
|
||||
&& exit 0
|
||||
|
||||
echo "Join cluster..."
|
||||
|
||||
local erlang_node_name=${ERLANG_NODE%@*}
|
||||
local erlang_cluster_node="${erlang_node_name}@${cluster_node}"
|
||||
|
||||
response=$(${EJABBERDCTL} ping ${erlang_cluster_node})
|
||||
while [ "$response" != "pong" ]; do
|
||||
echo "Waiting for ${erlang_cluster_node}..."
|
||||
sleep 2
|
||||
response=$(${EJABBERDCTL} ping ${erlang_cluster_node})
|
||||
done
|
||||
|
||||
echo "Join cluster at ${erlang_cluster_node}... "
|
||||
NO_WARNINGS=true ${EJABBERDCTL} join_cluster $erlang_cluster_node
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
touch ${CLUSTER_NODE_FILE}
|
||||
else
|
||||
echo "cloud not join cluster"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# Overridable file
|
||||
@@ -0,0 +1 @@
|
||||
# Overridable file
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Updates the known modules as to be found in https://github.com/processone/ejabberd-contrib
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
|
||||
run_modules_update_specs() {
|
||||
echo -n 'Updating module specs... '
|
||||
${EJABBERDCTL} modules_update_specs
|
||||
}
|
||||
|
||||
|
||||
is_true ${EJABBERD_SKIP_MODULES_UPDATE} \
|
||||
&& exit 0
|
||||
|
||||
run_modules_update_specs
|
||||
|
||||
|
||||
exit 0
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Installs modules as defined in environment variables
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
install_module_from_source() {
|
||||
local module_name=$1
|
||||
local module_source_path=${EJABBERD_HOME}/module_source/${module_name}
|
||||
local module_install_folder=${EJABBERD_HOME}/.ejabberd-modules/sources
|
||||
|
||||
echo "Analyzing module ${module_name} for installation"
|
||||
# Make sure that the module exists in the source folder before attempting a copy
|
||||
|
||||
if [ ! -d ${module_source_path} ]; then
|
||||
echo "Error: Module ${module_name} not found in ${EJABBERD_HOME}/module_source"
|
||||
echo "Please use a shared volume to populate your module in ${EJABBERD_HOME}/module_source"
|
||||
return 1;
|
||||
fi
|
||||
|
||||
# Check to see if the module is already installed
|
||||
local install_count=$(${EJABBERDCTL} modules_installed | grep -ce "^${module_name}[[:space:]]")
|
||||
if [ $install_count -gt 0 ]; then
|
||||
echo "Error: Module already installed: ${module_name}"
|
||||
return 1;
|
||||
fi
|
||||
|
||||
# Copy the module into the shared folder
|
||||
echo "Copying module to ejabberd folder ${module_install_folder}"
|
||||
mkdir -p ${module_install_folder}
|
||||
cp -R ${module_source_path} ${module_install_folder}
|
||||
|
||||
# Run the ejabberdctl module_check on the module
|
||||
echo "Running module_check on ${module_name}"
|
||||
${EJABBERDCTL} module_check ${module_name}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Module check failed for ${module_name}"
|
||||
return 1;
|
||||
fi
|
||||
echo "Module check succeeded for ${module_name}"
|
||||
|
||||
# Install the module
|
||||
echo "Running module_install on ${module_name}"
|
||||
${EJABBERDCTL} module_install ${module_name}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Module installation failed for ${module_name}"
|
||||
return 1;
|
||||
fi
|
||||
echo "Module installation succeeded for ${module_name}"
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
install_module_from_ejabberd_contrib() {
|
||||
local module_name=$1
|
||||
|
||||
# Check to see if the module is already installed
|
||||
local install_count=$(${EJABBERDCTL} modules_installed | grep -ce "^${module_name}[[:space:]]")
|
||||
if [ $install_count -gt 0 ]; then
|
||||
echo "Error: Module already installed: ejabberd_contrib ${module_name}"
|
||||
return 1;
|
||||
fi
|
||||
|
||||
# Install the module
|
||||
echo "Running module_install on ejabberd_contrib ${module_name}"
|
||||
${EJABBERDCTL} module_install ${module_name}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Module installation failed for ejabberd_contrib ${module_name}"
|
||||
return 1;
|
||||
fi
|
||||
echo "Module installation succeeded for ejabberd_contrib ${module_name}"
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enable_custom_auth_module_override() {
|
||||
module_name=$1;
|
||||
# When using custom authentication modules, the module name must be
|
||||
# in the following pattern: ejabberd_auth_foo, where foo is the
|
||||
# value you will use for your auth_method yml configuration.
|
||||
required_prefix="ejabberd_auth_"
|
||||
|
||||
if [[ "${module_name}" != "${required_prefix}"* ]]; then
|
||||
echo "Error: module_name must begin with ${required_prefix}"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
echo "Checking custom auth module: ${module_name}"
|
||||
# Make sure the auth module is installed
|
||||
local install_count=$(${EJABBERDCTL} modules_installed | grep -ce "^${module_name}[[:space:]]")
|
||||
if [ $install_count -eq 0 ]; then
|
||||
echo "Error: custom auth_module not installed: ${module_name}"
|
||||
return 1;
|
||||
fi
|
||||
|
||||
custom_auth_method=${module_name#$required_prefix}
|
||||
echo -e "\nauth_method: [${custom_auth_method}]" >> ${CONFIGFILE}
|
||||
echo "Custom auth module ${module_name} configuration complete."
|
||||
}
|
||||
|
||||
file_exist ${FIRST_START_DONE_FILE} \
|
||||
&& exit 0
|
||||
|
||||
is_restart_needed=0;
|
||||
|
||||
if [ -n "${EJABBERD_SOURCE_MODULES}" ]; then
|
||||
for module_name in ${EJABBERD_SOURCE_MODULES} ; do
|
||||
install_module_from_source ${module_name}
|
||||
done
|
||||
is_restart_needed=1;
|
||||
fi
|
||||
|
||||
# Check the EJABBERD_CONTRIB_MODULES variable for any ejabberd_contrib modules
|
||||
if [ -n "${EJABBERD_CONTRIB_MODULES}" ]; then
|
||||
for module_name in ${EJABBERD_CONTRIB_MODULES} ; do
|
||||
install_module_from_ejabberd_contrib ${module_name}
|
||||
done
|
||||
is_restart_needed=1;
|
||||
fi
|
||||
|
||||
# If a custom module was defined for handling auth, we need to override
|
||||
# the pre-defined auth methods in the config.
|
||||
if [ -n "${EJABBERD_CUSTOM_AUTH_MODULE_OVERRIDE}" ]; then
|
||||
enable_custom_auth_module_override "${EJABBERD_CUSTOM_AUTH_MODULE_OVERRIDE}"
|
||||
is_restart_needed=1;
|
||||
fi
|
||||
|
||||
# If any modules were installed, restart the server, if the option is enabled
|
||||
if [ ${is_restart_needed} -eq 1 ]; then
|
||||
if is_true ${EJABBERD_RESTART_AFTER_MODULE_INSTALL} ; then
|
||||
echo "Restarting ejabberd after successful module installation(s)"
|
||||
${EJABBERDCTL} restart
|
||||
child=$!
|
||||
${EJABBERDCTL} "started"
|
||||
wait $child
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
#!/bin/bash
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
# Do not exit if users already registered
|
||||
set +e
|
||||
|
||||
randpw() {
|
||||
< /dev/urandom tr -dc A-Z-a-z-0-9 | head -c ${1:-16};
|
||||
echo;
|
||||
}
|
||||
|
||||
|
||||
register_user() {
|
||||
local user=$1
|
||||
local domain=$2
|
||||
local password=$3
|
||||
|
||||
${EJABBERDCTL} register ${user} ${domain} ${password}
|
||||
return $?
|
||||
}
|
||||
|
||||
|
||||
register_all_users() {
|
||||
# register users from environment $EJABBERD_USERS with given
|
||||
# password or random password written to stout. Use whitespace
|
||||
# to seperate users.
|
||||
#
|
||||
# sample:
|
||||
# - add a user with an given password:
|
||||
# -e "EJABBERD_USERS=admin@example.com:adminSecret"
|
||||
# - add a user with a random password:
|
||||
# -e "EJABBERD_USERS=user@example.com"
|
||||
# - set password for admin and use random for user1:
|
||||
# -e "EJABBERD_USERS=admin@example.com:adminSecret user@example.com"
|
||||
|
||||
for user in ${EJABBERD_USERS} ; do
|
||||
local jid=${user%%:*}
|
||||
local password=${user#*:}
|
||||
|
||||
local username=${jid%%@*}
|
||||
local domain=${jid#*@}
|
||||
|
||||
[[ "${password}" == "${jid}" ]] \
|
||||
&& password=$(randpw)
|
||||
|
||||
register_user ${username} ${domain} ${password}
|
||||
local retval=$?
|
||||
|
||||
[[ ${retval} -eq 0 ]] \
|
||||
&& echo "Password for user ${username}@${domain} is ${password}"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
file_exist ${FIRST_START_DONE_FILE} \
|
||||
&& exit 0
|
||||
|
||||
|
||||
file_exist ${CLUSTER_NODE_FILE} \
|
||||
&& exit 0
|
||||
|
||||
|
||||
is_set ${EJABBERD_USERS} \
|
||||
&& register_all_users
|
||||
|
||||
|
||||
##################################
|
||||
## Keep for backward compatibility
|
||||
|
||||
register_all_ejabberd_admins() {
|
||||
# add all admins from environment $EJABBERD_ADMINS with the passwords from
|
||||
# environment $EJABBERD_ADMIN_PASS.
|
||||
|
||||
local passwords
|
||||
local IFS=' '
|
||||
read -a passwords <<< "${EJABBERD_ADMIN_PWD}"
|
||||
|
||||
for admin in ${EJABBERD_ADMINS} ; do
|
||||
local user=${admin%%@*}
|
||||
local domain=${admin#*@}
|
||||
local password=${passwords[0]}
|
||||
passwords=("${passwords[@]:1}")
|
||||
register_user ${user} ${domain} ${password}
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
register_all_ejabberd_admins_randpw() {
|
||||
# add all admins from environment $EJABBERD_ADMINS with a random
|
||||
# password and write the password to stdout.
|
||||
|
||||
for admin in ${EJABBERD_ADMINS} ; do
|
||||
local user=${admin%%@*}
|
||||
local domain=${admin#*@}
|
||||
local password=$(randpw)
|
||||
|
||||
register_user ${user} ${domain} ${password}
|
||||
local retval=$?
|
||||
|
||||
[[ ${retval} -eq 0 ]] \
|
||||
&& echo "Password for user ${user}@${domain} is ${password}"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
is_set ${EJABBERD_ADMIN_PWD} \
|
||||
&& register_all_ejabberd_admins
|
||||
|
||||
|
||||
is_true ${EJABBERD_ADMIN_RANDPWD} \
|
||||
&& register_all_ejabberd_admins_randpw
|
||||
|
||||
|
||||
exit 0
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Write a first-start-done file
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
|
||||
if [ ! -e "${FIRST_START_DONE_FILE}" ]; then
|
||||
touch ${FIRST_START_DONE_FILE}
|
||||
fi
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
|
||||
readonly whoami=$(whoami)
|
||||
|
||||
|
||||
change_ejabberd_run_user() {
|
||||
echo "Change ejabberd install user to root..."
|
||||
sed -i "s/INSTALLUSER=${EJABBERD_USER}/INSTALLUSER=${whoami}/" ${EJABBERDCTL}
|
||||
}
|
||||
|
||||
|
||||
[[ "${whoami}" == "root" ]] \
|
||||
&& change_ejabberd_run_user
|
||||
|
||||
|
||||
exit 0
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
# Instead of having to mount a direction, specify the ssl certs
|
||||
# via environment variables:
|
||||
# `EJABBERD_SSLCERT_HOST` and `EJABBERD_SSLCERT_{domain_name}`.
|
||||
# For example: `EJABBERD_SSLCERT_EXAMPLE_COM`.
|
||||
|
||||
write_file_from_env() {
|
||||
echo "Writing $1 to $2"
|
||||
mkdir -p "$(dirname $2)"
|
||||
echo "${!1}" > $2
|
||||
}
|
||||
|
||||
# Write the host certificate
|
||||
is_set ${EJABBERD_SSLCERT_HOST} \
|
||||
&& write_file_from_env "EJABBERD_SSLCERT_HOST" ${SSLCERTHOST}
|
||||
|
||||
# Write the domain certificates for each XMPP_DOMAIN
|
||||
for xmpp_domain in ${XMPP_DOMAIN} ; do
|
||||
var="EJABBERD_SSLCERT_$(echo $xmpp_domain | awk '{print toupper($0)}' | sed 's/\./_/g;s/-/_/g')"
|
||||
if is_set ${!var} ; then
|
||||
file_exist "${SSLCERTDIR}/${xmpp_domain}.pem" \
|
||||
|| write_file_from_env "$var" "${SSLCERTDIR}/${xmpp_domain}.pem"
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
|
||||
make_snakeoil_certificate() {
|
||||
local domain=$1
|
||||
local certfile=$2
|
||||
|
||||
openssl req -subj "/CN=${domain}" \
|
||||
-new \
|
||||
-newkey rsa:4096 \
|
||||
-days 365 \
|
||||
-nodes \
|
||||
-x509 \
|
||||
-keyout /tmp/selfsigned.key \
|
||||
-out /tmp/selfsigned.crt
|
||||
|
||||
echo "Writing ssl cert and private key to '${certfile}'..."
|
||||
cat /tmp/selfsigned.crt /tmp/selfsigned.key > ${certfile}
|
||||
rm /tmp/selfsigned.crt /tmp/selfsigned.key
|
||||
}
|
||||
|
||||
|
||||
make_host_snakeoil_certificate() {
|
||||
local IFS=@
|
||||
local domain='localhost'
|
||||
local erlang_node=${ERLANG_NODE}
|
||||
|
||||
if is_true ${erlang_node} ; then
|
||||
domain=${HOSTNAME}
|
||||
elif is_set ${erlang_node} ; then
|
||||
set ${erlang_node}
|
||||
local nodehost=$2
|
||||
if is_zero ${nodehost} ; then
|
||||
domain=${HOSTNAME}
|
||||
else
|
||||
domain=${nodehost}
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -n "Missing ssl cert for your host. "
|
||||
echo "Generating snakeoil ssl cert for ${domain}..."
|
||||
|
||||
make_snakeoil_certificate ${domain} ${SSLCERTHOST}
|
||||
}
|
||||
|
||||
|
||||
make_domain_snakeoil_certificate() {
|
||||
local domain=$1
|
||||
local certfile=$2
|
||||
|
||||
echo -n "Missing ssl cert for your xmpp domain. "
|
||||
echo "Generating snakeoil ssl cert for ${domain}..."
|
||||
|
||||
make_snakeoil_certificate ${domain} ${certfile}
|
||||
}
|
||||
|
||||
|
||||
## backward compatibility
|
||||
# link old xmpp_domain.pem file to the first <domainname>.pem in XMPP_DOMAIN
|
||||
readonly SSLCERTDOMAIN="${SSLCERTDIR}/xmpp_domain.pem"
|
||||
if file_exist ${SSLCERTDOMAIN} ; then
|
||||
for xmpp_domain in ${XMPP_DOMAIN} ; do
|
||||
file_exist "${SSLCERTDIR}/${xmpp_domain}.pem" \
|
||||
|| ln -s ${SSLCERTDOMAIN} "${SSLCERTDIR}/${xmpp_domain}.pem"
|
||||
break
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
is_true ${EJABBERD_SKIP_MAKE_SSLCERT} \
|
||||
&& echo "Skip certificate generation" \
|
||||
&& exit 0
|
||||
|
||||
# generate host ssl cert if missing
|
||||
file_exist ${SSLCERTHOST} \
|
||||
|| make_host_snakeoil_certificate
|
||||
|
||||
# generate xmmp domain ssl certificates if missing
|
||||
for xmpp_domain in ${XMPP_DOMAIN} ; do
|
||||
domain_certfile="${SSLCERTDIR}/${xmpp_domain}.pem"
|
||||
file_exist ${domain_certfile} \
|
||||
|| make_domain_snakeoil_certificate ${xmpp_domain} ${domain_certfile}
|
||||
done
|
||||
|
||||
exit 0
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
|
||||
make_dhparam() {
|
||||
local dhfile=$1
|
||||
local bits=$2
|
||||
|
||||
echo "Writing dh file to '${dhfile}'..."
|
||||
openssl dhparam -out ${dhfile} ${bits}
|
||||
}
|
||||
|
||||
|
||||
is_true ${EJABBERD_SKIP_MAKE_DHPARAM} \
|
||||
&& echo "Skip DH param generation" \
|
||||
&& exit 0
|
||||
|
||||
if is_true ${EJABBERD_DHPARAM} ; then
|
||||
file_exist ${SSLDHPARAM} \
|
||||
|| make_dhparam ${SSLDHPARAM} 4096
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
|
||||
set_erlang_cookie() {
|
||||
echo "Set erlang cookie to ${ERLANG_COOKIE}..."
|
||||
echo ${ERLANG_COOKIE} > ${ERLANGCOOKIEFILE}
|
||||
chmod 400 ${ERLANGCOOKIEFILE}
|
||||
}
|
||||
|
||||
|
||||
file_exist ${FIRST_START_DONE_FILE} \
|
||||
&& exit 0
|
||||
|
||||
|
||||
# set erlang cookie if ERLANG_COOKIE is set in environemt
|
||||
is_set ${ERLANG_COOKIE} \
|
||||
&& set_erlang_cookie
|
||||
|
||||
exit 0
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
|
||||
make_config() {
|
||||
if [ ! -e ${CONFIGFILE} ]; then
|
||||
echo "Generating ejabberd config file..."
|
||||
cat ${CONFIGTEMPLATE} \
|
||||
| python -c "${PYTHON_JINJA2}" \
|
||||
> ${CONFIGFILE}
|
||||
else
|
||||
echo "ejabberd config file exists."
|
||||
fi
|
||||
|
||||
if [ ! -e ${CTLCONFIGFILE} ]; then
|
||||
echo "Generating ejabberdctl config file..."
|
||||
cat ${CTLCONFIGTEMPLATE} \
|
||||
| python -c "${PYTHON_JINJA2}" \
|
||||
> ${CTLCONFIGFILE}
|
||||
else
|
||||
echo "ejabberdctl config file exists."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
file_exist ${FIRST_START_DONE_FILE} \
|
||||
&& exit 0
|
||||
|
||||
|
||||
# generate config file
|
||||
make_config
|
||||
|
||||
exit 0
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/config.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/base_functions.sh"
|
||||
source "${EJABBERD_HOME}/scripts/lib/functions.sh"
|
||||
|
||||
|
||||
leave_cluster() {
|
||||
echo "Leave cluster... "
|
||||
rm ${CLUSTER_NODE_FILE}
|
||||
NO_WARNINGS=true ${EJABBERDCTL} leave_cluster
|
||||
}
|
||||
|
||||
|
||||
file_exist ${CLUSTER_NODE_FILE} \
|
||||
&& leave_cluster
|
||||
|
||||
|
||||
exit 0
|
||||
@@ -3,22 +3,17 @@ Description=XMPP Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=ejabberd
|
||||
Group=ejabberd
|
||||
LimitNOFILE=16000
|
||||
LimitNOFILE=65536
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
ExecStart=@ctlscriptpath@/ejabberdctl start
|
||||
ExecStop=@ctlscriptpath@/ejabberdctl stop
|
||||
ExecStart=/bin/sh -c '@ctlscriptpath@/ejabberdctl start && @ctlscriptpath@/ejabberdctl started'
|
||||
ExecStop=/bin/sh -c '@ctlscriptpath@/ejabberdctl stop && @ctlscriptpath@/ejabberdctl stopped'
|
||||
ExecReload=@ctlscriptpath@/ejabberdctl reload_config
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
# The CAP_DAC_OVERRIDE capability is required for pam authentication to work
|
||||
CapabilityBoundingSet=CAP_DAC_OVERRIDE
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectHome=true
|
||||
ProtectSystem=full
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
+138
-27
@@ -106,6 +106,17 @@ hosts:
|
||||
###. ===============
|
||||
###' LISTENING PORTS
|
||||
|
||||
## Define common macros used by listeners
|
||||
## define_macro:
|
||||
## 'CERTFILE': "/path/to/xmpp.pem"
|
||||
## 'CIPHERS': "ECDH:DH:!3DES:!aNULL:!eNULL:!MEDIUM@STRENGTH"
|
||||
## 'TLSOPTS':
|
||||
## - "no_sslv3"
|
||||
## - "no_tlsv1"
|
||||
## - "cipher_server_preference"
|
||||
## - "no_compression"
|
||||
## 'DHFILE': "/path/to/dhparams.pem" # generated with: openssl dhparam -out dhparams.pem 2048
|
||||
|
||||
##
|
||||
## listen: The ports ejabberd will listen on, which service each is handled
|
||||
## by and what options to start it with.
|
||||
@@ -113,36 +124,53 @@ hosts:
|
||||
listen:
|
||||
-
|
||||
port: 5222
|
||||
ip: "::"
|
||||
module: ejabberd_c2s
|
||||
##
|
||||
## If TLS is compiled in and you installed a SSL
|
||||
## certificate, specify the full path to the
|
||||
## file and uncomment these lines:
|
||||
##
|
||||
## certfile: "/path/to/ssl.pem"
|
||||
## starttls: true
|
||||
## certfile: 'CERTFILE'
|
||||
## protocol_options: 'TLSOPTS'
|
||||
## dhfile: 'DHFILE'
|
||||
## ciphers: 'CIPHERS'
|
||||
##
|
||||
## To enforce TLS encryption for client connections,
|
||||
## use this instead of the "starttls" option:
|
||||
##
|
||||
## starttls_required: true
|
||||
##
|
||||
## Custom OpenSSL options
|
||||
## Stream compression
|
||||
##
|
||||
## zlib: true
|
||||
##
|
||||
## protocol_options:
|
||||
## - "no_sslv3"
|
||||
## - "no_tlsv1"
|
||||
max_stanza_size: 65536
|
||||
shaper: c2s_shaper
|
||||
access: c2s
|
||||
-
|
||||
port: 5269
|
||||
ip: "::"
|
||||
module: ejabberd_s2s_in
|
||||
-
|
||||
port: 5280
|
||||
ip: "::"
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
"/ws": ejabberd_http_ws
|
||||
"/bosh": mod_bosh
|
||||
"/api": mod_http_api
|
||||
## "/pub/archive": mod_http_fileserver
|
||||
web_admin: true
|
||||
## register: true
|
||||
captcha: true
|
||||
##
|
||||
## ejabberd_service: Interact with external components (transports, ...)
|
||||
##
|
||||
## -
|
||||
## port: 8888
|
||||
## ip: "::"
|
||||
## module: ejabberd_service
|
||||
## access: all
|
||||
## shaper_rule: fast
|
||||
@@ -175,39 +203,47 @@ listen:
|
||||
##
|
||||
## -
|
||||
## port: 4560
|
||||
## ip: "::"
|
||||
## module: ejabberd_xmlrpc
|
||||
## access_commands: {}
|
||||
-
|
||||
port: 5280
|
||||
module: ejabberd_http
|
||||
request_handlers:
|
||||
"/websocket": ejabberd_http_ws
|
||||
## "/pub/archive": mod_http_fileserver
|
||||
web_admin: true
|
||||
http_bind: true
|
||||
## register: true
|
||||
captcha: true
|
||||
|
||||
##
|
||||
## To enable secure http upload
|
||||
##
|
||||
## -
|
||||
## port: 5444
|
||||
## ip: "::"
|
||||
## module: ejabberd_http
|
||||
## request_handlers:
|
||||
## "": mod_http_upload
|
||||
## tls: true
|
||||
## certfile: 'CERTFILE'
|
||||
## protocol_options: 'TLSOPTS'
|
||||
## dhfile: 'DHFILE'
|
||||
## ciphers: 'CIPHERS'
|
||||
|
||||
## Disabling digest-md5 SASL authentication. digest-md5 requires plain-text
|
||||
## password storage (see auth_password_format option).
|
||||
## disable_sasl_mechanisms: "digest-md5"
|
||||
|
||||
###. ==================
|
||||
###' S2S GLOBAL OPTIONS
|
||||
|
||||
##
|
||||
## s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections.
|
||||
## s2s_use_starttls: Enable STARTTLS for S2S connections.
|
||||
## Allowed values are: false optional required required_trusted
|
||||
## You must specify a certificate file.
|
||||
##
|
||||
## s2s_use_starttls: optional
|
||||
## s2s_use_starttls: required
|
||||
|
||||
##
|
||||
## s2s_certfile: Specify a certificate file.
|
||||
##
|
||||
## s2s_certfile: "/path/to/ssl.pem"
|
||||
## s2s_certfile: 'CERTFILE'
|
||||
|
||||
## Custom OpenSSL options
|
||||
##
|
||||
## s2s_protocol_options:
|
||||
## - "no_sslv3"
|
||||
## - "no_tlsv1"
|
||||
## s2s_protocol_options: 'TLSOPTS'
|
||||
|
||||
##
|
||||
## domain_certfile: Specify a different certificate for each served hostname.
|
||||
@@ -229,12 +265,12 @@ listen:
|
||||
## Outgoing S2S options
|
||||
##
|
||||
## Preferred address families (which to try first) and connect timeout
|
||||
## in milliseconds.
|
||||
## in seconds.
|
||||
##
|
||||
## outgoing_s2s_families:
|
||||
## - ipv4
|
||||
## - ipv6
|
||||
## outgoing_s2s_timeout: 10000
|
||||
## outgoing_s2s_timeout: 190
|
||||
|
||||
###. ==============
|
||||
###' AUTHENTICATION
|
||||
@@ -451,6 +487,8 @@ acl:
|
||||
loopback:
|
||||
ip:
|
||||
- "127.0.0.0/8"
|
||||
- "::1/128"
|
||||
- "::FFFF:127.0.0.1/128"
|
||||
|
||||
##
|
||||
## Bad XMPP servers
|
||||
@@ -517,6 +555,7 @@ access_rules:
|
||||
trusted_network:
|
||||
- allow: loopback
|
||||
## Do not establish S2S connections with bad servers
|
||||
## If you enable this you also have to uncomment "s2s_access: s2s"
|
||||
## s2s:
|
||||
## - deny:
|
||||
## - ip: "XXX.XXX.XXX.XXX/32"
|
||||
@@ -524,6 +563,53 @@ access_rules:
|
||||
## - ip: "XXX.XXX.XXX.XXX/32"
|
||||
## - allow
|
||||
|
||||
## ===============
|
||||
## API PERMISSIONS
|
||||
## ===============
|
||||
##
|
||||
## This section allows you to define who and using what method
|
||||
## can execute commands offered by ejabberd.
|
||||
##
|
||||
## By default "console commands" section allow executing all commands
|
||||
## issued using ejabberdctl command, and "admin access" section allows
|
||||
## users in admin acl that connect from 127.0.0.1 to execute all
|
||||
## commands except start and stop with any available access method
|
||||
## (ejabberdctl, http-api, xmlrpc depending what is enabled on server).
|
||||
##
|
||||
## If you remove "console commands" there will be one added by
|
||||
## default allowing executing all commands, but if you just change
|
||||
## permissions in it, version from config file will be used instead
|
||||
## of default one.
|
||||
##
|
||||
api_permissions:
|
||||
"console commands":
|
||||
from:
|
||||
- ejabberd_ctl
|
||||
who: all
|
||||
what: "*"
|
||||
"admin access":
|
||||
who:
|
||||
- access:
|
||||
- allow:
|
||||
- acl: loopback
|
||||
- acl: admin
|
||||
- oauth:
|
||||
- scope: "ejabberd:admin"
|
||||
- access:
|
||||
- allow:
|
||||
- acl: loopback
|
||||
- acl: admin
|
||||
what:
|
||||
- "*"
|
||||
- "!stop"
|
||||
- "!start"
|
||||
"public commands":
|
||||
who:
|
||||
- ip: "127.0.0.1/8"
|
||||
what:
|
||||
- "status"
|
||||
- "connected_users_number"
|
||||
|
||||
## By default the frequency of account registrations from the same IP
|
||||
## is limited to 1 account every 10 minutes. To disable, specify: infinity
|
||||
## registration_timeout: 600
|
||||
@@ -581,7 +667,7 @@ language: "en"
|
||||
##
|
||||
modules:
|
||||
mod_adhoc: {}
|
||||
## mod_admin_extra: {}
|
||||
mod_admin_extra: {}
|
||||
mod_announce: # recommends mod_adhoc
|
||||
access: announce
|
||||
mod_blocking: {} # requires mod_privacy
|
||||
@@ -589,15 +675,25 @@ modules:
|
||||
mod_carboncopy: {}
|
||||
mod_client_state: {}
|
||||
mod_configure: {} # requires mod_adhoc
|
||||
##mod_delegation: {} # for xep0356
|
||||
## mod_delegation: {} # for xep0356
|
||||
mod_disco: {}
|
||||
## mod_echo: {}
|
||||
mod_echo: {}
|
||||
mod_irc: {}
|
||||
mod_http_bind: {}
|
||||
mod_bosh: {}
|
||||
## mod_http_fileserver:
|
||||
## docroot: "/var/www"
|
||||
## accesslog: "/var/log/ejabberd/access.log"
|
||||
## mod_http_upload:
|
||||
## # docroot: "@HOME@/upload"
|
||||
## put_url: "https://@HOST@:5444"
|
||||
## thumbnail: false # otherwise needs the identify command from ImageMagick installed
|
||||
## mod_http_upload_quota:
|
||||
## max_days: 30
|
||||
mod_last: {}
|
||||
## XEP-0313: Message Archive Management
|
||||
## You might want to setup a SQL backend for MAM because the mnesia database is
|
||||
## limited to 2GB which might be exceeded on large servers
|
||||
## mod_mam: {} # for xep0313, mnesia is limited to 2GB, better use an SQL backend
|
||||
mod_muc:
|
||||
## host: "conference.@HOST@"
|
||||
access:
|
||||
@@ -606,6 +702,7 @@ modules:
|
||||
- allow: admin
|
||||
access_create: muc_create
|
||||
access_persistent: muc_create
|
||||
mod_muc_admin: {}
|
||||
## mod_muc_log: {}
|
||||
## mod_multicast: {}
|
||||
mod_offline:
|
||||
@@ -628,6 +725,8 @@ modules:
|
||||
- "flat"
|
||||
- "hometree"
|
||||
- "pep" # pep requires mod_caps
|
||||
mod_push: {}
|
||||
mod_push_keepalive: {}
|
||||
## mod_register:
|
||||
##
|
||||
## Protect In-Band account registrations with CAPTCHA.
|
||||
@@ -668,6 +767,18 @@ modules:
|
||||
mod_vcard:
|
||||
search: false
|
||||
mod_version: {}
|
||||
mod_stream_mgmt: {}
|
||||
## Non-SASL Authentication (XEP-0078) is now disabled by default
|
||||
## because it's obsoleted and is used mostly by abandoned
|
||||
## client software
|
||||
## mod_legacy_auth: {}
|
||||
## The module for S2S dialback (XEP-0220). Please note that you cannot
|
||||
## rely solely on dialback if you want to federate with other servers,
|
||||
## because a lot of servers have dialback disabled and instead rely on
|
||||
## PKIX authentication. Make sure you have proper certificates installed
|
||||
## and check your accessibility at https://xmpp.net/
|
||||
mod_s2s_dialback: {}
|
||||
mod_http_api: {}
|
||||
|
||||
##
|
||||
## Enable modules with custom options in a specific virtual host
|
||||
|
||||
+152
-282
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
# define default configuration
|
||||
POLL=true
|
||||
@@ -10,135 +10,89 @@ FIREWALL_WINDOW=""
|
||||
ERLANG_NODE=ejabberd@localhost
|
||||
|
||||
# define default environment variables
|
||||
SCRIPT_DIR=`cd ${0%/*} && pwd`
|
||||
ERL={{erl}}
|
||||
IEX={{bindir}}/iex
|
||||
EPMD={{epmd}}
|
||||
INSTALLUSER={{installuser}}
|
||||
ERL_LIBS={{libdir}}
|
||||
ERL="{{erl}}"
|
||||
IEX="{{bindir}}/iex"
|
||||
EPMD="{{epmd}}"
|
||||
INSTALLUSER="{{installuser}}"
|
||||
|
||||
# check the proper system user is used if defined
|
||||
if [ "$INSTALLUSER" != "" ] ; then
|
||||
EXEC_CMD="false"
|
||||
for GID in `id -G`; do
|
||||
if [ $GID -eq 0 ] ; then
|
||||
INSTALLUSER_HOME=$(getent passwd "$INSTALLUSER" | cut -d: -f6)
|
||||
if [ -n "$INSTALLUSER_HOME" ] && [ ! -d "$INSTALLUSER_HOME" ] ; then
|
||||
mkdir -p "$INSTALLUSER_HOME"
|
||||
chown "$INSTALLUSER" "$INSTALLUSER_HOME"
|
||||
fi
|
||||
EXEC_CMD="su $INSTALLUSER -c"
|
||||
# check the proper system user is used
|
||||
case `id -un` in
|
||||
"$INSTALLUSER")
|
||||
EXEC_CMD="as_current_user"
|
||||
;;
|
||||
root)
|
||||
if [ -n "$INSTALLUSER" ] ; then
|
||||
EXEC_CMD="as_install_user"
|
||||
else
|
||||
EXEC_CMD="as_current_user"
|
||||
echo "WARNING: This is not recommended to run ejabberd as root" >&2
|
||||
fi
|
||||
done
|
||||
if [ `id -g` -eq `id -g $INSTALLUSER` ] ; then
|
||||
EXEC_CMD="bash -c"
|
||||
fi
|
||||
if [ "$EXEC_CMD" = "false" ] ; then
|
||||
echo "This command can only be run by root or the user $INSTALLUSER" >&2
|
||||
exit 4
|
||||
fi
|
||||
else
|
||||
EXEC_CMD="bash -c"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if [ -n "$INSTALLUSER" ] ; then
|
||||
echo "ERROR: This command can only be run by root or the user $INSTALLUSER" >&2
|
||||
exit 7
|
||||
else
|
||||
EXEC_CMD="as_current_user"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# parse command line parameters
|
||||
declare -a ARGS=()
|
||||
while [ $# -ne 0 ] ; do
|
||||
PARAM="$1"
|
||||
shift
|
||||
case $PARAM in
|
||||
--) break ;;
|
||||
--no-timeout) EJABBERD_NO_TIMEOUT="--no-timeout" ;;
|
||||
--node) ERLANG_NODE_ARG=$1 ; shift ;;
|
||||
--config-dir) ETC_DIR="$1" ; shift ;;
|
||||
--config) EJABBERD_CONFIG_PATH="$1" ; shift ;;
|
||||
--ctl-config) EJABBERDCTL_CONFIG_PATH="$1" ; shift ;;
|
||||
--logs) LOGS_DIR="$1" ; shift ;;
|
||||
--spool) SPOOL_DIR="$1" ; shift ;;
|
||||
*) ARGS=("${ARGS[@]}" "$PARAM") ;;
|
||||
for arg; do
|
||||
case $1 in
|
||||
-n|--node) ERLANG_NODE_ARG=$2; shift;;
|
||||
-s|--spool) SPOOL_DIR=$2; shift;;
|
||||
-l|--logs) LOGS_DIR=$2; shift;;
|
||||
-f|--config) EJABBERD_CONFIG_PATH=$2; shift;;
|
||||
-c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift;;
|
||||
-d|--config-dir) ETC_DIR=$2; shift;;
|
||||
-t|--no-timeout) NO_TIMEOUT="--no-timeout";;
|
||||
--) :;;
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Define ejabberd variable if they have not been defined from the command line
|
||||
if [ "$ETC_DIR" = "" ] ; then
|
||||
ETC_DIR={{sysconfdir}}/ejabberd
|
||||
fi
|
||||
if [ "$EJABBERDCTL_CONFIG_PATH" = "" ] ; then
|
||||
EJABBERDCTL_CONFIG_PATH=$ETC_DIR/ejabberdctl.cfg
|
||||
fi
|
||||
if [ -f "$EJABBERDCTL_CONFIG_PATH" ] ; then
|
||||
. "$EJABBERDCTL_CONFIG_PATH"
|
||||
fi
|
||||
if [ "$EJABBERD_CONFIG_PATH" = "" ] ; then
|
||||
EJABBERD_CONFIG_PATH=$ETC_DIR/ejabberd.yml
|
||||
fi
|
||||
if [ "$LOGS_DIR" = "" ] ; then
|
||||
LOGS_DIR={{localstatedir}}/log/ejabberd
|
||||
fi
|
||||
if [ "$SPOOL_DIR" = "" ] ; then
|
||||
SPOOL_DIR={{localstatedir}}/lib/ejabberd
|
||||
fi
|
||||
if [ "$EJABBERD_DOC_PATH" = "" ] ; then
|
||||
EJABBERD_DOC_PATH={{docdir}}
|
||||
fi
|
||||
if [ "$ERLANG_NODE_ARG" != "" ] ; then
|
||||
ERLANG_NODE=$ERLANG_NODE_ARG
|
||||
fi
|
||||
if [ "{{release}}" != "true" -a "$EJABBERD_BIN_PATH" = "" ] ; then
|
||||
EJABBERD_BIN_PATH={{libdir}}/ejabberd/priv/bin
|
||||
fi
|
||||
EJABBERD_LOG_PATH=$LOGS_DIR/ejabberd.log
|
||||
DATETIME=`date "+%Y%m%d-%H%M%S"`
|
||||
ERL_CRASH_DUMP=$LOGS_DIR/erl_crash_$DATETIME.dump
|
||||
ERL_INETRC=$ETC_DIR/inetrc
|
||||
# define ejabberd variables if not already defined from the command line
|
||||
: ${ETC_DIR:={{sysconfdir}}/ejabberd}
|
||||
: ${LOGS_DIR:={{localstatedir}}/log/ejabberd}
|
||||
: ${SPOOL_DIR:={{localstatedir}}/lib/ejabberd}
|
||||
: ${EJABBERD_CONFIG_PATH:="$ETC_DIR"/ejabberd.yml}
|
||||
: ${EJABBERDCTL_CONFIG_PATH:="$ETC_DIR"/ejabberdctl.cfg}
|
||||
[ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH"
|
||||
[ "$ERLANG_NODE_ARG" != "" ] && ERLANG_NODE=$ERLANG_NODE_ARG
|
||||
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s"
|
||||
: ${EJABBERD_DOC_PATH:={{docdir}}}
|
||||
: ${EJABBERD_LOG_PATH:="$LOGS_DIR"/ejabberd.log}
|
||||
|
||||
# define mnesia options
|
||||
MNESIA_OPTS="-mnesia dir \"\\\"$SPOOL_DIR\\\"\" $MNESIA_OPTIONS"
|
||||
# define erl parameters
|
||||
ERLANG_OPTS="+K $POLL -smp $SMP +P $ERL_PROCESSES $ERL_OPTIONS"
|
||||
KERNEL_OPTS=""
|
||||
if [ "$FIREWALL_WINDOW" != "" ] ; then
|
||||
KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
|
||||
ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
|
||||
fi
|
||||
if [ "$INET_DIST_INTERFACE" != "" ] ; then
|
||||
INET_DIST_INTERFACE2="$(echo $INET_DIST_INTERFACE | sed 's/\./,/g')"
|
||||
if [ "$INET_DIST_INTERFACE" != "$INET_DIST_INTERFACE2" ] ; then
|
||||
INET_DIST_INTERFACE2="{$INET_DIST_INTERFACE2}"
|
||||
INET_DIST_INTERFACE2=$("$ERL" -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt)
|
||||
if [ "$INET_DIST_INTERFACE2" != "" ] ; then
|
||||
ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2"
|
||||
fi
|
||||
KERNEL_OPTS="${KERNEL_OPTS} -kernel inet_dist_use_interface \"${INET_DIST_INTERFACE2}\""
|
||||
fi
|
||||
if [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] ; then
|
||||
NAME="-sname"
|
||||
else
|
||||
NAME="-name"
|
||||
fi
|
||||
IEXNAME="-$NAME"
|
||||
ERL_LIBS={{libdir}}
|
||||
ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump
|
||||
ERL_INETRC="$ETC_DIR"/inetrc
|
||||
|
||||
# define ejabberd environment parameters
|
||||
if [ "$EJABBERD_CONFIG_PATH" != "${EJABBERD_CONFIG_PATH%.yml}" ] ; then
|
||||
rate=$(sed '/^[ ]*log_rate_limit/!d;s/.*://;s/ *//' $EJABBERD_CONFIG_PATH)
|
||||
rotate=$(sed '/^[ ]*log_rotate_size/!d;s/.*://;s/ *//' $EJABBERD_CONFIG_PATH)
|
||||
count=$(sed '/^[ ]*log_rotate_count/!d;s/.*://;s/ *//' $EJABBERD_CONFIG_PATH)
|
||||
date=$(sed '/^[ ]*log_rotate_date/!d;s/.*://;s/ *//' $EJABBERD_CONFIG_PATH)
|
||||
else
|
||||
rate=$(sed '/^[ ]*log_rate_limit/!d;s/.*,//;s/ *//;s/}\.//' $EJABBERD_CONFIG_PATH)
|
||||
rotate=$(sed '/^[ ]*log_rotate_size/!d;s/.*,//;s/ *//;s/}\.//' $EJABBERD_CONFIG_PATH)
|
||||
count=$(sed '/^[ ]*log_rotate_count/!d;s/.*,//;s/ *//;s/}\.//' $EJABBERD_CONFIG_PATH)
|
||||
date=$(sed '/^[ ]*log_rotate_date/!d;s/.*,//;s/ *//;s/}\.//' $EJABBERD_CONFIG_PATH)
|
||||
fi
|
||||
[ -z "$rate" ] || EJABBERD_OPTS="log_rate_limit $rate"
|
||||
[ -z "$rotate" ] || EJABBERD_OPTS="${EJABBERD_OPTS} log_rotate_size $rotate"
|
||||
[ -z "$count" ] || EJABBERD_OPTS="${EJABBERD_OPTS} log_rotate_count $count"
|
||||
[ -z "$date" ] || EJABBERD_OPTS="${EJABBERD_OPTS} log_rotate_date '$date'"
|
||||
[ -z "$EJABBERD_OPTS" ] || EJABBERD_OPTS="-ejabberd ${EJABBERD_OPTS}"
|
||||
|
||||
[ -d "$SPOOL_DIR" ] || $EXEC_CMD "mkdir -p $SPOOL_DIR"
|
||||
cd "$SPOOL_DIR"
|
||||
# define ejabberd parameters
|
||||
EJABBERD_OPTS="$EJABBERD_OPTS\
|
||||
$(sed '/^log_rate_limit/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\
|
||||
$(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\
|
||||
$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\
|
||||
$(sed '/^log_rotate_date/!d;s/:[ \t]*\(.[^ ]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")"
|
||||
[ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS"
|
||||
EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd"
|
||||
|
||||
# export global variables
|
||||
export EJABBERD_CONFIG_PATH
|
||||
export EJABBERD_LOG_PATH
|
||||
export EJABBERD_BIN_PATH
|
||||
export EJABBERD_DOC_PATH
|
||||
export EJABBERD_PID_PATH
|
||||
export ERL_CRASH_DUMP
|
||||
@@ -150,116 +104,26 @@ export CONTRIB_MODULES_PATH
|
||||
export CONTRIB_MODULES_CONF_DIR
|
||||
export ERL_LIBS
|
||||
|
||||
shell_escape_str()
|
||||
# run command either directly or via su $INSTALLUSER
|
||||
exec_cmd()
|
||||
{
|
||||
if test $# -eq 0; then
|
||||
printf '"" '
|
||||
else
|
||||
shell_escape "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
shell_escape()
|
||||
{
|
||||
local RES=()
|
||||
for i in "$@"; do
|
||||
if test -z "$i"; then
|
||||
printf '"" '
|
||||
else
|
||||
printf '%q ' "$i"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# start server
|
||||
start()
|
||||
{
|
||||
check_start
|
||||
CMD="`shell_escape \"$ERL\" \"$NAME\" \"$ERLANG_NODE\"` \
|
||||
-noinput -detached \
|
||||
$MNESIA_OPTS \
|
||||
$KERNEL_OPTS \
|
||||
$EJABBERD_OPTS \
|
||||
-s ejabberd \
|
||||
$ERLANG_OPTS \
|
||||
`shell_escape \"${ARGS[@]}\" \"$@\"`"
|
||||
$EXEC_CMD "$CMD"
|
||||
}
|
||||
|
||||
# attach to server
|
||||
debug()
|
||||
{
|
||||
debugwarning
|
||||
NID=$(uid debug)
|
||||
CMD="`shell_escape \"$ERL\" \"$NAME\" \"$NID\"` \
|
||||
-remsh $ERLANG_NODE \
|
||||
-hidden \
|
||||
$KERNEL_OPTS \
|
||||
$ERLANG_OPTS \
|
||||
`shell_escape \"${ARGS[@]}\" \"$@\"`"
|
||||
$EXEC_CMD "$CMD"
|
||||
}
|
||||
|
||||
# attach to server using Elixir
|
||||
iexdebug()
|
||||
{
|
||||
debugwarning
|
||||
# Elixir shell is hidden as default
|
||||
NID=$(uid debug)
|
||||
CMD="`shell_escape \"$IEX\" \"$IEXNAME\" \"$NID\"` \
|
||||
-remsh $ERLANG_NODE \
|
||||
--erl `shell_escape \"$KERNEL_OPTS\"` \
|
||||
--erl `shell_escape \"$ERLANG_OPTS\"` \
|
||||
--erl `shell_escape \"${ARGS[@]}\"` \
|
||||
--erl `shell_escape_str \"$@\"`"
|
||||
$EXEC_CMD "ERL_PATH=\"$ERL\" $CMD"
|
||||
}
|
||||
|
||||
# start interactive server
|
||||
live()
|
||||
{
|
||||
livewarning
|
||||
CMD="`shell_escape \"$ERL\" \"$NAME\" \"${ERLANG_NODE}\"` \
|
||||
$MNESIA_OPTS \
|
||||
$KERNEL_OPTS \
|
||||
$EJABBERD_OPTS \
|
||||
-s ejabberd \
|
||||
$ERLANG_OPTS \
|
||||
`shell_escape \"${ARGS[@]}\" \"$@\"`"
|
||||
$EXEC_CMD "$CMD"
|
||||
}
|
||||
|
||||
# start interactive server with Elixir
|
||||
iexlive()
|
||||
{
|
||||
livewarning
|
||||
echo $@
|
||||
CMD="`shell_escape \"$IEX\" \"$IEXNAME\" \"${ERLANG_NODE}\"` \
|
||||
--erl \"-mnesia dir \\\"$SPOOL_DIR\\\"\" \
|
||||
--erl \"`shell_escape \"$KERNEL_OPTS\"`\" \
|
||||
--erl \"`shell_escape \"$EJABBERD_OPTS\"`\" \
|
||||
--app ejabberd \
|
||||
--erl `shell_escape \"$ERLANG_OPTS\"` \
|
||||
--erl `shell_escape \"${ARGS[@]}\"` \
|
||||
--erl `shell_escape_str \"$@\"`"
|
||||
$EXEC_CMD "ERL_PATH=\"$ERL\" $CMD"
|
||||
}
|
||||
|
||||
# start server in the foreground
|
||||
foreground()
|
||||
{
|
||||
check_start
|
||||
CMD="`shell_escape \"$ERL\" \"$NAME\" \"$ERLANG_NODE\"` \
|
||||
-noinput \
|
||||
$MNESIA_OPTS \
|
||||
$KERNEL_OPTS \
|
||||
$EJABBERD_OPTS \
|
||||
-s ejabberd \
|
||||
$ERLANG_OPTS \
|
||||
`shell_escape \"${ARGS[@]}\" \"$@\"`"
|
||||
$EXEC_CMD "$CMD"
|
||||
case $EXEC_CMD in
|
||||
as_install_user) su -c '"$0" "$@"' "$INSTALLUSER" -- "$@" ;;
|
||||
as_current_user) "$@" ;;
|
||||
esac
|
||||
}
|
||||
exec_erl()
|
||||
{
|
||||
NODE=$1; shift
|
||||
exec_cmd "$ERL" ${S:--}name $NODE $ERLANG_OPTS "$@"
|
||||
}
|
||||
exec_iex()
|
||||
{
|
||||
NODE=$1; shift
|
||||
exec_cmd "$IEX" ${S:---}name $NODE --erl "$ERLANG_OPTS" "$@"
|
||||
}
|
||||
|
||||
# usage
|
||||
debugwarning()
|
||||
{
|
||||
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
|
||||
@@ -286,7 +150,6 @@ debugwarning()
|
||||
|
||||
livewarning()
|
||||
{
|
||||
check_start
|
||||
if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
|
||||
echo "--------------------------------------------------------------------"
|
||||
echo ""
|
||||
@@ -308,32 +171,6 @@ livewarning()
|
||||
fi
|
||||
}
|
||||
|
||||
etop()
|
||||
{
|
||||
NID=$(uid top)
|
||||
$EXEC_CMD "$ERL \
|
||||
$NAME $NID \
|
||||
-hidden -s etop -s erlang halt -output text -node $ERLANG_NODE"
|
||||
}
|
||||
|
||||
ping()
|
||||
{
|
||||
[ -z "$1" ] && PEER=${ERLANG_NODE} || PEER=$1
|
||||
if [ "$PEER" = "${PEER%.*}" ] ; then
|
||||
PING_NAME="-sname"
|
||||
PING_NODE=$(hostname -s)
|
||||
else
|
||||
PING_NAME="-name"
|
||||
PING_NODE=$(hostname)
|
||||
fi
|
||||
NID=$(uid ping ${PING_NODE})
|
||||
$EXEC_CMD "$ERL \
|
||||
$PING_NAME $NID \
|
||||
-hidden $KERNEL_OPTS $ERLANG_OPTS \
|
||||
-eval 'io:format(\"~p~n\",[net_adm:ping('\"'\"'$PEER'\"'\"')])' \
|
||||
-s erlang halt -output text -noinput"
|
||||
}
|
||||
|
||||
help()
|
||||
{
|
||||
echo ""
|
||||
@@ -355,28 +192,11 @@ help()
|
||||
echo ""
|
||||
}
|
||||
|
||||
# common control function
|
||||
ctl()
|
||||
{
|
||||
NID=$(uid ctl)
|
||||
CMD="`shell_escape \"$ERL\" \"$NAME\" \"$NID\"` \
|
||||
-noinput -hidden $KERNEL_OPTS -s ejabberd_ctl \
|
||||
-extra `shell_escape \"$ERLANG_NODE\"` $EJABBERD_NO_TIMEOUT \
|
||||
`shell_escape \"$@\"`"
|
||||
$EXEC_CMD "$CMD"
|
||||
result=$?
|
||||
case $result in
|
||||
2) help;;
|
||||
3) help;;
|
||||
*) :;;
|
||||
esac
|
||||
return $result
|
||||
}
|
||||
|
||||
# dynamic node name helper
|
||||
uid()
|
||||
{
|
||||
uuid=$(uuidgen 2>/dev/null)
|
||||
[ -z "$uuid" -a -f /proc/sys/kernel/random/uuid ] && uuid=$(</proc/sys/kernel/random/uuid)
|
||||
[ -z "$uuid" -a -f /proc/sys/kernel/random/uuid ] && uuid=$(cat /proc/sys/kernel/random/uuid)
|
||||
[ -z "$uuid" ] && uuid=$(printf "%X" $RANDOM$(date +%M%S)$$)
|
||||
uuid=${uuid%%-*}
|
||||
[ $# -eq 0 ] && echo ${uuid}-${ERLANG_NODE}
|
||||
@@ -391,6 +211,7 @@ stop_epmd()
|
||||
}
|
||||
|
||||
# make sure node not already running and node name unregistered
|
||||
# if all ok, ensure runtime directory exists and make it current directory
|
||||
check_start()
|
||||
{
|
||||
"$EPMD" -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && {
|
||||
@@ -411,7 +232,7 @@ check_start()
|
||||
}
|
||||
|
||||
# allow sync calls
|
||||
wait_for_status()
|
||||
wait_status()
|
||||
{
|
||||
# args: status try delay
|
||||
# return: 0 OK, 1 KO
|
||||
@@ -420,27 +241,76 @@ wait_for_status()
|
||||
while [ $status -ne $1 ] ; do
|
||||
sleep $3
|
||||
timeout=`expr $timeout - 1`
|
||||
[ $timeout -eq 0 ] && {
|
||||
if [ $timeout -eq 0 ] ; then
|
||||
status=$1
|
||||
} || {
|
||||
ctl status > /dev/null
|
||||
else
|
||||
exec_erl $(uid ctl) -hidden -noinput -s ejabberd_ctl \
|
||||
-extra $ERLANG_NODE $NO_TIMEOUT status > /dev/null
|
||||
status=$?
|
||||
}
|
||||
fi
|
||||
done
|
||||
[ $timeout -eq 0 ] && return 1 || return 0
|
||||
[ $timeout -gt 0 ]
|
||||
}
|
||||
|
||||
# main handler
|
||||
case "${ARGS[0]}" in
|
||||
'start') start;;
|
||||
'debug') debug;;
|
||||
'iexdebug') iexdebug;;
|
||||
'live') live;;
|
||||
'iexlive') iexlive;;
|
||||
'foreground') foreground;;
|
||||
'ping'*) ping ${ARGS[1]};;
|
||||
'etop') etop;;
|
||||
'started') wait_for_status 0 30 2;; # wait 30x2s before timeout
|
||||
'stopped') wait_for_status 3 30 2 && stop_epmd;; # wait 30x2s before timeout
|
||||
*) ctl "${ARGS[@]}";;
|
||||
# ensure we can change current directory to SPOOL_DIR
|
||||
[ -d "$SPOOL_DIR" ] || exec_cmd mkdir -p "$SPOOL_DIR"
|
||||
cd "$SPOOL_DIR" || {
|
||||
echo "ERROR: can not access directory $SPOOL_DIR"
|
||||
exit 6
|
||||
}
|
||||
|
||||
# main
|
||||
case $1 in
|
||||
start)
|
||||
check_start
|
||||
exec_erl $ERLANG_NODE $EJABBERD_OPTS -noinput -detached
|
||||
;;
|
||||
foreground)
|
||||
check_start
|
||||
exec_erl $ERLANG_NODE $EJABBERD_OPTS -noinput
|
||||
;;
|
||||
live)
|
||||
livewarning
|
||||
check_start
|
||||
exec_erl $ERLANG_NODE $EJABBERD_OPTS
|
||||
;;
|
||||
debug)
|
||||
debugwarning
|
||||
exec_erl $(uid debug) -hidden -remsh $ERLANG_NODE
|
||||
;;
|
||||
etop)
|
||||
exec_erl $(uid top) -hidden -node $ERLANG_NODE -s etop \
|
||||
-s erlang halt -output text
|
||||
;;
|
||||
iexdebug)
|
||||
debugwarning
|
||||
exec_iex $(uid debug) --remsh "$ERLANG_NODE"
|
||||
;;
|
||||
iexlive)
|
||||
livewarning
|
||||
exec_iex $ERLANG_NODE --erl "$EJABBERD_OPTS" --app ejabberd
|
||||
;;
|
||||
ping)
|
||||
PEER=${2:-$ERLANG_NODE}
|
||||
[ "$PEER" = "${PEER%.*}" ] && PS="-s"
|
||||
exec_cmd "$ERL" ${PS:--}name $(uid ping $(hostname $PS)) $ERLANG_OPTS \
|
||||
-noinput -hidden -eval 'io:format("~p~n",[net_adm:ping('"$PEER"')])' \
|
||||
-s erlang halt -output text
|
||||
;;
|
||||
started)
|
||||
wait_status 0 30 2 # wait 30x2s before timeout
|
||||
;;
|
||||
stopped)
|
||||
wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout
|
||||
;;
|
||||
*)
|
||||
exec_erl $(uid ctl) -hidden -noinput -s ejabberd_ctl \
|
||||
-extra $ERLANG_NODE $NO_TIMEOUT "$@"
|
||||
result=$?
|
||||
case $result in
|
||||
2|3) help;;
|
||||
*) :;;
|
||||
esac
|
||||
exit $result
|
||||
;;
|
||||
esac
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(CT_XML,
|
||||
{<<"Content-Type">>, <<"text/xml; charset=utf-8">>}).
|
||||
|
||||
-define(CT_PLAIN,
|
||||
{<<"Content-Type">>, <<"text/plain">>}).
|
||||
|
||||
-define(CT_JSON,
|
||||
{<<"Content-Type">>, <<"application/json">>}).
|
||||
|
||||
-define(AC_ALLOW_ORIGIN,
|
||||
{<<"Access-Control-Allow-Origin">>, <<"*">>}).
|
||||
|
||||
-define(AC_ALLOW_METHODS,
|
||||
{<<"Access-Control-Allow-Methods">>,
|
||||
<<"GET, POST, OPTIONS">>}).
|
||||
|
||||
-define(AC_ALLOW_HEADERS,
|
||||
{<<"Access-Control-Allow-Headers">>,
|
||||
<<"Content-Type">>}).
|
||||
|
||||
-define(AC_MAX_AGE,
|
||||
{<<"Access-Control-Max-Age">>, <<"86400">>}).
|
||||
|
||||
-define(OPTIONS_HEADER,
|
||||
[?CT_PLAIN, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_METHODS,
|
||||
?AC_ALLOW_HEADERS, ?AC_MAX_AGE]).
|
||||
|
||||
-define(HEADER(CType),
|
||||
[CType, ?AC_ALLOW_ORIGIN, ?AC_ALLOW_HEADERS]).
|
||||
|
||||
-define(BOSH_CACHE, bosh_cache).
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -33,13 +33,13 @@
|
||||
|
||||
-define(SQL_DIR, filename:join(["priv", "sql"])).
|
||||
|
||||
-define(CONFIG_PATH, <<"ejabberd.cfg">>).
|
||||
-define(CONFIG_PATH, <<"ejabberd.yml">>).
|
||||
|
||||
-define(LOG_PATH, <<"ejabberd.log">>).
|
||||
-define(LOG_PATH, "ejabberd.log").
|
||||
|
||||
-define(EJABBERD_URI, <<"http://www.process-one.net/en/ejabberd/">>).
|
||||
|
||||
-define(S2STIMEOUT, 600000).
|
||||
-define(COPYRIGHT, "Copyright (c) 2002-2017 ProcessOne").
|
||||
|
||||
%%-define(DBGFSM, true).
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
-define(TDICT, dict:dict()).
|
||||
-define(TGB_TREE, gb_trees:tree()).
|
||||
-define(TGB_SET, gb_set:set()).
|
||||
-define(TGB_SET, gb_sets:set()).
|
||||
-define(TQUEUE, queue:queue()).
|
||||
|
||||
-endif.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -46,12 +46,13 @@
|
||||
%% to command, so that the command can perform additional check.
|
||||
|
||||
-record(ejabberd_commands,
|
||||
{name :: atom(),
|
||||
{name :: atom(),
|
||||
tags = [] :: [atom()] | '_' | '$2',
|
||||
desc = "" :: string() | '_' | '$3',
|
||||
longdesc = "" :: string() | '_',
|
||||
version = 0 :: integer(),
|
||||
module :: atom() | '_',
|
||||
version = 0 :: integer(),
|
||||
weight = 1 :: integer(),
|
||||
module :: atom() | '_',
|
||||
function :: atom() | '_',
|
||||
args = [] :: [aterm()] | '_' | '$1' | '$2',
|
||||
policy = restricted :: open | restricted | admin | user,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -22,5 +22,5 @@
|
||||
token = <<"">> :: binary() | '_',
|
||||
us = {<<"">>, <<"">>} :: {binary(), binary()} | '_',
|
||||
scope = [] :: [binary()] | '_',
|
||||
expire :: integer() | '$1'
|
||||
expire :: integer() | '$1' | '_'
|
||||
}).
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-define(ROUTES_CACHE, routes_cache).
|
||||
|
||||
-type local_hint() :: integer() | {apply, atom(), atom()}.
|
||||
|
||||
-record(route, {domain :: binary() | '_',
|
||||
server_host :: binary() | '_',
|
||||
pid :: undefined | pid(),
|
||||
local_hint :: local_hint() | undefined | '_'}).
|
||||
@@ -1,20 +0,0 @@
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-type filter_attr() :: {binary(), [binary()]}.
|
||||
|
||||
-record(state,
|
||||
{socket :: ejabberd_socket:socket_state(),
|
||||
sockmod = ejabberd_socket :: ejabberd_socket | ejabberd_frontend_socket,
|
||||
streamid = <<"">> :: binary(),
|
||||
host_opts = dict:new() :: ?TDICT,
|
||||
host = <<"">> :: binary(),
|
||||
access :: atom(),
|
||||
check_from = true :: boolean(),
|
||||
server_hosts = ?MYHOSTS :: [binary()],
|
||||
privilege_access :: [attr()],
|
||||
delegations :: [filter_attr()],
|
||||
last_pres = dict:new() :: ?TDICT}).
|
||||
|
||||
-type(state() :: #state{} ).
|
||||
+24
-1
@@ -1,13 +1,36 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-ifndef(EJABBERD_SM_HRL).
|
||||
-define(EJABBERD_SM_HRL, true).
|
||||
|
||||
-define(SM_CACHE, sm_cache).
|
||||
|
||||
-record(session, {sid, usr, us, priority, info = []}).
|
||||
-record(session_counter, {vhost, count}).
|
||||
-type sid() :: {erlang:timestamp(), pid()}.
|
||||
-type ip() :: {inet:ip_address(), inet:port_number()} | undefined.
|
||||
-type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()}
|
||||
| {oor, boolean()} | {auth_module, atom()}
|
||||
| {num_stanzas_in, non_neg_integer()}].
|
||||
| {num_stanzas_in, non_neg_integer()}
|
||||
| offline].
|
||||
-type prio() :: undefined | integer().
|
||||
|
||||
-endif.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -27,7 +27,7 @@
|
||||
-record(eldap_search,
|
||||
{scope = wholeSubtree :: scope(),
|
||||
base = <<"">> :: binary(),
|
||||
filter :: eldap:filter(),
|
||||
filter :: eldap:filter() | undefined,
|
||||
limit = 0 :: non_neg_integer(),
|
||||
attributes = [] :: [binary()],
|
||||
types_only = false :: boolean(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
+9
-13
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -19,11 +19,7 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-include("ns.hrl").
|
||||
-ifdef(NO_EXT_LIB).
|
||||
-include("fxml.hrl").
|
||||
-else.
|
||||
-include_lib("fast_xml/include/fxml.hrl").
|
||||
-endif.
|
||||
|
||||
-define(STANZA_ERROR(Code, Type, Condition),
|
||||
#xmlel{name = <<"error">>,
|
||||
@@ -475,15 +471,15 @@
|
||||
|
||||
-type(iq() :: iq_request() | iq_reply()).
|
||||
|
||||
-record(rsm_in, {max :: integer() | error,
|
||||
direction :: before | aft,
|
||||
id :: binary(),
|
||||
index :: integer() | error}).
|
||||
-record(rsm_in, {max :: integer() | error | undefined,
|
||||
direction :: before | aft | undefined,
|
||||
id :: binary() | undefined,
|
||||
index :: integer() | error | undefined}).
|
||||
|
||||
-record(rsm_out, {count :: integer(),
|
||||
index :: integer(),
|
||||
first :: binary(),
|
||||
last :: binary()}).
|
||||
-record(rsm_out, {count :: integer() | undefined,
|
||||
index :: integer() | undefined,
|
||||
first :: binary() | undefined,
|
||||
last :: binary() | undefined}).
|
||||
|
||||
-type(rsm_in() :: #rsm_in{}).
|
||||
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -17,6 +17,7 @@
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-define(PRINT(Format, Args), io:format(Format, Args)).
|
||||
-compile([{parse_transform, lager_transform}]).
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(motd, {server = <<"">> :: binary(),
|
||||
packet = #xmlel{} :: xmlel()}).
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(caps_features,
|
||||
{node_pair = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
features = [] :: [binary()] | pos_integer()
|
||||
|
||||
@@ -1,4 +1,27 @@
|
||||
-type matchspec_atom() :: '_' | '$1' | '$2' | '$3'.
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-type matchspec_atom() :: '_' | '$1' | '$2' | '$3' | '$4'.
|
||||
-record(carboncopy, {us :: {binary(), binary()} | matchspec_atom(),
|
||||
resource :: binary() | matchspec_atom(),
|
||||
version :: binary() | matchspec_atom()}).
|
||||
version :: binary() | matchspec_atom(),
|
||||
node = node() :: node() | matchspec_atom()}).
|
||||
|
||||
-define(CARBONCOPY_CACHE, carboncopy_cache).
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-type conn_param() :: {binary(), binary(), inet:port_number(), binary()} |
|
||||
{binary(), binary(), inet:port_number()} |
|
||||
{binary(), binary()} |
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(last_activity, {us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
timestamp = 0 :: non_neg_integer(),
|
||||
status = <<"">> :: binary()}).
|
||||
|
||||
+21
-1
@@ -1,10 +1,30 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(archive_msg,
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()} | '$2',
|
||||
id = <<>> :: binary() | '_',
|
||||
timestamp = p1_time_compat:timestamp() :: erlang:timestamp() | '_' | '$1',
|
||||
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3' | undefined,
|
||||
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
|
||||
packet = #xmlel{} :: xmlel() | '_',
|
||||
packet = #xmlel{} :: xmlel() | message() | '_',
|
||||
nick = <<"">> :: binary(),
|
||||
type = chat :: chat | groupchat}).
|
||||
|
||||
|
||||
+10
-7
@@ -1,7 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_muc.hrl
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -23,11 +22,15 @@
|
||||
{'_', binary()},
|
||||
opts = [] :: list() | '_'}).
|
||||
|
||||
-record(muc_online_room,
|
||||
{name_host = {<<"">>, <<"">>} :: {binary(), binary()} | '$1' |
|
||||
{'_', binary()} | '_',
|
||||
pid = self() :: pid() | '$2' | '_' | '$1'}).
|
||||
|
||||
-record(muc_registered,
|
||||
{us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()}, binary()} | '$1',
|
||||
nick = <<"">> :: binary()}).
|
||||
|
||||
-record(muc_online_room,
|
||||
{name_host :: {binary(), binary()} | '$1' | {'_', binary()} | '_',
|
||||
pid :: pid() | '$2' | '_' | '$1'}).
|
||||
|
||||
-record(muc_online_users, {us :: {binary(), binary()},
|
||||
resource :: binary() | '_',
|
||||
room :: binary() | '_' | '$1',
|
||||
host :: binary() | '_' | '$2'}).
|
||||
|
||||
+10
-19
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -28,9 +28,8 @@
|
||||
|
||||
-record(lqueue,
|
||||
{
|
||||
queue :: ?TQUEUE,
|
||||
len :: integer(),
|
||||
max :: integer()
|
||||
queue :: p1_queue:queue(),
|
||||
max = 0 :: integer()
|
||||
}).
|
||||
|
||||
-type lqueue() :: #lqueue{}.
|
||||
@@ -71,6 +70,7 @@
|
||||
-type config() :: #config{}.
|
||||
|
||||
-type role() :: moderator | participant | visitor | none.
|
||||
-type affiliation() :: admin | member | outcast | owner | none.
|
||||
|
||||
-record(user,
|
||||
{
|
||||
@@ -79,7 +79,7 @@
|
||||
role :: role(),
|
||||
%%is_subscriber = false :: boolean(),
|
||||
%%subscriptions = [] :: [binary()],
|
||||
last_presence :: xmlel()
|
||||
last_presence :: presence() | undefined
|
||||
}).
|
||||
|
||||
-record(subscriber, {jid :: jid(),
|
||||
@@ -90,10 +90,10 @@
|
||||
{
|
||||
message_time = 0 :: integer(),
|
||||
presence_time = 0 :: integer(),
|
||||
message_shaper :: shaper:shaper(),
|
||||
presence_shaper :: shaper:shaper(),
|
||||
message :: xmlel(),
|
||||
presence :: {binary(), xmlel()}
|
||||
message_shaper = none :: shaper:shaper(),
|
||||
presence_shaper = none :: shaper:shaper(),
|
||||
message :: message() | undefined,
|
||||
presence :: {binary(), presence()} | undefined
|
||||
}).
|
||||
|
||||
-record(state,
|
||||
@@ -117,14 +117,5 @@
|
||||
just_created = false :: boolean(),
|
||||
activity = treap:empty() :: treap:treap(),
|
||||
room_shaper = none :: shaper:shaper(),
|
||||
room_queue = queue:new() :: ?TQUEUE
|
||||
room_queue :: p1_queue:queue() | undefined
|
||||
}).
|
||||
|
||||
-record(muc_online_users, {us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
resource = <<>> :: binary() | '_',
|
||||
room = <<>> :: binary() | '_' | '$1',
|
||||
host = <<>> :: binary() | '_' | '$2'}).
|
||||
|
||||
-type muc_online_users() :: #muc_online_users{}.
|
||||
|
||||
-type muc_room_state() :: #state{}.
|
||||
|
||||
+23
-3
@@ -1,10 +1,30 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(offline_msg,
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
timestamp = now() :: erlang:timestamp() | '_',
|
||||
expire = now() :: erlang:timestamp() | never | '_',
|
||||
timestamp :: erlang:timestamp() | '_' | undefined,
|
||||
expire :: erlang:timestamp() | never | undefined | '_',
|
||||
from = #jid{} :: jid() | '_',
|
||||
to = #jid{} :: jid() | '_',
|
||||
packet = #xmlel{} :: xmlel() | '_'}).
|
||||
packet = #xmlel{} :: xmlel() | message() | '_'}).
|
||||
|
||||
-record(state,
|
||||
{host = <<"">> :: binary(),
|
||||
|
||||
+9
-12
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -22,9 +22,11 @@
|
||||
default = none :: none | binary(),
|
||||
lists = [] :: [{binary(), [listitem()]}]}).
|
||||
|
||||
-record(listitem, {type = none :: none | jid | group | subscription,
|
||||
value = none :: none | both | from | to | ljid() | binary(),
|
||||
action = allow :: allow | deny,
|
||||
-type privacy() :: #privacy{}.
|
||||
|
||||
-record(listitem, {type = none :: listitem_type(),
|
||||
value = none :: listitem_value(),
|
||||
action = allow :: listitem_action(),
|
||||
order = 0 :: integer(),
|
||||
match_all = false :: boolean(),
|
||||
match_iq = false :: boolean(),
|
||||
@@ -33,11 +35,6 @@
|
||||
match_presence_out = false :: boolean()}).
|
||||
|
||||
-type listitem() :: #listitem{}.
|
||||
|
||||
-record(userlist, {name = none :: none | binary(),
|
||||
list = [] :: [listitem()],
|
||||
needdb = false :: boolean()}).
|
||||
|
||||
-type userlist() :: #userlist{}.
|
||||
|
||||
-export_type([userlist/0]).
|
||||
-type listitem_type() :: none | jid | group | subscription.
|
||||
-type listitem_value() :: none | both | from | to | jid:ljid() | binary().
|
||||
-type listitem_action() :: allow | deny.
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(private_storage,
|
||||
{usns = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary() |
|
||||
'$1' | '_'},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
%%% RFC 1928 constants.
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -20,15 +20,15 @@
|
||||
|
||||
-record(roster,
|
||||
{
|
||||
usj = {<<>>, <<>>, {<<>>, <<>>, <<>>}} :: {binary(), binary(), ljid()} | '_',
|
||||
usj = {<<>>, <<>>, {<<>>, <<>>, <<>>}} :: {binary(), binary(), jid:ljid()} | '_',
|
||||
us = {<<>>, <<>>} :: {binary(), binary()} | '_',
|
||||
jid = {<<>>, <<>>, <<>>} :: ljid(),
|
||||
jid = {<<>>, <<>>, <<>>} :: jid:ljid(),
|
||||
name = <<>> :: binary() | '_',
|
||||
subscription = none :: subscription() | '_',
|
||||
ask = none :: ask() | '_',
|
||||
groups = [] :: [binary()] | '_',
|
||||
askmessage = <<"">> :: binary() | '_',
|
||||
xs = [] :: [xmlel()] | '_'
|
||||
xs = [] :: [fxml:xmlel()] | '_'
|
||||
}).
|
||||
|
||||
-record(roster_version,
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(sr_group, {group_host = {<<"">>, <<"">>} :: {'$1' | binary(), '$2' | binary()},
|
||||
opts = [] :: list() | '_' | '$2'}).
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(vcard_search,
|
||||
{us, user, luser, fn, lfn, family, lfamily, given,
|
||||
lgiven, middle, lmiddle, nickname, lnickname, bday,
|
||||
|
||||
@@ -1,2 +1,22 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-record(vcard_xupdate, {us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
hash = <<>> :: binary()}).
|
||||
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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_TIME, <<"urn:xmpp:time">>).
|
||||
-define(NS_LAST, <<"jabber:iq:last">>).
|
||||
-define(NS_XDATA, <<"jabber:x:data">>).
|
||||
-define(NS_IQDATA, <<"jabber:iq:data">>).
|
||||
-define(NS_DELAY, <<"urn:xmpp:delay">>).
|
||||
-define(NS_HINTS, <<"urn:xmpp:hints">>).
|
||||
-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_META_DATA,
|
||||
<<"http://jabber.org/protocol/pubsub#meta-data">>).
|
||||
-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_SUBSCRIBE_OPTIONS,
|
||||
<<"http://jabber.org/protocol/pubsub#subscribe_options">>).
|
||||
-define(NS_PUBSUB_PUBLISH_OPTIONS,
|
||||
<<"http://jabber.org/protocol/pubsub#publish_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_ADMIN_ANNOUNCE,
|
||||
<<"http://jabber.org/protocol/admin#announce">>).
|
||||
-define(NS_ADMIN_ANNOUNCE_ALL,
|
||||
<<"http://jabber.org/protocol/admin#announce-all">>).
|
||||
-define(NS_ADMIN_SET_MOTD,
|
||||
<<"http://jabber.org/protocol/admin#set-motd">>).
|
||||
-define(NS_ADMIN_EDIT_MOTD,
|
||||
<<"http://jabber.org/protocol/admin#edit-motd">>).
|
||||
-define(NS_ADMIN_DELETE_MOTD,
|
||||
<<"http://jabber.org/protocol/admin#delete-motd">>).
|
||||
-define(NS_ADMIN_ANNOUNCE_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#announce-allhosts">>).
|
||||
-define(NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#announce-all-allhosts">>).
|
||||
-define(NS_ADMIN_SET_MOTD_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#set-motd-allhosts">>).
|
||||
-define(NS_ADMIN_EDIT_MOTD_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#edit-motd-allhosts">>).
|
||||
-define(NS_ADMIN_DELETE_MOTD_ALLHOSTS,
|
||||
<<"http://jabber.org/protocol/admin#delete-motd-allhosts">>).
|
||||
-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_FLEX_OFFLINE, <<"http://jabber.org/protocol/offline">>).
|
||||
-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">>).
|
||||
-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">>).
|
||||
-define(NS_MAM_TMP, <<"urn:xmpp:mam:tmp">>).
|
||||
-define(NS_MAM_0, <<"urn:xmpp:mam:0">>).
|
||||
-define(NS_MAM_1, <<"urn:xmpp:mam:1">>).
|
||||
-define(NS_SID_0, <<"urn:xmpp:sid:0">>).
|
||||
-define(NS_PING, <<"urn:xmpp:ping">>).
|
||||
-define(NS_CARBONS_2, <<"urn:xmpp:carbons:2">>).
|
||||
-define(NS_CARBONS_1, <<"urn:xmpp:carbons:1">>).
|
||||
-define(NS_FORWARD, <<"urn:xmpp:forward:0">>).
|
||||
-define(NS_CLIENT_STATE, <<"urn:xmpp:csi:0">>).
|
||||
-define(NS_STREAM_MGMT_2, <<"urn:xmpp:sm:2">>).
|
||||
-define(NS_STREAM_MGMT_3, <<"urn:xmpp:sm:3">>).
|
||||
-define(NS_HTTP_UPLOAD, <<"urn:xmpp:http:upload">>).
|
||||
-define(NS_HTTP_UPLOAD_OLD, <<"eu:siacs:conversations:http:upload">>).
|
||||
-define(NS_THUMBS_1, <<"urn:xmpp:thumbs:1">>).
|
||||
-define(NS_NICK, <<"http://jabber.org/protocol/nick">>).
|
||||
-define(NS_MIX_0, <<"urn:xmpp:mix:0">>).
|
||||
-define(NS_MIX_SERVICEINFO_0, <<"urn:xmpp:mix:0#serviceinfo">>).
|
||||
-define(NS_MIX_NODES_MESSAGES, <<"urn:xmpp:mix:nodes:messages">>).
|
||||
-define(NS_MIX_NODES_PRESENCE, <<"urn:xmpp:mix:nodes:presence">>).
|
||||
-define(NS_MIX_NODES_PARTICIPANTS, <<"urn:xmpp:mix:nodes:participants">>).
|
||||
-define(NS_MIX_NODES_SUBJECT, <<"urn:xmpp:mix:nodes:subject">>).
|
||||
-define(NS_MIX_NODES_CONFIG, <<"urn:xmpp:mix:nodes:config">>).
|
||||
-define(NS_PRIVILEGE, <<"urn:xmpp:privilege:1">>).
|
||||
-define(NS_DELEGATION, <<"urn:xmpp:delegation:1">>).
|
||||
-define(NS_MUCSUB, <<"urn:xmpp:mucsub:0">>).
|
||||
-define(NS_MUCSUB_NODES_PRESENCE, <<"urn:xmpp:mucsub:nodes:presence">>).
|
||||
-define(NS_MUCSUB_NODES_MESSAGES, <<"urn:xmpp:mucsub:nodes:messages">>).
|
||||
-define(NS_MUCSUB_NODES_PARTICIPANTS, <<"urn:xmpp:mucsub:nodes:participants">>).
|
||||
-define(NS_MUCSUB_NODES_AFFILIATIONS, <<"urn:xmpp:mucsub:nodes:affiliations">>).
|
||||
-define(NS_MUCSUB_NODES_SUBJECT, <<"urn:xmpp:mucsub:nodes:subject">>).
|
||||
-define(NS_MUCSUB_NODES_CONFIG, <<"urn:xmpp:mucsub:nodes:config">>).
|
||||
-define(NS_MUCSUB_NODES_SYSTEM, <<"urn:xmpp:mucsub:nodes:system">>).
|
||||
+23
-27
@@ -1,27 +1,22 @@
|
||||
%%% ====================================================================
|
||||
%%% ``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.
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% The Initial Developer of the Original Code is ProcessOne.
|
||||
%%% Portions created by ProcessOne are Copyright 2006-2016, ProcessOne
|
||||
%%% All Rights Reserved.''
|
||||
%%% This software is copyright 2006-2016, 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.
|
||||
%%%
|
||||
%%% copyright 2006-2016 ProcessOne
|
||||
%%% 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.
|
||||
%%%
|
||||
%%% This file contains pubsub types definition.
|
||||
%%% ====================================================================
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
|
||||
@@ -109,13 +104,6 @@
|
||||
).
|
||||
%% @type affiliation() = 'none' | 'owner' | 'publisher' | 'publish-only' | 'member' | 'outcast'.
|
||||
|
||||
-type(subscription() :: 'none'
|
||||
| 'pending'
|
||||
| 'unconfigured'
|
||||
| 'subscribed'
|
||||
).
|
||||
%% @type subscription() = 'none' | 'pending' | 'unconfigured' | 'subscribed'.
|
||||
|
||||
-type(accessModel() :: 'open'
|
||||
| 'presence'
|
||||
| 'roster'
|
||||
@@ -150,6 +138,7 @@
|
||||
-record(pubsub_state,
|
||||
{
|
||||
stateid ,% :: {jlib:ljid(), mod_pubsub:nodeIdx()},
|
||||
nodeidx ,% :: mod_pubsub:nodeIdx(),
|
||||
items = [] ,% :: [mod_pubsub:itemId(),...],
|
||||
affiliation = 'none',% :: mod_pubsub:affiliation(),
|
||||
subscriptions = [] % :: [{mod_pubsub:subscription(), mod_pubsub:subId()}]
|
||||
@@ -158,6 +147,7 @@
|
||||
-record(pubsub_item,
|
||||
{
|
||||
itemid ,% :: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
|
||||
nodeidx ,% :: mod_pubsub:nodeIdx(),
|
||||
creation = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
|
||||
modification = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
|
||||
payload = [] % :: mod_pubsub:payload()
|
||||
@@ -171,8 +161,14 @@
|
||||
|
||||
-record(pubsub_last_item,
|
||||
{
|
||||
nodeid ,% :: mod_pubsub:nodeIdx(),
|
||||
nodeid ,% :: {binary(), mod_pubsub:nodeIdx()},
|
||||
itemid ,% :: mod_pubsub:itemId(),
|
||||
creation ,% :: {erlang:timestamp(), jlib:ljid()},
|
||||
payload % :: mod_pubsub:payload()
|
||||
}).
|
||||
|
||||
-record(pubsub_orphan,
|
||||
{
|
||||
nodeid ,% :: mod_pubsub:nodeIdx(),
|
||||
items = [] % :: list()
|
||||
}).
|
||||
|
||||
@@ -3,21 +3,21 @@ defmodule Ejabberd.Mixfile do
|
||||
|
||||
def project do
|
||||
[app: :ejabberd,
|
||||
version: "16.08.0",
|
||||
description: description,
|
||||
elixir: "~> 1.2",
|
||||
version: "17.8.0",
|
||||
description: description(),
|
||||
elixir: "~> 1.4",
|
||||
elixirc_paths: ["lib"],
|
||||
compile_path: ".",
|
||||
compilers: [:asn1] ++ Mix.compilers,
|
||||
erlc_options: erlc_options,
|
||||
erlc_options: erlc_options(),
|
||||
erlc_paths: ["asn1", "src"],
|
||||
# Elixir tests are starting the part of ejabberd they need
|
||||
aliases: [test: "test --no-start"],
|
||||
package: package,
|
||||
deps: deps]
|
||||
package: package(),
|
||||
deps: deps()]
|
||||
end
|
||||
|
||||
defp description do
|
||||
def description do
|
||||
"""
|
||||
Robust, ubiquitous and massively scalable Jabber / XMPP Instant Messaging platform.
|
||||
"""
|
||||
@@ -26,44 +26,67 @@ defmodule Ejabberd.Mixfile do
|
||||
def application do
|
||||
[mod: {:ejabberd_app, []},
|
||||
applications: [:ssl],
|
||||
included_applications: [:lager, :mnesia, :p1_utils, :cache_tab,
|
||||
:fast_tls, :stringprep, :fast_xml,
|
||||
:stun, :fast_yaml, :ezlib, :iconv,
|
||||
:esip, :jiffy, :p1_oauth2, :eredis,
|
||||
:p1_mysql, :p1_pgsql, :sqlite3]]
|
||||
included_applications: [:lager, :mnesia, :inets, :p1_utils, :cache_tab,
|
||||
:fast_tls, :stringprep, :fast_xml, :xmpp,
|
||||
:stun, :fast_yaml, :esip, :jiffy, :p1_oauth2]
|
||||
++ cond_apps()]
|
||||
end
|
||||
|
||||
defp erlc_options do
|
||||
# Use our own includes + includes from all dependencies
|
||||
includes = ["include"] ++ Path.wildcard(Path.join("..", "/*/include"))
|
||||
[:debug_info] ++ Enum.map(includes, fn(path) -> {:i, path} end)
|
||||
includes = ["include"] ++ deps_include(["fast_xml", "xmpp", "p1_utils"])
|
||||
[:debug_info, {:d, :ELIXIR_ENABLED}] ++ Enum.map(includes, fn(path) -> {:i, path} end)
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[{:lager, "~> 3.2"},
|
||||
[{:lager, "~> 3.4.0"},
|
||||
{:p1_utils, "~> 1.0"},
|
||||
{:fast_xml, "~> 1.1"},
|
||||
{:xmpp, "~> 1.1"},
|
||||
{:cache_tab, "~> 1.0"},
|
||||
{:stringprep, "~> 1.0"},
|
||||
{:fast_yaml, "~> 1.0"},
|
||||
{:fast_tls, "~> 1.0"},
|
||||
{:fast_xml, "~> 1.1"},
|
||||
{:stun, "~> 1.0"},
|
||||
{:esip, "~> 1.0"},
|
||||
{:jiffy, "~> 0.14.7"},
|
||||
{:p1_oauth2, "~> 0.6.1"},
|
||||
{:p1_mysql, "~> 1.0"},
|
||||
{:p1_pgsql, "~> 1.1"},
|
||||
{:sqlite3, "~> 1.1"},
|
||||
{:ezlib, "~> 1.0"},
|
||||
{:iconv, "~> 1.0"},
|
||||
{:eredis, "~> 1.0"},
|
||||
{:exrm, "~> 1.0.0", only: :dev},
|
||||
# relx is used by exrm. Lock version as for now, ejabberd doesn not compile fine with
|
||||
# version 3.20:
|
||||
{:relx, "~> 3.21", only: :dev},
|
||||
{:ex_doc, ">= 0.0.0", only: :dev},
|
||||
{:meck, "~> 0.8.4", only: :test},
|
||||
{:moka, github: "processone/moka", tag: "1.0.5c", only: :test}]
|
||||
{:distillery, "~> 1.0"},
|
||||
{:ex_doc, ">= 0.0.0", only: :dev}]
|
||||
++ cond_deps()
|
||||
end
|
||||
|
||||
defp deps_include(deps) do
|
||||
base = case Mix.Project.deps_paths()[:ejabberd] do
|
||||
nil -> "deps"
|
||||
_ -> ".."
|
||||
end
|
||||
Enum.map(deps, fn dep -> base<>"/#{dep}/include" end)
|
||||
end
|
||||
|
||||
defp cond_deps do
|
||||
for {:true, dep} <- [{config(:mysql), {:p1_mysql, "~> 1.0"}},
|
||||
{config(:pgsql), {:p1_pgsql, "~> 1.1"}},
|
||||
{config(:sqlite), {:sqlite3, "~> 1.1"}},
|
||||
{config(:riak), {:riakc, "~> 2.4"}},
|
||||
{config(:redis), {:eredis, "~> 1.0"}},
|
||||
{config(:zlib), {:ezlib, "~> 1.0"}},
|
||||
{config(:iconv), {:iconv, "~> 1.0"}},
|
||||
{config(:pam), {:epam, "~> 1.0"}},
|
||||
{config(:tools), {:luerl, github: "rvirding/luerl", tag: "v0.2"}},
|
||||
{config(:tools), {:meck, "~> 0.8.4"}},
|
||||
{config(:tools), {:moka, github: "processone/moka", tag: "1.0.5c"}}], do:
|
||||
dep
|
||||
end
|
||||
|
||||
defp cond_apps do
|
||||
for {:true, app} <- [{config(:redis), :eredis},
|
||||
{config(:mysql), :p1_mysql},
|
||||
{config(:pgsql), :p1_pgsql},
|
||||
{config(:sqlite), :sqlite3},
|
||||
{config(:zlib), :ezlib},
|
||||
{config(:iconv), :iconv}], do:
|
||||
app
|
||||
end
|
||||
|
||||
defp package do
|
||||
@@ -76,6 +99,21 @@ defmodule Ejabberd.Mixfile do
|
||||
"Source" => "https://github.com/processone/ejabberd",
|
||||
"ProcessOne" => "http://www.process-one.net/"}]
|
||||
end
|
||||
|
||||
defp vars do
|
||||
case :file.consult("vars.config") do
|
||||
{:ok,config} -> config
|
||||
_ -> [zlib: true, iconv: true]
|
||||
end
|
||||
end
|
||||
|
||||
defp config(key) do
|
||||
case vars()[key] do
|
||||
nil -> false
|
||||
value -> value
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
defmodule Mix.Tasks.Compile.Asn1 do
|
||||
@@ -104,7 +142,7 @@ defmodule Mix.Tasks.Compile.Asn1 do
|
||||
end)
|
||||
end
|
||||
|
||||
def manifests, do: [manifest]
|
||||
def manifests, do: [manifest()]
|
||||
defp manifest, do: Path.join(Mix.Project.manifest_path, @manifest)
|
||||
|
||||
def clean, do: Erlang.clean(manifest())
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
%{"bbmustache": {:hex, :bbmustache, "1.0.4", "7ba94f971c5afd7b6617918a4bb74705e36cab36eb84b19b6a1b7ee06427aa38", [:rebar], []},
|
||||
"cache_tab": {:hex, :cache_tab, "1.0.4", "3fd2b1ab40c36e7830a4e09e836c6b0fa89191cd4e5fd471873e4eb42f5cd37c", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
|
||||
"cf": {:hex, :cf, "0.2.1", "69d0b1349fd4d7d4dc55b7f407d29d7a840bf9a1ef5af529f1ebe0ce153fc2ab", [:rebar3], []},
|
||||
"earmark": {:hex, :earmark, "1.0.1", "2c2cd903bfdc3de3f189bd9a8d4569a075b88a8981ded9a0d95672f6e2b63141", [:mix], []},
|
||||
%{"cache_tab": {:hex, :cache_tab, "1.0.10", "dd6aba8951ba15cab4ad483d997f8eefdb0cb00225971d0629c730d107a2bed6", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
|
||||
"distillery": {:hex, :distillery, "1.4.1", "546d851bf27ae8fe0727e10e4fc4e146ad836eecee138263a60431e688044ed3", [:mix], []},
|
||||
"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], []},
|
||||
"eredis": {:hex, :eredis, "1.0.8", "ab4fda1c4ba7fbe6c19c26c249dc13da916d762502c4b4fa2df401a8d51c5364", [:rebar], []},
|
||||
"erlware_commons": {:hex, :erlware_commons, "0.21.0", "a04433071ad7d112edefc75ac77719dd3e6753e697ac09428fc83d7564b80b15", [:rebar3], [{:cf, "0.2.1", [hex: :cf, optional: false]}]},
|
||||
"esip": {:hex, :esip, "1.0.8", "69885a6c07964aabc6c077fe1372aa810a848bd3d9a415b160dabdce9c7a79b5", [:rebar3], [{:fast_tls, "1.0.7", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}, {:stun, "1.0.7", [hex: :stun, optional: false]}]},
|
||||
"ex_doc": {:hex, :ex_doc, "0.13.0", "aa2f8fe4c6136a2f7cfc0a7e06805f82530e91df00e2bff4b4362002b43ada65", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]},
|
||||
"exrm": {:hex, :exrm, "1.0.8", "5aa8990cdfe300282828b02cefdc339e235f7916388ce99f9a1f926a9271a45d", [:mix], [{:relx, "~> 3.5", [hex: :relx, optional: false]}]},
|
||||
"ezlib": {:hex, :ezlib, "1.0.1", "add8b2770a1a70c174aaea082b4a8668c0c7fdb03ee6cc81c6c68d3a6c3d767d", [:rebar3], []},
|
||||
"fast_tls": {:hex, :fast_tls, "1.0.7", "9b72ecfcdcad195ab072c196fab8334f49d8fea76bf1a51f536d69e7527d902a", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
|
||||
"fast_xml": {:hex, :fast_xml, "1.1.15", "6d23eb7f874e1357cf80a48d75a7bd0c8f6318029dc4b70122e9f54911f57f83", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.6", "3fe6feb7935ae8028b337e53e1db29e73ad3bca8041108f6a8f73b7175ece75c", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
|
||||
"getopt": {:hex, :getopt, "0.8.2", "b17556db683000ba50370b16c0619df1337e7af7ecbf7d64fbf8d1d6bce3109b", [:rebar], []},
|
||||
"goldrush": {:hex, :goldrush, "0.1.8", "2024ba375ceea47e27ea70e14d2c483b2d8610101b4e852ef7f89163cdb6e649", [:rebar3], []},
|
||||
"iconv": {:hex, :iconv, "1.0.2", "a0792f06ab4b5ea1b5bb49789405739f1281a91c44cf3879cb70e4d777666217", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
|
||||
"jiffy": {:hex, :jiffy, "0.14.7", "9f33b893edd6041ceae03bc1e50b412e858cc80b46f3d7535a7a9940a79a1c37", [:rebar, :make], []},
|
||||
"lager": {:hex, :lager, "3.2.1", "eef4e18b39e4195d37606d9088ea05bf1b745986cf8ec84f01d332456fe88d17", [:rebar3], [{:goldrush, "0.1.8", [hex: :goldrush, optional: false]}]},
|
||||
"meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:rebar, :make], []},
|
||||
"moka": {:git, "https://github.com/processone/moka.git", "3eed3a6dd7dedb70a6cd18f86c7561a18626eb3b", [tag: "1.0.5c"]},
|
||||
"p1_mysql": {:hex, :p1_mysql, "1.0.1", "d2be1cfc71bb4f1391090b62b74c3f5cb8e7a45b0076b8cb290cd6b2856c581b", [:rebar3], []},
|
||||
"esip": {:hex, :esip, "1.0.15", "82c8b0178618c10b1ac9690841d94025c982d63f8cd6c8f8bf920cf33e301658", [:rebar3], [{:fast_tls, "1.0.15", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}, {:stun, "1.0.14", [hex: :stun, optional: false]}]},
|
||||
"ex_doc": {:hex, :ex_doc, "0.16.2", "3b3e210ebcd85a7c76b4e73f85c5640c011d2a0b2f06dcdf5acdb2ae904e5084", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]},
|
||||
"ezlib": {:hex, :ezlib, "1.0.2", "22004ecf553a7d831404394d5642712e2aede90522e22bd6ccc089ca410ee098", [:rebar3], []},
|
||||
"fast_tls": {:hex, :fast_tls, "1.0.15", "96546e6a8b8384fbbcddf435c4c42cf2c0a3dc1858c3c9c2e62a74ae1ddd526a", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
|
||||
"fast_xml": {:hex, :fast_xml, "1.1.23", "1e7b311d3353806ee832d7630fef57713987cea40a7020669cf057d537de4721", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
|
||||
"fast_yaml": {:hex, :fast_yaml, "1.0.10", "ce5d52b77cb21968c8b73aa29b39f56a4ffd7e1e11f853d5597e7277858f155e", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
|
||||
"goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], []},
|
||||
"iconv": {:hex, :iconv, "1.0.5", "ae871aa11c854695db37e48fd5e5583b02e106126fbdf21bb53448f5a47c092b", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
|
||||
"jiffy": {:hex, :jiffy, "0.14.11", "919a87d491c5a6b5e3bbc27fafedc3a0761ca0b4c405394f121f582fd4e3f0e5", [:rebar3], []},
|
||||
"lager": {:hex, :lager, "3.4.2", "150b9a17b23ae6d3265cc10dc360747621cf217b7a22b8cddf03b2909dbf7aa5", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, optional: false]}]},
|
||||
"p1_mysql": {:hex, :p1_mysql, "1.0.3", "e2cc26f2e8d17c3885a9c2fee3ff64fcac5915896f50ab6f6aa9b0da1eed341c", [:rebar3], []},
|
||||
"p1_oauth2": {:hex, :p1_oauth2, "0.6.1", "4e021250cc198c538b097393671a41e7cebf463c248980320e038fe0316eb56b", [:rebar3], []},
|
||||
"p1_pgsql": {:hex, :p1_pgsql, "1.1.0", "ca525c42878eac095e5feb19563acc9915c845648f48fdec7ba6266c625d4ac7", [:rebar3], []},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.5", "3e698354fdc1fea5491d991457b0cb986c0a00a47d224feb841dc3ec82b9f721", [:rebar3], []},
|
||||
"providers": {:hex, :providers, "1.6.0", "db0e2f9043ae60c0155205fcd238d68516331d0e5146155e33d1e79dc452964a", [:rebar3], [{:getopt, "0.8.2", [hex: :getopt, optional: false]}]},
|
||||
"relx": {:hex, :relx, "3.21.0", "91e1ea9f09b4edfda8461901f4b5c5e0226e43ec161e147eeab29f7761df6eb5", [:rebar3], [{:bbmustache, "1.0.4", [hex: :bbmustache, optional: false]}, {:cf, "0.2.1", [hex: :cf, optional: false]}, {:erlware_commons, "0.21.0", [hex: :erlware_commons, optional: false]}, {:getopt, "0.8.2", [hex: :getopt, optional: false]}, {:providers, "1.6.0", [hex: :providers, optional: false]}]},
|
||||
"samerlib": {:git, "https://github.com/processone/samerlib", "fbbba035b1548ac4e681df00d61bf609645333a0", [tag: "0.8.0c"]},
|
||||
"p1_pgsql": {:hex, :p1_pgsql, "1.1.3", "ce94c83e9605c88d5f541b8f4b49edff3dc2bbacd1b6409c4cad0fbf7bef2ac4", [:rebar3], []},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.9", "c33c230efbeb4dcc02911161e3cb1a93231a92df15e3fc97de655a9271a26d9f", [:rebar3], []},
|
||||
"sqlite3": {:hex, :sqlite3, "1.1.5", "794738b6d07b6d36ec6d42492cb9d629bad9cf3761617b8b8d728e765db19840", [:rebar3], []},
|
||||
"stringprep": {:hex, :stringprep, "1.0.6", "1cf1c439eb038aa590da5456e019f86afbfbfeb5a2d37b6e5f873041624c6701", [:rebar3], [{:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]},
|
||||
"stun": {:hex, :stun, "1.0.7", "904dc6f26a3c30c54881c4c3003699f2a4968067ee6b3aecdf9895aad02df75e", [:rebar3], [{:fast_tls, "1.0.7", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.5", [hex: :p1_utils, optional: false]}]}}
|
||||
"stringprep": {:hex, :stringprep, "1.0.9", "9182ba39931cd1db528b8883cad0d63530abe2bf21835d26cec2f9af8bc00be0", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
|
||||
"stun": {:hex, :stun, "1.0.14", "6dc2080c25a72f7087301dc7333c1ea7d27ea4d88efaa379fc2b5924f3b17006", [:rebar3], [{:fast_tls, "1.0.15", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
|
||||
"xmpp": {:hex, :xmpp, "1.1.14", "e186f5208e7a448a4af784a8d2cb87cefe99dd49b24623e25d38115b23a50e12", [:rebar3], [{:fast_xml, "1.1.23", [hex: :fast_xml, optional: false]}, {:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}, {:stringprep, "1.0.9", [hex: :stringprep, optional: false]}]}}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
-module(override_deps_versions).
|
||||
-export([preprocess/2, 'pre_update-deps'/2, new_replace/1, new_replace/0]).
|
||||
|
||||
preprocess(Config, _Dirs) ->
|
||||
update_deps(Config).
|
||||
|
||||
update_deps(Config) ->
|
||||
LocalDeps = rebar_config:get_local(Config, deps, []),
|
||||
TopDeps = case rebar_config:get_xconf(Config, top_deps, []) of
|
||||
[] -> LocalDeps;
|
||||
Val -> Val
|
||||
end,
|
||||
Config2 = rebar_config:set_xconf(Config, top_deps, TopDeps),
|
||||
NewDeps = lists:map(fun({Name, _, _} = Dep) ->
|
||||
case lists:keyfind(Name, 1, TopDeps) of
|
||||
false -> Dep;
|
||||
TopDep -> TopDep
|
||||
end
|
||||
end, LocalDeps),
|
||||
%io:format("LD ~p~n", [LocalDeps]),
|
||||
%io:format("TD ~p~n", [TopDeps]),
|
||||
|
||||
Config3 = rebar_config:set(Config2, deps, NewDeps),
|
||||
{ok, Config3, []}.
|
||||
|
||||
|
||||
'pre_update-deps'(Config, _Dirs) ->
|
||||
{ok, Config2, _} = update_deps(Config),
|
||||
|
||||
case code:is_loaded(old_rebar_config) of
|
||||
false ->
|
||||
{_, Beam, _} = code:get_object_code(rebar_config),
|
||||
NBeam = rename(Beam, old_rebar_config),
|
||||
code:load_binary(old_rebar_config, "blank", NBeam),
|
||||
replace_mod(Beam);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{ok, Config2}.
|
||||
|
||||
new_replace() ->
|
||||
old_rebar_config:new().
|
||||
new_replace(Config) ->
|
||||
NC = old_rebar_config:new(Config),
|
||||
{ok, Conf, _} = update_deps(NC),
|
||||
Conf.
|
||||
|
||||
replace_mod(Beam) ->
|
||||
{ok, {_, [{exports, Exports}]}} = beam_lib:chunks(Beam, [exports]),
|
||||
Funcs = lists:filtermap(
|
||||
fun({module_info, _}) ->
|
||||
false;
|
||||
({Name, Arity}) ->
|
||||
Args = args(Arity),
|
||||
Call = case Name of
|
||||
new ->
|
||||
[erl_syntax:application(
|
||||
erl_syntax:abstract(override_deps_versions),
|
||||
erl_syntax:abstract(new_replace),
|
||||
Args)];
|
||||
_ ->
|
||||
[erl_syntax:application(
|
||||
erl_syntax:abstract(old_rebar_config),
|
||||
erl_syntax:abstract(Name),
|
||||
Args)]
|
||||
end,
|
||||
{true, erl_syntax:function(erl_syntax:abstract(Name),
|
||||
[erl_syntax:clause(Args, none,
|
||||
Call)])}
|
||||
end, Exports),
|
||||
Forms0 = ([erl_syntax:attribute(erl_syntax:abstract(module),
|
||||
[erl_syntax:abstract(rebar_config)])]
|
||||
++ Funcs),
|
||||
Forms = [erl_syntax:revert(Form) || Form <- Forms0],
|
||||
%io:format("--------------------------------------------------~n"
|
||||
% "~s~n",
|
||||
% [[erl_pp:form(Form) || Form <- Forms]]),
|
||||
{ok, Mod, Bin} = compile:forms(Forms, [report, export_all]),
|
||||
code:purge(rebar_config),
|
||||
{module, Mod} = code:load_binary(rebar_config, "mock", Bin).
|
||||
|
||||
|
||||
args(0) ->
|
||||
[];
|
||||
args(N) ->
|
||||
[arg(N) | args(N-1)].
|
||||
|
||||
arg(N) ->
|
||||
erl_syntax:variable(list_to_atom("A"++integer_to_list(N))).
|
||||
|
||||
rename(BeamBin0, Name) ->
|
||||
BeamBin = replace_in_atab(BeamBin0, Name),
|
||||
update_form_size(BeamBin).
|
||||
|
||||
%% Replace the first atom of the atom table with the new name
|
||||
replace_in_atab(<<"Atom", CnkSz0:32, Cnk:CnkSz0/binary, Rest/binary>>, Name) ->
|
||||
replace_first_atom(<<"Atom">>, Cnk, CnkSz0, Rest, latin1, Name);
|
||||
replace_in_atab(<<"AtU8", CnkSz0:32, Cnk:CnkSz0/binary, Rest/binary>>, Name) ->
|
||||
replace_first_atom(<<"AtU8">>, Cnk, CnkSz0, Rest, unicode, Name);
|
||||
replace_in_atab(<<C, Rest/binary>>, Name) ->
|
||||
<<C, (replace_in_atab(Rest, Name))/binary>>.
|
||||
|
||||
replace_first_atom(CnkName, Cnk, CnkSz0, Rest, Encoding, Name) ->
|
||||
<<NumAtoms:32, NameSz0:8, _Name0:NameSz0/binary, CnkRest/binary>> = Cnk,
|
||||
NumPad0 = num_pad_bytes(CnkSz0),
|
||||
<<_:NumPad0/unit:8, NextCnks/binary>> = Rest,
|
||||
NameBin = atom_to_binary(Name, Encoding),
|
||||
NameSz = byte_size(NameBin),
|
||||
CnkSz = CnkSz0 + NameSz - NameSz0,
|
||||
NumPad = num_pad_bytes(CnkSz),
|
||||
<<CnkName/binary, CnkSz:32, NumAtoms:32, NameSz:8, NameBin:NameSz/binary,
|
||||
CnkRest/binary, 0:NumPad/unit:8, NextCnks/binary>>.
|
||||
|
||||
|
||||
%% Calculate the number of padding bytes that have to be added for the
|
||||
%% BinSize to be an even multiple of ?beam_num_bytes_alignment.
|
||||
num_pad_bytes(BinSize) ->
|
||||
case 4 - (BinSize rem 4) of
|
||||
4 -> 0;
|
||||
N -> N
|
||||
end.
|
||||
|
||||
%% Update the size within the top-level form
|
||||
update_form_size(<<"FOR1", _OldSz:32, Rest/binary>> = Bin) ->
|
||||
Sz = size(Bin) - 8,
|
||||
<<"FOR1", Sz:32, Rest/binary>>.
|
||||
+2
-3
@@ -144,7 +144,7 @@
|
||||
{"Import Users from Dir at ","Importovat uživatele z adresáře na "}.
|
||||
{"Import Users From jabberd14 Spool Files","Importovat uživatele z jabberd14 spool souborů"}.
|
||||
{"Improper message type","Nesprávný typ zprávy"}.
|
||||
{"Incoming s2s Connections:",""}.
|
||||
{"Incoming s2s Connections:","Příchozí s2s spojení:"}.
|
||||
{"Incorrect password","Nesprávné heslo"}.
|
||||
{"Invalid affiliation: ~s","Neplatné přiřazení: ~s"}.
|
||||
{"Invalid role: ~s","Neplatná role: ~s"}.
|
||||
@@ -333,7 +333,6 @@
|
||||
{"September",". září"}.
|
||||
{"Server ~b","Server ~b"}.
|
||||
{"Server:","Server:"}.
|
||||
{"Server","Server"}.
|
||||
{"Set message of the day and send to online users","Nastavit zprávu dne a odeslat ji online uživatelům"}.
|
||||
{"Set message of the day on all hosts and send to online users","Nastavit zprávu dne a odeslat ji online uživatelům"}.
|
||||
{"Shared Roster Groups","Skupiny pro sdílený seznam kontaktů"}.
|
||||
@@ -409,10 +408,10 @@
|
||||
{"User JID","Jabber ID uživatele"}.
|
||||
{"User Management","Správa uživatelů"}.
|
||||
{"Username:","Uživatelské jméno:"}.
|
||||
{"User ~s",""}.
|
||||
{"Users are not allowed to register accounts so quickly","Je zakázáno registrovat účty v tak rychlém sledu"}.
|
||||
{"Users Last Activity","Poslední aktivita uživatele"}.
|
||||
{"Users","Uživatelé"}.
|
||||
{"User ~s","Uživatel ~s"}.
|
||||
{"User","Uživatel"}.
|
||||
{"Validate","Ověřit"}.
|
||||
{"vCard User Search","Hledání uživatelů podle vizitek"}.
|
||||
|
||||
+2
-6
@@ -242,9 +242,7 @@ msgstr "Odchozí s2s spojení:"
|
||||
|
||||
#: ejabberd_web_admin.erl:1559
|
||||
msgid "Incoming s2s Connections:"
|
||||
msgstr ""
|
||||
"Příchozí\n"
|
||||
" s2s spojení:"
|
||||
msgstr "Příchozí s2s spojení:"
|
||||
|
||||
#: ejabberd_web_admin.erl:1595 ejabberd_web_admin.erl:1794
|
||||
#: ejabberd_web_admin.erl:1804 ejabberd_web_admin.erl:2214 mod_roster.erl:1429
|
||||
@@ -258,9 +256,7 @@ msgstr "Změnit heslo"
|
||||
|
||||
#: ejabberd_web_admin.erl:1673
|
||||
msgid "User ~s"
|
||||
msgstr ""
|
||||
"Uživatel\n"
|
||||
" ~s"
|
||||
msgstr "Uživatel ~s"
|
||||
|
||||
#: ejabberd_web_admin.erl:1684
|
||||
msgid "Connected Resources:"
|
||||
|
||||
+11
-11
@@ -31,7 +31,7 @@ msgstr "foi removido"
|
||||
|
||||
#: ejabberd_c2s.erl:2105
|
||||
msgid "Your active privacy list has denied the routing of this stanza."
|
||||
msgstr "Sua lista de privacidade ativa negou o roteamento deste."
|
||||
msgstr "Sua lista de privacidade ativa negou o roteamento desta instância."
|
||||
|
||||
#: ejabberd_c2s.erl:2420
|
||||
msgid "Too many unacked stanzas"
|
||||
@@ -286,7 +286,7 @@ msgstr "Nós em execução"
|
||||
|
||||
#: ejabberd_web_admin.erl:1815 mod_configure.erl:526
|
||||
msgid "Stopped Nodes"
|
||||
msgstr "Nos parados"
|
||||
msgstr "Nós parados"
|
||||
|
||||
#: ejabberd_web_admin.erl:1833 ejabberd_web_admin.erl:1858
|
||||
msgid "Node ~p"
|
||||
@@ -302,7 +302,7 @@ msgstr "Salvar cópia de segurança"
|
||||
|
||||
#: ejabberd_web_admin.erl:1845
|
||||
msgid "Listened Ports"
|
||||
msgstr "Portas escutadas"
|
||||
msgstr "Portas abertas"
|
||||
|
||||
#: ejabberd_web_admin.erl:1848 ejabberd_web_admin.erl:2261
|
||||
msgid "Update"
|
||||
@@ -1013,7 +1013,7 @@ msgstr "Apelido"
|
||||
|
||||
#: mod_muc.erl:1050 mod_muc_room.erl:1104 mod_muc_room.erl:1843
|
||||
msgid "That nickname is registered by another person"
|
||||
msgstr "O nick já está registrado por outra pessoa"
|
||||
msgstr "O apelido já está registrado por outra pessoa"
|
||||
|
||||
#: mod_muc.erl:1078
|
||||
msgid "You must fill in field \"Nickname\" in the form"
|
||||
@@ -1303,7 +1303,7 @@ msgstr "O Jabber ID ~s não es válido"
|
||||
|
||||
#: mod_muc_room.erl:2772
|
||||
msgid "Nickname ~s does not exist in the room"
|
||||
msgstr "O nick ~s não existe na sala"
|
||||
msgstr "O apelido ~s não existe na sala"
|
||||
|
||||
#: mod_muc_room.erl:2795 mod_muc_room.erl:3175
|
||||
msgid "Invalid affiliation: ~s"
|
||||
@@ -1568,15 +1568,15 @@ msgstr "Entregar as notificações de evento"
|
||||
|
||||
#: mod_pubsub.erl:3749
|
||||
msgid "Notify subscribers when the node configuration changes"
|
||||
msgstr "Notificar subscritores quando cambia la configuração do nodo"
|
||||
msgstr "Notificar assinantes a configuração do nó mudar"
|
||||
|
||||
#: mod_pubsub.erl:3751
|
||||
msgid "Notify subscribers when the node is deleted"
|
||||
msgstr "Notificar subscritores quando o nodo se elimine"
|
||||
msgstr "Notificar assinantes quando o nó for eliminado se elimine"
|
||||
|
||||
#: mod_pubsub.erl:3753
|
||||
msgid "Notify subscribers when items are removed from the node"
|
||||
msgstr "Notificar subscritores quando os elementos se eliminem do nodo"
|
||||
msgstr "Notificar assinantes quando itens forem eliminados do nó"
|
||||
|
||||
#: mod_pubsub.erl:3755
|
||||
msgid "Persist items to storage"
|
||||
@@ -1637,7 +1637,7 @@ msgstr "A verificação do CAPTCHA falhou"
|
||||
#: mod_register.erl:253
|
||||
msgid "You need a client that supports x:data and CAPTCHA to register"
|
||||
msgstr ""
|
||||
"Você precisa de um cliente com suporte de x:data para poder registrar o nick"
|
||||
"Você precisa de um cliente com suporte de x:data para poder registrar o apelido"
|
||||
|
||||
#: mod_register.erl:259 mod_register.erl:320
|
||||
msgid "Choose a username and password to register with this server"
|
||||
@@ -1693,7 +1693,7 @@ msgid ""
|
||||
"(Jabber IDentifier) will be of the form: username@server. Please read "
|
||||
"carefully the instructions to fill correctly the fields."
|
||||
msgstr ""
|
||||
"Esta pagina aceita criações de novas contas Jabber neste servidor. A sua JID "
|
||||
"Esta pagina aceita criações de novas contas Jabber neste servidor. O seu JID "
|
||||
"(Identificador Jabber) será da seguinte forma: 'usuário@servidor'. Por "
|
||||
"favor, leia cuidadosamente as instruções para preencher corretamente os "
|
||||
"campos."
|
||||
@@ -1732,7 +1732,7 @@ msgid ""
|
||||
"Some Jabber clients can store your password in the computer, but you should "
|
||||
"do this only in your personal computer for safety reasons."
|
||||
msgstr ""
|
||||
"Alguns clientes jabber podem salvar a sua senha no seu computador. Use "
|
||||
"Alguns clientes jabber podem salvar a sua senha no seu computador. Use o "
|
||||
"recurso somente se você considera este computador seguro o suficiente."
|
||||
|
||||
#: mod_register_web.erl:256
|
||||
|
||||
+69
-44
@@ -1,49 +1,57 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @copyright (C) 2013-2016, Evgeniy Khramtsov
|
||||
%%% @doc
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 1 May 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
{deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.2.1"}}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.5"}}},
|
||||
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.4"}}},
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.7"}}},
|
||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.6"}}},
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.15"}}},
|
||||
{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.7"}}},
|
||||
{esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.8"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.6"}}},
|
||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.7"}}},
|
||||
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.1"}}},
|
||||
{deps, [{lager, ".*", {git, "https://github.com/erlang-lager/lager",
|
||||
{tag, {if_version_above, "17", "3.4.2", "3.2.1"}}}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.9"}}},
|
||||
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.10"}}},
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.15"}}},
|
||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.9"}}},
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.23"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.1.14"}}},
|
||||
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.10"}}},
|
||||
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
|
||||
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.2"}}},
|
||||
{luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.2"}}},
|
||||
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.14"}}}},
|
||||
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.15"}}}},
|
||||
{if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql",
|
||||
{tag, "1.0.1"}}}},
|
||||
{tag, "1.0.3"}}}},
|
||||
{if_var_true, pgsql, {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql",
|
||||
{tag, "1.1.0"}}}},
|
||||
{tag, "1.1.3"}}}},
|
||||
{if_var_true, sqlite, {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3",
|
||||
{tag, "1.1.5"}}}},
|
||||
{if_var_true, pam, {p1_pam, ".*", {git, "https://github.com/processone/epam",
|
||||
{tag, "1.0.0"}}}},
|
||||
{if_var_true, pam, {epam, ".*", {git, "https://github.com/processone/epam",
|
||||
{tag, "1.0.3"}}}},
|
||||
{if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib",
|
||||
{tag, "1.0.1"}}}},
|
||||
{tag, "1.0.2"}}}},
|
||||
{if_var_true, riak, {riakc, ".*", {git, "https://github.com/basho/riak-erlang-client",
|
||||
"527722d12d0433b837cdb92a60900c2cb5df8942"}}},
|
||||
%% Forces correct dependency for riakc and allow using newer meck version)
|
||||
{if_var_true, riak, {hamcrest, ".*", {git, "https://github.com/hyperthunk/hamcrest-erlang",
|
||||
"13f9bfb9b27d216e8e033b0e0a9a29097ed923dd"}}}, % for riak_pb-2.1.0.7
|
||||
{if_var_true, riak, {protobuffs, ".*", {git, "https://github.com/basho/erlang_protobuffs",
|
||||
"6e7fc924506e2dc166a6170e580ce1d95ebbd5bd"}}}, % for riak_pb-2.1.0.7 with correct meck dependency
|
||||
%% Elixir support, needed to run tests
|
||||
{tag, "2.4.1"}}}},
|
||||
%% Elixir support, needed to run tests
|
||||
{if_var_true, elixir, {elixir, ".*", {git, "https://github.com/elixir-lang/elixir",
|
||||
{tag, {if_version_above, "17", "v1.2.6", "v1.1.1"}}}}},
|
||||
{tag, {if_version_above, "17", "v1.4.4", "v1.1.1"}}}}},
|
||||
%% TODO: When modules are fully migrated to new structure and mix, we will not need anymore rebar_elixir_plugin
|
||||
{if_var_true, elixir, {rebar_elixir_plugin, ".*",
|
||||
{git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}},
|
||||
{if_not_rebar3, {if_var_true, elixir, {rebar_elixir_plugin, ".*",
|
||||
{git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}}},
|
||||
{if_var_true, iconv, {iconv, ".*", {git, "https://github.com/processone/iconv",
|
||||
{tag, "1.0.2"}}}},
|
||||
{tag, "1.0.5"}}}},
|
||||
{if_var_true, tools, {meck, "0.8.*", {git, "https://github.com/eproxus/meck",
|
||||
{tag, "0.8.4"}}}},
|
||||
{if_var_true, tools, {moka, ".*", {git, "https://github.com/processone/moka.git",
|
||||
@@ -57,25 +65,35 @@
|
||||
stringprep,
|
||||
fast_xml,
|
||||
esip,
|
||||
luerl,
|
||||
stun,
|
||||
fast_yaml,
|
||||
xmpp,
|
||||
p1_utils,
|
||||
p1_mysql,
|
||||
p1_pgsql,
|
||||
p1_pam,
|
||||
p1_oauth2,
|
||||
epam,
|
||||
ezlib,
|
||||
iconv]}}.
|
||||
|
||||
{erl_first_files, ["src/ejabberd_config.erl", "src/gen_mod.erl"]}.
|
||||
{erl_first_files, ["src/ejabberd_config.erl", "src/gen_mod.erl", "src/mod_muc_room.erl", "src/mod_push.erl"]}.
|
||||
|
||||
{erl_opts, [nowarn_deprecated_function,
|
||||
{i, "include"},
|
||||
{i, "deps/fast_xml/include"},
|
||||
{i, "deps/xmpp/include"},
|
||||
{i, "deps/p1_utils/include"},
|
||||
{if_var_false, debug, no_debug_info},
|
||||
{if_var_true, debug, debug_info},
|
||||
{if_var_true, sip, {d, 'SIP'}},
|
||||
{if_var_true, stun, {d, 'STUN'}},
|
||||
{if_var_true, roster_gateway_workaround, {d, 'ROSTER_GATWAY_WORKAROUND'}},
|
||||
{if_var_match, db_type, mssql, {d, 'mssql'}},
|
||||
{if_var_true, elixir, {d, 'ELIXIR_ENABLED'}},
|
||||
{if_var_true, erlang_deprecated_types, {d, 'ERL_DEPRECATED_TYPES'}},
|
||||
{if_have_fun, {crypto, strong_rand_bytes, 1}, {d, 'STRONG_RAND_BYTES'}},
|
||||
{if_have_fun, {gb_sets, iterator_from, 2}, {d, 'GB_SETS_ITERATOR_FROM'}},
|
||||
{if_have_fun, {public_key, short_name_hash, 1}, {d, 'SHORT_NAME_HASH'}},
|
||||
{if_var_true, hipe, native},
|
||||
{src_dirs, [asn1, src,
|
||||
{if_var_true, tools, tools},
|
||||
@@ -83,9 +101,12 @@
|
||||
|
||||
{deps_erl_opts, [{if_var_true, hipe, native}]}.
|
||||
|
||||
{plugins, [deps_erl_opts,
|
||||
{if_var_true, elixir, rebar_elixir_compiler},
|
||||
{if_var_true, elixir, rebar_exunit}]}.
|
||||
{if_rebar3, {plugins, [rebar3_hex, {provider_asn1, "0.2.0"}]}}.
|
||||
{if_not_rebar3, {plugins, [
|
||||
deps_erl_opts, override_deps_versions,
|
||||
{if_var_true, elixir, rebar_elixir_compiler},
|
||||
{if_var_true, elixir, rebar_exunit}
|
||||
]}}.
|
||||
|
||||
{if_var_true, elixir,
|
||||
{lib_dirs, ["deps/elixir/lib"]}}.
|
||||
@@ -113,10 +134,14 @@
|
||||
{if_var_false, iconv, "(\"iconv\":_/_)"},
|
||||
{if_var_false, odbc, "(\"odbc\":_/_)"},
|
||||
{if_var_false, sqlite, "(\"sqlite3\":_/_)"},
|
||||
{if_var_false, elixir, "(\"Elixir.Logger.*\":_/_)"},
|
||||
{if_var_false, elixir, "(\"Elixir.*\":_/_)"},
|
||||
{if_var_false, redis, "(\"eredis\":_/_)"}]}.
|
||||
|
||||
{eunit_compile_opts, [{i, "tools"}]}.
|
||||
{eunit_compile_opts, [{i, "tools"},
|
||||
{i, "include"},
|
||||
{i, "deps/p1_utils/include"},
|
||||
{i, "deps/fast_xml/include"},
|
||||
{i, "deps/xmpp/include"}]}.
|
||||
|
||||
{if_version_above, "17", {cover_enabled, true}}.
|
||||
{cover_export_enabled, true}.
|
||||
@@ -124,9 +149,9 @@
|
||||
{post_hook_configure, [{"fast_tls", []},
|
||||
{"stringprep", []},
|
||||
{"fast_yaml", []},
|
||||
{"esip", []},
|
||||
{if_var_true, sip, {"esip", []}},
|
||||
{"fast_xml", [{if_var_true, full_xml, "--enable-full-xml"}]},
|
||||
{if_var_true, pam, {"p1_pam", []}},
|
||||
{if_var_true, pam, {"epam", []}},
|
||||
{if_var_true, zlib, {"ezlib", []}},
|
||||
{if_var_true, iconv, {"iconv", []}}]}.
|
||||
|
||||
|
||||
+307
-129
@@ -1,32 +1,82 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @copyright (C) 2013-2016, Evgeniy Khramtsov
|
||||
%%% @doc
|
||||
%%%----------------------------------------------------------------------
|
||||
%%%
|
||||
%%% @end
|
||||
%%% Created : 1 May 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
ModCfg0 = fun(F, Cfg, [Key|Tail], Op, Default) ->
|
||||
{OldVal,PartCfg} = case lists:keytake(Key, 1, Cfg) of
|
||||
{value, {_, V1}, V2} -> {V1, V2};
|
||||
false -> {if Tail == [] -> Default; true -> [] end, Cfg}
|
||||
end,
|
||||
case Tail of
|
||||
[] ->
|
||||
[{Key, Op(OldVal)} | PartCfg];
|
||||
_ ->
|
||||
[{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg]
|
||||
end
|
||||
end,
|
||||
ModCfg = fun(Cfg, Keys, Op, Default) -> ModCfg0(ModCfg0, Cfg, Keys, Op, Default) end,
|
||||
Vars = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.config"])) of
|
||||
{ok, Terms} ->
|
||||
Terms;
|
||||
_Err ->
|
||||
[]
|
||||
end ++ [{cflags, "-g -O2 -Wall"}, {cppflags, "-g -O2 -Wall"},
|
||||
{ldflags, ""}, {system_deps, false}],
|
||||
{cflags, CFlags} = lists:keyfind(cflags, 1, Vars),
|
||||
{cppflags, CPPFlags} = lists:keyfind(cppflags, 1, Vars),
|
||||
{ldflags, LDFlags} = lists:keyfind(ldflags, 1, Vars),
|
||||
{system_deps, SystemDeps} = lists:keyfind(system_deps, 1, Vars),
|
||||
|
||||
Cfg = case file:consult(filename:join(filename:dirname(SCRIPT), "vars.config")) of
|
||||
{ok, Terms} ->
|
||||
Terms;
|
||||
_Err ->
|
||||
[]
|
||||
end,
|
||||
GetCfg0 = fun(F, Cfg, [Key | Tail], Default) ->
|
||||
Val = case lists:keyfind(Key, 1, Cfg) of
|
||||
{Key, V1} -> V1;
|
||||
false -> Default
|
||||
end,
|
||||
case Tail of
|
||||
[] ->
|
||||
Val;
|
||||
_ ->
|
||||
F(F, Val, Tail, Default)
|
||||
end
|
||||
end,
|
||||
ModCfg0 = fun(F, Cfg, [Key | Tail], Op, Default) ->
|
||||
{OldVal, PartCfg} = case lists:keytake(Key, 1, Cfg) of
|
||||
{value, {_, V1}, V2} -> {V1, V2};
|
||||
false -> {if Tail == [] -> Default; true -> [] end, Cfg}
|
||||
end,
|
||||
case Tail of
|
||||
[] ->
|
||||
[{Key, Op(OldVal)} | PartCfg];
|
||||
_ ->
|
||||
[{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg]
|
||||
end
|
||||
end,
|
||||
|
||||
FilterConfig = fun(F, Cfg, [{Path, true, ModFun, Default} | Tail]) ->
|
||||
F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun, Default), Tail);
|
||||
(F, Cfg, [{Path, SourcePath, true, ModFun, Default, SourceDefault} | Tail]) ->
|
||||
SourceVal = GetCfg0(GetCfg0, Cfg, SourcePath, SourceDefault),
|
||||
ModFun2 = fun(V) -> ModFun(V, SourceVal) end,
|
||||
F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun2, Default), Tail);
|
||||
(F, Cfg, [_ | Tail]) ->
|
||||
F(F, Cfg, Tail);
|
||||
(_, Cfg, []) ->
|
||||
Cfg
|
||||
end,
|
||||
|
||||
IsRebar3 = case application:get_key(rebar, vsn) of
|
||||
{ok, VSN} ->
|
||||
[VSN1 | _] = string:tokens(VSN, "-"),
|
||||
[Maj, _Min, _Patch] = string:tokens(VSN1, "."),
|
||||
(list_to_integer(Maj) >= 3);
|
||||
undefined ->
|
||||
lists:keymember(mix, 1, application:loaded_applications())
|
||||
end,
|
||||
|
||||
SysVer = erlang:system_info(otp_release),
|
||||
|
||||
ProcessSingleVar = fun(F, Var, Tail) ->
|
||||
case F(F, [Var], []) of
|
||||
@@ -36,10 +86,10 @@ ProcessSingleVar = fun(F, Var, Tail) ->
|
||||
end,
|
||||
|
||||
ProcessVars = fun(_F, [], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
(F, [{Type, Ver, Value} | Tail], Acc) when
|
||||
Type == if_version_above orelse
|
||||
Type == if_version_below ->
|
||||
lists:reverse(Acc);
|
||||
(F, [{Type, Ver, Value} | Tail], Acc) when
|
||||
Type == if_version_above orelse
|
||||
Type == if_version_below ->
|
||||
SysVer = erlang:system_info(otp_release),
|
||||
Include = if Type == if_version_above ->
|
||||
SysVer > Ver;
|
||||
@@ -47,129 +97,257 @@ ProcessVars = fun(_F, [], Acc) ->
|
||||
SysVer < Ver
|
||||
end,
|
||||
if Include ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
true ->
|
||||
F(F, Tail, Acc)
|
||||
F(F, Tail, Acc)
|
||||
end;
|
||||
(F, [{Type, Ver, Value, ElseValue} | Tail], Acc) when
|
||||
Type == if_version_above orelse
|
||||
Type == if_version_below ->
|
||||
SysVer = erlang:system_info(otp_release),
|
||||
(F, [{Type, Ver, Value, ElseValue} | Tail], Acc) when
|
||||
Type == if_version_above orelse
|
||||
Type == if_version_below ->
|
||||
Include = if Type == if_version_above ->
|
||||
SysVer > Ver;
|
||||
true ->
|
||||
SysVer < Ver
|
||||
end,
|
||||
if Include ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
true ->
|
||||
F(F, Tail, ProcessSingleVar(F, ElseValue, Acc))
|
||||
F(F, Tail, ProcessSingleVar(F, ElseValue, Acc))
|
||||
end;
|
||||
(F, [{Type, Var, Value} | Tail], Acc) when
|
||||
Type == if_var_true orelse
|
||||
Type == if_var_false ->
|
||||
Flag = Type == if_var_true,
|
||||
case proplists:get_bool(Var, Cfg) of
|
||||
V when V == Flag ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
_ ->
|
||||
F(F, Tail, Acc)
|
||||
end;
|
||||
(F, [{Type, Var, Match, Value} | Tail], Acc) when
|
||||
Type == if_var_match orelse
|
||||
Type == if_var_no_match ->
|
||||
case proplists:get_value(Var, Cfg) of
|
||||
V when V == Match ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
_ ->
|
||||
F(F, Tail, Acc)
|
||||
end;
|
||||
(F, [Other1 | Tail1], Acc) ->
|
||||
F(F, Tail1, [F(F, Other1, []) | Acc]);
|
||||
(F, Val, Acc) when is_tuple(Val) ->
|
||||
list_to_tuple(F(F, tuple_to_list(Val), Acc));
|
||||
(_F, Other2, _Acc) ->
|
||||
Other2
|
||||
end,
|
||||
(F, [{Type, Var, Value} | Tail], Acc) when
|
||||
Type == if_var_true orelse
|
||||
Type == if_var_false ->
|
||||
Flag = Type == if_var_true,
|
||||
case proplists:get_bool(Var, Vars) of
|
||||
V when V == Flag ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
_ ->
|
||||
F(F, Tail, Acc)
|
||||
end;
|
||||
(F, [{Type, Value} | Tail], Acc) when
|
||||
Type == if_rebar3 orelse
|
||||
Type == if_not_rebar3 ->
|
||||
Flag = Type == if_rebar3,
|
||||
case IsRebar3 == Flag of
|
||||
true ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
_ ->
|
||||
F(F, Tail, Acc)
|
||||
end;
|
||||
(F, [{Type, Var, Match, Value} | Tail], Acc) when
|
||||
Type == if_var_match orelse
|
||||
Type == if_var_no_match ->
|
||||
case proplists:get_value(Var, Vars) of
|
||||
V when V == Match ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
_ ->
|
||||
F(F, Tail, Acc)
|
||||
end;
|
||||
(F, [{if_have_fun, MFA, Value} | Tail], Acc) ->
|
||||
{Mod, Fun, Arity} = MFA,
|
||||
code:ensure_loaded(Mod),
|
||||
case erlang:function_exported(Mod, Fun, Arity) of
|
||||
true ->
|
||||
F(F, Tail, ProcessSingleVar(F, Value, Acc));
|
||||
false ->
|
||||
F(F, Tail, Acc)
|
||||
end;
|
||||
(F, [Other1 | Tail1], Acc) ->
|
||||
F(F, Tail1, [F(F, Other1, []) | Acc]);
|
||||
(F, Val, Acc) when is_tuple(Val) ->
|
||||
list_to_tuple(F(F, tuple_to_list(Val), Acc));
|
||||
(_F, Other2, _Acc) ->
|
||||
Other2
|
||||
end,
|
||||
|
||||
CFLags = proplists:get_value(cflags, Cfg, ""),
|
||||
CPPFLags = proplists:get_value(cppflags, Cfg, ""),
|
||||
LDFLags = proplists:get_value(ldflags, Cfg, ""),
|
||||
MaybeApply = fun(Val) when is_function(Val) ->
|
||||
Val();
|
||||
(Val) ->
|
||||
Val
|
||||
end,
|
||||
MaybeApply2 = fun(Val, Arg) when is_function(Val) ->
|
||||
Val(Arg);
|
||||
(Val, _) ->
|
||||
Val
|
||||
end,
|
||||
|
||||
ConfigureCmd = fun(Pkg, Flags) ->
|
||||
{'get-deps',
|
||||
"sh -c 'cd deps/" ++ Pkg ++
|
||||
" && CFLAGS=\""++ CFLags ++"\" CPPFLAGS=\""++ CPPFLags ++"\" LDFLAGS=\""++ LDFLags ++"\"" ++
|
||||
" ./configure " ++ Flags ++ "'"}
|
||||
end,
|
||||
AppendStr = fun(Append) ->
|
||||
fun("") ->
|
||||
lists:flatten(MaybeApply(Append));
|
||||
(Val) ->
|
||||
lists:flatten([Val, " ", MaybeApply(Append)])
|
||||
end
|
||||
end,
|
||||
AppendList = fun(Append) ->
|
||||
fun(Val) ->
|
||||
Val ++ MaybeApply(Append)
|
||||
end
|
||||
end,
|
||||
AppendStr2 = fun(Append) ->
|
||||
fun("", Arg) ->
|
||||
lists:flatten(MaybeApply2(Append, Arg));
|
||||
(Val, Arg) ->
|
||||
lists:flatten([Val, " ", MaybeApply2(Append, Arg)])
|
||||
end
|
||||
end,
|
||||
AppendList2 = fun(Append) ->
|
||||
fun(Val, Arg) ->
|
||||
Val ++ MaybeApply2(Append, Arg)
|
||||
end
|
||||
end,
|
||||
|
||||
Conf = ProcessVars(ProcessVars, CONFIG, []),
|
||||
Rebar3DepsFilter =
|
||||
fun(DepsList) ->
|
||||
lists:map(fun({DepName, _, {git, _, {tag, Version}}}) ->
|
||||
{DepName, Version};
|
||||
(Dep) ->
|
||||
Dep
|
||||
end, DepsList)
|
||||
end,
|
||||
|
||||
Conf1 = case lists:keytake(post_hook_configure, 1, Conf) of
|
||||
{value, {_, Items}, Rest} ->
|
||||
[{post_hooks, [ConfigureCmd(Mod, string:join(Opts, " ")) || {Mod, Opts} <- Items]} | Rest];
|
||||
_ ->
|
||||
Conf
|
||||
end,
|
||||
GlobalDepsFilter =
|
||||
fun(Deps) ->
|
||||
DepNames = lists:map(fun({DepName, _, _}) -> DepName;
|
||||
({DepName, _}) -> DepName
|
||||
end, Deps),
|
||||
lists:filtermap(fun(Dep) ->
|
||||
case code:lib_dir(Dep) of
|
||||
{error, _} ->
|
||||
{true, "Unable to locate dep '" ++ atom_to_list(Dep) ++ "' in system deps."};
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, DepNames)
|
||||
end,
|
||||
|
||||
{ok, Cwd} = file:get_cwd(),
|
||||
TestConfigFile = filename:join([Cwd, "test", "config.ctc"]),
|
||||
TestConfig = case file:read_file_info(TestConfigFile) of
|
||||
{ok, _} ->
|
||||
"-userconfig ct_config_plain " ++ TestConfigFile ++ " ";
|
||||
_ ->
|
||||
""
|
||||
end,
|
||||
{ok, _} ->
|
||||
[" -userconfig ct_config_plain ", TestConfigFile, " "];
|
||||
_ ->
|
||||
""
|
||||
end,
|
||||
|
||||
Conf2 = [{ct_extra_params, "-ct_hooks cth_surefire "
|
||||
++ TestConfig
|
||||
++ "-include "
|
||||
++ filename:join([Cwd, "tools"])} | Conf1],
|
||||
ResolveDepPath = case IsRebar3 of
|
||||
true ->
|
||||
fun("deps/" ++ Rest) ->
|
||||
Slash = string:str(Rest, "/"),
|
||||
Dir = "_build/default/lib/" ++
|
||||
string:sub_string(Rest, 1, Slash - 1),
|
||||
Dir ++ string:sub_string(Rest, Slash);
|
||||
(Path) ->
|
||||
Path
|
||||
end;
|
||||
_ ->
|
||||
fun(P) ->
|
||||
P
|
||||
end
|
||||
end,
|
||||
|
||||
Conf3 = case lists:keytake(xref_exclusions, 1, Conf2) of
|
||||
{value, {_, Items2}, Rest2} ->
|
||||
[{xref_queries, [{lists:flatten(["(XC - UC) || (XU - X - B ",
|
||||
[[" - ", V] || V <- Items2], ")"]), []}]} | Rest2];
|
||||
_ ->
|
||||
Conf2
|
||||
end,
|
||||
CtParams = fun(CompileOpts) ->
|
||||
["-ct_hooks cth_surefire ",
|
||||
lists:map(fun({i, IncPath}) ->
|
||||
[" -include ", filename:join([Cwd, ResolveDepPath(IncPath)])]
|
||||
end, CompileOpts),
|
||||
TestConfig]
|
||||
end,
|
||||
|
||||
Conf5 = case lists:keytake(floating_deps, 1, Conf3) of
|
||||
{value, {_, FloatingDeps}, Rest4} ->
|
||||
case lists:keytake(deps, 1, Rest4) of
|
||||
{value, {_, Deps}, Rest41} ->
|
||||
ND = lists:map(fun({DepName, Ver, {git, Repo, _Commit}}=Dep) ->
|
||||
case lists:member(DepName, FloatingDeps) of
|
||||
true ->
|
||||
{DepName, ".*", {git, Repo}};
|
||||
_ ->
|
||||
Dep
|
||||
end;
|
||||
(Dep2) ->
|
||||
Dep2
|
||||
end, Deps),
|
||||
[{deps, ND} | Rest41];
|
||||
_ ->
|
||||
Rest4
|
||||
end;
|
||||
_ ->
|
||||
Conf3
|
||||
end,
|
||||
GenDepConfigureLine =
|
||||
fun(DepPath, Flags) ->
|
||||
["sh -c 'if test ! -f ",DepPath,"config.status -o ",
|
||||
"config.status -nt ",DepPath,"config.status; ",
|
||||
"then (cd ", DepPath, " && ",
|
||||
"CFLAGS=\"", CFlags,"\" ",
|
||||
"CPPFLAGS=\"", CPPFlags, "\" "
|
||||
"LDFLAGS=\"", LDFlags, "\"",
|
||||
" ./configure ", string:join(Flags, " "),
|
||||
"); fi'"]
|
||||
end,
|
||||
|
||||
%% When running Travis test, upload test coverage result to coveralls:
|
||||
Conf6 = case {lists:keyfind(cover_enabled, 1, Conf5), os:getenv("TRAVIS")} of
|
||||
{{cover_enabled, true}, "true"} ->
|
||||
JobId = os:getenv("TRAVIS_JOB_ID"),
|
||||
CfgTemp = ModCfg(Conf5, [deps], fun(V) -> [{coveralls, ".*", {git, "https://github.com/markusn/coveralls-erl.git", "master"}}|V] end, []),
|
||||
ModCfg(CfgTemp, [post_hooks], fun(V) -> V ++ [{ct, "echo '\n%%! -pa ebin/ deps/coveralls/ebin\nmain(_)->{ok,F}=file:open(\"erlang.json\",[write]),io:fwrite(F,\"~s\",[coveralls:convert_file(\"logs/all.coverdata\", \""++JobId++"\", \"travis-ci\")]).' > getcover.erl"},
|
||||
{ct, "escript ./getcover.erl"}] end, []);
|
||||
_ ->
|
||||
Conf5
|
||||
end,
|
||||
GenDepsConfigure =
|
||||
fun(Hooks) ->
|
||||
lists:map(fun({Pkg, Flags}) ->
|
||||
DepPath = ResolveDepPath("deps/" ++ Pkg ++ "/"),
|
||||
{'compile',
|
||||
lists:flatten(GenDepConfigureLine(DepPath, Flags))}
|
||||
end, Hooks)
|
||||
end,
|
||||
|
||||
%io:format("ejabberd configuration:~n ~p~n", [Conf6]),
|
||||
ProcessErlOpt = fun(Vals) ->
|
||||
lists:map(
|
||||
fun({i, Path}) ->
|
||||
{i, ResolveDepPath(Path)};
|
||||
(ErlOpt) ->
|
||||
ErlOpt
|
||||
end, Vals)
|
||||
end,
|
||||
|
||||
Conf6.
|
||||
ProcssXrefExclusions = fun(Items) ->
|
||||
[{lists:flatten(["(XC - UC) || (XU - X - B ",
|
||||
[[" - ", V] || V <- Items], ")"]),
|
||||
[]}]
|
||||
end,
|
||||
|
||||
ProcessFloatingDeps =
|
||||
fun(Deps, FDeps) ->
|
||||
lists:map(fun({DepName, _Ver, {git, Repo, _Commit}} = Dep) ->
|
||||
case lists:member(DepName, FDeps) of
|
||||
true ->
|
||||
{DepName, ".*", {git, Repo}};
|
||||
_ ->
|
||||
Dep
|
||||
end;
|
||||
(Dep2) ->
|
||||
Dep2
|
||||
end, Deps)
|
||||
end,
|
||||
|
||||
TravisPostHooks =
|
||||
fun(true) ->
|
||||
[{eunit, "echo '\n%%! -pa .eunit/ deps/coveralls/ebin\n" ++
|
||||
"main(_)->{ok,F}=file:open(\"erlang.json\",[write])," ++
|
||||
"io:fwrite(F,\"~s\",[coveralls:convert_file(" ++
|
||||
"\".eunit/cover.coverdata\", \"" ++
|
||||
os:getenv("TRAVIS_JOB_ID") ++
|
||||
"\", \"travis-ci\",\"\")]).' > getcover.erl"},
|
||||
{eunit, "escript ./getcover.erl"}];
|
||||
(_) ->
|
||||
[]
|
||||
end,
|
||||
|
||||
Rules = [
|
||||
{[provider_hooks], IsRebar3,
|
||||
AppendList([{pre, [
|
||||
{compile, {asn, compile}},
|
||||
{clean, {asn, clean}}
|
||||
]}]), []},
|
||||
{[deps], os:getenv("TRAVIS") == "true",
|
||||
AppendList([{coveralls, ".*", {git, "https://github.com/markusn/coveralls-erl.git", "master"}}]), []},
|
||||
{[post_hooks], [cover_enabled], os:getenv("TRAVIS") == "true",
|
||||
AppendList2(TravisPostHooks), [], false},
|
||||
{[pre_hooks], [post_hook_configure], true,
|
||||
AppendList2(GenDepsConfigure), [], []},
|
||||
{[ct_extra_params], [eunit_compile_opts], true,
|
||||
AppendStr2(CtParams), "", []},
|
||||
{[erl_opts], true,
|
||||
ProcessErlOpt, []},
|
||||
{[xref_queries], [xref_exclusions], true,
|
||||
AppendList2(ProcssXrefExclusions), [], []},
|
||||
{[deps], [floating_deps], true,
|
||||
ProcessFloatingDeps, [], []},
|
||||
{[deps], IsRebar3,
|
||||
Rebar3DepsFilter, []},
|
||||
{[deps], SystemDeps /= false,
|
||||
GlobalDepsFilter, []}
|
||||
],
|
||||
|
||||
Config = [{plugin_dir, filename:join([filename:dirname(SCRIPT),"plugins"])}]++
|
||||
FilterConfig(FilterConfig, ProcessVars(ProcessVars, CONFIG, []), Rules),
|
||||
|
||||
%io:format("ejabberd configuration:~n ~p~n", [Config]),
|
||||
|
||||
Config.
|
||||
|
||||
%% Local Variables:
|
||||
%% mode: erlang
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%% @copyright (C) 2013-2016, Evgeniy Khramtsov
|
||||
%%% @copyright (C) 2013-2017, Evgeniy Khramtsov
|
||||
%%% @doc
|
||||
%%%
|
||||
%%% @end
|
||||
|
||||
+105
-50
@@ -1,5 +1,5 @@
|
||||
--
|
||||
-- ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
-- ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or
|
||||
-- modify it under the terms of the GNU General Public License as
|
||||
@@ -41,7 +41,7 @@ CREATE TABLE rosterusers (
|
||||
ask character(1) NOT NULL,
|
||||
askmessage text NOT NULL,
|
||||
server character(1) NOT NULL,
|
||||
subscribe text,
|
||||
subscribe text NOT NULL,
|
||||
type text,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
@@ -84,6 +84,31 @@ CREATE TABLE spool (
|
||||
|
||||
CREATE INDEX i_despool ON spool (username);
|
||||
|
||||
CREATE TABLE archive (
|
||||
username text NOT NULL,
|
||||
timestamp BIGINT UNSIGNED NOT NULL,
|
||||
peer text NOT NULL,
|
||||
bare_peer text NOT NULL,
|
||||
xml text NOT NULL,
|
||||
txt text,
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
kind text,
|
||||
nick text,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX i_username ON archive(username);
|
||||
CREATE INDEX i_timestamp ON archive(timestamp);
|
||||
CREATE INDEX i_peer ON archive(peer);
|
||||
CREATE INDEX i_bare_peer ON archive(bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL PRIMARY KEY,
|
||||
def text NOT NULL,
|
||||
always text NOT NULL,
|
||||
never text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE vcard (
|
||||
username text PRIMARY KEY,
|
||||
@@ -91,12 +116,6 @@ CREATE TABLE vcard (
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE vcard_xupdate (
|
||||
username text PRIMARY KEY,
|
||||
hash text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE vcard_search (
|
||||
username text NOT NULL,
|
||||
lusername text PRIMARY KEY,
|
||||
@@ -181,10 +200,10 @@ CREATE TABLE roster_version (
|
||||
);
|
||||
|
||||
CREATE TABLE pubsub_node (
|
||||
host text,
|
||||
node text,
|
||||
parent text,
|
||||
type text,
|
||||
host text NOT NULL,
|
||||
node text NOT NULL,
|
||||
parent text NOT NULL DEFAULT '',
|
||||
type text NOT NULL,
|
||||
nodeid INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
);
|
||||
CREATE INDEX i_pubsub_node_parent ON pubsub_node (parent);
|
||||
@@ -192,22 +211,22 @@ CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node (host, node);
|
||||
|
||||
CREATE TABLE pubsub_node_option (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
name text,
|
||||
val text
|
||||
name text NOT NULL,
|
||||
val text NOT NULL
|
||||
);
|
||||
CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option (nodeid);
|
||||
|
||||
CREATE TABLE pubsub_node_owner (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
owner text
|
||||
owner text NOT NULL
|
||||
);
|
||||
CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner (nodeid);
|
||||
|
||||
CREATE TABLE pubsub_state (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
jid text,
|
||||
jid text NOT NULL,
|
||||
affiliation character(1),
|
||||
subscriptions text,
|
||||
subscriptions text NOT NULL DEFAULT '',
|
||||
stateid INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
);
|
||||
CREATE INDEX i_pubsub_state_jid ON pubsub_state (jid);
|
||||
@@ -215,19 +234,19 @@ CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state (nodeid, jid);
|
||||
|
||||
CREATE TABLE pubsub_item (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
itemid text,
|
||||
publisher text,
|
||||
creation text,
|
||||
modification text,
|
||||
payload text
|
||||
itemid text NOT NULL,
|
||||
publisher text NOT NULL,
|
||||
creation text NOT NULL,
|
||||
modification text NOT NULL,
|
||||
payload text NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid);
|
||||
CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item (nodeid, itemid);
|
||||
|
||||
CREATE TABLE pubsub_subscription_opt (
|
||||
subid text,
|
||||
subid text NOT NULL,
|
||||
opt_name varchar(32),
|
||||
opt_value text
|
||||
opt_value text NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt (subid, opt_name);
|
||||
|
||||
@@ -250,6 +269,27 @@ CREATE TABLE muc_registered (
|
||||
CREATE INDEX i_muc_registered_nick ON muc_registered (nick);
|
||||
CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered (jid, host);
|
||||
|
||||
CREATE TABLE muc_online_room (
|
||||
name text NOT NULL,
|
||||
host text NOT NULL,
|
||||
node text NOT NULL,
|
||||
pid text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room (name, host);
|
||||
|
||||
CREATE TABLE muc_online_users (
|
||||
username text NOT NULL,
|
||||
server text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
name text NOT NULL,
|
||||
host text NOT NULL,
|
||||
node text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users (username, server, resource, name, host);
|
||||
CREATE INDEX i_muc_online_users_us ON muc_online_users (username, server);
|
||||
|
||||
CREATE TABLE irc_custom (
|
||||
jid text NOT NULL,
|
||||
host text NOT NULL,
|
||||
@@ -274,32 +314,6 @@ CREATE TABLE caps_features (
|
||||
|
||||
CREATE INDEX i_caps_features_node_subnode ON caps_features (node, subnode);
|
||||
|
||||
CREATE TABLE archive (
|
||||
username text NOT NULL,
|
||||
timestamp BIGINT UNSIGNED NOT NULL,
|
||||
peer text NOT NULL,
|
||||
bare_peer text NOT NULL,
|
||||
xml text NOT NULL,
|
||||
txt text,
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
kind text,
|
||||
nick text,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX i_username ON archive(username);
|
||||
CREATE INDEX i_timestamp ON archive(timestamp);
|
||||
CREATE INDEX i_peer ON archive(peer);
|
||||
CREATE INDEX i_bare_peer ON archive(bare_peer);
|
||||
|
||||
CREATE TABLE archive_prefs (
|
||||
username text NOT NULL PRIMARY KEY,
|
||||
def text NOT NULL,
|
||||
always text NOT NULL,
|
||||
never text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE sm (
|
||||
usec bigint NOT NULL,
|
||||
pid text NOT NULL,
|
||||
@@ -320,3 +334,44 @@ CREATE TABLE oauth_token (
|
||||
scope text NOT NULL,
|
||||
expire bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE route (
|
||||
domain text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
node text NOT NULL,
|
||||
pid text NOT NULL,
|
||||
local_hint text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_route ON route(domain, server_host, node, pid);
|
||||
CREATE INDEX i_route_domain ON route(domain);
|
||||
|
||||
CREATE TABLE bosh (
|
||||
sid text NOT NULL,
|
||||
node text NOT NULL,
|
||||
pid text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid);
|
||||
|
||||
CREATE TABLE carboncopy (
|
||||
username text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
namespace text NOT NULL,
|
||||
node text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy (username, resource);
|
||||
CREATE INDEX i_carboncopy_user ON carboncopy (username);
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
pid_t text NOT NULL,
|
||||
pid_i text NOT NULL,
|
||||
node_t text NOT NULL,
|
||||
node_i text NOT NULL,
|
||||
jid_i text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid);
|
||||
CREATE INDEX i_proxy65_jid ON proxy65 (jid_i);
|
||||
|
||||
+80
-29
@@ -1,5 +1,5 @@
|
||||
--
|
||||
-- ejabberd, Copyright (C) 2002-2015 ProcessOne
|
||||
-- ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or
|
||||
-- modify it under the terms of the GNU General Public License as
|
||||
@@ -125,6 +125,30 @@ CREATE TABLE [dbo].[muc_room] (
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_room_name_host] ON [muc_room] (name, host)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[muc_online_room] (
|
||||
[name] [varchar] (250) NOT NULL,
|
||||
[host] [varchar] (250) NOT NULL,
|
||||
[node] [text] NOT NULL,
|
||||
[pid] [text] NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_online_room_name_host] ON [muc_online_room] (name, host)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[muc_online_users] (
|
||||
[username] [varchar] (250) NOT NULL,
|
||||
[server] [varchar] (250) NOT NULL,
|
||||
[resource] [varchar] (250) NOT NULL,
|
||||
[name] [varchar] (250) NOT NULL,
|
||||
[host] [varchar] (250) NOT NULL,
|
||||
node text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_online_users_i] ON [muc_online_users] (username, server, resource, name, host)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
CREATE UNIQUE CLUSTERED INDEX [muc_online_users_us] ON [muc_online_users] (username, server);
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[privacy_default_list] (
|
||||
[username] [varchar] (250) NOT NULL,
|
||||
[name] [varchar] (250) NOT NULL,
|
||||
@@ -182,11 +206,11 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW
|
||||
|
||||
CREATE TABLE [dbo].[pubsub_item] (
|
||||
[nodeid] [bigint] NULL,
|
||||
[itemid] [varchar] (255) NULL,
|
||||
[publisher] [text] NULL,
|
||||
[creation] [text] NULL,
|
||||
[modification] [varchar] (255) NULL,
|
||||
[payload] [text] NULL
|
||||
[itemid] [varchar] (255) NOT NULL,
|
||||
[publisher] [text] NOT NULL,
|
||||
[creation] [text] NOT NULL,
|
||||
[modification] [varchar] (255) NOT NULL,
|
||||
[payload] [text] NOT NULL DEFAULT ''
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
CREATE INDEX [pubsub_item_itemid] ON [pubsub_item] (itemid)
|
||||
@@ -197,8 +221,8 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW
|
||||
|
||||
CREATE TABLE [dbo].[pubsub_node_option] (
|
||||
[nodeid] [bigint] NULL,
|
||||
[name] [text] NULL,
|
||||
[val] [text] NULL
|
||||
[name] [text] NOT NULL,
|
||||
[val] [text] NOT NULL
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
CREATE CLUSTERED INDEX [pubsub_node_option_nodeid] ON [pubsub_node_option] (nodeid)
|
||||
@@ -206,7 +230,7 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW
|
||||
|
||||
CREATE TABLE [dbo].[pubsub_node_owner] (
|
||||
[nodeid] [bigint] NULL,
|
||||
[owner] [text] NULL
|
||||
[owner] [text] NOT NULL
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
CREATE CLUSTERED INDEX [pubsub_node_owner_nodeid] ON [pubsub_node_owner] (nodeid)
|
||||
@@ -214,9 +238,9 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW
|
||||
|
||||
CREATE TABLE [dbo].[pubsub_state] (
|
||||
[nodeid] [bigint] NULL,
|
||||
[jid] [varchar] (255) NULL,
|
||||
[affiliation] [char] (1) NULL,
|
||||
[subscriptions] [text] NULL,
|
||||
[jid] [varchar] (255) NOT NULL,
|
||||
[affiliation] [char] (1) NOT NULL,
|
||||
[subscriptions] [text] NOT NULL DEFAULT '',
|
||||
[stateid] [bigint] IDENTITY(1,1) NOT NULL,
|
||||
CONSTRAINT [pubsub_state_PRIMARY] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
@@ -231,19 +255,19 @@ CREATE UNIQUE INDEX [pubsub_state_nodeid_jid] ON [pubsub_state] (nodeid, jid)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[pubsub_subscription_opt] (
|
||||
[subid] [varchar] (255) NULL,
|
||||
[opt_name] [varchar] (32) NULL,
|
||||
[opt_value] [text] NULL
|
||||
[subid] [varchar] (255) NOT NULL,
|
||||
[opt_name] [varchar] (32) NOT NULL,
|
||||
[opt_value] [text] NOT NULL
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [pubsub_subscription_opt_subid_opt_name] ON [pubsub_subscription_opt] (subid, opt_name)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[pubsub_node] (
|
||||
[host] [varchar] (255) NULL,
|
||||
[node] [varchar] (255) NULL,
|
||||
[parent] [varchar] (255) NULL,
|
||||
[type] [text] NULL,
|
||||
[host] [varchar] (255) NOT NULL,
|
||||
[node] [varchar] (255) NOT NULL,
|
||||
[parent] [varchar] (255) NOT NULL DEFAULT '',
|
||||
[type] [text] NOT NULL,
|
||||
[nodeid] [bigint] IDENTITY(1,1) NOT NULL,
|
||||
CONSTRAINT [pubsub_node_PRIMARY] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
@@ -446,16 +470,6 @@ WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW
|
||||
CREATE INDEX [vcard_search_lorgunit] ON [vcard_search] (lorgunit)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[vcard_xupdate] (
|
||||
[username] [varchar] (250) NOT NULL,
|
||||
[hash] [text] NOT NULL,
|
||||
[created_at] [datetime] NOT NULL DEFAULT GETDATE(),
|
||||
CONSTRAINT [vcard_xupdate_PRIMARY] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[username] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
ALTER TABLE [dbo].[pubsub_item] WITH CHECK ADD CONSTRAINT [pubsub_item_ibfk_1] FOREIGN KEY([nodeid])
|
||||
REFERENCES [dbo].[pubsub_node] ([nodeid])
|
||||
ON DELETE CASCADE;
|
||||
@@ -490,3 +504,40 @@ CREATE TABLE [dbo].[oauth_token] (
|
||||
[token] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
CREATE TABLE [dbo].[route] (
|
||||
[domain] [varchar] (255) NOT NULL,
|
||||
[server_host] [varchar] (255) NOT NULL,
|
||||
[node] [varchar] (255) NOT NULL,
|
||||
[pid] [varchar](100) NOT NULL,
|
||||
[local_hint] [text] NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [route_i] ON [route] (domain, server_host, node, pid)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE INDEX [route_domain] ON [route] (domain)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE TABLE [dbo].[bosh] (
|
||||
[sid] [varchar] (255) NOT NULL,
|
||||
[node] [varchar] (255) NOT NULL,
|
||||
[pid] [varchar](100) NOT NULL
|
||||
CONSTRAINT [bosh_PRIMARY] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[sid] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
|
||||
) TEXTIMAGE_ON [PRIMARY];
|
||||
|
||||
CREATE TABLE [dbo].[carboncopy] (
|
||||
[username] [varchar] (255) NOT NULL,
|
||||
[resource] [varchar] (255) NOT NULL,
|
||||
[namespace] [varchar] (255) NOT NULL,
|
||||
[node] [varchar] (255) NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE CLUSTERED INDEX [carboncopy_ur] ON [carboncopy] (username, resource)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
CREATE INDEX [carboncopy_user] ON [carboncopy] (username)
|
||||
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);
|
||||
|
||||
+79
-23
@@ -1,5 +1,5 @@
|
||||
--
|
||||
-- ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
-- ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or
|
||||
-- modify it under the terms of the GNU General Public License as
|
||||
@@ -121,12 +121,6 @@ CREATE TABLE vcard (
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE vcard_xupdate (
|
||||
username varchar(191) PRIMARY KEY,
|
||||
hash text NOT NULL,
|
||||
created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE vcard_search (
|
||||
username varchar(191) NOT NULL,
|
||||
lusername varchar(191) PRIMARY KEY,
|
||||
@@ -218,10 +212,10 @@ CREATE TABLE roster_version (
|
||||
-- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL;
|
||||
|
||||
CREATE TABLE pubsub_node (
|
||||
host text,
|
||||
node text,
|
||||
parent text,
|
||||
type text,
|
||||
host text NOT NULL,
|
||||
node text NOT NULL,
|
||||
parent VARCHAR(191) NOT NULL DEFAULT '',
|
||||
type text NOT NULL,
|
||||
nodeid bigint auto_increment primary key
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX i_pubsub_node_parent ON pubsub_node(parent(120));
|
||||
@@ -229,24 +223,24 @@ CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node(host(20), node(120));
|
||||
|
||||
CREATE TABLE pubsub_node_option (
|
||||
nodeid bigint,
|
||||
name text,
|
||||
val text
|
||||
name text NOT NULL,
|
||||
val text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option(nodeid);
|
||||
ALTER TABLE `pubsub_node_option` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE pubsub_node_owner (
|
||||
nodeid bigint,
|
||||
owner text
|
||||
owner text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner(nodeid);
|
||||
ALTER TABLE `pubsub_node_owner` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE pubsub_state (
|
||||
nodeid bigint,
|
||||
jid text,
|
||||
jid text NOT NULL,
|
||||
affiliation character(1),
|
||||
subscriptions text,
|
||||
subscriptions VARCHAR(191) NOT NULL DEFAULT '',
|
||||
stateid bigint auto_increment primary key
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX i_pubsub_state_jid ON pubsub_state(jid(60));
|
||||
@@ -255,20 +249,20 @@ ALTER TABLE `pubsub_state` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (
|
||||
|
||||
CREATE TABLE pubsub_item (
|
||||
nodeid bigint,
|
||||
itemid text,
|
||||
publisher text,
|
||||
creation text,
|
||||
modification text,
|
||||
payload text
|
||||
itemid text NOT NULL,
|
||||
publisher text NOT NULL,
|
||||
creation text NOT NULL,
|
||||
modification text NOT NULL,
|
||||
payload text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36));
|
||||
CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36));
|
||||
ALTER TABLE `pubsub_item` ADD FOREIGN KEY (`nodeid`) REFERENCES `pubsub_node` (`nodeid`) ON DELETE CASCADE;
|
||||
|
||||
CREATE TABLE pubsub_subscription_opt (
|
||||
subid text,
|
||||
subid text NOT NULL,
|
||||
opt_name varchar(32),
|
||||
opt_value text
|
||||
opt_value text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt(subid(32), opt_name(32));
|
||||
|
||||
@@ -291,6 +285,27 @@ CREATE TABLE muc_registered (
|
||||
CREATE INDEX i_muc_registered_nick USING BTREE ON muc_registered(nick(75));
|
||||
CREATE UNIQUE INDEX i_muc_registered_jid_host USING BTREE ON muc_registered(jid(75), host(75));
|
||||
|
||||
CREATE TABLE muc_online_room (
|
||||
name text NOT NULL,
|
||||
host text NOT NULL,
|
||||
node text NOT NULL,
|
||||
pid text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX i_muc_online_room_name_host USING BTREE ON muc_online_room(name(75), host(75));
|
||||
|
||||
CREATE TABLE muc_online_users (
|
||||
username text NOT NULL,
|
||||
server text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
name text NOT NULL,
|
||||
host text NOT NULL,
|
||||
node text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX i_muc_online_users USING BTREE ON muc_online_users(username(75), server(75), resource(75), name(75), host(75));
|
||||
CREATE INDEX i_muc_online_users_us USING BTREE ON muc_online_users(username(75), server(75));
|
||||
|
||||
CREATE TABLE irc_custom (
|
||||
jid text NOT NULL,
|
||||
host text NOT NULL,
|
||||
@@ -335,3 +350,44 @@ CREATE TABLE oauth_token (
|
||||
scope text NOT NULL,
|
||||
expire bigint NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE route (
|
||||
domain text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
node text NOT NULL,
|
||||
pid text NOT NULL,
|
||||
local_hint text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX i_route ON route(domain(75), server_host(75), node(75), pid(75));
|
||||
CREATE INDEX i_route_domain ON route(domain(75));
|
||||
|
||||
CREATE TABLE bosh (
|
||||
sid text NOT NULL,
|
||||
node text NOT NULL,
|
||||
pid text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75));
|
||||
|
||||
CREATE TABLE carboncopy (
|
||||
username text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
namespace text NOT NULL,
|
||||
node text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy (username(75), resource(75));
|
||||
CREATE INDEX i_carboncopy_user ON carboncopy (username(75));
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
pid_t text NOT NULL,
|
||||
pid_i text NOT NULL,
|
||||
node_t text NOT NULL,
|
||||
node_i text NOT NULL,
|
||||
jid_i text NOT NULL
|
||||
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid(191));
|
||||
CREATE INDEX i_proxy65_jid ON proxy65 (jid_i(191));
|
||||
|
||||
+81
-25
@@ -1,5 +1,5 @@
|
||||
--
|
||||
-- ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
-- ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
--
|
||||
-- This program is free software; you can redistribute it and/or
|
||||
-- modify it under the terms of the GNU General Public License as
|
||||
@@ -10,7 +10,7 @@
|
||||
-- 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.
|
||||
@@ -45,7 +45,7 @@ CREATE TABLE rosterusers (
|
||||
ask character(1) NOT NULL,
|
||||
askmessage text NOT NULL,
|
||||
server character(1) NOT NULL,
|
||||
subscribe text,
|
||||
subscribe text NOT NULL,
|
||||
"type" text,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now()
|
||||
);
|
||||
@@ -120,12 +120,6 @@ CREATE TABLE vcard (
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE vcard_xupdate (
|
||||
username text PRIMARY KEY,
|
||||
hash text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE vcard_search (
|
||||
username text NOT NULL,
|
||||
lusername text PRIMARY KEY,
|
||||
@@ -224,10 +218,10 @@ CREATE TABLE roster_version (
|
||||
-- ALTER TABLE rosterusers ALTER COLUMN askmessage SET NOT NULL;
|
||||
|
||||
CREATE TABLE pubsub_node (
|
||||
host text,
|
||||
node text,
|
||||
parent text,
|
||||
"type" text,
|
||||
host text NOT NULL,
|
||||
node text NOT NULL,
|
||||
parent text NOT NULL DEFAULT '',
|
||||
"type" text NOT NULL,
|
||||
nodeid SERIAL UNIQUE
|
||||
);
|
||||
CREATE INDEX i_pubsub_node_parent ON pubsub_node USING btree (parent);
|
||||
@@ -235,22 +229,22 @@ CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node USING btree (host, node);
|
||||
|
||||
CREATE TABLE pubsub_node_option (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
name text,
|
||||
val text
|
||||
name text NOT NULL,
|
||||
val text NOT NULL
|
||||
);
|
||||
CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option USING btree (nodeid);
|
||||
|
||||
CREATE TABLE pubsub_node_owner (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
owner text
|
||||
owner text NOT NULL
|
||||
);
|
||||
CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner USING btree (nodeid);
|
||||
|
||||
CREATE TABLE pubsub_state (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
jid text,
|
||||
jid text NOT NULL,
|
||||
affiliation character(1),
|
||||
subscriptions text,
|
||||
subscriptions text NOT NULL DEFAULT '',
|
||||
stateid SERIAL UNIQUE
|
||||
);
|
||||
CREATE INDEX i_pubsub_state_jid ON pubsub_state USING btree (jid);
|
||||
@@ -258,19 +252,19 @@ CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state USING btree (nodeid, ji
|
||||
|
||||
CREATE TABLE pubsub_item (
|
||||
nodeid bigint REFERENCES pubsub_node(nodeid) ON DELETE CASCADE,
|
||||
itemid text,
|
||||
publisher text,
|
||||
creation text,
|
||||
modification text,
|
||||
payload text
|
||||
itemid text NOT NULL,
|
||||
publisher text NOT NULL,
|
||||
creation text NOT NULL,
|
||||
modification text NOT NULL,
|
||||
payload text NOT NULL DEFAULT ''
|
||||
);
|
||||
CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid);
|
||||
CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item USING btree (nodeid, itemid);
|
||||
|
||||
CREATE TABLE pubsub_subscription_opt (
|
||||
subid text,
|
||||
subid text NOT NULL,
|
||||
opt_name varchar(32),
|
||||
opt_value text
|
||||
opt_value text NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt USING btree (subid, opt_name);
|
||||
|
||||
@@ -293,6 +287,27 @@ CREATE TABLE muc_registered (
|
||||
CREATE INDEX i_muc_registered_nick ON muc_registered USING btree (nick);
|
||||
CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered USING btree (jid, host);
|
||||
|
||||
CREATE TABLE muc_online_room (
|
||||
name text NOT NULL,
|
||||
host text NOT NULL,
|
||||
node text NOT NULL,
|
||||
pid text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room USING btree (name, host);
|
||||
|
||||
CREATE TABLE muc_online_users (
|
||||
username text NOT NULL,
|
||||
server text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
name text NOT NULL,
|
||||
host text NOT NULL,
|
||||
node text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users USING btree (username, server, resource, name, host);
|
||||
CREATE INDEX i_muc_online_users_us ON muc_online_users USING btree (username, server);
|
||||
|
||||
CREATE TABLE irc_custom (
|
||||
jid text NOT NULL,
|
||||
host text NOT NULL,
|
||||
@@ -339,3 +354,44 @@ CREATE TABLE oauth_token (
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token);
|
||||
|
||||
CREATE TABLE route (
|
||||
domain text NOT NULL,
|
||||
server_host text NOT NULL,
|
||||
node text NOT NULL,
|
||||
pid text NOT NULL,
|
||||
local_hint text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, node, pid);
|
||||
CREATE INDEX i_route_domain ON route USING btree (domain);
|
||||
|
||||
CREATE TABLE bosh (
|
||||
sid text NOT NULL,
|
||||
node text NOT NULL,
|
||||
pid text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid);
|
||||
|
||||
CREATE TABLE carboncopy (
|
||||
username text NOT NULL,
|
||||
resource text NOT NULL,
|
||||
namespace text NOT NULL,
|
||||
node text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy USING btree (username, resource);
|
||||
CREATE INDEX i_carboncopy_user ON carboncopy USING btree (username);
|
||||
|
||||
CREATE TABLE proxy65 (
|
||||
sid text NOT NULL,
|
||||
pid_t text NOT NULL,
|
||||
pid_i text NOT NULL,
|
||||
node_t text NOT NULL,
|
||||
node_i text NOT NULL,
|
||||
jid_i text NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 USING btree (sid);
|
||||
CREATE INDEX i_proxy65_jid ON proxy65 USING btree (jid_i);
|
||||
|
||||
+101
-58
@@ -5,7 +5,7 @@
|
||||
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -25,26 +25,32 @@
|
||||
|
||||
-module(acl).
|
||||
|
||||
-behaviour(gen_server).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([add_access/3, clear/0]).
|
||||
-export([start/0, add/3, add_list/3, add_local/3, add_list_local/3,
|
||||
-export([start_link/0, add/3, add_list/3, add_local/3, add_list_local/3,
|
||||
load_from_config/0, match_rule/3, any_rules_allowed/3,
|
||||
transform_options/1, opt_type/1, acl_rule_matches/3,
|
||||
acl_rule_verify/1, access_matches/3,
|
||||
transform_access_rules_config/1,
|
||||
parse_ip_netmask/1,
|
||||
access_rules_validator/1, shaper_rules_validator/1]).
|
||||
access_rules_validator/1, shaper_rules_validator/1,
|
||||
normalize_spec/1, resolve_access/2]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("jid.hrl").
|
||||
|
||||
-record(acl, {aclname, aclspec}).
|
||||
-record(access, {name :: aclname(),
|
||||
rules = [] :: [access_rule()]}).
|
||||
-record(state, {}).
|
||||
|
||||
-type regexp() :: binary().
|
||||
-type iprange() :: {inet:ip_address(), integer()} | binary().
|
||||
@@ -74,26 +80,38 @@
|
||||
|
||||
-export_type([acl/0]).
|
||||
|
||||
start() ->
|
||||
case catch mnesia:table_info(acl, storage_type) of
|
||||
disc_copies ->
|
||||
mnesia:delete_table(acl);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
mnesia:create_table(acl,
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
ejabberd_mnesia:create(?MODULE, acl,
|
||||
[{ram_copies, [node()]}, {type, bag},
|
||||
{local_content, true},
|
||||
{attributes, record_info(fields, acl)}]),
|
||||
mnesia:create_table(access,
|
||||
ejabberd_mnesia:create(?MODULE, access,
|
||||
[{ram_copies, [node()]},
|
||||
{local_content, true},
|
||||
{attributes, record_info(fields, access)}]),
|
||||
mnesia:add_table_copy(acl, node(), ram_copies),
|
||||
mnesia:add_table_copy(access, node(), ram_copies),
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, load_from_config, 20),
|
||||
load_from_config(),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
-spec add(binary(), aclname(), aclspec()) -> ok | {error, any()}.
|
||||
|
||||
add(Host, ACLName, ACLSpec) ->
|
||||
@@ -181,13 +199,13 @@ load_from_config() ->
|
||||
lists:foreach(
|
||||
fun(Host) ->
|
||||
ACLs = ejabberd_config:get_option(
|
||||
{acl, Host}, fun(V) -> V end, []),
|
||||
{acl, Host}, []),
|
||||
AccessRules = ejabberd_config:get_option(
|
||||
{access, Host}, fun(V) -> V end, []),
|
||||
{access, Host}, []),
|
||||
AccessRulesNew = ejabberd_config:get_option(
|
||||
{access_rules, Host}, fun(V) -> V end, []),
|
||||
{access_rules, Host}, []),
|
||||
ShaperRules = ejabberd_config:get_option(
|
||||
{shaper_rules, Host}, fun(V) -> V end, []),
|
||||
{shaper_rules, Host}, []),
|
||||
lists:foreach(
|
||||
fun({ACLName, SpecList}) ->
|
||||
lists:foreach(
|
||||
@@ -248,24 +266,42 @@ normalize_spec(Spec) ->
|
||||
case Spec of
|
||||
all -> all;
|
||||
none -> none;
|
||||
{acl, N} -> {acl, N};
|
||||
{user, {U, S}} -> {user, {nodeprep(U), nameprep(S)}};
|
||||
{user, U} -> {user, split_user_server(U, fun nodeprep/1, fun nameprep/1)};
|
||||
{shared_group, {G, H}} -> {shared_group, {b(G), nameprep(H)}};
|
||||
{shared_group, G} -> {shared_group, split_user_server(G, fun b/1, fun nameprep/1)};
|
||||
{user_regexp, {UR, S}} -> {user_regexp, {b(UR), nameprep(S)}};
|
||||
{user_regexp, UR} -> {user_regexp, split_user_server(UR, fun b/1, fun nameprep/1)};
|
||||
{node_regexp, {UR, SR}} -> {node_regexp, {b(UR), b(SR)}};
|
||||
{user_glob, {UR, S}} -> {user_glob, {b(UR), nameprep(S)}};
|
||||
{user_glob, UR} -> {user_glob, split_user_server(UR, fun b/1, fun nameprep/1)};
|
||||
{node_glob, {UR, SR}} -> {node_glob, {b(UR), b(SR)}};
|
||||
{server, S} -> {server, nameprep(S)};
|
||||
{resource, R} -> {resource, resourceprep(R)};
|
||||
{server_regexp, SR} -> {server_regexp, b(SR)};
|
||||
{resource_regexp, R} -> {resource_regexp, b(R)};
|
||||
{server_glob, S} -> {server_glob, b(S)};
|
||||
{resource_glob, R} -> {resource_glob, b(R)};
|
||||
{ip, {Net, Mask}} -> {ip, {Net, Mask}};
|
||||
{acl, N} when is_atom(N) ->
|
||||
{acl, N};
|
||||
{user, {U, S}} when is_binary(U), is_binary(S) ->
|
||||
{user, {nodeprep(U), nameprep(S)}};
|
||||
{user, U} when is_binary(U) ->
|
||||
{user, split_user_server(U, fun nodeprep/1, fun nameprep/1)};
|
||||
{shared_group, {G, H}} when is_binary(G), is_binary(H) ->
|
||||
{shared_group, {b(G), nameprep(H)}};
|
||||
{shared_group, G} when is_binary(G) ->
|
||||
{shared_group, split_user_server(G, fun b/1, fun nameprep/1)};
|
||||
{user_regexp, {UR, S}} when is_binary(UR), is_binary(S) ->
|
||||
{user_regexp, {b(UR), nameprep(S)}};
|
||||
{user_regexp, UR} when is_binary(UR) ->
|
||||
{user_regexp, split_user_server(UR, fun b/1, fun nameprep/1)};
|
||||
{node_regexp, {UR, SR}} when is_binary(UR), is_binary(SR) ->
|
||||
{node_regexp, {b(UR), b(SR)}};
|
||||
{user_glob, {UR, S}} when is_binary(UR), is_binary(S) ->
|
||||
{user_glob, {b(UR), nameprep(S)}};
|
||||
{user_glob, UR} when is_binary(UR) ->
|
||||
{user_glob, split_user_server(UR, fun b/1, fun nameprep/1)};
|
||||
{node_glob, {UR, SR}} when is_binary(UR), is_binary(SR) ->
|
||||
{node_glob, {b(UR), b(SR)}};
|
||||
{server, S} when is_binary(S) ->
|
||||
{server, nameprep(S)};
|
||||
{resource, R} when is_binary(R) ->
|
||||
{resource, resourceprep(R)};
|
||||
{server_regexp, SR} when is_binary(SR) ->
|
||||
{server_regexp, b(SR)};
|
||||
{resource_regexp, R} when is_binary(R) ->
|
||||
{resource_regexp, b(R)};
|
||||
{server_glob, S} when is_binary(S) ->
|
||||
{server_glob, b(S)};
|
||||
{resource_glob, R} when is_binary(R) ->
|
||||
{resource_glob, b(R)};
|
||||
{ip, {Net, Mask}} when is_binary(Net), is_integer(Mask) ->
|
||||
{ip, {Net, Mask}};
|
||||
{ip, S} ->
|
||||
case parse_ip_netmask(b(S)) of
|
||||
{ok, Net, Mask} ->
|
||||
@@ -273,10 +309,12 @@ normalize_spec(Spec) ->
|
||||
error ->
|
||||
?INFO_MSG("Invalid network address: ~p", [S]),
|
||||
none
|
||||
end
|
||||
end;
|
||||
BadVal ->
|
||||
throw({<<"Invalid acl value">>, BadVal})
|
||||
end.
|
||||
|
||||
-spec any_rules_allowed(global | binary(), access_name(),
|
||||
-spec any_rules_allowed(global | binary(), [access_name()],
|
||||
jid() | ljid() | inet:ip_address()) -> boolean().
|
||||
|
||||
any_rules_allowed(Host, Access, Entity) ->
|
||||
@@ -347,7 +385,7 @@ acl_rule_verify({node_glob, {UR, SR}}) when is_binary(UR), is_binary(SR) ->
|
||||
acl_rule_verify(_Spec) ->
|
||||
false.
|
||||
invalid_syntax(Msg, Data) ->
|
||||
throw({invalid_syntax, iolist_to_binary(io_lib:format(Msg, Data))}).
|
||||
throw({invalid_syntax, (str:format(Msg, Data))}).
|
||||
|
||||
acl_rules_verify([{acl, Name} | Rest], true) when is_atom(Name) ->
|
||||
acl_rules_verify(Rest, true);
|
||||
@@ -443,30 +481,35 @@ acl_rule_matches({node_glob, {UR, SR}}, #{usr := {U, S, _}}, _Host) ->
|
||||
acl_rule_matches(_ACL, _Data, _Host) ->
|
||||
false.
|
||||
|
||||
-spec access_matches(atom()|list(), any(), global|binary()) -> any().
|
||||
access_matches(all, _Data, _Host) ->
|
||||
allow;
|
||||
access_matches(none, _Data, _Host) ->
|
||||
deny;
|
||||
access_matches(Name, Data, Host) when is_atom(Name) ->
|
||||
GAccess = ets:lookup(access, {Name, global}),
|
||||
resolve_access(all, _Host) ->
|
||||
all;
|
||||
resolve_access(none, _Host) ->
|
||||
none;
|
||||
resolve_access(Name, Host) when is_atom(Name) ->
|
||||
GAccess = mnesia:dirty_read(access, {Name, global}),
|
||||
LAccess =
|
||||
if Host /= global -> ets:lookup(access, {Name, Host});
|
||||
if Host /= global -> mnesia:dirty_read(access, {Name, Host});
|
||||
true -> []
|
||||
end,
|
||||
case GAccess ++ LAccess of
|
||||
[] ->
|
||||
deny;
|
||||
[];
|
||||
AccessList ->
|
||||
Rules = lists:flatmap(
|
||||
lists:flatmap(
|
||||
fun(#access{rules = Rs}) ->
|
||||
Rs
|
||||
end, AccessList),
|
||||
access_rules_matches(Rules, Data, Host)
|
||||
end, AccessList)
|
||||
end;
|
||||
access_matches(Rules, Data, Host) when is_list(Rules) ->
|
||||
access_rules_matches(Rules, Data, Host).
|
||||
resolve_access(Rules, _Host) when is_list(Rules) ->
|
||||
Rules.
|
||||
|
||||
-spec access_matches(atom()|list(), any(), global|binary()) -> allow|deny|atom()|integer().
|
||||
access_matches(Rules, Data, Host) ->
|
||||
case resolve_access(Rules, Host) of
|
||||
all -> allow;
|
||||
none -> deny;
|
||||
RRules -> access_rules_matches(RRules, Data, Host)
|
||||
end.
|
||||
|
||||
-spec access_rules_matches(list(), any(), global|binary()) -> any().
|
||||
|
||||
@@ -484,7 +527,7 @@ access_rules_matches([], _Data, _Host, Default) ->
|
||||
Default.
|
||||
|
||||
get_aclspecs(ACL, Host) ->
|
||||
ets:lookup(acl, {ACL, Host}) ++ ets:lookup(acl, {ACL, global}).
|
||||
mnesia:dirty_read(acl, {ACL, Host}) ++ mnesia:dirty_read(acl, {ACL, global}).
|
||||
|
||||
is_regexp_match(String, RegExp) ->
|
||||
case ejabberd_regexp:run(String, RegExp) of
|
||||
@@ -542,7 +585,7 @@ parse_ip_netmask(S) ->
|
||||
_ -> error
|
||||
end;
|
||||
[IPStr, MaskStr] ->
|
||||
case catch jlib:binary_to_integer(MaskStr) of
|
||||
case catch binary_to_integer(MaskStr) of
|
||||
Mask when is_integer(Mask), Mask >= 0 ->
|
||||
case inet_parse:address(binary_to_list(IPStr)) of
|
||||
{ok, {_, _, _, _} = IP} when Mask =< 32 ->
|
||||
@@ -582,7 +625,7 @@ access_rules_validator(Rules0) ->
|
||||
(deny) -> true;
|
||||
(_) -> false
|
||||
end),
|
||||
throw({replace_with, Rules}).
|
||||
Rules.
|
||||
|
||||
|
||||
shaper_rules_validator(Name) when is_atom(Name) ->
|
||||
@@ -593,7 +636,7 @@ shaper_rules_validator(Rules0) ->
|
||||
(V2) when is_integer(V2) -> true;
|
||||
(_) -> false
|
||||
end),
|
||||
throw({replace_with, Rules}).
|
||||
Rules.
|
||||
|
||||
access_shaper_rules_validator([{Type, Acls} = Rule | Rest], RuleTypeCheck) ->
|
||||
case RuleTypeCheck(Type) of
|
||||
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : adhoc.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : Provide helper functions for ad-hoc commands (XEP-0050)
|
||||
%%% Created : 31 Oct 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(adhoc).
|
||||
|
||||
-author('henoch@dtek.chalmers.se').
|
||||
|
||||
-export([
|
||||
parse_request/1,
|
||||
produce_response/2,
|
||||
produce_response/1
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("adhoc.hrl").
|
||||
|
||||
%% Parse an ad-hoc request. Return either an adhoc_request record or
|
||||
%% an {error, ErrorType} tuple.
|
||||
%%
|
||||
-spec parse_request(IQ :: iq_request()) -> adhoc_response() | {error, _}.
|
||||
|
||||
parse_request(#iq{type = set, lang = Lang, sub_el = SubEl, xmlns = ?NS_COMMANDS}) ->
|
||||
?DEBUG("entering parse_request...", []),
|
||||
Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
|
||||
SessionID = fxml:get_tag_attr_s(<<"sessionid">>, SubEl),
|
||||
Action = fxml:get_tag_attr_s(<<"action">>, SubEl),
|
||||
XData = find_xdata_el(SubEl),
|
||||
#xmlel{children = AllEls} = SubEl,
|
||||
Others = case XData of
|
||||
false -> AllEls;
|
||||
_ -> lists:delete(XData, AllEls)
|
||||
end,
|
||||
#adhoc_request{
|
||||
lang = Lang,
|
||||
node = Node,
|
||||
sessionid = SessionID,
|
||||
action = Action,
|
||||
xdata = XData,
|
||||
others = Others
|
||||
};
|
||||
parse_request(#iq{lang = Lang}) ->
|
||||
Text = <<"Failed to parse ad-hoc command request">>,
|
||||
{error, ?ERRT_BAD_REQUEST(Lang, Text)}.
|
||||
|
||||
%% Borrowed from mod_vcard.erl
|
||||
find_xdata_el(#xmlel{children = SubEls}) ->
|
||||
find_xdata_el1(SubEls).
|
||||
|
||||
find_xdata_el1([]) -> false;
|
||||
find_xdata_el1([El | Els]) when is_record(El, xmlel) ->
|
||||
case fxml:get_tag_attr_s(<<"xmlns">>, El) of
|
||||
?NS_XDATA -> El;
|
||||
_ -> find_xdata_el1(Els)
|
||||
end;
|
||||
find_xdata_el1([_ | Els]) -> find_xdata_el1(Els).
|
||||
|
||||
%% Produce a <command/> node to use as response from an adhoc_response
|
||||
%% record, filling in values for language, node and session id from
|
||||
%% the request.
|
||||
%%
|
||||
-spec produce_response(Adhoc_Request :: adhoc_request(),
|
||||
Adhoc_Response :: adhoc_response()) ->
|
||||
Xmlel::xmlel().
|
||||
|
||||
%% Produce a <command/> node to use as response from an adhoc_response
|
||||
%% record.
|
||||
produce_response(#adhoc_request{lang = Lang, node = Node, sessionid = SessionID},
|
||||
Adhoc_Response) ->
|
||||
produce_response(Adhoc_Response#adhoc_response{
|
||||
lang = Lang, node = Node, sessionid = SessionID
|
||||
}).
|
||||
|
||||
%%
|
||||
-spec produce_response(Adhoc_Response::adhoc_response()) -> Xmlel::xmlel().
|
||||
|
||||
produce_response(
|
||||
#adhoc_response{
|
||||
%lang = _Lang,
|
||||
node = Node,
|
||||
sessionid = ProvidedSessionID,
|
||||
status = Status,
|
||||
defaultaction = DefaultAction,
|
||||
actions = Actions,
|
||||
notes = Notes,
|
||||
elements = Elements
|
||||
}) ->
|
||||
SessionID = if is_binary(ProvidedSessionID),
|
||||
ProvidedSessionID /= <<"">> -> ProvidedSessionID;
|
||||
true -> jlib:now_to_utc_string(p1_time_compat:timestamp())
|
||||
end,
|
||||
case Actions of
|
||||
[] ->
|
||||
ActionsEls = [];
|
||||
_ ->
|
||||
case DefaultAction of
|
||||
<<"">> -> ActionsElAttrs = [];
|
||||
_ -> ActionsElAttrs = [{<<"execute">>, DefaultAction}]
|
||||
end,
|
||||
ActionsEls = [
|
||||
#xmlel{
|
||||
name = <<"actions">>,
|
||||
attrs = ActionsElAttrs,
|
||||
children = [
|
||||
#xmlel{name = Action, attrs = [], children = []}
|
||||
|| Action <- Actions]
|
||||
}
|
||||
]
|
||||
end,
|
||||
NotesEls = lists:map(fun({Type, Text}) ->
|
||||
#xmlel{
|
||||
name = <<"note">>,
|
||||
attrs = [{<<"type">>, Type}],
|
||||
children = [{xmlcdata, Text}]
|
||||
}
|
||||
end, Notes),
|
||||
#xmlel{
|
||||
name = <<"command">>,
|
||||
attrs = [
|
||||
{<<"xmlns">>, ?NS_COMMANDS},
|
||||
{<<"sessionid">>, SessionID},
|
||||
{<<"node">>, Node},
|
||||
{<<"status">>, iolist_to_binary(atom_to_list(Status))}
|
||||
],
|
||||
children = ActionsEls ++ NotesEls ++ Elements
|
||||
}.
|
||||
+91
-93
@@ -5,7 +5,7 @@
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -25,23 +25,20 @@
|
||||
|
||||
-module(cyrsasl).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start/0, register_mechanism/3, listmech/1,
|
||||
-export([start_link/0, register_mechanism/3, listmech/1,
|
||||
server_new/7, server_start/3, server_step/2,
|
||||
opt_type/1]).
|
||||
get_mech/1, format_error/2]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
%%
|
||||
-export_type([
|
||||
mechanism/0,
|
||||
mechanisms/0,
|
||||
sasl_mechanism/0
|
||||
]).
|
||||
-record(state, {}).
|
||||
|
||||
-record(sasl_mechanism,
|
||||
{mechanism = <<"">> :: mechanism() | '$1',
|
||||
@@ -51,12 +48,22 @@
|
||||
-type(mechanism() :: binary()).
|
||||
-type(mechanisms() :: [mechanism(),...]).
|
||||
-type(password_type() :: plain | digest | scram).
|
||||
-type(props() :: [{username, binary()} |
|
||||
{authzid, binary()} |
|
||||
{auth_module, atom()}]).
|
||||
-type sasl_property() :: {username, binary()} |
|
||||
{authzid, binary()} |
|
||||
{mechanism, binary()} |
|
||||
{auth_module, atom()}.
|
||||
-type sasl_return() :: {ok, [sasl_property()]} |
|
||||
{ok, [sasl_property()], binary()} |
|
||||
{continue, binary(), sasl_state()} |
|
||||
{error, atom(), binary()}.
|
||||
|
||||
-type(sasl_mechanism() :: #sasl_mechanism{}).
|
||||
|
||||
-type error_reason() :: cyrsasl_digest:error_reason() |
|
||||
cyrsasl_oauth:error_reason() |
|
||||
cyrsasl_plain:error_reason() |
|
||||
cyrsasl_scram:error_reason() |
|
||||
unsupported_mechanism | nodeprep_failed |
|
||||
empty_username | aborted.
|
||||
-record(sasl_state,
|
||||
{
|
||||
service,
|
||||
@@ -65,18 +72,23 @@
|
||||
get_password,
|
||||
check_password,
|
||||
check_password_digest,
|
||||
mech_name = <<"">>,
|
||||
mech_mod,
|
||||
mech_state
|
||||
}).
|
||||
-type sasl_state() :: #sasl_state{}.
|
||||
-export_type([mechanism/0, mechanisms/0, sasl_mechanism/0, error_reason/0,
|
||||
sasl_state/0, sasl_return/0, sasl_property/0]).
|
||||
|
||||
-callback start(list()) -> any().
|
||||
-callback stop() -> any().
|
||||
-callback mech_new(binary(), fun(), fun(), fun()) -> any().
|
||||
-callback mech_step(any(), binary()) -> {ok, props()} |
|
||||
{ok, props(), binary()} |
|
||||
{continue, binary(), any()} |
|
||||
{error, binary()} |
|
||||
{error, binary(), binary()}.
|
||||
-callback mech_step(any(), binary()) -> sasl_return().
|
||||
|
||||
start() ->
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
ets:new(sasl_mechanism,
|
||||
[named_table, public,
|
||||
{keypos, #sasl_mechanism.mechanism}]),
|
||||
@@ -85,59 +97,67 @@ start() ->
|
||||
cyrsasl_scram:start([]),
|
||||
cyrsasl_anonymous:start([]),
|
||||
cyrsasl_oauth:start([]),
|
||||
ok.
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
cyrsasl_plain:stop(),
|
||||
cyrsasl_digest:stop(),
|
||||
cyrsasl_scram:stop(),
|
||||
cyrsasl_anonymous:stop(),
|
||||
cyrsasl_oauth:stop().
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
-spec format_error(mechanism() | sasl_state(), error_reason()) -> {atom(), binary()}.
|
||||
format_error(_, unsupported_mechanism) ->
|
||||
{'invalid-mechanism', <<"Unsupported mechanism">>};
|
||||
format_error(_, nodeprep_failed) ->
|
||||
{'bad-protocol', <<"Nodeprep failed">>};
|
||||
format_error(_, empty_username) ->
|
||||
{'bad-protocol', <<"Empty username">>};
|
||||
format_error(_, aborted) ->
|
||||
{'aborted', <<"Aborted">>};
|
||||
format_error(#sasl_state{mech_mod = Mod}, Reason) ->
|
||||
Mod:format_error(Reason);
|
||||
format_error(Mech, Reason) ->
|
||||
case ets:lookup(sasl_mechanism, Mech) of
|
||||
[#sasl_mechanism{module = Mod}] ->
|
||||
Mod:format_error(Reason);
|
||||
[] ->
|
||||
{'invalid-mechanism', <<"Unsupported mechanism">>}
|
||||
end.
|
||||
|
||||
%%
|
||||
-spec register_mechanism(Mechanim :: mechanism(), Module :: module(),
|
||||
PasswordType :: password_type()) -> any().
|
||||
|
||||
register_mechanism(Mechanism, Module, PasswordType) ->
|
||||
case is_disabled(Mechanism) of
|
||||
false ->
|
||||
ets:insert(sasl_mechanism,
|
||||
#sasl_mechanism{mechanism = Mechanism, module = Module,
|
||||
password_type = PasswordType});
|
||||
true ->
|
||||
?DEBUG("SASL mechanism ~p is disabled", [Mechanism]),
|
||||
true
|
||||
end.
|
||||
|
||||
%%% TODO: use callbacks
|
||||
%%-include("ejabberd.hrl").
|
||||
%%-include("jlib.hrl").
|
||||
%%check_authzid(_State, Props) ->
|
||||
%% AuthzId = fxml:get_attr_s(authzid, Props),
|
||||
%% case jid:from_string(AuthzId) of
|
||||
%% error ->
|
||||
%% {error, "invalid-authzid"};
|
||||
%% JID ->
|
||||
%% LUser = jid:nodeprep(fxml:get_attr_s(username, Props)),
|
||||
%% {U, S, R} = jid:tolower(JID),
|
||||
%% case R of
|
||||
%% "" ->
|
||||
%% {error, "invalid-authzid"};
|
||||
%% _ ->
|
||||
%% case {LUser, ?MYNAME} of
|
||||
%% {U, S} ->
|
||||
%% ok;
|
||||
%% _ ->
|
||||
%% {error, "invalid-authzid"}
|
||||
%% end
|
||||
%% end
|
||||
%% end.
|
||||
password_type = PasswordType}).
|
||||
|
||||
check_credentials(_State, Props) ->
|
||||
User = proplists:get_value(authzid, Props, <<>>),
|
||||
case jid:nodeprep(User) of
|
||||
error -> {error, <<"not-authorized">>};
|
||||
<<"">> -> {error, <<"not-authorized">>};
|
||||
error -> {error, nodeprep_failed};
|
||||
<<"">> -> {error, empty_username};
|
||||
_LUser -> ok
|
||||
end.
|
||||
|
||||
-spec listmech(Host ::binary()) -> Mechanisms::mechanisms().
|
||||
|
||||
listmech(Host) ->
|
||||
Mechs = ets:select(sasl_mechanism,
|
||||
ets:select(sasl_mechanism,
|
||||
[{#sasl_mechanism{mechanism = '$1',
|
||||
password_type = '$2', _ = '_'},
|
||||
case catch ejabberd_auth:store_type(Host) of
|
||||
@@ -149,9 +169,10 @@ listmech(Host) ->
|
||||
[];
|
||||
_Else -> []
|
||||
end,
|
||||
['$1']}]),
|
||||
filter_anonymous(Host, Mechs).
|
||||
['$1']}]).
|
||||
|
||||
-spec server_new(binary(), binary(), binary(), term(),
|
||||
fun(), fun(), fun()) -> sasl_state().
|
||||
server_new(Service, ServerFQDN, UserRealm, _SecFlags,
|
||||
GetPassword, CheckPassword, CheckPasswordDigest) ->
|
||||
#sasl_state{service = Service, myname = ServerFQDN,
|
||||
@@ -159,6 +180,7 @@ server_new(Service, ServerFQDN, UserRealm, _SecFlags,
|
||||
check_password = CheckPassword,
|
||||
check_password_digest = CheckPasswordDigest}.
|
||||
|
||||
-spec server_start(sasl_state(), mechanism(), binary()) -> sasl_return().
|
||||
server_start(State, Mech, ClientIn) ->
|
||||
case lists:member(Mech,
|
||||
listmech(State#sasl_state.myname))
|
||||
@@ -172,13 +194,15 @@ server_start(State, Mech, ClientIn) ->
|
||||
State#sasl_state.check_password,
|
||||
State#sasl_state.check_password_digest),
|
||||
server_step(State#sasl_state{mech_mod = Module,
|
||||
mech_name = Mech,
|
||||
mech_state = MechState},
|
||||
ClientIn);
|
||||
_ -> {error, <<"no-mechanism">>}
|
||||
_ -> {error, unsupported_mechanism, <<"">>}
|
||||
end;
|
||||
false -> {error, <<"no-mechanism">>}
|
||||
false -> {error, unsupported_mechanism, <<"">>}
|
||||
end.
|
||||
|
||||
-spec server_step(sasl_state(), binary()) -> sasl_return().
|
||||
server_step(State, ClientIn) ->
|
||||
Module = State#sasl_state.mech_mod,
|
||||
MechState = State#sasl_state.mech_state,
|
||||
@@ -186,47 +210,21 @@ server_step(State, ClientIn) ->
|
||||
{ok, Props} ->
|
||||
case check_credentials(State, Props) of
|
||||
ok -> {ok, Props};
|
||||
{error, Error} -> {error, Error}
|
||||
{error, Error} -> {error, Error, <<"">>}
|
||||
end;
|
||||
{ok, Props, ServerOut} ->
|
||||
case check_credentials(State, Props) of
|
||||
ok -> {ok, Props, ServerOut};
|
||||
{error, Error} -> {error, Error}
|
||||
{error, Error} -> {error, Error, <<"">>}
|
||||
end;
|
||||
{continue, ServerOut, NewMechState} ->
|
||||
{continue, ServerOut, State#sasl_state{mech_state = NewMechState}};
|
||||
{error, Error, Username} ->
|
||||
{error, Error, Username};
|
||||
{error, Error} ->
|
||||
{error, Error}
|
||||
{error, Error, <<"">>}
|
||||
end.
|
||||
|
||||
%% Remove the anonymous mechanism from the list if not enabled for the given
|
||||
%% host
|
||||
%%
|
||||
-spec filter_anonymous(Host :: binary(), Mechs :: mechanisms()) -> mechanisms().
|
||||
|
||||
filter_anonymous(Host, Mechs) ->
|
||||
case ejabberd_auth_anonymous:is_sasl_anonymous_enabled(Host) of
|
||||
true -> Mechs;
|
||||
false -> Mechs -- [<<"ANONYMOUS">>]
|
||||
end.
|
||||
|
||||
-spec is_disabled(Mechanism :: mechanism()) -> boolean().
|
||||
|
||||
is_disabled(Mechanism) ->
|
||||
Disabled = ejabberd_config:get_option(
|
||||
disable_sasl_mechanisms,
|
||||
fun(V) when is_list(V) ->
|
||||
lists:map(fun(M) -> str:to_upper(M) end, V);
|
||||
(V) ->
|
||||
[str:to_upper(V)]
|
||||
end, []),
|
||||
lists:member(Mechanism, Disabled).
|
||||
|
||||
opt_type(disable_sasl_mechanisms) ->
|
||||
fun (V) when is_list(V) ->
|
||||
lists:map(fun (M) -> str:to_upper(M) end, V);
|
||||
(V) -> [str:to_upper(V)]
|
||||
end;
|
||||
opt_type(_) -> [disable_sasl_mechanisms].
|
||||
-spec get_mech(sasl_state()) -> binary().
|
||||
get_mech(#sasl_state{mech_name = Mech}) ->
|
||||
Mech.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
%%% Created : 23 Aug 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -35,18 +35,16 @@
|
||||
-record(state, {server = <<"">> :: binary()}).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"ANONYMOUS">>, ?MODULE, plain),
|
||||
ok.
|
||||
cyrsasl:register_mechanism(<<"ANONYMOUS">>, ?MODULE, plain).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{server = Host}}.
|
||||
|
||||
mech_step(#state{server = Server} = S, ClientIn) ->
|
||||
mech_step(#state{}, _ClientIn) ->
|
||||
User = iolist_to_binary([randoms:get_string(),
|
||||
jlib:integer_to_binary(p1_time_compat:unique_integer([positive]))]),
|
||||
case ejabberd_auth:is_user_exists(User, Server) of
|
||||
true -> mech_step(S, ClientIn);
|
||||
false -> {ok, [{username, User}, {authzid, User}, {auth_module, ejabberd_auth_anonymous}]}
|
||||
end.
|
||||
integer_to_binary(p1_time_compat:unique_integer([positive]))]),
|
||||
{ok, [{username, User},
|
||||
{authzid, User},
|
||||
{auth_module, ejabberd_auth_anonymous}]}.
|
||||
|
||||
+44
-40
@@ -5,7 +5,7 @@
|
||||
%%% Created : 11 Mar 2003 by Alexey Shchepin <alexey@sevcom.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -30,7 +30,7 @@
|
||||
-author('alexey@sevcom.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2,
|
||||
parse/1, opt_type/1]).
|
||||
parse/1, format_error/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -39,31 +39,43 @@
|
||||
|
||||
-type get_password_fun() :: fun((binary()) -> {false, any()} |
|
||||
{binary(), atom()}).
|
||||
|
||||
-type check_password_fun() :: fun((binary(), binary(), binary(),
|
||||
-type check_password_fun() :: fun((binary(), binary(), binary(), binary(),
|
||||
fun((binary()) -> binary())) ->
|
||||
{boolean(), any()} |
|
||||
false).
|
||||
-type error_reason() :: parser_failed | invalid_digest_uri |
|
||||
not_authorized | unexpected_response.
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
-record(state, {step = 1 :: 1 | 3 | 5,
|
||||
nonce = <<"">> :: binary(),
|
||||
username = <<"">> :: binary(),
|
||||
authzid = <<"">> :: binary(),
|
||||
get_password = fun(_) -> {false, <<>>} end :: get_password_fun(),
|
||||
check_password = fun(_, _, _, _, _) -> false end :: check_password_fun(),
|
||||
get_password :: get_password_fun(),
|
||||
check_password :: check_password_fun(),
|
||||
auth_module :: atom(),
|
||||
host = <<"">> :: binary(),
|
||||
hostfqdn = <<"">> :: binary() | [binary()]}).
|
||||
hostfqdn = [] :: [binary()]}).
|
||||
|
||||
start(_Opts) ->
|
||||
Fqdn = get_local_fqdn(),
|
||||
?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~p",
|
||||
?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
|
||||
[Fqdn]),
|
||||
cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE,
|
||||
digest).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(invalid_digest_uri) ->
|
||||
{'bad-protocol', <<"Invalid digest URI">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid username or password">>};
|
||||
format_error(unexpected_response) ->
|
||||
{'bad-protocol', <<"Unexpected response">>}.
|
||||
|
||||
mech_new(Host, GetPassword, _CheckPassword,
|
||||
CheckPasswordDigest) ->
|
||||
{ok,
|
||||
@@ -80,8 +92,8 @@ mech_step(#state{step = 1, nonce = Nonce} = State, _) ->
|
||||
mech_step(#state{step = 3, nonce = Nonce} = State,
|
||||
ClientIn) ->
|
||||
case parse(ClientIn) of
|
||||
bad -> {error, <<"bad-protocol">>};
|
||||
KeyVals ->
|
||||
bad -> {error, parser_failed};
|
||||
KeyVals ->
|
||||
DigestURI = proplists:get_value(<<"digest-uri">>, KeyVals, <<>>),
|
||||
UserName = proplists:get_value(<<"username">>, KeyVals, <<>>),
|
||||
case is_digesturi_valid(DigestURI, State#state.host,
|
||||
@@ -92,14 +104,14 @@ mech_step(#state{step = 3, nonce = Nonce} = State,
|
||||
"seems invalid: ~p (checking for Host "
|
||||
"~p, FQDN ~p)",
|
||||
[DigestURI, State#state.host, State#state.hostfqdn]),
|
||||
{error, <<"not-authorized">>, UserName};
|
||||
{error, invalid_digest_uri, UserName};
|
||||
true ->
|
||||
AuthzId = proplists:get_value(<<"authzid">>, KeyVals, <<>>),
|
||||
case (State#state.get_password)(UserName) of
|
||||
{false, _} -> {error, <<"not-authorized">>, UserName};
|
||||
{false, _} -> {error, not_authorized, UserName};
|
||||
{Passwd, AuthModule} ->
|
||||
case (State#state.check_password)(UserName, UserName, <<"">>,
|
||||
proplists:get_value(<<"response">>, KeyVals, <<>>),
|
||||
proplists:get_value(<<"response">>, KeyVals, <<>>),
|
||||
fun (PW) ->
|
||||
response(KeyVals,
|
||||
UserName,
|
||||
@@ -116,8 +128,8 @@ mech_step(#state{step = 3, nonce = Nonce} = State,
|
||||
State#state{step = 5, auth_module = AuthModule,
|
||||
username = UserName,
|
||||
authzid = AuthzId}};
|
||||
false -> {error, <<"not-authorized">>, UserName};
|
||||
{false, _} -> {error, <<"not-authorized">>, UserName}
|
||||
false -> {error, not_authorized, UserName};
|
||||
{false, _} -> {error, not_authorized, UserName}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -134,7 +146,7 @@ mech_step(#state{step = 5, auth_module = AuthModule,
|
||||
{auth_module, AuthModule}]};
|
||||
mech_step(A, B) ->
|
||||
?DEBUG("SASL DIGEST: A ~p B ~p", [A, B]),
|
||||
{error, <<"bad-protocol">>}.
|
||||
{error, unexpected_response}.
|
||||
|
||||
parse(S) -> parse1(binary_to_list(S), "", []).
|
||||
|
||||
@@ -192,8 +204,6 @@ is_digesturi_valid(DigestURICase, JabberDomain,
|
||||
false
|
||||
end.
|
||||
|
||||
is_host_fqdn(Host, Fqdn) when is_binary(Fqdn) ->
|
||||
Host == Fqdn;
|
||||
is_host_fqdn(_Host, []) ->
|
||||
false;
|
||||
is_host_fqdn(Host, [Fqdn | _FqdnTail]) when Host == Fqdn ->
|
||||
@@ -202,30 +212,17 @@ is_host_fqdn(Host, [Fqdn | FqdnTail]) when Host /= Fqdn ->
|
||||
is_host_fqdn(Host, FqdnTail).
|
||||
|
||||
get_local_fqdn() ->
|
||||
case catch get_local_fqdn2() of
|
||||
Str when is_binary(Str) -> Str;
|
||||
List when is_list(List) -> List;
|
||||
_ ->
|
||||
<<"unknown-fqdn, please configure fqdn "
|
||||
"option in ejabberd.yml!">>
|
||||
end.
|
||||
|
||||
get_local_fqdn2() ->
|
||||
case ejabberd_config:get_option(
|
||||
fqdn, fun(X) -> X end) of
|
||||
ConfiguredFqdn when is_binary(ConfiguredFqdn) ->
|
||||
ConfiguredFqdn;
|
||||
[A | _] = ConfiguredFqdns when is_binary(A) ->
|
||||
ConfiguredFqdns;
|
||||
undefined ->
|
||||
{ok, Hostname} = inet:gethostname(),
|
||||
{ok, {hostent, Fqdn, _, _, _, _}} =
|
||||
inet:gethostbyname(Hostname),
|
||||
list_to_binary(Fqdn)
|
||||
case ejabberd_config:get_option(fqdn) of
|
||||
undefined ->
|
||||
{ok, Hostname} = inet:gethostname(),
|
||||
{ok, {hostent, Fqdn, _, _, _, _}} = inet:gethostbyname(Hostname),
|
||||
[list_to_binary(Fqdn)];
|
||||
Fqdn ->
|
||||
Fqdn
|
||||
end.
|
||||
|
||||
hex(S) ->
|
||||
p1_sha:to_hexlist(S).
|
||||
str:to_hexlist(S).
|
||||
|
||||
proplists_get_bin_value(Key, Pairs, Default) ->
|
||||
case proplists:get_value(Key, Pairs, Default) of
|
||||
@@ -263,5 +260,12 @@ response(KeyVals, User, Passwd, Nonce, AuthzId,
|
||||
":", (hex((erlang:md5(A2))))/binary>>,
|
||||
hex((erlang:md5(T))).
|
||||
|
||||
opt_type(fqdn) -> fun iolist_to_binary/1;
|
||||
-spec opt_type(fqdn) -> fun((binary() | [binary()]) -> [binary()]);
|
||||
(atom()) -> [atom()].
|
||||
opt_type(fqdn) ->
|
||||
fun(FQDN) when is_binary(FQDN) ->
|
||||
[FQDN];
|
||||
(FQDNs) when is_list(FQDNs) ->
|
||||
[iolist_to_binary(FQDN) || FQDN <- FQDNs]
|
||||
end;
|
||||
opt_type(_) -> [fqdn].
|
||||
|
||||
+23
-10
@@ -5,7 +5,7 @@
|
||||
%%% Created : 17 Sep 2015 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -27,18 +27,25 @@
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]).
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1, format_error/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {host}).
|
||||
-type error_reason() :: parser_failed | not_authorized.
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"X-OAUTH2">>, ?MODULE, plain),
|
||||
ok.
|
||||
cyrsasl:register_mechanism(<<"X-OAUTH2">>, ?MODULE, plain).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid token">>}.
|
||||
|
||||
mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{host = Host}}.
|
||||
|
||||
@@ -46,15 +53,15 @@ mech_step(State, ClientIn) ->
|
||||
case prepare(ClientIn) of
|
||||
[AuthzId, User, Token] ->
|
||||
case ejabberd_oauth:check_token(
|
||||
User, State#state.host, <<"sasl_auth">>, Token) of
|
||||
User, State#state.host, [<<"sasl_auth">>], Token) of
|
||||
true ->
|
||||
{ok,
|
||||
[{username, User}, {authzid, AuthzId},
|
||||
{auth_module, ejabberd_oauth}]};
|
||||
_ ->
|
||||
{error, <<"not-authorized">>, User}
|
||||
{error, not_authorized, User}
|
||||
end;
|
||||
_ -> {error, <<"bad-protocol">>}
|
||||
_ -> {error, parser_failed}
|
||||
end.
|
||||
|
||||
prepare(ClientIn) ->
|
||||
@@ -62,12 +69,18 @@ prepare(ClientIn) ->
|
||||
[<<"">>, UserMaybeDomain, Token] ->
|
||||
case parse_domain(UserMaybeDomain) of
|
||||
%% <NUL>login@domain<NUL>pwd
|
||||
[User, _Domain] -> [UserMaybeDomain, User, Token];
|
||||
[User, _Domain] -> [User, User, Token];
|
||||
%% <NUL>login<NUL>pwd
|
||||
[User] -> [<<"">>, User, Token]
|
||||
[User] -> [User, User, Token]
|
||||
end;
|
||||
%% login@domain<NUL>login<NUL>pwd
|
||||
[AuthzId, User, Token] -> [AuthzId, User, Token];
|
||||
[AuthzId, User, Token] ->
|
||||
case parse_domain(AuthzId) of
|
||||
%% login@domain<NUL>login<NUL>pwd
|
||||
[AuthzUser, _Domain] -> [AuthzUser, User, Token];
|
||||
%% login<NUL>login<NUL>pwd
|
||||
[AuthzUser] -> [AuthzUser, User, Token]
|
||||
end;
|
||||
_ -> error
|
||||
end.
|
||||
|
||||
|
||||
+13
-6
@@ -5,7 +5,7 @@
|
||||
%%% Created : 8 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -27,18 +27,25 @@
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]).
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, parse/1, format_error/1]).
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state, {check_password}).
|
||||
-type error_reason() :: parser_failed | not_authorized.
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"PLAIN">>, ?MODULE, plain),
|
||||
ok.
|
||||
cyrsasl:register_mechanism(<<"PLAIN">>, ?MODULE, plain).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid username or password">>}.
|
||||
|
||||
mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
|
||||
{ok, #state{check_password = CheckPassword}}.
|
||||
|
||||
@@ -50,9 +57,9 @@ mech_step(State, ClientIn) ->
|
||||
{ok,
|
||||
[{username, User}, {authzid, AuthzId},
|
||||
{auth_module, AuthModule}]};
|
||||
_ -> {error, <<"not-authorized">>, User}
|
||||
_ -> {error, not_authorized, User}
|
||||
end;
|
||||
_ -> {error, <<"bad-protocol">>}
|
||||
_ -> {error, parser_failed}
|
||||
end.
|
||||
|
||||
prepare(ClientIn) ->
|
||||
|
||||
+76
-48
@@ -5,7 +5,7 @@
|
||||
%%% Created : 7 Aug 2011 by Stephen Röttger <stephen.roettger@googlemail.com>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -29,13 +29,11 @@
|
||||
|
||||
-protocol({rfc, 5802}).
|
||||
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2]).
|
||||
-export([start/1, stop/0, mech_new/4, mech_step/2, format_error/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("jlib.hrl").
|
||||
|
||||
-behaviour(cyrsasl).
|
||||
|
||||
-record(state,
|
||||
@@ -43,22 +41,47 @@
|
||||
stored_key = <<"">> :: binary(),
|
||||
server_key = <<"">> :: binary(),
|
||||
username = <<"">> :: binary(),
|
||||
get_password :: fun(),
|
||||
check_password :: fun(),
|
||||
auth_module :: module(),
|
||||
get_password :: fun((binary()) ->
|
||||
{false | ejabberd_auth:password(), module()}),
|
||||
auth_message = <<"">> :: binary(),
|
||||
client_nonce = <<"">> :: binary(),
|
||||
server_nonce = <<"">> :: binary()}).
|
||||
|
||||
-define(SALT_LENGTH, 16).
|
||||
|
||||
-define(NONCE_LENGTH, 16).
|
||||
|
||||
-type error_reason() :: unsupported_extension | bad_username |
|
||||
not_authorized | saslprep_failed |
|
||||
parser_failed | bad_attribute |
|
||||
nonce_mismatch | bad_channel_binding.
|
||||
|
||||
-export_type([error_reason/0]).
|
||||
|
||||
start(_Opts) ->
|
||||
cyrsasl:register_mechanism(<<"SCRAM-SHA-1">>, ?MODULE,
|
||||
scram).
|
||||
|
||||
stop() -> ok.
|
||||
|
||||
-spec format_error(error_reason()) -> {atom(), binary()}.
|
||||
format_error(unsupported_extension) ->
|
||||
{'bad-protocol', <<"Unsupported extension">>};
|
||||
format_error(bad_username) ->
|
||||
{'invalid-authzid', <<"Malformed username">>};
|
||||
format_error(not_authorized) ->
|
||||
{'not-authorized', <<"Invalid username or password">>};
|
||||
format_error(saslprep_failed) ->
|
||||
{'not-authorized', <<"SASLprep failed">>};
|
||||
format_error(parser_failed) ->
|
||||
{'bad-protocol', <<"Response decoding failed">>};
|
||||
format_error(bad_attribute) ->
|
||||
{'bad-protocol', <<"Malformed or unexpected attribute">>};
|
||||
format_error(nonce_mismatch) ->
|
||||
{'bad-protocol', <<"Nonce mismatch">>};
|
||||
format_error(bad_channel_binding) ->
|
||||
{'bad-protocol', <<"Invalid channel binding">>}.
|
||||
|
||||
mech_new(_Host, GetPassword, _CheckPassword,
|
||||
_CheckPasswordDigest) ->
|
||||
{ok, #state{step = 2, get_password = GetPassword}}.
|
||||
@@ -66,30 +89,38 @@ mech_new(_Host, GetPassword, _CheckPassword,
|
||||
mech_step(#state{step = 2} = State, ClientIn) ->
|
||||
case re:split(ClientIn, <<",">>, [{return, binary}]) of
|
||||
[_CBind, _AuthorizationIdentity, _UserNameAttribute, _ClientNonceAttribute, ExtensionAttribute | _]
|
||||
when ExtensionAttribute /= [] ->
|
||||
{error, <<"protocol-error-extension-not-supported">>};
|
||||
when ExtensionAttribute /= <<"">> ->
|
||||
{error, unsupported_extension};
|
||||
[CBind, _AuthorizationIdentity, UserNameAttribute, ClientNonceAttribute | _]
|
||||
when (CBind == <<"y">>) or (CBind == <<"n">>) ->
|
||||
case parse_attribute(UserNameAttribute) of
|
||||
{error, Reason} -> {error, Reason};
|
||||
{_, EscapedUserName} ->
|
||||
case unescape_username(EscapedUserName) of
|
||||
error -> {error, <<"protocol-error-bad-username">>};
|
||||
error -> {error, bad_username};
|
||||
UserName ->
|
||||
case parse_attribute(ClientNonceAttribute) of
|
||||
{$r, ClientNonce} ->
|
||||
{Ret, _AuthModule} = (State#state.get_password)(UserName),
|
||||
case {Ret, jid:resourceprep(Ret)} of
|
||||
{false, _} -> {error, <<"not-authorized">>, UserName};
|
||||
{_, error} when is_binary(Ret) -> ?WARNING_MSG("invalid plain password", []), {error, <<"not-authorized">>, UserName};
|
||||
{Ret, _} ->
|
||||
{Pass, AuthModule} = (State#state.get_password)(UserName),
|
||||
LPass = if is_binary(Pass) -> jid:resourceprep(Pass);
|
||||
true -> Pass
|
||||
end,
|
||||
if Pass == false ->
|
||||
{error, not_authorized, UserName};
|
||||
LPass == error ->
|
||||
{error, saslprep_failed, UserName};
|
||||
true ->
|
||||
{StoredKey, ServerKey, Salt, IterationCount} =
|
||||
if is_tuple(Ret) -> Ret;
|
||||
if is_record(Pass, scram) ->
|
||||
{base64:decode(Pass#scram.storedkey),
|
||||
base64:decode(Pass#scram.serverkey),
|
||||
base64:decode(Pass#scram.salt),
|
||||
Pass#scram.iterationcount};
|
||||
true ->
|
||||
TempSalt =
|
||||
crypto:rand_bytes(?SALT_LENGTH),
|
||||
randoms:bytes(?SALT_LENGTH),
|
||||
SaltedPassword =
|
||||
scram:salted_password(Ret,
|
||||
scram:salted_password(Pass,
|
||||
TempSalt,
|
||||
?SCRAM_DEFAULT_ITERATION_COUNT),
|
||||
{scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
@@ -101,19 +132,20 @@ mech_step(#state{step = 2} = State, ClientIn) ->
|
||||
str:substr(ClientIn,
|
||||
str:str(ClientIn, <<"n=">>)),
|
||||
ServerNonce =
|
||||
jlib:encode_base64(crypto:rand_bytes(?NONCE_LENGTH)),
|
||||
base64:encode(randoms:bytes(?NONCE_LENGTH)),
|
||||
ServerFirstMessage =
|
||||
iolist_to_binary(
|
||||
["r=",
|
||||
ClientNonce,
|
||||
ServerNonce,
|
||||
",", "s=",
|
||||
jlib:encode_base64(Salt),
|
||||
base64:encode(Salt),
|
||||
",", "i=",
|
||||
integer_to_list(IterationCount)]),
|
||||
{continue, ServerFirstMessage,
|
||||
State#state{step = 4, stored_key = StoredKey,
|
||||
server_key = ServerKey,
|
||||
auth_module = AuthModule,
|
||||
auth_message =
|
||||
<<ClientFirstMessageBare/binary,
|
||||
",", ServerFirstMessage/binary>>,
|
||||
@@ -121,11 +153,11 @@ mech_step(#state{step = 2} = State, ClientIn) ->
|
||||
server_nonce = ServerNonce,
|
||||
username = UserName}}
|
||||
end;
|
||||
_Else -> {error, <<"not-supported">>}
|
||||
_ -> {error, bad_attribute}
|
||||
end
|
||||
end
|
||||
end;
|
||||
_Else -> {error, <<"bad-protocol">>}
|
||||
_Else -> {error, parser_failed}
|
||||
end;
|
||||
mech_step(#state{step = 4} = State, ClientIn) ->
|
||||
case str:tokens(ClientIn, <<",">>) of
|
||||
@@ -133,7 +165,9 @@ mech_step(#state{step = 4} = State, ClientIn) ->
|
||||
ClientProofAttribute] ->
|
||||
case parse_attribute(GS2ChannelBindingAttribute) of
|
||||
{$c, CVal} ->
|
||||
ChannelBindingSupport = binary:at(jlib:decode_base64(CVal), 0),
|
||||
ChannelBindingSupport = try binary:first(base64:decode(CVal))
|
||||
catch _:badarg -> 0
|
||||
end,
|
||||
if (ChannelBindingSupport == $n)
|
||||
or (ChannelBindingSupport == $y) ->
|
||||
Nonce = <<(State#state.client_nonce)/binary,
|
||||
@@ -142,7 +176,9 @@ mech_step(#state{step = 4} = State, ClientIn) ->
|
||||
{$r, CompareNonce} when CompareNonce == Nonce ->
|
||||
case parse_attribute(ClientProofAttribute) of
|
||||
{$p, ClientProofB64} ->
|
||||
ClientProof = jlib:decode_base64(ClientProofB64),
|
||||
ClientProof = try base64:decode(ClientProofB64)
|
||||
catch _:badarg -> <<>>
|
||||
end,
|
||||
AuthMessage = iolist_to_binary(
|
||||
[State#state.auth_message,
|
||||
",",
|
||||
@@ -160,39 +196,31 @@ mech_step(#state{step = 4} = State, ClientIn) ->
|
||||
scram:server_signature(State#state.server_key,
|
||||
AuthMessage),
|
||||
{ok, [{username, State#state.username},
|
||||
{auth_module, State#state.auth_module},
|
||||
{authzid, State#state.username}],
|
||||
<<"v=",
|
||||
(jlib:encode_base64(ServerSignature))/binary>>};
|
||||
true -> {error, <<"bad-auth">>, State#state.username}
|
||||
(base64:encode(ServerSignature))/binary>>};
|
||||
true -> {error, not_authorized, State#state.username}
|
||||
end;
|
||||
_Else -> {error, <<"bad-protocol">>}
|
||||
_ -> {error, bad_attribute}
|
||||
end;
|
||||
{$r, _} -> {error, <<"bad-nonce">>};
|
||||
_Else -> {error, <<"bad-protocol">>}
|
||||
{$r, _} -> {error, nonce_mismatch};
|
||||
_ -> {error, bad_attribute}
|
||||
end;
|
||||
true -> {error, <<"bad-channel-binding">>}
|
||||
true -> {error, bad_channel_binding}
|
||||
end;
|
||||
_Else -> {error, <<"bad-protocol">>}
|
||||
_ -> {error, bad_attribute}
|
||||
end;
|
||||
_Else -> {error, <<"bad-protocol">>}
|
||||
_ -> {error, parser_failed}
|
||||
end.
|
||||
|
||||
parse_attribute(Attribute) ->
|
||||
AttributeLen = byte_size(Attribute),
|
||||
if AttributeLen >= 3 ->
|
||||
AttributeS = binary_to_list(Attribute),
|
||||
SecondChar = lists:nth(2, AttributeS),
|
||||
case is_alpha(lists:nth(1, AttributeS)) of
|
||||
true ->
|
||||
if SecondChar == $= ->
|
||||
String = str:substr(Attribute, 3),
|
||||
{lists:nth(1, AttributeS), String};
|
||||
true -> {error, <<"bad-format second char not equal sign">>}
|
||||
end;
|
||||
_Else -> {error, <<"bad-format first char not a letter">>}
|
||||
end;
|
||||
true -> {error, <<"bad-format attribute too short">>}
|
||||
end.
|
||||
parse_attribute(<<Name, $=, Val/binary>>) when Val /= <<>> ->
|
||||
case is_alpha(Name) of
|
||||
true -> {Name, Val};
|
||||
false -> {error, bad_attribute}
|
||||
end;
|
||||
parse_attribute(_) ->
|
||||
{error, bad_attribute}.
|
||||
|
||||
unescape_username(<<"">>) -> <<"">>;
|
||||
unescape_username(EscapedUsername) ->
|
||||
|
||||
+2
-4
@@ -5,7 +5,7 @@
|
||||
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -105,8 +105,6 @@ start_app([], _Type, _StartFlag) ->
|
||||
ok.
|
||||
|
||||
check_app_modules(App, StartFlag) ->
|
||||
{A, B, C} = p1_time_compat:timestamp(),
|
||||
random:seed(A, B, C),
|
||||
sleep(5000),
|
||||
case application:get_key(App, modules) of
|
||||
{ok, Mods} ->
|
||||
@@ -140,7 +138,7 @@ exit_or_halt(Reason, StartFlag) ->
|
||||
end.
|
||||
|
||||
sleep(N) ->
|
||||
timer:sleep(random:uniform(N)).
|
||||
timer:sleep(randoms:uniform(N)).
|
||||
|
||||
get_module_file(App, Mod) ->
|
||||
BaseName = atom_to_list(Mod),
|
||||
|
||||
@@ -0,0 +1,546 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% File : ejabberd_access_permissions.erl
|
||||
%%% Author : Paweł Chmielowski <pawel@process-one.net>
|
||||
%%% Purpose : Administrative functions and commands
|
||||
%%% Created : 7 Sep 2016 by Paweł Chmielowski <pawel@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2017 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.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(ejabberd_access_permissions).
|
||||
-author("pawel@process-one.net").
|
||||
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-behaviour(gen_server).
|
||||
-behavior(ejabberd_config).
|
||||
|
||||
%% API
|
||||
-export([start_link/0,
|
||||
parse_api_permissions/1,
|
||||
can_access/2,
|
||||
invalidate/0,
|
||||
opt_type/1,
|
||||
show_current_definitions/0,
|
||||
register_permission_addon/2,
|
||||
unregister_permission_addon/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
terminate/2,
|
||||
code_change/3]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-record(state, {
|
||||
definitions = none,
|
||||
fragments_generators = []
|
||||
}).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
|
||||
-spec can_access(atom(), map()) -> allow | deny.
|
||||
can_access(Cmd, CallerInfo) ->
|
||||
gen_server:call(?MODULE, {can_access, Cmd, CallerInfo}).
|
||||
|
||||
-spec invalidate() -> ok.
|
||||
invalidate() ->
|
||||
gen_server:cast(?MODULE, invalidate).
|
||||
|
||||
-spec register_permission_addon(atom(), fun()) -> ok.
|
||||
register_permission_addon(Name, Fun) ->
|
||||
gen_server:call(?MODULE, {register_config_fragment_generator, Name, Fun}).
|
||||
|
||||
-spec unregister_permission_addon(atom()) -> ok.
|
||||
unregister_permission_addon(Name) ->
|
||||
gen_server:call(?MODULE, {unregister_config_fragment_generator, Name}).
|
||||
|
||||
-spec show_current_definitions() -> any().
|
||||
show_current_definitions() ->
|
||||
gen_server:call(?MODULE, show_current_definitions).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc
|
||||
%% Starts the server
|
||||
%%
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}.
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% Initializes the server
|
||||
%%
|
||||
%% @spec init(Args) -> {ok, State} |
|
||||
%% {ok, State, Timeout} |
|
||||
%% ignore |
|
||||
%% {stop, Reason}
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec init(Args :: term()) ->
|
||||
{ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
|
||||
{stop, Reason :: term()} | ignore.
|
||||
init([]) ->
|
||||
ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% Handling call messages
|
||||
%%
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec handle_call(Request :: term(), From :: {pid(), Tag :: term()},
|
||||
State :: #state{}) ->
|
||||
{reply, Reply :: term(), NewState :: #state{}} |
|
||||
{reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
|
||||
{noreply, NewState :: #state{}} |
|
||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||
{stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
|
||||
{stop, Reason :: term(), NewState :: #state{}}.
|
||||
handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
|
||||
CallerModule = maps:get(caller_module, CallerInfo, none),
|
||||
Host = maps:get(caller_host, CallerInfo, global),
|
||||
{State2, Defs0} = get_definitions(State),
|
||||
Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0,
|
||||
Res = lists:foldl(
|
||||
fun({Name, _} = Def, none) ->
|
||||
case matches_definition(Def, Cmd, CallerModule, Host, CallerInfo) of
|
||||
true ->
|
||||
?DEBUG("Command '~p' execution allowed by rule '~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
|
||||
allow;
|
||||
_ ->
|
||||
none
|
||||
end;
|
||||
(_, Val) ->
|
||||
Val
|
||||
end, none, Defs),
|
||||
Res2 = case Res of
|
||||
allow -> allow;
|
||||
_ ->
|
||||
?DEBUG("Command '~p' execution denied (CallerInfo=~p)", [Cmd, CallerInfo]),
|
||||
deny
|
||||
end,
|
||||
{reply, Res2, State2};
|
||||
handle_call(show_current_definitions, _From, State) ->
|
||||
{State2, Defs} = get_definitions(State),
|
||||
{reply, Defs, State2};
|
||||
handle_call({register_config_fragment_generator, Name, Fun}, _From, #state{fragments_generators = Gens} = State) ->
|
||||
NGens = lists:keystore(Name, 1, Gens, {Name, Fun}),
|
||||
{reply, ok, State#state{fragments_generators = NGens}};
|
||||
handle_call({unregister_config_fragment_generator, Name}, _From, #state{fragments_generators = Gens} = State) ->
|
||||
NGens = lists:keydelete(Name, 1, Gens),
|
||||
{reply, ok, State#state{fragments_generators = NGens}};
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% Handling cast messages
|
||||
%%
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec handle_cast(Request :: term(), State :: #state{}) ->
|
||||
{noreply, NewState :: #state{}} |
|
||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||
{stop, Reason :: term(), NewState :: #state{}}.
|
||||
handle_cast(invalidate, State) ->
|
||||
{noreply, State#state{definitions = none}};
|
||||
handle_cast(_Request, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% Handling all non call/cast messages
|
||||
%%
|
||||
%% @spec handle_info(Info, State) -> {noreply, State} |
|
||||
%% {noreply, State, Timeout} |
|
||||
%% {stop, Reason, State}
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec handle_info(Info :: timeout() | term(), State :: #state{}) ->
|
||||
{noreply, NewState :: #state{}} |
|
||||
{noreply, NewState :: #state{}, timeout() | hibernate} |
|
||||
{stop, Reason :: term(), NewState :: #state{}}.
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% This function is called by a gen_server when it is about to
|
||||
%% terminate. It should be the opposite of Module:init/1 and do any
|
||||
%% necessary cleaning up. When it returns, the gen_server terminates
|
||||
%% with Reason. The return value is ignored.
|
||||
%%
|
||||
%% @spec terminate(Reason, State) -> void()
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
|
||||
State :: #state{}) -> term().
|
||||
terminate(_Reason, _State) ->
|
||||
ejabberd_hooks:delete(config_reloaded, ?MODULE, invalidate, 90).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% @private
|
||||
%% @doc
|
||||
%% Convert process state when code is changed
|
||||
%%
|
||||
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
|
||||
%% @end
|
||||
%%--------------------------------------------------------------------
|
||||
-spec code_change(OldVsn :: term() | {down, term()}, State :: #state{},
|
||||
Extra :: term()) ->
|
||||
{ok, NewState :: #state{}} | {error, Reason :: term()}.
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
|
||||
-spec get_definitions(#state{}) -> {#state{}, any()}.
|
||||
get_definitions(#state{definitions = Defs} = State) when Defs /= none ->
|
||||
{State, Defs};
|
||||
get_definitions(#state{definitions = none, fragments_generators = Gens} = State) ->
|
||||
DefaultOptions = [{<<"admin access">>,
|
||||
{[],
|
||||
[{acl,{acl,admin}},
|
||||
{oauth,[<<"ejabberd:admin">>],[{acl,{acl,admin}}]}],
|
||||
{all, [start, stop]}}}],
|
||||
ApiPerms = ejabberd_config:get_option(api_permissions, DefaultOptions),
|
||||
AllCommands = ejabberd_commands:get_commands_definition(),
|
||||
Frags = lists:foldl(
|
||||
fun({_Name, Generator}, Acc) ->
|
||||
Acc ++ Generator()
|
||||
end, [], Gens),
|
||||
NDefs0 = lists:map(
|
||||
fun({Name, {From, Who, {Add, Del}}}) ->
|
||||
Cmds = filter_commands_with_permissions(AllCommands, Add, Del),
|
||||
{Name, {From, Who, Cmds}}
|
||||
end, ApiPerms ++ Frags),
|
||||
NDefs = case lists:keyfind(<<"console commands">>, 1, NDefs0) of
|
||||
false ->
|
||||
[{<<"console commands">>,
|
||||
{[ejabberd_ctl],
|
||||
[{acl, all}],
|
||||
filter_commands_with_permissions(AllCommands, all, none)}} | NDefs0];
|
||||
_ ->
|
||||
NDefs0
|
||||
end,
|
||||
{State#state{definitions = NDefs}, NDefs}.
|
||||
|
||||
matches_definition({_Name, {From, Who, What}}, Cmd, Module, Host, CallerInfo) ->
|
||||
case What == all orelse lists:member(Cmd, What) of
|
||||
true ->
|
||||
case From == [] orelse lists:member(Module, From) of
|
||||
true ->
|
||||
Scope = maps:get(oauth_scope, CallerInfo, none),
|
||||
lists:any(
|
||||
fun({access, Access}) when Scope == none ->
|
||||
acl:access_matches(Access, CallerInfo, Host) == allow;
|
||||
({acl, Acl}) when Scope == none ->
|
||||
acl:acl_rule_matches(Acl, CallerInfo, Host);
|
||||
({oauth, Scopes, List}) when Scope /= none ->
|
||||
case ejabberd_oauth:scope_in_scope_list(Scope, Scopes) of
|
||||
true ->
|
||||
lists:any(
|
||||
fun({access, Access}) ->
|
||||
acl:access_matches(Access, CallerInfo, Host) == allow;
|
||||
({acl, Acl}) ->
|
||||
acl:acl_rule_matches(Acl, CallerInfo, Host)
|
||||
end, List);
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
(_) ->
|
||||
false
|
||||
end, Who);
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
filter_commands_with_permissions(AllCommands, Add, Del) ->
|
||||
CommandsAdd = filter_commands_with_patterns(AllCommands, Add, []),
|
||||
CommandsDel = filter_commands_with_patterns(CommandsAdd, Del, []),
|
||||
lists:map(fun(#ejabberd_commands{name = N}) -> N end,
|
||||
CommandsAdd -- CommandsDel).
|
||||
|
||||
filter_commands_with_patterns([], _Patterns, Acc) ->
|
||||
Acc;
|
||||
filter_commands_with_patterns([C | CRest], Patterns, Acc) ->
|
||||
case command_matches_patterns(C, Patterns) of
|
||||
true ->
|
||||
filter_commands_with_patterns(CRest, Patterns, [C | Acc]);
|
||||
_ ->
|
||||
filter_commands_with_patterns(CRest, Patterns, Acc)
|
||||
end.
|
||||
|
||||
command_matches_patterns(_, all) ->
|
||||
true;
|
||||
command_matches_patterns(_, none) ->
|
||||
false;
|
||||
command_matches_patterns(_, []) ->
|
||||
false;
|
||||
command_matches_patterns(#ejabberd_commands{tags = Tags} = C, [{tag, Tag} | Tail]) ->
|
||||
case lists:member(Tag, Tags) of
|
||||
true ->
|
||||
true;
|
||||
_ ->
|
||||
command_matches_patterns(C, Tail)
|
||||
end;
|
||||
command_matches_patterns(#ejabberd_commands{name = Name}, [Name | _Tail]) ->
|
||||
true;
|
||||
command_matches_patterns(C, [_ | Tail]) ->
|
||||
command_matches_patterns(C, Tail).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Options parsing code
|
||||
%%%===================================================================
|
||||
|
||||
parse_api_permissions(Data) when is_list(Data) ->
|
||||
[parse_api_permission(Name, Args) || {Name, Args} <- Data].
|
||||
|
||||
parse_api_permission(Name, Args0) ->
|
||||
Args = lists:flatten(Args0),
|
||||
{From, Who, What} = case key_split(Args, [{from, []}, {who, none}, {what, []}]) of
|
||||
{error, Msg} ->
|
||||
report_error(<<"~s inside api_permission '~s' section">>, [Msg, Name]);
|
||||
Val -> Val
|
||||
end,
|
||||
{Name, {parse_from(Name, From), parse_who(Name, Who, oauth), parse_what(Name, What)}}.
|
||||
|
||||
parse_from(_Name, Module) when is_atom(Module) ->
|
||||
[Module];
|
||||
parse_from(Name, Modules) when is_list(Modules) ->
|
||||
lists:foreach(fun(Module) when is_atom(Module) ->
|
||||
ok;
|
||||
(Val) ->
|
||||
report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>,
|
||||
[Val, Name])
|
||||
end, Modules),
|
||||
Modules;
|
||||
parse_from(Name, Val) ->
|
||||
report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>,
|
||||
[Val, Name]).
|
||||
|
||||
parse_who(Name, Atom, ParseOauth) when is_atom(Atom) ->
|
||||
parse_who(Name, [Atom], ParseOauth);
|
||||
parse_who(Name, Defs, ParseOauth) when is_list(Defs) ->
|
||||
lists:map(
|
||||
fun([Val]) ->
|
||||
[NVal] = parse_who(Name, [Val], ParseOauth),
|
||||
NVal;
|
||||
({access, Val}) ->
|
||||
try acl:access_rules_validator(Val) of
|
||||
Rule ->
|
||||
{access, Rule}
|
||||
catch
|
||||
throw:{invalid_syntax, Msg} ->
|
||||
report_error(<<"Invalid access rule: '~s' used inside 'who' section for api_permission '~s'">>,
|
||||
[Msg, Name]);
|
||||
error:_ ->
|
||||
report_error(<<"Invalid access rule '~p' used inside 'who' section for api_permission '~s'">>,
|
||||
[Val, Name])
|
||||
end;
|
||||
({oauth, OauthList}) when is_list(OauthList) ->
|
||||
case ParseOauth of
|
||||
oauth ->
|
||||
Nested = parse_who(Name, lists:flatten(OauthList), scope),
|
||||
{Scopes, Rest} = lists:partition(
|
||||
fun({scope, _}) -> true;
|
||||
(_) -> false
|
||||
end, Nested),
|
||||
case Scopes of
|
||||
[] ->
|
||||
report_error(<<"Oauth rule must contain at least one scope rule in 'who' section for api_permission '~s'">>,
|
||||
[Name]);
|
||||
_ ->
|
||||
{oauth, lists:foldl(fun({scope, S}, A) -> S ++ A end, [], Scopes), Rest}
|
||||
end;
|
||||
scope ->
|
||||
report_error(<<"Oauth rule can't be embeded inside other oauth rule in 'who' section for api_permission '~s'">>,
|
||||
[Name])
|
||||
end;
|
||||
({scope, ScopeList}) ->
|
||||
case ParseOauth of
|
||||
oauth ->
|
||||
report_error(<<"Scope can be included only inside oauth rule in 'who' section for api_permission '~s'">>,
|
||||
[Name]);
|
||||
scope ->
|
||||
ScopeList2 = case ScopeList of
|
||||
V when is_binary(V) -> [V];
|
||||
V2 when is_list(V2) -> V2;
|
||||
V3 ->
|
||||
report_error(<<"Invalid value for scope '~p' in 'who' section for api_permission '~s'">>,
|
||||
[V3, Name])
|
||||
end,
|
||||
{scope, ScopeList2}
|
||||
end;
|
||||
(Atom) when is_atom(Atom) ->
|
||||
{acl, {acl, Atom}};
|
||||
(Other) ->
|
||||
try acl:normalize_spec(Other) of
|
||||
Rule2 ->
|
||||
{acl, Rule2}
|
||||
catch
|
||||
_:_ ->
|
||||
report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>,
|
||||
[Other, Name])
|
||||
end
|
||||
end, Defs);
|
||||
parse_who(Name, Val, _ParseOauth) ->
|
||||
report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>,
|
||||
[Val, Name]).
|
||||
|
||||
parse_what(Name, Binary) when is_binary(Binary) ->
|
||||
parse_what(Name, [Binary]);
|
||||
parse_what(Name, Defs) when is_list(Defs) ->
|
||||
{A, D} = lists:foldl(
|
||||
fun(Def, {Add, Del}) ->
|
||||
case parse_single_what(Def) of
|
||||
{error, Err} ->
|
||||
report_error(<<"~s used in value '~p' in 'what' section for api_permission '~s'">>,
|
||||
[Err, Def, Name]);
|
||||
all ->
|
||||
{case Add of none -> none; _ -> all end, Del};
|
||||
{neg, all} ->
|
||||
{none, all};
|
||||
{neg, Value} ->
|
||||
{Add, case Del of L when is_list(L) -> [Value | L]; L2 -> L2 end};
|
||||
Value ->
|
||||
{case Add of L when is_list(L) -> [Value | L]; L2 -> L2 end, Del}
|
||||
end
|
||||
end, {[], []}, Defs),
|
||||
case {A, D} of
|
||||
{[], _} ->
|
||||
{none, all};
|
||||
{A2, []} ->
|
||||
{A2, none};
|
||||
V ->
|
||||
V
|
||||
end;
|
||||
parse_what(Name, Val) ->
|
||||
report_error(<<"Invalid value '~p' used inside 'what' section for api_permission '~s'">>,
|
||||
[Val, Name]).
|
||||
|
||||
parse_single_what(<<"*">>) ->
|
||||
all;
|
||||
parse_single_what(<<"!*">>) ->
|
||||
{neg, all};
|
||||
parse_single_what(<<"!", Rest/binary>>) ->
|
||||
case parse_single_what(Rest) of
|
||||
{neg, _} ->
|
||||
{error, <<"Double negation">>};
|
||||
{error, _} = Err ->
|
||||
Err;
|
||||
V ->
|
||||
{neg, V}
|
||||
end;
|
||||
parse_single_what(<<"[tag:", Rest/binary>>) ->
|
||||
case binary:split(Rest, <<"]">>) of
|
||||
[TagName, <<"">>] ->
|
||||
case parse_single_what(TagName) of
|
||||
{error, _} = Err ->
|
||||
Err;
|
||||
V when is_atom(V) ->
|
||||
{tag, V};
|
||||
_ ->
|
||||
{error, <<"Invalid tag">>}
|
||||
end;
|
||||
_ ->
|
||||
{error, <<"Invalid tag">>}
|
||||
end;
|
||||
parse_single_what(Binary) when is_binary(Binary) ->
|
||||
case is_valid_command_name(Binary) of
|
||||
true ->
|
||||
binary_to_atom(Binary, latin1);
|
||||
_ ->
|
||||
{error, <<"Invalid value">>}
|
||||
end;
|
||||
parse_single_what(_) ->
|
||||
{error, <<"Invalid value">>}.
|
||||
|
||||
is_valid_command_name(<<>>) ->
|
||||
false;
|
||||
is_valid_command_name(Val) ->
|
||||
is_valid_command_name2(Val).
|
||||
|
||||
is_valid_command_name2(<<>>) ->
|
||||
true;
|
||||
is_valid_command_name2(<<K:8, Rest/binary>>) when K >= $a andalso K =< $z orelse K == $_ ->
|
||||
is_valid_command_name2(Rest);
|
||||
is_valid_command_name2(_) ->
|
||||
false.
|
||||
|
||||
key_split(Args, Fields) ->
|
||||
{_, Order1, Results1, Required1} = lists:foldl(
|
||||
fun({Field, Default}, {Idx, Order, Results, Required}) ->
|
||||
{Idx + 1, maps:put(Field, Idx, Order), [Default | Results], Required};
|
||||
(Field, {Idx, Order, Results, Required}) ->
|
||||
{Idx + 1, maps:put(Field, Idx, Order), [none | Results], maps:put(Field, 1, Required)}
|
||||
end, {1, #{}, [], #{}}, Fields),
|
||||
key_split(Args, list_to_tuple(Results1), Order1, Required1, #{}).
|
||||
|
||||
key_split([], _Results, _Order, Required, _Duplicates) when map_size(Required) > 0 ->
|
||||
parse_error(<<"Missing fields '~s">>, [str:join(maps:keys(Required), <<", ">>)]);
|
||||
key_split([], Results, _Order, _Required, _Duplicates) ->
|
||||
Results;
|
||||
key_split([{Arg, Value} | Rest], Results, Order, Required, Duplicates) ->
|
||||
case maps:find(Arg, Order) of
|
||||
{ok, Idx} ->
|
||||
case maps:is_key(Arg, Duplicates) of
|
||||
false ->
|
||||
Results2 = setelement(Idx, Results, Value),
|
||||
key_split(Rest, Results2, Order, maps:remove(Arg, Required), maps:put(Arg, 1, Duplicates));
|
||||
true ->
|
||||
parse_error(<<"Duplicate field '~s'">>, [Arg])
|
||||
end;
|
||||
_ ->
|
||||
parse_error(<<"Unknown field '~s'">>, [Arg])
|
||||
end.
|
||||
|
||||
report_error(Format, Args) ->
|
||||
throw({invalid_syntax, (str:format(Format, Args))}).
|
||||
|
||||
parse_error(Format, Args) ->
|
||||
{error, (str:format(Format, Args))}.
|
||||
|
||||
opt_type(api_permissions) ->
|
||||
fun parse_api_permissions/1;
|
||||
opt_type(_) ->
|
||||
[api_permissions].
|
||||
+129
-25
@@ -5,7 +5,7 @@
|
||||
%%% Created : 7 May 2006 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -26,7 +26,9 @@
|
||||
-module(ejabberd_admin).
|
||||
-author('mickael.remond@process-one.net').
|
||||
|
||||
-export([start/0, stop/0,
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start_link/0,
|
||||
%% Server
|
||||
status/0, reopen_log/0, rotate_log/0,
|
||||
set_loglevel/1,
|
||||
@@ -52,19 +54,43 @@
|
||||
dump_to_textfile/1, dump_to_textfile/2,
|
||||
mnesia_change_nodename/4,
|
||||
restore/1, % Still used by some modules
|
||||
clear_cache/0,
|
||||
get_commands_spec/0
|
||||
]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
|
||||
start() ->
|
||||
ejabberd_commands:register_commands(get_commands_spec()).
|
||||
-record(state, {}).
|
||||
|
||||
stop() ->
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
init([]) ->
|
||||
process_flag(trap_exit, true),
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ejabberd_commands:unregister_commands(get_commands_spec()).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%%
|
||||
%%% ejabberd commands
|
||||
%%%
|
||||
@@ -76,6 +102,9 @@ get_commands_spec() ->
|
||||
#ejabberd_commands{name = status, tags = [server],
|
||||
desc = "Get status of the ejabberd server",
|
||||
module = ?MODULE, function = status,
|
||||
result_desc = "Result tuple",
|
||||
result_example = {ok, <<"The node ejabberd@localhost is started with status: started"
|
||||
"ejabberd X.X is running in that node">>},
|
||||
args = [], result = {res, restuple}},
|
||||
#ejabberd_commands{name = stop, tags = [server],
|
||||
desc = "Stop ejabberd gracefully",
|
||||
@@ -101,11 +130,15 @@ get_commands_spec() ->
|
||||
"ejabberdctl stop_kindly 60 "
|
||||
"\\\"The server will stop in one minute.\\\"",
|
||||
module = ?MODULE, function = stop_kindly,
|
||||
args_desc = ["Seconds to wait", "Announcement to send, with quotes"],
|
||||
args_example = [60, <<"Server will stop now.">>],
|
||||
args = [{delay, integer}, {announcement, string}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = get_loglevel, tags = [logs, server],
|
||||
desc = "Get the current loglevel",
|
||||
module = ejabberd_logger, function = get,
|
||||
result_desc = "Tuple with the log level number, its keyword and description",
|
||||
result_example = {4, info, <<"Info">>},
|
||||
args = [],
|
||||
result = {leveltuple, {tuple, [{levelnumber, integer},
|
||||
{levelatom, atom},
|
||||
@@ -114,6 +147,10 @@ get_commands_spec() ->
|
||||
#ejabberd_commands{name = set_loglevel, tags = [logs, server],
|
||||
desc = "Set the loglevel (0 to 5)",
|
||||
module = ?MODULE, function = set_loglevel,
|
||||
args_desc = ["Integer of the desired logging level, between 1 and 5"],
|
||||
args_example = [5],
|
||||
result_desc = "The type of logger module used",
|
||||
result_example = lager,
|
||||
args = [{loglevel, integer}],
|
||||
result = {logger, atom}},
|
||||
|
||||
@@ -121,10 +158,12 @@ get_commands_spec() ->
|
||||
desc = "List modified modules that can be updated",
|
||||
module = ?MODULE, function = update_list,
|
||||
args = [],
|
||||
result_example = ["mod_configure", "mod_vcard"],
|
||||
result = {modules, {list, {module, string}}}},
|
||||
#ejabberd_commands{name = update, tags = [server],
|
||||
desc = "Update the given module, or use the keyword: all",
|
||||
module = ?MODULE, function = update,
|
||||
args_example = ["mod_vcard"],
|
||||
args = [{module, string}],
|
||||
result = {res, restuple}},
|
||||
|
||||
@@ -132,25 +171,36 @@ get_commands_spec() ->
|
||||
desc = "Register a user",
|
||||
policy = admin,
|
||||
module = ?MODULE, function = register,
|
||||
args_desc = ["Username", "Local vhost served by ejabberd", "Password"],
|
||||
args_example = [<<"bob">>, <<"example.com">>, <<"SomEPass44">>],
|
||||
args = [{user, binary}, {host, binary}, {password, binary}],
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = unregister, tags = [accounts],
|
||||
desc = "Unregister a user",
|
||||
policy = admin,
|
||||
module = ?MODULE, function = unregister,
|
||||
args_desc = ["Username", "Local vhost served by ejabberd"],
|
||||
args_example = [<<"bob">>, <<"example.com">>],
|
||||
args = [{user, binary}, {host, binary}],
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = registered_users, tags = [accounts],
|
||||
desc = "List all registered users in HOST",
|
||||
module = ?MODULE, function = registered_users,
|
||||
args_desc = ["Local vhost"],
|
||||
args_example = [<<"example.com">>],
|
||||
result_desc = "List of registered accounts usernames",
|
||||
result_example = [<<"user1">>, <<"user2">>],
|
||||
args = [{host, binary}],
|
||||
result = {users, {list, {username, string}}}},
|
||||
#ejabberd_commands{name = registered_vhosts, tags = [server],
|
||||
desc = "List all registered vhosts in SERVER",
|
||||
module = ?MODULE, function = registered_vhosts,
|
||||
result_desc = "List of available vhosts",
|
||||
result_example = [<<"example.com">>, <<"anon.example.com">>],
|
||||
args = [],
|
||||
result = {vhosts, {list, {vhost, string}}}},
|
||||
#ejabberd_commands{name = reload_config, tags = [server],
|
||||
desc = "Reload config file in memory (only affects ACL and Access)",
|
||||
desc = "Reload config file in memory",
|
||||
module = ?MODULE, function = reload_config,
|
||||
args = [],
|
||||
result = {res, rescode}},
|
||||
@@ -158,59 +208,85 @@ get_commands_spec() ->
|
||||
#ejabberd_commands{name = join_cluster, tags = [cluster],
|
||||
desc = "Join this node into the cluster handled by Node",
|
||||
module = ?MODULE, function = join_cluster,
|
||||
args_desc = ["Nodename of the node to join"],
|
||||
args_example = [<<"ejabberd1@machine7">>],
|
||||
args = [{node, binary}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = leave_cluster, tags = [cluster],
|
||||
desc = "Remove node handled by Node from the cluster",
|
||||
desc = "Remove and shutdown Node from the running cluster",
|
||||
longdesc = "This command can be run from any running node of the cluster, "
|
||||
"even the node to be removed.",
|
||||
module = ?MODULE, function = leave_cluster,
|
||||
args_desc = ["Nodename of the node to kick from the cluster"],
|
||||
args_example = [<<"ejabberd1@machine8">>],
|
||||
args = [{node, binary}],
|
||||
result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = list_cluster, tags = [cluster],
|
||||
desc = "List nodes that are part of the cluster handled by Node",
|
||||
module = ?MODULE, function = list_cluster,
|
||||
result_example = [ejabberd1@machine7, ejabberd1@machine8],
|
||||
args = [],
|
||||
result = {nodes, {list, {node, atom}}}},
|
||||
|
||||
#ejabberd_commands{name = import_file, tags = [mnesia],
|
||||
desc = "Import user data from jabberd14 spool file",
|
||||
module = ?MODULE, function = import_file,
|
||||
args_desc = ["Full path to the jabberd14 spool file"],
|
||||
args_example = ["/var/lib/ejabberd/jabberd14.spool"],
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = import_dir, tags = [mnesia],
|
||||
desc = "Import users data from jabberd14 spool dir",
|
||||
module = ?MODULE, function = import_dir,
|
||||
args_desc = ["Full path to the jabberd14 spool directory"],
|
||||
args_example = ["/var/lib/ejabberd/jabberd14/"],
|
||||
args = [{file, string}],
|
||||
result = {res, restuple}},
|
||||
|
||||
#ejabberd_commands{name = import_piefxis, tags = [mnesia],
|
||||
desc = "Import users data from a PIEFXIS file (XEP-0227)",
|
||||
module = ejabberd_piefxis, function = import_file,
|
||||
args_desc = ["Full path to the PIEFXIS file"],
|
||||
args_example = ["/var/lib/ejabberd/example.com.xml"],
|
||||
args = [{file, string}], result = {res, rescode}},
|
||||
#ejabberd_commands{name = export_piefxis, tags = [mnesia],
|
||||
desc = "Export data of all users in the server to PIEFXIS files (XEP-0227)",
|
||||
module = ejabberd_piefxis, function = export_server,
|
||||
args_desc = ["Full path to a directory"],
|
||||
args_example = ["/var/lib/ejabberd/"],
|
||||
args = [{dir, string}], result = {res, rescode}},
|
||||
#ejabberd_commands{name = export_piefxis_host, tags = [mnesia],
|
||||
desc = "Export data of users in a host to PIEFXIS files (XEP-0227)",
|
||||
module = ejabberd_piefxis, function = export_host,
|
||||
args_desc = ["Full path to a directory", "Vhost to export"],
|
||||
args_example = ["/var/lib/ejabberd/", "example.com"],
|
||||
args = [{dir, string}, {host, string}], result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = delete_mnesia, tags = [mnesia, sql],
|
||||
desc = "Export all tables as SQL queries to a file",
|
||||
desc = "Delete elements in Mnesia database for a given vhost",
|
||||
module = ejd2sql, function = delete,
|
||||
args_desc = ["Vhost which content will be deleted in Mnesia database"],
|
||||
args_example = ["example.com"],
|
||||
args = [{host, string}], result = {res, rescode}},
|
||||
#ejabberd_commands{name = convert_to_scram, tags = [sql],
|
||||
desc = "Convert the passwords in 'users' ODBC table to SCRAM",
|
||||
module = ejabberd_auth_sql, function = convert_to_scram,
|
||||
args_desc = ["Vhost which users' passwords will be scrammed"],
|
||||
args_example = ["example.com"],
|
||||
args = [{host, binary}], result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = import_prosody, tags = [mnesia, sql, riak],
|
||||
desc = "Import data from Prosody",
|
||||
module = prosody2ejabberd, function = from_dir,
|
||||
args_desc = ["Full path to the Prosody data directory"],
|
||||
args_example = ["/var/lib/prosody/datadump/"],
|
||||
args = [{dir, string}], result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = convert_to_yaml, tags = [config],
|
||||
desc = "Convert the input file from Erlang to YAML format",
|
||||
module = ejabberd_config, function = convert_to_yaml,
|
||||
args_desc = ["Full path to the original configuration file", "And full path to final file"],
|
||||
args_example = ["/etc/ejabberd/ejabberd.cfg", "/etc/ejabberd/ejabberd.yml"],
|
||||
args = [{in, string}, {out, string}],
|
||||
result = {res, rescode}},
|
||||
|
||||
@@ -221,11 +297,16 @@ get_commands_spec() ->
|
||||
#ejabberd_commands{name = delete_old_messages, tags = [purge],
|
||||
desc = "Delete offline messages older than DAYS",
|
||||
module = ?MODULE, function = delete_old_messages,
|
||||
args_desc = ["Number of days"],
|
||||
args_example = [31],
|
||||
args = [{days, integer}], result = {res, rescode}},
|
||||
|
||||
#ejabberd_commands{name = export2sql, tags = [mnesia],
|
||||
desc = "Export virtual host information from Mnesia tables to SQL files",
|
||||
desc = "Export virtual host information from Mnesia tables to SQL file",
|
||||
longdesc = "Configure the modules to use SQL, then call this command.",
|
||||
module = ejd2sql, function = export,
|
||||
args_desc = ["Vhost", "Full path to the destination SQL file"],
|
||||
args_example = ["example.com", "/var/lib/ejabberd/example.com.sql"],
|
||||
args = [{host, string}, {file, string}],
|
||||
result = {res, rescode}},
|
||||
#ejabberd_commands{name = set_master, tags = [mnesia],
|
||||
@@ -233,37 +314,59 @@ get_commands_spec() ->
|
||||
longdesc = "If you provide as nodename \"self\", this "
|
||||
"node will be set as its own master.",
|
||||
module = ?MODULE, function = set_master,
|
||||
args_desc = ["Name of the erlang node that will be considered master of this node"],
|
||||
args_example = ["ejabberd@machine7"],
|
||||
args = [{nodename, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = mnesia_change_nodename, tags = [mnesia],
|
||||
desc = "Change the erlang node name in a backup file",
|
||||
module = ?MODULE, function = mnesia_change_nodename,
|
||||
args_desc = ["Name of the old erlang node", "Name of the new node",
|
||||
"Path to old backup file", "Path to the new backup file"],
|
||||
args_example = ["ejabberd@machine1", "ejabberd@machine2",
|
||||
"/var/lib/ejabberd/old.backup", "/var/lib/ejabberd/new.backup"],
|
||||
args = [{oldnodename, string}, {newnodename, string},
|
||||
{oldbackup, string}, {newbackup, string}],
|
||||
result = {res, restuple}},
|
||||
#ejabberd_commands{name = backup, tags = [mnesia],
|
||||
desc = "Store the database to backup file",
|
||||
module = ?MODULE, function = backup_mnesia,
|
||||
args_desc = ["Full path for the destination backup file"],
|
||||
args_example = ["/var/lib/ejabberd/database.backup"],
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = restore, tags = [mnesia],
|
||||
desc = "Restore the database from backup file",
|
||||
module = ?MODULE, function = restore_mnesia,
|
||||
args_desc = ["Full path to the backup file"],
|
||||
args_example = ["/var/lib/ejabberd/database.backup"],
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = dump, tags = [mnesia],
|
||||
desc = "Dump the database to text file",
|
||||
desc = "Dump the database to a text file",
|
||||
module = ?MODULE, function = dump_mnesia,
|
||||
args_desc = ["Full path for the text file"],
|
||||
args_example = ["/var/lib/ejabberd/database.txt"],
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = dump_table, tags = [mnesia],
|
||||
desc = "Dump a table to text file",
|
||||
desc = "Dump a table to a text file",
|
||||
module = ?MODULE, function = dump_table,
|
||||
args_desc = ["Full path for the text file", "Table name"],
|
||||
args_example = ["/var/lib/ejabberd/table-muc-registered.txt", "muc_registered"],
|
||||
args = [{file, string}, {table, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = load, tags = [mnesia],
|
||||
desc = "Restore the database from text file",
|
||||
desc = "Restore the database from a text file",
|
||||
module = ?MODULE, function = load_mnesia,
|
||||
args_desc = ["Full path to the text file"],
|
||||
args_example = ["/var/lib/ejabberd/database.txt"],
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = install_fallback, tags = [mnesia],
|
||||
desc = "Install the database from a fallback file",
|
||||
module = ?MODULE, function = install_fallback_mnesia,
|
||||
args = [{file, string}], result = {res, restuple}}
|
||||
args_desc = ["Full path to the fallback file"],
|
||||
args_example = ["/var/lib/ejabberd/database.fallback"],
|
||||
args = [{file, string}], result = {res, restuple}},
|
||||
#ejabberd_commands{name = clear_cache, tags = [server],
|
||||
desc = "Clear database cache on all nodes",
|
||||
module = ?MODULE, function = clear_cache,
|
||||
args = [], result = {res, rescode}}
|
||||
].
|
||||
|
||||
|
||||
@@ -302,8 +405,8 @@ set_loglevel(LogLevel) ->
|
||||
%%%
|
||||
|
||||
stop_kindly(DelaySeconds, AnnouncementTextString) ->
|
||||
Subject = list_to_binary(io_lib:format("Server stop in ~p seconds!", [DelaySeconds])),
|
||||
WaitingDesc = list_to_binary(io_lib:format("Waiting ~p seconds", [DelaySeconds])),
|
||||
Subject = (str:format("Server stop in ~p seconds!", [DelaySeconds])),
|
||||
WaitingDesc = (str:format("Waiting ~p seconds", [DelaySeconds])),
|
||||
AnnouncementText = list_to_binary(AnnouncementTextString),
|
||||
Steps = [
|
||||
{"Stopping ejabberd port listeners",
|
||||
@@ -337,13 +440,12 @@ stop_kindly(DelaySeconds, AnnouncementTextString) ->
|
||||
ok.
|
||||
|
||||
send_service_message_all_mucs(Subject, AnnouncementText) ->
|
||||
Message = list_to_binary(
|
||||
io_lib:format("~s~n~s", [Subject, AnnouncementText])),
|
||||
Message = str:format("~s~n~s", [Subject, AnnouncementText]),
|
||||
lists:foreach(
|
||||
fun(ServerHost) ->
|
||||
MUCHost = gen_mod:get_module_opt_host(
|
||||
ServerHost, mod_muc, <<"conference.@HOST@">>),
|
||||
mod_muc:broadcast_service_message(MUCHost, Message)
|
||||
mod_muc:broadcast_service_message(ServerHost, MUCHost, Message)
|
||||
end,
|
||||
?MYHOSTS).
|
||||
|
||||
@@ -377,9 +479,9 @@ update_module(ModuleNameString) ->
|
||||
|
||||
register(User, Host, Password) ->
|
||||
case ejabberd_auth:try_register(User, Host, Password) of
|
||||
{atomic, ok} ->
|
||||
ok ->
|
||||
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
|
||||
{atomic, exists} ->
|
||||
{error, exists} ->
|
||||
Msg = io_lib:format("User ~s@~s already registered", [User, Host]),
|
||||
{error, conflict, 10090, Msg};
|
||||
{error, Reason} ->
|
||||
@@ -393,7 +495,7 @@ unregister(User, Host) ->
|
||||
{ok, ""}.
|
||||
|
||||
registered_users(Host) ->
|
||||
Users = ejabberd_auth:get_vh_registered_users(Host),
|
||||
Users = ejabberd_auth:get_users(Host),
|
||||
SUsers = lists:sort(Users),
|
||||
lists:map(fun({U, _S}) -> U end, SUsers).
|
||||
|
||||
@@ -401,9 +503,7 @@ registered_vhosts() ->
|
||||
?MYHOSTS.
|
||||
|
||||
reload_config() ->
|
||||
ejabberd_config:reload_file(),
|
||||
acl:start(),
|
||||
shaper:start().
|
||||
ejabberd_config:reload_file().
|
||||
|
||||
%%%
|
||||
%%% Cluster management
|
||||
@@ -517,7 +617,7 @@ restore(Path) ->
|
||||
%% Obsolete tables or tables created by module who are no longer used are not
|
||||
%% restored and are ignored.
|
||||
keep_tables() ->
|
||||
lists:flatten([acl, passwd, config, local_config,
|
||||
lists:flatten([acl, passwd, config,
|
||||
keep_modules_tables()]).
|
||||
|
||||
%% Returns the list of modules tables in use, according to the list of actually
|
||||
@@ -665,3 +765,7 @@ mnesia_change_nodename(FromString, ToString, Source, Target) ->
|
||||
{[Other], Acc}
|
||||
end,
|
||||
mnesia:traverse_backup(Source, Target, Convert, switched).
|
||||
|
||||
clear_cache() ->
|
||||
Nodes = ejabberd_cluster:get_nodes(),
|
||||
lists:foreach(fun(T) -> ets_cache:clear(T, Nodes) end, ets_cache:all()).
|
||||
|
||||
+29
-123
@@ -5,7 +5,7 @@
|
||||
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -25,13 +25,11 @@
|
||||
|
||||
-module(ejabberd_app).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(application).
|
||||
|
||||
-export([start/2, prep_stop/1, stop/1,
|
||||
init/0, opt_type/1]).
|
||||
-export([start/2, prep_stop/1, stop/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -41,45 +39,29 @@
|
||||
%%%
|
||||
|
||||
start(normal, _Args) ->
|
||||
{T1, _} = statistics(wall_clock),
|
||||
ejabberd_logger:start(),
|
||||
write_pid_file(),
|
||||
jid:start(),
|
||||
start_apps(),
|
||||
start_elixir_application(),
|
||||
ejabberd:check_app(ejabberd),
|
||||
randoms:start(),
|
||||
db_init(),
|
||||
start(),
|
||||
translate:start(),
|
||||
ejabberd_ctl:init(),
|
||||
ejabberd_commands:init(),
|
||||
ejabberd_admin:start(),
|
||||
gen_mod:start(),
|
||||
ext_mod:start(),
|
||||
setup_if_elixir_conf_used(),
|
||||
ejabberd_config:start(),
|
||||
set_settings_from_config(),
|
||||
acl:start(),
|
||||
shaper:start(),
|
||||
connect_nodes(),
|
||||
Sup = ejabberd_sup:start_link(),
|
||||
ejabberd_rdbms:start(),
|
||||
ejabberd_riak_sup:start(),
|
||||
ejabberd_redis:start(),
|
||||
ejabberd_sm:start(),
|
||||
cyrsasl:start(),
|
||||
% Profiling
|
||||
%ejabberd_debug:eprof_start(),
|
||||
%ejabberd_debug:fprof_start(),
|
||||
ejabberd_mnesia:start(),
|
||||
file_queue_init(),
|
||||
maybe_add_nameservers(),
|
||||
ejabberd_auth:start(),
|
||||
ejabberd_oauth:start(),
|
||||
gen_mod:start_modules(),
|
||||
ejabberd_listener:start_listeners(),
|
||||
ejabberd_service:start(),
|
||||
register_elixir_config_hooks(),
|
||||
?INFO_MSG("ejabberd ~s is started in the node ~p", [?VERSION, node()]),
|
||||
Sup;
|
||||
case ejabberd_sup:start_link() of
|
||||
{ok, SupPid} ->
|
||||
register_elixir_config_hooks(),
|
||||
ejabberd_cluster:wait_for_sync(infinity),
|
||||
{T2, _} = statistics(wall_clock),
|
||||
?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs",
|
||||
[?VERSION, node(), (T2-T1)/1000]),
|
||||
lists:foreach(fun erlang:garbage_collect/1, processes()),
|
||||
{ok, SupPid};
|
||||
Err ->
|
||||
Err
|
||||
end;
|
||||
start(_, _) ->
|
||||
{error, badarg}.
|
||||
|
||||
@@ -88,10 +70,8 @@ start(_, _) ->
|
||||
%% before shutting down the processes of the application.
|
||||
prep_stop(State) ->
|
||||
ejabberd_listener:stop_listeners(),
|
||||
ejabberd_admin:stop(),
|
||||
broadcast_c2s_shutdown(),
|
||||
ejabberd_sm:stop(),
|
||||
gen_mod:stop_modules(),
|
||||
timer:sleep(5000),
|
||||
State.
|
||||
|
||||
%% All the processes were killed when this function is called
|
||||
@@ -106,53 +86,6 @@ stop(_State) ->
|
||||
%%% Internal functions
|
||||
%%%
|
||||
|
||||
start() ->
|
||||
spawn_link(?MODULE, init, []).
|
||||
|
||||
init() ->
|
||||
register(ejabberd, self()),
|
||||
loop().
|
||||
|
||||
loop() ->
|
||||
receive
|
||||
_ ->
|
||||
loop()
|
||||
end.
|
||||
|
||||
db_init() ->
|
||||
ejabberd_config:env_binary_to_list(mnesia, dir),
|
||||
MyNode = node(),
|
||||
DbNodes = mnesia:system_info(db_nodes),
|
||||
case lists:member(MyNode, DbNodes) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
?CRITICAL_MSG("Node name mismatch: I'm [~s], "
|
||||
"the database is owned by ~p", [MyNode, DbNodes]),
|
||||
?CRITICAL_MSG("Either set ERLANG_NODE in ejabberdctl.cfg "
|
||||
"or change node name in Mnesia", []),
|
||||
erlang:error(node_name_mismatch)
|
||||
end,
|
||||
case mnesia:system_info(extra_db_nodes) of
|
||||
[] ->
|
||||
mnesia:create_schema([node()]);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
ejabberd:start_app(mnesia, permanent),
|
||||
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
|
||||
|
||||
connect_nodes() ->
|
||||
Nodes = ejabberd_config:get_option(
|
||||
cluster_nodes,
|
||||
fun(Ns) ->
|
||||
true = lists:all(fun is_atom/1, Ns),
|
||||
Ns
|
||||
end, []),
|
||||
lists:foreach(fun(Node) ->
|
||||
net_kernel:connect_node(Node)
|
||||
end, Nodes).
|
||||
|
||||
%% If ejabberd is running on some Windows machine, get nameservers and add to Erlang
|
||||
maybe_add_nameservers() ->
|
||||
case os:type() of
|
||||
@@ -165,16 +98,6 @@ add_windows_nameservers() ->
|
||||
?INFO_MSG("Adding machine's DNS IPs to Erlang system:~n~p", [IPTs]),
|
||||
lists:foreach(fun(IPT) -> inet_db:add_ns(IPT) end, IPTs).
|
||||
|
||||
|
||||
broadcast_c2s_shutdown() ->
|
||||
Children = ejabberd_sm:get_all_pids(),
|
||||
lists:foreach(
|
||||
fun(C2SPid) when node(C2SPid) == node() ->
|
||||
C2SPid ! system_shutdown;
|
||||
(_) ->
|
||||
ok
|
||||
end, Children).
|
||||
|
||||
%%%
|
||||
%%% PID file
|
||||
%%%
|
||||
@@ -205,43 +128,26 @@ delete_pid_file() ->
|
||||
file:delete(PidFilename)
|
||||
end.
|
||||
|
||||
set_settings_from_config() ->
|
||||
Level = ejabberd_config:get_option(
|
||||
loglevel,
|
||||
fun(P) when P>=0, P=<5 -> P end,
|
||||
4),
|
||||
ejabberd_logger:set(Level),
|
||||
Ticktime = ejabberd_config:get_option(
|
||||
net_ticktime,
|
||||
opt_type(net_ticktime),
|
||||
60),
|
||||
net_kernel:set_net_ticktime(Ticktime).
|
||||
file_queue_init() ->
|
||||
QueueDir = case ejabberd_config:queue_dir() of
|
||||
undefined ->
|
||||
MnesiaDir = mnesia:system_info(directory),
|
||||
filename:join(MnesiaDir, "queue");
|
||||
Path ->
|
||||
Path
|
||||
end,
|
||||
p1_queue:start(QueueDir).
|
||||
|
||||
start_apps() ->
|
||||
crypto:start(),
|
||||
ejabberd:start_app(sasl),
|
||||
ejabberd:start_app(ssl),
|
||||
ejabberd:start_app(p1_utils),
|
||||
ejabberd:start_app(fast_yaml),
|
||||
ejabberd:start_app(fast_tls),
|
||||
ejabberd:start_app(fast_xml),
|
||||
ejabberd:start_app(stringprep),
|
||||
ejabberd:start_app(xmpp),
|
||||
ejabberd:start_app(cache_tab).
|
||||
|
||||
opt_type(net_ticktime) ->
|
||||
fun (P) when is_integer(P), P > 0 -> P end;
|
||||
opt_type(cluster_nodes) ->
|
||||
fun (Ns) -> true = lists:all(fun is_atom/1, Ns), Ns end;
|
||||
opt_type(loglevel) ->
|
||||
fun (P) when P >= 0, P =< 5 -> P end;
|
||||
opt_type(modules) ->
|
||||
fun (Mods) ->
|
||||
lists:map(fun ({M, A}) when is_atom(M), is_list(A) ->
|
||||
{M, A}
|
||||
end,
|
||||
Mods)
|
||||
end;
|
||||
opt_type(_) -> [cluster_nodes, loglevel, modules, net_ticktime].
|
||||
|
||||
setup_if_elixir_conf_used() ->
|
||||
case ejabberd_config:is_using_elixir_config() of
|
||||
true -> 'Elixir.Ejabberd.Config.Store':start_link();
|
||||
|
||||
+705
-310
File diff suppressed because it is too large
Load Diff
+50
-143
@@ -5,7 +5,7 @@
|
||||
%%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -26,9 +26,11 @@
|
||||
-module(ejabberd_auth_anonymous).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
-behaviour(ejabberd_auth).
|
||||
-author('mickael.remond@process-one.net').
|
||||
|
||||
-export([start/1,
|
||||
stop/1,
|
||||
allow_anonymous/1,
|
||||
is_sasl_anonymous_enabled/1,
|
||||
is_login_anonymous_enabled/1,
|
||||
@@ -38,38 +40,27 @@
|
||||
unregister_connection/3
|
||||
]).
|
||||
|
||||
-export([login/2, set_password/3, check_password/4,
|
||||
check_password/6, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password_s/2,
|
||||
get_password/2, get_password/3, is_user_exists/2,
|
||||
remove_user/2, remove_user/3, store_type/0,
|
||||
plain_password_required/0, opt_type/1]).
|
||||
-export([login/2, check_password/4, user_exists/2,
|
||||
get_users/2, count_users/2, store_type/1,
|
||||
plain_password_required/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("jlib.hrl").
|
||||
|
||||
%% Create the anonymous table if at least one virtual host has anonymous features enabled
|
||||
%% Register to login / logout events
|
||||
-record(anonymous, {us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
sid = ejabberd_sm:make_sid() :: ejabberd_sm:sid()}).
|
||||
-include("jid.hrl").
|
||||
|
||||
start(Host) ->
|
||||
%% TODO: Check cluster mode
|
||||
mnesia:create_table(anonymous, [{ram_copies, [node()]},
|
||||
{type, bag},
|
||||
{attributes, record_info(fields, anonymous)}]),
|
||||
%% The hooks are needed to add / remove users from the anonymous tables
|
||||
ejabberd_hooks:add(sm_register_connection_hook, Host,
|
||||
?MODULE, register_connection, 100),
|
||||
ejabberd_hooks:add(sm_remove_connection_hook, Host,
|
||||
?MODULE, unregister_connection, 100),
|
||||
ok.
|
||||
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(sm_register_connection_hook, Host,
|
||||
?MODULE, register_connection, 100),
|
||||
ejabberd_hooks:delete(sm_remove_connection_hook, Host,
|
||||
?MODULE, unregister_connection, 100).
|
||||
|
||||
%% Return true if anonymous is allowed for host or false otherwise
|
||||
allow_anonymous(Host) ->
|
||||
lists:member(?MODULE, ejabberd_auth:auth_modules(Host)).
|
||||
@@ -104,85 +95,47 @@ is_login_anonymous_enabled(Host) ->
|
||||
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
|
||||
%% defaults to login_anon
|
||||
anonymous_protocol(Host) ->
|
||||
ejabberd_config:get_option(
|
||||
{anonymous_protocol, Host},
|
||||
fun(sasl_anon) -> sasl_anon;
|
||||
(login_anon) -> login_anon;
|
||||
(both) -> both
|
||||
end,
|
||||
sasl_anon).
|
||||
ejabberd_config:get_option({anonymous_protocol, Host}, sasl_anon).
|
||||
|
||||
%% Return true if multiple connections have been allowed in the config file
|
||||
%% defaults to false
|
||||
allow_multiple_connections(Host) ->
|
||||
ejabberd_config:get_option(
|
||||
{allow_multiple_connections, Host},
|
||||
fun(V) when is_boolean(V) -> V end,
|
||||
false).
|
||||
ejabberd_config:get_option({allow_multiple_connections, Host}, false).
|
||||
|
||||
%% Check if user exist in the anonymus database
|
||||
anonymous_user_exist(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({anonymous, US}) of
|
||||
[] ->
|
||||
false;
|
||||
[_H|_T] ->
|
||||
true
|
||||
end.
|
||||
|
||||
%% Remove connection from Mnesia tables
|
||||
remove_connection(SID, LUser, LServer) ->
|
||||
US = {LUser, LServer},
|
||||
F = fun () -> mnesia:delete_object({anonymous, US, SID})
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
lists:any(
|
||||
fun({_LResource, Info}) ->
|
||||
proplists:get_value(auth_module, Info) == ?MODULE
|
||||
end, ejabberd_sm:get_user_info(User, Server)).
|
||||
|
||||
%% Register connection
|
||||
register_connection(SID,
|
||||
-spec register_connection(ejabberd_sm:sid(), jid(), ejabberd_sm:info()) -> ok.
|
||||
register_connection(_SID,
|
||||
#jid{luser = LUser, lserver = LServer}, Info) ->
|
||||
AuthModule = proplists:get_value(auth_module, Info, undefined),
|
||||
case AuthModule == (?MODULE) of
|
||||
true ->
|
||||
ejabberd_hooks:run(register_user, LServer,
|
||||
[LUser, LServer]),
|
||||
US = {LUser, LServer},
|
||||
mnesia:sync_dirty(fun () ->
|
||||
mnesia:write(#anonymous{us = US,
|
||||
sid = SID})
|
||||
end);
|
||||
false -> ok
|
||||
case proplists:get_value(auth_module, Info) of
|
||||
?MODULE ->
|
||||
ejabberd_hooks:run(register_user, LServer, [LUser, LServer]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% Remove an anonymous user from the anonymous users table
|
||||
unregister_connection(SID,
|
||||
#jid{luser = LUser, lserver = LServer}, _) ->
|
||||
purge_hook(anonymous_user_exist(LUser, LServer), LUser,
|
||||
LServer),
|
||||
remove_connection(SID, LUser, LServer).
|
||||
|
||||
%% Launch the hook to purge user data only for anonymous users
|
||||
purge_hook(false, _LUser, _LServer) ->
|
||||
ok;
|
||||
purge_hook(true, LUser, LServer) ->
|
||||
ejabberd_hooks:run(anonymous_purge_hook, LServer,
|
||||
[LUser, LServer]).
|
||||
-spec unregister_connection(ejabberd_sm:sid(), jid(), ejabberd_sm:info()) -> any().
|
||||
unregister_connection(_SID,
|
||||
#jid{luser = LUser, lserver = LServer}, Info) ->
|
||||
case proplists:get_value(auth_module, Info) of
|
||||
?MODULE ->
|
||||
ejabberd_hooks:run(remove_user, LServer, [LUser, LServer]);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
%% ---------------------------------
|
||||
%% Specific anonymous auth functions
|
||||
%% ---------------------------------
|
||||
|
||||
%% When anonymous login is enabled, check the password for permenant users
|
||||
%% before allowing access
|
||||
check_password(User, AuthzId, Server, Password) ->
|
||||
check_password(User, AuthzId, Server, Password, undefined,
|
||||
undefined).
|
||||
|
||||
check_password(User, _AuthzId, Server, _Password, _Digest,
|
||||
_DigestGen) ->
|
||||
check_password(User, _AuthzId, Server, _Password) ->
|
||||
case
|
||||
ejabberd_auth:is_user_exists_in_other_modules(?MODULE,
|
||||
ejabberd_auth:user_exists_in_other_modules(?MODULE,
|
||||
User, Server)
|
||||
of
|
||||
%% If user exists in other module, reject anonnymous authentication
|
||||
@@ -206,71 +159,25 @@ login(User, Server) ->
|
||||
end
|
||||
end.
|
||||
|
||||
%% When anonymous login is enabled, check that the user is permanent before
|
||||
%% changing its password
|
||||
set_password(User, Server, _Password) ->
|
||||
case anonymous_user_exist(User, Server) of
|
||||
true -> ok;
|
||||
false -> {error, not_allowed}
|
||||
end.
|
||||
get_users(Server, _) ->
|
||||
[{U, S} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Server)].
|
||||
|
||||
%% When anonymous login is enabled, check if permanent users are allowed on
|
||||
%% the server:
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
count_users(Server, Opts) ->
|
||||
length(get_users(Server, Opts)).
|
||||
|
||||
dirty_get_registered_users() -> [].
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
[{U, S}
|
||||
|| {U, S, _R}
|
||||
<- ejabberd_sm:get_vh_session_list(Server)].
|
||||
|
||||
get_vh_registered_users(Server, _) ->
|
||||
get_vh_registered_users(Server).
|
||||
|
||||
get_vh_registered_users_number(Server) ->
|
||||
length(get_vh_registered_users(Server)).
|
||||
|
||||
get_vh_registered_users_number(Server, _) ->
|
||||
get_vh_registered_users_number(Server).
|
||||
|
||||
%% Return password of permanent user or false for anonymous users
|
||||
get_password(User, Server) ->
|
||||
get_password(User, Server, <<"">>).
|
||||
|
||||
get_password(User, Server, DefaultValue) ->
|
||||
case anonymous_user_exist(User, Server) or
|
||||
login(User, Server)
|
||||
of
|
||||
%% We return the default value if the user is anonymous
|
||||
true -> DefaultValue;
|
||||
%% We return the permanent user password otherwise
|
||||
false -> false
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
case get_password(User, Server) of
|
||||
false ->
|
||||
<<"">>;
|
||||
Password ->
|
||||
Password
|
||||
end.
|
||||
|
||||
%% Returns true if the user exists in the DB or if an anonymous user is logged
|
||||
%% under the given name
|
||||
is_user_exists(User, Server) ->
|
||||
user_exists(User, Server) ->
|
||||
anonymous_user_exist(User, Server).
|
||||
|
||||
remove_user(_User, _Server) -> {error, not_allowed}.
|
||||
plain_password_required(_) ->
|
||||
false.
|
||||
|
||||
remove_user(_User, _Server, _Password) -> not_allowed.
|
||||
|
||||
plain_password_required() -> false.
|
||||
|
||||
store_type() ->
|
||||
plain.
|
||||
store_type(_) ->
|
||||
external.
|
||||
|
||||
-spec opt_type(allow_multiple_connection) -> fun((boolean()) -> boolean());
|
||||
(anonymous_protocol) -> fun((sasl_anon | login_anon | both) ->
|
||||
sasl_anon | login_anon | both);
|
||||
(atom()) -> [atom()].
|
||||
opt_type(allow_multiple_connections) ->
|
||||
fun (V) when is_boolean(V) -> V end;
|
||||
opt_type(anonymous_protocol) ->
|
||||
|
||||
+32
-255
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -31,15 +31,9 @@
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
-export([start/1, set_password/3, check_password/4,
|
||||
check_password/6, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, plain_password_required/0,
|
||||
opt_type/1]).
|
||||
-export([start/1, stop/1, set_password/3, check_password/4,
|
||||
try_register/3, user_exists/2, remove_user/2,
|
||||
store_type/1, plain_password_required/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -48,279 +42,62 @@
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(Host) ->
|
||||
Cmd = ejabberd_config:get_option(
|
||||
{extauth_program, Host},
|
||||
fun(V) ->
|
||||
binary_to_list(iolist_to_binary(V))
|
||||
end,
|
||||
"extauth"),
|
||||
extauth:start(Host, Cmd),
|
||||
check_cache_last_options(Host),
|
||||
ejabberd_auth_mnesia:start(Host).
|
||||
Cmd = ejabberd_config:get_option({extauth_program, Host}, "extauth"),
|
||||
extauth:start(Host, Cmd).
|
||||
|
||||
check_cache_last_options(Server) ->
|
||||
case get_cache_option(Server) of
|
||||
false -> no_cache;
|
||||
{true, _CacheTime} ->
|
||||
case get_mod_last_configured(Server) of
|
||||
no_mod_last ->
|
||||
?ERROR_MSG("In host ~p extauth is used, extauth_cache "
|
||||
"is enabled but mod_last is not enabled.",
|
||||
[Server]),
|
||||
no_cache;
|
||||
_ -> cache
|
||||
end
|
||||
end.
|
||||
stop(Host) ->
|
||||
extauth:stop(Host).
|
||||
|
||||
plain_password_required() -> true.
|
||||
plain_password_required(_) -> true.
|
||||
|
||||
store_type() -> external.
|
||||
store_type(_) -> external.
|
||||
|
||||
check_password(User, AuthzId, Server, Password) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
false;
|
||||
true ->
|
||||
case get_cache_option(Server) of
|
||||
false ->
|
||||
check_password_extauth(User, AuthzId, Server, Password);
|
||||
{true, CacheTime} ->
|
||||
check_password_cache(User, AuthzId, Server, Password,
|
||||
CacheTime)
|
||||
end
|
||||
check_password_extauth(User, AuthzId, Server, Password)
|
||||
end.
|
||||
|
||||
check_password(User, AuthzId, Server, Password, _Digest,
|
||||
_DigestGen) ->
|
||||
check_password(User, AuthzId, Server, Password).
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
case extauth:set_password(User, Server, Password) of
|
||||
true ->
|
||||
set_password_mnesia(User, Server, Password), ok;
|
||||
_ -> {error, unknown_problem}
|
||||
true -> ok;
|
||||
_ -> {error, db_failure}
|
||||
end.
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
case get_cache_option(Server) of
|
||||
false -> try_register_extauth(User, Server, Password);
|
||||
{true, _CacheTime} ->
|
||||
try_register_external_cache(User, Server, Password)
|
||||
end.
|
||||
extauth:try_register(User, Server, Password).
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
ejabberd_auth_mnesia:dirty_get_registered_users().
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
ejabberd_auth_mnesia:get_vh_registered_users(Server).
|
||||
|
||||
get_vh_registered_users(Server, Data) ->
|
||||
ejabberd_auth_mnesia:get_vh_registered_users(Server,
|
||||
Data).
|
||||
|
||||
get_vh_registered_users_number(Server) ->
|
||||
ejabberd_auth_mnesia:get_vh_registered_users_number(Server).
|
||||
|
||||
get_vh_registered_users_number(Server, Data) ->
|
||||
ejabberd_auth_mnesia:get_vh_registered_users_number(Server,
|
||||
Data).
|
||||
|
||||
%% The password can only be returned if cache is enabled, cached info exists and is fresh enough.
|
||||
get_password(User, Server) ->
|
||||
case get_cache_option(Server) of
|
||||
false -> false;
|
||||
{true, CacheTime} ->
|
||||
get_password_cache(User, Server, CacheTime)
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
case get_password(User, Server) of
|
||||
false -> <<"">>;
|
||||
Other -> Other
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
try extauth:is_user_exists(User, Server) of
|
||||
Res -> Res
|
||||
user_exists(User, Server) ->
|
||||
try extauth:user_exists(User, Server) of
|
||||
Res -> Res
|
||||
catch
|
||||
_:Error -> {error, Error}
|
||||
_:Error ->
|
||||
?ERROR_MSG("external authentication program failure: ~p",
|
||||
[Error]),
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
case extauth:remove_user(User, Server) of
|
||||
false -> false;
|
||||
true ->
|
||||
case get_cache_option(Server) of
|
||||
false -> false;
|
||||
{true, _CacheTime} ->
|
||||
ejabberd_auth_mnesia:remove_user(User, Server)
|
||||
end
|
||||
false -> {error, not_allowed};
|
||||
true -> ok
|
||||
end.
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
case extauth:remove_user(User, Server, Password) of
|
||||
false -> false;
|
||||
true ->
|
||||
case get_cache_option(Server) of
|
||||
false -> false;
|
||||
{true, _CacheTime} ->
|
||||
ejabberd_auth_mnesia:remove_user(User, Server,
|
||||
Password)
|
||||
end
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% Extauth cache management
|
||||
%%%
|
||||
|
||||
%% @spec (Host::string()) -> false | {true, CacheTime::integer()}
|
||||
get_cache_option(Host) ->
|
||||
case ejabberd_config:get_option(
|
||||
{extauth_cache, Host},
|
||||
fun(false) -> undefined;
|
||||
(I) when is_integer(I), I >= 0 -> I
|
||||
end) of
|
||||
undefined -> false;
|
||||
CacheTime -> {true, CacheTime}
|
||||
end.
|
||||
|
||||
%% @spec (User, AuthzId, Server, Password) -> true | false
|
||||
check_password_extauth(User, _AuthzId, Server, Password) ->
|
||||
extauth:check_password(User, Server, Password) andalso
|
||||
Password /= <<"">>.
|
||||
|
||||
%% @spec (User, Server, Password) -> true | false
|
||||
try_register_extauth(User, Server, Password) ->
|
||||
extauth:try_register(User, Server, Password).
|
||||
|
||||
check_password_cache(User, AuthzId, Server, Password, 0) ->
|
||||
check_password_external_cache(User, AuthzId, Server, Password);
|
||||
check_password_cache(User, AuthzId, Server, Password,
|
||||
CacheTime) ->
|
||||
case get_last_access(User, Server) of
|
||||
online ->
|
||||
check_password_mnesia(User, AuthzId, Server, Password);
|
||||
never ->
|
||||
check_password_external_cache(User, AuthzId, Server, Password);
|
||||
mod_last_required ->
|
||||
?ERROR_MSG("extauth is used, extauth_cache is enabled "
|
||||
"but mod_last is not enabled in that "
|
||||
"host",
|
||||
[]),
|
||||
check_password_external_cache(User, AuthzId, Server, Password);
|
||||
TimeStamp ->
|
||||
case is_fresh_enough(TimeStamp, CacheTime) of
|
||||
%% If no need to refresh, check password against Mnesia
|
||||
true ->
|
||||
case check_password_mnesia(User, AuthzId, Server, Password) of
|
||||
%% If password valid in Mnesia, accept it
|
||||
true -> true;
|
||||
%% Else (password nonvalid in Mnesia), check in extauth and cache result
|
||||
false ->
|
||||
check_password_external_cache(User, AuthzId, Server, Password)
|
||||
end;
|
||||
%% Else (need to refresh), check in extauth and cache result
|
||||
false ->
|
||||
check_password_external_cache(User, AuthzId, Server, Password)
|
||||
end
|
||||
end.
|
||||
|
||||
get_password_mnesia(User, Server) ->
|
||||
ejabberd_auth_mnesia:get_password(User, Server).
|
||||
|
||||
-spec get_password_cache(User::binary(), Server::binary(), CacheTime::integer()) -> Password::string() | false.
|
||||
get_password_cache(User, Server, CacheTime) ->
|
||||
case get_last_access(User, Server) of
|
||||
online -> get_password_mnesia(User, Server);
|
||||
never -> false;
|
||||
mod_last_required ->
|
||||
?ERROR_MSG("extauth is used, extauth_cache is enabled "
|
||||
"but mod_last is not enabled in that "
|
||||
"host",
|
||||
[]),
|
||||
false;
|
||||
TimeStamp ->
|
||||
case is_fresh_enough(TimeStamp, CacheTime) of
|
||||
true -> get_password_mnesia(User, Server);
|
||||
false -> false
|
||||
end
|
||||
end.
|
||||
|
||||
%% Check the password using extauth; if success then cache it
|
||||
check_password_external_cache(User, AuthzId, Server, Password) ->
|
||||
case check_password_extauth(User, AuthzId, Server, Password) of
|
||||
true ->
|
||||
set_password_mnesia(User, Server, Password), true;
|
||||
false -> false
|
||||
end.
|
||||
|
||||
%% Try to register using extauth; if success then cache it
|
||||
try_register_external_cache(User, Server, Password) ->
|
||||
case try_register_extauth(User, Server, Password) of
|
||||
{atomic, ok} = R ->
|
||||
set_password_mnesia(User, Server, Password), R;
|
||||
_ -> {error, not_allowed}
|
||||
end.
|
||||
|
||||
%% @spec (User, AuthzId, Server, Password) -> true | false
|
||||
check_password_mnesia(User, AuthzId, Server, Password) ->
|
||||
ejabberd_auth_mnesia:check_password(User, AuthzId, Server,
|
||||
Password).
|
||||
|
||||
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
|
||||
set_password_mnesia(User, Server, Password) ->
|
||||
%% @spec (TimeLast, CacheTime) -> true | false
|
||||
%% TimeLast = online | never | integer()
|
||||
%% CacheTime = integer() | false
|
||||
ejabberd_auth_mnesia:set_password(User, Server,
|
||||
Password).
|
||||
|
||||
is_fresh_enough(TimeStampLast, CacheTime) ->
|
||||
Now = p1_time_compat:system_time(seconds),
|
||||
TimeStampLast + CacheTime > Now.
|
||||
|
||||
%% Code copied from mod_configure.erl
|
||||
%% Code copied from web/ejabberd_web_admin.erl
|
||||
%% TODO: Update time format to XEP-0202: Entity Time
|
||||
-spec(get_last_access(User::binary(), Server::binary()) -> (online | never | mod_last_required | integer())).
|
||||
get_last_access(User, Server) ->
|
||||
case ejabberd_sm:get_user_resources(User, Server) of
|
||||
[] ->
|
||||
case get_last_info(User, Server) of
|
||||
mod_last_required -> mod_last_required;
|
||||
not_found -> never;
|
||||
{ok, Timestamp, _Status} -> Timestamp
|
||||
end;
|
||||
_ -> online
|
||||
end.
|
||||
%% @spec (User, Server) -> {ok, Timestamp, Status} | not_found | mod_last_required
|
||||
|
||||
get_last_info(User, Server) ->
|
||||
case get_mod_last_enabled(Server) of
|
||||
mod_last -> mod_last:get_last_info(User, Server);
|
||||
no_mod_last -> mod_last_required
|
||||
end.
|
||||
|
||||
%% @spec (Server) -> mod_last | no_mod_last
|
||||
get_mod_last_enabled(Server) ->
|
||||
case gen_mod:is_loaded(Server, mod_last) of
|
||||
true -> mod_last;
|
||||
false -> no_mod_last
|
||||
end.
|
||||
|
||||
get_mod_last_configured(Server) ->
|
||||
case is_configured(Server, mod_last) of
|
||||
true -> mod_last;
|
||||
false -> no_mod_last
|
||||
end.
|
||||
|
||||
is_configured(Host, Module) ->
|
||||
Os = ejabberd_config:get_local_option({modules, Host},
|
||||
fun(M) when is_list(M) -> M end),
|
||||
lists:keymember(Module, 1, Os).
|
||||
|
||||
-spec opt_type(extauth_cache) -> fun((false | non_neg_integer()) ->
|
||||
false | non_neg_integer());
|
||||
(extauth_program) -> fun((binary()) -> string());
|
||||
(atom()) -> [atom()].
|
||||
opt_type(extauth_cache) ->
|
||||
fun (false) -> undefined;
|
||||
?WARNING_MSG("option 'extauth_cache' is deprecated and has no effect, "
|
||||
"use authentication or global cache configuration "
|
||||
"options: auth_use_cache, auth_cache_life_time, "
|
||||
"use_cache, cache_life_time, and so on", []),
|
||||
fun (false) -> false;
|
||||
(I) when is_integer(I), I >= 0 -> I
|
||||
end;
|
||||
opt_type(extauth_program) ->
|
||||
|
||||
+40
-107
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -37,13 +37,9 @@
|
||||
handle_cast/2, terminate/2, code_change/3]).
|
||||
|
||||
-export([start/1, stop/1, start_link/1, set_password/3,
|
||||
check_password/4, check_password/6, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, plain_password_required/0,
|
||||
check_password/4, user_exists/2,
|
||||
get_users/2, count_users/2,
|
||||
store_type/1, plain_password_required/1,
|
||||
opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -65,9 +61,9 @@
|
||||
uids = [] :: [{binary()} | {binary(), binary()}],
|
||||
ufilter = <<"">> :: binary(),
|
||||
sfilter = <<"">> :: binary(),
|
||||
lfilter :: {any(), any()},
|
||||
lfilter :: {any(), any()} | undefined,
|
||||
deref_aliases = never :: never | searching | finding | always,
|
||||
dn_filter :: binary(),
|
||||
dn_filter :: binary() | undefined,
|
||||
dn_filter_attrs = [] :: [binary()]}).
|
||||
|
||||
handle_cast(_Request, State) -> {noreply, State}.
|
||||
@@ -86,13 +82,12 @@ start(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
ChildSpec = {Proc, {?MODULE, start_link, [Host]},
|
||||
transient, 1000, worker, [?MODULE]},
|
||||
supervisor:start_child(ejabberd_sup, ChildSpec).
|
||||
supervisor:start_child(ejabberd_backend_sup, ChildSpec).
|
||||
|
||||
stop(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
gen_server:call(Proc, stop),
|
||||
supervisor:terminate_child(ejabberd_sup, Proc),
|
||||
supervisor:delete_child(ejabberd_sup, Proc).
|
||||
supervisor:terminate_child(ejabberd_backend_sup, Proc),
|
||||
supervisor:delete_child(ejabberd_backend_sup, Proc).
|
||||
|
||||
start_link(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
@@ -101,6 +96,7 @@ start_link(Host) ->
|
||||
terminate(_Reason, _State) -> ok.
|
||||
|
||||
init(Host) ->
|
||||
process_flag(trap_exit, true),
|
||||
State = parse_options(Host),
|
||||
eldap_pool:start_link(State#state.eldap_id,
|
||||
State#state.servers, State#state.backups,
|
||||
@@ -112,9 +108,9 @@ init(Host) ->
|
||||
State#state.password, State#state.tls_options),
|
||||
{ok, State}.
|
||||
|
||||
plain_password_required() -> true.
|
||||
plain_password_required(_) -> true.
|
||||
|
||||
store_type() -> external.
|
||||
store_type(_) -> external.
|
||||
|
||||
check_password(User, AuthzId, Server, Password) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
@@ -129,60 +125,34 @@ check_password(User, AuthzId, Server, Password) ->
|
||||
end
|
||||
end.
|
||||
|
||||
check_password(User, AuthzId, Server, Password, _Digest,
|
||||
_DigestGen) ->
|
||||
check_password(User, AuthzId, Server, Password).
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
||||
case find_user_dn(User, State) of
|
||||
false -> {error, user_not_found};
|
||||
false -> {error, notfound};
|
||||
DN ->
|
||||
eldap_pool:modify_passwd(State#state.eldap_id, DN,
|
||||
Password)
|
||||
case eldap_pool:modify_passwd(State#state.eldap_id, DN,
|
||||
Password) of
|
||||
ok -> ok;
|
||||
_Err -> {error, db_failure}
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> {error, not_allowed}
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
Servers = ejabberd_config:get_vh_by_auth_method(ldap),
|
||||
lists:flatmap(fun (Server) ->
|
||||
get_vh_registered_users(Server)
|
||||
end,
|
||||
Servers).
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
case catch get_vh_registered_users_ldap(Server) of
|
||||
get_users(Server, []) ->
|
||||
case catch get_users_ldap(Server) of
|
||||
{'EXIT', _} -> [];
|
||||
Result -> Result
|
||||
end.
|
||||
|
||||
get_vh_registered_users(Server, _) ->
|
||||
get_vh_registered_users(Server).
|
||||
|
||||
get_vh_registered_users_number(Server) ->
|
||||
length(get_vh_registered_users(Server)).
|
||||
|
||||
get_vh_registered_users_number(Server, _) ->
|
||||
get_vh_registered_users_number(Server).
|
||||
|
||||
get_password(_User, _Server) -> false.
|
||||
|
||||
get_password_s(_User, _Server) -> <<"">>.
|
||||
count_users(Server, Opts) ->
|
||||
length(get_users(Server, Opts)).
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
case catch is_user_exists_ldap(User, Server) of
|
||||
{'EXIT', Error} -> {error, Error};
|
||||
user_exists(User, Server) ->
|
||||
case catch user_exists_ldap(User, Server) of
|
||||
{'EXIT', _Error} -> {error, db_failure};
|
||||
Result -> Result
|
||||
end.
|
||||
|
||||
remove_user(_User, _Server) -> {error, not_allowed}.
|
||||
|
||||
remove_user(_User, _Server, _Password) -> not_allowed.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
%%%----------------------------------------------------------------------
|
||||
@@ -199,7 +169,7 @@ check_password_ldap(User, Server, Password) ->
|
||||
end
|
||||
end.
|
||||
|
||||
get_vh_registered_users_ldap(Server) ->
|
||||
get_users_ldap(Server) ->
|
||||
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
||||
UIDs = State#state.uids,
|
||||
Eldap_ID = State#state.eldap_id,
|
||||
@@ -248,7 +218,7 @@ get_vh_registered_users_ldap(Server) ->
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
is_user_exists_ldap(User, Server) ->
|
||||
user_exists_ldap(User, Server) ->
|
||||
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
||||
case find_user_dn(User, State) of
|
||||
false -> false;
|
||||
@@ -361,27 +331,14 @@ result_attrs(#state{uids = UIDs,
|
||||
%%%----------------------------------------------------------------------
|
||||
parse_options(Host) ->
|
||||
Cfg = eldap_utils:get_config(Host, []),
|
||||
Eldap_ID = jlib:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)),
|
||||
Bind_Eldap_ID = jlib:atom_to_binary(
|
||||
Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)),
|
||||
Bind_Eldap_ID = misc:atom_to_binary(
|
||||
gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
|
||||
UIDsTemp = gen_mod:get_opt(
|
||||
{ldap_uids, Host}, [],
|
||||
fun(Us) ->
|
||||
lists:map(
|
||||
fun({U, P}) ->
|
||||
{iolist_to_binary(U),
|
||||
iolist_to_binary(P)};
|
||||
({U}) ->
|
||||
{iolist_to_binary(U)};
|
||||
(U) ->
|
||||
{iolist_to_binary(U)}
|
||||
end, lists:flatten(Us))
|
||||
end, [{<<"uid">>, <<"%u">>}]),
|
||||
UIDsTemp = ejabberd_config:get_option(
|
||||
{ldap_uids, Host}, [{<<"uid">>, <<"%u">>}]),
|
||||
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
|
||||
SubFilter = eldap_utils:generate_subfilter(UIDs),
|
||||
UserFilter = case gen_mod:get_opt(
|
||||
{ldap_filter, Host}, [],
|
||||
fun check_filter/1, <<"">>) of
|
||||
UserFilter = case ejabberd_config:get_option({ldap_filter, Host}, <<"">>) of
|
||||
<<"">> ->
|
||||
SubFilter;
|
||||
F ->
|
||||
@@ -390,20 +347,8 @@ parse_options(Host) ->
|
||||
SearchFilter = eldap_filter:do_sub(UserFilter,
|
||||
[{<<"%u">>, <<"*">>}]),
|
||||
{DNFilter, DNFilterAttrs} =
|
||||
gen_mod:get_opt({ldap_dn_filter, Host}, [],
|
||||
fun([{DNF, DNFA}]) ->
|
||||
NewDNFA = case DNFA of
|
||||
undefined ->
|
||||
[];
|
||||
_ ->
|
||||
[iolist_to_binary(A)
|
||||
|| A <- DNFA]
|
||||
end,
|
||||
NewDNF = check_filter(DNF),
|
||||
{NewDNF, NewDNFA}
|
||||
end, {undefined, []}),
|
||||
LocalFilter = gen_mod:get_opt(
|
||||
{ldap_local_filter, Host}, [], fun(V) -> V end),
|
||||
ejabberd_config:get_option({ldap_dn_filter, Host}, {undefined, []}),
|
||||
LocalFilter = ejabberd_config:get_option({ldap_local_filter, Host}),
|
||||
#state{host = Host, eldap_id = Eldap_ID,
|
||||
bind_eldap_id = Bind_Eldap_ID,
|
||||
servers = Cfg#eldap_config.servers,
|
||||
@@ -418,31 +363,19 @@ parse_options(Host) ->
|
||||
sfilter = SearchFilter, lfilter = LocalFilter,
|
||||
dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs}.
|
||||
|
||||
check_filter(F) ->
|
||||
NewF = iolist_to_binary(F),
|
||||
{ok, _} = eldap_filter:parse(NewF),
|
||||
NewF.
|
||||
|
||||
-spec opt_type(ldap_dn_filter) -> fun(([{binary(), binary()}]) ->
|
||||
[{binary(), binary()}]);
|
||||
(ldap_local_filter) -> fun((any()) -> any());
|
||||
(atom()) -> [atom()].
|
||||
opt_type(ldap_dn_filter) ->
|
||||
fun ([{DNF, DNFA}]) ->
|
||||
NewDNFA = case DNFA of
|
||||
undefined -> [];
|
||||
_ -> [iolist_to_binary(A) || A <- DNFA]
|
||||
end,
|
||||
NewDNF = check_filter(DNF),
|
||||
NewDNF = eldap_utils:check_filter(DNF),
|
||||
{NewDNF, NewDNFA}
|
||||
end;
|
||||
opt_type(ldap_filter) -> fun check_filter/1;
|
||||
opt_type(ldap_local_filter) -> fun (V) -> V end;
|
||||
opt_type(ldap_uids) ->
|
||||
fun (Us) ->
|
||||
lists:map(fun ({U, P}) ->
|
||||
{iolist_to_binary(U), iolist_to_binary(P)};
|
||||
({U}) -> {iolist_to_binary(U)};
|
||||
(U) -> {iolist_to_binary(U)}
|
||||
end,
|
||||
lists:flatten(Us))
|
||||
end;
|
||||
opt_type(_) ->
|
||||
[ldap_dn_filter, ldap_filter, ldap_local_filter,
|
||||
ldap_uids].
|
||||
[ldap_dn_filter, ldap_local_filter].
|
||||
|
||||
+196
-404
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -27,21 +27,16 @@
|
||||
|
||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
-export([start/1, set_password/3, check_password/4,
|
||||
check_password/6, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, export/1, import/1,
|
||||
import/3, plain_password_required/0, opt_type/1]).
|
||||
-export([start/1, stop/1, set_password/3, try_register/3,
|
||||
get_users/2, init_db/0,
|
||||
count_users/2, get_password/2,
|
||||
remove_user/2, store_type/1, export/1, import/2,
|
||||
plain_password_required/1, use_cache/1]).
|
||||
-export([need_transform/1, transform/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@@ -53,25 +48,27 @@
|
||||
-record(reg_users_counter, {vhost = <<"">> :: binary(),
|
||||
count = 0 :: integer() | '$1'}).
|
||||
|
||||
-define(SALT_LENGTH, 16).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% API
|
||||
%%%----------------------------------------------------------------------
|
||||
start(Host) ->
|
||||
mnesia:create_table(passwd,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
mnesia:create_table(reg_users_counter,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, reg_users_counter)}]),
|
||||
update_table(),
|
||||
init_db(),
|
||||
update_reg_users_counter_table(Host),
|
||||
maybe_alert_password_scrammed_without_option(),
|
||||
ok.
|
||||
|
||||
stop(_Host) ->
|
||||
ok.
|
||||
|
||||
init_db() ->
|
||||
ejabberd_mnesia:create(?MODULE, passwd,
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes, record_info(fields, passwd)}]),
|
||||
ejabberd_mnesia:create(?MODULE, reg_users_counter,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, reg_users_counter)}]).
|
||||
|
||||
update_reg_users_counter_table(Server) ->
|
||||
Set = get_vh_registered_users(Server),
|
||||
Set = get_users(Server, []),
|
||||
Size = length(Set),
|
||||
LServer = jid:nameprep(Server),
|
||||
F = fun () ->
|
||||
@@ -80,395 +77,200 @@ update_reg_users_counter_table(Server) ->
|
||||
end,
|
||||
mnesia:sync_dirty(F).
|
||||
|
||||
plain_password_required() ->
|
||||
is_scrammed().
|
||||
|
||||
store_type() ->
|
||||
case is_scrammed() of
|
||||
false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM
|
||||
true -> scram %% allows: PLAIN SCRAM
|
||||
use_cache(Host) ->
|
||||
case mnesia:table_info(passwd, storage_type) of
|
||||
disc_only_copies ->
|
||||
ejabberd_config:get_option(
|
||||
{auth_use_cache, Host},
|
||||
ejabberd_config:use_cache(Host));
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
check_password(User, AuthzId, Server, Password) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
false;
|
||||
true ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[#passwd{password = Password}] when is_binary(Password) ->
|
||||
Password /= <<"">>;
|
||||
[#passwd{password = Scram}] when is_record(Scram, scram) ->
|
||||
is_password_scram_valid(Password, Scram);
|
||||
_ -> false
|
||||
end
|
||||
end.
|
||||
plain_password_required(Server) ->
|
||||
store_type(Server) == scram.
|
||||
|
||||
check_password(User, AuthzId, Server, Password, Digest,
|
||||
DigestGen) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
false;
|
||||
true ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[#passwd{password = Passwd}] when is_binary(Passwd) ->
|
||||
DigRes = if Digest /= <<"">> ->
|
||||
Digest == DigestGen(Passwd);
|
||||
true -> false
|
||||
end,
|
||||
if DigRes -> true;
|
||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
||||
end;
|
||||
[#passwd{password = Scram}] when is_record(Scram, scram) ->
|
||||
Passwd = jlib:decode_base64(Scram#scram.storedkey),
|
||||
DigRes = if Digest /= <<"">> ->
|
||||
Digest == DigestGen(Passwd);
|
||||
true -> false
|
||||
end,
|
||||
if DigRes -> true;
|
||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
||||
end;
|
||||
_ -> false
|
||||
end
|
||||
end.
|
||||
store_type(Server) ->
|
||||
ejabberd_auth:password_format(Server).
|
||||
|
||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
||||
%% ok | {error, invalid_jid}
|
||||
set_password(User, Server, Password) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
F = fun () ->
|
||||
Password2 = case is_scrammed() and is_binary(Password)
|
||||
of
|
||||
true -> password_to_scram(Password);
|
||||
false -> Password
|
||||
end,
|
||||
mnesia:write(#passwd{us = US, password = Password2})
|
||||
end,
|
||||
{atomic, ok} = mnesia:transaction(F),
|
||||
ok
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} | {error, not_allowed} | {error, Reason}
|
||||
try_register(User, Server, PasswordList) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
Password = if is_list(PasswordList); is_binary(PasswordList) ->
|
||||
iolist_to_binary(PasswordList);
|
||||
true -> PasswordList
|
||||
end,
|
||||
US = {LUser, LServer},
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
F = fun () ->
|
||||
case mnesia:read({passwd, US}) of
|
||||
[] ->
|
||||
Password2 = case is_scrammed() and
|
||||
is_binary(Password)
|
||||
of
|
||||
true -> password_to_scram(Password);
|
||||
false -> Password
|
||||
end,
|
||||
mnesia:write(#passwd{us = US,
|
||||
password = Password2}),
|
||||
mnesia:dirty_update_counter(reg_users_counter,
|
||||
LServer, 1),
|
||||
ok;
|
||||
[_E] -> exists
|
||||
end
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end.
|
||||
|
||||
%% Get all registered users in Mnesia
|
||||
dirty_get_registered_users() ->
|
||||
mnesia:dirty_all_keys(passwd).
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
mnesia:dirty_select(passwd,
|
||||
[{#passwd{us = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, LServer}], ['$1']}]).
|
||||
|
||||
get_vh_registered_users(Server,
|
||||
[{from, Start}, {to, End}])
|
||||
when is_integer(Start) and is_integer(End) ->
|
||||
get_vh_registered_users(Server,
|
||||
[{limit, End - Start + 1}, {offset, Start}]);
|
||||
get_vh_registered_users(Server,
|
||||
[{limit, Limit}, {offset, Offset}])
|
||||
when is_integer(Limit) and is_integer(Offset) ->
|
||||
case get_vh_registered_users(Server) of
|
||||
[] -> [];
|
||||
Users ->
|
||||
Set = lists:keysort(1, Users),
|
||||
L = length(Set),
|
||||
Start = if Offset < 1 -> 1;
|
||||
Offset > L -> L;
|
||||
true -> Offset
|
||||
end,
|
||||
lists:sublist(Set, Start, Limit)
|
||||
end;
|
||||
get_vh_registered_users(Server, [{prefix, Prefix}])
|
||||
when is_binary(Prefix) ->
|
||||
Set = [{U, S}
|
||||
|| {U, S} <- get_vh_registered_users(Server),
|
||||
str:prefix(Prefix, U)],
|
||||
lists:keysort(1, Set);
|
||||
get_vh_registered_users(Server,
|
||||
[{prefix, Prefix}, {from, Start}, {to, End}])
|
||||
when is_binary(Prefix) and is_integer(Start) and
|
||||
is_integer(End) ->
|
||||
get_vh_registered_users(Server,
|
||||
[{prefix, Prefix}, {limit, End - Start + 1},
|
||||
{offset, Start}]);
|
||||
get_vh_registered_users(Server,
|
||||
[{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
|
||||
when is_binary(Prefix) and is_integer(Limit) and
|
||||
is_integer(Offset) ->
|
||||
case [{U, S}
|
||||
|| {U, S} <- get_vh_registered_users(Server),
|
||||
str:prefix(Prefix, U)]
|
||||
of
|
||||
[] -> [];
|
||||
Users ->
|
||||
Set = lists:keysort(1, Users),
|
||||
L = length(Set),
|
||||
Start = if Offset < 1 -> 1;
|
||||
Offset > L -> L;
|
||||
true -> Offset
|
||||
end,
|
||||
lists:sublist(Set, Start, Limit)
|
||||
end;
|
||||
get_vh_registered_users(Server, _) ->
|
||||
get_vh_registered_users(Server).
|
||||
|
||||
get_vh_registered_users_number(Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
Query = mnesia:dirty_select(reg_users_counter,
|
||||
[{#reg_users_counter{vhost = LServer,
|
||||
count = '$1'},
|
||||
[], ['$1']}]),
|
||||
case Query of
|
||||
[Count] -> Count;
|
||||
_ -> 0
|
||||
end.
|
||||
|
||||
get_vh_registered_users_number(Server,
|
||||
[{prefix, Prefix}])
|
||||
when is_binary(Prefix) ->
|
||||
Set = [{U, S}
|
||||
|| {U, S} <- get_vh_registered_users(Server),
|
||||
str:prefix(Prefix, U)],
|
||||
length(Set);
|
||||
get_vh_registered_users_number(Server, _) ->
|
||||
get_vh_registered_users_number(Server).
|
||||
|
||||
get_password(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read(passwd, US) of
|
||||
[#passwd{password = Password}]
|
||||
when is_binary(Password) ->
|
||||
Password;
|
||||
[#passwd{password = Scram}]
|
||||
when is_record(Scram, scram) ->
|
||||
{jlib:decode_base64(Scram#scram.storedkey),
|
||||
jlib:decode_base64(Scram#scram.serverkey),
|
||||
jlib:decode_base64(Scram#scram.salt),
|
||||
Scram#scram.iterationcount};
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read(passwd, US) of
|
||||
[#passwd{password = Password}]
|
||||
when is_binary(Password) ->
|
||||
Password;
|
||||
[#passwd{password = Scram}]
|
||||
when is_record(Scram, scram) ->
|
||||
<<"">>;
|
||||
_ -> <<"">>
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({passwd, US}) of
|
||||
[] -> false;
|
||||
[_] -> true;
|
||||
Other -> {error, Other}
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> ok
|
||||
%% @doc Remove user.
|
||||
%% Note: it returns ok even if there was some problem removing the user.
|
||||
remove_user(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
US = {User, Server},
|
||||
F = fun () ->
|
||||
mnesia:delete({passwd, US}),
|
||||
mnesia:dirty_update_counter(reg_users_counter, LServer,
|
||||
-1)
|
||||
mnesia:write(#passwd{us = US, password = Password})
|
||||
end,
|
||||
mnesia:transaction(F),
|
||||
ok.
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]),
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request
|
||||
%% @doc Remove user if the provided password is correct.
|
||||
remove_user(User, Server, Password) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
try_register(User, Server, Password) ->
|
||||
US = {User, Server},
|
||||
F = fun () ->
|
||||
case mnesia:read({passwd, US}) of
|
||||
[#passwd{password = Password}]
|
||||
when is_binary(Password) ->
|
||||
mnesia:delete({passwd, US}),
|
||||
mnesia:dirty_update_counter(reg_users_counter, LServer,
|
||||
-1),
|
||||
ok;
|
||||
[#passwd{password = Scram}]
|
||||
when is_record(Scram, scram) ->
|
||||
case is_password_scram_valid(Password, Scram) of
|
||||
true ->
|
||||
mnesia:delete({passwd, US}),
|
||||
mnesia:dirty_update_counter(reg_users_counter,
|
||||
LServer, -1),
|
||||
ok;
|
||||
false -> not_allowed
|
||||
end;
|
||||
_ -> not_exists
|
||||
[] ->
|
||||
mnesia:write(#passwd{us = US, password = Password}),
|
||||
mnesia:dirty_update_counter(reg_users_counter, Server, 1),
|
||||
ok;
|
||||
[_] ->
|
||||
{error, exists}
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} -> ok;
|
||||
{atomic, Res} -> Res;
|
||||
_ -> bad_request
|
||||
{atomic, Res} ->
|
||||
Res;
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]),
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
update_table() ->
|
||||
Fields = record_info(fields, passwd),
|
||||
case mnesia:table_info(passwd, attributes) of
|
||||
Fields ->
|
||||
convert_to_binary(Fields),
|
||||
maybe_scram_passwords(),
|
||||
ok;
|
||||
_ ->
|
||||
?INFO_MSG("Recreating passwd table", []),
|
||||
mnesia:transform_table(passwd, ignore, Fields)
|
||||
get_users(Server, []) ->
|
||||
mnesia:dirty_select(passwd,
|
||||
[{#passwd{us = '$1', _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, Server}], ['$1']}]);
|
||||
get_users(Server, [{from, Start}, {to, End}])
|
||||
when is_integer(Start) and is_integer(End) ->
|
||||
get_users(Server, [{limit, End - Start + 1}, {offset, Start}]);
|
||||
get_users(Server, [{limit, Limit}, {offset, Offset}])
|
||||
when is_integer(Limit) and is_integer(Offset) ->
|
||||
case get_users(Server, []) of
|
||||
[] ->
|
||||
[];
|
||||
Users ->
|
||||
Set = lists:keysort(1, Users),
|
||||
L = length(Set),
|
||||
Start = if Offset < 1 -> 1;
|
||||
Offset > L -> L;
|
||||
true -> Offset
|
||||
end,
|
||||
lists:sublist(Set, Start, Limit)
|
||||
end;
|
||||
get_users(Server, [{prefix, Prefix}]) when is_binary(Prefix) ->
|
||||
Set = [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)],
|
||||
lists:keysort(1, Set);
|
||||
get_users(Server, [{prefix, Prefix}, {from, Start}, {to, End}])
|
||||
when is_binary(Prefix) and is_integer(Start) and is_integer(End) ->
|
||||
get_users(Server, [{prefix, Prefix}, {limit, End - Start + 1},
|
||||
{offset, Start}]);
|
||||
get_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
|
||||
when is_binary(Prefix) and is_integer(Limit) and is_integer(Offset) ->
|
||||
case [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)] of
|
||||
[] ->
|
||||
[];
|
||||
Users ->
|
||||
Set = lists:keysort(1, Users),
|
||||
L = length(Set),
|
||||
Start = if Offset < 1 -> 1;
|
||||
Offset > L -> L;
|
||||
true -> Offset
|
||||
end,
|
||||
lists:sublist(Set, Start, Limit)
|
||||
end;
|
||||
get_users(Server, _) ->
|
||||
get_users(Server, []).
|
||||
|
||||
count_users(Server, []) ->
|
||||
case mnesia:dirty_select(
|
||||
reg_users_counter,
|
||||
[{#reg_users_counter{vhost = Server, count = '$1'},
|
||||
[], ['$1']}]) of
|
||||
[Count] -> Count;
|
||||
_ -> 0
|
||||
end;
|
||||
count_users(Server, [{prefix, Prefix}]) when is_binary(Prefix) ->
|
||||
Set = [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)],
|
||||
length(Set);
|
||||
count_users(Server, _) ->
|
||||
count_users(Server, []).
|
||||
|
||||
get_password(User, Server) ->
|
||||
case mnesia:dirty_read(passwd, {User, Server}) of
|
||||
[#passwd{password = Password}] ->
|
||||
{ok, Password};
|
||||
_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
convert_to_binary(Fields) ->
|
||||
ejabberd_config:convert_table_to_binary(
|
||||
passwd, Fields, set,
|
||||
fun(#passwd{us = {U, _}}) -> U end,
|
||||
fun(#passwd{us = {U, S}, password = Pass} = R) ->
|
||||
NewUS = {iolist_to_binary(U), iolist_to_binary(S)},
|
||||
NewPass = case Pass of
|
||||
#scram{storedkey = StoredKey,
|
||||
serverkey = ServerKey,
|
||||
salt = Salt} ->
|
||||
Pass#scram{
|
||||
storedkey = iolist_to_binary(StoredKey),
|
||||
serverkey = iolist_to_binary(ServerKey),
|
||||
salt = iolist_to_binary(Salt)};
|
||||
_ ->
|
||||
iolist_to_binary(Pass)
|
||||
end,
|
||||
R#passwd{us = NewUS, password = NewPass}
|
||||
end).
|
||||
|
||||
%%%
|
||||
%%% SCRAM
|
||||
%%%
|
||||
|
||||
%% The passwords are stored scrammed in the table either if the option says so,
|
||||
%% or if at least the first password is scrammed.
|
||||
is_scrammed() ->
|
||||
OptionScram = is_option_scram(),
|
||||
FirstElement = mnesia:dirty_read(passwd,
|
||||
mnesia:dirty_first(passwd)),
|
||||
case {OptionScram, FirstElement} of
|
||||
{true, _} -> true;
|
||||
{false, [#passwd{password = Scram}]}
|
||||
when is_record(Scram, scram) ->
|
||||
true;
|
||||
_ -> false
|
||||
remove_user(User, Server) ->
|
||||
US = {User, Server},
|
||||
F = fun () ->
|
||||
mnesia:delete({passwd, US}),
|
||||
mnesia:dirty_update_counter(reg_users_counter, Server, -1),
|
||||
ok
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, ok} ->
|
||||
ok;
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]),
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
is_option_scram() ->
|
||||
scram ==
|
||||
ejabberd_config:get_option({auth_password_format, ?MYNAME},
|
||||
fun(V) -> V end).
|
||||
|
||||
maybe_alert_password_scrammed_without_option() ->
|
||||
case is_scrammed() andalso not is_option_scram() of
|
||||
true ->
|
||||
?ERROR_MSG("Some passwords were stored in the database "
|
||||
"as SCRAM, but 'auth_password_format' "
|
||||
"is not configured 'scram'. The option "
|
||||
"will now be considered to be 'scram'.",
|
||||
[]);
|
||||
false -> ok
|
||||
need_transform(#reg_users_counter{}) ->
|
||||
false;
|
||||
need_transform(#passwd{us = {U, S}, password = Pass}) ->
|
||||
if is_binary(Pass) ->
|
||||
case store_type(S) of
|
||||
scram ->
|
||||
?INFO_MSG("Passwords in Mnesia table 'passwd' "
|
||||
"will be SCRAM'ed", []),
|
||||
true;
|
||||
plain ->
|
||||
false
|
||||
end;
|
||||
is_record(Pass, scram) ->
|
||||
case store_type(S) of
|
||||
scram ->
|
||||
false;
|
||||
plain ->
|
||||
?WARNING_MSG("Some passwords were stored in the database "
|
||||
"as SCRAM, but 'auth_password_format' "
|
||||
"is not configured as 'scram': some "
|
||||
"authentication mechanisms such as DIGEST-MD5 "
|
||||
"would *fail*", []),
|
||||
false
|
||||
end;
|
||||
is_list(U) orelse is_list(S) orelse is_list(Pass) ->
|
||||
?INFO_MSG("Mnesia table 'passwd' will be converted to binary", []),
|
||||
true
|
||||
end.
|
||||
|
||||
maybe_scram_passwords() ->
|
||||
case is_scrammed() of
|
||||
true -> scram_passwords();
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
scram_passwords() ->
|
||||
?INFO_MSG("Converting the stored passwords into "
|
||||
"SCRAM bits",
|
||||
[]),
|
||||
Fun = fun (#passwd{password = Password} = P) ->
|
||||
Scram = password_to_scram(Password),
|
||||
P#passwd{password = Scram}
|
||||
end,
|
||||
Fields = record_info(fields, passwd),
|
||||
mnesia:transform_table(passwd, Fun, Fields).
|
||||
|
||||
password_to_scram(Password) ->
|
||||
password_to_scram(Password,
|
||||
?SCRAM_DEFAULT_ITERATION_COUNT).
|
||||
|
||||
password_to_scram(Password, IterationCount) ->
|
||||
Salt = crypto:rand_bytes(?SALT_LENGTH),
|
||||
SaltedPassword = scram:salted_password(Password, Salt,
|
||||
IterationCount),
|
||||
StoredKey =
|
||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
ServerKey = scram:server_key(SaltedPassword),
|
||||
#scram{storedkey = jlib:encode_base64(StoredKey),
|
||||
serverkey = jlib:encode_base64(ServerKey),
|
||||
salt = jlib:encode_base64(Salt),
|
||||
iterationcount = IterationCount}.
|
||||
|
||||
is_password_scram_valid(Password, Scram) ->
|
||||
IterationCount = Scram#scram.iterationcount,
|
||||
Salt = jlib:decode_base64(Scram#scram.salt),
|
||||
SaltedPassword = scram:salted_password(Password, Salt,
|
||||
IterationCount),
|
||||
StoredKey =
|
||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
jlib:decode_base64(Scram#scram.storedkey) == StoredKey.
|
||||
transform(#passwd{us = {U, S}, password = Pass} = R)
|
||||
when is_list(U) orelse is_list(S) orelse is_list(Pass) ->
|
||||
NewUS = {iolist_to_binary(U), iolist_to_binary(S)},
|
||||
NewPass = case Pass of
|
||||
#scram{storedkey = StoredKey,
|
||||
serverkey = ServerKey,
|
||||
salt = Salt} ->
|
||||
Pass#scram{
|
||||
storedkey = iolist_to_binary(StoredKey),
|
||||
serverkey = iolist_to_binary(ServerKey),
|
||||
salt = iolist_to_binary(Salt)};
|
||||
_ ->
|
||||
iolist_to_binary(Pass)
|
||||
end,
|
||||
transform(R#passwd{us = NewUS, password = NewPass});
|
||||
transform(#passwd{us = {U, S}, password = Password} = P)
|
||||
when is_binary(Password) ->
|
||||
case store_type(S) of
|
||||
scram ->
|
||||
case jid:resourceprep(Password) of
|
||||
error ->
|
||||
?ERROR_MSG("SASLprep failed for password of user ~s@~s",
|
||||
[U, S]),
|
||||
P;
|
||||
_ ->
|
||||
Scram = ejabberd_auth:password_to_scram(Password),
|
||||
P#passwd{password = Scram}
|
||||
end;
|
||||
plain ->
|
||||
P
|
||||
end;
|
||||
transform(#passwd{password = Password} = P)
|
||||
when is_record(Password, scram) ->
|
||||
P.
|
||||
|
||||
export(_Server) ->
|
||||
[{passwd,
|
||||
@@ -493,16 +295,6 @@ export(_Server) ->
|
||||
[]
|
||||
end}].
|
||||
|
||||
import(LServer) ->
|
||||
[{<<"select username, password from users;">>,
|
||||
fun([LUser, Password]) ->
|
||||
#passwd{us = {LUser, LServer}, password = Password}
|
||||
end}].
|
||||
|
||||
import(_LServer, mnesia, #passwd{} = P) ->
|
||||
mnesia:dirty_write(P);
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
|
||||
opt_type(auth_password_format) -> fun (V) -> V end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
import(LServer, [LUser, Password, _TimeStamp]) ->
|
||||
mnesia:dirty_write(
|
||||
#passwd{us = {LUser, LServer}, password = Password}).
|
||||
|
||||
+16
-53
@@ -5,7 +5,7 @@
|
||||
%%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -30,25 +30,15 @@
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
-export([start/1, set_password/3, check_password/4,
|
||||
check_password/6, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, plain_password_required/0,
|
||||
-export([start/1, stop/1, check_password/4,
|
||||
user_exists/2, store_type/1, plain_password_required/1,
|
||||
opt_type/1]).
|
||||
|
||||
start(_Host) ->
|
||||
ejabberd:start_app(p1_pam).
|
||||
ejabberd:start_app(epam).
|
||||
|
||||
set_password(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
check_password(User, AuthzId, Server, Password, _Digest,
|
||||
_DigestGen) ->
|
||||
check_password(User, AuthzId, Server, Password).
|
||||
stop(_Host) ->
|
||||
ok.
|
||||
|
||||
check_password(User, AuthzId, Host, Password) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
@@ -67,26 +57,7 @@ check_password(User, AuthzId, Host, Password) ->
|
||||
end
|
||||
end.
|
||||
|
||||
try_register(_User, _Server, _Password) ->
|
||||
{error, not_allowed}.
|
||||
|
||||
dirty_get_registered_users() -> [].
|
||||
|
||||
get_vh_registered_users(_Host) -> [].
|
||||
|
||||
get_vh_registered_users(_Host, _) -> [].
|
||||
|
||||
get_vh_registered_users_number(_Host) -> 0.
|
||||
|
||||
get_vh_registered_users_number(_Host, _) -> 0.
|
||||
|
||||
get_password(_User, _Server) -> false.
|
||||
|
||||
get_password_s(_User, _Server) -> <<"">>.
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
%% TODO: Improve this function to return an error instead of 'false' when connection to PAM failed
|
||||
is_user_exists(User, Host) ->
|
||||
user_exists(User, Host) ->
|
||||
Service = get_pam_service(Host),
|
||||
UserInfo = case get_pam_userinfotype(Host) of
|
||||
username -> User;
|
||||
@@ -94,34 +65,26 @@ is_user_exists(User, Host) ->
|
||||
end,
|
||||
case catch epam:acct_mgmt(Service, UserInfo) of
|
||||
true -> true;
|
||||
_ -> false
|
||||
false -> false;
|
||||
_Err -> {error, db_failure}
|
||||
end.
|
||||
|
||||
remove_user(_User, _Server) -> {error, not_allowed}.
|
||||
plain_password_required(_) -> true.
|
||||
|
||||
remove_user(_User, _Server, _Password) -> not_allowed.
|
||||
|
||||
plain_password_required() -> true.
|
||||
|
||||
store_type() -> external.
|
||||
store_type(_) -> external.
|
||||
|
||||
%%====================================================================
|
||||
%% Internal functions
|
||||
%%====================================================================
|
||||
get_pam_service(Host) ->
|
||||
ejabberd_config:get_option(
|
||||
{pam_service, Host},
|
||||
fun iolist_to_binary/1,
|
||||
<<"ejabberd">>).
|
||||
ejabberd_config:get_option({pam_service, Host}, <<"ejabberd">>).
|
||||
|
||||
get_pam_userinfotype(Host) ->
|
||||
ejabberd_config:get_option(
|
||||
{pam_userinfotype, Host},
|
||||
fun(username) -> username;
|
||||
(jid) -> jid
|
||||
end,
|
||||
username).
|
||||
ejabberd_config:get_option({pam_userinfotype, Host}, username).
|
||||
|
||||
-spec opt_type(pam_service) -> fun((binary()) -> binary());
|
||||
(pam_userinfotype) -> fun((username | jid) -> username | jid);
|
||||
(atom()) -> [atom()].
|
||||
opt_type(pam_service) -> fun iolist_to_binary/1;
|
||||
opt_type(pam_userinfotype) ->
|
||||
fun (username) -> username;
|
||||
|
||||
+40
-227
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Nov 2012 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -32,15 +32,10 @@
|
||||
-behaviour(ejabberd_auth).
|
||||
|
||||
%% External exports
|
||||
-export([start/1, set_password/3, check_password/4,
|
||||
check_password/6, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, export/1, import/3,
|
||||
plain_password_required/0]).
|
||||
-export([start/1, stop/1, set_password/3, try_register/3,
|
||||
get_users/2, count_users/2,
|
||||
get_password/2, remove_user/2, store_type/1, export/1, import/2,
|
||||
plain_password_required/1]).
|
||||
-export([passwd_schema/0]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -49,246 +44,65 @@
|
||||
-record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1',
|
||||
password = <<"">> :: binary() | scram() | '_'}).
|
||||
|
||||
-define(SALT_LENGTH, 16).
|
||||
|
||||
start(_Host) ->
|
||||
ok.
|
||||
|
||||
plain_password_required() ->
|
||||
case is_scrammed() of
|
||||
false -> false;
|
||||
true -> true
|
||||
end.
|
||||
stop(_Host) ->
|
||||
ok.
|
||||
|
||||
store_type() ->
|
||||
case is_scrammed() of
|
||||
false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM
|
||||
true -> scram %% allows: PLAIN SCRAM
|
||||
end.
|
||||
plain_password_required(Server) ->
|
||||
store_type(Server) == scram.
|
||||
|
||||
store_type(Server) ->
|
||||
ejabberd_auth:password_format(Server).
|
||||
|
||||
passwd_schema() ->
|
||||
{record_info(fields, passwd), #passwd{}}.
|
||||
|
||||
check_password(User, AuthzId, Server, Password) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
false;
|
||||
true ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
||||
{ok, #passwd{password = Password}} when is_binary(Password) ->
|
||||
Password /= <<"">>;
|
||||
{ok, #passwd{password = Scram}} when is_record(Scram, scram) ->
|
||||
is_password_scram_valid(Password, Scram);
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end.
|
||||
|
||||
check_password(User, AuthzId, Server, Password, Digest,
|
||||
DigestGen) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
false;
|
||||
true ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
||||
{ok, #passwd{password = Passwd}} when is_binary(Passwd) ->
|
||||
DigRes = if Digest /= <<"">> ->
|
||||
Digest == DigestGen(Passwd);
|
||||
true -> false
|
||||
end,
|
||||
if DigRes -> true;
|
||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
||||
end;
|
||||
{ok, #passwd{password = Scram}}
|
||||
when is_record(Scram, scram) ->
|
||||
Passwd = jlib:decode_base64(Scram#scram.storedkey),
|
||||
DigRes = if Digest /= <<"">> ->
|
||||
Digest == DigestGen(Passwd);
|
||||
true -> false
|
||||
end,
|
||||
if DigRes -> true;
|
||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
||||
end;
|
||||
_ -> false
|
||||
end
|
||||
end.
|
||||
|
||||
set_password(User, Server, Password) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
US = {LUser, LServer},
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
Password2 = case is_scrammed() and is_binary(Password)
|
||||
of
|
||||
true -> password_to_scram(Password);
|
||||
false -> Password
|
||||
end,
|
||||
ok = ejabberd_riak:put(#passwd{us = US, password = Password2},
|
||||
passwd_schema(),
|
||||
[{'2i', [{<<"host">>, LServer}]}])
|
||||
ejabberd_riak:put(#passwd{us = {User, Server}, password = Password},
|
||||
passwd_schema(),
|
||||
[{'2i', [{<<"host">>, Server}]}]).
|
||||
|
||||
try_register(User, Server, Password) ->
|
||||
US = {User, Server},
|
||||
case ejabberd_riak:get(passwd, passwd_schema(), US) of
|
||||
{error, notfound} ->
|
||||
ejabberd_riak:put(#passwd{us = US, password = Password},
|
||||
passwd_schema(),
|
||||
[{'2i', [{<<"host">>, Server}]}]);
|
||||
{ok, _} ->
|
||||
{error, exists};
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
try_register(User, Server, PasswordList) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
Password = if is_list(PasswordList); is_binary(PasswordList) ->
|
||||
iolist_to_binary(PasswordList);
|
||||
true -> PasswordList
|
||||
end,
|
||||
US = {LUser, LServer},
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
case ejabberd_riak:get(passwd, passwd_schema(), US) of
|
||||
{error, notfound} ->
|
||||
Password2 = case is_scrammed() and
|
||||
is_binary(Password)
|
||||
of
|
||||
true -> password_to_scram(Password);
|
||||
false -> Password
|
||||
end,
|
||||
{atomic, ejabberd_riak:put(
|
||||
#passwd{us = US,
|
||||
password = Password2},
|
||||
passwd_schema(),
|
||||
[{'2i', [{<<"host">>, LServer}]}])};
|
||||
{ok, _} ->
|
||||
exists;
|
||||
Err ->
|
||||
{atomic, Err}
|
||||
end
|
||||
end.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
lists:flatmap(
|
||||
fun(Server) ->
|
||||
get_vh_registered_users(Server)
|
||||
end, ejabberd_config:get_vh_by_auth_method(riak)).
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
case ejabberd_riak:get_keys_by_index(passwd, <<"host">>, LServer) of
|
||||
get_users(Server, _) ->
|
||||
case ejabberd_riak:get_keys_by_index(passwd, <<"host">>, Server) of
|
||||
{ok, Users} ->
|
||||
Users;
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
get_vh_registered_users(Server, _) ->
|
||||
get_vh_registered_users(Server).
|
||||
|
||||
get_vh_registered_users_number(Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
case ejabberd_riak:count_by_index(passwd, <<"host">>, LServer) of
|
||||
count_users(Server, _) ->
|
||||
case ejabberd_riak:count_by_index(passwd, <<"host">>, Server) of
|
||||
{ok, N} ->
|
||||
N;
|
||||
_ ->
|
||||
0
|
||||
end.
|
||||
|
||||
get_vh_registered_users_number(Server, _) ->
|
||||
get_vh_registered_users_number(Server).
|
||||
|
||||
get_password(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
||||
{ok, #passwd{password = Password}}
|
||||
when is_binary(Password) ->
|
||||
Password;
|
||||
{ok, #passwd{password = Scram}}
|
||||
when is_record(Scram, scram) ->
|
||||
{jlib:decode_base64(Scram#scram.storedkey),
|
||||
jlib:decode_base64(Scram#scram.serverkey),
|
||||
jlib:decode_base64(Scram#scram.salt),
|
||||
Scram#scram.iterationcount};
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
||||
{ok, #passwd{password = Password}}
|
||||
when is_binary(Password) ->
|
||||
Password;
|
||||
{ok, #passwd{password = Scram}}
|
||||
when is_record(Scram, scram) ->
|
||||
<<"">>;
|
||||
_ -> <<"">>
|
||||
end.
|
||||
|
||||
is_user_exists(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
||||
{error, notfound} -> false;
|
||||
{ok, _} -> true;
|
||||
Err -> Err
|
||||
case ejabberd_riak:get(passwd, passwd_schema(), {User, Server}) of
|
||||
{ok, Password} ->
|
||||
{ok, Password};
|
||||
{error, _} ->
|
||||
error
|
||||
end.
|
||||
|
||||
remove_user(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
ejabberd_riak:delete(passwd, {LUser, LServer}),
|
||||
ok.
|
||||
|
||||
remove_user(User, Server, Password) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
||||
{ok, #passwd{password = Password}}
|
||||
when is_binary(Password) ->
|
||||
ejabberd_riak:delete(passwd, {LUser, LServer}),
|
||||
ok;
|
||||
{ok, #passwd{password = Scram}}
|
||||
when is_record(Scram, scram) ->
|
||||
case is_password_scram_valid(Password, Scram) of
|
||||
true ->
|
||||
ejabberd_riak:delete(passwd, {LUser, LServer}),
|
||||
ok;
|
||||
false -> not_allowed
|
||||
end;
|
||||
_ -> not_exists
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% SCRAM
|
||||
%%%
|
||||
|
||||
is_scrammed() ->
|
||||
scram ==
|
||||
ejabberd_config:get_local_option({auth_password_format, ?MYNAME},
|
||||
fun(V) -> V end).
|
||||
|
||||
password_to_scram(Password) ->
|
||||
password_to_scram(Password,
|
||||
?SCRAM_DEFAULT_ITERATION_COUNT).
|
||||
|
||||
password_to_scram(Password, IterationCount) ->
|
||||
Salt = crypto:rand_bytes(?SALT_LENGTH),
|
||||
SaltedPassword = scram:salted_password(Password, Salt,
|
||||
IterationCount),
|
||||
StoredKey =
|
||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
ServerKey = scram:server_key(SaltedPassword),
|
||||
#scram{storedkey = jlib:encode_base64(StoredKey),
|
||||
serverkey = jlib:encode_base64(ServerKey),
|
||||
salt = jlib:encode_base64(Salt),
|
||||
iterationcount = IterationCount}.
|
||||
|
||||
is_password_scram_valid(Password, Scram) ->
|
||||
IterationCount = Scram#scram.iterationcount,
|
||||
Salt = jlib:decode_base64(Scram#scram.salt),
|
||||
SaltedPassword = scram:salted_password(Password, Salt,
|
||||
IterationCount),
|
||||
StoredKey =
|
||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
jlib:decode_base64(Scram#scram.storedkey) == StoredKey.
|
||||
ejabberd_riak:delete(passwd, {User, Server}).
|
||||
|
||||
export(_Server) ->
|
||||
[{passwd,
|
||||
@@ -301,7 +115,6 @@ export(_Server) ->
|
||||
[]
|
||||
end}].
|
||||
|
||||
import(LServer, riak, #passwd{} = Passwd) ->
|
||||
ejabberd_riak:put(Passwd, passwd_schema(), [{'2i', [{<<"host">>, LServer}]}]);
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
import(LServer, [LUser, Password, _TimeStamp]) ->
|
||||
Passwd = #passwd{us = {LUser, LServer}, password = Password},
|
||||
ejabberd_riak:put(Passwd, passwd_schema(), [{'2i', [{<<"host">>, LServer}]}]).
|
||||
|
||||
+188
-384
@@ -5,7 +5,7 @@
|
||||
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
@@ -27,20 +27,14 @@
|
||||
|
||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-behaviour(ejabberd_auth).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-export([start/1, set_password/3, check_password/4,
|
||||
check_password/6, try_register/3,
|
||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
||||
get_vh_registered_users/2,
|
||||
get_vh_registered_users_number/1,
|
||||
get_vh_registered_users_number/2, get_password/2,
|
||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
||||
remove_user/3, store_type/0, plain_password_required/0,
|
||||
-export([start/1, stop/1, set_password/3, try_register/3,
|
||||
get_users/2, count_users/2, get_password/2,
|
||||
remove_user/2, store_type/1, plain_password_required/1,
|
||||
convert_to_scram/1, opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@@ -54,387 +48,88 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
start(_Host) -> ok.
|
||||
|
||||
plain_password_required() ->
|
||||
case is_scrammed() of
|
||||
false -> false;
|
||||
true -> true
|
||||
end.
|
||||
stop(_Host) -> ok.
|
||||
|
||||
store_type() ->
|
||||
case is_scrammed() of
|
||||
false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM
|
||||
true -> scram %% allows: PLAIN SCRAM
|
||||
end.
|
||||
plain_password_required(Server) ->
|
||||
store_type(Server) == scram.
|
||||
|
||||
%% @spec (User, AuthzId, Server, Password) -> true | false | {error, Error}
|
||||
check_password(User, AuthzId, Server, Password) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
false;
|
||||
true ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
false;
|
||||
(LUser == <<>>) or (LServer == <<>>) ->
|
||||
false;
|
||||
true ->
|
||||
case is_scrammed() of
|
||||
true ->
|
||||
try sql_queries:get_password_scram(LServer, LUser) of
|
||||
{selected,
|
||||
[{StoredKey, ServerKey, Salt, IterationCount}]} ->
|
||||
Scram =
|
||||
#scram{storedkey = StoredKey,
|
||||
serverkey = ServerKey,
|
||||
salt = Salt,
|
||||
iterationcount = IterationCount},
|
||||
is_password_scram_valid_stored(Password, Scram, LUser, LServer);
|
||||
{selected, []} ->
|
||||
false; %% Account does not exist
|
||||
{error, _Error} ->
|
||||
false %% Typical error is that table doesn't exist
|
||||
catch
|
||||
_:_ ->
|
||||
false %% Typical error is database not accessible
|
||||
end;
|
||||
false ->
|
||||
try sql_queries:get_password(LServer, LUser) of
|
||||
{selected, [{Password}]} ->
|
||||
Password /= <<"">>;
|
||||
{selected, [{_Password2}]} ->
|
||||
false; %% Password is not correct
|
||||
{selected, []} ->
|
||||
false; %% Account does not exist
|
||||
{error, _Error} ->
|
||||
false %% Typical error is that table doesn't exist
|
||||
catch
|
||||
_:_ ->
|
||||
false %% Typical error is database not accessible
|
||||
end
|
||||
end
|
||||
end
|
||||
end.
|
||||
store_type(Server) ->
|
||||
ejabberd_auth:password_format(Server).
|
||||
|
||||
%% @spec (User, AuthzId, Server, Password, Digest, DigestGen) -> true | false | {error, Error}
|
||||
check_password(User, AuthzId, Server, Password, Digest,
|
||||
DigestGen) ->
|
||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||
false;
|
||||
true ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
false;
|
||||
(LUser == <<>>) or (LServer == <<>>) ->
|
||||
false;
|
||||
true ->
|
||||
case is_scrammed() of
|
||||
false ->
|
||||
try sql_queries:get_password(LServer, LUser) of
|
||||
%% Account exists, check if password is valid
|
||||
{selected, [{Passwd}]} ->
|
||||
DigRes = if Digest /= <<"">> ->
|
||||
Digest == DigestGen(Passwd);
|
||||
true -> false
|
||||
end,
|
||||
if DigRes -> true;
|
||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
||||
end;
|
||||
{selected, []} ->
|
||||
false; %% Account does not exist
|
||||
{error, _Error} ->
|
||||
false %% Typical error is that table doesn't exist
|
||||
catch
|
||||
_:_ ->
|
||||
false %% Typical error is database not accessible
|
||||
end;
|
||||
true ->
|
||||
false
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
||||
%% ok | {error, invalid_jid}
|
||||
set_password(User, Server, Password) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
(LUser == <<>>) or (LServer == <<>>) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
case is_scrammed() of
|
||||
true ->
|
||||
Scram = password_to_scram(Password),
|
||||
case catch sql_queries:set_password_scram_t(
|
||||
LServer,
|
||||
LUser,
|
||||
Scram#scram.storedkey,
|
||||
Scram#scram.serverkey,
|
||||
Scram#scram.salt,
|
||||
Scram#scram.iterationcount
|
||||
)
|
||||
of
|
||||
{atomic, ok} -> ok;
|
||||
Other -> {error, Other}
|
||||
end;
|
||||
false ->
|
||||
case catch sql_queries:set_password_t(LServer,
|
||||
LUser, Password)
|
||||
of
|
||||
{atomic, ok} -> ok;
|
||||
Other -> {error, Other}
|
||||
end
|
||||
end
|
||||
F = fun() ->
|
||||
if is_record(Password, scram) ->
|
||||
set_password_scram_t(
|
||||
User,
|
||||
Password#scram.storedkey, Password#scram.serverkey,
|
||||
Password#scram.salt, Password#scram.iterationcount);
|
||||
true ->
|
||||
set_password_t(User, Password)
|
||||
end
|
||||
end,
|
||||
case ejabberd_sql:sql_transaction(Server, F) of
|
||||
{atomic, _} ->
|
||||
ok;
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("failed to write to SQL table: ~p", [Reason]),
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid}
|
||||
try_register(User, Server, Password) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
{error, invalid_jid};
|
||||
(LUser == <<>>) or (LServer == <<>>) ->
|
||||
{error, invalid_jid};
|
||||
true ->
|
||||
case is_scrammed() of
|
||||
true ->
|
||||
Scram = password_to_scram(Password),
|
||||
case catch sql_queries:add_user_scram(
|
||||
LServer,
|
||||
LUser,
|
||||
Scram#scram.storedkey,
|
||||
Scram#scram.serverkey,
|
||||
Scram#scram.salt,
|
||||
Scram#scram.iterationcount
|
||||
) of
|
||||
{updated, 1} -> {atomic, ok};
|
||||
_ -> {atomic, exists}
|
||||
end;
|
||||
false ->
|
||||
case catch sql_queries:add_user(LServer, LUser,
|
||||
Password) of
|
||||
{updated, 1} -> {atomic, ok};
|
||||
_ -> {atomic, exists}
|
||||
end
|
||||
end
|
||||
Res = if is_record(Password, scram) ->
|
||||
add_user_scram(
|
||||
Server, User,
|
||||
Password#scram.storedkey, Password#scram.serverkey,
|
||||
Password#scram.salt, Password#scram.iterationcount);
|
||||
true ->
|
||||
add_user(Server, User, Password)
|
||||
end,
|
||||
case Res of
|
||||
{updated, 1} -> ok;
|
||||
_ -> {error, exists}
|
||||
end.
|
||||
|
||||
dirty_get_registered_users() ->
|
||||
Servers = ejabberd_config:get_vh_by_auth_method(sql),
|
||||
lists:flatmap(fun (Server) ->
|
||||
get_vh_registered_users(Server)
|
||||
end,
|
||||
Servers).
|
||||
|
||||
get_vh_registered_users(Server) ->
|
||||
case jid:nameprep(Server) of
|
||||
error -> [];
|
||||
<<>> -> [];
|
||||
LServer ->
|
||||
case catch sql_queries:list_users(LServer) of
|
||||
{selected, Res} ->
|
||||
[{U, LServer} || {U} <- Res];
|
||||
_ -> []
|
||||
end
|
||||
get_users(Server, Opts) ->
|
||||
case list_users(Server, Opts) of
|
||||
{selected, Res} ->
|
||||
[{U, Server} || {U} <- Res];
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
get_vh_registered_users(Server, Opts) ->
|
||||
case jid:nameprep(Server) of
|
||||
error -> [];
|
||||
<<>> -> [];
|
||||
LServer ->
|
||||
case catch sql_queries:list_users(LServer, Opts) of
|
||||
{selected, Res} ->
|
||||
[{U, LServer} || {U} <- Res];
|
||||
_ -> []
|
||||
end
|
||||
end.
|
||||
|
||||
get_vh_registered_users_number(Server) ->
|
||||
case jid:nameprep(Server) of
|
||||
error -> 0;
|
||||
<<>> -> 0;
|
||||
LServer ->
|
||||
case catch sql_queries:users_number(LServer) of
|
||||
{selected, [{Res}]} ->
|
||||
Res;
|
||||
_ -> 0
|
||||
end
|
||||
end.
|
||||
|
||||
get_vh_registered_users_number(Server, Opts) ->
|
||||
case jid:nameprep(Server) of
|
||||
error -> 0;
|
||||
<<>> -> 0;
|
||||
LServer ->
|
||||
case catch sql_queries:users_number(LServer, Opts) of
|
||||
{selected, [{Res}]} ->
|
||||
Res;
|
||||
_Other -> 0
|
||||
end
|
||||
count_users(Server, Opts) ->
|
||||
case users_number(Server, Opts) of
|
||||
{selected, [{Res}]} ->
|
||||
Res;
|
||||
_Other -> 0
|
||||
end.
|
||||
|
||||
get_password(User, Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
false;
|
||||
(LUser == <<>>) or (LServer == <<>>) ->
|
||||
false;
|
||||
true ->
|
||||
case is_scrammed() of
|
||||
true ->
|
||||
case catch sql_queries:get_password_scram(
|
||||
LServer, LUser) of
|
||||
{selected,
|
||||
[{StoredKey, ServerKey, Salt, IterationCount}]} ->
|
||||
{jlib:decode_base64(StoredKey),
|
||||
jlib:decode_base64(ServerKey),
|
||||
jlib:decode_base64(Salt),
|
||||
IterationCount};
|
||||
_ -> false
|
||||
end;
|
||||
false ->
|
||||
case catch sql_queries:get_password(LServer, LUser)
|
||||
of
|
||||
{selected, [{Password}]} -> Password;
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
case get_password_scram(Server, User) of
|
||||
{selected, [{Password, <<>>, <<>>, 0}]} ->
|
||||
{ok, Password};
|
||||
{selected, [{StoredKey, ServerKey, Salt, IterationCount}]} ->
|
||||
{ok, #scram{storedkey = StoredKey,
|
||||
serverkey = ServerKey,
|
||||
salt = Salt,
|
||||
iterationcount = IterationCount}};
|
||||
{selected, []} ->
|
||||
error;
|
||||
Err ->
|
||||
?ERROR_MSG("Failed to read password for user ~s@~s: ~p",
|
||||
[User, Server, Err]),
|
||||
error
|
||||
end.
|
||||
|
||||
get_password_s(User, Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
<<"">>;
|
||||
(LUser == <<>>) or (LServer == <<>>) ->
|
||||
<<"">>;
|
||||
true ->
|
||||
case is_scrammed() of
|
||||
false ->
|
||||
case catch sql_queries:get_password(LServer, LUser) of
|
||||
{selected, [{Password}]} -> Password;
|
||||
_ -> <<"">>
|
||||
end;
|
||||
true -> <<"">>
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> true | false | {error, Error}
|
||||
is_user_exists(User, Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
false;
|
||||
(LUser == <<>>) or (LServer == <<>>) ->
|
||||
false;
|
||||
true ->
|
||||
try sql_queries:get_password(LServer, LUser) of
|
||||
{selected, [{_Password}]} ->
|
||||
true; %% Account exists
|
||||
{selected, []} ->
|
||||
false; %% Account does not exist
|
||||
{error, Error} -> {error, Error}
|
||||
catch
|
||||
_:B -> {error, B}
|
||||
end
|
||||
end.
|
||||
|
||||
%% @spec (User, Server) -> ok | error
|
||||
%% @doc Remove user.
|
||||
%% Note: it may return ok even if there was some problem removing the user.
|
||||
remove_user(User, Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
error;
|
||||
(LUser == <<>>) or (LServer == <<>>) ->
|
||||
error;
|
||||
true ->
|
||||
catch sql_queries:del_user(LServer, LUser),
|
||||
ok
|
||||
case del_user(Server, User) of
|
||||
{updated, _} ->
|
||||
ok;
|
||||
Err ->
|
||||
?ERROR_MSG("failed to delete user ~s@~s: ~p",
|
||||
[User, Server, Err]),
|
||||
{error, db_failure}
|
||||
end.
|
||||
|
||||
%% @spec (User, Server, Password) -> ok | error | not_exists | not_allowed
|
||||
%% @doc Remove user if the provided password is correct.
|
||||
remove_user(User, Server, Password) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
LUser = jid:nodeprep(User),
|
||||
if (LUser == error) or (LServer == error) ->
|
||||
error;
|
||||
(LUser == <<>>) or (LServer == <<>>) ->
|
||||
error;
|
||||
true ->
|
||||
case is_scrammed() of
|
||||
true ->
|
||||
case check_password(User, <<"">>, Server, Password) of
|
||||
true ->
|
||||
remove_user(User, Server),
|
||||
ok;
|
||||
false -> not_allowed
|
||||
end;
|
||||
false ->
|
||||
F = fun () ->
|
||||
Result = sql_queries:del_user_return_password(
|
||||
LServer, LUser, Password),
|
||||
case Result of
|
||||
{selected, [{Password}]} -> ok;
|
||||
{selected, []} -> not_exists;
|
||||
_ -> not_allowed
|
||||
end
|
||||
end,
|
||||
{atomic, Result} = sql_queries:sql_transaction(
|
||||
LServer, F),
|
||||
Result
|
||||
end
|
||||
end.
|
||||
|
||||
%%%
|
||||
%%% SCRAM
|
||||
%%%
|
||||
|
||||
is_scrammed() ->
|
||||
scram ==
|
||||
ejabberd_config:get_option({auth_password_format, ?MYNAME},
|
||||
fun(V) -> V end).
|
||||
|
||||
password_to_scram(Password) ->
|
||||
password_to_scram(Password,
|
||||
?SCRAM_DEFAULT_ITERATION_COUNT).
|
||||
|
||||
password_to_scram(Password, IterationCount) ->
|
||||
Salt = crypto:rand_bytes(?SALT_LENGTH),
|
||||
SaltedPassword = scram:salted_password(Password, Salt,
|
||||
IterationCount),
|
||||
StoredKey =
|
||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
ServerKey = scram:server_key(SaltedPassword),
|
||||
#scram{storedkey = jlib:encode_base64(StoredKey),
|
||||
serverkey = jlib:encode_base64(ServerKey),
|
||||
salt = jlib:encode_base64(Salt),
|
||||
iterationcount = IterationCount}.
|
||||
|
||||
is_password_scram_valid_stored(Pass, {scram,Pass,<<>>,<<>>,0}, LUser, LServer) ->
|
||||
?INFO_MSG("Apparently, SQL auth method and scram password formatting are "
|
||||
"enabled, but the password of user '~s' in the 'users' table is not "
|
||||
"scrammed. You may want to execute this command: "
|
||||
"ejabberdctl convert_to_scram ~s", [LUser, LServer]),
|
||||
false;
|
||||
is_password_scram_valid_stored(Password, Scram, _, _) ->
|
||||
is_password_scram_valid(Password, Scram).
|
||||
|
||||
is_password_scram_valid(Password, Scram) ->
|
||||
IterationCount = Scram#scram.iterationcount,
|
||||
Salt = jlib:decode_base64(Scram#scram.salt),
|
||||
SaltedPassword = scram:salted_password(Password, Salt,
|
||||
IterationCount),
|
||||
StoredKey =
|
||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
||||
jlib:decode_base64(Scram#scram.storedkey) == StoredKey.
|
||||
|
||||
-define(BATCH_SIZE, 1000).
|
||||
|
||||
set_password_scram_t(LUser,
|
||||
@@ -447,6 +142,105 @@ set_password_scram_t(LUser,
|
||||
"salt=%(Salt)s",
|
||||
"iterationcount=%(IterationCount)d"]).
|
||||
|
||||
set_password_t(LUser, Password) ->
|
||||
?SQL_UPSERT_T(
|
||||
"users",
|
||||
["!username=%(LUser)s",
|
||||
"password=%(Password)s"]).
|
||||
|
||||
get_password_scram(LServer, LUser) ->
|
||||
ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("select @(password)s, @(serverkey)s, @(salt)s, @(iterationcount)d"
|
||||
" from users"
|
||||
" where username=%(LUser)s")).
|
||||
|
||||
add_user_scram(LServer, LUser,
|
||||
StoredKey, ServerKey, Salt, IterationCount) ->
|
||||
ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("insert into users(username, password, serverkey, salt, "
|
||||
"iterationcount) "
|
||||
"values (%(LUser)s, %(StoredKey)s, %(ServerKey)s,"
|
||||
" %(Salt)s, %(IterationCount)d)")).
|
||||
|
||||
add_user(LServer, LUser, Password) ->
|
||||
ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("insert into users(username, password) "
|
||||
"values (%(LUser)s, %(Password)s)")).
|
||||
|
||||
del_user(LServer, LUser) ->
|
||||
ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("delete from users where username=%(LUser)s")).
|
||||
|
||||
list_users(LServer, []) ->
|
||||
ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("select @(username)s from users"));
|
||||
list_users(LServer, [{from, Start}, {to, End}])
|
||||
when is_integer(Start) and is_integer(End) ->
|
||||
list_users(LServer,
|
||||
[{limit, End - Start + 1}, {offset, Start - 1}]);
|
||||
list_users(LServer,
|
||||
[{prefix, Prefix}, {from, Start}, {to, End}])
|
||||
when is_binary(Prefix) and is_integer(Start) and
|
||||
is_integer(End) ->
|
||||
list_users(LServer,
|
||||
[{prefix, Prefix}, {limit, End - Start + 1},
|
||||
{offset, Start - 1}]);
|
||||
list_users(LServer, [{limit, Limit}, {offset, Offset}])
|
||||
when is_integer(Limit) and is_integer(Offset) ->
|
||||
ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("select @(username)s from users "
|
||||
"order by username "
|
||||
"limit %(Limit)d offset %(Offset)d"));
|
||||
list_users(LServer,
|
||||
[{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
|
||||
when is_binary(Prefix) and is_integer(Limit) and
|
||||
is_integer(Offset) ->
|
||||
SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix),
|
||||
SPrefix2 = <<SPrefix/binary, $%>>,
|
||||
ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("select @(username)s from users "
|
||||
"where username like %(SPrefix2)s escape '^' "
|
||||
"order by username "
|
||||
"limit %(Limit)d offset %(Offset)d")).
|
||||
|
||||
users_number(LServer) ->
|
||||
ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
fun(pgsql, _) ->
|
||||
case
|
||||
ejabberd_config:get_option(
|
||||
{pgsql_users_number_estimate, LServer}, false) of
|
||||
true ->
|
||||
ejabberd_sql:sql_query_t(
|
||||
?SQL("select @(reltuples :: bigint)d from pg_class"
|
||||
" where oid = 'users'::regclass::oid"));
|
||||
_ ->
|
||||
ejabberd_sql:sql_query_t(
|
||||
?SQL("select @(count(*))d from users"))
|
||||
end;
|
||||
(_Type, _) ->
|
||||
ejabberd_sql:sql_query_t(
|
||||
?SQL("select @(count(*))d from users"))
|
||||
end).
|
||||
|
||||
users_number(LServer, [{prefix, Prefix}])
|
||||
when is_binary(Prefix) ->
|
||||
SPrefix = ejabberd_sql:escape_like_arg_circumflex(Prefix),
|
||||
SPrefix2 = <<SPrefix/binary, $%>>,
|
||||
ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("select @(count(*))d from users "
|
||||
"where username like %(SPrefix2)s escape '^'"));
|
||||
users_number(LServer, []) ->
|
||||
users_number(LServer).
|
||||
|
||||
convert_to_scram(Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
if
|
||||
@@ -466,20 +260,27 @@ convert_to_scram(Server) ->
|
||||
{selected, Rs} ->
|
||||
lists:foreach(
|
||||
fun({LUser, Password}) ->
|
||||
Scram = password_to_scram(Password),
|
||||
set_password_scram_t(
|
||||
LUser,
|
||||
Scram#scram.storedkey,
|
||||
Scram#scram.serverkey,
|
||||
Scram#scram.salt,
|
||||
Scram#scram.iterationcount
|
||||
)
|
||||
case jid:resourceprep(Password) of
|
||||
error ->
|
||||
?ERROR_MSG(
|
||||
"SASLprep failed for "
|
||||
"password of user ~s@~s",
|
||||
[LUser, LServer]);
|
||||
_ ->
|
||||
Scram = ejabberd_auth:password_to_scram(Password),
|
||||
set_password_scram_t(
|
||||
LUser,
|
||||
Scram#scram.storedkey,
|
||||
Scram#scram.serverkey,
|
||||
Scram#scram.salt,
|
||||
Scram#scram.iterationcount)
|
||||
end
|
||||
end, Rs),
|
||||
continue;
|
||||
Err -> {bad_reply, Err}
|
||||
end
|
||||
end,
|
||||
case sql_queries:sql_transaction(LServer, F) of
|
||||
case ejabberd_sql:sql_transaction(LServer, F) of
|
||||
{atomic, ok} -> ok;
|
||||
{atomic, continue} -> convert_to_scram(Server);
|
||||
{atomic, Error} -> {error, Error};
|
||||
@@ -487,5 +288,8 @@ convert_to_scram(Server) ->
|
||||
end
|
||||
end.
|
||||
|
||||
opt_type(auth_password_format) -> fun (V) -> V end;
|
||||
opt_type(_) -> [auth_password_format].
|
||||
-spec opt_type(pgsql_users_number_estimate) -> fun((boolean()) -> boolean());
|
||||
(atom()) -> [atom()].
|
||||
opt_type(pgsql_users_number_estimate) ->
|
||||
fun (V) when is_boolean(V) -> V end;
|
||||
opt_type(_) -> [pgsql_users_number_estimate].
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user