Compare commits
496 Commits
Author | SHA1 | Date |
---|---|---|
Asterisk Development Team | e528299031 | |
Asterisk Development Team | b40f0f6fce | |
George Joseph | 7129752201 | |
Asterisk Development Team | 1eee96543b | |
Asterisk Development Team | c4703f070a | |
Alexandre Fournier | c900a7dc39 | |
Ben Ford | 1c8f57e298 | |
George Joseph | 69c2459c33 | |
Mike Bradeen | 764ca33473 | |
Mike Bradeen | 42ecf402a3 | |
Naveen Albert | e553546eff | |
Mike Bradeen | d7dae7b1fc | |
Igor Goncharovsky | 09af7e0aca | |
Naveen Albert | 025dbcfd68 | |
Henning Westerholt | a6aebab79b | |
Naveen Albert | 05a7b4132a | |
Naveen Albert | a0dd8c27b5 | |
Frederic LE FOLL | 83498bec35 | |
Naveen Albert | 3bae133afa | |
Naveen Albert | 8bea5052db | |
Naveen Albert | 57b1f5a7c3 | |
Philip Prindeville | 23a4135fe7 | |
Mike Bradeen | f61dbd566b | |
Naveen Albert | b397dc3ca3 | |
Naveen Albert | f9297117d9 | |
Naveen Albert | 11d97dc5ca | |
Philip Prindeville | 44684fdd51 | |
Philip Prindeville | 109c2335ef | |
George Joseph | 54cafbc67f | |
Maximilian Fridrich | 6170073800 | |
Asterisk Development Team | 2209afddb9 | |
George Joseph | 95a25fddac | |
Holger Hans Peter Freyther | 02be2a5f1a | |
Naveen Albert | aefb9fc216 | |
Naveen Albert | 0a0b141278 | |
Naveen Albert | 9ba789d297 | |
Naveen Albert | 231f99b397 | |
Naveen Albert | 999b162786 | |
Naveen Albert | fd5683f74b | |
Philip Prindeville | 1d6e7c6843 | |
Naveen Albert | 5199a70c07 | |
Maximilian Fridrich | 2efcb5890e | |
Jaco Kroon | ef20afda63 | |
Naveen Albert | 086b1abf66 | |
Naveen Albert | 0bf6d7af33 | |
George Joseph | f09b9e6678 | |
George Joseph | c2a343b8c9 | |
Philip Prindeville | 70489083b1 | |
Asterisk Development Team | 6d8d367722 | |
Mike Bradeen | 9227fb4d52 | |
George Joseph | 7beedd85e1 | |
sungtae kim | bdf8ef8882 | |
Ben Ford | 94731d815a | |
Philip Prindeville | a5fb810f5f | |
Philip Prindeville | 63e7832f26 | |
Philip Prindeville | 6f7280a4ca | |
Philip Prindeville | a11495affd | |
Philip Prindeville | b89130c184 | |
Philip Prindeville | 55328b1e5c | |
Philip Prindeville | 81bcceca5a | |
Naveen Albert | 62264edde2 | |
Naveen Albert | 1456dc757b | |
Naveen Albert | f4ea243e49 | |
George Joseph | e8220afbd8 | |
George Joseph | a9c6f4bd54 | |
George Joseph | 833b9319a4 | |
George Joseph | a8997a6896 | |
Joshua C. Colp | 273dd16c3c | |
Naveen Albert | 116ec0b9bb | |
Joshua C. Colp | 03bda8845c | |
Naveen Albert | 5f712c0060 | |
Mike Bradeen | b6c953dcf7 | |
Sean Bright | ddc6d1f796 | |
Alexei Gradinari | 9eadb789d5 | |
Sean Bright | 217766b706 | |
Naveen Albert | a5d5c3a92c | |
Mike Bradeen | 1390a247a6 | |
Mike Bradeen | f22516a4ac | |
Naveen Albert | 409cc1f36e | |
Asterisk Development Team | c40b0134ce | |
George Joseph | 4846dd5d18 | |
Naveen Albert | bb7ffce381 | |
Naveen Albert | ba5febd14c | |
George Joseph | df4ba41731 | |
Naveen Albert | d09ccb17ce | |
Naveen Albert | eccc4593b0 | |
Naveen Albert | 890cbf4293 | |
Naveen Albert | 865582f582 | |
Naveen Albert | 9be41593f0 | |
Naveen Albert | eb316354a9 | |
Naveen Albert | 419932691b | |
Asterisk Development Team | 3814cf5703 | |
Naveen Albert | f6c3c7d8f8 | |
Sergey V. Lobanov | 085b71ccad | |
Naveen Albert | f7e39e2eca | |
Naveen Albert | 3b05eee506 | |
Michael Neuhauser | 477f6ec887 | |
Sam Banks | 35bb9c0970 | |
Naveen Albert | 0bd21cc88f | |
Naveen Albert | d2746b6e99 | |
Mike Bradeen | ba51a05a6f | |
Naveen Albert | 6f9d07ecb9 | |
Sean Bright | fbd84f9e74 | |
George Joseph | 6163de3295 | |
George Joseph | 6c2f4d57a3 | |
George Joseph | abacf2d1a4 | |
Naveen Albert | 7448443f21 | |
Kevin Harwell | b7599a380d | |
Naveen Albert | f6cac9d0ab | |
Joshua C. Colp | 311e3e11e2 | |
Naveen Albert | daa81f6359 | |
Trevor Peirce | a19fc264b4 | |
George Joseph | 104e53aecd | |
Boris P. Korzun | a692e79ec6 | |
Jose Lopes | 71adb35c00 | |
Boris P. Korzun | cd9b332118 | |
Naveen Albert | 2619350741 | |
Naveen Albert | d0ee3652be | |
Naveen Albert | 55fece6198 | |
Naveen Albert | 5b730d0bc8 | |
Naveen Albert | 90b403adee | |
Naveen Albert | 1b93cf97fc | |
Kevin Harwell | b47d3be483 | |
Naveen Albert | e23ed9bf06 | |
Stanislav Abramenkov | 28d8647d4d | |
Asterisk Development Team | df5bc6468f | |
Naveen Albert | 8fe7eb35f2 | |
Kevin Harwell | 4367f1ac41 | |
Naveen Albert | 00b44f1c25 | |
Kevin Harwell | 49433ac1c5 | |
Naveen Albert | efbcab8d65 | |
Alexei Gradinari | b3671a55f0 | |
Alexei Gradinari | a852fe9402 | |
Naveen Albert | f88cc626af | |
Trevor Peirce | 6436d82654 | |
Naveen Albert | 7f892d03a5 | |
Christof Efkemann | e92f047e5f | |
Naveen Albert | d8bbcf4b25 | |
Naveen Albert | 05a0b08222 | |
Naveen Albert | 6e88448e80 | |
Naveen Albert | 61bc3a8aa1 | |
Naveen Albert | b86ce7fbb9 | |
Maximilian Fridrich | 318d6b02ee | |
Alexei Gradinari | 1576004e07 | |
Shloime Rosenblum | d1f32e75d7 | |
Sean Bright | 29165bf2e1 | |
Moritz Fain | de838c241b | |
Sean Bright | 8db9a02962 | |
Joshua C. Colp | dc1e446ebd | |
Naveen Albert | a143c37fc0 | |
Thomas Guebels | d457838b77 | |
Naveen Albert | 46d395c248 | |
Naveen Albert | 2b8086e382 | |
George Joseph | 910d9caf86 | |
George Joseph | 3c96566655 | |
Maximilian Fridrich | 4fec26923e | |
Naveen Albert | 0c3f7c98e5 | |
Naveen Albert | 09e9efc4c6 | |
Michael Cargile | 72c8c263e8 | |
Naveen Albert | 2391af167b | |
Naveen Albert | aece339a22 | |
Naveen Albert | 2ba9ba349f | |
Naveen Albert | 1e29935131 | |
Naveen Albert | 7d80c8a49c | |
Naveen Albert | 45ed328d08 | |
Naveen Albert | 2c6ae41a97 | |
Naveen Albert | 942db8c58d | |
Naveen Albert | bbc27f835b | |
Naveen Albert | a3e7b2d010 | |
Naveen Albert | dc570ae623 | |
Naveen Albert | ea401644fe | |
Mark Petersen | 40df42fd03 | |
Naveen Albert | 06756a1608 | |
Mark Petersen | 9ffc2f711c | |
Mark Petersen | 73fb22714e | |
Yury Kirsanov | 1937f30202 | |
Kevin Harwell | d1bfbb65eb | |
Joshua C. Colp | 18d2f273b5 | |
Ben Ford | b42934d019 | |
Joshua C. Colp | 8584ad425a | |
Maximilian Fridrich | 5d3125fa71 | |
Sean Bright | f565982e0e | |
Joshua C. Colp | c7ca797922 | |
Joshua C. Colp | ebbbe78842 | |
Asterisk Development Team | 25431555d1 | |
Ben Ford | aba30b11a6 | |
Joshua C. Colp | 1e3ffda3db | |
Ben Ford | ca53a8a833 | |
Naveen Albert | 1873b7ece6 | |
Naveen Albert | 3707f2e144 | |
Naveen Albert | 7a81f3ad35 | |
Boris P. Korzun | dae30a301f | |
Naveen Albert | 96e0b79938 | |
Kevin Harwell | 629ced6db0 | |
Joshua C. Colp | c812ad62f7 | |
Naveen Albert | 3e3e3f3ce5 | |
George Joseph | a3a9a48734 | |
George Joseph | 56a8d47586 | |
Naveen Albert | 6474550175 | |
Sean Bright | 939a7c0dc3 | |
Marcel Wagner | 459d6f7ad2 | |
Naveen Albert | 0bf75aa0a2 | |
Naveen Albert | 56ed64e086 | |
Hugh McMaster | d132184520 | |
Philip Prindeville | ed8ca6c38a | |
Sean Bright | bf825a5050 | |
Philip Prindeville | 70a958a051 | |
Naveen Albert | 297ceeb9e4 | |
Sean Bright | b15d2f9e4b | |
Sean Bright | 5a30f0c6fe | |
Alexei Gradinari | 4027626709 | |
Naveen Albert | c5b49f1405 | |
Asterisk Development Team | 96f98908f9 | |
Ben Ford | 0c07f94c00 | |
Alexei Gradinari | 49928fee53 | |
Boris P. Korzun | 00a7fa985e | |
Kfir Itzhak | 5edbc54c54 | |
Kevin Harwell | b81db677c9 | |
Kevin Harwell | f9cd83ff35 | |
Kevin Harwell | ac8de6e31d | |
George Joseph | 1dc792a312 | |
George Joseph | d00108df72 | |
George Joseph | 38df1c35ee | |
George Joseph | e29cd99975 | |
George Joseph | ca18e68470 | |
Joshua C. Colp | 3d30f68fbc | |
Naveen Albert | 8a94702386 | |
Naveen Albert | ff603d16c7 | |
Naveen Albert | 51146db5fe | |
Naveen Albert | e16fce2251 | |
Naveen Albert | d5c72f35fd | |
Naveen Albert | eb77229b53 | |
Naveen Albert | 4cb4a3ed01 | |
Naveen Albert | 19481fc1f0 | |
Alexei Gradinari | 76f90bff75 | |
Naveen Albert | 257590b4e2 | |
Alexei Gradinari | e85ee05e9d | |
Naveen Albert | 57685c139b | |
Naveen Albert | e8f8d84ffd | |
Naveen Albert | b54060021f | |
Naveen Albert | ba2f780930 | |
Mike Bradeen | 5764414d10 | |
Alexei Gradinari | 5f22f586ff | |
Sean Bright | 67eb7c9e85 | |
Mark Petersen | 97ac3c1385 | |
Sean Bright | dfbb547f6a | |
Sean Bright | b42dd930f4 | |
Asterisk Development Team | 3d390c4df7 | |
Sean Bright | 8b4531051b | |
George Joseph | 3f108867d6 | |
Mark Petersen | 25edc8ba47 | |
George Joseph | 8bc6511c0c | |
George Joseph | 4142e548e8 | |
Naveen Albert | 1a8d320a29 | |
Naveen Albert | cf17b3d211 | |
Naveen Albert | d107ebd94a | |
Torrey Searle | 34065c7d84 | |
Sean Bright | 8d1858e570 | |
Kevin Harwell | 9f5c1d30ed | |
Mike Bradeen | c1ca028a0b | |
Mark Petersen | 3522b07b64 | |
Luke Escude | c3d571f335 | |
Michał Górny | 43661122f5 | |
Michał Górny | de420bffa7 | |
Michał Górny | a7f7e5ab76 | |
Michał Górny | addb7278d4 | |
Naveen Albert | c452cc3f22 | |
Michał Górny | e32aec6c09 | |
Naveen Albert | d3bd4a3a73 | |
George Joseph | 3a18886803 | |
George Joseph | eb1e20cf89 | |
George Joseph | f41cce9c12 | |
George Joseph | a611c07dd4 | |
George Joseph | 1ce57621de | |
Sean Bright | 101a72d048 | |
Naveen Albert | 23c7c101ed | |
Naveen Albert | 84a596da5f | |
George Joseph | d6cdfb8204 | |
Sean Bright | 95fe90c746 | |
Naveen Albert | 8b6fca363e | |
Sean Bright | cdd9b11a2d | |
Mark Petersen | 3f8ed7202f | |
Naveen Albert | 09a0a952c6 | |
Naveen Albert | 4794582c92 | |
Naveen Albert | 4dcd77f6cc | |
Naveen Albert | dc7daf57b0 | |
Naveen Albert | 3e7e1d1d05 | |
Kevin Harwell | 935b8cb04e | |
Naveen Albert | 584118c7f0 | |
Steve Davies | 12d2c09dec | |
Sean Bright | c1d0e23b10 | |
Florentin Mayer | 9eacfda84d | |
Joshua C. Colp | 220e885bcc | |
Josh Soref | b738e119f2 | |
George Joseph | 09b70525e0 | |
Frederic Van Espen | 669b50983f | |
Naveen Albert | 5452ab997d | |
Mark Petersen | 476b8aa4e4 | |
Kevin Harwell | df355eeb87 | |
Naveen Albert | ee163da56d | |
Mark Petersen | c8c9496600 | |
Naveen Albert | e2e38798f0 | |
Naveen Albert | bf3043ac14 | |
Naveen Albert | eaa3e32f0c | |
Naveen Albert | 10f50f1e30 | |
Naveen Albert | 0f9bf737b2 | |
Naveen Albert | d93a776476 | |
Naveen Albert | 828eb997b1 | |
Sean Bright | a507049eb5 | |
Naveen Albert | 50716bb3e4 | |
Alexander Traud | e3ff42aee6 | |
Alexander Traud | 6cda1db595 | |
Alexander Traud | 81a9b566e8 | |
Alexander Traud | 8261e0f0da | |
Alexander Traud | bba2f14c59 | |
Sean Bright | 4195949e07 | |
Mike Bradeen | b0a3951849 | |
Mark Petersen | fc321db1f8 | |
Alexander Traud | f872750add | |
Dustin Marquess | 78e19885e8 | |
Alexander Traud | edaf3a6c77 | |
Asterisk Development Team | 8d0852552b | |
Alexander Traud | 30d2c99698 | |
Alexander Traud | fb08c717aa | |
Naveen Albert | bfac8fbeed | |
Sean Bright | dbc1a15146 | |
Naveen Albert | 05032c7cab | |
Alexander Traud | 657252b132 | |
Alexander Traud | 275ea6c111 | |
Alexander Traud | 849daee910 | |
Jaco Kroon | bee73b7c84 | |
Alexander Traud | e8c18eeb28 | |
Mike Bradeen | 07d73dbc76 | |
Joshua C. Colp | 426ee9a607 | |
Naveen Albert | fbf03832da | |
Alexander Traud | c37e1ceb5a | |
Boris P. Korzun | f800f23d9c | |
Alexander Traud | 5aec2bfee1 | |
Alexander Traud | 015c594fcc | |
Alexander Traud | aaa1509fad | |
Alexander Traud | 0119f59ac3 | |
Alexander Traud | fa3c079c55 | |
Alexander Traud | 14c309a6c5 | |
Alexander Traud | 244790e040 | |
Alexander Traud | 72e1234168 | |
Alexander Traud | 76a019c377 | |
Alexander Traud | a44fa27db4 | |
Alexander Traud | f71c0e7500 | |
Alexander Traud | 52b99811a8 | |
Alexander Traud | 46162d2d64 | |
Alexander Traud | 0bcf649a37 | |
Alexander Traud | 95976be293 | |
Alexander Traud | d2a8d37af4 | |
Alexander Traud | 6e0ce3fde3 | |
Alexander Traud | 2ff4b7dd2f | |
Alexander Traud | dfdbf5007d | |
Josh Soref | 8c0b7fd45a | |
Josh Soref | bff338bf88 | |
Naveen Albert | 02b7af8cdf | |
Naveen Albert | 45c132a375 | |
Naveen Albert | b2256ea993 | |
Josh Soref | 22db079aaf | |
Josh Soref | bf7b327edd | |
Josh Soref | 6b03525827 | |
Josh Soref | acf74178b1 | |
Josh Soref | d081fc1784 | |
Josh Soref | 99b079f551 | |
Josh Soref | ccf5835e49 | |
Josh Soref | 02f40c1f01 | |
Josh Soref | b6295840d7 | |
Josh Soref | 50a43ab987 | |
Josh Soref | 706258db54 | |
Josh Soref | c03a3e4d4a | |
Josh Soref | 617cb9dbc4 | |
Josh Soref | 08a3eae879 | |
Josh Soref | 0c7c6a3d5d | |
Josh Soref | 5ad9ec2447 | |
Josh Soref | ec877e0c27 | |
Josh Soref | 31aaceac01 | |
Josh Soref | 09691e2bfb | |
Josh Soref | 49e317924e | |
Josh Soref | c06342a3cb | |
Josh Soref | 42ba751f5a | |
George Joseph | fa839616fd | |
Alexander Traud | 9970b8df7c | |
Naveen Albert | 53fff3ec7c | |
Sean Bright | a7ce06b098 | |
Naveen Albert | 656880a4b1 | |
Naveen Albert | cf422d35a5 | |
Alexander Traud | 9660d2e182 | |
Alexander Traud | 85049ab55a | |
Alexander Traud | 5e5afe2a5b | |
Sean Bright | c206203a5c | |
George Joseph | 9ff0c31335 | |
Kevin Harwell | ed384e652c | |
Ben Ford | ba3f6c0b1e | |
Rodrigo Ramírez Norambuena | d81d5ad832 | |
Kevin Harwell | cf0fa9b82f | |
Mike Bradeen | 6b094e905b | |
Shloime Rosenblum | 154c592799 | |
George Joseph | 2b9cddc7d0 | |
Sean Bright | 093fabba84 | |
Mike Bradeen | 702484431d | |
Sean Bright | ce9cb32307 | |
Sebastien Duthil | 4bc7a5ac53 | |
Asterisk Development Team | 9ff955f4d1 | |
Sean Bright | 9175012a12 | |
Mark Murawski | 1f5ac24fa3 | |
Naveen Albert | 32ea7c7ca5 | |
Matthew Kern | 9d04535bbd | |
Naveen Albert | 60bbfe4572 | |
Jean Aunis | 576119e076 | |
Shloime Rosenblum | f3ff893310 | |
Joseph Nadiv | 6a04c43035 | |
Joshua C. Colp | 35a94ec708 | |
Naveen Albert | 13ec117595 | |
Sean Bright | 52b5821694 | |
Naveen Albert | f38c7d67d3 | |
Naveen Albert | eff78c8549 | |
Sean Bright | ff493d6f7d | |
Naveen Albert | eb874f92db | |
Sean Bright | 245778a756 | |
Guido Falsi | 675adbf0f5 | |
George Joseph | 3d6e133ccf | |
Carlos Oliva | ad1f7fae70 | |
Naveen Albert | 203e73f5af | |
Naveen Albert | f8bf5e7b47 | |
Naveen Albert | 5fe3a745e4 | |
Sean Bright | f26505d615 | |
Naveen Albert | d5a53efb4f | |
Sungtae Kim | 4d9ba65c53 | |
Sean Bright | 085cc94f16 | |
Naveen Albert | 71b021433f | |
Naveen Albert | 0b8ae58e67 | |
Naveen Albert | a94b51ee60 | |
George Joseph | df63a99337 | |
Sean Bright | 61136fd297 | |
Sean Bright | f67b72093e | |
Jasper Hafkenscheid | f1e1f9f37f | |
Sean Bright | 5a5ea06ffc | |
George Joseph | 0070b9184c | |
sungtae kim | 3c31b6aaa2 | |
Sean Bright | 16b0f460f6 | |
Naveen Albert | 29770520b3 | |
Mark Murawski | 185321066f | |
Naveen Albert | 0e4a1c5079 | |
Sebastien Duthil | 18189ff594 | |
Naveen Albert | 4301fe20d1 | |
Naveen Albert | 9e947b0463 | |
Alexander Traud | f22b413ece | |
Alexander Traud | e65e1c5c6c | |
Sarah Autumn | db4a3b117d | |
George Joseph | a662d75556 | |
Andre Barbosa | 2451dfd89f | |
Naveen Albert | e01a6c026d | |
Naveen Albert | d6034df64a | |
Naveen Albert | b5044586f7 | |
Naveen Albert | 3f9ef427b5 | |
Naveen Albert | 2394757e55 | |
Alexander Traud | 73e2288db7 | |
Naveen Albert | 432fe9dc2a | |
Joshua C. Colp | ecf699c325 | |
Joshua C. Colp | daca793ad4 | |
Joshua C. Colp | 650cf0b444 | |
Joshua C. Colp | 368aa47962 | |
Joshua C. Colp | 9d5f55a5f3 | |
Joshua C. Colp | 72a2140a50 | |
Joshua C. Colp | 7b0d3d3550 | |
Joshua C. Colp | 7361a52820 | |
Joshua C. Colp | d0ad32c7cf | |
Joshua C. Colp | e4b6f24a1d | |
Joshua C. Colp | f18107f191 | |
Joshua C. Colp | b1e5b1874c | |
Joshua C. Colp | 7ee6fb0372 | |
Joshua C. Colp | 0b3a149001 | |
Joshua C. Colp | 41afcb9422 | |
Joshua C. Colp | 83cad340fc | |
Joshua C. Colp | 1961a1b83e | |
Joshua C. Colp | 3e07b1ff62 | |
Sean Bright | 41ed46f474 | |
Joshua C. Colp | 141dc519b0 | |
Naveen Albert | 7383f74dfc | |
Alexander Traud | 835ab50724 | |
Kevin Harwell | 37f7d19c8c | |
Naveen Albert | 4c49c84dee | |
Naveen Albert | 0975cff6c0 | |
Igor Goncharovsky | ac958b0f50 | |
Rijnhard Hessel | f13eef719c | |
Sean Bright | 382143e58e | |
under | ff8ca2c9f1 | |
Naveen Albert | 6645cf8d45 | |
Joshua C. Colp | 90c9c90b11 | |
Kevin Harwell | 56f9c28a50 | |
Joshua C. Colp | 45af7e9984 | |
Kevin Harwell | 151bdbc658 | |
Ben Ford | 0ac346ec47 |
|
@ -37,4 +37,4 @@ doxygen.log
|
|||
out/
|
||||
*.orig
|
||||
tests/CI/output
|
||||
|
||||
.develvars
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[gerrit]
|
||||
defaultbranch=master
|
||||
defaultbranch=19
|
||||
basebranch=19
|
||||
#
|
||||
# Intentional padding to ensure it is possible to point a commit
|
||||
# to an alternative gerrit server/repository without breaking
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
40
|
2
CREDITS
2
CREDITS
|
@ -40,7 +40,7 @@
|
|||
rewrite of SIP transfers
|
||||
|
||||
|
||||
=== WISHLIST CONTRIBUTERS ===
|
||||
=== WISHLIST CONTRIBUTORS ===
|
||||
|
||||
We'd like to thank the following for contributing to our wishlist
|
||||
|
||||
|
|
3
LICENSE
3
LICENSE
|
@ -45,7 +45,7 @@ redistribution of Asterisk source code obtained from Digium, you
|
|||
should contact our licensing department to determine the necessary
|
||||
steps you must take. For more information on this policy, please read:
|
||||
|
||||
http://www.digium.com/en/company/profile/trademarkpolicy.php
|
||||
https://www.sangoma.com/wp-content/uploads/Sangoma-Trademark-Policy.pdf
|
||||
|
||||
If you have any questions regarding our licensing policy, please
|
||||
contact us:
|
||||
|
@ -53,7 +53,6 @@ contact us:
|
|||
+1.877.344.4861 (via telephone in the USA)
|
||||
+1.256.428.6000 (via telephone outside the USA)
|
||||
+1.256.864.0464 (via FAX inside or outside the USA)
|
||||
IAX2/pbx.digium.com (via IAX2)
|
||||
licensing@digium.com (via email)
|
||||
|
||||
Digium, Inc.
|
||||
|
|
127
Makefile
127
Makefile
|
@ -21,7 +21,7 @@
|
|||
# on a single object just for that object
|
||||
# SOLINK - linker flags used only for creating dynamically loadable modules
|
||||
# as .so files
|
||||
# DYLINK - linker flags used only for creating shared libaries
|
||||
# DYLINK - linker flags used only for creating shared libraries
|
||||
# (.so files on Unix-type platforms, .dylib on Darwin)
|
||||
#
|
||||
# Values for ASTCFLAGS and ASTLDFLAGS can be specified in the
|
||||
|
@ -101,6 +101,11 @@ export TAR
|
|||
export PATCH
|
||||
export SED
|
||||
export NM
|
||||
export FIND
|
||||
export BASENAME
|
||||
export DIRNAME
|
||||
export XMLLINT
|
||||
export XMLSTARLET
|
||||
|
||||
# makeopts is required unless the goal is just {dist{-}}clean
|
||||
ifeq ($(MAKECMDGOALS),clean)
|
||||
|
@ -135,7 +140,7 @@ empty:=
|
|||
space:=$(empty) $(empty)
|
||||
ASTTOPDIR:=$(subst $(space),\$(space),$(CURDIR))
|
||||
|
||||
# Overwite config files on "make samples" or other config installation targets
|
||||
# Overwrite config files on "make samples" or other config installation targets
|
||||
OVERWRITE=y
|
||||
|
||||
# Include debug and macro symbols in the executables (-g) and profiling info (-pg)
|
||||
|
@ -322,6 +327,9 @@ else
|
|||
SUBMAKE:=$(MAKE) --quiet --no-print-directory
|
||||
endif
|
||||
|
||||
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
mkfile_dir := $(dir $(mkfile_path))
|
||||
|
||||
# $(MAKE) is printed in several places, and we want it to be a
|
||||
# fixed size string. Define a variable whose name has also the
|
||||
# same size, so we can easily align text.
|
||||
|
@ -442,7 +450,7 @@ distclean: $(SUBDIRS_DIST_CLEAN) _clean
|
|||
rm -f include/asterisk/autoconfig.h
|
||||
rm -f include/asterisk/buildopts.h
|
||||
rm -rf doc/api
|
||||
rm -f doc/asterisk-ng-doxygen
|
||||
rm -f doc/Doxyfile
|
||||
rm -f build_tools/menuselect-deps
|
||||
|
||||
datafiles: _all $(CORE_XMLDOC)
|
||||
|
@ -475,60 +483,24 @@ endif
|
|||
$(INSTALL) -m 644 $$x "$(DESTDIR)$(ASTDATADIR)/rest-api" ; \
|
||||
done
|
||||
|
||||
ifeq ($(GREP),)
|
||||
else ifeq ($(GREP),:)
|
||||
else
|
||||
XML_core_en_US = $(foreach dir,$(MOD_SUBDIRS),$(shell $(GREP) -l "language=\"en_US\"" $(dir)/*.c $(dir)/*.cc 2>/dev/null))
|
||||
endif
|
||||
|
||||
DOC_MOD_SUBDIRS := $(filter-out third-party,$(MOD_SUBDIRS))
|
||||
XML_core_en_US := $(shell build_tools/make_xml_documentation --command=print_dependencies --source-tree=. --mod-subdirs="$(DOC_MOD_SUBDIRS)")
|
||||
# core-en_US.xml is the normal documentation created with asterisk builds.
|
||||
doc/core-en_US.xml: makeopts .lastclean $(XML_core_en_US)
|
||||
@printf "Building Documentation For: "
|
||||
@echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $@
|
||||
@echo "<!DOCTYPE docs SYSTEM \"appdocsxml.dtd\">" >> $@
|
||||
@echo "<?xml-stylesheet type=\"text/xsl\" href=\"appdocsxml.xslt\"?>" >> $@
|
||||
@echo "<docs xmlns:xi=\"http://www.w3.org/2001/XInclude\">" >> $@
|
||||
@for x in $(MOD_SUBDIRS); do \
|
||||
printf "$$x " ; \
|
||||
for i in `find $$x -name '*.c'`; do \
|
||||
MODULEINFO=$$($(AWK) -f build_tools/get_moduleinfo $$i) ; \
|
||||
if [ -n "$$MODULEINFO" ] ; \
|
||||
then \
|
||||
echo "<module language=\"en_US\" name=\"`$(BASENAME) -s .c $$i`\">" >> $@ ; \
|
||||
echo "$$MODULEINFO" >> $@ ; \
|
||||
echo "</module>" >> $@ ; \
|
||||
fi ; \
|
||||
$(AWK) -f build_tools/get_documentation $$i >> $@ ; \
|
||||
done ; \
|
||||
done
|
||||
@echo
|
||||
@echo "</docs>" >> $@
|
||||
@build_tools/make_xml_documentation --command=create_xml --source-tree=. --mod-subdirs="$(DOC_MOD_SUBDIRS)" \
|
||||
--with-moduleinfo --output-file=$@
|
||||
|
||||
ifeq ($(GREP),)
|
||||
else ifeq ($(GREP),:)
|
||||
else
|
||||
XML_full_en_US = $(foreach dir,$(MOD_SUBDIRS),$(shell $(GREP) -l "language=\"en_US\"" $(dir)/*.c $(dir)/*.cc 2>/dev/null))
|
||||
endif
|
||||
|
||||
doc/full-en_US.xml: makeopts .lastclean $(XML_full_en_US)
|
||||
# The full-en_US.xml target is only called by the wiki documentation generation process
|
||||
# and does special post-processing in preparation for uploading to the wiki.
|
||||
# It creates full-en_US.xml but then re-creates core-en_US.xml as well.
|
||||
doc/full-en_US.xml: makeopts .lastclean $(XML_core_en_US)
|
||||
ifeq ($(PYTHON),:)
|
||||
@echo "--------------------------------------------------------------------------"
|
||||
@echo "--- Please install python to build full documentation ---"
|
||||
@echo "--------------------------------------------------------------------------"
|
||||
else
|
||||
@printf "Building Documentation For: "
|
||||
@echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $@
|
||||
@echo "<!DOCTYPE docs SYSTEM \"appdocsxml.dtd\">" >> $@
|
||||
@echo "<?xml-stylesheet type=\"text/xsl\" href=\"appdocsxml.xslt\"?>" >> $@
|
||||
@echo "<docs xmlns:xi=\"http://www.w3.org/2001/XInclude\">" >> $@
|
||||
@for x in $(filter-out third-party,$(MOD_SUBDIRS)); do \
|
||||
printf "$$x " ; \
|
||||
for i in `find $$x -name '*.c'`; do \
|
||||
$(PYTHON) build_tools/get_documentation.py < $$i >> $@ ; \
|
||||
done ; \
|
||||
done
|
||||
@echo
|
||||
@echo "</docs>" >> $@
|
||||
@$(PYTHON) build_tools/post_process_documentation.py -i $@ -o "doc/core-en_US.xml"
|
||||
@build_tools/make_xml_documentation --command=create_xml --source-tree=. --mod-subdirs="$(DOC_MOD_SUBDIRS)" \
|
||||
--for-wiki --output-file=$@ --core-output-file=./doc/core-en_US.xml
|
||||
endif
|
||||
|
||||
validate-docs: doc/core-en_US.xml
|
||||
|
@ -589,9 +561,9 @@ bininstall: _all installdirs $(SUBDIRS_INSTALL) main-bininstall
|
|||
$(INSTALL) -m 755 contrib/scripts/astversion "$(DESTDIR)$(ASTSBINDIR)/"
|
||||
$(INSTALL) -m 755 contrib/scripts/astgenkey "$(DESTDIR)$(ASTSBINDIR)/"
|
||||
$(INSTALL) -m 755 contrib/scripts/autosupport "$(DESTDIR)$(ASTSBINDIR)/"
|
||||
if [ ! -f /sbin/launchd ]; then \
|
||||
./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk"; \
|
||||
fi
|
||||
ifneq ($(HAVE_SBIN_LAUNCHD),1)
|
||||
./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk";
|
||||
endif
|
||||
|
||||
ifneq ($(DISABLE_XMLDOC),yes)
|
||||
$(INSTALL) -m 644 doc/core-*.xml "$(DESTDIR)$(ASTDATADIR)/documentation"
|
||||
|
@ -724,7 +696,17 @@ ifneq ($(filter ~%,$(DESTDIR)),)
|
|||
@exit 1
|
||||
endif
|
||||
|
||||
install: badshell bininstall datafiles
|
||||
versioncheck:
|
||||
ifeq ($(ASTERISKVERSION),UNKNOWN__git_check_fail)
|
||||
@echo "Asterisk Version is unknown due to a git error. If you are running make"
|
||||
@echo "as a different user than the project owner, this can be resolved by"
|
||||
@echo "running the following command as the user currently executing make: "$$USER
|
||||
@echo "git config --global --add safe.directory "$(mkfile_dir:/=)
|
||||
@exit 1
|
||||
endif
|
||||
|
||||
|
||||
install: badshell versioncheck bininstall datafiles
|
||||
@if [ -x /usr/sbin/asterisk-post-install ]; then \
|
||||
/usr/sbin/asterisk-post-install "$(DESTDIR)" . ; \
|
||||
fi
|
||||
|
@ -891,29 +873,36 @@ webvmail:
|
|||
@echo " +-------------------------------------------+"
|
||||
|
||||
progdocs:
|
||||
# Note, Makefile conditionals must not be tabbed out. Wasted hours with that.
|
||||
@cp doc/asterisk-ng-doxygen.in doc/asterisk-ng-doxygen
|
||||
ifeq ($(DOXYGEN),:)
|
||||
@echo "Doxygen is not installed. Please install and re-run the configuration script."
|
||||
else
|
||||
@cp doc/Doxyfile.in doc/Doxyfile
|
||||
ifeq ($(DOT),:)
|
||||
@echo "DOT is not installed. Doxygen will not produce any diagrams. Please install and re-run the configuration script."
|
||||
else
|
||||
# Enable DOT
|
||||
@echo "HAVE_DOT = YES" >> doc/asterisk-ng-doxygen
|
||||
@echo "HAVE_DOT = YES" >> doc/Doxyfile
|
||||
endif
|
||||
# Set Doxygen PROJECT_NUMBER variable
|
||||
ifneq ($(ASTERISKVERSION),UNKNOWN__and_probably_unsupported)
|
||||
@echo "PROJECT_NUMBER = $(ASTERISKVERSION)" >> doc/asterisk-ng-doxygen
|
||||
ifneq ($(NOISY_BUILD),yes)
|
||||
@echo "EXTRACT_ALL = YES" >> doc/Doxyfile
|
||||
endif
|
||||
ifeq ($(AST_DEVMODE),yes)
|
||||
@echo "INTERNAL_DOCS = YES" >> doc/Doxyfile
|
||||
@echo "WARN_NO_PARAMDOC = YES" >> doc/Doxyfile
|
||||
endif
|
||||
ifeq ($(ASTERISKVERSION),UNKNOWN__and_probably_unsupported)
|
||||
@echo "Asterisk Version is unknown, not configuring Doxygen PROJECT_NUMBER."
|
||||
else ifeq ($(ASTERISKVERSION),UNKNOWN__git_check_fail)
|
||||
@echo "Asterisk Version is unknown due to a git error. If you are running make"
|
||||
@echo "as a different user than the project owner, this can be resolved by"
|
||||
@echo "running the following command as the user currently executing make: "$$USER
|
||||
@echo "git config --global --add safe.directory "$(mkfile_dir:/=)
|
||||
@echo "not configuring Doxygen PROJECT_NUMBER."
|
||||
else
|
||||
echo "Asterisk Version is unknown, not configuring Doxygen PROJECT_NUMBER."
|
||||
@echo "PROJECT_NUMBER = $(ASTERISKVERSION)" >> doc/Doxyfile
|
||||
endif
|
||||
# Validate and auto-update local copy
|
||||
@doxygen -u doc/asterisk-ng-doxygen
|
||||
# Run Doxygen
|
||||
@doxygen doc/asterisk-ng-doxygen
|
||||
# Remove configuration backup file
|
||||
@rm -f doc/asterisk-ng-doxygen.bak
|
||||
@echo "Generating C-API documentation. This will take a while."
|
||||
@doxygen doc/Doxyfile
|
||||
@echo "Generation complete. Any warnings are in ./doxygen.log."
|
||||
endif
|
||||
|
||||
install-logrotate:
|
||||
|
@ -998,6 +987,7 @@ sounds:
|
|||
@$(MAKE) clean
|
||||
@[ -f "$(DESTDIR)$(ASTDBDIR)/astdb.sqlite3" ] || [ ! -f "$(DESTDIR)$(ASTDBDIR)/astdb" ] || [ ! -f menuselect.makeopts ] || grep -q MENUSELECT_UTILS=.*astdb2sqlite3 menuselect.makeopts || (sed -i.orig -e's/MENUSELECT_UTILS=\(.*\)/MENUSELECT_UTILS=\1 astdb2sqlite3/' menuselect.makeopts && echo "Updating menuselect.makeopts to include astdb2sqlite3" && echo "Original version backed up to menuselect.makeopts.orig")
|
||||
|
||||
|
||||
$(SUBDIRS_UNINSTALL):
|
||||
+@DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" ASTDATADIR="$(ASTDATADIR)" $(SUBMAKE) -C $(@:-uninstall=) uninstall
|
||||
|
||||
|
@ -1154,6 +1144,7 @@ check-alembic: makeopts
|
|||
.PHONY: uninstall-headers
|
||||
.PHONY: badshell
|
||||
.PHONY: installdirs
|
||||
.PHONY: progdocs
|
||||
.PHONY: validate-docs
|
||||
.PHONY: _clean
|
||||
.PHONY: ari-stubs
|
||||
|
|
|
@ -204,4 +204,19 @@ endif
|
|||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CXX) -o $@ $(PTHREAD_CFLAGS) $(_ASTLDFLAGS) $^ $(CXX_LIBS) $(ASTLDFLAGS)
|
||||
|
||||
# These CC commands just create an object file with the input file embedded in it.
|
||||
# It can be access from code as follows:
|
||||
# If your input file is named abc_def.xml...
|
||||
#
|
||||
# extern const uint8_t _binary_abc_def_xml_start[];
|
||||
# extern const uint8_t _binary_abc_def_xml_end[];
|
||||
# extern const size_t _binary_abc_def_xml_size;
|
||||
%.o: %.xml
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
|
||||
%.o: %.xslt
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
|
||||
dist-clean:: clean
|
||||
|
|
|
@ -91,7 +91,10 @@ guides in the [configs] directory.
|
|||
2. Run `./configure`
|
||||
|
||||
Execute the configure script to guess values for system-dependent
|
||||
variables used during compilation.
|
||||
variables used during compilation. If the script indicates that some required
|
||||
components are missing, you can run `./contrib/scripts/install_prereq install`
|
||||
to install the necessary components. Note that this will install all dependencies for every functionality of Asterisk. After running the script, you will need
|
||||
to rerun `./configure`.
|
||||
|
||||
3. Run `make menuselect` _\[optional]_
|
||||
|
||||
|
|
292
UPGRADE.txt
292
UPGRADE.txt
|
@ -18,6 +18,290 @@
|
|||
===
|
||||
===========================================================
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.7.0 to Asterisk 19.8.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
AMI (Asterisk Manager Interface)
|
||||
------------------
|
||||
* Previously, GetConfig and UpdateConfig were able to access files outside of
|
||||
the Asterisk configuration directory. Now this access is put behind the
|
||||
live_dangerously configuration option in asterisk.conf, which is disabled by
|
||||
default. If access to configuration files outside of the Asterisk configuation
|
||||
directory is required via AMI, then the live_dangerously configuration option
|
||||
must be set to yes.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.6.0 to Asterisk 19.7.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
res_crypto
|
||||
------------------
|
||||
* In addition to only paying attention to files ending with .key or .pub
|
||||
in the keys directory, we now also ignore any files which aren't regular
|
||||
files.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.4.0 to Asterisk 19.5.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
res_pjsip
|
||||
------------------
|
||||
* The 'async_operations' setting on transports is no longer
|
||||
obeyed and instead is always set to 1. This is due to the
|
||||
functionality not being applicable to Asterisk and causing
|
||||
excess unnecessary memory usage. This setting will now be
|
||||
ignored but can also be removed from the configuration file.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.2.0 to Asterisk 19.3.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
AMI
|
||||
------------------
|
||||
* The XML Manager Event Interface (amxml) now generates attribute names
|
||||
that are compliant with the XML 1.1 specification. Previously, an
|
||||
attribute name that started with a digit would be rendered as-is, even
|
||||
though attribute names must not begin with a digit. We now prefix
|
||||
attribute names that start with a digit with an underscore ('_') to
|
||||
prevent XML validation failures.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.0.0 to Asterisk 19.1.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
STIR/SHAKEN
|
||||
------------------
|
||||
* The STIR/SHAKEN configuration option has been split into
|
||||
4 different choices: off, attest, verify, and on. Off and
|
||||
on behave the same way as before. Attest will only perform
|
||||
attestation on the endpoint, and verify will only perform
|
||||
verification on the endpoint.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- New functionality introduced in Asterisk 19.0.0 --------------------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Log Rotate
|
||||
------------------
|
||||
* The sample logger files have been changed to have .log as their file
|
||||
extension. This was done so that when attached to issues on the issue
|
||||
tracker, they are able to be opened in the browser for convenience.
|
||||
Because of this, the asterisk.logrotate script has been updated to look
|
||||
for .log extensions instead of no extension for files such as full
|
||||
and messages.
|
||||
|
||||
app_dahdiras
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
app_fax
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
app_ices
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
app_image
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
app_meetme
|
||||
------------------
|
||||
* This module is now deprecated and will no
|
||||
longer be built by default. It is scheduled
|
||||
to be removed as of Asterisk 21.
|
||||
|
||||
app_mysql
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 1.8
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
app_nbscat
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
app_osplookup
|
||||
------------------
|
||||
* This module is now deprecated and will no
|
||||
longer be built by default. It is scheduled
|
||||
to be removed as of Asterisk 21.
|
||||
|
||||
app_url
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
cdr_mysql
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 1.8
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
cdr_syslog
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
chan_alsa
|
||||
------------------
|
||||
* This module is now deprecated and will no
|
||||
longer be built by default. It is scheduled
|
||||
to be removed as of Asterisk 21.
|
||||
|
||||
chan_mgcp
|
||||
------------------
|
||||
* This module is now deprecated and will no
|
||||
longer be built by default. It is scheduled
|
||||
to be removed as of Asterisk 21.
|
||||
|
||||
chan_misdn
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
chan_nbs
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
chan_oss
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
chan_phone
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
chan_sip
|
||||
------------------
|
||||
* chan_sip is no longer built by default. To build it, make sure to
|
||||
enable it when running 'make menuselect'
|
||||
|
||||
chan_skinny
|
||||
------------------
|
||||
* This module is now deprecated and will no
|
||||
longer be built by default. It is scheduled
|
||||
to be removed as of Asterisk 21.
|
||||
|
||||
chan_vpb
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
conf2ael
|
||||
------------------
|
||||
* This application was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
muted
|
||||
------------------
|
||||
* This application was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
res_config_sqlite
|
||||
------------------
|
||||
* This module was deprecated in Asterisk 16
|
||||
and is now being removed in accordance with
|
||||
the Asterisk Module Deprecation policy.
|
||||
|
||||
res_monitor
|
||||
------------------
|
||||
* This module is no longer built by default in
|
||||
accordance with the Module Deprecation Policy.
|
||||
If you require this functionality you will need
|
||||
to enable it for building in menuselect. Note
|
||||
that in the future res_monitor will be removed.
|
||||
|
||||
res_pktccops
|
||||
------------------
|
||||
* This module is now deprecated and will no
|
||||
longer be built by default. It is scheduled
|
||||
to be removed as of Asterisk 21.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 18.0.0 to Asterisk 19.0.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
STIR/SHAKEN
|
||||
------------------
|
||||
* The configuration option public_key_url in stir_shaken.conf
|
||||
has been renamed to public_cert_url to better fit what it
|
||||
contains. Only the name has changed - functionality is the
|
||||
same.
|
||||
|
||||
* STIR/SHAKEN originally needed an origid to be specified in
|
||||
stir_shaken.conf under the certificate config object in
|
||||
order to work. Now, one is automatically created by
|
||||
generating a UUID, as recommended by RFC8588. Any origid
|
||||
you have in your stir_shaken.conf will need to be removed
|
||||
for the module to read in certificates.
|
||||
|
||||
chan_iax2
|
||||
------------------
|
||||
* Encryption is now supported for RSA authentication.
|
||||
|
||||
Currently, these auth configurations will cause a crash:
|
||||
auth = md5,rsa
|
||||
auth = plaintext,md5,rsa
|
||||
|
||||
With a patched peer, the following will cause a crash:
|
||||
auth = rsa
|
||||
auth = md5,rsa
|
||||
auth = plaintext,md5,rsa
|
||||
|
||||
If both the peer and user are patches, no crash occurs.
|
||||
Existing good configurations should continue to work.
|
||||
|
||||
menuselect
|
||||
------------------
|
||||
* menuselect --enable, --disable, --enable-category and --disable-category will
|
||||
now fail with a non-zero exit code instead of silently failing if an invalid
|
||||
option or category is specified.
|
||||
|
||||
res_http_media_cache
|
||||
------------------
|
||||
* When fetching a file for playback from a URL, Asterisk will now first
|
||||
use the value of the Content-Type header in the HTTP response to
|
||||
determine the format of the audio data, and only if it is unable to do
|
||||
that will it attempt to parse the URL and extract the extension from
|
||||
the path portion. Previously Asterisk would first look at the end of
|
||||
the URL, which may have included query string parameters or a URL
|
||||
fragment, which was error prone.
|
||||
|
||||
res_srtp
|
||||
------------------
|
||||
* SRTP replay protection has been added to res_srtp and
|
||||
a new configuration option "srtpreplayprotection" has
|
||||
been added to the rtp.conf config file. For security
|
||||
reasons, the default setting is "yes". Buggy clients
|
||||
may not handle this correctly which could result in
|
||||
no, or one way, audio and Asterisk error messages like
|
||||
"replay check failed".
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- New functionality introduced in Asterisk 18.0.0 --------------------------
|
||||
------------------------------------------------------------------------------
|
||||
|
@ -610,7 +894,7 @@ chan_unistim:
|
|||
values used inside Unistim protocol
|
||||
|
||||
- Added 'dtmf_duration' option with changing default operation to disable
|
||||
receivied dtmf playback on unistim phone
|
||||
received dtmf playback on unistim phone
|
||||
|
||||
Core:
|
||||
|
||||
|
@ -1528,7 +1812,7 @@ UDPTL:
|
|||
From 10.4 to 10.5:
|
||||
|
||||
* The complex processor detection and optimization has been removed from
|
||||
the makefile in favor of using native optimization suppport when available.
|
||||
the makefile in favor of using native optimization support when available.
|
||||
BUILD_NATIVE can be disabled via menuselect under "Compiler Flags".
|
||||
|
||||
From 10.2 to 10.3:
|
||||
|
@ -1601,7 +1885,7 @@ From 1.8.13 to 1.8.14:
|
|||
|
||||
From 1.8.12 to 1.8.13:
|
||||
* The complex processor detection and optimization has been removed from
|
||||
the makefile in favor of using native optimization suppport when available.
|
||||
the makefile in favor of using native optimization support when available.
|
||||
BUILD_NATIVE can be disabled via menuselect under "Compiler Flags".
|
||||
|
||||
From 1.8.10 to 1.8.11:
|
||||
|
@ -2380,7 +2664,7 @@ Applications:
|
|||
the 'm' option now provides the functionality of "initially muted".
|
||||
In practice, most existing dialplans using the 'm' flag should not notice
|
||||
any difference, unless the keypad menu is enabled, allowing the user
|
||||
to unmute themsleves.
|
||||
to unmute themselves.
|
||||
|
||||
* ast_play_and_record would attempt to cancel the recording if a DTMF
|
||||
'0' was received. This behavior was not documented in most of the
|
||||
|
|
|
@ -16,10 +16,12 @@ renamed; the new names are:
|
|||
|
||||
chan_zap.so -> chan_dahdi.so
|
||||
app_zapbarge.so -> app_dahdibarge.so
|
||||
app_zapras.so -> app_dahdiras.so
|
||||
app_zapscan.so -> app_dahdiscan.so
|
||||
codec_zap.so -> codec_dahdi.so
|
||||
|
||||
The following modules have been removed:
|
||||
app_zapras.so -> app_dahdiras.so
|
||||
|
||||
Second, the behavior of many modules has changed due to the switch to
|
||||
DAHDI; the changes are listed below.
|
||||
|
||||
|
@ -46,7 +48,8 @@ app_dahdibarge.so:
|
|||
|
||||
app_dahdiras.so:
|
||||
|
||||
The ZapRAS application has been renamed to DAHDIRAS.
|
||||
The ZapRAS application was renamed to DAHDIRAS. This application has
|
||||
since been removed.
|
||||
|
||||
app_dahdiscan.so:
|
||||
|
||||
|
|
|
@ -28,9 +28,7 @@ H323SOURCE:=$(addprefix ooh323c/src/,$(OOH323C)) ooh323cDriver.c
|
|||
|
||||
H323CFLAGS:=-Iooh323c/src -Iooh323c/src/h323
|
||||
|
||||
ALL_C_MODS:=app_mysql \
|
||||
cdr_mysql \
|
||||
chan_mobile \
|
||||
ALL_C_MODS:=chan_mobile \
|
||||
chan_ooh323 \
|
||||
format_mp3 \
|
||||
res_config_mysql
|
||||
|
@ -64,6 +62,10 @@ chan_ooh323.so: _ASTCFLAGS+=$(H323CFLAGS)
|
|||
$(call MOD_ADD_C,chan_ooh323,$(H323SOURCE))
|
||||
|
||||
ifneq ($(wildcard mp3/Makefile),)
|
||||
# At the current time, the fate of mp3 is in flux so it didn't make sense to
|
||||
# add configure/makeopts processing for array-bounds since this is the only
|
||||
# source file that needs that warning suppressed.
|
||||
mp3/layer3.o: _ASTCFLAGS+=-Wno-array-bounds
|
||||
$(call MOD_ADD_C,format_mp3,mp3/common.c mp3/dct64_i386.c mp3/decode_ntom.c mp3/layer3.c mp3/tabinit.c mp3/interface.c)
|
||||
|
||||
.PHONY: check_mp3
|
||||
|
|
|
@ -1,667 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2004, Constantine Filin and Christos Ricudis
|
||||
*
|
||||
* Christos Ricudis <ricudis@itc.auth.gr>
|
||||
* Constantine Filin <cf@intermedia.net>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file
|
||||
* \brief MYSQL dialplan application
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*! \li \ref app_mysql.c uses the configuration file \ref app_mysql.conf
|
||||
* \addtogroup configuration_file Configuration Files
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \page app_mysql.conf app_mysql.conf
|
||||
* \verbinclude app_mysql.conf.sample
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>mysqlclient</depend>
|
||||
<defaultenabled>no</defaultenabled>
|
||||
<support_level>deprecated</support_level>
|
||||
<replacement>func_odbc</replacement>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/chanvars.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/config.h"
|
||||
|
||||
#define EXTRA_LOG 0
|
||||
|
||||
enum { NULLSTRING, NULLVALUE, EMPTYSTRING } nullvalue = NULLSTRING;
|
||||
|
||||
static const char app[] = "MYSQL";
|
||||
|
||||
static const char synopsis[] = "Do several mySQLy things";
|
||||
|
||||
static const char descrip[] =
|
||||
"MYSQL(): Do several mySQLy things\n"
|
||||
"Syntax:\n"
|
||||
" MYSQL(Set timeout <num>)\n"
|
||||
" Set the connection timeout, in seconds.\n"
|
||||
" MYSQL(Connect connid dhhost[:dbport] dbuser dbpass dbname [dbcharset])\n"
|
||||
" Connects to a database. Arguments contain standard MySQL parameters\n"
|
||||
" passed to function mysql_real_connect. Optional parameter dbcharset\n"
|
||||
" defaults to 'latin1'. Connection identifer returned in ${connid}\n"
|
||||
" MYSQL(Query resultid ${connid} query-string)\n"
|
||||
" Executes standard MySQL query contained in query-string using established\n"
|
||||
" connection identified by ${connid}. Result of query is stored in ${resultid}.\n"
|
||||
" MYSQL(Nextresult resultid ${connid}\n"
|
||||
" If last query returned more than one result set, it stores the next\n"
|
||||
" result set in ${resultid}. It's useful with stored procedures\n"
|
||||
" MYSQL(Fetch fetchid ${resultid} var1 var2 ... varN)\n"
|
||||
" Fetches a single row from a result set contained in ${result_identifier}.\n"
|
||||
" Assigns returned fields to ${var1} ... ${varn}. ${fetchid} is set TRUE\n"
|
||||
" if additional rows exist in result set.\n"
|
||||
" MYSQL(Clear ${resultid})\n"
|
||||
" Frees memory and datastructures associated with result set.\n"
|
||||
" MYSQL(Disconnect ${connid})\n"
|
||||
" Disconnects from named connection to MySQL.\n"
|
||||
" On exit, always returns 0. Sets MYSQL_STATUS to 0 on success and -1 on error.\n";
|
||||
|
||||
/*
|
||||
EXAMPLES OF USE :
|
||||
|
||||
exten => s,2,MYSQL(Connect connid localhost asterisk mypass credit utf8)
|
||||
exten => s,3,MYSQL(Query resultid ${connid} SELECT username,credit FROM credit WHERE callerid=${CALLERIDNUM})
|
||||
exten => s,4,MYSQL(Fetch fetchid ${resultid} datavar1 datavar2)
|
||||
exten => s,5,GotoIf(${fetchid}?6:8)
|
||||
exten => s,6,Festival("User ${datavar1} currently has credit balance of ${datavar2} dollars.")
|
||||
exten => s,7,Goto(s,4)
|
||||
exten => s,8,MYSQL(Clear ${resultid})
|
||||
exten => s,9,MYSQL(Disconnect ${connid})
|
||||
*/
|
||||
|
||||
AST_MUTEX_DEFINE_STATIC(_mysql_mutex);
|
||||
|
||||
#define MYSQL_CONFIG "app_mysql.conf"
|
||||
#define MYSQL_CONFIG_OLD "mysql.conf"
|
||||
#define AST_MYSQL_ID_DUMMY 0
|
||||
#define AST_MYSQL_ID_CONNID 1
|
||||
#define AST_MYSQL_ID_RESID 2
|
||||
#define AST_MYSQL_ID_FETCHID 3
|
||||
|
||||
static int autoclear = 0;
|
||||
|
||||
static void mysql_ds_destroy(void *data);
|
||||
static void mysql_ds_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan);
|
||||
|
||||
static const struct ast_datastore_info mysql_ds_info = {
|
||||
.type = "APP_ADDON_SQL_MYSQL",
|
||||
.destroy = mysql_ds_destroy,
|
||||
.chan_fixup = mysql_ds_fixup,
|
||||
};
|
||||
|
||||
struct ast_MYSQL_id {
|
||||
struct ast_channel *owner;
|
||||
int identifier_type; /* 0=dummy, 1=connid, 2=resultid */
|
||||
int identifier;
|
||||
void *data;
|
||||
AST_LIST_ENTRY(ast_MYSQL_id) entries;
|
||||
} *ast_MYSQL_id;
|
||||
|
||||
AST_LIST_HEAD(MYSQLidshead,ast_MYSQL_id) _mysql_ids_head;
|
||||
|
||||
static void mysql_ds_destroy(void *data)
|
||||
{
|
||||
/* Destroy any IDs owned by the channel */
|
||||
struct ast_MYSQL_id *i;
|
||||
if (AST_LIST_LOCK(&_mysql_ids_head)) {
|
||||
ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
|
||||
} else {
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&_mysql_ids_head, i, entries) {
|
||||
if (i->owner == data) {
|
||||
AST_LIST_REMOVE_CURRENT(entries);
|
||||
if (i->identifier_type == AST_MYSQL_ID_CONNID) {
|
||||
/* Drop connection */
|
||||
mysql_close(i->data);
|
||||
} else if (i->identifier_type == AST_MYSQL_ID_RESID) {
|
||||
/* Drop result */
|
||||
mysql_free_result(i->data);
|
||||
}
|
||||
ast_free(i);
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END
|
||||
AST_LIST_UNLOCK(&_mysql_ids_head);
|
||||
}
|
||||
}
|
||||
|
||||
static void mysql_ds_fixup(void *data, struct ast_channel *oldchan, struct ast_channel *newchan)
|
||||
{
|
||||
/* Destroy any IDs owned by the channel */
|
||||
struct ast_MYSQL_id *i;
|
||||
if (AST_LIST_LOCK(&_mysql_ids_head)) {
|
||||
ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
|
||||
} else {
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&_mysql_ids_head, i, entries) {
|
||||
if (i->owner == data) {
|
||||
AST_LIST_REMOVE_CURRENT(entries);
|
||||
if (i->identifier_type == AST_MYSQL_ID_CONNID) {
|
||||
/* Drop connection */
|
||||
mysql_close(i->data);
|
||||
} else if (i->identifier_type == AST_MYSQL_ID_RESID) {
|
||||
/* Drop result */
|
||||
mysql_free_result(i->data);
|
||||
}
|
||||
ast_free(i);
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END
|
||||
AST_LIST_UNLOCK(&_mysql_ids_head);
|
||||
}
|
||||
}
|
||||
|
||||
/* helpful procs */
|
||||
static void *find_identifier(int identifier, int identifier_type)
|
||||
{
|
||||
struct MYSQLidshead *headp = &_mysql_ids_head;
|
||||
struct ast_MYSQL_id *i;
|
||||
void *res=NULL;
|
||||
int found=0;
|
||||
|
||||
if (AST_LIST_LOCK(headp)) {
|
||||
ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
|
||||
} else {
|
||||
AST_LIST_TRAVERSE(headp, i, entries) {
|
||||
if ((i->identifier == identifier) && (i->identifier_type == identifier_type)) {
|
||||
found = 1;
|
||||
res = i->data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ast_log(LOG_WARNING, "Identifier %d, identifier_type %d not found in identifier list\n", identifier, identifier_type);
|
||||
}
|
||||
AST_LIST_UNLOCK(headp);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int add_identifier(struct ast_channel *chan, int identifier_type, void *data)
|
||||
{
|
||||
struct ast_MYSQL_id *i = NULL, *j = NULL;
|
||||
struct MYSQLidshead *headp = &_mysql_ids_head;
|
||||
int maxidentifier = 0;
|
||||
|
||||
if (AST_LIST_LOCK(headp)) {
|
||||
ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
|
||||
return -1;
|
||||
} else {
|
||||
i = ast_malloc(sizeof(*i));
|
||||
AST_LIST_TRAVERSE(headp, j, entries) {
|
||||
if (j->identifier > maxidentifier) {
|
||||
maxidentifier = j->identifier;
|
||||
}
|
||||
}
|
||||
i->identifier = maxidentifier + 1;
|
||||
i->identifier_type = identifier_type;
|
||||
i->data = data;
|
||||
i->owner = chan;
|
||||
AST_LIST_INSERT_HEAD(headp, i, entries);
|
||||
AST_LIST_UNLOCK(headp);
|
||||
}
|
||||
return i->identifier;
|
||||
}
|
||||
|
||||
static int del_identifier(int identifier, int identifier_type)
|
||||
{
|
||||
struct ast_MYSQL_id *i;
|
||||
struct MYSQLidshead *headp = &_mysql_ids_head;
|
||||
int found = 0;
|
||||
|
||||
if (AST_LIST_LOCK(headp)) {
|
||||
ast_log(LOG_WARNING, "Unable to lock identifiers list\n");
|
||||
} else {
|
||||
AST_LIST_TRAVERSE(headp, i, entries) {
|
||||
if ((i->identifier == identifier) &&
|
||||
(i->identifier_type == identifier_type)) {
|
||||
AST_LIST_REMOVE(headp, i, entries);
|
||||
ast_free(i);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
AST_LIST_UNLOCK(headp);
|
||||
}
|
||||
|
||||
if (found == 0) {
|
||||
ast_log(LOG_WARNING, "Could not find identifier %d, identifier_type %d in list to delete\n", identifier, identifier_type);
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int set_asterisk_int(struct ast_channel *chan, char *varname, int id)
|
||||
{
|
||||
if (id >= 0) {
|
||||
char s[12] = "";
|
||||
snprintf(s, sizeof(s), "%d", id);
|
||||
ast_debug(5, "MYSQL: setting var '%s' to value '%s'\n", varname, s);
|
||||
pbx_builtin_setvar_helper(chan, varname, s);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static int add_identifier_and_set_asterisk_int(struct ast_channel *chan, char *varname, int identifier_type, void *data)
|
||||
{
|
||||
return set_asterisk_int(chan, varname, add_identifier(chan, identifier_type, data));
|
||||
}
|
||||
|
||||
static int safe_scan_int(char **data, char *delim, int def)
|
||||
{
|
||||
char *end;
|
||||
int res = def;
|
||||
char *s = strsep(data, delim);
|
||||
if (s) {
|
||||
res = strtol(s, &end, 10);
|
||||
if (*end)
|
||||
res = def; /* not an integer */
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int aMYSQL_set(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *var, *tmp, *parse;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(set);
|
||||
AST_APP_ARG(variable);
|
||||
AST_APP_ARG(value);
|
||||
);
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
|
||||
|
||||
if (args.argc == 3) {
|
||||
var = ast_alloca(6 + strlen(args.variable) + 1);
|
||||
sprintf(var, "MYSQL_%s", args.variable);
|
||||
|
||||
/* Make the parameter case-insensitive */
|
||||
for (tmp = var + 6; *tmp; tmp++)
|
||||
*tmp = toupper(*tmp);
|
||||
|
||||
pbx_builtin_setvar_helper(chan, var, args.value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* MYSQL operations */
|
||||
static int aMYSQL_connect(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(connect);
|
||||
AST_APP_ARG(connid);
|
||||
AST_APP_ARG(dbhost);
|
||||
AST_APP_ARG(dbuser);
|
||||
AST_APP_ARG(dbpass);
|
||||
AST_APP_ARG(dbname);
|
||||
AST_APP_ARG(dbcharset);
|
||||
);
|
||||
MYSQL *mysql;
|
||||
int timeout;
|
||||
const char *ctimeout;
|
||||
unsigned int port = 0;
|
||||
char *port_str;
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
|
||||
|
||||
if (args.argc < 6) {
|
||||
ast_log(LOG_WARNING, "MYSQL_connect is missing some arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(mysql = mysql_init(NULL))) {
|
||||
ast_log(LOG_WARNING, "mysql_init returned NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctimeout = pbx_builtin_getvar_helper(chan, "MYSQL_TIMEOUT");
|
||||
if (ctimeout && sscanf(ctimeout, "%30d", &timeout) == 1) {
|
||||
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&timeout);
|
||||
}
|
||||
if(args.dbcharset && strlen(args.dbcharset) > 2){
|
||||
char set_names[255];
|
||||
char statement[512];
|
||||
snprintf(set_names, sizeof(set_names), "SET NAMES %s", args.dbcharset);
|
||||
mysql_real_escape_string(mysql, statement, set_names, sizeof(set_names));
|
||||
mysql_options(mysql, MYSQL_INIT_COMMAND, set_names);
|
||||
mysql_options(mysql, MYSQL_SET_CHARSET_NAME, args.dbcharset);
|
||||
}
|
||||
|
||||
if ((port_str = strchr(args.dbhost, ':'))) {
|
||||
*port_str++ = '\0';
|
||||
if (sscanf(port_str, "%u", &port) != 1) {
|
||||
ast_log(LOG_WARNING, "Invalid port: '%s'\n", port_str);
|
||||
port = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mysql_real_connect(mysql, args.dbhost, args.dbuser, args.dbpass, args.dbname, port, NULL,
|
||||
#ifdef CLIENT_MULTI_STATEMENTS
|
||||
CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS
|
||||
#elif defined(CLIENT_MULTI_QUERIES)
|
||||
CLIENT_MULTI_QUERIES
|
||||
#else
|
||||
0
|
||||
#endif
|
||||
)) {
|
||||
ast_log(LOG_WARNING, "mysql_real_connect(mysql,%s,%s,dbpass,%s,...) failed(%d): %s\n",
|
||||
args.dbhost, args.dbuser, args.dbname, mysql_errno(mysql), mysql_error(mysql));
|
||||
return -1;
|
||||
}
|
||||
|
||||
add_identifier_and_set_asterisk_int(chan, args.connid, AST_MYSQL_ID_CONNID, mysql);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aMYSQL_query(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(query);
|
||||
AST_APP_ARG(resultid);
|
||||
AST_APP_ARG(connid);
|
||||
AST_APP_ARG(sql);
|
||||
);
|
||||
MYSQL *mysql;
|
||||
MYSQL_RES *mysqlres;
|
||||
int connid;
|
||||
int mysql_query_res;
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
|
||||
|
||||
if (args.argc != 4 || (connid = atoi(args.connid)) == 0) {
|
||||
ast_log(LOG_WARNING, "missing some arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(mysql = find_identifier(connid, AST_MYSQL_ID_CONNID))) {
|
||||
ast_log(LOG_WARNING, "Invalid connection identifier %s passed in aMYSQL_query\n", args.connid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((mysql_query_res = mysql_query(mysql, args.sql)) != 0) {
|
||||
ast_log(LOG_WARNING, "aMYSQL_query: mysql_query failed. Error: %s\n", mysql_error(mysql));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((mysqlres = mysql_store_result(mysql))) {
|
||||
add_identifier_and_set_asterisk_int(chan, args.resultid, AST_MYSQL_ID_RESID, mysqlres);
|
||||
return 0;
|
||||
} else if (!mysql_field_count(mysql)) {
|
||||
return 0;
|
||||
} else
|
||||
ast_log(LOG_WARNING, "mysql_store_result() failed on query %s\n", args.sql);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int aMYSQL_nextresult(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
MYSQL *mysql;
|
||||
MYSQL_RES *mysqlres;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(nextresult);
|
||||
AST_APP_ARG(resultid);
|
||||
AST_APP_ARG(connid);
|
||||
);
|
||||
int connid = -1;
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
|
||||
sscanf(args.connid, "%30d", &connid);
|
||||
|
||||
if (args.argc != 3 || connid <= 0) {
|
||||
ast_log(LOG_WARNING, "missing some arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(mysql = find_identifier(connid, AST_MYSQL_ID_CONNID))) {
|
||||
ast_log(LOG_WARNING, "Invalid connection identifier %d passed in aMYSQL_query\n", connid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mysql_more_results(mysql)) {
|
||||
mysql_next_result(mysql);
|
||||
if ((mysqlres = mysql_store_result(mysql))) {
|
||||
add_identifier_and_set_asterisk_int(chan, args.resultid, AST_MYSQL_ID_RESID, mysqlres);
|
||||
return 0;
|
||||
} else if (!mysql_field_count(mysql)) {
|
||||
return 0;
|
||||
} else
|
||||
ast_log(LOG_WARNING, "mysql_store_result() failed on storing next_result\n");
|
||||
} else
|
||||
ast_log(LOG_WARNING, "mysql_more_results() result set has no more results\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int aMYSQL_fetch(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
MYSQL_RES *mysqlres;
|
||||
MYSQL_ROW mysqlrow;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(fetch);
|
||||
AST_APP_ARG(resultvar);
|
||||
AST_APP_ARG(fetchid);
|
||||
AST_APP_ARG(vars);
|
||||
);
|
||||
char *s5, *parse;
|
||||
int resultid = -1, numFields, j;
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
|
||||
sscanf(args.fetchid, "%30d", &resultid);
|
||||
|
||||
if (args.resultvar && (resultid >= 0) ) {
|
||||
if ((mysqlres = find_identifier(resultid, AST_MYSQL_ID_RESID)) != NULL) {
|
||||
/* Grab the next row */
|
||||
if ((mysqlrow = mysql_fetch_row(mysqlres)) != NULL) {
|
||||
numFields = mysql_num_fields(mysqlres);
|
||||
for (j = 0; j < numFields; j++) {
|
||||
s5 = strsep(&args.vars, " ");
|
||||
if (s5 == NULL) {
|
||||
ast_log(LOG_WARNING, "ast_MYSQL_fetch: More fields (%d) than variables (%d)\n", numFields, j);
|
||||
break;
|
||||
}
|
||||
|
||||
pbx_builtin_setvar_helper(chan, s5, mysqlrow[j] ? mysqlrow[j] :
|
||||
nullvalue == NULLSTRING ? "NULL" :
|
||||
nullvalue == EMPTYSTRING ? "" :
|
||||
NULL);
|
||||
}
|
||||
ast_debug(5, "ast_MYSQL_fetch: numFields=%d\n", numFields);
|
||||
set_asterisk_int(chan, args.resultvar, 1); /* try more rows */
|
||||
} else {
|
||||
ast_debug(5, "ast_MYSQL_fetch : EOF\n");
|
||||
set_asterisk_int(chan, args.resultvar, 0); /* no more rows */
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
set_asterisk_int(chan, args.resultvar, 0);
|
||||
ast_log(LOG_WARNING, "aMYSQL_fetch: Invalid result identifier %d passed\n", resultid);
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "aMYSQL_fetch: missing some arguments\n");
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int aMYSQL_clear(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
MYSQL_RES *mysqlres;
|
||||
|
||||
int id;
|
||||
char *parse = ast_strdupa(data);
|
||||
strsep(&parse, " "); /* eat the first token, we already know it :P */
|
||||
id = safe_scan_int(&parse, " \n", -1);
|
||||
if ((mysqlres = find_identifier(id, AST_MYSQL_ID_RESID)) == NULL) {
|
||||
ast_log(LOG_WARNING, "Invalid result identifier %d passed in aMYSQL_clear\n", id);
|
||||
} else {
|
||||
mysql_free_result(mysqlres);
|
||||
del_identifier(id, AST_MYSQL_ID_RESID);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aMYSQL_disconnect(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
MYSQL *mysql;
|
||||
int id;
|
||||
char *parse = ast_strdupa(data);
|
||||
strsep(&parse, " "); /* eat the first token, we already know it :P */
|
||||
|
||||
id = safe_scan_int(&parse, " \n", -1);
|
||||
if ((mysql = find_identifier(id, AST_MYSQL_ID_CONNID)) == NULL) {
|
||||
ast_log(LOG_WARNING, "Invalid connection identifier %d passed in aMYSQL_disconnect\n", id);
|
||||
} else {
|
||||
mysql_close(mysql);
|
||||
del_identifier(id, AST_MYSQL_ID_CONNID);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int MYSQL_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
int result;
|
||||
char sresult[10];
|
||||
|
||||
ast_debug(5, "MYSQL: data=%s\n", data);
|
||||
|
||||
if (!data) {
|
||||
ast_log(LOG_WARNING, "MYSQL requires an argument (see manual)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
if (autoclear) {
|
||||
struct ast_datastore *mysql_store = NULL;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
mysql_store = ast_channel_datastore_find(chan, &mysql_ds_info, NULL);
|
||||
if (!mysql_store) {
|
||||
if (!(mysql_store = ast_datastore_alloc(&mysql_ds_info, NULL))) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
|
||||
} else {
|
||||
mysql_store->data = chan;
|
||||
ast_channel_datastore_add(chan, mysql_store);
|
||||
}
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
ast_mutex_lock(&_mysql_mutex);
|
||||
|
||||
if (strncasecmp("connect", data, strlen("connect")) == 0) {
|
||||
result = aMYSQL_connect(chan, data);
|
||||
} else if (strncasecmp("query", data, strlen("query")) == 0) {
|
||||
result = aMYSQL_query(chan, data);
|
||||
} else if (strncasecmp("nextresult", data, strlen("nextresult")) == 0) {
|
||||
result = aMYSQL_nextresult(chan, data);
|
||||
} else if (strncasecmp("fetch", data, strlen("fetch")) == 0) {
|
||||
result = aMYSQL_fetch(chan, data);
|
||||
} else if (strncasecmp("clear", data, strlen("clear")) == 0) {
|
||||
result = aMYSQL_clear(chan, data);
|
||||
} else if (strncasecmp("disconnect", data, strlen("disconnect")) == 0) {
|
||||
result = aMYSQL_disconnect(chan, data);
|
||||
} else if (strncasecmp("set", data, 3) == 0) {
|
||||
result = aMYSQL_set(chan, data);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n", data);
|
||||
result = -1;
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&_mysql_mutex);
|
||||
|
||||
snprintf(sresult, sizeof(sresult), "%d", result);
|
||||
pbx_builtin_setvar_helper(chan, "MYSQL_STATUS", sresult);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Load the module
|
||||
*
|
||||
* Module loading including tests for configuration or dependencies.
|
||||
* This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
|
||||
* or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
|
||||
* tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
|
||||
* configuration file or other non-critical problem return
|
||||
* AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
|
||||
*/
|
||||
static int load_module(void)
|
||||
{
|
||||
struct MYSQLidshead *headp = &_mysql_ids_head;
|
||||
struct ast_flags config_flags = { 0 };
|
||||
struct ast_config *cfg = ast_config_load(MYSQL_CONFIG, config_flags);
|
||||
const char *temp;
|
||||
|
||||
if (!cfg) {
|
||||
/* Backwards compatibility ftw */
|
||||
cfg = ast_config_load(MYSQL_CONFIG_OLD, config_flags);
|
||||
}
|
||||
|
||||
if (cfg) {
|
||||
if ((temp = ast_variable_retrieve(cfg, "general", "nullvalue"))) {
|
||||
if (!strcasecmp(temp, "nullstring")) {
|
||||
nullvalue = NULLSTRING;
|
||||
} else if (!strcasecmp(temp, "emptystring")) {
|
||||
nullvalue = EMPTYSTRING;
|
||||
} else if (!strcasecmp(temp, "null")) {
|
||||
nullvalue = NULLVALUE;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Illegal value for 'nullvalue': '%s' (must be 'nullstring', 'null', or 'emptystring')\n", temp);
|
||||
}
|
||||
}
|
||||
if ((temp = ast_variable_retrieve(cfg, "general", "autoclear")) && ast_true(temp)) {
|
||||
autoclear = 1;
|
||||
}
|
||||
ast_config_destroy(cfg);
|
||||
}
|
||||
|
||||
AST_LIST_HEAD_INIT(headp);
|
||||
return ast_register_application(app, MYSQL_exec, synopsis, descrip);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_DEPRECATED(ASTERISK_GPL_KEY, "Simple Mysql Interface");
|
|
@ -1,758 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* James Sharp <jsharp@psychoses.org>
|
||||
*
|
||||
* Modified August 2003
|
||||
* Tilghman Lesher <asterisk__cdr__cdr_mysql__200308@the-tilghman.com>
|
||||
*
|
||||
* Modified August 6, 2005
|
||||
* Joseph Benden <joe@thrallingpenguin.com>
|
||||
* Added mysql connection timeout parameter
|
||||
* Added an automatic reconnect as to not lose a cdr record
|
||||
* Cleaned up the original code to match the coding guidelines
|
||||
*
|
||||
* Modified Juli 2006
|
||||
* Martin Portmann <map@infinitum.ch>
|
||||
* Added mysql ssl support
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file
|
||||
* \brief MySQL CDR backend
|
||||
* \ingroup cdr_drivers
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>mysqlclient</depend>
|
||||
<defaultenabled>no</defaultenabled>
|
||||
<support_level>deprecated</support_level>
|
||||
<replacement>cdr_adaptive_odbc</replacement>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <mysql/mysql.h>
|
||||
#include <mysql/errmsg.h>
|
||||
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/cdr.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/strings.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/threadstorage.h"
|
||||
|
||||
#define DATE_FORMAT "%Y-%m-%d %T"
|
||||
|
||||
#ifndef MYSQL_PORT
|
||||
# ifdef MARIADB_PORT
|
||||
# define MYSQL_PORT MARIADB_PORT
|
||||
# else
|
||||
# define MYSQL_PORT 3306
|
||||
# endif
|
||||
#endif
|
||||
|
||||
AST_THREADSTORAGE(sql1_buf);
|
||||
AST_THREADSTORAGE(sql2_buf);
|
||||
AST_THREADSTORAGE(escape_buf);
|
||||
|
||||
static const char desc[] = "MySQL CDR Backend";
|
||||
static const char name[] = "mysql";
|
||||
static const char config[] = "cdr_mysql.conf";
|
||||
|
||||
static struct ast_str *hostname = NULL, *dbname = NULL, *dbuser = NULL, *password = NULL, *dbsock = NULL, *dbtable = NULL, *dbcharset = NULL, *cdrzone = NULL;
|
||||
|
||||
static struct ast_str *ssl_ca = NULL, *ssl_cert = NULL, *ssl_key = NULL;
|
||||
|
||||
static int dbport = 0;
|
||||
static int connected = 0;
|
||||
static time_t connect_time = 0;
|
||||
static int records = 0;
|
||||
static int totalrecords = 0;
|
||||
static int timeout = 0;
|
||||
static int calldate_compat = 0;
|
||||
|
||||
AST_MUTEX_DEFINE_STATIC(mysql_lock);
|
||||
|
||||
struct unload_string {
|
||||
AST_LIST_ENTRY(unload_string) entry;
|
||||
struct ast_str *str;
|
||||
};
|
||||
|
||||
static AST_LIST_HEAD_STATIC(unload_strings, unload_string);
|
||||
|
||||
struct column {
|
||||
char *name;
|
||||
char *cdrname;
|
||||
char *staticvalue;
|
||||
char *type;
|
||||
AST_LIST_ENTRY(column) list;
|
||||
};
|
||||
|
||||
/* Protected with mysql_lock */
|
||||
static AST_RWLIST_HEAD_STATIC(columns, column);
|
||||
|
||||
static MYSQL mysql = { { NULL }, };
|
||||
|
||||
static char *handle_cli_cdr_mysql_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "cdr mysql status";
|
||||
e->usage =
|
||||
"Usage: cdr mysql status\n"
|
||||
" Shows current connection status for cdr_mysql\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc != 3)
|
||||
return CLI_SHOWUSAGE;
|
||||
|
||||
if (connected) {
|
||||
char status[256];
|
||||
char status2[100] = "";
|
||||
char buf[362]; /* 256+100+" for "+NULL */
|
||||
int ctime = time(NULL) - connect_time;
|
||||
if (dbport)
|
||||
snprintf(status, 255, "Connected to %s@%s, port %d", ast_str_buffer(dbname), ast_str_buffer(hostname), dbport);
|
||||
else if (dbsock)
|
||||
snprintf(status, 255, "Connected to %s on socket file %s", ast_str_buffer(dbname), S_OR(ast_str_buffer(dbsock), "default"));
|
||||
else
|
||||
snprintf(status, 255, "Connected to %s@%s", ast_str_buffer(dbname), ast_str_buffer(hostname));
|
||||
|
||||
if (ast_str_strlen(dbuser))
|
||||
snprintf(status2, 99, " with username %s", ast_str_buffer(dbuser));
|
||||
if (ast_str_strlen(dbtable))
|
||||
snprintf(status2, 99, " using table %s", ast_str_buffer(dbtable));
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%s for ", status, status2);
|
||||
ast_cli_print_timestr_fromseconds(a->fd, ctime, buf);
|
||||
|
||||
if (records == totalrecords)
|
||||
ast_cli(a->fd, " Wrote %d records since last restart.\n", totalrecords);
|
||||
else
|
||||
ast_cli(a->fd, " Wrote %d records since last restart and %d records since last reconnect.\n", totalrecords, records);
|
||||
} else {
|
||||
ast_cli(a->fd, "Not currently connected to a MySQL server.\n");
|
||||
}
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry cdr_mysql_status_cli[] = {
|
||||
AST_CLI_DEFINE(handle_cli_cdr_mysql_status, "Show connection status of cdr_mysql"),
|
||||
};
|
||||
|
||||
static void configure_connection_charset(void)
|
||||
{
|
||||
if (ast_str_strlen(dbcharset)) {
|
||||
const char *charset = ast_str_buffer(dbcharset);
|
||||
if (mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, charset)) {
|
||||
ast_log(LOG_WARNING, "Failed to set connection charset. Data inserted might be invalid.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mysql_log(struct ast_cdr *cdr)
|
||||
{
|
||||
struct ast_str *sql1 = ast_str_thread_get(&sql1_buf, 1024), *sql2 = ast_str_thread_get(&sql2_buf, 1024);
|
||||
int retries = 5;
|
||||
#ifdef HAVE_MYSQLCLIENT_BOOL
|
||||
bool my_bool_true = 1;
|
||||
#elif HAVE_MYSQLCLIENT_MY_BOOL
|
||||
my_bool my_bool_true = 1;
|
||||
#endif
|
||||
|
||||
if (!sql1 || !sql2) {
|
||||
ast_log(LOG_ERROR, "Memory error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_mutex_lock(&mysql_lock);
|
||||
|
||||
db_reconnect:
|
||||
if ((!connected) && (hostname || dbsock) && dbuser && password && dbname && dbtable ) {
|
||||
/* Attempt to connect */
|
||||
mysql_init(&mysql);
|
||||
/* Add option to quickly timeout the connection */
|
||||
if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout) != 0) {
|
||||
ast_log(LOG_ERROR, "mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
|
||||
}
|
||||
#if MYSQL_VERSION_ID >= 50013
|
||||
/* Add option for automatic reconnection */
|
||||
if (mysql_options(&mysql, MYSQL_OPT_RECONNECT, &my_bool_true) != 0) {
|
||||
ast_log(LOG_ERROR, "mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
|
||||
}
|
||||
#endif
|
||||
if (ssl_ca || ssl_cert || ssl_key) {
|
||||
mysql_ssl_set(&mysql, ssl_key ? ast_str_buffer(ssl_key) : NULL, ssl_cert ? ast_str_buffer(ssl_cert) : NULL, ssl_ca ? ast_str_buffer(ssl_ca) : NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
configure_connection_charset();
|
||||
|
||||
if (mysql_real_connect(&mysql, ast_str_buffer(hostname), ast_str_buffer(dbuser), ast_str_buffer(password), ast_str_buffer(dbname), dbport, dbsock && ast_str_strlen(dbsock) ? ast_str_buffer(dbsock) : NULL, ssl_ca ? CLIENT_SSL : 0)) {
|
||||
connected = 1;
|
||||
connect_time = time(NULL);
|
||||
records = 0;
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Cannot connect to database server %s: (%d) %s\n", ast_str_buffer(hostname), mysql_errno(&mysql), mysql_error(&mysql));
|
||||
connected = 0;
|
||||
}
|
||||
} else {
|
||||
/* Long connection - ping the server */
|
||||
int error;
|
||||
if ((error = mysql_ping(&mysql))) {
|
||||
connected = 0;
|
||||
records = 0;
|
||||
switch (mysql_errno(&mysql)) {
|
||||
case CR_SERVER_GONE_ERROR:
|
||||
case CR_SERVER_LOST:
|
||||
ast_log(LOG_ERROR, "Server has gone away. Attempting to reconnect.\n");
|
||||
break;
|
||||
default:
|
||||
ast_log(LOG_ERROR, "Unknown connection error: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
|
||||
}
|
||||
retries--;
|
||||
if (retries) {
|
||||
goto db_reconnect;
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Retried to connect five times, giving up.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
int column_count = 0;
|
||||
char *cdrname;
|
||||
char workspace[2048], *value = NULL;
|
||||
struct column *entry;
|
||||
struct ast_str *escape = ast_str_thread_get(&escape_buf, 16);
|
||||
|
||||
ast_str_set(&sql1, 0, "INSERT INTO %s (", AS_OR(dbtable, "cdr"));
|
||||
ast_str_set(&sql2, 0, ") VALUES (");
|
||||
|
||||
AST_RWLIST_RDLOCK(&columns);
|
||||
AST_RWLIST_TRAVERSE(&columns, entry, list) {
|
||||
if (!strcmp(entry->name, "calldate")) {
|
||||
/*!\note
|
||||
* For some dumb reason, "calldate" used to be formulated using
|
||||
* the datetime the record was posted, rather than the start
|
||||
* time of the call. If someone really wants the old compatible
|
||||
* behavior, it's provided here.
|
||||
*/
|
||||
if (calldate_compat) {
|
||||
struct timeval tv = ast_tvnow();
|
||||
struct ast_tm tm;
|
||||
char timestr[128];
|
||||
ast_localtime(&tv, &tm, ast_str_strlen(cdrzone) ? ast_str_buffer(cdrzone) : NULL);
|
||||
ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
|
||||
value = ast_strdupa(timestr);
|
||||
cdrname = "calldate";
|
||||
} else {
|
||||
cdrname = "start";
|
||||
}
|
||||
} else {
|
||||
cdrname = entry->cdrname;
|
||||
}
|
||||
|
||||
/* Construct SQL */
|
||||
|
||||
/* Need the type and value to determine if we want the raw value or not */
|
||||
if (entry->staticvalue) {
|
||||
value = ast_strdupa(entry->staticvalue);
|
||||
} else if ((!strcmp(cdrname, "disposition") ||
|
||||
!strcmp(cdrname, "amaflags")) &&
|
||||
(strstr(entry->type, "int") ||
|
||||
strstr(entry->type, "dec") ||
|
||||
strstr(entry->type, "float") ||
|
||||
strstr(entry->type, "double") ||
|
||||
strstr(entry->type, "real") ||
|
||||
strstr(entry->type, "numeric") ||
|
||||
strstr(entry->type, "fixed"))) {
|
||||
ast_cdr_format_var(cdr, cdrname, &value, workspace, sizeof(workspace), 1);
|
||||
} else if (!strcmp(cdrname, "start") || !strcmp(cdrname, "answer") ||
|
||||
!strcmp(cdrname, "end")) {
|
||||
struct ast_tm tm;
|
||||
char timestr[128];
|
||||
ast_localtime(&cdr->start, &tm, ast_str_strlen(cdrzone) ? ast_str_buffer(cdrzone) : NULL);
|
||||
ast_strftime(timestr, sizeof(timestr), DATE_FORMAT, &tm);
|
||||
value = ast_strdupa(timestr);
|
||||
} else if (!strcmp(cdrname, "calldate")) {
|
||||
/* Skip calldate - the value has already been dup'd */
|
||||
} else {
|
||||
ast_cdr_format_var(cdr, cdrname, &value, workspace, sizeof(workspace), 0);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
size_t valsz;
|
||||
|
||||
if (column_count++) {
|
||||
ast_str_append(&sql1, 0, ",");
|
||||
ast_str_append(&sql2, 0, ",");
|
||||
}
|
||||
|
||||
if (!strcasecmp(cdrname, "billsec") &&
|
||||
(strstr(entry->type, "float") ||
|
||||
strstr(entry->type, "double") ||
|
||||
strstr(entry->type, "decimal") ||
|
||||
strstr(entry->type, "numeric") ||
|
||||
strstr(entry->type, "real"))) {
|
||||
|
||||
if (!ast_tvzero(cdr->answer)) {
|
||||
snprintf(workspace, sizeof(workspace), "%lf",
|
||||
(double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
|
||||
} else {
|
||||
ast_copy_string(workspace, "0", sizeof(workspace));
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(workspace)) {
|
||||
value = workspace;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcasecmp(cdrname, "duration") &&
|
||||
(strstr(entry->type, "float") ||
|
||||
strstr(entry->type, "double") ||
|
||||
strstr(entry->type, "decimal") ||
|
||||
strstr(entry->type, "numeric") ||
|
||||
strstr(entry->type, "real"))) {
|
||||
|
||||
snprintf(workspace, sizeof(workspace), "%lf",
|
||||
(double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
|
||||
|
||||
if (!ast_strlen_zero(workspace)) {
|
||||
value = workspace;
|
||||
}
|
||||
}
|
||||
|
||||
ast_str_make_space(&escape, (valsz = strlen(value)) * 2 + 1);
|
||||
mysql_real_escape_string(&mysql, ast_str_buffer(escape), value, valsz);
|
||||
|
||||
ast_str_append(&sql1, 0, "`%s`", entry->name);
|
||||
ast_str_append(&sql2, 0, "'%s'", ast_str_buffer(escape));
|
||||
}
|
||||
}
|
||||
AST_RWLIST_UNLOCK(&columns);
|
||||
|
||||
ast_debug(1, "Inserting a CDR record.\n");
|
||||
ast_str_append(&sql1, 0, "%s)", ast_str_buffer(sql2));
|
||||
|
||||
ast_debug(1, "SQL command as follows: %s\n", ast_str_buffer(sql1));
|
||||
|
||||
if (mysql_real_query(&mysql, ast_str_buffer(sql1), ast_str_strlen(sql1))) {
|
||||
ast_log(LOG_ERROR, "Failed to insert into database: (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
|
||||
mysql_close(&mysql);
|
||||
connected = 0;
|
||||
} else {
|
||||
records++;
|
||||
totalrecords++;
|
||||
}
|
||||
}
|
||||
ast_mutex_unlock(&mysql_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_strings(void)
|
||||
{
|
||||
struct unload_string *us;
|
||||
|
||||
AST_LIST_LOCK(&unload_strings);
|
||||
while ((us = AST_LIST_REMOVE_HEAD(&unload_strings, entry))) {
|
||||
ast_free(us->str);
|
||||
ast_free(us);
|
||||
}
|
||||
AST_LIST_UNLOCK(&unload_strings);
|
||||
}
|
||||
|
||||
static int my_unload_module(int reload)
|
||||
{
|
||||
struct column *entry;
|
||||
|
||||
if (!reload) {
|
||||
if (ast_cdr_unregister(name)) {
|
||||
/* If we can't unregister the backend, we can't unload the module */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ast_cli_unregister_multiple(cdr_mysql_status_cli, sizeof(cdr_mysql_status_cli) / sizeof(struct ast_cli_entry));
|
||||
|
||||
if (connected) {
|
||||
mysql_close(&mysql);
|
||||
connected = 0;
|
||||
records = 0;
|
||||
}
|
||||
|
||||
free_strings();
|
||||
|
||||
if (!reload) {
|
||||
AST_RWLIST_WRLOCK(&columns);
|
||||
}
|
||||
while ((entry = AST_RWLIST_REMOVE_HEAD(&columns, list))) {
|
||||
ast_free(entry);
|
||||
}
|
||||
if (!reload) {
|
||||
AST_RWLIST_UNLOCK(&columns);
|
||||
}
|
||||
|
||||
dbport = 0;
|
||||
if (reload) {
|
||||
return ast_cdr_backend_suspend(name);
|
||||
} else {
|
||||
/* We unregistered earlier */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int my_load_config_string(struct ast_config *cfg, const char *category, const char *variable, struct ast_str **field, const char *def)
|
||||
{
|
||||
struct unload_string *us;
|
||||
const char *tmp;
|
||||
|
||||
if (!(us = ast_calloc(1, sizeof(*us))))
|
||||
return -1;
|
||||
|
||||
if (!(*field = ast_str_create(16))) {
|
||||
ast_free(us);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp = ast_variable_retrieve(cfg, category, variable);
|
||||
|
||||
ast_str_set(field, 0, "%s", tmp ? tmp : def);
|
||||
|
||||
us->str = *field;
|
||||
|
||||
AST_LIST_LOCK(&unload_strings);
|
||||
AST_LIST_INSERT_HEAD(&unload_strings, us, entry);
|
||||
AST_LIST_UNLOCK(&unload_strings);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int my_load_config_number(struct ast_config *cfg, const char *category, const char *variable, int *field, int def)
|
||||
{
|
||||
const char *tmp;
|
||||
|
||||
tmp = ast_variable_retrieve(cfg, category, variable);
|
||||
|
||||
if (!tmp || sscanf(tmp, "%30d", field) < 1)
|
||||
*field = def;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Connect to MySQL. Initializes the connection.
|
||||
*
|
||||
* * Assumes the read-write lock for columns is held.
|
||||
* * Caller should allocate and free cfg
|
||||
* */
|
||||
static int my_connect_db(struct ast_config *cfg)
|
||||
{
|
||||
struct ast_variable *var;
|
||||
char *temp;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
char sqldesc[128];
|
||||
#ifdef HAVE_MYSQLCLIENT_BOOL
|
||||
bool my_bool_true = 1;
|
||||
#elif HAVE_MYSQLCLIENT_MY_BOOL
|
||||
my_bool my_bool_true = 1;
|
||||
#endif
|
||||
|
||||
mysql_init(&mysql);
|
||||
|
||||
if (timeout && mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout) != 0) {
|
||||
ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
|
||||
}
|
||||
|
||||
#if MYSQL_VERSION_ID >= 50013
|
||||
/* Add option for automatic reconnection */
|
||||
if (mysql_options(&mysql, MYSQL_OPT_RECONNECT, &my_bool_true) != 0) {
|
||||
ast_log(LOG_ERROR, "cdr_mysql: mysql_options returned (%d) %s\n", mysql_errno(&mysql), mysql_error(&mysql));
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((ssl_ca && ast_str_strlen(ssl_ca)) || (ssl_cert && ast_str_strlen(ssl_cert)) || (ssl_key && ast_str_strlen(ssl_key))) {
|
||||
mysql_ssl_set(&mysql,
|
||||
ssl_key ? ast_str_buffer(ssl_key) : NULL,
|
||||
ssl_cert ? ast_str_buffer(ssl_cert) : NULL,
|
||||
ssl_ca ? ast_str_buffer(ssl_ca) : NULL,
|
||||
NULL, NULL);
|
||||
}
|
||||
temp = dbsock && ast_str_strlen(dbsock) ? ast_str_buffer(dbsock) : NULL;
|
||||
|
||||
configure_connection_charset();
|
||||
|
||||
if (!mysql_real_connect(&mysql, ast_str_buffer(hostname), ast_str_buffer(dbuser), ast_str_buffer(password), ast_str_buffer(dbname), dbport, temp, ssl_ca && ast_str_strlen(ssl_ca) ? CLIENT_SSL : 0)) {
|
||||
ast_log(LOG_ERROR, "Failed to connect to mysql database %s on %s.\n", ast_str_buffer(dbname), ast_str_buffer(hostname));
|
||||
connected = 0;
|
||||
records = 0;
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS; /* May be reconnected later */
|
||||
}
|
||||
|
||||
ast_debug(1, "Successfully connected to MySQL database.\n");
|
||||
connected = 1;
|
||||
records = 0;
|
||||
connect_time = time(NULL);
|
||||
|
||||
/* Get table description */
|
||||
snprintf(sqldesc, sizeof(sqldesc), "DESC %s", dbtable ? ast_str_buffer(dbtable) : "cdr");
|
||||
if (mysql_query(&mysql, sqldesc)) {
|
||||
ast_log(LOG_ERROR, "Unable to query table description!! Logging disabled.\n");
|
||||
mysql_close(&mysql);
|
||||
connected = 0;
|
||||
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
if (!(result = mysql_store_result(&mysql))) {
|
||||
ast_log(LOG_ERROR, "Unable to query table description!! Logging disabled.\n");
|
||||
mysql_close(&mysql);
|
||||
connected = 0;
|
||||
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
struct column *entry;
|
||||
char *cdrvar = "", *staticvalue = "";
|
||||
|
||||
ast_debug(1, "Got a field '%s' of type '%s'\n", row[0], row[1]);
|
||||
/* Check for an alias or a static value */
|
||||
for (var = ast_variable_browse(cfg, "columns"); var; var = var->next) {
|
||||
if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, row[0]) == 0 ) {
|
||||
char *alias = ast_strdupa(var->name + 5);
|
||||
cdrvar = ast_strip(alias);
|
||||
ast_verb(3, "Found alias %s for column %s\n", cdrvar, row[0]);
|
||||
break;
|
||||
} else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, row[0]) == 0) {
|
||||
char *item = ast_strdupa(var->name + 6);
|
||||
item = ast_strip(item);
|
||||
if (item[0] == '"' && item[strlen(item) - 1] == '"') {
|
||||
/* Remove surrounding quotes */
|
||||
item[strlen(item) - 1] = '\0';
|
||||
item++;
|
||||
}
|
||||
staticvalue = item;
|
||||
}
|
||||
}
|
||||
|
||||
entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(row[0]) + 1 + strlen(cdrvar) + 1 + strlen(staticvalue) + 1 + strlen(row[1]) + 1);
|
||||
if (!entry) {
|
||||
ast_log(LOG_ERROR, "Out of memory creating entry for column '%s'\n", row[0]);
|
||||
mysql_free_result(result);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
entry->name = (char *)entry + sizeof(*entry);
|
||||
strcpy(entry->name, row[0]);
|
||||
|
||||
if (!ast_strlen_zero(cdrvar)) {
|
||||
entry->cdrname = entry->name + strlen(row[0]) + 1;
|
||||
strcpy(entry->cdrname, cdrvar);
|
||||
} else { /* Point to same place as the column name */
|
||||
entry->cdrname = (char *)entry + sizeof(*entry);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(staticvalue)) {
|
||||
entry->staticvalue = entry->cdrname + strlen(entry->cdrname) + 1;
|
||||
strcpy(entry->staticvalue, staticvalue);
|
||||
ast_debug(1, "staticvalue length: %d\n", (int) strlen(staticvalue) );
|
||||
entry->type = entry->staticvalue + strlen(entry->staticvalue) + 1;
|
||||
} else {
|
||||
entry->type = entry->cdrname + strlen(entry->cdrname) + 1;
|
||||
}
|
||||
strcpy(entry->type, row[1]);
|
||||
|
||||
ast_debug(1, "Entry name '%s'\n", entry->name);
|
||||
ast_debug(1, " cdrname '%s'\n", entry->cdrname);
|
||||
ast_debug(1, " static '%s'\n", entry->staticvalue);
|
||||
ast_debug(1, " type '%s'\n", entry->type);
|
||||
|
||||
AST_LIST_INSERT_TAIL(&columns, entry, list);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
static int my_load_module(int reload)
|
||||
{
|
||||
int res;
|
||||
struct ast_config *cfg;
|
||||
struct ast_variable *var;
|
||||
/* CONFIG_STATUS_FILEUNCHANGED is impossible when config_flags is always 0,
|
||||
* and it has to be zero, so a reload can be sent to tell the driver to
|
||||
* rescan the table layout. */
|
||||
struct ast_flags config_flags = { 0 };
|
||||
struct column *entry;
|
||||
struct ast_str *compat;
|
||||
|
||||
/* Cannot use a conditionally different flag, because the table layout may
|
||||
* have changed, which is not detectable by config file change detection,
|
||||
* but should still cause the configuration to be re-parsed. */
|
||||
cfg = ast_config_load(config, config_flags);
|
||||
if (cfg == CONFIG_STATUS_FILEMISSING) {
|
||||
ast_log(LOG_WARNING, "Unable to load config for mysql CDR's: %s\n", config);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
||||
ast_log(LOG_ERROR, "Unable to load configuration file '%s'\n", config);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
AST_RWLIST_WRLOCK(&columns);
|
||||
my_unload_module(1);
|
||||
}
|
||||
|
||||
var = ast_variable_browse(cfg, "global");
|
||||
if (!var) {
|
||||
/* nothing configured */
|
||||
if (reload) {
|
||||
AST_RWLIST_UNLOCK(&columns);
|
||||
}
|
||||
ast_config_destroy(cfg);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
res = 0;
|
||||
|
||||
res |= my_load_config_string(cfg, "global", "hostname", &hostname, "localhost");
|
||||
res |= my_load_config_string(cfg, "global", "dbname", &dbname, "astriskcdrdb");
|
||||
res |= my_load_config_string(cfg, "global", "user", &dbuser, "root");
|
||||
res |= my_load_config_string(cfg, "global", "sock", &dbsock, "");
|
||||
res |= my_load_config_string(cfg, "global", "table", &dbtable, "cdr");
|
||||
res |= my_load_config_string(cfg, "global", "password", &password, "");
|
||||
|
||||
res |= my_load_config_string(cfg, "global", "charset", &dbcharset, "");
|
||||
|
||||
res |= my_load_config_string(cfg, "global", "ssl_ca", &ssl_ca, "");
|
||||
res |= my_load_config_string(cfg, "global", "ssl_cert", &ssl_cert, "");
|
||||
res |= my_load_config_string(cfg, "global", "ssl_key", &ssl_key, "");
|
||||
|
||||
res |= my_load_config_number(cfg, "global", "port", &dbport, MYSQL_PORT);
|
||||
res |= my_load_config_number(cfg, "global", "timeout", &timeout, 0);
|
||||
res |= my_load_config_string(cfg, "global", "compat", &compat, "no");
|
||||
res |= my_load_config_string(cfg, "global", "cdrzone", &cdrzone, "");
|
||||
if (ast_str_strlen(cdrzone) == 0) {
|
||||
for (; var; var = var->next) {
|
||||
if (!strcasecmp(var->name, "usegmtime") && ast_true(var->value)) {
|
||||
ast_str_set(&cdrzone, 0, "UTC");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_true(ast_str_buffer(compat))) {
|
||||
calldate_compat = 1;
|
||||
} else {
|
||||
calldate_compat = 0;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
if (reload) {
|
||||
AST_RWLIST_UNLOCK(&columns);
|
||||
}
|
||||
ast_config_destroy(cfg);
|
||||
free_strings();
|
||||
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
/* Check for any aliases */
|
||||
if (!reload) {
|
||||
/* Lock, if not already */
|
||||
AST_RWLIST_WRLOCK(&columns);
|
||||
}
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&columns, list))) {
|
||||
ast_free(entry);
|
||||
}
|
||||
|
||||
ast_debug(1, "Got hostname of %s\n", ast_str_buffer(hostname));
|
||||
ast_debug(1, "Got port of %d\n", dbport);
|
||||
ast_debug(1, "Got a timeout of %d\n", timeout);
|
||||
if (ast_str_strlen(dbsock)) {
|
||||
ast_debug(1, "Got sock file of %s\n", ast_str_buffer(dbsock));
|
||||
}
|
||||
ast_debug(1, "Got user of %s\n", ast_str_buffer(dbuser));
|
||||
ast_debug(1, "Got dbname of %s\n", ast_str_buffer(dbname));
|
||||
ast_debug(1, "Got password of %s\n", ast_str_buffer(password));
|
||||
ast_debug(1, "%sunning in calldate compatibility mode\n", calldate_compat ? "R" : "Not r");
|
||||
ast_debug(1, "Dates and times are localized to %s\n", S_OR(ast_str_buffer(cdrzone), "local timezone"));
|
||||
|
||||
if (ast_str_strlen(dbcharset)) {
|
||||
ast_debug(1, "Got DB charset of %s\n", ast_str_buffer(dbcharset));
|
||||
}
|
||||
|
||||
res = my_connect_db(cfg);
|
||||
AST_RWLIST_UNLOCK(&columns);
|
||||
ast_config_destroy(cfg);
|
||||
if (res != AST_MODULE_LOAD_SUCCESS) {
|
||||
my_unload_module(0);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!reload) {
|
||||
res = ast_cdr_register(name, desc, mysql_log);
|
||||
} else {
|
||||
res = ast_cdr_backend_unsuspend(name);
|
||||
}
|
||||
if (res) {
|
||||
ast_log(LOG_ERROR, "Unable to register MySQL CDR handling\n");
|
||||
} else {
|
||||
res = ast_cli_register_multiple(cdr_mysql_status_cli, sizeof(cdr_mysql_status_cli) / sizeof(struct ast_cli_entry));
|
||||
}
|
||||
|
||||
if (res) {
|
||||
my_unload_module(0);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return my_load_module(0);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return my_unload_module(0);
|
||||
}
|
||||
|
||||
static int reload(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ast_mutex_lock(&mysql_lock);
|
||||
ret = my_load_module(1);
|
||||
ast_mutex_unlock(&mysql_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "MySQL CDR Backend",
|
||||
.support_level = AST_MODULE_SUPPORT_DEPRECATED,
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
.requires = "cdr",
|
||||
);
|
|
@ -102,7 +102,7 @@ struct adapter_pvt {
|
|||
char id[31]; /* the 'name' from mobile.conf */
|
||||
bdaddr_t addr; /* adddress of adapter */
|
||||
unsigned int inuse:1; /* are we in use ? */
|
||||
unsigned int alignment_detection:1; /* do alignment detection on this adpater? */
|
||||
unsigned int alignment_detection:1; /* do alignment detection on this adapter? */
|
||||
struct io_context *io; /*!< io context for audio connections */
|
||||
struct io_context *accept_io; /*!< io context for sco listener */
|
||||
int *sco_id; /*!< the io context id of the sco listener socket */
|
||||
|
@ -2006,7 +2006,7 @@ static int at_match_prefix(char *buf, char *prefix)
|
|||
}
|
||||
|
||||
/*!
|
||||
* \brief Read an AT message and clasify it.
|
||||
* \brief Read an AT message and classify it.
|
||||
* \param rsock an rfcomm socket
|
||||
* \param buf the buffer to store the result in
|
||||
* \param count the size of the buffer or the maximum number of characters to read
|
||||
|
@ -2156,10 +2156,12 @@ static inline const char *at_msg2str(at_message_t msg)
|
|||
* \param buf the buffer to parse (null terminated)
|
||||
* \return -1 on error (parse error) or a ECAM value on success
|
||||
*
|
||||
* Example string: *ECAV: <ccid>,<ccstatus>,<calltype>[,<processid>]
|
||||
* [,exitcause][,<number>,<type>]
|
||||
* Example:
|
||||
* \verbatim *ECAV: <ccid>,<ccstatus>,<calltype>[,<processid>]
|
||||
[,exitcause][,<number>,<type>] \endverbatim
|
||||
*
|
||||
* Example indicating busy: *ECAV: 1,7,1
|
||||
* Example indicating busy:
|
||||
* \verbatim *ECAV: 1,7,1 \endverbatim
|
||||
*/
|
||||
static int hfp_parse_ecav(struct hfp_pvt *hfp, char *buf)
|
||||
{
|
||||
|
@ -2176,7 +2178,7 @@ static int hfp_parse_ecav(struct hfp_pvt *hfp, char *buf)
|
|||
}
|
||||
|
||||
/*!
|
||||
* \brief Enable Sony Erricson extensions / indications.
|
||||
* \brief Enable Sony Ericsson extensions / indications.
|
||||
* \param hfp an hfp_pvt struct
|
||||
*/
|
||||
static int hfp_send_ecam(struct hfp_pvt *hfp)
|
||||
|
@ -3040,7 +3042,7 @@ static void msg_queue_free_and_pop(struct mbl_pvt *pvt)
|
|||
}
|
||||
|
||||
/*!
|
||||
* \brief Remove all itmes from the queue and free them.
|
||||
* \brief Remove all items from the queue and free them.
|
||||
* \param pvt a mbl_pvt structure
|
||||
*/
|
||||
static void msg_queue_flush(struct mbl_pvt *pvt)
|
||||
|
@ -3347,7 +3349,7 @@ static int handle_response_ok(struct mbl_pvt *pvt, char *buf)
|
|||
}
|
||||
break;
|
||||
case AT_CLIP:
|
||||
ast_debug(1, "[%s] caling line indication enabled\n", pvt->id);
|
||||
ast_debug(1, "[%s] calling line indication enabled\n", pvt->id);
|
||||
if (hfp_send_ecam(pvt->hfp) || msg_queue_push(pvt, AT_OK, AT_ECAM)) {
|
||||
ast_debug(1, "[%s] error enabling Sony Ericsson call monitoring extensions\n", pvt->id);
|
||||
goto e_return;
|
||||
|
@ -3567,7 +3569,7 @@ static int handle_response_ciev(struct mbl_pvt *pvt, char *buf)
|
|||
if (pvt->owner) {
|
||||
ast_debug(1, "[%s] hanging up owner\n", pvt->id);
|
||||
if (mbl_queue_hangup(pvt)) {
|
||||
ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
|
||||
ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnecting...\n", pvt->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -3601,7 +3603,7 @@ static int handle_response_ciev(struct mbl_pvt *pvt, char *buf)
|
|||
handle_response_busy(pvt);
|
||||
}
|
||||
if (mbl_queue_hangup(pvt)) {
|
||||
ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
|
||||
ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnecting...\n", pvt->id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -4431,7 +4433,7 @@ static struct adapter_pvt *mbl_load_adapter(struct ast_config *cfg, const char *
|
|||
|
||||
/* bind the sco listener socket */
|
||||
if (sco_bind(adapter) < 0) {
|
||||
ast_log(LOG_ERROR, "Skipping adapter %s. Error binding audio connection listerner socket.\n", adapter->id);
|
||||
ast_log(LOG_ERROR, "Skipping adapter %s. Error binding audio connection listener socket.\n", adapter->id);
|
||||
goto e_destroy_io;
|
||||
}
|
||||
|
||||
|
@ -4443,7 +4445,7 @@ static struct adapter_pvt *mbl_load_adapter(struct ast_config *cfg, const char *
|
|||
|
||||
/* start the sco listener for this adapter */
|
||||
if (ast_pthread_create_background(&adapter->sco_listener_thread, NULL, do_sco_listen, adapter)) {
|
||||
ast_log(LOG_ERROR, "Skipping adapter %s. Error creating audio connection listerner thread.\n", adapter->id);
|
||||
ast_log(LOG_ERROR, "Skipping adapter %s. Error creating audio connection listener thread.\n", adapter->id);
|
||||
goto e_remove_sco;
|
||||
}
|
||||
|
||||
|
@ -4499,7 +4501,7 @@ static struct mbl_pvt *mbl_load_device(struct ast_config *cfg, const char *cat)
|
|||
}
|
||||
AST_RWLIST_UNLOCK(&adapters);
|
||||
if (!adapter) {
|
||||
ast_log(LOG_ERROR, "Skiping device %s. Unknown adapter '%s' specified.\n", cat, adapter_str);
|
||||
ast_log(LOG_ERROR, "Skipping device %s. Unknown adapter '%s' specified.\n", cat, adapter_str);
|
||||
goto e_return;
|
||||
}
|
||||
|
||||
|
|
|
@ -2055,7 +2055,7 @@ int onOutgoingCall(ooCallData *call)
|
|||
}
|
||||
ooCallAddAliasDialedDigits(call, p->caller_dialedDigits);
|
||||
} else if (!ast_strlen_zero(p->callerid_num)) {
|
||||
if (ooIsDailedDigit(p->callerid_num)) {
|
||||
if (ooIsDialedDigit(p->callerid_num)) {
|
||||
if (gH323Debug) {
|
||||
ast_verb(0, "setting callid number %s\n", p->callerid_num);
|
||||
}
|
||||
|
@ -2136,7 +2136,7 @@ int onNewCallCreated(ooCallData *call)
|
|||
}
|
||||
ooCallAddAliasDialedDigits(call, p->caller_dialedDigits);
|
||||
} else if (!ast_strlen_zero(p->callerid_num)) {
|
||||
if (ooIsDailedDigit(p->callerid_num)) {
|
||||
if (ooIsDialedDigit(p->callerid_num)) {
|
||||
if (gH323Debug) {
|
||||
ast_verb(0, "setting callid number %s\n", p->callerid_num);
|
||||
}
|
||||
|
@ -2148,7 +2148,7 @@ int onNewCallCreated(ooCallData *call)
|
|||
|
||||
|
||||
if (!ast_strlen_zero(p->exten)) {
|
||||
if (ooIsDailedDigit(p->exten)) {
|
||||
if (ooIsDialedDigit(p->exten)) {
|
||||
ooCallSetCalledPartyNumber(call, p->exten);
|
||||
ooCallAddRemoteAliasDialedDigits(call, p->exten);
|
||||
} else {
|
||||
|
|
|
@ -75,7 +75,7 @@ To run the stack test application chansetup
|
|||
3. For running calling instance
|
||||
|
||||
./h323peer [--use-ip ip] -n <number of calls> -duration <call duration>
|
||||
-interval <inetrval between successive calls> destination
|
||||
-interval <interval between successive calls> destination
|
||||
|
||||
where all times are in seconds. Interval of 0 means next call will be placed
|
||||
after current call finishes. "destination" is the dotted ip address of the
|
||||
|
|
|
@ -417,7 +417,7 @@ int decodeDynBitString (OOCTXT* pctxt, ASN1DynBitStr* pBitStr)
|
|||
ASN1OCTET* ptmp;
|
||||
int nbits, stat = ASN_OK;
|
||||
|
||||
/* If "fast copy" option is not set (ASN1FATSCOPY) or if constructed,
|
||||
/* If "fast copy" option is not set (ASN1FASTCOPY) or if constructed,
|
||||
* copy the bit string value into a dynamic memory buffer;
|
||||
* otherwise, store the pointer to the value in the decode
|
||||
* buffer in the data pointer argument. */
|
||||
|
|
|
@ -1007,7 +1007,7 @@ int encode2sCompBinInt (OOCTXT* pctxt, ASN1INT value)
|
|||
{
|
||||
/* 10.4.6 A minimum octet 2's-complement-binary-integer encoding */
|
||||
/* of the whole number has a field width that is a multiple of 8 */
|
||||
/* bits and also satisifies the condition that the leading 9 bits */
|
||||
/* bits and also satisfies the condition that the leading 9 bits */
|
||||
/* field shall not be all zeros and shall not be all ones. */
|
||||
|
||||
/* first encode integer value into a local buffer */
|
||||
|
@ -1048,7 +1048,7 @@ static int encodeNonNegBinInt (OOCTXT* pctxt, ASN1UINT value)
|
|||
/* 10.3.6 A minimum octet non-negative binary integer encoding of */
|
||||
/* the whole number (which does not predetermine the number of */
|
||||
/* octets to be used for the encoding) has a field which is a */
|
||||
/* multiple of 8 bits and also satisifies the condition that the */
|
||||
/* multiple of 8 bits and also satisfies the condition that the */
|
||||
/* leading eight bits of the field shall not be zero unless the */
|
||||
/* field is precisely 8 bits long. */
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ extern "C" {
|
|||
* SEQUENCE OF, SET OF, or CHOICE construct is parsed.
|
||||
*
|
||||
* @param name For SEQUENCE, SET, or CHOICE, this is the name of the
|
||||
* element as defined in the ASN.1 defination. For
|
||||
* element as defined in the ASN.1 definition. For
|
||||
* SEQUENCE OF or SET OF, this is set to the name
|
||||
* "element".
|
||||
* @param index For SEQUENCE, SET, or CHOICE, this is not used and is
|
||||
|
@ -68,7 +68,7 @@ typedef void (*StartElement) (const char* name, int index) ;
|
|||
* SEQUENCE, SET, SEQUENCE OF, SET OF, or CHOICE construct.
|
||||
*
|
||||
* @param name For SEQUENCE, SET, or CHOICE, this is the name of the
|
||||
* element as defined in the ASN.1 defination. For
|
||||
* element as defined in the ASN.1 definition. For
|
||||
* SEQUENCE OF or SET OF, this is set to the name
|
||||
* "element".
|
||||
* @param index For SEQUENCE, SET, or CHOICE, this is not used and is
|
||||
|
@ -194,7 +194,7 @@ typedef void (*EnumValue) (ASN1UINT value) ;
|
|||
* within a decode function when an ASN.1 open type is parsed.
|
||||
*
|
||||
* @param numocts Number of octets in the parsed value.
|
||||
* @param data Pointer to byet array contain in tencoded ASN.1
|
||||
* @param data Pointer to byte array contain in tencoded ASN.1
|
||||
* value.
|
||||
* @return - none
|
||||
*/
|
||||
|
|
|
@ -1926,7 +1926,7 @@ typedef struct EXTERN H225SecurityCapabilities {
|
|||
} m;
|
||||
H225NonStandardParameter nonStandard;
|
||||
H225SecurityServiceMode encryption;
|
||||
H225SecurityServiceMode authenticaton;
|
||||
H225SecurityServiceMode authentication;
|
||||
H225SecurityServiceMode integrity;
|
||||
} H225SecurityCapabilities;
|
||||
|
||||
|
|
|
@ -6357,14 +6357,14 @@ EXTERN int asn1PD_H225SecurityCapabilities (OOCTXT* pctxt, H225SecurityCapabilit
|
|||
|
||||
invokeEndElement (pctxt, "encryption", -1);
|
||||
|
||||
/* decode authenticaton */
|
||||
/* decode authentication */
|
||||
|
||||
invokeStartElement (pctxt, "authenticaton", -1);
|
||||
invokeStartElement (pctxt, "authentication", -1);
|
||||
|
||||
stat = asn1PD_H225SecurityServiceMode (pctxt, &pvalue->authenticaton);
|
||||
stat = asn1PD_H225SecurityServiceMode (pctxt, &pvalue->authentication);
|
||||
if (stat != ASN_OK) return stat;
|
||||
|
||||
invokeEndElement (pctxt, "authenticaton", -1);
|
||||
invokeEndElement (pctxt, "authentication", -1);
|
||||
|
||||
/* decode integrity */
|
||||
|
||||
|
|
|
@ -4408,9 +4408,9 @@ EXTERN int asn1PE_H225SecurityCapabilities (OOCTXT* pctxt, H225SecurityCapabilit
|
|||
stat = asn1PE_H225SecurityServiceMode (pctxt, &pvalue->encryption);
|
||||
if (stat != ASN_OK) return stat;
|
||||
|
||||
/* encode authenticaton */
|
||||
/* encode authentication */
|
||||
|
||||
stat = asn1PE_H225SecurityServiceMode (pctxt, &pvalue->authenticaton);
|
||||
stat = asn1PE_H225SecurityServiceMode (pctxt, &pvalue->authentication);
|
||||
if (stat != ASN_OK) return stat;
|
||||
|
||||
/* encode integrity */
|
||||
|
|
|
@ -265,7 +265,7 @@ void* memHeapAlloc (void** ppvMemHeap, int nbytes)
|
|||
if (nunits <= (ASN1UINT)pElem_nunits (pElem)) {
|
||||
RTMEMDIAG3
|
||||
("memHeapAlloc: "
|
||||
"found an exisiting free element 0x%x, size %d\n",
|
||||
"found an existing free element 0x%x, size %d\n",
|
||||
pElem, (pElem_nunits (pElem) * 8u));
|
||||
|
||||
if (pMemBlk->freeElemOff ==
|
||||
|
@ -373,7 +373,7 @@ void* memHeapAlloc (void** ppvMemHeap, int nbytes)
|
|||
CHECKMEMBLOCK (pMemHeap, pMemBlk);
|
||||
}
|
||||
else {
|
||||
ast_mutex_unlock(&pMemHeap->pLock);
|
||||
ast_mutex_unlock(&pMemHeap->pLock);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -753,9 +753,10 @@ void* memHeapRealloc (void** ppvMemHeap, void* mem_p, int nbytes_)
|
|||
if (newMemBlk == 0)
|
||||
return 0;
|
||||
pMemLink->pMemBlk = newMemBlk;
|
||||
}
|
||||
else
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
*(int*)(((char*)pMemLink) + sizeof (OSMemLink)) = nbytes_;
|
||||
return pMemLink->pMemBlk;
|
||||
}
|
||||
|
@ -1132,7 +1133,7 @@ void* memHeapMarkSaved (void** ppvMemHeap, const void* mem_p,
|
|||
RTMEMDIAG2 ("memHeapMarkSaved: the element 0x%x is "
|
||||
"already free!\n", pElem);
|
||||
|
||||
ast_mutex_unlock(&pMemHeap->pLock);
|
||||
ast_mutex_unlock(&pMemHeap->pLock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1151,9 +1152,10 @@ void* memHeapMarkSaved (void** ppvMemHeap, const void* mem_p,
|
|||
CLEAR_SAVED (pMemBlk, pElem);
|
||||
nsaved = pMemBlk->nsaved;
|
||||
}
|
||||
else
|
||||
ast_mutex_unlock(&pMemHeap->pLock);
|
||||
return 0;
|
||||
else {
|
||||
ast_mutex_unlock(&pMemHeap->pLock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (saved && nsaved > 0)
|
||||
pMemLink->blockType |= RTMEMSAVED;
|
||||
|
@ -1212,7 +1214,7 @@ static OSMemLink* memHeapAddBlock (OSMemLink** ppMemLink,
|
|||
|
||||
/* if pMemBlk has RTMEMLINK flags it means that it is allocated
|
||||
* cooperatively with OSMemLink, and we don't need to do additional
|
||||
* allocations for it. Just use pointer's arithemtic. */
|
||||
* allocations for it. Just use pointer's arithmetic. */
|
||||
|
||||
if (blockType & RTMEMLINK)
|
||||
pMemLink = (OSMemLink*) (((ASN1OCTET*)pMemBlk) - sizeof (OSMemLink));
|
||||
|
|
|
@ -783,7 +783,7 @@ OOH323CallData* ooFindCallByToken(const char *callToken)
|
|||
|
||||
|
||||
|
||||
/* Checks whether session with suplied ID and direction is already active*/
|
||||
/* Checks whether session with supplied ID and direction is already active*/
|
||||
ASN1BOOL ooIsSessionEstablished(OOH323CallData *call, int sessionID, char* dir)
|
||||
{
|
||||
OOLogicalChannel * temp = NULL;
|
||||
|
|
|
@ -294,7 +294,7 @@ EXTERN int ooCapabilityDisableDTMFQ931Keypad(struct OOH323CallData *call);
|
|||
* and txframes parameters to the endpoint or call.(ex. G711, G728, G723.1,
|
||||
* G729)
|
||||
* @param call Handle to a call. If this is not Null, then
|
||||
* capability is added to call's remote enpoint
|
||||
* capability is added to call's remote endpoint
|
||||
* capability list, else it is added to local H323
|
||||
* endpoint list.
|
||||
* @param cap Type of G711 capability to be added.
|
||||
|
@ -326,7 +326,7 @@ EXTERN int ooCapabilityAddSimpleCapability
|
|||
* to local endpoints capability list or to remote endpoints capability list or
|
||||
* to a call's capability list.
|
||||
* @param call Handle to a call. If this is not Null, then
|
||||
* capability is added to call's remote enpoint
|
||||
* capability is added to call's remote endpoint
|
||||
* capability list, else it is added to local H323
|
||||
* endpoint list.
|
||||
* @param cap Type of GSM capability to be added.
|
||||
|
@ -357,7 +357,7 @@ int ooCapabilityAddGSMCapability(struct OOH323CallData *call, int cap,
|
|||
* capability list or to remote endpoints capability list or to a call's
|
||||
* capability list.
|
||||
* @param call Handle to a call. If this is not Null, then
|
||||
* capability is added to call's remote enpoint
|
||||
* capability is added to call's remote endpoint
|
||||
* capability list, else it is added to local H323
|
||||
* endpoint list.
|
||||
* @param sqcifMPI Minimum picture interval for encoding/decoding
|
||||
|
@ -396,7 +396,7 @@ EXTERN int ooCapabilityAddH263VideoCapability(struct OOH323CallData *call,
|
|||
/**
|
||||
* This function is an helper function to ooCapabilityAddH263VideoCapability.
|
||||
* @param call Handle to a call. If this is not Null, then
|
||||
* capability is added to call's remote enpoint
|
||||
* capability is added to call's remote endpoint
|
||||
* capability list, else it is added to local H323
|
||||
* endpoint list.
|
||||
* @param sqcifMPI Minimum picture interval for encoding/decoding
|
||||
|
@ -717,11 +717,11 @@ EXTERN int ooAppendCapToCapPrefs(struct OOH323CallData *call, int cap);
|
|||
EXTERN int ooChangeCapPrefOrder(struct OOH323CallData *call, int cap, int pos);
|
||||
|
||||
/**
|
||||
* This function is used to preppend a particular capability to preference
|
||||
* This function is used to prepend a particular capability to preference
|
||||
* list.
|
||||
* @param call Handle to call, if call's preference list has to be modified
|
||||
* else NULL, to modify endpoint's preference list.
|
||||
* @param cap Capability to be preppended.
|
||||
* @param cap Capability to be prepended.
|
||||
*
|
||||
* @return OO_OK, on success. OO_FAILED, otherwise.
|
||||
*/
|
||||
|
|
|
@ -186,7 +186,7 @@ int ooReadAndProcessStackCommand()
|
|||
if(!pCall) {
|
||||
OOTRACEINFO2("Call \"%s\" does not exist\n",
|
||||
(char*)cmd.param1);
|
||||
OOTRACEINFO1("Call migth be cleared/closed\n");
|
||||
OOTRACEINFO1("Call might be cleared/closed\n");
|
||||
}
|
||||
else {
|
||||
ooSendProgress(ooFindCallByToken((char*)cmd.param1));
|
||||
|
@ -200,7 +200,7 @@ int ooReadAndProcessStackCommand()
|
|||
if(!pCall) {
|
||||
OOTRACEINFO2("Call \"%s\" does not exist\n",
|
||||
(char*)cmd.param1);
|
||||
OOTRACEINFO1("Call migth be cleared/closed\n");
|
||||
OOTRACEINFO1("Call might be cleared/closed\n");
|
||||
}
|
||||
else {
|
||||
ooSendAlerting(ooFindCallByToken((char*)cmd.param1));
|
||||
|
|
|
@ -1231,7 +1231,7 @@ int ooGkClientHandleRegistrationConfirm
|
|||
(&pGkClient->ctxt, sizeof(ooGkClientTimerCb));
|
||||
if(!cbData)
|
||||
{
|
||||
OOTRACEERR1("Error:Failed to allocate memory for Regisration timer."
|
||||
OOTRACEERR1("Error:Failed to allocate memory for Registration timer."
|
||||
"\n");
|
||||
pGkClient->state = GkClientFailed;
|
||||
return OO_FAILED;
|
||||
|
@ -1927,7 +1927,7 @@ int ooGkClientSendAdmissionRequest
|
|||
(&pGkClient->ctxt, sizeof(ooGkClientTimerCb));
|
||||
if(!cbData)
|
||||
{
|
||||
OOTRACEERR1("Error:Failed to allocate memory for Regisration timer."
|
||||
OOTRACEERR1("Error:Failed to allocate memory for Registration timer."
|
||||
"\n");
|
||||
pGkClient->state = GkClientFailed;
|
||||
ast_mutex_unlock(&pGkClient->Lock);
|
||||
|
|
|
@ -318,7 +318,7 @@ EXTERN int ooGkClientHandleRASMessage
|
|||
|
||||
|
||||
/**
|
||||
* This function is used to send a message on Gatekeeper clien't RAS channel.
|
||||
* This function is used to send a message on Gatekeeper client RAS channel.
|
||||
* @param pGkClient Handle to the gatekeeper client.
|
||||
* @param pRasMsg Handle to Ras message to be sent.
|
||||
*
|
||||
|
|
|
@ -78,7 +78,7 @@ OOLogicalChannel* ooAddNewLogicalChannel(OOH323CallData *call, int channelNo,
|
|||
OOTRACEDBGC3("Using configured media info (%s, %s)\n", call->callType,
|
||||
call->callToken);
|
||||
pNewChannel->localRtpPort = pMediaInfo->lMediaRedirPort ? pMediaInfo->lMediaRedirPort : pMediaInfo->lMediaPort;
|
||||
/* check MediaRedirPort here because RedirCPort is ReditPort + 1 and can't be 0 ;) */
|
||||
/* check MediaRedirPort here because RedirCPort is RedirPort + 1 and can't be 0 ;) */
|
||||
pNewChannel->localRtcpPort = pMediaInfo->lMediaRedirPort ? pMediaInfo->lMediaRedirCPort : pMediaInfo->lMediaCntrlPort;
|
||||
/* If user application has not specified a specific ip and is using
|
||||
multihomed mode, substitute appropriate ip.
|
||||
|
|
|
@ -537,8 +537,6 @@ int ooPDWrite(struct pollfd *pfds, int nfds, int fd)
|
|||
int ooGetLocalIPAddress(char * pIPAddrs)
|
||||
{
|
||||
int ret;
|
||||
struct hostent *hp;
|
||||
struct ast_hostent phost;
|
||||
char hostname[100];
|
||||
|
||||
if(pIPAddrs == NULL)
|
||||
|
@ -546,20 +544,11 @@ int ooGetLocalIPAddress(char * pIPAddrs)
|
|||
ret = gethostname(hostname, 100);
|
||||
if(ret == 0)
|
||||
{
|
||||
if ((hp = ast_gethostbyname(hostname, &phost))) {
|
||||
if (hp->h_addrtype == AF_INET6) {
|
||||
struct in6_addr i;
|
||||
memcpy(&i, hp->h_addr, sizeof(i));
|
||||
strcpy(pIPAddrs, (inet_ntop(AF_INET6, &i,
|
||||
hostname, sizeof(hostname))) == NULL ? "::1" :
|
||||
inet_ntop(AF_INET6, &i, hostname, sizeof(hostname)));
|
||||
} else {
|
||||
struct in_addr i;
|
||||
memcpy(&i, hp->h_addr, sizeof(i));
|
||||
strcpy(pIPAddrs, (ast_inet_ntoa(i) == NULL) ? "127.0.0.1" : ast_inet_ntoa(i));
|
||||
}
|
||||
} else {
|
||||
struct ast_sockaddr addr = { {0,} };
|
||||
if (ast_sockaddr_resolve_first_af(&addr, hostname, PARSE_PORT_FORBID, AF_UNSPEC)) {
|
||||
return -1;
|
||||
} else {
|
||||
strcpy(pIPAddrs, ast_sockaddr_stringify_addr(&addr));
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
|
|
@ -318,7 +318,7 @@ EXTERN int ooSocketSendTo(OOSOCKET socket, const ASN1OCTET* pdata,
|
|||
|
||||
/**
|
||||
* This function is used for synchronous monitoring of multiple sockets.
|
||||
* For more information refer to documnetation of "select" system call.
|
||||
* For more information refer to documentation of "select" system call.
|
||||
*
|
||||
* @param nfds The highest numbered descriptor to be monitored
|
||||
* plus one.
|
||||
|
|
|
@ -30,7 +30,7 @@ OOBOOL ooUtilsIsStrEmpty (const char* str)
|
|||
}
|
||||
|
||||
|
||||
OOBOOL ooIsDailedDigit(const char* str)
|
||||
OOBOOL ooIsDialedDigit(const char* str)
|
||||
{
|
||||
if(str == NULL || *str =='\0') { return FALSE; }
|
||||
while(*str != '\0')
|
||||
|
|
|
@ -48,6 +48,6 @@ EXTERN OOBOOL ooUtilsIsStrEmpty (const char * str);
|
|||
* @param str String to test
|
||||
* @return TRUE if string contains all digits; FALSE otherwise
|
||||
*/
|
||||
EXTERN OOBOOL ooIsDailedDigit(const char* str);
|
||||
EXTERN OOBOOL ooIsDialedDigit(const char* str);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -607,7 +607,7 @@ EXTERN int initContextBuffer
|
|||
/**
|
||||
* This function initializes a context block. It makes sure that if the block
|
||||
* was not previosly initialized, that all key working parameters are set to
|
||||
* thier correct initial state values (i.e. declared within a function as a
|
||||
* their correct initial state values (i.e. declared within a function as a
|
||||
* normal working variable), it is required that they invoke this function
|
||||
* before using it.
|
||||
*
|
||||
|
|
|
@ -297,7 +297,7 @@ int ooCreateH225Connection(OOH323CallData *call)
|
|||
{
|
||||
call->pH225Channel->sock = channelSocket;
|
||||
|
||||
OOTRACEINFO3("H2250 transmiter channel creation - successful "
|
||||
OOTRACEINFO3("H2250 transmitter channel creation - successful "
|
||||
"(%s, %s)\n", call->callType, call->callToken);
|
||||
|
||||
/* If multihomed, get ip from socket */
|
||||
|
|
|
@ -2559,7 +2559,7 @@ int ooOnReceivedRequestChannelClose(OOH323CallData *call,
|
|||
if (lChannel->state == OO_LOGICALCHAN_ESTABLISHED) {
|
||||
ret = ooSendCloseLogicalChannel(call, lChannel);
|
||||
if (ret != OO_OK) {
|
||||
OOTRACEERR3("ERROR:Failed to build CloseLgicalChannel message(%s, %s)\n",
|
||||
OOTRACEERR3("ERROR:Failed to build CloseLogicalChannel message(%s, %s)\n",
|
||||
call->callType, call->callToken);
|
||||
return OO_FAILED;
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ EXTERN int ooSendMasterSlaveDeterminationReject (struct OOH323CallData* call);
|
|||
* MasterSlaveDetermination procedure.
|
||||
* @param call Handle to the call for which MasterSlaveReject is
|
||||
* received.
|
||||
* @param reject Poinetr to the received reject message.
|
||||
* @param reject Pointer to the received reject message.
|
||||
*
|
||||
* @return OO_OK, on success. OO_FAILED, on failure.
|
||||
*/
|
||||
|
@ -271,7 +271,7 @@ EXTERN int ooOnReceivedUserInputIndication
|
|||
(OOH323CallData *call, H245UserInputIndication *indication);
|
||||
|
||||
/**
|
||||
* This function is called on receiving a TreminalCapabilitySetAck message.
|
||||
* This function is called on receiving a TerminalCapabilitySetAck message.
|
||||
* If the MasterSlaveDetermination process is also over, this function
|
||||
* initiates the process of opening logical channels.
|
||||
* @param call Pointer to call for which TCSAck is received.
|
||||
|
|
|
@ -616,7 +616,7 @@ int ooOnReceivedSetup(OOH323CallData *call, Q931Message *q931Msg)
|
|||
removeEventHandler(call->pctxt);
|
||||
return OO_FAILED;
|
||||
}
|
||||
/* For now, just add decoded fast start elemts to list. This list
|
||||
/* For now, just add decoded fast start elements to list. This list
|
||||
will be processed at the time of sending CONNECT message. */
|
||||
dListAppend(call->pctxt, &call->remoteFastStartOLCs, olc);
|
||||
}
|
||||
|
@ -1062,16 +1062,17 @@ int ooOnReceivedAlerting(OOH323CallData *call, Q931Message *q931Msg)
|
|||
if(q931Msg->userInfo->h323_uu_pdu.m.h245TunnelingPresent &&
|
||||
q931Msg->userInfo->h323_uu_pdu.h245Tunneling &&
|
||||
OO_TESTFLAG (call->flags, OO_M_TUNNELING)) {
|
||||
if (alerting->m.h245AddressPresent)
|
||||
if (alerting->m.h245AddressPresent) {
|
||||
OOTRACEINFO3("Tunneling and h245address provided."
|
||||
"Giving preference to Tunneling (%s, %s)\n",
|
||||
call->callType, call->callToken);
|
||||
if (call->h225version >= 4) {
|
||||
ret =ooSendTCSandMSD(call);
|
||||
}
|
||||
if (ret != OO_OK)
|
||||
return ret;
|
||||
|
||||
}
|
||||
if (call->h225version >= 4) {
|
||||
ret =ooSendTCSandMSD(call);
|
||||
}
|
||||
if (ret != OO_OK) {
|
||||
return ret;
|
||||
}
|
||||
} else if(alerting->m.h245AddressPresent) {
|
||||
if (OO_TESTFLAG (call->flags, OO_M_TUNNELING))
|
||||
{
|
||||
|
@ -1314,15 +1315,17 @@ int ooOnReceivedProgress(OOH323CallData *call, Q931Message *q931Msg)
|
|||
if(q931Msg->userInfo->h323_uu_pdu.m.h245TunnelingPresent &&
|
||||
q931Msg->userInfo->h323_uu_pdu.h245Tunneling &&
|
||||
OO_TESTFLAG (call->flags, OO_M_TUNNELING)) {
|
||||
if (progress->m.h245AddressPresent)
|
||||
if (progress->m.h245AddressPresent) {
|
||||
OOTRACEINFO3("Tunneling and h245address provided."
|
||||
"Giving preference to Tunneling (%s, %s)\n",
|
||||
call->callType, call->callToken);
|
||||
if (call->h225version >= 4) {
|
||||
ret =ooSendTCSandMSD(call);
|
||||
}
|
||||
if (ret != OO_OK)
|
||||
}
|
||||
if (call->h225version >= 4) {
|
||||
ret =ooSendTCSandMSD(call);
|
||||
}
|
||||
if (ret != OO_OK) {
|
||||
return ret;
|
||||
}
|
||||
} else if(progress->m.h245AddressPresent) {
|
||||
if (OO_TESTFLAG (call->flags, OO_M_TUNNELING))
|
||||
{
|
||||
|
|
|
@ -65,7 +65,7 @@ int ooH323EpInitialize
|
|||
}
|
||||
|
||||
/* Initialize default port ranges that will be used by stack.
|
||||
Apps can override these by explicitely setting port ranges
|
||||
Apps can override these by explicitly setting port ranges
|
||||
*/
|
||||
|
||||
gH323ep.tcpPorts.start = TCPPORTSSTART;
|
||||
|
|
|
@ -192,11 +192,13 @@ EXTERN int ooQ931Decode
|
|||
screening indicators ;-) */
|
||||
if(ie->discriminator == Q931CallingPartyNumberIE)
|
||||
{
|
||||
int numoffset=1;
|
||||
OOTRACEDBGB1(" CallingPartyNumber IE = {\n");
|
||||
if(ie->length < OO_MAX_NUMBER_LENGTH)
|
||||
if(!(0x80 & ie->data[0])) numoffset = 2;
|
||||
|
||||
if( (ie->length >= numoffset) &&
|
||||
(ie->length < OO_MAX_NUMBER_LENGTH) )
|
||||
{
|
||||
int numoffset=1;
|
||||
if(!(0x80 & ie->data[0])) numoffset = 2;
|
||||
memcpy(number, ie->data+numoffset,ie->length-numoffset);
|
||||
number[ie->length-numoffset]='\0';
|
||||
OOTRACEDBGB2(" %s\n", number);
|
||||
|
@ -204,7 +206,7 @@ EXTERN int ooQ931Decode
|
|||
ooCallSetCallingPartyNumber(call, number);
|
||||
}
|
||||
else{
|
||||
OOTRACEERR3("Error:Calling party number too long. (%s, %s)\n",
|
||||
OOTRACEERR3("Error:Calling party number outside range. (%s, %s)\n",
|
||||
call->callType, call->callToken);
|
||||
}
|
||||
OOTRACEDBGB1(" }\n");
|
||||
|
@ -214,7 +216,8 @@ EXTERN int ooQ931Decode
|
|||
if(ie->discriminator == Q931CalledPartyNumberIE)
|
||||
{
|
||||
OOTRACEDBGB1(" CalledPartyNumber IE = {\n");
|
||||
if(ie->length < OO_MAX_NUMBER_LENGTH)
|
||||
if( (ie->length >= 1) &&
|
||||
(ie->length < OO_MAX_NUMBER_LENGTH) )
|
||||
{
|
||||
memcpy(number, ie->data+1,ie->length-1);
|
||||
number[ie->length-1]='\0';
|
||||
|
@ -223,7 +226,7 @@ EXTERN int ooQ931Decode
|
|||
ooCallSetCalledPartyNumber(call, number);
|
||||
}
|
||||
else{
|
||||
OOTRACEERR3("Error:Calling party number too long. (%s, %s)\n",
|
||||
OOTRACEERR3("Error:Calling party number outside range. (%s, %s)\n",
|
||||
call->callType, call->callToken);
|
||||
}
|
||||
OOTRACEDBGB1(" }\n");
|
||||
|
@ -1210,7 +1213,7 @@ int ooSetFastStartResponse(OOH323CallData *pCall, Q931Message *pQ931msg,
|
|||
pChannel = ooFindLogicalChannelByLogicalChannelNo
|
||||
(pCall, olc->forwardLogicalChannelNumber);
|
||||
|
||||
/* start receive and tramsmit channel listening */
|
||||
/* start receive and transmit channel listening */
|
||||
if(dir & OORX)
|
||||
{
|
||||
strcpy(pChannel->remoteIP, remoteMediaControlIP);
|
||||
|
@ -2025,7 +2028,7 @@ int ooSendStatusInquiry(OOH323CallData *call)
|
|||
/* OOCTXT *pctxt = &gH323ep.msgctxt; */
|
||||
OOCTXT *pctxt = call->msgctxt;
|
||||
|
||||
OOTRACEDBGC3("Building StatusInquryMsg (%s, %s)\n", call->callType,
|
||||
OOTRACEDBGC3("Building StatusInquiryMsg (%s, %s)\n", call->callType,
|
||||
call->callToken);
|
||||
ret = ooCreateQ931Message(pctxt, &q931msg, Q931StatusEnquiryMsg);
|
||||
if(ret != OO_OK)
|
||||
|
@ -2987,7 +2990,7 @@ int ooH323MakeCall_helper(OOH323CallData *call)
|
|||
}
|
||||
if(!epCap)
|
||||
{
|
||||
OOTRACEWARN4("Warn:Preferred capability %s is abscent in "
|
||||
OOTRACEWARN4("Warn:Preferred capability %s is absent in "
|
||||
"capability list. (%s, %s)\n",
|
||||
ooGetCapTypeText(call->capPrefs.order[k]),
|
||||
call->callType, call->callToken);
|
||||
|
|
|
@ -582,7 +582,7 @@ EXTERN int ooH323MakeCall_helper(struct OOH323CallData *call);
|
|||
* @param dest Destination string to be parsed.
|
||||
* @param parsedIP Pointer to buffer in which parsed ip:port will be returned.
|
||||
* @param len Length of the buffer passed.
|
||||
* @param aliasList Aliase List in which new aliases will be added.
|
||||
* @param aliasList Aliases List in which new aliases will be added.
|
||||
*
|
||||
* @return OO_OK, on success. OO_FAILED, on failure.
|
||||
*/
|
||||
|
|
|
@ -93,7 +93,7 @@ static char *wait_result(void)
|
|||
fprintf(stderr, "Got %d/%d bytes of audio\n", res, bytes);
|
||||
#endif
|
||||
bytes += res;
|
||||
/* Prentend we detected some audio after 3 seconds */
|
||||
/* Pretend we detected some audio after 3 seconds */
|
||||
if (bytes > 16000 * 3) {
|
||||
return "Sample Message";
|
||||
bytes = 0;
|
||||
|
|
|
@ -348,7 +348,7 @@ sub music_dir_cache {
|
|||
}
|
||||
|
||||
# this function will fill the @masterCacheList of all the files that
|
||||
# need to have text2speeced ulaw files of their names generated
|
||||
# need to have text2speech ulaw files of their names generated
|
||||
sub music_dir_cache_genlist {
|
||||
my $dir = shift;
|
||||
if (!opendir(THEDIR, rmts($MUSIC.$dir))) {
|
||||
|
|
|
@ -24,8 +24,14 @@
|
|||
*
|
||||
* See Also:
|
||||
* \arg \ref AstCREDITS
|
||||
* \arg \ref Config_agent
|
||||
* \arg \ref agents.conf "Config_agent"
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \page agents.conf agents.conf
|
||||
* \verbinclude agents.conf.sample
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
@ -456,17 +462,11 @@ struct agents_cfg {
|
|||
struct ao2_container *agents;
|
||||
};
|
||||
|
||||
static const char *agent_type_blacklist[] = {
|
||||
"general",
|
||||
"agents",
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct aco_type agent_type = {
|
||||
.type = ACO_ITEM,
|
||||
.name = "agent-id",
|
||||
.category_match = ACO_BLACKLIST_ARRAY,
|
||||
.category = (const char *)agent_type_blacklist,
|
||||
.category_match = ACO_BLACKLIST_EXACT,
|
||||
.category = "general",
|
||||
.item_alloc = agent_cfg_alloc,
|
||||
.item_find = agent_cfg_find,
|
||||
.item_offset = offsetof(struct agents_cfg, agents),
|
||||
|
@ -652,8 +652,6 @@ static struct ao2_container *agents;
|
|||
* \brief Lock the agent.
|
||||
*
|
||||
* \param agent Agent to lock
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
#define agent_lock(agent) _agent_lock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
|
||||
static inline void _agent_lock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
|
||||
|
@ -665,8 +663,6 @@ static inline void _agent_lock(struct agent_pvt *agent, const char *file, const
|
|||
* \brief Unlock the agent.
|
||||
*
|
||||
* \param agent Agent to unlock
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
#define agent_unlock(agent) _agent_unlock(agent, __FILE__, __PRETTY_FUNCTION__, __LINE__, #agent)
|
||||
static inline void _agent_unlock(struct agent_pvt *agent, const char *file, const char *function, int line, const char *var)
|
||||
|
@ -684,8 +680,6 @@ static inline void _agent_unlock(struct agent_pvt *agent, const char *file, cons
|
|||
* \note Assumes the agent lock is already obtained.
|
||||
*
|
||||
* \note Defined locking order is channel lock then agent lock.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static struct ast_channel *agent_lock_logged(struct agent_pvt *agent)
|
||||
{
|
||||
|
@ -750,8 +744,6 @@ static enum ast_device_state agent_pvt_devstate_get(const char *agent_id)
|
|||
* \since 12.0.0
|
||||
*
|
||||
* \param agent_id Which agent needs the device state updated.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void agent_devstate_changed(const char *agent_id)
|
||||
{
|
||||
|
@ -1011,8 +1003,6 @@ AST_MUTEX_DEFINE_STATIC(agent_holding_lock);
|
|||
* \param payload_size Size of the payload if payload is non-NULL. A number otherwise.
|
||||
*
|
||||
* \note The payload MUST NOT have any resources that need to be freed.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void clear_agent_status(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size)
|
||||
{
|
||||
|
@ -1028,8 +1018,6 @@ static void clear_agent_status(struct ast_bridge_channel *bridge_channel, const
|
|||
* \param agent Which agent is connecting to the caller.
|
||||
*
|
||||
* \note The agent is locked on entry and not locked on exit.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, struct agent_pvt *agent)
|
||||
{
|
||||
|
@ -1363,8 +1351,6 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha
|
|||
* bridge_channel->bridge_pvt.
|
||||
*
|
||||
* \note On entry, self is already locked.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
|
@ -1382,8 +1368,6 @@ static void bridge_agent_hold_pull(struct ast_bridge *self, struct ast_bridge_ch
|
|||
* references to the bridge so it can be destroyed.
|
||||
*
|
||||
* \note On entry, self must NOT be locked.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void bridge_agent_hold_dissolving(struct ast_bridge *self)
|
||||
{
|
||||
|
@ -1475,8 +1459,6 @@ static void send_agent_logoff(struct ast_channel *chan, const char *agent, long
|
|||
* \param agent Which agent logging out.
|
||||
*
|
||||
* \note On entry agent is already locked. On exit it is no longer locked.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void agent_logout(struct agent_pvt *agent)
|
||||
{
|
||||
|
@ -1514,8 +1496,6 @@ static void agent_logout(struct agent_pvt *agent)
|
|||
*
|
||||
* \param agent Which agent.
|
||||
* \param logged The logged in channel.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void agent_run(struct agent_pvt *agent, struct ast_channel *logged)
|
||||
{
|
||||
|
@ -2034,8 +2014,6 @@ static int agent_request_exec(struct ast_channel *chan, const char *data)
|
|||
*
|
||||
* \param agent What to setup channel config values on.
|
||||
* \param chan Channel logging in as an agent.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void agent_login_channel_config(struct agent_pvt *agent, struct ast_channel *chan)
|
||||
{
|
||||
|
|
|
@ -158,7 +158,7 @@ static const char app[] = "AlarmReceiver";
|
|||
events to the standard input of the application.
|
||||
The configuration file also contains settings for DTMF timing, and for the loudness of the
|
||||
acknowledgement tones.</para>
|
||||
<note><para>Few Ademco DTMF signalling formats are detected automaticaly: Contact ID, Express 4+1,
|
||||
<note><para>Few Ademco DTMF signalling formats are detected automatically: Contact ID, Express 4+1,
|
||||
Express 4+2, High Speed and Super Fast.</para></note>
|
||||
<para>The application is affected by the following variables:</para>
|
||||
<variablelist>
|
||||
|
@ -203,7 +203,6 @@ static char event_file[14] = "/event-XXXXXX";
|
|||
* family, then create it and set its value to 1.
|
||||
*
|
||||
* \param key A database key to increment
|
||||
* \return Nothing
|
||||
*/
|
||||
static void database_increment(char *key)
|
||||
{
|
||||
|
|
|
@ -92,6 +92,12 @@
|
|||
<para>Is the maximum duration of a word to accept.</para>
|
||||
<para>If exceeded, then the result is detection as a MACHINE</para>
|
||||
</parameter>
|
||||
<parameter name="audioFile" required="false">
|
||||
<para>Is an audio file to play to the caller while AMD is in progress.</para>
|
||||
<para>By default, no audio file is played.</para>
|
||||
<para>If an audio file is configured in amd.conf, then that file will be used
|
||||
if one is not specified here. That file may be overridden by this argument.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This application attempts to detect answering machines at the beginning
|
||||
|
@ -155,6 +161,9 @@ static int dfltBetweenWordsSilence = 50;
|
|||
static int dfltMaximumNumberOfWords = 2;
|
||||
static int dfltSilenceThreshold = 256;
|
||||
static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
|
||||
static char *dfltAudioFile = NULL;
|
||||
|
||||
static ast_mutex_t config_lock;
|
||||
|
||||
/* Set to the lowest ms value provided in amd.conf or application parameters */
|
||||
static int dfltMaxWaitTimeForFrame = 50;
|
||||
|
@ -179,7 +188,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
char amdCause[256] = "", amdStatus[256] = "";
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
/* Lets set the initial values of the variables that will control the algorithm.
|
||||
/* Let's set the initial values of the variables that will control the algorithm.
|
||||
The initial values are the default ones. If they are passed as arguments
|
||||
when invoking the application, then the default values will be overwritten
|
||||
by the ones passed as parameters. */
|
||||
|
@ -193,6 +202,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
int silenceThreshold = dfltSilenceThreshold;
|
||||
int maximumWordLength = dfltMaximumWordLength;
|
||||
int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
|
||||
const char *audioFile = NULL;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(argInitialSilence);
|
||||
|
@ -204,8 +214,15 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
AST_APP_ARG(argMaximumNumberOfWords);
|
||||
AST_APP_ARG(argSilenceThreshold);
|
||||
AST_APP_ARG(argMaximumWordLength);
|
||||
AST_APP_ARG(audioFile);
|
||||
);
|
||||
|
||||
ast_mutex_lock(&config_lock);
|
||||
if (!ast_strlen_zero(dfltAudioFile)) {
|
||||
audioFile = ast_strdupa(dfltAudioFile);
|
||||
}
|
||||
ast_mutex_unlock(&config_lock);
|
||||
|
||||
ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
|
||||
S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
|
||||
S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
|
||||
|
@ -233,6 +250,9 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
silenceThreshold = atoi(args.argSilenceThreshold);
|
||||
if (!ast_strlen_zero(args.argMaximumWordLength))
|
||||
maximumWordLength = atoi(args.argMaximumWordLength);
|
||||
if (!ast_strlen_zero(args.audioFile)) {
|
||||
audioFile = args.audioFile;
|
||||
}
|
||||
} else {
|
||||
ast_debug(1, "AMD using the default parameters.\n");
|
||||
}
|
||||
|
@ -280,6 +300,11 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
/* Set our start time so we can tie the loop to real world time and not RTP updates */
|
||||
amd_tvstart = ast_tvnow();
|
||||
|
||||
/* Optional audio file to play to caller while AMD is doing its thing. */
|
||||
if (!ast_strlen_zero(audioFile)) {
|
||||
ast_streamfile(chan, audioFile, ast_channel_language(chan));
|
||||
}
|
||||
|
||||
/* Now we go into a loop waiting for frames from the channel */
|
||||
while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
|
||||
int ms = 0;
|
||||
|
@ -462,10 +487,14 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
|||
/* Free the DSP used to detect silence */
|
||||
ast_dsp_free(silenceDetector);
|
||||
|
||||
/* If we were playing something to pass the time, stop it now. */
|
||||
if (!ast_strlen_zero(audioFile)) {
|
||||
ast_stopstream(chan);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static int amd_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
isAnsweringMachine(chan, data);
|
||||
|
@ -516,7 +545,16 @@ static int load_config(int reload)
|
|||
dfltMaximumNumberOfWords = atoi(var->value);
|
||||
} else if (!strcasecmp(var->name, "maximum_word_length")) {
|
||||
dfltMaximumWordLength = atoi(var->value);
|
||||
|
||||
} else if (!strcasecmp(var->name, "playback_file")) {
|
||||
ast_mutex_lock(&config_lock);
|
||||
if (dfltAudioFile) {
|
||||
ast_free(dfltAudioFile);
|
||||
dfltAudioFile = NULL;
|
||||
}
|
||||
if (!ast_strlen_zero(var->value)) {
|
||||
dfltAudioFile = ast_strdup(var->value);
|
||||
}
|
||||
ast_mutex_unlock(&config_lock);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
|
||||
app, cat, var->name, var->lineno);
|
||||
|
@ -539,6 +577,12 @@ static int load_config(int reload)
|
|||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_mutex_lock(&config_lock);
|
||||
if (dfltAudioFile) {
|
||||
ast_free(dfltAudioFile);
|
||||
}
|
||||
ast_mutex_unlock(&config_lock);
|
||||
ast_mutex_destroy(&config_lock);
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
|
@ -554,6 +598,7 @@ static int unload_module(void)
|
|||
*/
|
||||
static int load_module(void)
|
||||
{
|
||||
ast_mutex_init(&config_lock);
|
||||
if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
|
|
@ -100,6 +100,9 @@
|
|||
<para>Automatically exit the bridge and return to the PBX after
|
||||
<emphasis>duration</emphasis> seconds.</para>
|
||||
</option>
|
||||
<option name="n">
|
||||
<para>Do not automatically answer the channel.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
|
@ -108,7 +111,7 @@
|
|||
The channel will then wait in the holding bridge until some event occurs
|
||||
which removes it from the holding bridge.</para>
|
||||
<note><para>This application will answer calls which haven't already
|
||||
been answered.</para></note>
|
||||
been answered, unless the n option is provided.</para></note>
|
||||
</description>
|
||||
</application>
|
||||
***/
|
||||
|
@ -186,6 +189,7 @@ enum bridgewait_flags {
|
|||
MUXFLAG_MOHCLASS = (1 << 0),
|
||||
MUXFLAG_ENTERTAINMENT = (1 << 1),
|
||||
MUXFLAG_TIMEOUT = (1 << 2),
|
||||
MUXFLAG_NOANSWER = (1 << 3),
|
||||
};
|
||||
|
||||
enum bridgewait_args {
|
||||
|
@ -199,6 +203,7 @@ AST_APP_OPTIONS(bridgewait_opts, {
|
|||
AST_APP_OPTION_ARG('e', MUXFLAG_ENTERTAINMENT, OPT_ARG_ENTERTAINMENT),
|
||||
AST_APP_OPTION_ARG('m', MUXFLAG_MOHCLASS, OPT_ARG_MOHCLASS),
|
||||
AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
|
||||
AST_APP_OPTION('n', MUXFLAG_NOANSWER),
|
||||
});
|
||||
|
||||
static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||
|
@ -391,17 +396,6 @@ static void wait_wrapper_removal(struct wait_bridge_wrapper *wrapper)
|
|||
ao2_cleanup(wrapper);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \since 12.0.0
|
||||
* \brief Application callback for the bridgewait application
|
||||
*
|
||||
* \param chan channel running the application
|
||||
* \param data Arguments to the application
|
||||
*
|
||||
* \retval 0 Ran successfully and the call didn't hang up
|
||||
* \retval -1 Failed or the call was hung up by the time the channel exited the holding bridge
|
||||
*/
|
||||
static enum wait_bridge_roles validate_role(const char *role)
|
||||
{
|
||||
if (!strcmp(role, "participant")) {
|
||||
|
@ -413,6 +407,17 @@ static enum wait_bridge_roles validate_role(const char *role)
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \since 12.0.0
|
||||
* \brief Application callback for the bridgewait application
|
||||
*
|
||||
* \param chan channel running the application
|
||||
* \param data Arguments to the application
|
||||
*
|
||||
* \retval 0 Ran successfully and the call didn't hang up
|
||||
* \retval -1 Failed or the call was hung up by the time the channel exited the holding bridge
|
||||
*/
|
||||
static int bridgewait_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *bridge_name = DEFAULT_BRIDGE_NAME;
|
||||
|
@ -458,7 +463,7 @@ static int bridgewait_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
|
||||
/* Answer the channel if needed */
|
||||
if (ast_channel_state(chan) != AST_STATE_UP) {
|
||||
if (ast_channel_state(chan) != AST_STATE_UP && !ast_test_flag(&flags, MUXFLAG_NOANSWER)) {
|
||||
ast_answer(chan);
|
||||
}
|
||||
|
||||
|
|
|
@ -559,7 +559,7 @@ static int pack_channel_into_message(struct ast_channel *chan, const char *role,
|
|||
* \brief Publish the chanspy message over Stasis-Core
|
||||
* \param spyer The channel doing the spying
|
||||
* \param spyee Who is being spied upon
|
||||
* \start start If non-zero, the spying is starting. Otherwise, the spyer is
|
||||
* \param start If non-zero, the spying is starting. Otherwise, the spyer is
|
||||
* finishing
|
||||
*/
|
||||
static void publish_chanspy_message(struct ast_channel *spyer,
|
||||
|
@ -899,7 +899,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
|
|||
signed char zero_volume = 0;
|
||||
int waitms;
|
||||
int res;
|
||||
int num_spyed_upon = 1;
|
||||
int num_spied_upon = 1;
|
||||
struct ast_channel_iterator *iter = NULL;
|
||||
|
||||
if (ast_test_flag(flags, OPTION_EXIT)) {
|
||||
|
@ -926,7 +926,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
|
|||
struct ast_autochan *autochan = NULL, *next_autochan = NULL;
|
||||
struct ast_channel *prev = NULL;
|
||||
|
||||
if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
|
||||
if (!ast_test_flag(flags, OPTION_QUIET) && num_spied_upon) {
|
||||
res = ast_streamfile(chan, "beep", ast_channel_language(chan));
|
||||
if (!res)
|
||||
res = ast_waitstream(chan, "");
|
||||
|
@ -991,7 +991,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
|
|||
|
||||
/* reset for the next loop around, unless overridden later */
|
||||
waitms = 100;
|
||||
num_spyed_upon = 0;
|
||||
num_spied_upon = 0;
|
||||
|
||||
for (autochan = next_channel(iter, chan);
|
||||
autochan;
|
||||
|
@ -1146,7 +1146,7 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
|
|||
}
|
||||
|
||||
res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
|
||||
num_spyed_upon++;
|
||||
num_spied_upon++;
|
||||
|
||||
if (res == -1) {
|
||||
ast_autochan_destroy(autochan);
|
||||
|
|
|
@ -128,9 +128,15 @@
|
|||
<ref type="application">ConfKick</ref>
|
||||
<ref type="function">CONFBRIDGE</ref>
|
||||
<ref type="function">CONFBRIDGE_INFO</ref>
|
||||
<ref type="function">CONFBRIDGE_CHANNELS</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="ConfKick" language="en_US">
|
||||
<since>
|
||||
<version>16.19.0</version>
|
||||
<version>18.5.0</version>
|
||||
<version>19.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Kicks channel(s) from the requested ConfBridge.
|
||||
</synopsis>
|
||||
|
@ -159,6 +165,7 @@
|
|||
<ref type="application">ConfBridge</ref>
|
||||
<ref type="function">CONFBRIDGE</ref>
|
||||
<ref type="function">CONFBRIDGE_INFO</ref>
|
||||
<ref type="function">CONFBRIDGE_CHANNELS</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<function name="CONFBRIDGE" language="en_US">
|
||||
|
@ -191,23 +198,29 @@
|
|||
<para>---- Example 1 ----</para>
|
||||
<para>In this example the custom user profile set on the channel will
|
||||
automatically be used by the ConfBridge application.</para>
|
||||
<para>exten => 1,1,Answer()</para>
|
||||
<example title="Example 1">
|
||||
exten => 1,1,Answer()
|
||||
</example>
|
||||
<para>; In this example the effect of the following line is</para>
|
||||
<para>; implied:</para>
|
||||
<para>; same => n,Set(CONFBRIDGE(user,template)=default_user)</para>
|
||||
<para>same => n,Set(CONFBRIDGE(user,announce_join_leave)=yes)</para>
|
||||
<para>same => n,Set(CONFBRIDGE(user,startmuted)=yes)</para>
|
||||
<para>same => n,ConfBridge(1) </para>
|
||||
<example title="Example 1b">
|
||||
same => n,Set(CONFBRIDGE(user,template)=default_user)
|
||||
same => n,Set(CONFBRIDGE(user,announce_join_leave)=yes)
|
||||
same => n,Set(CONFBRIDGE(user,startmuted)=yes)
|
||||
same => n,ConfBridge(1)
|
||||
</example>
|
||||
<para>---- Example 2 ----</para>
|
||||
<para>This example shows how to use a predefined user profile in
|
||||
<filename>confbridge.conf</filename> as a template for a dynamic profile.
|
||||
Here we make an admin/marked user out of the <literal>my_user</literal>
|
||||
profile that you define in <filename>confbridge.conf</filename>.</para>
|
||||
<para>exten => 1,1,Answer()</para>
|
||||
<para>same => n,Set(CONFBRIDGE(user,template)=my_user)</para>
|
||||
<para>same => n,Set(CONFBRIDGE(user,admin)=yes)</para>
|
||||
<para>same => n,Set(CONFBRIDGE(user,marked)=yes)</para>
|
||||
<para>same => n,ConfBridge(1)</para>
|
||||
<example title="Example 2">
|
||||
exten => 1,1,Answer()
|
||||
same => n,Set(CONFBRIDGE(user,template)=my_user)
|
||||
same => n,Set(CONFBRIDGE(user,admin)=yes)
|
||||
same => n,Set(CONFBRIDGE(user,marked)=yes)
|
||||
same => n,ConfBridge(1)
|
||||
</example>
|
||||
</description>
|
||||
</function>
|
||||
<function name="CONFBRIDGE_INFO" language="en_US">
|
||||
|
@ -243,6 +256,50 @@
|
|||
<para>This function returns a non-negative integer for valid conference
|
||||
names and an empty string for invalid conference names.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">CONFBRIDGE_CHANNELS</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="CONFBRIDGE_CHANNELS" language="en_US">
|
||||
<since>
|
||||
<version>16.26.0</version>
|
||||
<version>18.12.0</version>
|
||||
<version>19.4.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Get a list of channels in a ConfBridge conference.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="type" required="true">
|
||||
<para>What conference information is requested.</para>
|
||||
<enumlist>
|
||||
<enum name="admins">
|
||||
<para>Get the number of admin users in the conference.</para>
|
||||
</enum>
|
||||
<enum name="marked">
|
||||
<para>Get the number of marked users in the conference.</para>
|
||||
</enum>
|
||||
<enum name="parties">
|
||||
<para>Get the number of total users in the conference.</para>
|
||||
</enum>
|
||||
<enum name="active">
|
||||
<para>Get the number of active users in the conference.</para>
|
||||
</enum>
|
||||
<enum name="waiting">
|
||||
<para>Get the number of waiting users in the conference.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="conf" required="true">
|
||||
<para>The name of the conference being referenced.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This function returns a comma-separated list of channels in a ConfBridge conference, optionally filtered by a type of participant.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">CONFBRIDGE_INFO</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<manager name="ConfbridgeList" language="en_US">
|
||||
<synopsis>
|
||||
|
@ -336,6 +393,37 @@
|
|||
ConfbridgeListRoomsComplete.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<managerEvent language="en_US" name="ConfbridgeListRooms">
|
||||
<managerEventInstance class="EVENT_FLAG_REPORTING">
|
||||
<synopsis>Raised as part of the ConfbridgeListRooms action response list.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Conference">
|
||||
<para>The name of the Confbridge conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Parties">
|
||||
<para>Number of users in the conference.</para>
|
||||
<para>This includes both active and waiting users.</para>
|
||||
</parameter>
|
||||
<parameter name="Marked">
|
||||
<para>Number of marked users in the conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Locked">
|
||||
<para>Is the conference locked?</para>
|
||||
<enumlist>
|
||||
<enum name="Yes"/>
|
||||
<enum name="No"/>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="Muted">
|
||||
<para>Is the conference muted?</para>
|
||||
<enumlist>
|
||||
<enum name="Yes"/>
|
||||
<enum name="No"/>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<manager name="ConfbridgeMute" language="en_US">
|
||||
<synopsis>
|
||||
Mute a Confbridge user.
|
||||
|
@ -797,8 +885,8 @@ struct confbridge_conference *conf_find_bridge(const char *conference_name)
|
|||
*
|
||||
* \note Must be called with the conference locked
|
||||
*
|
||||
* \retval 1, conference is recording.
|
||||
* \retval 0, conference is NOT recording.
|
||||
* \retval 1 conference is recording.
|
||||
* \retval 0 conference is NOT recording.
|
||||
*/
|
||||
static int conf_is_recording(struct confbridge_conference *conference)
|
||||
{
|
||||
|
@ -909,7 +997,7 @@ static int conf_start_record(struct confbridge_conference *conference)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* \brief Playback the given filename and monitor for any dtmf interrupts.
|
||||
/*! \brief Playback the given filename and monitor for any dtmf interrupts.
|
||||
*
|
||||
* This function is used to playback sound files on a given channel and optionally
|
||||
* allow dtmf interrupts to occur.
|
||||
|
@ -923,7 +1011,9 @@ static int conf_start_record(struct confbridge_conference *conference)
|
|||
* \param channel Optional channel to play file on if bridge_channel not given
|
||||
* \param filename The file name to playback
|
||||
*
|
||||
* \retval -1 failure during playback, 0 on file was fully played, 1 on dtmf interrupt.
|
||||
* \retval -1 failure during playback.
|
||||
* \retval 0 on file was fully played.
|
||||
* \retval 1 on dtmf interrupt.
|
||||
*/
|
||||
static int play_file(struct ast_bridge_channel *bridge_channel, struct ast_channel *channel,
|
||||
const char *filename)
|
||||
|
@ -980,7 +1070,8 @@ static int sound_file_exists(const char *filename)
|
|||
* \param bridge_channel The bridged channel involved
|
||||
*
|
||||
* \note if caller is NULL, the announcment will be sent to all participants in the conference.
|
||||
* \return Returns 0 on success, -1 if the user hung up
|
||||
* \retval 0 on success.
|
||||
* \retval -1 if the user hung up.
|
||||
*/
|
||||
static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user,
|
||||
struct ast_bridge_channel *bridge_channel)
|
||||
|
@ -1030,7 +1121,9 @@ static int announce_user_count(struct confbridge_conference *conference, struct
|
|||
* \param user User to play audio prompt to
|
||||
* \param filename Prompt to play
|
||||
*
|
||||
* \return Returns 0 on success, -1 if the user hung up
|
||||
* \retval 0 on success.
|
||||
* \retval -1 if the user hung up.
|
||||
*
|
||||
* \note Generally this should be called when the conference is unlocked to avoid blocking
|
||||
* the entire conference while the sound is played. But don't unlock the conference bridge
|
||||
* in the middle of a state transition.
|
||||
|
@ -1167,8 +1260,6 @@ static void hangup_data_destroy(struct hangup_data *hangup)
|
|||
* \brief Destroy a conference bridge
|
||||
*
|
||||
* \param obj The conference bridge object
|
||||
*
|
||||
* \return Returns nothing
|
||||
*/
|
||||
static void destroy_conference_bridge(void *obj)
|
||||
{
|
||||
|
@ -1305,7 +1396,7 @@ void conf_update_user_mute(struct confbridge_user *user)
|
|||
ast_channel_name(user->chan));
|
||||
}
|
||||
|
||||
/*
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Mute/unmute a single user.
|
||||
*/
|
||||
|
@ -1389,8 +1480,6 @@ void conf_moh_start(struct confbridge_user *user)
|
|||
* \brief Unsuspend MOH for the conference user.
|
||||
*
|
||||
* \param user Conference user to unsuspend MOH on.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void conf_moh_unsuspend(struct confbridge_user *user)
|
||||
{
|
||||
|
@ -1406,8 +1495,6 @@ static void conf_moh_unsuspend(struct confbridge_user *user)
|
|||
* \brief Suspend MOH for the conference user.
|
||||
*
|
||||
* \param user Conference user to suspend MOH on.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void conf_moh_suspend(struct confbridge_user *user)
|
||||
{
|
||||
|
@ -1682,7 +1769,7 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
|
|||
struct post_join_action *action;
|
||||
int max_members_reached = 0;
|
||||
|
||||
/* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
|
||||
/* We explicitly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same time */
|
||||
ao2_lock(conference_bridges);
|
||||
|
||||
ast_debug(1, "Trying to find conference bridge '%s'\n", conference_name);
|
||||
|
@ -1748,7 +1835,6 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
|
|||
ast_bridge_set_talker_src_video_mode(conference->bridge);
|
||||
} else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
|
||||
ast_bridge_set_sfu_video_mode(conference->bridge);
|
||||
ast_bridge_set_video_update_discard(conference->bridge, conference->b_profile.video_update_discard);
|
||||
ast_bridge_set_remb_send_interval(conference->bridge, conference->b_profile.remb_send_interval);
|
||||
if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE)) {
|
||||
ast_brige_set_remb_behavior(conference->bridge, AST_BRIDGE_VIDEO_SFU_REMB_AVERAGE);
|
||||
|
@ -1768,6 +1854,9 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
|
|||
}
|
||||
}
|
||||
|
||||
/* Always set the minimum interval between video updates, to avoid infinite video updates. */
|
||||
ast_bridge_set_video_update_discard(conference->bridge, conference->b_profile.video_update_discard);
|
||||
|
||||
if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_ENABLE_EVENTS)) {
|
||||
ast_bridge_set_send_sdp_label(conference->bridge, 1);
|
||||
}
|
||||
|
@ -2721,17 +2810,24 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
|
|||
ast_autoservice_stop(chan);
|
||||
}
|
||||
|
||||
/* Play the Join sound to both the conference and the user entering. */
|
||||
if (!quiet) {
|
||||
const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference->b_profile.sounds);
|
||||
|
||||
if (strcmp(conference->b_profile.language, ast_channel_language(chan))) {
|
||||
ast_stream_and_wait(chan, join_sound, "");
|
||||
/* if hear_own_join_sound is enabled play the Join sound to everyone */
|
||||
if (ast_test_flag(&user.u_profile, USER_OPT_HEAR_OWN_JOIN_SOUND) ) {
|
||||
if (strcmp(conference->b_profile.language, ast_channel_language(chan))) {
|
||||
ast_stream_and_wait(chan, join_sound, "");
|
||||
ast_autoservice_start(chan);
|
||||
play_sound_file(conference, join_sound);
|
||||
ast_autoservice_stop(chan);
|
||||
} else {
|
||||
async_play_sound_file(conference, join_sound, chan);
|
||||
}
|
||||
/* if hear_own_join_sound is disabled only play the Join sound to just the conference */
|
||||
} else {
|
||||
ast_autoservice_start(chan);
|
||||
play_sound_file(conference, join_sound);
|
||||
ast_autoservice_stop(chan);
|
||||
} else {
|
||||
async_play_sound_file(conference, join_sound, chan);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3494,7 +3590,7 @@ static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct
|
|||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
/* \internal
|
||||
/*! \internal
|
||||
* \brief finds a conference by name and locks/unlocks.
|
||||
*
|
||||
* \retval 0 success
|
||||
|
@ -3518,7 +3614,7 @@ static int generic_lock_unlock_helper(int lock, const char *conference_name)
|
|||
return res;
|
||||
}
|
||||
|
||||
/* \internal
|
||||
/*! \internal
|
||||
* \brief finds a conference user by channel name and mutes/unmutes them.
|
||||
*
|
||||
* \retval 0 success
|
||||
|
@ -3818,6 +3914,90 @@ static struct ast_custom_function confbridge_info_function = {
|
|||
.read = func_confbridge_info,
|
||||
};
|
||||
|
||||
static int func_confbridge_channels(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
char *parse, *outbuf;
|
||||
struct confbridge_conference *conference;
|
||||
struct confbridge_user *user;
|
||||
int bytes, count = 0;
|
||||
size_t outlen;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(type);
|
||||
AST_APP_ARG(confno);
|
||||
);
|
||||
|
||||
/* parse all the required arguments and make sure they exist. */
|
||||
if (ast_strlen_zero(data)) {
|
||||
return -1;
|
||||
}
|
||||
parse = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
|
||||
ast_log(LOG_WARNING, "Usage: %s(category,confno)", cmd);
|
||||
return -1;
|
||||
}
|
||||
conference = ao2_find(conference_bridges, args.confno, OBJ_KEY);
|
||||
if (!conference) {
|
||||
ast_debug(1, "No such conference: %s\n", args.confno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
outbuf = buf;
|
||||
outlen = len;
|
||||
|
||||
ao2_lock(conference);
|
||||
if (!strcasecmp(args.type, "parties")) {
|
||||
AST_LIST_TRAVERSE(&conference->active_list, user, list) {
|
||||
bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
|
||||
outbuf += bytes;
|
||||
outlen -= bytes;
|
||||
}
|
||||
AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
|
||||
bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
|
||||
outbuf += bytes;
|
||||
outlen -= bytes;
|
||||
}
|
||||
} else if (!strcasecmp(args.type, "active")) {
|
||||
AST_LIST_TRAVERSE(&conference->active_list, user, list) {
|
||||
bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
|
||||
outbuf += bytes;
|
||||
outlen -= bytes;
|
||||
}
|
||||
} else if (!strcasecmp(args.type, "waiting")) {
|
||||
AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
|
||||
bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
|
||||
outbuf += bytes;
|
||||
outlen -= bytes;
|
||||
}
|
||||
} else if (!strcasecmp(args.type, "admins")) {
|
||||
AST_LIST_TRAVERSE(&conference->active_list, user, list) {
|
||||
if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
|
||||
bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
|
||||
outbuf += bytes;
|
||||
outlen -= bytes;
|
||||
}
|
||||
}
|
||||
} else if (!strcasecmp(args.type, "marked")) {
|
||||
AST_LIST_TRAVERSE(&conference->active_list, user, list) {
|
||||
if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
bytes = snprintf(outbuf, outlen, "%s%s", count++ ? "," : "", ast_channel_name(user->chan));
|
||||
outbuf += bytes;
|
||||
outlen -= bytes;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Invalid keyword '%s' passed to %s.\n", args.type, cmd);
|
||||
}
|
||||
ao2_unlock(conference);
|
||||
ao2_ref(conference, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_custom_function confbridge_channels_function = {
|
||||
.name = "CONFBRIDGE_CHANNELS",
|
||||
.read = func_confbridge_channels,
|
||||
};
|
||||
|
||||
static int action_confbridgelist_item(struct mansession *s, const char *id_text, struct confbridge_conference *conference, struct confbridge_user *user, int waiting)
|
||||
{
|
||||
struct ast_channel_snapshot *snapshot;
|
||||
|
@ -3842,6 +4022,7 @@ static int action_confbridgelist_item(struct mansession *s, const char *id_text,
|
|||
"MarkedUser: %s\r\n"
|
||||
"WaitMarked: %s\r\n"
|
||||
"EndMarked: %s\r\n"
|
||||
"EndMarkedAny: %s\r\n"
|
||||
"Waiting: %s\r\n"
|
||||
"Muted: %s\r\n"
|
||||
"Talking: %s\r\n"
|
||||
|
@ -3854,6 +4035,7 @@ static int action_confbridgelist_item(struct mansession *s, const char *id_text,
|
|||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)),
|
||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)),
|
||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)),
|
||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKEDANY)),
|
||||
AST_YESNO(waiting),
|
||||
AST_YESNO(user->muted),
|
||||
AST_YESNO(user->talking),
|
||||
|
@ -4355,8 +4537,6 @@ void conf_remove_user_waiting(struct confbridge_conference *conference, struct c
|
|||
* \since 12.0.0
|
||||
*
|
||||
* \param tech What to unregister.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void unregister_channel_tech(struct ast_channel_tech *tech)
|
||||
{
|
||||
|
@ -4397,6 +4577,7 @@ static int unload_module(void)
|
|||
|
||||
ast_custom_function_unregister(&confbridge_function);
|
||||
ast_custom_function_unregister(&confbridge_info_function);
|
||||
ast_custom_function_unregister(&confbridge_channels_function);
|
||||
|
||||
ast_cli_unregister_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge));
|
||||
|
||||
|
@ -4468,6 +4649,7 @@ static int load_module(void)
|
|||
|
||||
res |= ast_custom_function_register_escalating(&confbridge_function, AST_CFE_WRITE);
|
||||
res |= ast_custom_function_register(&confbridge_info_function);
|
||||
res |= ast_custom_function_register(&confbridge_channels_function);
|
||||
|
||||
res |= ast_cli_register_multiple(cli_confbridge, ARRAY_LEN(cli_confbridge));
|
||||
|
||||
|
|
|
@ -1,231 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Execute an ISDN RAS
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>dahdi</depend>
|
||||
<support_level>deprecated</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <dahdi/user.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="DAHDIRAS" language="en_US">
|
||||
<synopsis>
|
||||
Executes DAHDI ISDN RAS application.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="args" required="true">
|
||||
<para>A list of parameters to pass to the pppd daemon,
|
||||
separated by <literal>,</literal> characters.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Executes a RAS server using pppd on the given channel.
|
||||
The channel must be a clear channel (i.e. PRI source) and a DAHDI
|
||||
channel to be able to use this function (No modem emulation is included).</para>
|
||||
<para>Your pppd must be patched to be DAHDI aware.</para>
|
||||
</description>
|
||||
</application>
|
||||
|
||||
***/
|
||||
|
||||
static const char app[] = "DAHDIRAS";
|
||||
|
||||
#define PPP_MAX_ARGS 32
|
||||
#define PPP_EXEC "/usr/sbin/pppd"
|
||||
|
||||
static pid_t spawn_ras(struct ast_channel *chan, char *args)
|
||||
{
|
||||
pid_t pid;
|
||||
char *c;
|
||||
|
||||
char *argv[PPP_MAX_ARGS];
|
||||
int argc = 0;
|
||||
char *stringp=NULL;
|
||||
|
||||
/* Start by forking */
|
||||
pid = ast_safe_fork(1);
|
||||
if (pid) {
|
||||
return pid;
|
||||
}
|
||||
|
||||
/* Execute RAS on File handles */
|
||||
dup2(ast_channel_fd(chan, 0), STDIN_FILENO);
|
||||
|
||||
/* Drop high priority */
|
||||
if (ast_opt_high_priority)
|
||||
ast_set_priority(0);
|
||||
|
||||
/* Close other file descriptors */
|
||||
ast_close_fds_above_n(STDERR_FILENO);
|
||||
|
||||
/* Reset all arguments */
|
||||
memset(argv, 0, sizeof(argv));
|
||||
|
||||
/* First argument is executable, followed by standard
|
||||
arguments for DAHDI PPP */
|
||||
argv[argc++] = PPP_EXEC;
|
||||
argv[argc++] = "nodetach";
|
||||
|
||||
/* And all the other arguments */
|
||||
stringp=args;
|
||||
c = strsep(&stringp, ",");
|
||||
while(c && strlen(c) && (argc < (PPP_MAX_ARGS - 4))) {
|
||||
argv[argc++] = c;
|
||||
c = strsep(&stringp, ",");
|
||||
}
|
||||
|
||||
if (geteuid() == 0) {
|
||||
argv[argc++] = "plugin";
|
||||
argv[argc++] = "dahdi.so";
|
||||
}
|
||||
argv[argc++] = "stdin";
|
||||
|
||||
/* Finally launch PPP */
|
||||
execv(PPP_EXEC, argv);
|
||||
fprintf(stderr, "Failed to exec PPPD!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void run_ras(struct ast_channel *chan, char *args)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
int res;
|
||||
int signalled = 0;
|
||||
struct dahdi_bufferinfo savebi;
|
||||
int x;
|
||||
|
||||
res = ioctl(ast_channel_fd(chan, 0), DAHDI_GET_BUFINFO, &savebi);
|
||||
if(res) {
|
||||
ast_log(LOG_WARNING, "Unable to check buffer policy on channel %s\n", ast_channel_name(chan));
|
||||
return;
|
||||
}
|
||||
|
||||
pid = spawn_ras(chan, args);
|
||||
if (pid < 0) {
|
||||
ast_log(LOG_WARNING, "Failed to spawn RAS\n");
|
||||
} else {
|
||||
for (;;) {
|
||||
res = waitpid(pid, &status, WNOHANG);
|
||||
if (!res) {
|
||||
/* Check for hangup */
|
||||
if (ast_check_hangup(chan) && !signalled) {
|
||||
ast_debug(1, "Channel '%s' hungup. Signalling RAS at %d to die...\n", ast_channel_name(chan), pid);
|
||||
kill(pid, SIGTERM);
|
||||
signalled=1;
|
||||
}
|
||||
/* Try again */
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "waitpid returned %d: %s\n", res, strerror(errno));
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
ast_verb(3, "RAS on %s terminated with status %d\n", ast_channel_name(chan), WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
ast_verb(3, "RAS on %s terminated with signal %d\n",
|
||||
ast_channel_name(chan), WTERMSIG(status));
|
||||
} else {
|
||||
ast_verb(3, "RAS on %s terminated weirdly.\n", ast_channel_name(chan));
|
||||
}
|
||||
/* Throw back into audio mode */
|
||||
x = 1;
|
||||
ioctl(ast_channel_fd(chan, 0), DAHDI_AUDIOMODE, &x);
|
||||
|
||||
/* Restore saved values */
|
||||
res = ioctl(ast_channel_fd(chan, 0), DAHDI_SET_BUFINFO, &savebi);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", ast_channel_name(chan));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ast_safe_fork_cleanup();
|
||||
}
|
||||
|
||||
static int dahdiras_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
int res=-1;
|
||||
char *args;
|
||||
struct dahdi_params dahdip;
|
||||
|
||||
if (!data)
|
||||
data = "";
|
||||
|
||||
args = ast_strdupa(data);
|
||||
|
||||
/* Answer the channel if it's not up */
|
||||
if (ast_channel_state(chan) != AST_STATE_UP)
|
||||
ast_answer(chan);
|
||||
if (strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) {
|
||||
/* If it's not a DAHDI channel, we're done. Wait a couple of
|
||||
seconds and then hangup... */
|
||||
ast_verb(2, "Channel %s is not a DAHDI channel\n", ast_channel_name(chan));
|
||||
sleep(2);
|
||||
} else {
|
||||
memset(&dahdip, 0, sizeof(dahdip));
|
||||
if (ioctl(ast_channel_fd(chan, 0), DAHDI_GET_PARAMS, &dahdip)) {
|
||||
ast_log(LOG_WARNING, "Unable to get DAHDI parameters\n");
|
||||
} else if (dahdip.sigtype != DAHDI_SIG_CLEAR) {
|
||||
ast_verb(2, "Channel %s is not a clear channel\n", ast_channel_name(chan));
|
||||
} else {
|
||||
/* Everything should be okay. Run PPP. */
|
||||
ast_verb(3, "Starting RAS on %s\n", ast_channel_name(chan));
|
||||
/* Execute RAS */
|
||||
run_ras(chan, args);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ((ast_register_application_xml(app, dahdiras_exec)) ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_DEPRECATED(ASTERISK_GPL_KEY, "DAHDI ISDN Remote Access Server");
|
188
apps/app_dial.c
188
apps/app_dial.c
|
@ -86,6 +86,7 @@
|
|||
<para>If you need more than one enter them as
|
||||
Technology2/Resource2&Technology3/Resource3&.....</para>
|
||||
</argument>
|
||||
<xi:include xpointer="xpointer(/docs/info[@name='Dial_Resource'])" />
|
||||
</parameter>
|
||||
<parameter name="timeout" required="false">
|
||||
<para>Specifies the number of seconds we attempt to dial the specified devices.</para>
|
||||
|
@ -156,6 +157,10 @@
|
|||
<argument name="called" />
|
||||
<argument name="calling" />
|
||||
<argument name="progress" />
|
||||
<argument name="mfprogress" />
|
||||
<argument name="mfwink" />
|
||||
<argument name="sfprogress" />
|
||||
<argument name="sfwink" />
|
||||
<para>Send the specified DTMF strings <emphasis>after</emphasis> the called
|
||||
party has answered, but before the call gets bridged. The
|
||||
<replaceable>called</replaceable> DTMF string is sent to the called party, and the
|
||||
|
@ -163,6 +168,20 @@
|
|||
can be used alone. If <replaceable>progress</replaceable> is specified, its DTMF is sent
|
||||
to the called party immediately after receiving a <literal>PROGRESS</literal> message.</para>
|
||||
<para>See <literal>SendDTMF</literal> for valid digits.</para>
|
||||
<para>If <replaceable>mfprogress</replaceable> is specified, its MF is sent
|
||||
to the called party immediately after receiving a <literal>PROGRESS</literal> message.
|
||||
If <replaceable>mfwink</replaceable> is specified, its MF is sent
|
||||
to the called party immediately after receiving a <literal>WINK</literal> message.</para>
|
||||
<para>See <literal>SendMF</literal> for valid digits.</para>
|
||||
<para>If <replaceable>sfprogress</replaceable> is specified, its SF is sent
|
||||
to the called party immediately after receiving a <literal>PROGRESS</literal> message.
|
||||
If <replaceable>sfwink</replaceable> is specified, its SF is sent
|
||||
to the called party immediately after receiving a <literal>WINK</literal> message.</para>
|
||||
<para>See <literal>SendSF</literal> for valid digits.</para>
|
||||
</option>
|
||||
<option name="E">
|
||||
<para>Enable echoing of sent MF or SF digits back to caller (e.g. "hearpulsing").
|
||||
Used in conjunction with the D option.</para>
|
||||
</option>
|
||||
<option name="e">
|
||||
<para>Execute the <literal>h</literal> extension for peer after the call ends</para>
|
||||
|
@ -353,7 +372,7 @@
|
|||
</argument>
|
||||
<para>Enables <emphasis>operator services</emphasis> mode. This option only
|
||||
works when bridging a DAHDI channel to another DAHDI channel
|
||||
only. if specified on non-DAHDI interfaces, it will be ignored.
|
||||
only. If specified on non-DAHDI interfaces, it will be ignored.
|
||||
When the destination answers (presumably an operator services
|
||||
station), the originator no longer has control of their line.
|
||||
They may hang up, but the switch will not release their line
|
||||
|
@ -499,7 +518,6 @@
|
|||
answered, if it has not already been answered. These two channels will then
|
||||
be active in a bridged call. All other channels that were requested will then
|
||||
be hung up.</para>
|
||||
|
||||
<para>Unless there is a timeout specified, the Dial application will wait
|
||||
indefinitely until one of the called channels answers, the user hangs up, or
|
||||
if all of the called channels are busy or unavailable. Dialplan execution will
|
||||
|
@ -512,7 +530,6 @@
|
|||
If the <variable>OUTBOUND_GROUP_ONCE</variable> variable is set, all peer channels created by this
|
||||
application will be put into that group (as in <literal>Set(GROUP()=...</literal>). Unlike <variable>OUTBOUND_GROUP</variable>,
|
||||
however, the variable will be unset after use.</para>
|
||||
|
||||
<example title="Dial with 30 second timeout">
|
||||
same => n,Dial(PJSIP/alice,30)
|
||||
</example>
|
||||
|
@ -534,28 +551,22 @@
|
|||
</example>
|
||||
<example title="Dial with pre-dial subroutines">
|
||||
[default]
|
||||
|
||||
exten => callee_channel,1,NoOp(ARG1=${ARG1} ARG2=${ARG2})
|
||||
same => n,Log(NOTICE, I'm called on channel ${CHANNEL} prior to it starting the dial attempt)
|
||||
same => n,Return()
|
||||
|
||||
exten => called_channel,1,NoOp(ARG1=${ARG1} ARG2=${ARG2})
|
||||
same => n,Log(NOTICE, I'm called on outbound channel ${CHANNEL} prior to it being used to dial someone)
|
||||
same => n,Return()
|
||||
|
||||
exten => _X.,1,NoOp()
|
||||
same => n,Dial(PJSIP/alice,,b(default^called_channel^1(my_gosub_arg1^my_gosub_arg2))B(default^callee_channel^1(my_gosub_arg1^my_gosub_arg2)))
|
||||
same => n,Hangup()
|
||||
</example>
|
||||
<example title="Dial with post-answer subroutine executed on outbound channel">
|
||||
[my_gosub_routine]
|
||||
|
||||
exten => s,1,NoOp(ARG1=${ARG1} ARG2=${ARG2})
|
||||
same => n,Playback(hello)
|
||||
same => n,Return()
|
||||
|
||||
[default]
|
||||
|
||||
exten => _X.,1,NoOp()
|
||||
same => n,Dial(PJSIP/alice,,U(my_gosub_routine^my_gosub_arg1^my_gosub_arg2))
|
||||
same => n,Hangup()
|
||||
|
@ -603,12 +614,31 @@
|
|||
</variable>
|
||||
<variable name="DIALSTATUS">
|
||||
<para>This is the status of the call</para>
|
||||
<value name="CHANUNAVAIL" />
|
||||
<value name="CONGESTION" />
|
||||
<value name="NOANSWER" />
|
||||
<value name="BUSY" />
|
||||
<value name="ANSWER" />
|
||||
<value name="CANCEL" />
|
||||
<value name="CHANUNAVAIL">
|
||||
Either the dialed peer exists but is not currently reachable, e.g.
|
||||
endpoint is not registered, or an attempt was made to call a
|
||||
nonexistent location, e.g. nonexistent DNS hostname.
|
||||
</value>
|
||||
<value name="CONGESTION">
|
||||
Channel or switching congestion occured when routing the call.
|
||||
This can occur if there is a slow or no response from the remote end.
|
||||
</value>
|
||||
<value name="NOANSWER">
|
||||
Called party did not answer.
|
||||
</value>
|
||||
<value name="BUSY">
|
||||
The called party was busy or indicated a busy status.
|
||||
Note that some SIP devices will respond with 486 Busy if their Do Not Disturb
|
||||
modes are active. In this case, you can use DEVICE_STATUS to check if the
|
||||
endpoint is actually in use, if needed.
|
||||
</value>
|
||||
<value name="ANSWER">
|
||||
The call was answered.
|
||||
Any other result implicitly indicates the call was not answered.
|
||||
</value>
|
||||
<value name="CANCEL">
|
||||
Dial was cancelled before call was answered or reached some other terminating event.
|
||||
</value>
|
||||
<value name="DONTCALL">
|
||||
For the Privacy and Screening Modes.
|
||||
Will be set if the called party chooses to send the calling party to the 'Go Away' script.
|
||||
|
@ -617,7 +647,9 @@
|
|||
For the Privacy and Screening Modes.
|
||||
Will be set if the called party chooses to send the calling party to the 'torture' script.
|
||||
</value>
|
||||
<value name="INVALIDARGS" />
|
||||
<value name="INVALIDARGS">
|
||||
Dial failed due to invalid syntax.
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
|
@ -717,6 +749,7 @@ enum {
|
|||
#define OPT_PREDIAL_CALLER (1LLU << 42)
|
||||
#define OPT_RING_WITH_EARLY_MEDIA (1LLU << 43)
|
||||
#define OPT_HANGUPCAUSE (1LLU << 44)
|
||||
#define OPT_HEARPULSING (1LLU << 45)
|
||||
|
||||
enum {
|
||||
OPT_ARG_ANNOUNCE = 0,
|
||||
|
@ -752,6 +785,7 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
|
|||
AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE),
|
||||
AST_APP_OPTION('d', OPT_DTMF_EXIT),
|
||||
AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
|
||||
AST_APP_OPTION('E', OPT_HEARPULSING),
|
||||
AST_APP_OPTION('e', OPT_PEER_H),
|
||||
AST_APP_OPTION_ARG('f', OPT_FORCECLID, OPT_ARG_FORCECLID),
|
||||
AST_APP_OPTION_ARG('F', OPT_CALLEE_GO_ON, OPT_ARG_CALLEE_GO_ON),
|
||||
|
@ -930,7 +964,7 @@ static const char *get_cid_name(char *name, int namelen, struct ast_channel *cha
|
|||
* XXX this code is highly suspicious, as it essentially overwrites
|
||||
* the outgoing channel without properly deleting it.
|
||||
*
|
||||
* \todo eventually this function should be intergrated into and replaced by ast_call_forward()
|
||||
* \todo eventually this function should be integrated into and replaced by ast_call_forward()
|
||||
*/
|
||||
static void do_forward(struct chanlist *o, struct cause_args *num,
|
||||
struct ast_flags64 *peerflags, int single, int caller_entertained, int *to,
|
||||
|
@ -1146,6 +1180,7 @@ struct privacy_args {
|
|||
char privcid[256];
|
||||
char privintro[1024];
|
||||
char status[256];
|
||||
int canceled;
|
||||
};
|
||||
|
||||
static void publish_dial_end_event(struct ast_channel *in, struct dial_head *out_chans, struct ast_channel *exception, const char *status)
|
||||
|
@ -1167,8 +1202,6 @@ static void publish_dial_end_event(struct ast_channel *in, struct dial_head *out
|
|||
* \param chan Channel to get connected line updated.
|
||||
* \param peer Channel providing connected line information.
|
||||
* \param is_caller Non-zero if chan is the calling channel.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void update_connected_line_from_peer(struct ast_channel *chan, struct ast_channel *peer, int is_caller)
|
||||
{
|
||||
|
@ -1209,6 +1242,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
char *opt_args[],
|
||||
struct privacy_args *pa,
|
||||
const struct cause_args *num_in, int *result, char *dtmf_progress,
|
||||
char *mf_progress, char *mf_wink,
|
||||
char *sf_progress, char *sf_wink,
|
||||
const int hearpulsing,
|
||||
const int ignore_cc,
|
||||
struct ast_party_id *forced_clid, struct ast_party_id *stored_clid,
|
||||
struct ast_bridge_config *config)
|
||||
|
@ -1228,7 +1264,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
int cc_frame_received = 0;
|
||||
int num_ringing = 0;
|
||||
int sent_ring = 0;
|
||||
int sent_progress = 0;
|
||||
int sent_progress = 0, sent_wink = 0;
|
||||
struct timeval start = ast_tvnow();
|
||||
SCOPE_ENTER(3, "%s\n", ast_channel_name(in));
|
||||
|
||||
|
@ -1289,7 +1325,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
if (is_cc_recall) {
|
||||
ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad");
|
||||
}
|
||||
SCOPE_EXIT_RTN_VALUE(NULL, "%s: No outging channels available\n", ast_channel_name(in));
|
||||
SCOPE_EXIT_RTN_VALUE(NULL, "%s: No outgoing channels available\n", ast_channel_name(in));
|
||||
}
|
||||
winner = ast_waitfor_n(watchers, pos, to);
|
||||
AST_LIST_TRAVERSE(out_chans, o, node) {
|
||||
|
@ -1566,6 +1602,22 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_channel_unlock(in);
|
||||
sent_progress = 1;
|
||||
|
||||
if (!ast_strlen_zero(mf_progress)) {
|
||||
ast_verb(3,
|
||||
"Sending MF '%s' to %s as result of "
|
||||
"receiving a PROGRESS message.\n",
|
||||
mf_progress, hearpulsing ? "parties" : "called party");
|
||||
ast_mf_stream(c, (hearpulsing ? NULL : in),
|
||||
(hearpulsing ? in : NULL), mf_progress, 50, 55, 120, 65, 0);
|
||||
}
|
||||
if (!ast_strlen_zero(sf_progress)) {
|
||||
ast_verb(3,
|
||||
"Sending SF '%s' to %s as result of "
|
||||
"receiving a PROGRESS message.\n",
|
||||
sf_progress, (hearpulsing ? "parties" : "called party"));
|
||||
ast_sf_stream(c, (hearpulsing ? NULL : in),
|
||||
(hearpulsing ? in : NULL), sf_progress, 0, 0);
|
||||
}
|
||||
if (!ast_strlen_zero(dtmf_progress)) {
|
||||
ast_verb(3,
|
||||
"Sending DTMF '%s' to the called party as result of "
|
||||
|
@ -1576,6 +1628,29 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
}
|
||||
ast_channel_publish_dial(in, c, NULL, "PROGRESS");
|
||||
break;
|
||||
case AST_CONTROL_WINK:
|
||||
ast_verb(3, "%s winked, passing it to %s\n", ast_channel_name(c), ast_channel_name(in));
|
||||
if (!sent_wink) {
|
||||
sent_wink = 1;
|
||||
if (!ast_strlen_zero(mf_wink)) {
|
||||
ast_verb(3,
|
||||
"Sending MF '%s' to %s as result of "
|
||||
"receiving a WINK message.\n",
|
||||
mf_wink, (hearpulsing ? "parties" : "called party"));
|
||||
ast_mf_stream(c, (hearpulsing ? NULL : in),
|
||||
(hearpulsing ? in : NULL), mf_wink, 50, 55, 120, 65, 0);
|
||||
}
|
||||
if (!ast_strlen_zero(sf_wink)) {
|
||||
ast_verb(3,
|
||||
"Sending SF '%s' to %s as result of "
|
||||
"receiving a WINK message.\n",
|
||||
sf_wink, (hearpulsing ? "parties" : "called party"));
|
||||
ast_sf_stream(c, (hearpulsing ? NULL : in),
|
||||
(hearpulsing ? in : NULL), sf_wink, 0, 0);
|
||||
}
|
||||
}
|
||||
ast_indicate(in, AST_CONTROL_WINK);
|
||||
break;
|
||||
case AST_CONTROL_VIDUPDATE:
|
||||
case AST_CONTROL_SRCUPDATE:
|
||||
case AST_CONTROL_SRCCHANGE:
|
||||
|
@ -1720,6 +1795,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
/* Got hung up */
|
||||
*to = -1;
|
||||
strcpy(pa->status, "CANCEL");
|
||||
pa->canceled = 1;
|
||||
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
||||
if (f) {
|
||||
if (f->data.uint32) {
|
||||
|
@ -1744,6 +1820,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
*to = 0;
|
||||
*result = f->subclass.integer;
|
||||
strcpy(pa->status, "CANCEL");
|
||||
pa->canceled = 1;
|
||||
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
||||
ast_frfree(f);
|
||||
ast_channel_unlock(in);
|
||||
|
@ -1761,6 +1838,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_verb(3, "User requested call disconnect.\n");
|
||||
*to = 0;
|
||||
strcpy(pa->status, "CANCEL");
|
||||
pa->canceled = 1;
|
||||
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
||||
ast_frfree(f);
|
||||
if (is_cc_recall) {
|
||||
|
@ -1815,6 +1893,10 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_verb(3, "Call on %s left from hold\n", ast_channel_name(o->chan));
|
||||
ast_indicate(o->chan, AST_CONTROL_UNHOLD);
|
||||
break;
|
||||
case AST_CONTROL_FLASH:
|
||||
ast_verb(3, "Hook flash on %s\n", ast_channel_name(o->chan));
|
||||
ast_indicate(o->chan, AST_CONTROL_FLASH);
|
||||
break;
|
||||
case AST_CONTROL_VIDUPDATE:
|
||||
case AST_CONTROL_SRCUPDATE:
|
||||
case AST_CONTROL_SRCCHANGE:
|
||||
|
@ -2130,9 +2212,7 @@ static int setup_privacy_args(struct privacy_args *pa,
|
|||
/* the file doesn't exist yet. Let the caller submit his
|
||||
vocal intro for posterity */
|
||||
/* priv-recordintro script:
|
||||
|
||||
"At the tone, please say your name:"
|
||||
|
||||
*/
|
||||
int silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
|
||||
ast_answer(chan);
|
||||
|
@ -2210,8 +2290,6 @@ static int dial_handle_playtones(struct ast_channel *chan, const char *data)
|
|||
* \param peer Peer channel for bridge.
|
||||
* \param opts Dialing option flags.
|
||||
* \param opt_args Dialing option argument strings.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags64 *opts, char *opt_args[])
|
||||
{
|
||||
|
@ -2245,15 +2323,18 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
struct ast_channel *peer = NULL;
|
||||
int to; /* timeout */
|
||||
struct cause_args num = { chan, 0, 0, 0 };
|
||||
int cause;
|
||||
int cause, hanguptreecause = -1;
|
||||
|
||||
struct ast_bridge_config config = { { 0, } };
|
||||
struct timeval calldurationlimit = { 0, };
|
||||
char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress=NULL;
|
||||
char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress = NULL;
|
||||
char *mf_progress = NULL, *mf_wink = NULL;
|
||||
char *sf_progress = NULL, *sf_wink = NULL;
|
||||
struct privacy_args pa = {
|
||||
.sentringing = 0,
|
||||
.privdb_val = 0,
|
||||
.status = "INVALIDARGS",
|
||||
.canceled = 0,
|
||||
};
|
||||
int sentringing = 0, moh = 0;
|
||||
const char *outbound_group = NULL;
|
||||
|
@ -2384,9 +2465,13 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
}
|
||||
|
||||
if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {
|
||||
dtmf_progress = opt_args[OPT_ARG_SENDDTMF];
|
||||
dtmfcalled = strsep(&dtmf_progress, ":");
|
||||
dtmfcalling = strsep(&dtmf_progress, ":");
|
||||
sf_wink = opt_args[OPT_ARG_SENDDTMF];
|
||||
dtmfcalled = strsep(&sf_wink, ":");
|
||||
dtmfcalling = strsep(&sf_wink, ":");
|
||||
dtmf_progress = strsep(&sf_wink, ":");
|
||||
mf_progress = strsep(&sf_wink, ":");
|
||||
mf_wink = strsep(&sf_wink, ":");
|
||||
sf_progress = strsep(&sf_wink, ":");
|
||||
}
|
||||
|
||||
if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
|
||||
|
@ -2527,9 +2612,11 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
struct ast_channel *tc; /* channel for this destination */
|
||||
char *number;
|
||||
char *tech;
|
||||
int i;
|
||||
size_t tech_len;
|
||||
size_t number_len;
|
||||
struct ast_stream_topology *topology;
|
||||
struct ast_stream *stream;
|
||||
|
||||
cur = ast_strip(cur);
|
||||
if (ast_strlen_zero(cur)) {
|
||||
|
@ -2595,13 +2682,29 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
for (i = 0; i < ast_stream_topology_get_count(topology); ++i) {
|
||||
stream = ast_stream_topology_get_stream(topology, i);
|
||||
/* For both recvonly and sendonly the stream state reflects our state, that is we
|
||||
* are receiving only and we are sending only. Since we are requesting a
|
||||
* channel for the peer, we need to swap this to reflect what we will be doing.
|
||||
* That is, if we are receiving from Alice then we want to be sending to Bob,
|
||||
* so swap recvonly to sendonly and vice versa.
|
||||
*/
|
||||
if (ast_stream_get_state(stream) == AST_STREAM_STATE_RECVONLY) {
|
||||
ast_stream_set_state(stream, AST_STREAM_STATE_SENDONLY);
|
||||
} else if (ast_stream_get_state(stream) == AST_STREAM_STATE_SENDONLY) {
|
||||
ast_stream_set_state(stream, AST_STREAM_STATE_RECVONLY);
|
||||
}
|
||||
}
|
||||
|
||||
tc = ast_request_with_stream_topology(tmp->tech, topology, NULL, chan, tmp->number, &cause);
|
||||
|
||||
ast_stream_topology_free(topology);
|
||||
|
||||
if (!tc) {
|
||||
/* If we can't, just go on to the next call */
|
||||
ast_log(LOG_WARNING, "Unable to create channel of type '%s' (cause %d - %s)\n",
|
||||
/* Failure doesn't necessarily mean user error. DAHDI channels could be busy. */
|
||||
ast_log(LOG_NOTICE, "Unable to create channel of type '%s' (cause %d - %s)\n",
|
||||
tmp->tech, cause, ast_cause2str(cause));
|
||||
handle_cause(cause, &num);
|
||||
if (!rest) {
|
||||
|
@ -2739,7 +2842,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
AST_LIST_INSERT_TAIL(&out_chans, tmp, node);
|
||||
}
|
||||
|
||||
if (AST_LIST_EMPTY(&out_chans)) {
|
||||
/* As long as we attempted to dial valid peers, don't throw a warning. */
|
||||
/* If a DAHDI peer is busy, out_chans will be empty so checking list size is misleading. */
|
||||
if (!num_dialed) {
|
||||
ast_verb(3, "No devices or endpoints to dial (technology/resource)\n");
|
||||
if (continue_exec) {
|
||||
/* There is no point in having RetryDial try again */
|
||||
|
@ -2863,7 +2968,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
}
|
||||
|
||||
peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,
|
||||
dtmf_progress, ignore_cc, &forced_clid, &stored_clid, &config);
|
||||
dtmf_progress, mf_progress, mf_wink, sf_progress, sf_wink,
|
||||
(ast_test_flag64(&opts, OPT_HEARPULSING) ? 1 : 0),
|
||||
ignore_cc, &forced_clid, &stored_clid, &config);
|
||||
|
||||
if (!peer) {
|
||||
if (result) {
|
||||
|
@ -3354,11 +3461,16 @@ out:
|
|||
}
|
||||
|
||||
ast_channel_early_bridge(chan, NULL);
|
||||
/* forward 'answered elsewhere' if we received it */
|
||||
hanguptree(&out_chans, NULL,
|
||||
ast_channel_hangupcause(chan) == AST_CAUSE_ANSWERED_ELSEWHERE
|
||||
|| ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE)
|
||||
? AST_CAUSE_ANSWERED_ELSEWHERE : -1);
|
||||
/* forward 'answered elsewhere' if we received it */
|
||||
if (ast_channel_hangupcause(chan) == AST_CAUSE_ANSWERED_ELSEWHERE || ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE)) {
|
||||
hanguptreecause = AST_CAUSE_ANSWERED_ELSEWHERE;
|
||||
} else if (pa.canceled) { /* Caller canceled */
|
||||
if (ast_channel_hangupcause(chan))
|
||||
hanguptreecause = ast_channel_hangupcause(chan);
|
||||
else
|
||||
hanguptreecause = AST_CAUSE_NORMAL_CLEARING;
|
||||
}
|
||||
hanguptree(&out_chans, NULL, hanguptreecause);
|
||||
pbx_builtin_setvar_helper(chan, "DIALSTATUS", pa.status);
|
||||
ast_debug(1, "Exiting with DIALSTATUS=%s.\n", pa.status);
|
||||
|
||||
|
|
|
@ -361,7 +361,6 @@ static int disa_exec(struct ast_channel *chan, const char *data)
|
|||
|
||||
if (k == 3) {
|
||||
int recheck = 0;
|
||||
struct ast_app *app_reset_cdr;
|
||||
|
||||
if (!ast_exists_extension(chan, args.context, exten, 1,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
|
@ -386,10 +385,7 @@ static int disa_exec(struct ast_channel *chan, const char *data)
|
|||
ast_channel_unlock(chan);
|
||||
}
|
||||
|
||||
app_reset_cdr = pbx_findapp("ResetCDR");
|
||||
if (app_reset_cdr) {
|
||||
pbx_exec(chan, app_reset_cdr, special_noanswer ? "" : "e");
|
||||
} else {
|
||||
if (ast_pbx_exec_application(chan, "ResetCDR", special_noanswer ? "" : "e")) {
|
||||
ast_log(AST_LOG_NOTICE, "ResetCDR application not found; CDR will not be reset\n");
|
||||
}
|
||||
ast_explicit_goto(chan, args.context, exten, 1);
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2021, Naveen Albert
|
||||
*
|
||||
* Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Technology independent asynchronous DTMF collection
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup functions
|
||||
*
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/framehook.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/conversions.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="StoreDTMF" language="en_US">
|
||||
<since>
|
||||
<version>16.20.0</version>
|
||||
<version>18.6.0</version>
|
||||
<version>19.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Stores DTMF digits transmitted or received on a channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="direction" required="true">
|
||||
<para>Must be <literal>TX</literal> or <literal>RX</literal> to
|
||||
store digits, or <literal>remove</literal> to disable.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>The StoreDTMF function can be used to obtain digits sent in the
|
||||
<literal>TX</literal> or <literal>RX</literal> direction of any channel.</para>
|
||||
<para>The arguments are:</para>
|
||||
<para><replaceable>var_name</replaceable>: Name of variable to which to append
|
||||
digits.</para>
|
||||
<para><replaceable>max_digits</replaceable>: The maximum number of digits to
|
||||
store in the variable. Defaults to 0 (no maximum). After reading <literal>
|
||||
maximum</literal> digits, no more digits will be stored.</para>
|
||||
<example title="Store digits in CDR variable">
|
||||
same => n,StoreDTMF(TX,CDR(digits))
|
||||
</example>
|
||||
<example title="Store up to 24 digits">
|
||||
same => n,StoreDTMF(RX,testvar,24)
|
||||
</example>
|
||||
<example title="Disable digit collection">
|
||||
same => n,StoreDTMF(remove)
|
||||
</example>
|
||||
</description>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static char *app = "StoreDTMF";
|
||||
|
||||
/*! \brief Private data structure used with the function's datastore */
|
||||
struct dtmf_store_data {
|
||||
int framehook_id;
|
||||
char *rx_var;
|
||||
char *tx_var;
|
||||
int maxdigits;
|
||||
};
|
||||
|
||||
static void datastore_destroy_cb(void *data) {
|
||||
struct dtmf_store_data *d;
|
||||
d = data;
|
||||
if (d) {
|
||||
if (d->rx_var) {
|
||||
ast_free(d->rx_var);
|
||||
}
|
||||
if (d->tx_var) {
|
||||
ast_free(d->tx_var);
|
||||
}
|
||||
ast_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief The channel datastore the function uses to store state */
|
||||
static const struct ast_datastore_info dtmf_store_datastore = {
|
||||
.type = "dtmf_store",
|
||||
.destroy = datastore_destroy_cb
|
||||
};
|
||||
|
||||
/*! \internal \brief Store digits tx/rx on the channel */
|
||||
static int remove_dtmf_store(struct ast_channel *chan)
|
||||
{
|
||||
struct ast_datastore *datastore = NULL;
|
||||
struct dtmf_store_data *data;
|
||||
SCOPED_CHANNELLOCK(chan_lock, chan);
|
||||
|
||||
datastore = ast_channel_datastore_find(chan, &dtmf_store_datastore, NULL);
|
||||
if (!datastore) {
|
||||
ast_log(AST_LOG_WARNING, "Cannot remove StoreDTMF from %s: StoreDTMF not currently enabled\n",
|
||||
ast_channel_name(chan));
|
||||
return -1;
|
||||
}
|
||||
data = datastore->data;
|
||||
|
||||
if (ast_framehook_detach(chan, data->framehook_id)) {
|
||||
ast_log(AST_LOG_WARNING, "Failed to remove StoreDTMF framehook from channel %s\n",
|
||||
ast_channel_name(chan));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_channel_datastore_remove(chan, datastore)) {
|
||||
ast_log(AST_LOG_WARNING, "Failed to remove StoreDTMF datastore from channel %s\n",
|
||||
ast_channel_name(chan));
|
||||
return -1;
|
||||
}
|
||||
ast_datastore_free(datastore);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Frame hook that is called to intercept digit/undigit */
|
||||
static struct ast_frame *dtmf_store_framehook(struct ast_channel *chan,
|
||||
struct ast_frame *f, enum ast_framehook_event event, void *data)
|
||||
{
|
||||
char currentdata[512];
|
||||
char varnamesub[64];
|
||||
char *varname = NULL;
|
||||
struct dtmf_store_data *framedata = data;
|
||||
int len;
|
||||
|
||||
if (!f || !framedata) {
|
||||
return f;
|
||||
}
|
||||
|
||||
if ((event != AST_FRAMEHOOK_EVENT_WRITE) && (event != AST_FRAMEHOOK_EVENT_READ)) {
|
||||
return f;
|
||||
}
|
||||
|
||||
if (f->frametype != AST_FRAME_DTMF_END) {
|
||||
return f;
|
||||
}
|
||||
|
||||
/* If this is DTMF then store the digits */
|
||||
if (event == AST_FRAMEHOOK_EVENT_READ && framedata->rx_var) { /* coming from source */
|
||||
varname = framedata->rx_var;
|
||||
} else if (event == AST_FRAMEHOOK_EVENT_WRITE && framedata->tx_var) { /* going to source */
|
||||
varname = framedata->tx_var;
|
||||
}
|
||||
|
||||
if (!varname) {
|
||||
return f;
|
||||
}
|
||||
|
||||
sprintf(varnamesub, "${%s}", varname);
|
||||
pbx_substitute_variables_helper(chan, varnamesub, currentdata, 511);
|
||||
/* pbx_builtin_getvar_helper works for regular vars but not CDR vars */
|
||||
if (ast_strlen_zero(currentdata)) { /* var doesn't exist yet */
|
||||
ast_debug(3, "Creating new digit store: %s\n", varname);
|
||||
}
|
||||
len = strlen(currentdata);
|
||||
if (framedata->maxdigits > 0 && len >= framedata->maxdigits) {
|
||||
ast_debug(3, "Reached digit limit: %d\n", framedata->maxdigits);
|
||||
remove_dtmf_store(chan); /* reached max digit count, stop now */
|
||||
return f;
|
||||
} else {
|
||||
char newdata[len + 2]; /* one more char + terminator */
|
||||
if (len > 0) {
|
||||
ast_copy_string(newdata, currentdata, len + 2);
|
||||
}
|
||||
newdata[len] = (unsigned) f->subclass.integer;
|
||||
newdata[len + 1] = '\0';
|
||||
ast_debug(3, "Appending to digit store: now %s\n", newdata);
|
||||
pbx_builtin_setvar_helper(chan, varname, newdata);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/*! \internal \brief Enable digit interception on the channel */
|
||||
static int dtmfstore_exec(struct ast_channel *chan, const char *appdata)
|
||||
{
|
||||
struct ast_datastore *datastore;
|
||||
struct dtmf_store_data *data;
|
||||
static struct ast_framehook_interface digit_framehook_interface = {
|
||||
.version = AST_FRAMEHOOK_INTERFACE_VERSION,
|
||||
.event_cb = dtmf_store_framehook,
|
||||
.disable_inheritance = 1,
|
||||
};
|
||||
char *parse = ast_strdupa(appdata);
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(direction);
|
||||
AST_APP_ARG(varname);
|
||||
AST_APP_ARG(maxdigits);
|
||||
);
|
||||
SCOPED_CHANNELLOCK(chan_lock, chan);
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(appdata)) {
|
||||
ast_log(AST_LOG_WARNING, "StoreDTMF requires an argument\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcasecmp(args.direction, "remove")) {
|
||||
return remove_dtmf_store(chan);
|
||||
}
|
||||
|
||||
datastore = ast_channel_datastore_find(chan, &dtmf_store_datastore, NULL);
|
||||
if (datastore) {
|
||||
ast_log(AST_LOG_WARNING, "StoreDTMF already set on '%s'\n",
|
||||
ast_channel_name(chan));
|
||||
return 0;
|
||||
}
|
||||
|
||||
datastore = ast_datastore_alloc(&dtmf_store_datastore, NULL);
|
||||
if (!datastore) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = ast_calloc(1, sizeof(*data));
|
||||
if (!data) {
|
||||
ast_datastore_free(datastore);
|
||||
return -1;
|
||||
}
|
||||
|
||||
digit_framehook_interface.data = data;
|
||||
|
||||
data->rx_var = NULL;
|
||||
data->tx_var = NULL;
|
||||
data->maxdigits = 0;
|
||||
|
||||
if (!strcasecmp(args.direction, "tx")) {
|
||||
data->tx_var = ast_strdup(args.varname);
|
||||
} else if (!strcasecmp(args.direction, "rx")) {
|
||||
data->rx_var = ast_strdup(args.varname);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Direction must be either RX or TX\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(args.maxdigits)) {
|
||||
if (ast_str_to_int(args.maxdigits,&(data->maxdigits))) {
|
||||
ast_log(LOG_ERROR, "Invalid integer: %s\n", args.maxdigits);
|
||||
return -1;
|
||||
}
|
||||
if (data->maxdigits < 0) {
|
||||
ast_log(LOG_ERROR, "Invalid natural number: %d\n", data->maxdigits);
|
||||
return -1;
|
||||
} else if (data->maxdigits == 0) {
|
||||
ast_log(LOG_WARNING, "No maximum digit count set\n");
|
||||
}
|
||||
}
|
||||
|
||||
data->framehook_id = ast_framehook_attach(chan, &digit_framehook_interface);
|
||||
if (data->framehook_id < 0) {
|
||||
ast_log(AST_LOG_WARNING, "Failed to attach StoreDTMF framehook to '%s'\n",
|
||||
ast_channel_name(chan));
|
||||
ast_datastore_free(datastore);
|
||||
ast_free(data);
|
||||
return -1;
|
||||
}
|
||||
datastore->data = data;
|
||||
|
||||
ast_channel_datastore_add(chan, datastore);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app, dtmfstore_exec);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Technology independent async DTMF storage");
|
|
@ -254,9 +254,9 @@ static struct ast_frame *gen_readframe(struct gen_state *state)
|
|||
if (!(state->stream && (f = ast_readframe(state->stream)))) {
|
||||
if (state->current) {
|
||||
/* remove finished file from playlist */
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
AST_LIST_REMOVE_HEAD(&u->playlist, list);
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
AST_LIST_REMOVE_HEAD(&u->playlist, list);
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
/* add finished file to finishlist */
|
||||
AST_LIST_LOCK(&u->finishlist);
|
||||
AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
|
||||
|
@ -581,7 +581,7 @@ static int app_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
exit:
|
||||
if (u->gen_active) {
|
||||
ast_deactivate_generator(chan);
|
||||
}
|
||||
|
@ -622,21 +622,21 @@ static int app_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
|
||||
static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
struct ast_iostream *eivr_events,
|
||||
struct ast_iostream *eivr_commands,
|
||||
struct ast_iostream *eivr_errors,
|
||||
const struct ast_str *args, const struct ast_flags flags)
|
||||
struct ast_iostream *eivr_events,
|
||||
struct ast_iostream *eivr_commands,
|
||||
struct ast_iostream *eivr_errors,
|
||||
const struct ast_str *args, const struct ast_flags flags)
|
||||
{
|
||||
char input[1024];
|
||||
struct playlist_entry *entry;
|
||||
struct ast_frame *f;
|
||||
int ms;
|
||||
int exception;
|
||||
int ready_fd;
|
||||
int exception;
|
||||
int ready_fd;
|
||||
int waitfds[2];
|
||||
int r;
|
||||
struct ast_channel *rchan;
|
||||
int res = -1;
|
||||
struct ast_channel *rchan;
|
||||
int res = -1;
|
||||
int hangup_info_sent = 0;
|
||||
|
||||
waitfds[0] = ast_iostream_get_fd(eivr_commands);
|
||||
|
@ -645,78 +645,78 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
|||
while (1) {
|
||||
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) {
|
||||
ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
|
||||
break;
|
||||
}
|
||||
if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
|
||||
break;
|
||||
}
|
||||
if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
|
||||
if (ast_test_flag(&flags, ignore_hangup)) {
|
||||
ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
|
||||
send_eivr_event(eivr_events, 'I', "HANGUP", chan);
|
||||
hangup_info_sent = 1;
|
||||
} else {
|
||||
ast_verb(3, "Got check_hangup\n");
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
break;
|
||||
ast_verb(3, "Got check_hangup\n");
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ready_fd = 0;
|
||||
ms = 100;
|
||||
errno = 0;
|
||||
exception = 0;
|
||||
ready_fd = 0;
|
||||
ms = 100;
|
||||
errno = 0;
|
||||
exception = 0;
|
||||
|
||||
rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors) ? 2 : 1, &exception, &ready_fd, &ms);
|
||||
|
||||
if (ast_channel_state(chan) == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
|
||||
AST_LIST_LOCK(&u->finishlist);
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
|
||||
send_eivr_event(eivr_events, 'F', entry->filename, chan);
|
||||
ast_free(entry);
|
||||
}
|
||||
AST_LIST_UNLOCK(&u->finishlist);
|
||||
}
|
||||
if (ast_channel_state(chan) == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
|
||||
AST_LIST_LOCK(&u->finishlist);
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
|
||||
send_eivr_event(eivr_events, 'F', entry->filename, chan);
|
||||
ast_free(entry);
|
||||
}
|
||||
AST_LIST_UNLOCK(&u->finishlist);
|
||||
}
|
||||
|
||||
if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
|
||||
/* the channel has something */
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
ast_verb(3, "Returned no frame\n");
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
break;
|
||||
}
|
||||
if (f->frametype == AST_FRAME_DTMF) {
|
||||
send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
|
||||
if (u->option_autoclear) {
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
if (!u->abort_current_sound && !u->playing_silence) {
|
||||
if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
|
||||
/* the channel has something */
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
ast_verb(3, "Returned no frame\n");
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
break;
|
||||
}
|
||||
if (f->frametype == AST_FRAME_DTMF) {
|
||||
send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
|
||||
if (u->option_autoclear) {
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
if (!u->abort_current_sound && !u->playing_silence) {
|
||||
/* send interrupted file as T data */
|
||||
if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_eivr_event(eivr_events, 'T', entry->filename, chan);
|
||||
if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_eivr_event(eivr_events, 'T', entry->filename, chan);
|
||||
ast_free(entry);
|
||||
}
|
||||
}
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_eivr_event(eivr_events, 'D', entry->filename, chan);
|
||||
ast_free(entry);
|
||||
}
|
||||
if (!u->playing_silence)
|
||||
u->abort_current_sound = 1;
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
}
|
||||
} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
|
||||
ast_verb(3, "Got AST_CONTROL_HANGUP\n");
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_eivr_event(eivr_events, 'D', entry->filename, chan);
|
||||
ast_free(entry);
|
||||
}
|
||||
if (!u->playing_silence)
|
||||
u->abort_current_sound = 1;
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
}
|
||||
} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
|
||||
ast_verb(3, "Got AST_CONTROL_HANGUP\n");
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
if (f->data.uint32) {
|
||||
ast_channel_hangupcause_set(chan, f->data.uint32);
|
||||
}
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
} else if (ready_fd == waitfds[0]) {
|
||||
if (exception) {
|
||||
ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
} else if (ready_fd == waitfds[0]) {
|
||||
if (exception) {
|
||||
ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
|
||||
break;
|
||||
}
|
||||
|
||||
r = ast_iostream_gets(eivr_commands, input, sizeof(input));
|
||||
if (r <= 0) {
|
||||
|
@ -784,112 +784,112 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
|||
u->abort_current_sound = 1;
|
||||
}
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
} else if (input[0] == EIVR_CMD_SQUE) {
|
||||
} else if (input[0] == EIVR_CMD_SQUE) {
|
||||
if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
|
||||
send_eivr_event(eivr_events, 'Z', NULL, chan);
|
||||
continue;
|
||||
}
|
||||
if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'Z', &input[2], chan);
|
||||
} else {
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
if (!u->abort_current_sound && !u->playing_silence) {
|
||||
if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'Z', &input[2], chan);
|
||||
} else {
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
if (!u->abort_current_sound && !u->playing_silence) {
|
||||
/* send interrupted file as T data */
|
||||
if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_eivr_event(eivr_events, 'T', entry->filename, chan);
|
||||
if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_eivr_event(eivr_events, 'T', entry->filename, chan);
|
||||
ast_free(entry);
|
||||
}
|
||||
}
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_eivr_event(eivr_events, 'D', entry->filename, chan);
|
||||
ast_free(entry);
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
|
||||
send_eivr_event(eivr_events, 'D', entry->filename, chan);
|
||||
ast_free(entry);
|
||||
}
|
||||
if (!u->playing_silence) {
|
||||
u->abort_current_sound = 1;
|
||||
if (!u->playing_silence) {
|
||||
u->abort_current_sound = 1;
|
||||
}
|
||||
entry = make_entry(&input[2]);
|
||||
if (entry) {
|
||||
AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
|
||||
entry = make_entry(&input[2]);
|
||||
if (entry) {
|
||||
AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
|
||||
}
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
}
|
||||
} else if (input[0] == EIVR_CMD_APND) {
|
||||
} else if (input[0] == EIVR_CMD_APND) {
|
||||
if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
|
||||
send_eivr_event(eivr_events, 'Z', NULL, chan);
|
||||
continue;
|
||||
}
|
||||
if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'Z', &input[2], chan);
|
||||
} else {
|
||||
entry = make_entry(&input[2]);
|
||||
if (entry) {
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'Z', &input[2], chan);
|
||||
} else {
|
||||
entry = make_entry(&input[2]);
|
||||
if (entry) {
|
||||
AST_LIST_LOCK(&u->playlist);
|
||||
AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
}
|
||||
}
|
||||
} else if (input[0] == EIVR_CMD_GET) {
|
||||
char response[2048];
|
||||
}
|
||||
} else if (input[0] == EIVR_CMD_GET) {
|
||||
char response[2048];
|
||||
ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
|
||||
ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
|
||||
send_eivr_event(eivr_events, 'G', response, chan);
|
||||
} else if (input[0] == EIVR_CMD_SVAR) {
|
||||
ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
|
||||
send_eivr_event(eivr_events, 'G', response, chan);
|
||||
} else if (input[0] == EIVR_CMD_SVAR) {
|
||||
ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
|
||||
ast_eivr_setvariable(chan, &input[2]);
|
||||
} else if (input[0] == EIVR_CMD_LOG) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
|
||||
} else if (input[0] == EIVR_CMD_XIT) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
|
||||
ast_eivr_setvariable(chan, &input[2]);
|
||||
} else if (input[0] == EIVR_CMD_LOG) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
|
||||
} else if (input[0] == EIVR_CMD_XIT) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
|
||||
ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
|
||||
res = 0;
|
||||
break;
|
||||
res = 0;
|
||||
break;
|
||||
} else if (input[0] == EIVR_CMD_EXIT) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'E', NULL, chan);
|
||||
res = 0;
|
||||
break;
|
||||
} else if (input[0] == EIVR_CMD_HGUP) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
break;
|
||||
} else if (input[0] == EIVR_CMD_OPT) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'E', NULL, chan);
|
||||
res = 0;
|
||||
break;
|
||||
} else if (input[0] == EIVR_CMD_HGUP) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
break;
|
||||
} else if (input[0] == EIVR_CMD_OPT) {
|
||||
if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
|
||||
send_eivr_event(eivr_events, 'Z', NULL, chan);
|
||||
continue;
|
||||
}
|
||||
if (!strcasecmp(&input[2], "autoclear"))
|
||||
u->option_autoclear = 1;
|
||||
else if (!strcasecmp(&input[2], "noautoclear"))
|
||||
u->option_autoclear = 0;
|
||||
else
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
|
||||
}
|
||||
} else if (ready_fd == waitfds[1]) {
|
||||
if (exception) {
|
||||
ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
|
||||
break;
|
||||
}
|
||||
|
||||
r = ast_iostream_gets(eivr_errors, input, sizeof(input));
|
||||
if (r > 0) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
|
||||
} else if (r == 0) {
|
||||
ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
|
||||
break;
|
||||
if (!strcasecmp(&input[2], "autoclear"))
|
||||
u->option_autoclear = 1;
|
||||
else if (!strcasecmp(&input[2], "noautoclear"))
|
||||
u->option_autoclear = 0;
|
||||
else
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
|
||||
}
|
||||
} else if (ready_fd == waitfds[1]) {
|
||||
if (exception) {
|
||||
ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
|
||||
break;
|
||||
}
|
||||
} else if ((ready_fd < 0) && ms) {
|
||||
if (errno == 0 || errno == EINTR)
|
||||
continue;
|
||||
|
||||
ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
r = ast_iostream_gets(eivr_errors, input, sizeof(input));
|
||||
if (r > 0) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
|
||||
} else if (r == 0) {
|
||||
ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
|
||||
break;
|
||||
}
|
||||
} else if ((ready_fd < 0) && ms) {
|
||||
if (errno == 0 || errno == EINTR)
|
||||
continue;
|
||||
|
||||
ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
1003
apps/app_fax.c
1003
apps/app_fax.c
File diff suppressed because it is too large
Load Diff
|
@ -281,8 +281,6 @@ static int festival_exec(struct ast_channel *chan, const char *vdata)
|
|||
int usecache;
|
||||
int res = 0;
|
||||
struct sockaddr_in serv_addr;
|
||||
struct hostent *serverhost;
|
||||
struct ast_hostent ahp;
|
||||
int fd;
|
||||
FILE *fs;
|
||||
const char *host;
|
||||
|
@ -398,15 +396,17 @@ static int festival_exec(struct ast_channel *chan, const char *vdata)
|
|||
|
||||
if ((serv_addr.sin_addr.s_addr = inet_addr(host)) == -1) {
|
||||
/* its a name rather than an ipnum */
|
||||
serverhost = ast_gethostbyname(host, &ahp);
|
||||
struct ast_sockaddr addr = { {0,} };
|
||||
|
||||
if (serverhost == NULL) {
|
||||
ast_log(LOG_WARNING, "festival_client: gethostbyname failed\n");
|
||||
if (ast_sockaddr_resolve_first_af(&addr, host, PARSE_PORT_FORBID, AF_INET)) {
|
||||
ast_log(LOG_WARNING, "festival_client: ast_sockaddr_resolve_first_af() failed\n");
|
||||
ast_config_destroy(cfg);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
memmove(&serv_addr.sin_addr, serverhost->h_addr, serverhost->h_length);
|
||||
|
||||
/* We'll overwrite port and family in a sec */
|
||||
ast_sockaddr_to_sin(&addr, &serv_addr);
|
||||
}
|
||||
|
||||
serv_addr.sin_family = AF_INET;
|
||||
|
@ -433,7 +433,7 @@ static int festival_exec(struct ast_channel *chan, const char *vdata)
|
|||
}
|
||||
readcache = 0;
|
||||
writecache = 0;
|
||||
if (strlen(cachedir) + strlen(MD5Hex) + 1 <= MAXFESTLEN && (usecache == -1)) {
|
||||
if (strlen(cachedir) + sizeof(MD5Hex) + 1 <= MAXFESTLEN && (usecache == -1)) {
|
||||
snprintf(cachefile, sizeof(cachefile), "%s/%s", cachedir, MD5Hex);
|
||||
fdesc = open(cachefile, O_RDWR);
|
||||
if (fdesc == -1) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Anthony Minessale anthmct@yahoo.com
|
||||
* Development of this app Sponsered/Funded by TAAN Softworks Corp
|
||||
* Development of this app Sponsored/Funded by TAAN Softworks Corp
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
|
|
214
apps/app_ices.c
214
apps/app_ices.c
|
@ -1,214 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Stream to an icecast server via ICES (see contrib/asterisk-ices.xml)
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* ICES - http://www.icecast.org/ices.php
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>deprecated</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/frame.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/format_cache.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="ICES" language="en_US">
|
||||
<synopsis>
|
||||
Encode and stream using 'ices'.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="config" required="true">
|
||||
<para>ICES configuration file.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Streams to an icecast server using ices (available separately).
|
||||
A configuration file must be supplied for ices (see contrib/asterisk-ices.xml).</para>
|
||||
<note><para>ICES version 2 client and server required.</para></note>
|
||||
</description>
|
||||
</application>
|
||||
|
||||
***/
|
||||
|
||||
#define path_BIN "/usr/bin/"
|
||||
#define path_LOCAL "/usr/local/bin/"
|
||||
|
||||
static char *app = "ICES";
|
||||
|
||||
static int icesencode(char *filename, int fd)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_safe_fork(0);
|
||||
if (res < 0)
|
||||
ast_log(LOG_WARNING, "Fork failed\n");
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (ast_opt_high_priority)
|
||||
ast_set_priority(0);
|
||||
dup2(fd, STDIN_FILENO);
|
||||
ast_close_fds_above_n(STDERR_FILENO);
|
||||
|
||||
/* Most commonly installed in /usr/local/bin
|
||||
* But many places has it in /usr/bin
|
||||
* As a last-ditch effort, try to use PATH
|
||||
*/
|
||||
execl(path_LOCAL "ices2", "ices", filename, SENTINEL);
|
||||
execl(path_BIN "ices2", "ices", filename, SENTINEL);
|
||||
execlp("ices2", "ices", filename, SENTINEL);
|
||||
|
||||
ast_debug(1, "Couldn't find ices version 2, attempting to use ices version 1.\n");
|
||||
|
||||
execl(path_LOCAL "ices", "ices", filename, SENTINEL);
|
||||
execl(path_BIN "ices", "ices", filename, SENTINEL);
|
||||
execlp("ices", "ices", filename, SENTINEL);
|
||||
|
||||
ast_log(LOG_WARNING, "Execute of ices failed, could not find command.\n");
|
||||
close(fd);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
static int ices_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
int res = 0;
|
||||
int fds[2];
|
||||
int ms = -1;
|
||||
int pid = -1;
|
||||
struct ast_format *oreadformat;
|
||||
struct ast_frame *f;
|
||||
char filename[256]="";
|
||||
char *c;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pipe(fds)) {
|
||||
ast_log(LOG_WARNING, "Unable to create pipe\n");
|
||||
return -1;
|
||||
}
|
||||
ast_fd_set_flags(fds[1], O_NONBLOCK);
|
||||
|
||||
ast_stopstream(chan);
|
||||
|
||||
if (ast_channel_state(chan) != AST_STATE_UP)
|
||||
res = ast_answer(chan);
|
||||
|
||||
if (res) {
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
ast_log(LOG_WARNING, "Answer failed!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
oreadformat = ao2_bump(ast_channel_readformat(chan));
|
||||
res = ast_set_read_format(chan, ast_format_slin);
|
||||
if (res < 0) {
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
|
||||
ao2_cleanup(oreadformat);
|
||||
return -1;
|
||||
}
|
||||
if (((char *)data)[0] == '/')
|
||||
ast_copy_string(filename, (char *) data, sizeof(filename));
|
||||
else
|
||||
snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_CONFIG_DIR, (char *)data);
|
||||
/* Placeholder for options */
|
||||
c = strchr(filename, '|');
|
||||
if (c)
|
||||
*c = '\0';
|
||||
res = icesencode(filename, fds[0]);
|
||||
if (res >= 0) {
|
||||
pid = res;
|
||||
for (;;) {
|
||||
/* Wait for audio, and stream */
|
||||
ms = ast_waitfor(chan, -1);
|
||||
if (ms < 0) {
|
||||
ast_debug(1, "Hangup detected\n");
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
ast_debug(1, "Null frame == hangup() detected\n");
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if (f->frametype == AST_FRAME_VOICE) {
|
||||
res = write(fds[1], f->data.ptr, f->datalen);
|
||||
if (res < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
ast_log(LOG_WARNING, "Write failed to pipe: %s\n", strerror(errno));
|
||||
res = -1;
|
||||
ast_frfree(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
}
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
|
||||
if (pid > -1)
|
||||
kill(pid, SIGKILL);
|
||||
if (!res && oreadformat)
|
||||
ast_set_read_format(chan, oreadformat);
|
||||
ao2_cleanup(oreadformat);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app, ices_exec);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_DEPRECATED(ASTERISK_GPL_KEY, "Encode and Stream via icecast and ices");
|
107
apps/app_image.c
107
apps/app_image.c
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to transmit an image
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>deprecated</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/image.h"
|
||||
|
||||
static char *app = "SendImage";
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="SendImage" language="en_US">
|
||||
<synopsis>
|
||||
Sends an image file.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="filename" required="true">
|
||||
<para>Path of the filename (image) to send.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Send an image file on a channel supporting it.</para>
|
||||
<para>Result of transmission will be stored in <variable>SENDIMAGESTATUS</variable></para>
|
||||
<variablelist>
|
||||
<variable name="SENDIMAGESTATUS">
|
||||
<value name="SUCCESS">
|
||||
Transmission succeeded.
|
||||
</value>
|
||||
<value name="FAILURE">
|
||||
Transmission failed.
|
||||
</value>
|
||||
<value name="UNSUPPORTED">
|
||||
Image transmission not supported by channel.
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendText</ref>
|
||||
<ref type="application">SendURL</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static int sendimage_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "SendImage requires an argument (filename)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ast_supports_images(chan)) {
|
||||
/* Does not support transport */
|
||||
pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "UNSUPPORTED");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ast_send_image(chan, data)) {
|
||||
pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "SUCCESS");
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "FAILURE");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app, sendimage_exec);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_DEPRECATED(ASTERISK_GPL_KEY, "Image Transmission Application");
|
|
@ -615,8 +615,6 @@ static int queue_voice_frame(struct jack_data *jack_data, struct ast_frame *f)
|
|||
* Read data from the input ringbuffer, which is the properly resampled audio
|
||||
* that was read from the jack input port. Write it to the channel in 20 ms frames,
|
||||
* or fill up an output frame instead if one is provided.
|
||||
*
|
||||
* \return Nothing.
|
||||
*/
|
||||
static void handle_jack_audio(struct ast_channel *chan, struct jack_data *jack_data,
|
||||
struct ast_frame *out_frame)
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
<defaultenabled>no</defaultenabled>
|
||||
<support_level>deprecated</support_level>
|
||||
<replacement>app_stack (GoSub)</replacement>
|
||||
<deprecated_in>16</deprecated_in>
|
||||
<removed_in>21</removed_in>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
@ -73,7 +75,7 @@
|
|||
of nesting (macro calling macro calling macro, etc.); It may be possible that stack-intensive
|
||||
applications in deeply nested macros could cause asterisk to crash earlier than this limit.
|
||||
It is advised that if you need to deeply nest macro calls, that you use the Gosub application
|
||||
(now allows arguments like a Macro) with explict Return() calls instead.</para></warning>
|
||||
(now allows arguments like a Macro) with explicit Return() calls instead.</para></warning>
|
||||
<warning><para>Use of the application <literal>WaitExten</literal> within a macro will not function
|
||||
as expected. Please use the <literal>Read</literal> application in order to read DTMF from a channel
|
||||
currently executing a macro.</para></warning>
|
||||
|
|
|
@ -41,8 +41,10 @@
|
|||
/*** MODULEINFO
|
||||
<depend>dahdi</depend>
|
||||
<defaultenabled>no</defaultenabled>
|
||||
<support_level>extended</support_level>
|
||||
<support_level>deprecated</support_level>
|
||||
<replacement>app_confbridge</replacement>
|
||||
<deprecated_in>19</deprecated_in>
|
||||
<removed_in>21</removed_in>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
@ -637,6 +639,82 @@
|
|||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="MeetmeList">
|
||||
<managerEventInstance class="EVENT_FLAG_CALL">
|
||||
<synopsis>Raised in response to a MeetmeList command.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Conference">
|
||||
<para>Conference ID.</para>
|
||||
</parameter>
|
||||
<parameter name="UserNumber">
|
||||
<para>User ID.</para>
|
||||
</parameter>
|
||||
<parameter name="CallerIDNum">
|
||||
<para>Caller ID number.</para>
|
||||
</parameter>
|
||||
<parameter name="CallerIDName">
|
||||
<para>Caller ID name.</para>
|
||||
</parameter>
|
||||
<parameter name="ConnectedLineNum">
|
||||
<para>Connected Line number.</para>
|
||||
</parameter>
|
||||
<parameter name="ConnectedLineName">
|
||||
<para>Connected Line name.</para>
|
||||
</parameter>
|
||||
<parameter name="Channel">
|
||||
<para>Channel name</para>
|
||||
</parameter>
|
||||
<parameter name="Admin">
|
||||
<para>Whether or not the user is an admin.</para>
|
||||
</parameter>
|
||||
<parameter name="Role">
|
||||
<para>User role. Can be "Listen only", "Talk only", or "Talk and listen".</para>
|
||||
</parameter>
|
||||
<parameter name="MarkedUser">
|
||||
<para>Whether or not the user is a marked user.</para>
|
||||
</parameter>
|
||||
<parameter name="Muted">
|
||||
<para>Whether or not the user is currently muted.</para>
|
||||
</parameter>
|
||||
<parameter name="Talking">
|
||||
<para>Whether or not the user is currently talking.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<see-also>
|
||||
<ref type="manager">MeetmeList</ref>
|
||||
<ref type="application">MeetMe</ref>
|
||||
</see-also>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="MeetmeListRooms">
|
||||
<managerEventInstance class="EVENT_FLAG_CALL">
|
||||
<synopsis>Raised in response to a MeetmeListRooms command.</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Conference">
|
||||
<para>Conference ID.</para>
|
||||
</parameter>
|
||||
<parameter name="Parties">
|
||||
<para>Number of parties in the conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Marked">
|
||||
<para>Number of marked users in the conference.</para>
|
||||
</parameter>
|
||||
<parameter name="Activity">
|
||||
<para>Total duration of conference in HH:MM:SS format.</para>
|
||||
</parameter>
|
||||
<parameter name="Creation">
|
||||
<para>How the conference was created: "Dyanmic" or "Static".</para>
|
||||
</parameter>
|
||||
<parameter name="Locked">
|
||||
<para>Whether or not the conference is locked.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<see-also>
|
||||
<ref type="manager">MeetmeListRooms</ref>
|
||||
<ref type="application">MeetMe</ref>
|
||||
</see-also>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
***/
|
||||
|
||||
#define CONFIG_FILE_NAME "meetme.conf"
|
||||
|
@ -1334,7 +1412,7 @@ static struct ast_json *status_to_json(int on)
|
|||
* \brief Generate a stasis message associated with a meetme event
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param meetme_confere The conference responsible for generating this message
|
||||
* \param meetme_conference The conference responsible for generating this message
|
||||
* \param chan The channel involved in the message (NULL allowed)
|
||||
* \param user The conference user involved in the message (NULL allowed)
|
||||
* \param message_type the type the stasis message being generated
|
||||
|
@ -2834,7 +2912,7 @@ static void meetme_menu_normal(enum menu_modes *menu_mode, int *dtmf, struct ast
|
|||
}
|
||||
|
||||
/*! \internal
|
||||
* \brief Processes menu options for the adminstrator menu (accessible through the 's' option for app_meetme)
|
||||
* \brief Processes menu options for the administrator menu (accessible through the 's' option for app_meetme)
|
||||
*
|
||||
* \param menu_mode a pointer to the currently active menu_mode.
|
||||
* \param dtmf a pointer to the dtmf value currently being processed against the menu.
|
||||
|
@ -2961,7 +3039,8 @@ static void meetme_menu_admin(enum menu_modes *menu_mode, int *dtmf, struct ast_
|
|||
* \param confflags flags used by conf for various options
|
||||
* \param chan ast_channel belonging to the user who called the menu
|
||||
* \param user which meetme conference user invoked the menu
|
||||
* \param recordingtmp character buffer which may hold the name of the conference recording file
|
||||
* \param recordingtmp, recordingtmp_size character buffer which may hold the name of the conference recording file
|
||||
* \param cap_slin
|
||||
*/
|
||||
static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf,
|
||||
struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
|
||||
|
@ -3148,7 +3227,8 @@ static void meetme_menu_admin_extended(enum menu_modes *menu_mode, int *dtmf,
|
|||
* \param confflags flags used by conf for various options
|
||||
* \param chan ast_channel belonging to the user who called the menu
|
||||
* \param user which meetme conference user invoked the menu
|
||||
* \param recordingtmp character buffer which may hold the name of the conference recording file
|
||||
* \param recordingtmp,recordingtmp_size character buffer which may hold the name of the conference recording file
|
||||
* \param cap_slin
|
||||
*/
|
||||
static void meetme_menu(enum menu_modes *menu_mode, int *dtmf,
|
||||
struct ast_conference *conf, struct ast_flags64 *confflags, struct ast_channel *chan,
|
||||
|
@ -4757,7 +4837,7 @@ static struct ast_conference *find_conf(struct ast_channel *chan, char *confno,
|
|||
}
|
||||
}
|
||||
if (!var) {
|
||||
ast_debug(1, "%s isn't a valid conference\n", confno);
|
||||
ast_log(LOG_WARNING, "%s isn't a valid conference\n", confno);
|
||||
}
|
||||
ast_config_destroy(cfg);
|
||||
}
|
||||
|
@ -5219,7 +5299,7 @@ static int user_chan_cb(void *obj, void *args, int flags)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief The MeetMeadmin application
|
||||
/*! \brief The MeetMeAdmin application
|
||||
|
||||
MeetMeAdmin(confno, command, caller) */
|
||||
static int admin_exec(struct ast_channel *chan, const char *data) {
|
||||
|
@ -5235,7 +5315,9 @@ static int admin_exec(struct ast_channel *chan, const char *data) {
|
|||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "MeetMeAdmin requires an argument!\n");
|
||||
pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
|
||||
if (chan) {
|
||||
pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -5244,7 +5326,9 @@ static int admin_exec(struct ast_channel *chan, const char *data) {
|
|||
|
||||
if (!args.command) {
|
||||
ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
|
||||
pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
|
||||
if (chan) {
|
||||
pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOPARSE");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -5257,7 +5341,9 @@ static int admin_exec(struct ast_channel *chan, const char *data) {
|
|||
if (!cnf) {
|
||||
ast_log(LOG_WARNING, "Conference number '%s' not found!\n", args.confno);
|
||||
AST_LIST_UNLOCK(&confs);
|
||||
pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
|
||||
if (chan) {
|
||||
pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", "NOTFOUND");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5380,7 +5466,9 @@ usernotfound:
|
|||
AST_LIST_UNLOCK(&confs);
|
||||
|
||||
dispose_conf(cnf);
|
||||
pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
|
||||
if (chan) {
|
||||
pbx_builtin_setvar_helper(chan, "MEETMEADMINSTATUS", res == -2 ? "NOTFOUND" : res ? "FAILED" : "OK");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -8076,7 +8164,7 @@ static int reload(void)
|
|||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "MeetMe conference bridge",
|
||||
.support_level = AST_MODULE_SUPPORT_EXTENDED,
|
||||
.support_level = AST_MODULE_SUPPORT_DEPRECATED,
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
|
|
|
@ -0,0 +1,546 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2021, Naveen Albert
|
||||
*
|
||||
* Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief MF sender and receiver applications
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/dsp.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/indications.h"
|
||||
#include "asterisk/conversions.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="ReceiveMF" language="en_US">
|
||||
<since>
|
||||
<version>16.21.0</version>
|
||||
<version>18.7.0</version>
|
||||
<version>19.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Detects MF digits on a channel and saves them to a variable.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="variable" required="true">
|
||||
<para>The input digits will be stored in the given
|
||||
<replaceable>variable</replaceable> name.</para>
|
||||
</parameter>
|
||||
<parameter name="timeout">
|
||||
<para>The number of seconds to wait for all digits, if greater
|
||||
than <literal>0</literal>. Can be floating point. Default
|
||||
is no timeout.</para>
|
||||
</parameter>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="d">
|
||||
<para>Delay audio by a frame to try to extra quelch.</para>
|
||||
</option>
|
||||
<option name="l">
|
||||
<para>Receive digits even if a key pulse (KP) has not yet
|
||||
been received. By default, this application will ignore
|
||||
all other digits until a KP has been received.</para>
|
||||
</option>
|
||||
<option name="k">
|
||||
<para>Do not return a character for the KP digit.</para>
|
||||
</option>
|
||||
<option name="m">
|
||||
<para>Mute conference.</para>
|
||||
</option>
|
||||
<option name="n">
|
||||
<para>Maximum number of digits, regardless of the sequence.</para>
|
||||
</option>
|
||||
<option name="o">
|
||||
<para>Enable override. Repeated KPs will clear all previous digits.</para>
|
||||
</option>
|
||||
<option name="q">
|
||||
<para>Quelch MF from in-band.</para>
|
||||
</option>
|
||||
<option name="r">
|
||||
<para>"Radio" mode (relaxed MF).</para>
|
||||
</option>
|
||||
<option name="s">
|
||||
<para>Do not return a character for ST digits.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Reads a ST, STP, ST2P, or ST3P-terminated string of MF digits from
|
||||
the user in to the given <replaceable>variable</replaceable>.</para>
|
||||
<para>This application does not automatically answer the channel and
|
||||
should be preceded with <literal>Answer</literal> or
|
||||
<literal>Progress</literal> as needed.</para>
|
||||
<variablelist>
|
||||
<variable name="RECEIVEMFSTATUS">
|
||||
<para>This is the status of the read operation.</para>
|
||||
<value name="START" />
|
||||
<value name="ERROR" />
|
||||
<value name="HANGUP" />
|
||||
<value name="MAXDIGITS" />
|
||||
<value name="TIMEOUT" />
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Read</ref>
|
||||
<ref type="application">SendMF</ref>
|
||||
<ref type="application">ReceiveSF</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="SendMF" language="en_US">
|
||||
<since>
|
||||
<version>16.21.0</version>
|
||||
<version>18.7.0</version>
|
||||
<version>19.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Sends arbitrary MF digits on the current or specified channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="digits" required="true">
|
||||
<para>List of digits 0-9,*#ABC to send; w for a half-second pause,
|
||||
also f or F for a flash-hook if the channel supports flash-hook,
|
||||
h or H for 250 ms of 2600 Hz,
|
||||
and W for a wink if the channel supports wink.</para>
|
||||
<para>Key pulse and start digits are not included automatically.
|
||||
* is used for KP, # for ST, A for STP, B for ST2P, and C for ST3P.</para>
|
||||
</parameter>
|
||||
<parameter name="timeout_ms" required="false">
|
||||
<para>Amount of time to wait in ms between tones. (defaults to 50ms).</para>
|
||||
</parameter>
|
||||
<parameter name="duration_ms" required="false">
|
||||
<para>Duration of each numeric digit (defaults to 55ms).</para>
|
||||
</parameter>
|
||||
<parameter name="duration_ms_kp" required="false">
|
||||
<para>Duration of KP digits (defaults to 120ms).</para>
|
||||
</parameter>
|
||||
<parameter name="duration_ms_st" required="false">
|
||||
<para>Duration of ST, STP, ST2P, and ST3P digits (defaults to 65ms).</para>
|
||||
</parameter>
|
||||
<parameter name="channel" required="false">
|
||||
<para>Channel where digits will be played</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>It will send all digits or terminate if it encounters an error.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ReceiveMF</ref>
|
||||
<ref type="application">SendSF</ref>
|
||||
<ref type="application">SendDTMF</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<manager name="PlayMF" language="en_US">
|
||||
<since>
|
||||
<version>16.21.0</version>
|
||||
<version>18.7.0</version>
|
||||
<version>19.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Play MF digit on a specific channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Channel" required="true">
|
||||
<para>Channel name to send digit to.</para>
|
||||
</parameter>
|
||||
<parameter name="Digit" required="true">
|
||||
<para>The MF digit to play.</para>
|
||||
</parameter>
|
||||
<parameter name="Duration" required="false">
|
||||
<para>The duration, in milliseconds, of the digit to be played.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Plays an MF digit on the specified channel.</para>
|
||||
</description>
|
||||
</manager>
|
||||
***/
|
||||
|
||||
enum read_option_flags {
|
||||
OPT_DELAY = (1 << 0),
|
||||
OPT_MUTE = (1 << 1),
|
||||
OPT_QUELCH = (1 << 2),
|
||||
OPT_RELAXED = (1 << 3),
|
||||
OPT_LAX_KP = (1 << 4),
|
||||
OPT_PROCESS = (1 << 5),
|
||||
OPT_NO_KP = (1 << 6),
|
||||
OPT_NO_ST = (1 << 7),
|
||||
OPT_KP_OVERRIDE = (1 << 8),
|
||||
OPT_MAXDIGITS = (1 << 9),
|
||||
};
|
||||
|
||||
enum {
|
||||
OPT_ARG_MAXDIGITS,
|
||||
/* Must be the last element */
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(read_app_options, {
|
||||
AST_APP_OPTION('d', OPT_DELAY),
|
||||
AST_APP_OPTION('l', OPT_LAX_KP),
|
||||
AST_APP_OPTION('k', OPT_NO_KP),
|
||||
AST_APP_OPTION('m', OPT_MUTE),
|
||||
AST_APP_OPTION_ARG('n', OPT_MAXDIGITS, OPT_ARG_MAXDIGITS),
|
||||
AST_APP_OPTION('o', OPT_KP_OVERRIDE),
|
||||
AST_APP_OPTION('p', OPT_PROCESS),
|
||||
AST_APP_OPTION('q', OPT_QUELCH),
|
||||
AST_APP_OPTION('r', OPT_RELAXED),
|
||||
AST_APP_OPTION('s', OPT_NO_ST),
|
||||
});
|
||||
|
||||
static const char *readmf_name = "ReceiveMF";
|
||||
static const char sendmf_name[] = "SendMF";
|
||||
|
||||
#define MF_BETWEEN_MS 50
|
||||
#define MF_DURATION 55
|
||||
#define MF_KP_DURATION 120
|
||||
#define MF_ST_DURATION 65
|
||||
|
||||
/*!
|
||||
* \brief Detects MF digits on channel using DSP, terminated by ST, STP, ST2P, or ST3P
|
||||
*
|
||||
* \param chan channel on which to read digits
|
||||
* \param buf Buffer in which to store digits
|
||||
* \param buflen Size of buffer
|
||||
* \param timeout ms to wait for all digits before giving up
|
||||
* \param features Any additional DSP features to use
|
||||
* \param override Start over if we receive additional KPs
|
||||
* \param no_kp Don't include KP in the output
|
||||
* \param no_st Don't include start digits in the output
|
||||
* \param maxdigits If greater than 0, only read this many digits no matter what
|
||||
*
|
||||
* \retval 0 if successful
|
||||
* \retval -1 if unsuccessful (including hangup).
|
||||
*/
|
||||
static int read_mf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int features, int laxkp, int override, int no_kp, int no_st, int maxdigits) {
|
||||
struct ast_dsp *dsp;
|
||||
struct ast_frame *frame = NULL;
|
||||
struct timeval start;
|
||||
int remaining_time = timeout;
|
||||
int digits_read = 0;
|
||||
int is_start_digit = 0;
|
||||
char *str = buf;
|
||||
int res = 0;
|
||||
|
||||
if (!(dsp = ast_dsp_new())) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "ERROR");
|
||||
return -1;
|
||||
}
|
||||
ast_dsp_set_features(dsp, DSP_FEATURE_DIGIT_DETECT);
|
||||
ast_dsp_set_digitmode(dsp, DSP_DIGITMODE_MF | features);
|
||||
|
||||
start = ast_tvnow();
|
||||
*str = 0; /* start with empty output buffer */
|
||||
|
||||
/* based on app_read and generic_fax_exec from res_fax */
|
||||
while (timeout == 0 || remaining_time > 0) {
|
||||
if (timeout > 0) {
|
||||
remaining_time = ast_remaining_ms(start, timeout);
|
||||
if (remaining_time <= 0) {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "TIMEOUT");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((maxdigits && digits_read >= maxdigits) || digits_read >= (buflen - 1)) { /* we don't have room to store any more digits (very unlikely to happen for a legitimate reason) */
|
||||
/* This result will probably not be usable, so status should not be START */
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "MAXDIGITS");
|
||||
break;
|
||||
}
|
||||
/* ast_waitfordigit only waits for DTMF frames, we need to do DSP on voice frames */
|
||||
if (ast_waitfor(chan, 1000) > 0) {
|
||||
frame = ast_read(chan);
|
||||
if (!frame) {
|
||||
ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "HANGUP");
|
||||
break;
|
||||
} else if (frame->frametype == AST_FRAME_VOICE) {
|
||||
frame = ast_dsp_process(chan, dsp, frame);
|
||||
/* AST_FRAME_DTMF is used all over the DSP code for DTMF, MF, fax, etc.
|
||||
It's used because we can use the frame to store the digit detected.
|
||||
All this means is that we received something we care about. */
|
||||
if (frame->frametype == AST_FRAME_DTMF) {
|
||||
char result = frame->subclass.integer;
|
||||
if (digits_read == 0 && !laxkp && result != '*') {
|
||||
ast_debug(1, "Received MF digit, but no KP yet, ignoring: %c\n", result);
|
||||
ast_frfree(frame);
|
||||
continue;
|
||||
}
|
||||
ast_debug(1, "Received MF digit: %c\n", result);
|
||||
if (result == '*') {
|
||||
/* We received an additional KP, start over? */
|
||||
if (override && digits_read > 0) {
|
||||
ast_debug(1, "Received another KP, starting over\n");
|
||||
str = buf;
|
||||
*str = 0;
|
||||
digits_read = 1; /* we just detected a KP */
|
||||
} else {
|
||||
digits_read++;
|
||||
}
|
||||
/* if we were told not to include the KP digit in the output string, then skip it */
|
||||
if (no_kp) {
|
||||
ast_frfree(frame);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
digits_read++;
|
||||
}
|
||||
is_start_digit = (strchr("#", result) || strchr("A", result) || strchr("B", result) || strchr("C", result));
|
||||
/* if we were told not to include the ST digit in the output string, then skip it */
|
||||
if (!no_st || !is_start_digit) {
|
||||
*str++ = result; /* won't write past allotted memory, because of buffer check at top of loop */
|
||||
*str = 0;
|
||||
}
|
||||
/* we received a ST digit (ST, STP, ST2P, or ST3P), so we're done */
|
||||
if (is_start_digit) {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "START");
|
||||
ast_frfree(frame);
|
||||
break;
|
||||
}
|
||||
/* only free frame if it was a DSP match. The MF itself should not be muted. */
|
||||
ast_frfree(frame);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "HANGUP");
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
ast_dsp_free(dsp);
|
||||
ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int read_mf_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
#define BUFFER_SIZE 256
|
||||
char tmp[BUFFER_SIZE] = "";
|
||||
int to = 0;
|
||||
double tosec;
|
||||
struct ast_flags flags = {0};
|
||||
char *optargs[OPT_ARG_ARRAY_SIZE];
|
||||
char *argcopy = NULL;
|
||||
int res, features = 0, maxdigits = 0;
|
||||
|
||||
AST_DECLARE_APP_ARGS(arglist,
|
||||
AST_APP_ARG(variable);
|
||||
AST_APP_ARG(timeout);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "ReceiveMF requires an argument (variable)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(arglist, argcopy);
|
||||
|
||||
if (!ast_strlen_zero(arglist.options)) {
|
||||
ast_app_parse_options(read_app_options, &flags, optargs, arglist.options);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.timeout)) {
|
||||
tosec = atof(arglist.timeout);
|
||||
if (tosec <= 0) {
|
||||
to = 0;
|
||||
} else {
|
||||
to = tosec * 1000.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(arglist.variable)) {
|
||||
ast_log(LOG_WARNING, "Invalid! Usage: ReceiveMF(variable[,timeout][,option])\n");
|
||||
return -1;
|
||||
}
|
||||
if (ast_test_flag(&flags, OPT_MAXDIGITS) && !ast_strlen_zero(optargs[OPT_ARG_MAXDIGITS])) {
|
||||
maxdigits = atoi(optargs[OPT_ARG_MAXDIGITS]);
|
||||
if (maxdigits <= 0) {
|
||||
ast_log(LOG_WARNING, "Invalid maximum number of digits, ignoring: '%s'\n", optargs[OPT_ARG_MAXDIGITS]);
|
||||
maxdigits = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPT_DELAY)) {
|
||||
features |= DSP_DIGITMODE_MUTEMAX;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPT_MUTE)) {
|
||||
features |= DSP_DIGITMODE_MUTECONF;
|
||||
}
|
||||
|
||||
if (!ast_test_flag(&flags, OPT_QUELCH)) {
|
||||
features |= DSP_DIGITMODE_NOQUELCH;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPT_RELAXED)) {
|
||||
features |= DSP_DIGITMODE_RELAXDTMF;
|
||||
}
|
||||
|
||||
res = read_mf_digits(chan, tmp, BUFFER_SIZE, to, features, (ast_test_flag(&flags, OPT_LAX_KP)),
|
||||
(ast_test_flag(&flags, OPT_KP_OVERRIDE)), (ast_test_flag(&flags, OPT_NO_KP)), (ast_test_flag(&flags, OPT_NO_ST)), maxdigits);
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||
if (!ast_strlen_zero(tmp)) {
|
||||
ast_verb(3, "MF digits received: '%s'\n", tmp);
|
||||
} else if (!res) { /* if channel hung up, don't print anything out */
|
||||
ast_verb(3, "No MF digits received.\n");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int sendmf_exec(struct ast_channel *chan, const char *vdata)
|
||||
{
|
||||
int res;
|
||||
char *data;
|
||||
int dinterval = 0, duration = 0, durationkp = 0, durationst = 0;
|
||||
struct ast_channel *chan_found = NULL;
|
||||
struct ast_channel *chan_dest = chan;
|
||||
struct ast_channel *chan_autoservice = NULL;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(digits);
|
||||
AST_APP_ARG(dinterval);
|
||||
AST_APP_ARG(duration);
|
||||
AST_APP_ARG(durationkp);
|
||||
AST_APP_ARG(durationst);
|
||||
AST_APP_ARG(channel);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(vdata)) {
|
||||
ast_log(LOG_WARNING, "SendMF requires an argument\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = ast_strdupa(vdata);
|
||||
AST_STANDARD_APP_ARGS(args, data);
|
||||
|
||||
if (ast_strlen_zero(args.digits)) {
|
||||
ast_log(LOG_WARNING, "The digits argument is required (0-9,*#ABC,wf)\n");
|
||||
return 0;
|
||||
}
|
||||
if (!ast_strlen_zero(args.dinterval)) {
|
||||
ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
|
||||
}
|
||||
if (!ast_strlen_zero(args.duration)) {
|
||||
ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
|
||||
}
|
||||
if (!ast_strlen_zero(args.durationkp)) {
|
||||
ast_app_parse_timelen(args.durationkp, &durationkp, TIMELEN_MILLISECONDS);
|
||||
}
|
||||
if (!ast_strlen_zero(args.durationst)) {
|
||||
ast_app_parse_timelen(args.durationst, &durationst, TIMELEN_MILLISECONDS);
|
||||
}
|
||||
if (!ast_strlen_zero(args.channel)) {
|
||||
chan_found = ast_channel_get_by_name(args.channel);
|
||||
if (!chan_found) {
|
||||
ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
|
||||
return 0;
|
||||
}
|
||||
chan_dest = chan_found;
|
||||
if (chan_found != chan) {
|
||||
chan_autoservice = chan;
|
||||
}
|
||||
}
|
||||
res = ast_mf_stream(chan_dest, chan_autoservice, NULL, args.digits, dinterval <= 0 ? MF_BETWEEN_MS : dinterval,
|
||||
duration <= 0 ? MF_DURATION : duration, durationkp <= 0 ? MF_KP_DURATION : durationkp,
|
||||
durationst <= 0 ? MF_ST_DURATION : durationst, 0);
|
||||
ast_channel_cleanup(chan_found);
|
||||
|
||||
return chan_autoservice ? 0 : res;
|
||||
}
|
||||
|
||||
static int manager_play_mf(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *channel = astman_get_header(m, "Channel");
|
||||
const char *digit = astman_get_header(m, "Digit");
|
||||
const char *duration = astman_get_header(m, "Duration");
|
||||
struct ast_channel *chan;
|
||||
unsigned int duration_ms = MF_DURATION;
|
||||
|
||||
if (!(chan = ast_channel_get_by_name(channel))) {
|
||||
astman_send_error(s, m, "Channel not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(digit)) {
|
||||
astman_send_error(s, m, "No digit specified");
|
||||
chan = ast_channel_unref(chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Override default duration with KP or ST-specific default durations */
|
||||
if (!strcmp(digit, "*"))
|
||||
duration_ms = MF_KP_DURATION;
|
||||
|
||||
if (!strcmp(digit, "#") || !strcmp(digit, "A") || !strcmp(digit, "B") || !strcmp(digit, "C"))
|
||||
duration_ms = MF_ST_DURATION;
|
||||
|
||||
if (!ast_strlen_zero(duration) && (sscanf(duration, "%30u", &duration_ms) != 1)) {
|
||||
astman_send_error(s, m, "Could not convert Duration parameter");
|
||||
chan = ast_channel_unref(chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_mf_stream(chan, NULL, NULL, digit, 0, duration_ms, duration_ms, duration_ms, 1);
|
||||
|
||||
chan = ast_channel_unref(chan);
|
||||
|
||||
astman_send_ack(s, m, "MF successfully queued");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(readmf_name);
|
||||
res |= ast_unregister_application(sendmf_name);
|
||||
res |= ast_manager_unregister("PlayMF");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(readmf_name, read_mf_exec);
|
||||
res |= ast_register_application_xml(sendmf_name, sendmf_exec);
|
||||
res |= ast_manager_register_xml("PlayMF", EVENT_FLAG_CALL, manager_play_mf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "MF Sender and Receiver Applications");
|
|
@ -40,19 +40,29 @@
|
|||
/*** DOCUMENTATION
|
||||
<application name="Milliwatt" language="en_US">
|
||||
<synopsis>
|
||||
Generate a Constant 1004Hz tone at 0dbm (mu-law).
|
||||
Generates a 1004 Hz test tone at 0dbm (mu-law).
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="m">
|
||||
<para>Generate a 1004 Hz Milliwatt test tone at 0dbm, with a
|
||||
1 second silent interval. This option must be specified
|
||||
if you are using this for a milliwatt test line.</para>
|
||||
</option>
|
||||
<option name="o">
|
||||
<para>Generate the tone at 1000Hz like previous version.</para>
|
||||
<para>Generate a constant tone at 1000 Hz like previous version.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Previous versions of this application generated the tone at 1000Hz. If for
|
||||
<para>Generates a 1004 Hz test tone.</para>
|
||||
<para>By default, this application does not provide a Milliwatt test tone. It simply
|
||||
plays a 1004 Hz tone, which is not suitable for performing a milliwatt test.
|
||||
The <literal>m</literal> option should be used so that a real Milliwatt test tone
|
||||
is provided. This will include a 1 second silent interval every 10 seconds.</para>
|
||||
<para>Previous versions of this application generated a constant tone at 1000 Hz. If for
|
||||
some reason you would prefer that behavior, supply the <literal>o</literal> option to get the
|
||||
old behavior.</para>
|
||||
</description>
|
||||
|
@ -155,8 +165,11 @@ static int milliwatt_exec(struct ast_channel *chan, const char *data)
|
|||
if (!ast_strlen_zero(options) && strchr(options, 'o')) {
|
||||
return old_milliwatt_exec(chan);
|
||||
}
|
||||
|
||||
res = ast_playtones_start(chan, 23255, "1004/1000", 0);
|
||||
if (!ast_strlen_zero(options) && strchr(options, 'm')) {
|
||||
res = ast_playtones_start(chan, 23255, "1004/9000,0/1000", 0);
|
||||
} else {
|
||||
res = ast_playtones_start(chan, 23255, "1004/1000", 0);
|
||||
}
|
||||
|
||||
while (!res) {
|
||||
res = ast_safe_sleep(chan, 10000);
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* based on the Comedian Mail voicemail system (app_voicemail.c).
|
||||
*
|
||||
* \par See also
|
||||
* \arg \ref Config_minivm_examples
|
||||
* \arg \ref minivm.conf "Config_minivm"
|
||||
* \arg \ref App_minivm
|
||||
*
|
||||
* \ingroup applications
|
||||
|
@ -78,16 +78,16 @@
|
|||
* - English, GB en_gb
|
||||
*
|
||||
* \par See also
|
||||
* \arg \ref Config_minivm
|
||||
* \arg \ref minivm.conf "Config_minivm"
|
||||
* \arg \ref Config_minivm_examples
|
||||
* \arg \ref Minivm_directories
|
||||
* \arg \ref app_minivm.c
|
||||
* \arg Comedian mail: app_voicemail.c
|
||||
* \arg \ref descrip_minivm_accmess
|
||||
* \arg \ref descrip_minivm_greet
|
||||
* \arg \ref descrip_minivm_record
|
||||
* \arg \ref descrip_minivm_delete
|
||||
* \arg \ref descrip_minivm_notify
|
||||
* \arg \ref minivm_accmess_exec
|
||||
* \arg \ref minivm_greet_exec
|
||||
* \arg \ref minivm_record_exec
|
||||
* \arg \ref minivm_delete_exec
|
||||
* \arg \ref minivm_notify_exec
|
||||
*
|
||||
* \arg \ref App_minivm_todo
|
||||
*/
|
||||
|
@ -113,6 +113,13 @@
|
|||
* Back: \ref App_minivm
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \page minivm.conf minivm.conf
|
||||
* \verbinclude minivm.conf.sample
|
||||
*
|
||||
* Back: \ref App_minivm
|
||||
*/
|
||||
|
||||
/*! \page Config_minivm_examples Example dialplan for Mini-Voicemail
|
||||
* \section Example dialplan scripts for Mini-Voicemail
|
||||
* \verbinclude extensions_minivm.conf.sample
|
||||
|
@ -154,7 +161,6 @@
|
|||
#include <dirent.h>
|
||||
#include <locale.h>
|
||||
|
||||
|
||||
#include "asterisk/paths.h" /* use various paths */
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
|
@ -535,8 +541,6 @@
|
|||
#define SENDMAIL "/usr/sbin/sendmail -t"
|
||||
|
||||
#define SOUND_INTRO "vm-intro"
|
||||
#define B64_BASEMAXINLINE 256 /*!< Buffer size for Base 64 attachment encoding */
|
||||
#define B64_BASELINELEN 72 /*!< Line length for Base 64 endoded messages */
|
||||
#define EOL "\r\n"
|
||||
|
||||
#define MAX_DATETIME_FORMAT 512
|
||||
|
@ -652,15 +656,6 @@ struct leave_vm_options {
|
|||
signed char record_gain;
|
||||
};
|
||||
|
||||
/*! \brief Structure for base64 encoding */
|
||||
struct b64_baseio {
|
||||
int iocp;
|
||||
int iolen;
|
||||
int linelength;
|
||||
int ateof;
|
||||
unsigned char iobuf[B64_BASEMAXINLINE];
|
||||
};
|
||||
|
||||
/*! \brief Voicemail time zones */
|
||||
struct minivm_zone {
|
||||
char name[80]; /*!< Name of this time zone */
|
||||
|
@ -846,134 +841,6 @@ static void message_destroy_list(void)
|
|||
AST_LIST_UNLOCK(&message_templates);
|
||||
}
|
||||
|
||||
/*!\internal
|
||||
* \brief read buffer from file (base64 conversion) */
|
||||
static int b64_inbuf(struct b64_baseio *bio, FILE *fi)
|
||||
{
|
||||
int l;
|
||||
|
||||
if (bio->ateof)
|
||||
return 0;
|
||||
|
||||
if ((l = fread(bio->iobuf, 1, B64_BASEMAXINLINE, fi)) != B64_BASEMAXINLINE) {
|
||||
bio->ateof = 1;
|
||||
if (l == 0) {
|
||||
/* Assume EOF */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bio->iolen = l;
|
||||
bio->iocp = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*!\internal
|
||||
* \brief read character from file to buffer (base64 conversion) */
|
||||
static int b64_inchar(struct b64_baseio *bio, FILE *fi)
|
||||
{
|
||||
if (bio->iocp >= bio->iolen) {
|
||||
if (!b64_inbuf(bio, fi))
|
||||
return EOF;
|
||||
}
|
||||
|
||||
return bio->iobuf[bio->iocp++];
|
||||
}
|
||||
|
||||
/*!\internal
|
||||
* \brief write buffer to file (base64 conversion) */
|
||||
static int b64_ochar(struct b64_baseio *bio, int c, FILE *so)
|
||||
{
|
||||
if (bio->linelength >= B64_BASELINELEN) {
|
||||
if (fputs(EOL,so) == EOF)
|
||||
return -1;
|
||||
|
||||
bio->linelength= 0;
|
||||
}
|
||||
|
||||
if (putc(((unsigned char) c), so) == EOF)
|
||||
return -1;
|
||||
|
||||
bio->linelength++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*!\internal
|
||||
* \brief Encode file to base64 encoding for email attachment (base64 conversion) */
|
||||
static int base_encode(char *filename, FILE *so)
|
||||
{
|
||||
unsigned char dtable[B64_BASEMAXINLINE];
|
||||
int i,hiteof= 0;
|
||||
FILE *fi;
|
||||
struct b64_baseio bio;
|
||||
|
||||
memset(&bio, 0, sizeof(bio));
|
||||
bio.iocp = B64_BASEMAXINLINE;
|
||||
|
||||
if (!(fi = fopen(filename, "rb"))) {
|
||||
ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i= 0; i<9; i++) {
|
||||
dtable[i]= 'A'+i;
|
||||
dtable[i+9]= 'J'+i;
|
||||
dtable[26+i]= 'a'+i;
|
||||
dtable[26+i+9]= 'j'+i;
|
||||
}
|
||||
for (i= 0; i < 8; i++) {
|
||||
dtable[i+18]= 'S'+i;
|
||||
dtable[26+i+18]= 's'+i;
|
||||
}
|
||||
for (i= 0; i < 10; i++) {
|
||||
dtable[52+i]= '0'+i;
|
||||
}
|
||||
dtable[62]= '+';
|
||||
dtable[63]= '/';
|
||||
|
||||
while (!hiteof){
|
||||
unsigned char igroup[3], ogroup[4];
|
||||
int c,n;
|
||||
|
||||
igroup[0]= igroup[1]= igroup[2]= 0;
|
||||
|
||||
for (n= 0; n < 3; n++) {
|
||||
if ((c = b64_inchar(&bio, fi)) == EOF) {
|
||||
hiteof= 1;
|
||||
break;
|
||||
}
|
||||
igroup[n]= (unsigned char)c;
|
||||
}
|
||||
|
||||
if (n> 0) {
|
||||
ogroup[0]= dtable[igroup[0]>>2];
|
||||
ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
|
||||
ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
|
||||
ogroup[3]= dtable[igroup[2]&0x3F];
|
||||
|
||||
if (n<3) {
|
||||
ogroup[3]= '=';
|
||||
|
||||
if (n<2)
|
||||
ogroup[2]= '=';
|
||||
}
|
||||
|
||||
for (i= 0;i<4;i++)
|
||||
b64_ochar(&bio, ogroup[i], so);
|
||||
}
|
||||
}
|
||||
|
||||
/* Put end of line - line feed */
|
||||
if (fputs(EOL, so) == EOF)
|
||||
return 0;
|
||||
|
||||
fclose(fi);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_date(char *s, int len)
|
||||
{
|
||||
struct ast_tm tm;
|
||||
|
@ -1474,7 +1341,7 @@ static int sendmail(struct minivm_template *template, struct minivm_account *vmu
|
|||
fprintf(p, "Content-Description: Voicemail sound attachment.\n");
|
||||
fprintf(p, "Content-Disposition: attachment; filename=\"voicemail%s.%s\"\n\n", counter ? counter : "", format);
|
||||
|
||||
base_encode(fname, p);
|
||||
ast_base64_encode_file_path(fname, p, EOL);
|
||||
fprintf(p, "\n\n--%s--\n.\n", bound);
|
||||
}
|
||||
fclose(p);
|
||||
|
@ -1737,7 +1604,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
|
|||
}
|
||||
return cmd;
|
||||
default:
|
||||
/* If the caller is an ouside caller, and the review option is enabled,
|
||||
/* If the caller is an outside caller, and the review option is enabled,
|
||||
allow them to review the message, but let the owner of the box review
|
||||
their OGM's */
|
||||
if (outsidecaller && !ast_test_flag(vmu, MVM_REVIEW))
|
||||
|
@ -2827,7 +2694,7 @@ static int apply_general_options(struct ast_variable *var)
|
|||
} else if (!strcmp(var->name, "externnotify")) {
|
||||
/* External voicemail notify application */
|
||||
ast_copy_string(global_externnotify, var->value, sizeof(global_externnotify));
|
||||
} else if (!strcmp(var->name, "silencetreshold")) {
|
||||
} else if (!strcmp(var->name, "silencethreshold") || !strcmp(var->name, "silencetreshold")) {
|
||||
/* Silence treshold */
|
||||
global_silencethreshold = atoi(var->value);
|
||||
} else if (!strcmp(var->name, "maxmessage")) {
|
||||
|
@ -2956,6 +2823,8 @@ static int load_config(int reload)
|
|||
ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
|
||||
if ((chanvar = ast_variable_retrieve(cfg, "general", "emailaaddress")))
|
||||
ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
|
||||
if ((chanvar = ast_variable_retrieve(cfg, "general", "emailaddress")))
|
||||
ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
|
||||
if ((chanvar = ast_variable_retrieve(cfg, "general", "emailcharset")))
|
||||
ast_copy_string(template->charset, chanvar, sizeof(template->charset));
|
||||
if ((chanvar = ast_variable_retrieve(cfg, "general", "emailsubject")))
|
||||
|
|
|
@ -631,6 +631,7 @@ static void mixmonitor_free(struct mixmonitor *mixmonitor)
|
|||
* \brief Copies the mixmonitor to all voicemail recipients
|
||||
* \param mixmonitor The mixmonitor that needs to forward its file to recipients
|
||||
* \param ext Format of the file that was saved
|
||||
* \param filename
|
||||
*/
|
||||
static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
|
||||
{
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
*
|
||||
* Copyright (c) 2006, Tilghman Lesher. All rights reserved.
|
||||
*
|
||||
* Updated by Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* Tilghman Lesher <app_morsecode__v001@the-tilghman.com>
|
||||
*
|
||||
* This code is released by the author with no restrictions on usage.
|
||||
|
@ -20,6 +22,7 @@
|
|||
* \brief Morsecode application
|
||||
*
|
||||
* \author Tilghman Lesher <app_morsecode__v001@the-tilghman.com>
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
@ -58,6 +61,14 @@
|
|||
<variable name="MORSETONE">
|
||||
<para>The pitch of the tone in (Hz), default is 800</para>
|
||||
</variable>
|
||||
<variable name="MORSESPACETONE">
|
||||
<para>The pitch of the spaces in (Hz), default is 0</para>
|
||||
</variable>
|
||||
<variable name="MORSETYPE">
|
||||
<para>The code type to use (AMERICAN for standard American Morse
|
||||
or INTERNATIONAL for international code.
|
||||
Default is INTERNATIONAL).</para>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
|
@ -68,7 +79,7 @@
|
|||
***/
|
||||
static const char app_morsecode[] = "Morsecode";
|
||||
|
||||
static const char * const morsecode[] = {
|
||||
static const char * const internationalcode[] = {
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 0-15 */
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 16-31 */
|
||||
" ", /* 32 - <space> */
|
||||
|
@ -95,16 +106,16 @@ static const char * const morsecode[] = {
|
|||
"", /* 62 - > */
|
||||
"..--..", /* 63 - ? */
|
||||
".--.-.", /* 64 - @ */
|
||||
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--",
|
||||
"-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
|
||||
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", /* A-M */
|
||||
"-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", /* N-Z */
|
||||
"-.--.-", /* 91 - [ (really '(') */
|
||||
"-..-.", /* 92 - \ (really '/') */
|
||||
"-.--.-", /* 93 - ] (really ')') */
|
||||
"", /* 94 - ^ */
|
||||
"..--.-", /* 95 - _ */
|
||||
".----.", /* 96 - ` */
|
||||
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--",
|
||||
"-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..",
|
||||
".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", /* a-m */
|
||||
"-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", /* n-z */
|
||||
"-.--.-", /* 123 - { (really '(') */
|
||||
"", /* 124 - | */
|
||||
"-.--.-", /* 125 - } (really ')') */
|
||||
|
@ -112,63 +123,159 @@ static const char * const morsecode[] = {
|
|||
". . .", /* 127 - <del> (error) */
|
||||
};
|
||||
|
||||
static void playtone(struct ast_channel *chan, int tone, int len)
|
||||
static const char * const americanmorsecode[] = {
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 0-15 */
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", /* 16-31 */
|
||||
" ", /* 32 - <space> */
|
||||
"---.", /* 33 - ! */
|
||||
"..-. -.",/* 34 - " (QN)*/
|
||||
"", /* 35 - # */
|
||||
"... .-..",/* 36 - $ (SX) */
|
||||
"", /* 37 - % */
|
||||
". ...", /* 38 - & (ES) */
|
||||
"..-. .-..",/* 39 - ' (QX) */
|
||||
"..... -.", /* 40 - ( (PN) */
|
||||
"..... .. ..", /* 41 - ) (PY) */
|
||||
"", /* 42 - * */
|
||||
"", /* 43 - + */
|
||||
".-.-", /* 44 - , */
|
||||
".... .-..",/* 45 - (HX) */
|
||||
"..--..", /* 46 - . */
|
||||
"..- -", /* 47 - / (UT) */
|
||||
".--.", "..-..", "...-.", "....-", "---", "......", "--..", "-....", "-..-", "0", /* 48-57 - 0-9 */
|
||||
"-.- . .",/* 58 - : (KO) */
|
||||
"... ..", /* 59 - ; */
|
||||
"", /* 60 - < */
|
||||
"-...-", /* 61 - = (paragraph mark) */
|
||||
"", /* 62 - > */
|
||||
"-..-.", /* 63 - ? */
|
||||
".--.-.", /* 64 - @ */
|
||||
".-", "-...", ".. .", "-..", ".", ".-.", "--.", "....", "..", ".-.-", "-.-", "L", "--", /* A-M */
|
||||
"-.", ". .", ".....", "..-.", ". ..", "...", "-", "..-", "...-", ".--", ".-..", ".. ..", "... .", /* N-Z */
|
||||
"..... -.", /* 91 - [ (really '(') */
|
||||
"..- -", /* 92 - \ (really '/') */
|
||||
"..... .. ..", /* 93 - ] (really ')') */
|
||||
"", /* 94 - ^ */
|
||||
"..--.-", /* 95 - _ */
|
||||
".----.", /* 96 - ` */
|
||||
".-", "-...", ".. .", "-..", ".", ".-.", "--.", "....", "..", ".-.-", "-.-", "L", "--", /* a-m */
|
||||
"-.", ". .", ".....", "..-.", ". ..", "...", "-", "..-", "...-", ".--", ".-..", ".. ..", "... .", /* n-z */
|
||||
"..... -.", /* 123 - { (really '(') */
|
||||
"", /* 124 - | */
|
||||
"..... .. ..", /* 125 - } (really ')') */
|
||||
"..- -", /* 126 - ~ (really bar) */
|
||||
". . .", /* 127 - <del> (error) */
|
||||
};
|
||||
|
||||
static int playtone(struct ast_channel *chan, int tone, int len)
|
||||
{
|
||||
int res;
|
||||
char dtmf[20];
|
||||
snprintf(dtmf, sizeof(dtmf), "%d/%d", tone, len);
|
||||
ast_playtones_start(chan, 0, dtmf, 0);
|
||||
ast_safe_sleep(chan, len);
|
||||
res = ast_safe_sleep(chan, len);
|
||||
ast_playtones_stop(chan);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int morsecode_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
int res=0, ditlen, tone;
|
||||
int res = 0, ditlen, tone, toneoff, digit2;
|
||||
const char *digit;
|
||||
const char *ditlenc, *tonec;
|
||||
const char *ditlenc, *tonec, *toneb, *codetype;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Syntax: Morsecode(<string>) - no argument found\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use variable MORESEDITLEN, if set (else 80) */
|
||||
ast_channel_lock(chan);
|
||||
/* Use variable MORESEDITLEN, if set (else 80) */
|
||||
ditlenc = pbx_builtin_getvar_helper(chan, "MORSEDITLEN");
|
||||
if (ast_strlen_zero(ditlenc) || (sscanf(ditlenc, "%30d", &ditlen) != 1)) {
|
||||
ditlen = 80;
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
/* Use variable MORSETONE, if set (else 800) */
|
||||
ast_channel_lock(chan);
|
||||
tonec = pbx_builtin_getvar_helper(chan, "MORSETONE");
|
||||
if (ast_strlen_zero(tonec) || (sscanf(tonec, "%30d", &tone) != 1)) {
|
||||
tone = 800;
|
||||
}
|
||||
|
||||
/* Use variable MORSESPACETONE, if set (else 0) */
|
||||
toneb = pbx_builtin_getvar_helper(chan, "MORSESPACETONE");
|
||||
if (ast_strlen_zero(toneb) || (sscanf(toneb, "%30d", &toneoff) != 1)) {
|
||||
toneoff = 0;
|
||||
}
|
||||
|
||||
/* Use variable MORSETYPE, if set (else INTERNATIONAL) */
|
||||
codetype = pbx_builtin_getvar_helper(chan, "MORSETYPE");
|
||||
if (!codetype || strcmp(codetype, "AMERICAN")) {
|
||||
codetype = "INTERNATIONAL";
|
||||
}
|
||||
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
for (digit = data; *digit; digit++) {
|
||||
int digit2 = *digit;
|
||||
const char *dahdit;
|
||||
if (digit2 < 0) {
|
||||
continue;
|
||||
}
|
||||
for (dahdit = morsecode[digit2]; *dahdit; dahdit++) {
|
||||
if (*dahdit == '-') {
|
||||
playtone(chan, tone, 3 * ditlen);
|
||||
} else if (*dahdit == '.') {
|
||||
playtone(chan, tone, 1 * ditlen);
|
||||
} else {
|
||||
/* Account for ditlen of silence immediately following */
|
||||
playtone(chan, 0, 2 * ditlen);
|
||||
if (!strcmp(codetype, "AMERICAN")) {
|
||||
for (digit = data; *digit; digit++) {
|
||||
const char *dahdit;
|
||||
digit2 = *digit;
|
||||
if (digit2 < 0 || digit2 > 127) {
|
||||
continue;
|
||||
}
|
||||
for (dahdit = americanmorsecode[digit2]; *dahdit; dahdit++) {
|
||||
if (*dahdit == '-') {
|
||||
res = playtone(chan, tone, 3 * ditlen);
|
||||
} else if (*dahdit == '.') {
|
||||
res = playtone(chan, tone, 1 * ditlen);
|
||||
} else if (*dahdit == 'L' || *dahdit == 'l') {
|
||||
res = playtone(chan, tone, 6 * ditlen); /* long dash */
|
||||
} else if (*dahdit == '0') {
|
||||
res = playtone(chan, tone, 9 * ditlen); /* extra long dash */
|
||||
} else if (*dahdit == ' ') { /* space char (x20) = 6 dot lengths */
|
||||
/* Intra-char pauses, specific to American Morse */
|
||||
res = playtone(chan, toneoff, 3 * ditlen);
|
||||
} else {
|
||||
/* Account for ditlen of silence immediately following */
|
||||
res = playtone(chan, toneoff, 2 * ditlen);
|
||||
}
|
||||
|
||||
/* Pause slightly between each dit and dah */
|
||||
playtone(chan, 0, 1 * ditlen);
|
||||
/* Pause slightly between each dit and dah */
|
||||
res = playtone(chan, toneoff, 1 * ditlen);
|
||||
if (res)
|
||||
break;
|
||||
}
|
||||
/* Pause between characters */
|
||||
res = playtone(chan, toneoff, 3 * ditlen);
|
||||
if (res)
|
||||
break;
|
||||
}
|
||||
} else { /* International */
|
||||
for (digit = data; *digit; digit++) {
|
||||
const char *dahdit;
|
||||
digit2 = *digit;
|
||||
if (digit2 < 0 || digit2 > 127) {
|
||||
continue;
|
||||
}
|
||||
for (dahdit = internationalcode[digit2]; *dahdit; dahdit++) {
|
||||
if (*dahdit == '-') {
|
||||
res = playtone(chan, tone, 3 * ditlen);
|
||||
} else if (*dahdit == '.') {
|
||||
res = playtone(chan, tone, 1 * ditlen);
|
||||
} else {
|
||||
/* Account for ditlen of silence immediately following */
|
||||
res = playtone(chan, toneoff, 2 * ditlen);
|
||||
}
|
||||
|
||||
/* Pause slightly between each dit and dah */
|
||||
res = playtone(chan, toneoff, 1 * ditlen);
|
||||
if (res)
|
||||
break;
|
||||
}
|
||||
/* Pause between characters */
|
||||
res = playtone(chan, toneoff, 2 * ditlen);
|
||||
if (res)
|
||||
break;
|
||||
}
|
||||
/* Pause between characters */
|
||||
playtone(chan, 0, 2 * ditlen);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
|
|
@ -64,10 +64,13 @@
|
|||
</syntax>
|
||||
<description>
|
||||
<para>Executes mpg123 to play the given location, which typically would be a mp3 filename
|
||||
or m3u playlist filename or a URL. Please read http://en.wikipedia.org/wiki/M3U
|
||||
to see how M3U playlist file format is like, Example usage would be
|
||||
or m3u playlist filename or a URL. Please read https://en.wikipedia.org/wiki/M3U
|
||||
to see what the M3U playlist file format is like.</para>
|
||||
<para>Note that mpg123 does not support HTTPS, so use HTTP for web streams.</para>
|
||||
<para>User can exit by pressing any key on the dialpad, or by hanging up.</para>
|
||||
<example title="Play an MP3 playlist">
|
||||
exten => 1234,1,MP3Player(/var/lib/asterisk/playlist.m3u)
|
||||
User can exit by pressing any key on the dialpad, or by hanging up.</para>
|
||||
</example>
|
||||
<para>This application does not automatically answer and should be preceeded by an
|
||||
application such as Answer() or Progress().</para>
|
||||
</description>
|
||||
|
@ -98,39 +101,39 @@ static int mp3play(const char *filename, unsigned int sampling_rate, int fd)
|
|||
/* Execute mpg123, but buffer if it's a net connection */
|
||||
if (!strncasecmp(filename, "http://", 7) && strstr(filename, ".m3u")) {
|
||||
char buffer_size_str[8];
|
||||
snprintf(buffer_size_str, 8, "%u", (int) 0.5*2*sampling_rate/1000); // 0.5 seconds for a live stream
|
||||
snprintf(buffer_size_str, 8, "%u", (int) 0.5*2*sampling_rate/1000); /* 0.5 seconds for a live stream */
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
/* But many places has it in /usr/bin */
|
||||
execl(MPG_123, "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
execl(MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
/* As a last-ditch effort, try to use PATH */
|
||||
execlp("mpg123", "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
execlp("mpg123", "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
}
|
||||
else if (!strncasecmp(filename, "http://", 7)) {
|
||||
char buffer_size_str[8];
|
||||
snprintf(buffer_size_str, 8, "%u", 6*2*sampling_rate/1000); // 6 seconds for a remote MP3 file
|
||||
snprintf(buffer_size_str, 8, "%u", 6*2*sampling_rate/1000); /* 6 seconds for a remote MP3 file */
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
/* But many places has it in /usr/bin */
|
||||
execl(MPG_123, "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
execl(MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
/* As a last-ditch effort, try to use PATH */
|
||||
execlp("mpg123", "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
execlp("mpg123", "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
}
|
||||
else if (strstr(filename, ".m3u")) {
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(LOCAL_MPG_123, "mpg123", "-q", "-z", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-z", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
/* But many places has it in /usr/bin */
|
||||
execl(MPG_123, "mpg123", "-q", "-z", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
execl(MPG_123, "mpg123", "-e", "s16", "-q", "-z", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
/* As a last-ditch effort, try to use PATH */
|
||||
execlp("mpg123", "mpg123", "-q", "-z", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
execlp("mpg123", "mpg123", "-e", "s16", "-q", "-z", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||
}
|
||||
else {
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
execl(MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
/* But many places has it in /usr/bin */
|
||||
execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
/* As a last-ditch effort, try to use PATH */
|
||||
execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
execlp("mpg123", "mpg123", "-e", "s16", "-q", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||
}
|
||||
/* Can't use ast_log since FD's are closed */
|
||||
fprintf(stderr, "Execute of mpg123 failed\n");
|
||||
|
@ -177,6 +180,7 @@ static int mp3_exec(struct ast_channel *chan, const char *data)
|
|||
int pid = -1;
|
||||
RAII_VAR(struct ast_format *, owriteformat, NULL, ao2_cleanup);
|
||||
int timeout = 2;
|
||||
int startedmp3 = 0;
|
||||
struct timeval next;
|
||||
struct ast_frame *f;
|
||||
struct myframe {
|
||||
|
@ -240,12 +244,19 @@ static int mp3_exec(struct ast_channel *chan, const char *data)
|
|||
if (res > 0) {
|
||||
myf.f.datalen = res;
|
||||
myf.f.samples = res / 2;
|
||||
startedmp3 = 1;
|
||||
if (ast_write(chan, &myf.f) < 0) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ast_debug(1, "No more mp3\n");
|
||||
if (!startedmp3) { /* we couldn't do anything, which means this stream doesn't work */
|
||||
if (!strncasecmp(data, "https://", 8)) {
|
||||
ast_log(LOG_WARNING, "%s() does not support HTTPS streams. Use HTTP instead.\n", app);
|
||||
}
|
||||
ast_log(LOG_WARNING, "MP3 stream '%s' is broken or nonexistent\n", data);
|
||||
}
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Silly application to play an NBScat file -- uses nbscat8k
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>deprecated</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/frame.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/translate.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/format_cache.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="NBScat" language="en_US">
|
||||
<synopsis>
|
||||
Play an NBS local stream.
|
||||
</synopsis>
|
||||
<syntax />
|
||||
<description>
|
||||
<para>Executes nbscat to listen to the local NBS stream.
|
||||
User can exit by pressing any key.</para>
|
||||
</description>
|
||||
</application>
|
||||
***/
|
||||
|
||||
#define LOCAL_NBSCAT "/usr/local/bin/nbscat8k"
|
||||
#define NBSCAT "/usr/bin/nbscat8k"
|
||||
|
||||
#ifndef AF_LOCAL
|
||||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
|
||||
static char *app = "NBScat";
|
||||
|
||||
static int NBScatplay(int fd)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_safe_fork(0);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Fork failed\n");
|
||||
}
|
||||
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (ast_opt_high_priority)
|
||||
ast_set_priority(0);
|
||||
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
ast_close_fds_above_n(STDERR_FILENO);
|
||||
/* Most commonly installed in /usr/local/bin */
|
||||
execl(NBSCAT, "nbscat8k", "-d", (char *)NULL);
|
||||
execl(LOCAL_NBSCAT, "nbscat8k", "-d", (char *)NULL);
|
||||
fprintf(stderr, "Execute of nbscat8k failed\n");
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
static int timed_read(int fd, void *data, int datalen)
|
||||
{
|
||||
int res;
|
||||
struct pollfd fds[1];
|
||||
fds[0].fd = fd;
|
||||
fds[0].events = POLLIN;
|
||||
res = ast_poll(fds, 1, 2000);
|
||||
if (res < 1) {
|
||||
ast_log(LOG_NOTICE, "Selected timed out/errored out with %d\n", res);
|
||||
return -1;
|
||||
}
|
||||
return read(fd, data, datalen);
|
||||
|
||||
}
|
||||
|
||||
static int NBScat_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
int res=0;
|
||||
int fds[2];
|
||||
int ms = -1;
|
||||
int pid = -1;
|
||||
struct ast_format *owriteformat;
|
||||
struct timeval next;
|
||||
struct ast_frame *f;
|
||||
struct myframe {
|
||||
struct ast_frame f;
|
||||
char offset[AST_FRIENDLY_OFFSET];
|
||||
short frdata[160];
|
||||
} myf;
|
||||
|
||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds)) {
|
||||
ast_log(LOG_WARNING, "Unable to create socketpair\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_stopstream(chan);
|
||||
|
||||
owriteformat = ao2_bump(ast_channel_writeformat(chan));
|
||||
res = ast_set_write_format(chan, ast_format_slin);
|
||||
if (res < 0) {
|
||||
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
|
||||
ao2_cleanup(owriteformat);
|
||||
return -1;
|
||||
}
|
||||
|
||||
myf.f.frametype = AST_FRAME_VOICE;
|
||||
myf.f.subclass.format = ast_format_slin;
|
||||
myf.f.mallocd = 0;
|
||||
myf.f.offset = AST_FRIENDLY_OFFSET;
|
||||
myf.f.src = __PRETTY_FUNCTION__;
|
||||
myf.f.delivery.tv_sec = 0;
|
||||
myf.f.delivery.tv_usec = 0;
|
||||
myf.f.data.ptr = myf.frdata;
|
||||
|
||||
res = NBScatplay(fds[1]);
|
||||
/* Wait 1000 ms first */
|
||||
next = ast_tvnow();
|
||||
next.tv_sec += 1;
|
||||
if (res >= 0) {
|
||||
pid = res;
|
||||
/* Order is important -- there's almost always going to be mp3... we want to prioritize the
|
||||
user */
|
||||
for (;;) {
|
||||
ms = ast_tvdiff_ms(next, ast_tvnow());
|
||||
if (ms <= 0) {
|
||||
res = timed_read(fds[0], myf.frdata, sizeof(myf.frdata));
|
||||
if (res > 0) {
|
||||
myf.f.datalen = res;
|
||||
myf.f.samples = res / 2;
|
||||
if (ast_write(chan, &myf.f) < 0) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ast_debug(1, "No more mp3\n");
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
|
||||
} else {
|
||||
ms = ast_waitfor(chan, ms);
|
||||
if (ms < 0) {
|
||||
ast_debug(1, "Hangup detected\n");
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if (ms) {
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
ast_debug(1, "Null frame == hangup() detected\n");
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if (f->frametype == AST_FRAME_DTMF) {
|
||||
ast_debug(1, "User pressed a key\n");
|
||||
ast_frfree(f);
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
ast_frfree(&myf.f);
|
||||
|
||||
if (pid > -1)
|
||||
kill(pid, SIGKILL);
|
||||
if (!res && owriteformat)
|
||||
ast_set_write_format(chan, owriteformat);
|
||||
ao2_cleanup(owriteformat);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app, NBScat_exec);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_DEPRECATED(ASTERISK_GPL_KEY, "Silly NBS Stream Application");
|
|
@ -95,6 +95,10 @@ static const char app_originate[] = "Originate";
|
|||
<argument name="argN" />
|
||||
</argument>
|
||||
</option>
|
||||
<option name="C">
|
||||
<para>Comma-separated list of codecs to use for this call.
|
||||
Default is <literal>slin</literal>.</para>
|
||||
</option>
|
||||
<option name="c">
|
||||
<para>The caller ID number to use for the called channel. Default is
|
||||
the current channel's Caller ID number.</para>
|
||||
|
@ -114,7 +118,7 @@ static const char app_originate[] = "Originate";
|
|||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This application originates an outbound call and connects it to a specified extension or application. This application will block until the outgoing call fails or gets answered. At that point, this application will exit with the status variable set and dialplan processing will continue.</para>
|
||||
<para>This application originates an outbound call and connects it to a specified extension or application. This application will block until the outgoing call fails or gets answered, unless the async option is used. At that point, this application will exit with the status variable set and dialplan processing will continue.</para>
|
||||
<para>This application sets the following channel variable before exiting:</para>
|
||||
<variablelist>
|
||||
<variable name="ORIGINATE_STATUS">
|
||||
|
@ -141,7 +145,8 @@ enum {
|
|||
OPT_ASYNC = (1 << 2),
|
||||
OPT_CALLER_NUM = (1 << 3),
|
||||
OPT_CALLER_NAME = (1 << 4),
|
||||
OPT_VARIABLES = (1 << 5),
|
||||
OPT_CODECS = (1 << 5),
|
||||
OPT_VARIABLES = (1 << 6),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -149,6 +154,7 @@ enum {
|
|||
OPT_ARG_PREDIAL_CALLER,
|
||||
OPT_ARG_CALLER_NUM,
|
||||
OPT_ARG_CALLER_NAME,
|
||||
OPT_ARG_CODECS,
|
||||
OPT_ARG_VARIABLES,
|
||||
/* note: this entry _MUST_ be the last one in the enum */
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
|
@ -158,6 +164,7 @@ AST_APP_OPTIONS(originate_exec_options, BEGIN_OPTIONS
|
|||
AST_APP_OPTION('a', OPT_ASYNC),
|
||||
AST_APP_OPTION_ARG('b', OPT_PREDIAL_CALLEE, OPT_ARG_PREDIAL_CALLEE),
|
||||
AST_APP_OPTION_ARG('B', OPT_PREDIAL_CALLER, OPT_ARG_PREDIAL_CALLER),
|
||||
AST_APP_OPTION_ARG('C', OPT_CODECS, OPT_ARG_CODECS),
|
||||
AST_APP_OPTION_ARG('c', OPT_CALLER_NUM, OPT_ARG_CALLER_NUM),
|
||||
AST_APP_OPTION_ARG('n', OPT_CALLER_NAME, OPT_ARG_CALLER_NAME),
|
||||
AST_APP_OPTION_ARG('v', OPT_VARIABLES, OPT_ARG_VARIABLES),
|
||||
|
@ -178,7 +185,7 @@ static int originate_exec(struct ast_channel *chan, const char *data)
|
|||
char *opt_args[OPT_ARG_ARRAY_SIZE];
|
||||
char *predial_callee = NULL;
|
||||
char *parse, *cnum = NULL, *cname = NULL;
|
||||
|
||||
|
||||
struct ast_variable *vars = NULL;
|
||||
char *chantech, *chandata;
|
||||
int res = -1;
|
||||
|
@ -186,22 +193,15 @@ static int originate_exec(struct ast_channel *chan, const char *data)
|
|||
int outgoing_status = 0;
|
||||
unsigned int timeout = 30;
|
||||
static const char default_exten[] = "s";
|
||||
struct ast_format_cap *cap_slin = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
|
||||
struct ast_format_cap *capabilities;
|
||||
capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
|
||||
|
||||
ast_autoservice_start(chan);
|
||||
if (!cap_slin) {
|
||||
if (!capabilities) {
|
||||
goto return_cleanup;
|
||||
}
|
||||
|
||||
ast_format_cap_append(cap_slin, ast_format_slin, 0);
|
||||
ast_format_cap_append(cap_slin, ast_format_slin12, 0);
|
||||
ast_format_cap_append(cap_slin, ast_format_slin16, 0);
|
||||
ast_format_cap_append(cap_slin, ast_format_slin24, 0);
|
||||
ast_format_cap_append(cap_slin, ast_format_slin32, 0);
|
||||
ast_format_cap_append(cap_slin, ast_format_slin44, 0);
|
||||
ast_format_cap_append(cap_slin, ast_format_slin48, 0);
|
||||
ast_format_cap_append(cap_slin, ast_format_slin96, 0);
|
||||
ast_format_cap_append(cap_slin, ast_format_slin192, 0);
|
||||
ast_format_cap_append(capabilities, ast_format_slin, 0);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_ERROR, "Originate() requires arguments\n");
|
||||
|
@ -257,6 +257,13 @@ static int originate_exec(struct ast_channel *chan, const char *data)
|
|||
goto return_cleanup;
|
||||
}
|
||||
|
||||
if (ast_test_flag64(&opts, OPT_CODECS)) {
|
||||
if (!ast_strlen_zero(opt_args[OPT_ARG_CODECS])) {
|
||||
ast_format_cap_remove_by_type(capabilities, AST_MEDIA_TYPE_UNKNOWN);
|
||||
ast_format_cap_update_by_allow_disallow(capabilities, opt_args[OPT_ARG_CODECS], 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_test_flag64(&opts, OPT_CALLER_NUM)) {
|
||||
if (!ast_strlen_zero(opt_args[OPT_ARG_CALLER_NUM])) {
|
||||
cnum = opt_args[OPT_ARG_CALLER_NUM];
|
||||
|
@ -318,7 +325,7 @@ static int originate_exec(struct ast_channel *chan, const char *data)
|
|||
ast_debug(1, "Originating call to '%s/%s' and connecting them to extension %s,%s,%d\n",
|
||||
chantech, chandata, args.arg1, exten, priority);
|
||||
|
||||
res = ast_pbx_outgoing_exten_predial(chantech, cap_slin, chandata,
|
||||
res = ast_pbx_outgoing_exten_predial(chantech, capabilities, chandata,
|
||||
timeout * 1000, args.arg1, exten, priority, &outgoing_status,
|
||||
ast_test_flag64(&opts, OPT_ASYNC) ? AST_OUTGOING_NO_WAIT : AST_OUTGOING_WAIT,
|
||||
cid_num, cid_name, vars, NULL, NULL, 0, NULL,
|
||||
|
@ -329,7 +336,7 @@ static int originate_exec(struct ast_channel *chan, const char *data)
|
|||
ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n",
|
||||
chantech, chandata, args.arg1, S_OR(args.arg2, ""));
|
||||
|
||||
res = ast_pbx_outgoing_app_predial(chantech, cap_slin, chandata,
|
||||
res = ast_pbx_outgoing_app_predial(chantech, capabilities, chandata,
|
||||
timeout * 1000, args.arg1, args.arg2, &outgoing_status,
|
||||
ast_test_flag64(&opts, OPT_ASYNC) ? AST_OUTGOING_NO_WAIT : AST_OUTGOING_WAIT,
|
||||
cid_num, cid_name, vars, NULL, NULL, NULL,
|
||||
|
@ -380,7 +387,7 @@ return_cleanup:
|
|||
if (vars) {
|
||||
ast_variables_destroy(vars);
|
||||
}
|
||||
ao2_cleanup(cap_slin);
|
||||
ao2_cleanup(capabilities);
|
||||
ast_autoservice_stop(chan);
|
||||
|
||||
return continue_in_dialplan ? 0 : -1;
|
||||
|
|
|
@ -31,7 +31,10 @@
|
|||
/*** MODULEINFO
|
||||
<depend>osptk</depend>
|
||||
<depend>openssl</depend>
|
||||
<support_level>extended</support_level>
|
||||
<defaultenabled>no</defaultenabled>
|
||||
<support_level>deprecated</support_level>
|
||||
<deprecated_in>19</deprecated_in>
|
||||
<removed_in>21</removed_in>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
@ -3163,7 +3166,7 @@ static int reload(void)
|
|||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Open Settlement Protocol Applications",
|
||||
.support_level = AST_MODULE_SUPPORT_EXTENDED,
|
||||
.support_level = AST_MODULE_SUPPORT_DEPRECATED,
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
|
|
|
@ -174,8 +174,6 @@ struct page_options {
|
|||
*
|
||||
* \param chan Setup bridge profile on this channel.
|
||||
* \param options Options to setup bridge profile.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void setup_profile_bridge(struct ast_channel *chan, struct page_options *options)
|
||||
{
|
||||
|
@ -192,8 +190,6 @@ static void setup_profile_bridge(struct ast_channel *chan, struct page_options *
|
|||
*
|
||||
* \param chan Setup user profile on this channel.
|
||||
* \param options Options to setup paged user profile.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void setup_profile_paged(struct ast_channel *chan, struct page_options *options)
|
||||
{
|
||||
|
@ -216,8 +212,6 @@ static void setup_profile_paged(struct ast_channel *chan, struct page_options *o
|
|||
*
|
||||
* \param chan Setup user profile on this channel.
|
||||
* \param options Options to setup caller user profile.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void setup_profile_caller(struct ast_channel *chan, struct page_options *options)
|
||||
{
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
/* This file provides config-file based 'say' functions, and implenents
|
||||
/* This file provides config-file based 'say' functions, and implements
|
||||
* some CLI commands.
|
||||
*/
|
||||
#include "asterisk/say.h" /*!< provides config-file based 'say' functions */
|
||||
|
@ -62,13 +62,19 @@
|
|||
be answered before the sound is played.</para>
|
||||
<note><para>Not all channel types support playing messages while still on hook.</para></note>
|
||||
</option>
|
||||
<option name="say">
|
||||
<para>Play using the say.conf file.</para>
|
||||
</option>
|
||||
<option name="mix">
|
||||
<para>Play using a mix of filename and the say.conf file.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Plays back given filenames (do not put extension of wav/alaw etc).
|
||||
The playback command answer the channel if no options are specified.
|
||||
If the file is non-existant it will fail</para>
|
||||
The Playback application answers the channel if no options are specified.
|
||||
If the file is non-existent it will fail.</para>
|
||||
<para>This application sets the following channel variable upon completion:</para>
|
||||
<variablelist>
|
||||
<variable name="PLAYBACKSTATUS">
|
||||
|
@ -446,6 +452,7 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
|||
char *tmp;
|
||||
int option_skip=0;
|
||||
int option_say=0;
|
||||
int option_mix=0;
|
||||
int option_noanswer = 0;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
|
@ -466,6 +473,8 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
|||
option_skip = 1;
|
||||
if (strcasestr(args.options, "say"))
|
||||
option_say = 1;
|
||||
if (strcasestr(args.options, "mix"))
|
||||
option_mix = 1;
|
||||
if (strcasestr(args.options, "noanswer"))
|
||||
option_noanswer = 1;
|
||||
}
|
||||
|
@ -486,6 +495,13 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
|||
while (!res && (front = strsep(&back, "&"))) {
|
||||
if (option_say)
|
||||
res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
|
||||
else if (option_mix){
|
||||
/* Check if it is in say format but not remote audio file */
|
||||
if (strcasestr(front, ":") && !strcasestr(front, "://"))
|
||||
res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
|
||||
else
|
||||
res = ast_streamfile(chan, front, ast_channel_language(chan));
|
||||
}
|
||||
else
|
||||
res = ast_streamfile(chan, front, ast_channel_language(chan));
|
||||
if (!res) {
|
||||
|
|
522
apps/app_queue.c
522
apps/app_queue.c
File diff suppressed because it is too large
Load Diff
|
@ -75,6 +75,16 @@
|
|||
<option name="n">
|
||||
<para>to read digits even if the line is not up.</para>
|
||||
</option>
|
||||
<option name="t">
|
||||
<para>Terminator digit(s) to use for ending input.
|
||||
Default is <literal>#</literal>. If you need to read
|
||||
the digit <literal>#</literal> literally, you should
|
||||
remove or change the terminator character. Multiple
|
||||
terminator characters may be specified. If no terminator
|
||||
digit is present, input cannot be ended using digits
|
||||
and you will need to rely on duration and max digits
|
||||
for ending input.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
<parameter name="attempts">
|
||||
|
@ -114,12 +124,20 @@ enum read_option_flags {
|
|||
OPT_SKIP = (1 << 0),
|
||||
OPT_INDICATION = (1 << 1),
|
||||
OPT_NOANSWER = (1 << 2),
|
||||
OPT_TERMINATOR = (1 << 3),
|
||||
};
|
||||
|
||||
enum {
|
||||
OPT_ARG_TERMINATOR,
|
||||
/* note: this entry _MUST_ be the last one in the enum */
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(read_app_options, {
|
||||
AST_APP_OPTION('s', OPT_SKIP),
|
||||
AST_APP_OPTION('i', OPT_INDICATION),
|
||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||
AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
|
||||
});
|
||||
|
||||
static char *app = "Read";
|
||||
|
@ -132,9 +150,11 @@ static int read_exec(struct ast_channel *chan, const char *data)
|
|||
int tries = 1, to = 0, x = 0;
|
||||
double tosec;
|
||||
char *argcopy = NULL;
|
||||
char *opt_args[OPT_ARG_ARRAY_SIZE];
|
||||
struct ast_tone_zone_sound *ts = NULL;
|
||||
struct ast_flags flags = {0};
|
||||
const char *status = "ERROR";
|
||||
char *terminator = "#"; /* use default terminator # by default */
|
||||
|
||||
AST_DECLARE_APP_ARGS(arglist,
|
||||
AST_APP_ARG(variable);
|
||||
|
@ -156,7 +176,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
|
|||
AST_STANDARD_APP_ARGS(arglist, argcopy);
|
||||
|
||||
if (!ast_strlen_zero(arglist.options)) {
|
||||
ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
|
||||
ast_app_parse_options(read_app_options, &flags, opt_args, arglist.options);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.attempts)) {
|
||||
|
@ -192,6 +212,13 @@ static int read_exec(struct ast_channel *chan, const char *data)
|
|||
ts = ast_get_indication_tone(ast_channel_zone(chan), arglist.filename);
|
||||
}
|
||||
}
|
||||
if (ast_test_flag(&flags, OPT_TERMINATOR)) {
|
||||
if (!ast_strlen_zero(opt_args[OPT_ARG_TERMINATOR])) {
|
||||
terminator = opt_args[OPT_ARG_TERMINATOR];
|
||||
} else {
|
||||
terminator = ""; /* no digit inherently will terminate input */
|
||||
}
|
||||
}
|
||||
if (ast_channel_state(chan) != AST_STATE_UP) {
|
||||
if (ast_test_flag(&flags, OPT_SKIP)) {
|
||||
/* At the user's option, skip if the line is not up */
|
||||
|
@ -223,7 +250,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
|
|||
break;
|
||||
}
|
||||
tmp[x++] = res;
|
||||
if (tmp[x-1] == '#') {
|
||||
if (terminator && strchr(terminator, tmp[x-1])) {
|
||||
tmp[x-1] = '\0';
|
||||
status = "OK";
|
||||
break;
|
||||
|
@ -233,7 +260,7 @@ static int read_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
res = ast_app_getdata(chan, arglist.filename, tmp, maxdigits, to);
|
||||
res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
|
||||
if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
|
||||
status = "OK";
|
||||
else if (res == AST_GETDATA_TIMEOUT)
|
||||
|
|
|
@ -39,6 +39,11 @@
|
|||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Reload" language="en_US">
|
||||
<since>
|
||||
<version>16.20.0</version>
|
||||
<version>18.6.0</version>
|
||||
<version>19.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Reloads an Asterisk module, blocking the channel until the reload has completed.
|
||||
</synopsis>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
* \brief App to transmit a text message
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \note Requires support of sending text messages from channel driver
|
||||
*
|
||||
|
@ -140,11 +141,55 @@
|
|||
<see-also>
|
||||
<ref type="application">SendImage</ref>
|
||||
<ref type="application">SendURL</ref>
|
||||
<ref type="application">ReceiveText</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="ReceiveText" language="en_US">
|
||||
<since>
|
||||
<version>16.24.0</version>
|
||||
<version>18.10.0</version>
|
||||
<version>19.2.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Receive a Text Message on a channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="timeout" required="false">
|
||||
<para>Time in seconds to wait for text. Default is 0 (forever).</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Waits for <replaceable>timeout</replaceable> seconds on the current channel
|
||||
to receive text.</para>
|
||||
<para>Result of transmission will be stored in the following variables:</para>
|
||||
<variablelist>
|
||||
<variable name="RECEIVETEXTMESSAGE">
|
||||
<para>The received text message.</para>
|
||||
</variable>
|
||||
<variable name="RECEIVETEXTSTATUS">
|
||||
<value name="SUCCESS">
|
||||
Transmission succeeded.
|
||||
</value>
|
||||
<value name="FAILURE">
|
||||
Transmission failed or timed out.
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<example title="Receive message on channel">
|
||||
same => n,ReceiveText()
|
||||
same => n,NoOp(${RECEIVETEXTMESSAGE})
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendText</ref>
|
||||
<ref type="application">SendImage</ref>
|
||||
<ref type="application">SendURL</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static const char * const app = "SendText";
|
||||
static const char * const app2 = "ReceiveText";
|
||||
|
||||
static int sendtext_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
|
@ -237,14 +282,55 @@ cleanup:
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int recvtext_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
double timeout = 0, timeout_ms = 0;
|
||||
char *parse, *buf;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(timeout);
|
||||
);
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (!ast_strlen_zero(args.timeout)) {
|
||||
if (sscanf(args.timeout, "%30lg", &timeout) != 1) {
|
||||
ast_log(LOG_WARNING, "Invalid timeout provided: %s. No timeout set.\n", args.timeout);
|
||||
return -1;
|
||||
}
|
||||
timeout_ms = timeout * 1000.0;
|
||||
}
|
||||
|
||||
buf = ast_recvtext(chan, timeout_ms);
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVETEXTSTATUS", buf ? "SUCCESS" : "FAILURE");
|
||||
if (buf) {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVETEXTMESSAGE", buf);
|
||||
ast_free(buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app);
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(app);
|
||||
res |= ast_unregister_application(app2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app, sendtext_exec);
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(app, sendtext_exec);
|
||||
res |= ast_register_application_xml(app2, recvtext_exec);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send Text Applications");
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Send and Receive Text Applications");
|
||||
|
|
|
@ -0,0 +1,467 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2021, Naveen Albert
|
||||
*
|
||||
* Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief SF sender and receiver applications
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/dsp.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/indications.h"
|
||||
#include "asterisk/conversions.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="ReceiveSF" language="en_US">
|
||||
<since>
|
||||
<version>16.24.0</version>
|
||||
<version>18.10.0</version>
|
||||
<version>19.2.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Detects SF digits on a channel and saves them to a variable.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="variable" required="true">
|
||||
<para>The input digits will be stored in the given
|
||||
<replaceable>variable</replaceable> name.</para>
|
||||
</parameter>
|
||||
<parameter name="digits" required="false">
|
||||
<para>Maximum number of digits to read. Default is unlimited.</para>
|
||||
</parameter>
|
||||
<parameter name="timeout">
|
||||
<para>The number of seconds to wait for all digits, if greater
|
||||
than <literal>0</literal>. Can be floating point. Default
|
||||
is no timeout.</para>
|
||||
</parameter>
|
||||
<parameter name="frequency">
|
||||
<para>The frequency for which to detect pulsed digits.
|
||||
Default is 2600 Hz.</para>
|
||||
</parameter>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="d">
|
||||
<para>Delay audio by a frame to try to extra quelch.</para>
|
||||
</option>
|
||||
<option name="e">
|
||||
<para>Allow receiving extra pulses 11 through 16.</para>
|
||||
</option>
|
||||
<option name="m">
|
||||
<para>Mute conference.</para>
|
||||
</option>
|
||||
<option name="q">
|
||||
<para>Quelch SF from in-band.</para>
|
||||
</option>
|
||||
<option name="r">
|
||||
<para>"Radio" mode (relaxed SF).</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Reads SF digits from the user in to the given
|
||||
<replaceable>variable</replaceable>.</para>
|
||||
<para>This application does not automatically answer the channel and
|
||||
should be preceded with <literal>Answer</literal> or
|
||||
<literal>Progress</literal> as needed.</para>
|
||||
<variablelist>
|
||||
<variable name="RECEIVESFSTATUS">
|
||||
<para>This is the status of the read operation.</para>
|
||||
<value name="START" />
|
||||
<value name="ERROR" />
|
||||
<value name="HANGUP" />
|
||||
<value name="MAXDIGITS" />
|
||||
<value name="TIMEOUT" />
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ReceiveMF</ref>
|
||||
<ref type="application">SendMF</ref>
|
||||
<ref type="application">SendSF</ref>
|
||||
<ref type="application">Read</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="SendSF" language="en_US">
|
||||
<since>
|
||||
<version>16.24.0</version>
|
||||
<version>18.10.0</version>
|
||||
<version>19.2.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Sends arbitrary SF digits on the current or specified channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="digits" required="true">
|
||||
<para>List of digits 0-9 to send; w for a half-second pause,
|
||||
also f or F for a flash-hook if the channel supports flash-hook,
|
||||
h or H for 250 ms of 2600 Hz, and W for a wink if the channel
|
||||
supports wink.</para>
|
||||
</parameter>
|
||||
<parameter name="frequency" required="false">
|
||||
<para>Frequency to use. (defaults to 2600 Hz).</para>
|
||||
</parameter>
|
||||
<parameter name="channel" required="false">
|
||||
<para>Channel where digits will be played</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>It will send all digits or terminate if it encounters an error.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendDTMF</ref>
|
||||
<ref type="application">SendMF</ref>
|
||||
<ref type="application">ReceiveMF</ref>
|
||||
<ref type="application">ReceiveSF</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
enum read_option_flags {
|
||||
OPT_DELAY = (1 << 0),
|
||||
OPT_MUTE = (1 << 1),
|
||||
OPT_QUELCH = (1 << 2),
|
||||
OPT_RELAXED = (1 << 3),
|
||||
OPT_EXTRAPULSES = (1 << 4),
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(read_app_options, {
|
||||
AST_APP_OPTION('d', OPT_DELAY),
|
||||
AST_APP_OPTION('e', OPT_EXTRAPULSES),
|
||||
AST_APP_OPTION('m', OPT_MUTE),
|
||||
AST_APP_OPTION('q', OPT_QUELCH),
|
||||
AST_APP_OPTION('r', OPT_RELAXED),
|
||||
});
|
||||
|
||||
static const char *readsf_name = "ReceiveSF";
|
||||
static const char sendsf_name[] = "SendSF";
|
||||
|
||||
/*!
|
||||
* \brief Detects SF digits on channel using DSP
|
||||
*
|
||||
* \param chan channel on which to read digits
|
||||
* \param buf Buffer in which to store digits
|
||||
* \param buflen Size of buffer
|
||||
* \param timeout ms to wait for all digits before giving up
|
||||
* \param maxdigits Maximum number of digits
|
||||
* \param freq Frequency to use
|
||||
* \param features DSP features
|
||||
* \param extrapulses Whether to recognize extra pulses
|
||||
*
|
||||
* \retval 0 if successful
|
||||
* \retval -1 if unsuccessful (including hangup).
|
||||
*/
|
||||
static int read_sf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int maxdigits, int freq, int features, int extrapulses) {
|
||||
/* Bell System Technical Journal 39 (Nov. 1960) */
|
||||
#define SF_MIN_OFF 25
|
||||
#define SF_ON 67
|
||||
#define SF_BETWEEN 600
|
||||
#define SF_MIN_DETECT 50
|
||||
|
||||
struct ast_dsp *dsp = NULL;
|
||||
struct ast_frame *frame = NULL;
|
||||
struct timeval start, pulsetimer, digittimer;
|
||||
int remaining_time = timeout;
|
||||
char *str = buf;
|
||||
int hits = 0, digits_read = 0;
|
||||
unsigned short int sf_on = 0;
|
||||
int res = 0;
|
||||
|
||||
if (!(dsp = ast_dsp_new())) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "ERROR");
|
||||
return -1;
|
||||
}
|
||||
ast_dsp_set_features(dsp, DSP_FEATURE_FREQ_DETECT);
|
||||
/* tolerance is 46 to 76% make break at 8 to 12 pps */
|
||||
ast_dsp_set_freqmode(dsp, freq, SF_MIN_DETECT, 16, 0);
|
||||
|
||||
start = ast_tvnow();
|
||||
*str = 0; /* start with empty output buffer */
|
||||
|
||||
while (timeout == 0 || remaining_time > 0) {
|
||||
if (timeout > 0) {
|
||||
remaining_time = ast_remaining_ms(start, timeout);
|
||||
if (remaining_time <= 0) {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "TIMEOUT");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (digits_read >= (buflen - 1)) { /* we don't have room to store any more digits (very unlikely to happen for a legitimate reason) */
|
||||
/* This result will probably not be usable, so status should not be START */
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "MAXDIGITS");
|
||||
break;
|
||||
}
|
||||
if (ast_waitfor(chan, 1000) > 0) {
|
||||
frame = ast_read(chan);
|
||||
if (!frame) {
|
||||
ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "HANGUP");
|
||||
break;
|
||||
} else if (frame->frametype == AST_FRAME_VOICE) {
|
||||
frame = ast_dsp_process(chan, dsp, frame);
|
||||
if (frame->frametype == AST_FRAME_DTMF) {
|
||||
char result = frame->subclass.integer;
|
||||
if (result == 'q') {
|
||||
sf_on = 1;
|
||||
pulsetimer = ast_tvnow(); /* reset the pulse timer */
|
||||
/* now, we need at least a 33ms pause to register the pulse */
|
||||
}
|
||||
} else {
|
||||
if (sf_on) {
|
||||
int timeleft = ast_remaining_ms(pulsetimer, SF_MIN_OFF);
|
||||
if (timeleft <= 0) {
|
||||
sf_on = 0;
|
||||
/* The pulse needs to end no more than 30ms after we detected it */
|
||||
if (timeleft > -30) {
|
||||
hits++;
|
||||
digittimer = ast_tvnow(); /* reset the digit timer */
|
||||
ast_debug(5, "Detected SF pulse (pulse #%d)\n", hits);
|
||||
ast_dsp_free(dsp);
|
||||
if (!(dsp = ast_dsp_new())) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "ERROR");
|
||||
ast_frfree(frame);
|
||||
return -1;
|
||||
}
|
||||
ast_dsp_set_features(dsp, DSP_FEATURE_FREQ_DETECT);
|
||||
ast_dsp_set_freqmode(dsp, freq, SF_MIN_DETECT, 16, 0);
|
||||
} else {
|
||||
ast_debug(5, "SF noise, ignoring, time elapsed was %d ms\n", timeleft);
|
||||
}
|
||||
}
|
||||
} else if (hits > 0 && ast_remaining_ms(digittimer, SF_BETWEEN) <= 0) {
|
||||
/* has the digit finished? */
|
||||
ast_debug(2, "Received SF digit: %d\n", hits);
|
||||
digits_read++;
|
||||
if (hits > 10) {
|
||||
if (extrapulses) {
|
||||
/* dahdi-base.c translates 11 to * and 12 to # */
|
||||
if (hits == 11) {
|
||||
hits = '*';
|
||||
} else if (hits == 12) {
|
||||
hits = '#';
|
||||
} else if (hits == 13) {
|
||||
hits = 'D';
|
||||
} else if (hits == 14) {
|
||||
hits = 'C';
|
||||
} else if (hits == 15) {
|
||||
hits = 'B';
|
||||
} else if (hits == 16) {
|
||||
hits = 'A';
|
||||
} else {
|
||||
ast_debug(3, "Got %d SF pulses, is someone playing with the phone?\n", hits);
|
||||
hits = 'A';
|
||||
}
|
||||
*str++ = hits;
|
||||
} else {
|
||||
ast_debug(2, "Got more than 10 pulses, truncating to 10\n");
|
||||
hits = 0; /* 10 dial pulses = digit 0 */
|
||||
*str++ = hits + '0';
|
||||
}
|
||||
} else {
|
||||
if (hits == 10) {
|
||||
hits = 0; /* 10 dial pulses = digit 0 */
|
||||
}
|
||||
*str++ = hits + '0';
|
||||
}
|
||||
*str = 0;
|
||||
hits = 0;
|
||||
if (maxdigits > 0 && digits_read >= maxdigits) {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "START");
|
||||
ast_frfree(frame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_frfree(frame);
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVESFSTATUS", "HANGUP");
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
if (dsp) {
|
||||
ast_dsp_free(dsp);
|
||||
}
|
||||
ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int read_sf_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
#define BUFFER_SIZE 256
|
||||
char tmp[BUFFER_SIZE] = "";
|
||||
double tosec;
|
||||
struct ast_flags flags = {0};
|
||||
char *argcopy = NULL;
|
||||
int res, features = 0, digits = 0, to = 0, freq = 2600;
|
||||
|
||||
AST_DECLARE_APP_ARGS(arglist,
|
||||
AST_APP_ARG(variable);
|
||||
AST_APP_ARG(digits);
|
||||
AST_APP_ARG(timeout);
|
||||
AST_APP_ARG(freq);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "ReceiveSF requires an argument (variable)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(arglist, argcopy);
|
||||
|
||||
if (!ast_strlen_zero(arglist.options)) {
|
||||
ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.timeout)) {
|
||||
tosec = atof(arglist.timeout);
|
||||
if (tosec <= 0) {
|
||||
to = 0;
|
||||
} else {
|
||||
to = tosec * 1000.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.digits) && (ast_str_to_int(arglist.digits, &digits) || digits <= 0)) {
|
||||
ast_log(LOG_WARNING, "Invalid number of digits: %s\n", arglist.digits);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(arglist.freq) && (ast_str_to_int(arglist.freq, &freq) || freq <= 0)) {
|
||||
ast_log(LOG_WARNING, "Invalid freq: %s\n", arglist.freq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(arglist.variable)) {
|
||||
ast_log(LOG_WARNING, "Invalid! Usage: ReceiveSF(variable[,timeout][,option])\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPT_DELAY)) {
|
||||
features |= DSP_DIGITMODE_MUTEMAX;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPT_MUTE)) {
|
||||
features |= DSP_DIGITMODE_MUTECONF;
|
||||
}
|
||||
|
||||
if (!ast_test_flag(&flags, OPT_QUELCH)) {
|
||||
features |= DSP_DIGITMODE_NOQUELCH;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPT_RELAXED)) {
|
||||
features |= DSP_DIGITMODE_RELAXDTMF;
|
||||
}
|
||||
|
||||
res = read_sf_digits(chan, tmp, BUFFER_SIZE, to, digits, freq, features, ast_test_flag(&flags, OPT_EXTRAPULSES));
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||
if (!ast_strlen_zero(tmp)) {
|
||||
ast_verb(3, "SF digits received: '%s'\n", tmp);
|
||||
} else if (!res) { /* if channel hung up, don't print anything out */
|
||||
ast_verb(3, "No SF digits received.\n");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int sendsf_exec(struct ast_channel *chan, const char *vdata)
|
||||
{
|
||||
int res;
|
||||
char *data;
|
||||
int frequency = 2600;
|
||||
struct ast_channel *chan_found = NULL;
|
||||
struct ast_channel *chan_dest = chan;
|
||||
struct ast_channel *chan_autoservice = NULL;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(digits);
|
||||
AST_APP_ARG(frequency);
|
||||
AST_APP_ARG(channel);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(vdata)) {
|
||||
ast_log(LOG_WARNING, "SendSF requires an argument\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = ast_strdupa(vdata);
|
||||
AST_STANDARD_APP_ARGS(args, data);
|
||||
|
||||
if (ast_strlen_zero(args.digits)) {
|
||||
ast_log(LOG_WARNING, "The digits argument is required (0-9,wf)\n");
|
||||
return 0;
|
||||
}
|
||||
if (!ast_strlen_zero(args.frequency) && (ast_str_to_int(args.frequency, &frequency) || frequency < 1)) {
|
||||
ast_log(LOG_WARNING, "Invalid duration: %s\n", args.frequency);
|
||||
return -1;
|
||||
}
|
||||
if (!ast_strlen_zero(args.channel)) {
|
||||
chan_found = ast_channel_get_by_name(args.channel);
|
||||
if (!chan_found) {
|
||||
ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
|
||||
return 0;
|
||||
}
|
||||
chan_dest = chan_found;
|
||||
if (chan_found != chan) {
|
||||
chan_autoservice = chan;
|
||||
}
|
||||
}
|
||||
res = ast_sf_stream(chan_dest, chan_autoservice, NULL, args.digits, frequency, 0);
|
||||
ast_channel_cleanup(chan_found);
|
||||
|
||||
return chan_autoservice ? 0 : res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(readsf_name);
|
||||
res |= ast_unregister_application(sendsf_name);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(readsf_name, read_sf_exec);
|
||||
res |= ast_register_application_xml(sendsf_name, sendsf_exec);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "SF Sender and Receiver Applications");
|
|
@ -590,8 +590,8 @@ static int gosub_exec(struct ast_channel *chan, const char *data)
|
|||
ast_channel_unlock(chan);
|
||||
|
||||
if (!ast_exists_extension(chan, dest_context, dest_exten, dest_priority, caller_id)) {
|
||||
ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for %s: (Context:%s, Extension:%s, Priority:%d)\n",
|
||||
app_gosub, dest_context, dest_exten, dest_priority);
|
||||
ast_log(LOG_ERROR, "%s attempted to reach non-existent destination '%s,%s,%d' from '%s,%s,%d'",
|
||||
app_gosub, dest_context, dest_exten, dest_priority, orig_context, orig_exten, orig_priority);
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
|
@ -924,8 +924,6 @@ static struct ast_custom_function stackpeek_function = {
|
|||
* \param chan Channel to balance stack on.
|
||||
*
|
||||
* \note The channel is already locked when called.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void balance_stack(struct ast_channel *chan)
|
||||
{
|
||||
|
@ -1078,7 +1076,7 @@ static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_
|
|||
ast_channel_name(chan), app_gosub, sub_args,
|
||||
S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
|
||||
ast_log(LOG_WARNING, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
|
||||
ast_channel_name(chan), app_gosub, sub_args);
|
||||
balance_stack(chan);
|
||||
pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
/*! \brief Dialplan application name */
|
||||
static const char *stasis = "Stasis";
|
||||
|
||||
/*! /brief Stasis dialplan application callback */
|
||||
/*! \brief Stasis dialplan application callback */
|
||||
static int app_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *parse = NULL;
|
||||
|
|
|
@ -78,7 +78,7 @@ static const char app[] = "StatsD";
|
|||
*
|
||||
* \param value The value of the statistic to be sent to StatsD.
|
||||
*
|
||||
* This function checks to see if the value given to the StatsD daialplan
|
||||
* This function checks to see if the value given to the StatsD dailplan
|
||||
* application is within the allowed range of [-2^63, 2^63] as specified by StatsD.
|
||||
*
|
||||
* \retval zero on success.
|
||||
|
@ -100,7 +100,7 @@ static int value_in_range(const char *value) {
|
|||
*
|
||||
* \param value The value of the statistic to be sent to StatsD.
|
||||
*
|
||||
* This function checks to see if the value given to the StatsD daialplan
|
||||
* This function checks to see if the value given to the StatsD dailplan
|
||||
* application is within the allowed range of [0, 2^64] as specified by StatsD.
|
||||
*
|
||||
* \retval zero on success.
|
||||
|
|
|
@ -352,7 +352,7 @@ static int testserver_exec(struct ast_channel *chan, const char *data)
|
|||
if (!res)
|
||||
res = ast_app_getdata(chan, NULL, testid, sizeof(testid) - 1, 0);
|
||||
ast_debug(1, "read test identifier: %s\n", testid);
|
||||
/* Check for sneakyness */
|
||||
/* Check for sneakiness */
|
||||
if (strchr(testid, '/'))
|
||||
res = -1;
|
||||
if ((res >=0) && (!ast_strlen_zero(testid))) {
|
||||
|
|
180
apps/app_url.c
180
apps/app_url.c
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 1999 - 2005, Digium, Inc.
|
||||
*
|
||||
* Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to transmit a URL
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>deprecated</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/channel.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="SendURL" language="en_US">
|
||||
<synopsis>
|
||||
Send a URL.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="URL" required="true" />
|
||||
<parameter name="option">
|
||||
<optionlist>
|
||||
<option name="w">
|
||||
<para>Execution will wait for an acknowledgement that the
|
||||
URL has been loaded before continuing.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Requests client go to <replaceable>URL</replaceable> (IAX2) or sends the
|
||||
URL to the client (other channels).</para>
|
||||
<para>Result is returned in the <variable>SENDURLSTATUS</variable> channel variable:</para>
|
||||
<variablelist>
|
||||
<variable name="SENDURLSTATUS">
|
||||
<value name="SUCCESS">
|
||||
URL successfully sent to client.
|
||||
</value>
|
||||
<value name="FAILURE">
|
||||
Failed to send URL.
|
||||
</value>
|
||||
<value name="NOLOAD">
|
||||
Client failed to load URL (wait enabled).
|
||||
</value>
|
||||
<value name="UNSUPPORTED">
|
||||
Channel does not support URL transport.
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<para>SendURL continues normally if the URL was sent correctly or if the channel
|
||||
does not support HTML transport. Otherwise, the channel is hung up.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendImage</ref>
|
||||
<ref type="application">SendText</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static char *app = "SendURL";
|
||||
|
||||
enum option_flags {
|
||||
OPTION_WAIT = (1 << 0),
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(app_opts,{
|
||||
AST_APP_OPTION('w', OPTION_WAIT),
|
||||
});
|
||||
|
||||
static int sendurl_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
int res = 0;
|
||||
char *tmp;
|
||||
struct ast_frame *f;
|
||||
char *status = "FAILURE";
|
||||
char *opts[0];
|
||||
struct ast_flags flags = { 0 };
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(url);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "SendURL requires an argument (URL)\n");
|
||||
pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tmp = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, tmp);
|
||||
if (args.argc == 2)
|
||||
ast_app_parse_options(app_opts, &flags, opts, args.options);
|
||||
|
||||
if (!ast_channel_supports_html(chan)) {
|
||||
/* Does not support transport */
|
||||
pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "UNSUPPORTED");
|
||||
return 0;
|
||||
}
|
||||
res = ast_channel_sendurl(chan, args.url);
|
||||
if (res == -1) {
|
||||
pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", "FAILURE");
|
||||
return res;
|
||||
}
|
||||
status = "SUCCESS";
|
||||
if (ast_test_flag(&flags, OPTION_WAIT)) {
|
||||
for(;;) {
|
||||
/* Wait for an event */
|
||||
res = ast_waitfor(chan, -1);
|
||||
if (res < 0)
|
||||
break;
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
res = -1;
|
||||
status = "FAILURE";
|
||||
break;
|
||||
}
|
||||
if (f->frametype == AST_FRAME_HTML) {
|
||||
switch (f->subclass.integer) {
|
||||
case AST_HTML_LDCOMPLETE:
|
||||
res = 0;
|
||||
ast_frfree(f);
|
||||
status = "NOLOAD";
|
||||
goto out;
|
||||
break;
|
||||
case AST_HTML_NOSUPPORT:
|
||||
/* Does not support transport */
|
||||
status = "UNSUPPORTED";
|
||||
res = 0;
|
||||
ast_frfree(f);
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
ast_log(LOG_WARNING, "Don't know what to do with HTML subclass %d\n", f->subclass.integer);
|
||||
};
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
}
|
||||
out:
|
||||
pbx_builtin_setvar_helper(chan, "SENDURLSTATUS", status);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app, sendurl_exec);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_DEPRECATED(ASTERISK_GPL_KEY, "Send URL Applications");
|
|
@ -33,6 +33,7 @@
|
|||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/logger.h"
|
||||
|
||||
static char *app_verbose = "Verbose";
|
||||
static char *app_log = "Log";
|
||||
|
@ -61,7 +62,8 @@ static char *app_log = "Log";
|
|||
<syntax>
|
||||
<parameter name="level" required="true">
|
||||
<para>Level must be one of <literal>ERROR</literal>, <literal>WARNING</literal>, <literal>NOTICE</literal>,
|
||||
<literal>DEBUG</literal>, <literal>VERBOSE</literal> or <literal>DTMF</literal>.</para>
|
||||
<literal>DEBUG</literal>, <literal>VERBOSE</literal>, <literal>DTMF</literal>, or
|
||||
the name of a custom dynamic logging level.</para>
|
||||
</parameter>
|
||||
<parameter name="message" required="true">
|
||||
<para>Output text message.</para>
|
||||
|
@ -135,7 +137,7 @@ static int log_exec(struct ast_channel *chan, const char *data)
|
|||
} else if (!strcasecmp(args.level, "DTMF")) {
|
||||
lnum = __LOG_DTMF;
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Unknown log level: '%s'\n", args.level);
|
||||
lnum = ast_logger_get_dynamic_level(args.level);
|
||||
}
|
||||
|
||||
if (lnum > -1) {
|
||||
|
@ -143,6 +145,9 @@ static int log_exec(struct ast_channel *chan, const char *data)
|
|||
snprintf(extension, sizeof(extension), "Ext. %s", ast_channel_exten(chan));
|
||||
|
||||
ast_log(lnum, extension, ast_channel_priority(chan), context, "%s\n", args.msg);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Unknown log level: '%s'\n", args.level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
* (http://www.washington.edu/imap/)
|
||||
*
|
||||
* \par See also
|
||||
* \arg \ref Config_vm
|
||||
* \arg \ref voicemail.conf "Config_voicemail"
|
||||
* \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
|
||||
* \ingroup applications
|
||||
* \todo This module requires res_adsi to load. This needs to be optional
|
||||
|
@ -149,6 +149,11 @@
|
|||
<para>Skip the playback of instructions for leaving a message to the
|
||||
calling party.</para>
|
||||
</option>
|
||||
<option name="S">
|
||||
<para>Skip the playback of instructions for leaving a message to the
|
||||
calling party, but only if a greeting has been recorded by the
|
||||
mailbox user.</para>
|
||||
</option>
|
||||
<option name="t">
|
||||
<argument name="x" required="false" />
|
||||
<para>Play a custom beep tone to the caller instead of the default one.
|
||||
|
@ -214,6 +219,10 @@
|
|||
<para>Use the specified amount of gain when recording a voicemail message.
|
||||
The units are whole-number decibels (dB).</para>
|
||||
</option>
|
||||
<option name="r">
|
||||
<para>"Read only". Prevent user from deleting any messages.</para>
|
||||
<para>This applies only to specific executions of VoiceMailMain, NOT the mailbox itself.</para>
|
||||
</option>
|
||||
<option name="s">
|
||||
<para>Skip checking the passcode for the mailbox.</para>
|
||||
</option>
|
||||
|
@ -524,7 +533,6 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
|
|||
/* Default mail command to mail voicemail. Change it with the
|
||||
* mailcmd= command in voicemail.conf */
|
||||
#define SENDMAIL "/usr/sbin/sendmail -t"
|
||||
|
||||
#define INTRO "vm-intro"
|
||||
|
||||
#define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
|
||||
|
@ -534,8 +542,6 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
|
|||
|
||||
#define MINPASSWORD 0 /*!< Default minimum mailbox password length */
|
||||
|
||||
#define BASELINELEN 72
|
||||
#define BASEMAXINLINE 256
|
||||
#ifdef IMAP_STORAGE
|
||||
#define ENDL "\r\n"
|
||||
#else
|
||||
|
@ -555,7 +561,7 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
|
|||
#define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
|
||||
#define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
|
||||
#define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
|
||||
#define VM_DIRECFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
|
||||
#define VM_DIRECTFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
|
||||
#define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
|
||||
#define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
|
||||
#define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
|
||||
|
@ -588,7 +594,9 @@ enum vm_option_flags {
|
|||
OPT_MESSAGE_Urgent = (1 << 8),
|
||||
OPT_MESSAGE_PRIORITY = (1 << 9),
|
||||
OPT_EARLYM_GREETING = (1 << 10),
|
||||
OPT_BEEP = (1 << 11)
|
||||
OPT_BEEP = (1 << 11),
|
||||
OPT_SILENT_IF_GREET = (1 << 12),
|
||||
OPT_READONLY = (1 << 13),
|
||||
};
|
||||
|
||||
enum vm_option_args {
|
||||
|
@ -608,6 +616,7 @@ enum vm_passwordlocation {
|
|||
|
||||
AST_APP_OPTIONS(vm_app_options, {
|
||||
AST_APP_OPTION('s', OPT_SILENT),
|
||||
AST_APP_OPTION('S', OPT_SILENT_IF_GREET),
|
||||
AST_APP_OPTION('b', OPT_BUSY_GREETING),
|
||||
AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
|
||||
AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
|
||||
|
@ -617,7 +626,8 @@ AST_APP_OPTIONS(vm_app_options, {
|
|||
AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
|
||||
AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY),
|
||||
AST_APP_OPTION('e', OPT_EARLYM_GREETING),
|
||||
AST_APP_OPTION_ARG('t', OPT_BEEP, OPT_ARG_BEEP_TONE)
|
||||
AST_APP_OPTION_ARG('t', OPT_BEEP, OPT_ARG_BEEP_TONE),
|
||||
AST_APP_OPTION('r', OPT_READONLY),
|
||||
});
|
||||
|
||||
static const char * const mailbox_folders[] = {
|
||||
|
@ -737,14 +747,6 @@ and vm-Old are spelled plural, to make them sound more as folder name than an ad
|
|||
|
||||
*/
|
||||
|
||||
struct baseio {
|
||||
int iocp;
|
||||
int iolen;
|
||||
int linelength;
|
||||
int ateof;
|
||||
unsigned char iobuf[BASEMAXINLINE];
|
||||
};
|
||||
|
||||
#define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION)
|
||||
#define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT)
|
||||
/* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */
|
||||
|
@ -1084,7 +1086,7 @@ static int vm_test_create_user(const char *context, const char *mailbox);
|
|||
* \brief Parse the given mailbox_id into mailbox and context.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param mailbox_id The mailbox@context string to separate.
|
||||
* \param mailbox_id The mailbox\@context string to separate.
|
||||
* \param mailbox Where the mailbox part will start.
|
||||
* \param context Where the context part will start. ("default" if not present)
|
||||
*
|
||||
|
@ -1921,22 +1923,6 @@ static int make_file(char *dest, const int len, const char *dir, const int num)
|
|||
return snprintf(dest, len, "%s/msg%04d", dir, num);
|
||||
}
|
||||
|
||||
/* same as mkstemp, but return a FILE * */
|
||||
static FILE *vm_mkftemp(char *template)
|
||||
{
|
||||
FILE *p = NULL;
|
||||
int pfd = mkstemp(template);
|
||||
chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
|
||||
if (pfd > -1) {
|
||||
p = fdopen(pfd, "w+");
|
||||
if (!p) {
|
||||
close(pfd);
|
||||
pfd = -1;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*! \brief basically mkdir -p $dest/$context/$ext/$folder
|
||||
* \param dest String. base directory.
|
||||
* \param len Length of dest.
|
||||
|
@ -2690,7 +2676,7 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char
|
|||
|
||||
/* Make a temporary file instead of piping directly to sendmail, in case the mail
|
||||
command hangs. */
|
||||
if (!(p = vm_mkftemp(tmp))) {
|
||||
if (!(p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask))) {
|
||||
ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
|
||||
if (tempcopy) {
|
||||
ast_free(vmu->email);
|
||||
|
@ -2843,9 +2829,9 @@ static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsg
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/*!
|
||||
* \brief Determines if the given folder has messages.
|
||||
* \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
|
||||
* \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
|
||||
* \param folder the folder to look in
|
||||
*
|
||||
* This function is used when the mailbox is stored in an IMAP back end.
|
||||
|
@ -2885,6 +2871,7 @@ static int has_voicemail(const char *mailbox, const char *folder)
|
|||
* \param recip
|
||||
* \param fmt
|
||||
* \param dir
|
||||
* \param flag, dest_folder
|
||||
*
|
||||
* This works with IMAP storage based mailboxes.
|
||||
*
|
||||
|
@ -3017,7 +3004,7 @@ static int init_mailstream(struct vm_state *vms, int box)
|
|||
ast_mutex_lock(&vms->lock);
|
||||
ast_mutex_lock(&mail_open_lock);
|
||||
vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
|
||||
/* Create the folder if it dosn't exist */
|
||||
/* Create the folder if it doesn't exist */
|
||||
if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
|
||||
mail_create(vms->mailstream, tmp);
|
||||
}
|
||||
|
@ -4174,8 +4161,6 @@ bail:
|
|||
*
|
||||
* This method is used when mailboxes are stored in an ODBC back end.
|
||||
* The specified message is directly deleted from the database 'voicemessages' table.
|
||||
*
|
||||
* \return the value greater than zero on success to indicate the number of messages, less than zero on error.
|
||||
*/
|
||||
static void delete_file(const char *sdir, int smsg)
|
||||
{
|
||||
|
@ -4764,134 +4749,6 @@ static int vm_delete(char *file)
|
|||
return ast_filedelete(file, NULL);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief utility used by inchar(), for base_encode()
|
||||
*/
|
||||
static int inbuf(struct baseio *bio, FILE *fi)
|
||||
{
|
||||
int l;
|
||||
|
||||
if (bio->ateof)
|
||||
return 0;
|
||||
|
||||
if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) != BASEMAXINLINE) {
|
||||
bio->ateof = 1;
|
||||
if (l == 0) {
|
||||
/* Assume EOF */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bio->iolen = l;
|
||||
bio->iocp = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief utility used by base_encode()
|
||||
*/
|
||||
static int inchar(struct baseio *bio, FILE *fi)
|
||||
{
|
||||
if (bio->iocp>=bio->iolen) {
|
||||
if (!inbuf(bio, fi))
|
||||
return EOF;
|
||||
}
|
||||
|
||||
return bio->iobuf[bio->iocp++];
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief utility used by base_encode()
|
||||
*/
|
||||
static int ochar(struct baseio *bio, int c, FILE *so)
|
||||
{
|
||||
if (bio->linelength >= BASELINELEN) {
|
||||
if (fputs(ENDL, so) == EOF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bio->linelength = 0;
|
||||
}
|
||||
|
||||
if (putc(((unsigned char) c), so) == EOF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bio->linelength++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Performs a base 64 encode algorithm on the contents of a File
|
||||
* \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
|
||||
* \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
|
||||
*
|
||||
* TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
|
||||
*
|
||||
* \return zero on success, -1 on error.
|
||||
*/
|
||||
static int base_encode(char *filename, FILE *so)
|
||||
{
|
||||
static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
|
||||
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
|
||||
'1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
|
||||
int i, hiteof = 0;
|
||||
FILE *fi;
|
||||
struct baseio bio;
|
||||
|
||||
memset(&bio, 0, sizeof(bio));
|
||||
bio.iocp = BASEMAXINLINE;
|
||||
|
||||
if (!(fi = fopen(filename, "rb"))) {
|
||||
ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!hiteof){
|
||||
unsigned char igroup[3], ogroup[4];
|
||||
int c, n;
|
||||
|
||||
memset(igroup, 0, sizeof(igroup));
|
||||
|
||||
for (n = 0; n < 3; n++) {
|
||||
if ((c = inchar(&bio, fi)) == EOF) {
|
||||
hiteof = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
igroup[n] = (unsigned char) c;
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
ogroup[0]= dtable[igroup[0] >> 2];
|
||||
ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
|
||||
ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
|
||||
ogroup[3]= dtable[igroup[2] & 0x3F];
|
||||
|
||||
if (n < 3) {
|
||||
ogroup[3] = '=';
|
||||
|
||||
if (n < 2)
|
||||
ogroup[2] = '=';
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
ochar(&bio, ogroup[i], so);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fi);
|
||||
|
||||
if (fputs(ENDL, so) == EOF) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag)
|
||||
{
|
||||
char callerid[256];
|
||||
|
@ -5080,7 +4937,7 @@ static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, con
|
|||
* \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
|
||||
* \param flag, msg_id
|
||||
*
|
||||
* The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email. That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
|
||||
* The email body, and base 64 encoded attachment (if any) are stored to the file identified by *p. This method does not actually send the email. That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
|
||||
*/
|
||||
static void make_email_file(FILE *p,
|
||||
char *srcemail,
|
||||
|
@ -5502,7 +5359,7 @@ static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format,
|
|||
fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
|
||||
else
|
||||
fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
|
||||
base_encode(fname, p);
|
||||
ast_base64_encode_file_path(fname, p, ENDL);
|
||||
if (last)
|
||||
fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
|
||||
|
||||
|
@ -5555,7 +5412,7 @@ static int sendmail(char *srcemail,
|
|||
ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %u\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
|
||||
/* Make a temporary file instead of piping directly to sendmail, in case the mail
|
||||
command hangs */
|
||||
if ((p = vm_mkftemp(tmp)) == NULL) {
|
||||
if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) {
|
||||
ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
|
||||
return -1;
|
||||
} else {
|
||||
|
@ -5594,7 +5451,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
|
|||
strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
|
||||
}
|
||||
|
||||
if ((p = vm_mkftemp(tmp)) == NULL) {
|
||||
if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) {
|
||||
ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
|
||||
ast_free(str1);
|
||||
ast_free(str2);
|
||||
|
@ -5940,9 +5797,9 @@ bail:
|
|||
return nummsgs;
|
||||
}
|
||||
|
||||
/**
|
||||
/*!
|
||||
* \brief Determines if the given folder has messages.
|
||||
* \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
|
||||
* \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
|
||||
*
|
||||
* This function is used when the mailbox is stored in an ODBC back end.
|
||||
* This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
|
||||
|
@ -6112,7 +5969,7 @@ static int __has_voicemail(const char *context, const char *mailbox, const char
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
/*!
|
||||
* \brief Determines if the given folder has messages.
|
||||
* \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
|
||||
* \param folder the folder to look in
|
||||
|
@ -6145,7 +6002,7 @@ static int has_voicemail(const char *mailbox, const char *folder)
|
|||
|
||||
/*!
|
||||
* \brief Check the given mailbox's message count.
|
||||
* \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
|
||||
* \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
|
||||
* \param urgentmsgs urgent message count.
|
||||
* \param newmsgs new message count.
|
||||
* \param oldmsgs old message count pointer
|
||||
|
@ -6781,8 +6638,14 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
|
|||
#endif
|
||||
RETRIEVE(prefile, -1, ext, context);
|
||||
if (ast_fileexists(prefile, NULL, NULL) > 0) {
|
||||
if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1)
|
||||
if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1) {
|
||||
/* We know we have a greeting at this point, so squelch the instructions
|
||||
* if that is what is being asked of us */
|
||||
if (ast_test_flag(options, OPT_SILENT_IF_GREET)) {
|
||||
ast_set_flag(options, OPT_SILENT);
|
||||
}
|
||||
res = ast_waitstream(chan, ecodes);
|
||||
}
|
||||
#ifdef ODBC_STORAGE
|
||||
if (success == -1) {
|
||||
/* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
|
||||
|
@ -7240,7 +7103,7 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
|
|||
/* get the current mailbox so that we can point the mailstream back to it later */
|
||||
curr_mbox = get_folder_by_name(vms->curbox);
|
||||
|
||||
/* Create the folder if it dosn't exist */
|
||||
/* Create the folder if it doesn't exist */
|
||||
imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
|
||||
if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
|
||||
if (mail_create(vms->mailstream, mailbox) != NIL) {
|
||||
|
@ -7359,7 +7222,7 @@ static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
|
|||
bytes = 0;
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advanced", "3", 1);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
|
||||
|
@ -8301,7 +8164,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
|
|||
ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
|
||||
while (!res && !valid_extensions) {
|
||||
int use_directory = 0;
|
||||
if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
|
||||
if (ast_test_flag((&globalflags), VM_DIRECTFORWARD)) {
|
||||
int done = 0;
|
||||
int retries = 0;
|
||||
cmd = 0;
|
||||
|
@ -8370,7 +8233,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
|
|||
ast_channel_priority_set(chan, old_priority);
|
||||
} else {
|
||||
ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
|
||||
ast_clear_flag((&globalflags), VM_DIRECFORWARD);
|
||||
ast_clear_flag((&globalflags), VM_DIRECTFORWARD);
|
||||
}
|
||||
} else {
|
||||
/* Ask for an extension */
|
||||
|
@ -8694,7 +8557,7 @@ static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *v
|
|||
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
|
||||
} else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
|
||||
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
|
||||
} else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* Brazillian PORTUGUESE syntax */
|
||||
} else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* Brazilian PORTUGUESE syntax */
|
||||
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
|
||||
} else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
|
||||
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
|
||||
|
@ -9434,9 +9297,9 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
|
|||
* on vm-message.
|
||||
* 3) Call ast_say_counted_adjective() to put the proper gender and number
|
||||
* prefix on vm-new and vm-old (none for English).
|
||||
* 4) Pass the gender of the language's word for "message" as an agument to
|
||||
* 4) Pass the gender of the language's word for "message" as an argument to
|
||||
* this function which is can in turn pass on to the functions which
|
||||
* say numbers and put endings on nounds and adjectives.
|
||||
* say numbers and put endings on nouns and adjectives.
|
||||
*
|
||||
* All languages require these messages:
|
||||
* vm-youhave "You have..."
|
||||
|
@ -9923,6 +9786,51 @@ static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
|
|||
return res;
|
||||
}
|
||||
|
||||
/* Danish syntax */
|
||||
static int vm_intro_da(struct ast_channel *chan, struct vm_state *vms)
|
||||
{
|
||||
/* Introduce messages they have */
|
||||
int res;
|
||||
|
||||
res = ast_play_and_wait(chan, "vm-youhave");
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
|
||||
res = ast_play_and_wait(chan, "vm-no");
|
||||
res = res ? res : ast_play_and_wait(chan, "vm-messages");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (vms->newmessages) {
|
||||
if ((vms->newmessages == 1)) {
|
||||
res = ast_play_and_wait(chan, "digits/1");
|
||||
res = res ? res : ast_play_and_wait(chan, "vm-INBOX");
|
||||
res = res ? res : ast_play_and_wait(chan, "vm-message");
|
||||
} else {
|
||||
res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
|
||||
res = res ? res : ast_play_and_wait(chan, "vm-INBOXs");
|
||||
res = res ? res : ast_play_and_wait(chan, "vm-messages");
|
||||
}
|
||||
if (!res && vms->oldmessages)
|
||||
res = ast_play_and_wait(chan, "vm-and");
|
||||
}
|
||||
if (!res && vms->oldmessages) {
|
||||
if (vms->oldmessages == 1) {
|
||||
res = ast_play_and_wait(chan, "digits/1");
|
||||
res = res ? res : ast_play_and_wait(chan, "vm-Old");
|
||||
res = res ? res : ast_play_and_wait(chan, "vm-message");
|
||||
} else {
|
||||
res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
|
||||
res = res ? res : ast_play_and_wait(chan, "vm-Olds");
|
||||
res = res ? res : ast_play_and_wait(chan, "vm-messages");
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* GERMAN syntax */
|
||||
static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
|
||||
{
|
||||
|
@ -10403,6 +10311,8 @@ static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm
|
|||
return vm_intro_nl(chan, vms);
|
||||
} else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
|
||||
return vm_intro_no(chan, vms);
|
||||
} else if (!strncasecmp(ast_channel_language(chan), "da", 2)) { /* DANISH syntax */
|
||||
return vm_intro_da(chan, vms);
|
||||
} else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
|
||||
return vm_intro_pl(chan, vms);
|
||||
} else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
|
||||
|
@ -10424,7 +10334,7 @@ static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm
|
|||
}
|
||||
}
|
||||
|
||||
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
|
||||
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
|
||||
{
|
||||
int res = 0;
|
||||
/* Play instructions and wait for new command */
|
||||
|
@ -10484,10 +10394,12 @@ static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu,
|
|||
#ifdef IMAP_STORAGE
|
||||
ast_mutex_unlock(&vms->lock);
|
||||
#endif
|
||||
if (!curmsg_deleted) {
|
||||
res = ast_play_and_wait(chan, "vm-delete");
|
||||
} else {
|
||||
res = ast_play_and_wait(chan, "vm-undelete");
|
||||
if (!nodelete) {
|
||||
if (!curmsg_deleted) {
|
||||
res = ast_play_and_wait(chan, "vm-delete");
|
||||
} else {
|
||||
res = ast_play_and_wait(chan, "vm-undelete");
|
||||
}
|
||||
}
|
||||
if (!res) {
|
||||
res = ast_play_and_wait(chan, "vm-toforward");
|
||||
|
@ -10512,7 +10424,7 @@ static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu,
|
|||
return res;
|
||||
}
|
||||
|
||||
static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
|
||||
static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
|
||||
{
|
||||
int res = 0;
|
||||
/* Play instructions and wait for new command */
|
||||
|
@ -10608,7 +10520,7 @@ static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu,
|
|||
return res;
|
||||
}
|
||||
|
||||
static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
|
||||
static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
|
||||
{
|
||||
int res = 0;
|
||||
/* Play instructions and wait for new command */
|
||||
|
@ -10626,20 +10538,20 @@ static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu,
|
|||
res = ast_play_and_wait(chan, "vm-opts");
|
||||
if (!res) {
|
||||
vms->starting = 0;
|
||||
return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
|
||||
return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
|
||||
static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
|
||||
{
|
||||
if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
|
||||
return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent);
|
||||
return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
|
||||
} else if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
|
||||
return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
|
||||
return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
|
||||
} else { /* Default to ENGLISH */
|
||||
return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
|
||||
return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11285,12 +11197,14 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
|
|||
password[0] = '\0';
|
||||
} else {
|
||||
if (ast_streamfile(chan, vm_password, ast_channel_language(chan))) {
|
||||
ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
|
||||
if (!ast_check_hangup(chan)) {
|
||||
ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
|
||||
}
|
||||
free_user(vmu);
|
||||
return -1;
|
||||
}
|
||||
if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
|
||||
ast_log(AST_LOG_WARNING, "Unable to read password\n");
|
||||
ast_log(AST_LOG_NOTICE, "Unable to read password\n");
|
||||
free_user(vmu);
|
||||
return -1;
|
||||
} else if (password[0] == '*') {
|
||||
|
@ -11522,6 +11436,7 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
|
|||
int play_auto = 0;
|
||||
int play_folder = 0;
|
||||
int in_urgent = 0;
|
||||
int nodelete = 0;
|
||||
#ifdef IMAP_STORAGE
|
||||
int deleted = 0;
|
||||
#endif
|
||||
|
@ -11584,6 +11499,9 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
|
|||
play_folder = 0;
|
||||
}
|
||||
}
|
||||
if (ast_test_flag(&flags, OPT_READONLY)) {
|
||||
nodelete = 1;
|
||||
}
|
||||
} else {
|
||||
/* old style options parsing */
|
||||
while (*(args.argv0)) {
|
||||
|
@ -11608,10 +11526,16 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
|
|||
else
|
||||
ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
|
||||
|
||||
if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
|
||||
skipuser++;
|
||||
else
|
||||
if (!ast_strlen_zero(vms.username)) {
|
||||
if ((vmu = find_user(&vmus, context ,vms.username))) {
|
||||
skipuser++;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Mailbox '%s%s%s' doesn't exist\n", vms.username, context ? "@": "", context ? context : "");
|
||||
valid = 0;
|
||||
}
|
||||
} else {
|
||||
valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
|
@ -11991,7 +11915,7 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
break;
|
||||
case '7': /* Delete the current message */
|
||||
if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
|
||||
if (!nodelete && vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
|
||||
vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
|
||||
if (useadsi)
|
||||
adsi_delete(chan, &vms);
|
||||
|
@ -12180,7 +12104,7 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
|
|||
if (!cmd)
|
||||
cmd = ast_play_and_wait(chan, "vm-opts");
|
||||
if (!cmd)
|
||||
cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
|
||||
cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
|
||||
break;
|
||||
}
|
||||
cmd = ast_play_and_wait(chan, "vm-onefor");
|
||||
|
@ -12192,7 +12116,7 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
|
|||
if (!cmd)
|
||||
cmd = ast_play_and_wait(chan, "vm-opts");
|
||||
if (!cmd)
|
||||
cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
|
||||
cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
|
||||
} else
|
||||
cmd = 0;
|
||||
break;
|
||||
|
@ -12209,7 +12133,7 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
|
|||
break;
|
||||
default: /* Nothing */
|
||||
ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
|
||||
cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
|
||||
cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent, nodelete);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -12295,7 +12219,7 @@ static int vm_exec(struct ast_channel *chan, const char *data)
|
|||
if (args.argc == 2) {
|
||||
if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
|
||||
return -1;
|
||||
ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
|
||||
ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_SILENT_IF_GREET | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
|
||||
if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
|
||||
int gain;
|
||||
|
||||
|
@ -12527,7 +12451,7 @@ AST_TEST_DEFINE(test_voicemail_vmuser)
|
|||
res = 1;
|
||||
}
|
||||
if (strcasecmp(vmu->attachfmt, "wav49")) {
|
||||
ast_test_status_update(test, "Parse failure for attachftm option\n");
|
||||
ast_test_status_update(test, "Parse failure for attachfmt option\n");
|
||||
res = 1;
|
||||
}
|
||||
if (strcasecmp(vmu->fromstring, "Voicemail System")) {
|
||||
|
@ -14199,7 +14123,7 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
|
|||
|
||||
if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
|
||||
val = "no";
|
||||
ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
|
||||
ast_set2_flag((&globalflags), ast_true(val), VM_DIRECTFORWARD);
|
||||
|
||||
if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
|
||||
val = "voicemail.conf";
|
||||
|
@ -15577,6 +15501,12 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
|
|||
}
|
||||
cmd = ast_play_and_wait(chan, "beep");
|
||||
}
|
||||
if (cmd == -1) {
|
||||
/* User has hung up, no options to give */
|
||||
ast_debug(1, "User hung up before message could be rerecorded\n");
|
||||
ast_filedelete(tempfile, NULL);
|
||||
return cmd;
|
||||
}
|
||||
recorded = 1;
|
||||
/* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
|
||||
if (record_gain)
|
||||
|
@ -15695,7 +15625,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
|
|||
}
|
||||
return cmd;
|
||||
default:
|
||||
/* If the caller is an ouside caller and the review option is enabled or it's forward intro
|
||||
/* If the caller is an outside caller and the review option is enabled or it's forward intro
|
||||
allow them to review the message, but let the owner of the box review
|
||||
their OGM's */
|
||||
if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro)
|
||||
|
@ -16071,7 +16001,7 @@ static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm
|
|||
* \param vms The voicemail state corresponding to an open mailbox
|
||||
* \param msg_ids An array of message identifiers
|
||||
* \param num_msgs The number of identifiers in msg_ids
|
||||
* \param msg_nums [out] The message indexes corresponding to the given
|
||||
* \param[out] msg_nums The message indexes corresponding to the given
|
||||
* \param vmu
|
||||
* message IDs
|
||||
* \pre vms must have open_mailbox() called on it prior to this function.
|
||||
|
|
|
@ -39,6 +39,11 @@
|
|||
|
||||
/*** DOCUMENTATION
|
||||
<application name="WaitForCondition" language="en_US">
|
||||
<since>
|
||||
<version>16.20.0</version>
|
||||
<version>18.6.0</version>
|
||||
<version>19.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Wait (sleep) until the given condition is true.
|
||||
</synopsis>
|
||||
|
@ -66,7 +71,7 @@
|
|||
<para>Waits until <replaceable>expression</replaceable> evaluates to true, checking every
|
||||
<replaceable>interval</replaceable> seconds for up to <replaceable>timeout</replaceable>. Default
|
||||
is evaluate <replaceable>expression</replaceable> every 50 milliseconds with no timeout.</para>
|
||||
<example title="Wait for ${condition} to become 1 for up to 40 seconds, checking every 500ms">
|
||||
<example title="Wait for condition dialplan variable/function to become 1 for up to 40 seconds, checking every 500ms">
|
||||
same => n,WaitForCondition(#,#["#{condition}"="1"],40,0.5)
|
||||
</example>
|
||||
<para>Sets <variable>WAITFORCONDITIONSTATUS</variable> to one of the following values:</para>
|
||||
|
|
|
@ -23,9 +23,6 @@
|
|||
*
|
||||
* \brief Wait for Silence
|
||||
* - Waits for up to 'x' milliseconds of silence, 'y' times \n
|
||||
* - WaitForSilence(500,2) will wait for 1/2 second of silence, twice \n
|
||||
* - WaitForSilence(1000,1) will wait for 1 second of silence, once \n
|
||||
* - WaitForSilence(300,3,10) will wait for 300ms of silence, 3 times, and return after 10sec \n
|
||||
*
|
||||
* \author David C. Troy <dave@popvox.com>
|
||||
*
|
||||
|
@ -78,11 +75,15 @@
|
|||
playing a message.</para>
|
||||
<para>Typically you will want to include two or more calls to WaitForSilence when dealing with an answering
|
||||
machine; first waiting for the spiel to finish, then waiting for the beep, etc.</para>
|
||||
<para>Examples:</para>
|
||||
<para>WaitForSilence(500,2) will wait for 1/2 second of silence, twice</para>
|
||||
<para>WaitForSilence(1000) will wait for 1 second of silence, once</para>
|
||||
<para>WaitForSilence(300,3,10) will wait for 300ms silence, 3 times, and returns after 10 sec, even if silence
|
||||
is not detected</para>
|
||||
<example title="Wait for half a second of silence, twice">
|
||||
same => n,WaitForSilence(500,2)
|
||||
</example>
|
||||
<example title="Wait for one second of silence, once">
|
||||
same => n,WaitForSilence(1000)
|
||||
</example>
|
||||
<example title="Wait for 300 ms of silence, 3 times, and returns after 10 seconds, even if no silence detected">
|
||||
same => n,WaitForSilence(300,3,10)
|
||||
</example>
|
||||
<para>Sets the channel variable <variable>WAITSTATUS</variable> to one of these values:</para>
|
||||
<variablelist>
|
||||
<variable name="WAITSTATUS">
|
||||
|
|
|
@ -97,6 +97,9 @@
|
|||
<configOption name="quiet">
|
||||
<synopsis>Silence enter/leave prompts and user intros for this user</synopsis>
|
||||
</configOption>
|
||||
<configOption name="hear_own_join_sound">
|
||||
<synopsis>Determines if the user also hears the join sound when they enter a conference</synopsis>
|
||||
</configOption>
|
||||
<configOption name="announce_user_count">
|
||||
<synopsis>Sets if the number of users should be announced to the user</synopsis>
|
||||
</configOption>
|
||||
|
@ -117,6 +120,9 @@
|
|||
<configOption name="end_marked">
|
||||
<synopsis>Kick the user from the conference when the last marked user leaves</synopsis>
|
||||
</configOption>
|
||||
<configOption name="end_marked_any">
|
||||
<synopsis>Kick the user from the conference when any marked user leaves</synopsis>
|
||||
</configOption>
|
||||
<configOption name="talk_detection_events">
|
||||
<synopsis>Set whether or not notifications of when a user begins and ends talking should be sent out as events over AMI</synopsis>
|
||||
</configOption>
|
||||
|
@ -384,7 +390,7 @@
|
|||
specific conference bridge.
|
||||
You should be aware that there are potential races between testing for the
|
||||
existence of a bridge, and taking action upon that information, consider
|
||||
for example two callers executing the check simultaniously, and then taking
|
||||
for example two callers executing the check simultaneously, and then taking
|
||||
special action as "first caller" into the bridge. The same for exiting,
|
||||
directly after the check the bridge can be destroyed before the new caller
|
||||
enters (creating a new bridge), for example, and the "first member" actions
|
||||
|
@ -459,7 +465,7 @@
|
|||
<enum name="sound_kicked"><para>The sound played to a user who has been kicked from the conference.</para></enum>
|
||||
<enum name="sound_muted"><para>The sound played when the mute option it toggled on.</para></enum>
|
||||
<enum name="sound_unmuted"><para>The sound played when the mute option it toggled off.</para></enum>
|
||||
<enum name="sound_binaural_on"><para>The sound played when binaural auudio is turned on.</para></enum>
|
||||
<enum name="sound_binaural_on"><para>The sound played when binaural audio is turned on.</para></enum>
|
||||
<enum name="sound_binaural_off"><para>The sound played when the binaural audio is turned off.</para></enum>
|
||||
<enum name="sound_only_person"><para>The sound played when the user is the only person in the conference.</para></enum>
|
||||
<enum name="sound_only_one"><para>The sound played to a user when there is only one other
|
||||
|
@ -1353,7 +1359,7 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
|
|||
comma = strchr(tmp_action_names, ',');
|
||||
|
||||
/* If the next action has brackets with comma delimited arguments in it,
|
||||
* make the delimeter ')' instead of a comma to preserve the argments */
|
||||
* make the delimeter ')' instead of a comma to preserve the arguments */
|
||||
if (startbrace && endbrace && comma && (comma > startbrace && comma < endbrace)) {
|
||||
delimiter = ")";
|
||||
} else {
|
||||
|
@ -1437,10 +1443,7 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
|
|||
|
||||
/* if adding any of the actions failed, bail */
|
||||
if (res) {
|
||||
struct conf_menu_action *menu_action;
|
||||
while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
|
||||
ast_free(menu_action);
|
||||
}
|
||||
conf_menu_entry_destroy(menu_entry);
|
||||
ast_free(menu_entry);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1449,6 +1452,7 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
|
|||
AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
|
||||
if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
conf_menu_entry_destroy(cur);
|
||||
ast_free(cur);
|
||||
break;
|
||||
}
|
||||
|
@ -1574,12 +1578,18 @@ static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, in
|
|||
ast_cli(a->fd,"Quiet: %s\n",
|
||||
u_profile.flags & USER_OPT_QUIET ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"Hear Join: %s\n",
|
||||
u_profile.flags & USER_OPT_HEAR_OWN_JOIN_SOUND ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"Wait Marked: %s\n",
|
||||
u_profile.flags & USER_OPT_WAITMARKED ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"END Marked: %s\n",
|
||||
ast_cli(a->fd,"END Marked (All): %s\n",
|
||||
u_profile.flags & USER_OPT_ENDMARKED ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"END Marked (Any): %s\n",
|
||||
u_profile.flags & USER_OPT_ENDMARKEDANY ?
|
||||
"enabled" : "disabled");
|
||||
ast_cli(a->fd,"Drop_silence: %s\n",
|
||||
u_profile.flags & USER_OPT_DROP_SILENCE ?
|
||||
"enabled" : "disabled");
|
||||
|
@ -2396,12 +2406,14 @@ int conf_load_config(void)
|
|||
aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED);
|
||||
aco_option_register(&cfg_info, "music_on_hold_when_empty", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MUSICONHOLD);
|
||||
aco_option_register(&cfg_info, "quiet", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_QUIET);
|
||||
aco_option_register(&cfg_info, "hear_own_join_sound", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_HEAR_OWN_JOIN_SOUND);
|
||||
aco_option_register_custom(&cfg_info, "announce_user_count_all", ACO_EXACT, user_types, "no", announce_user_count_all_handler, 0);
|
||||
aco_option_register(&cfg_info, "announce_user_count", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCEUSERCOUNT);
|
||||
/* Negative logic. Defaults to "yes" and evaluates with ast_false(). If !ast_false(), USER_OPT_NOONLYPERSON is cleared */
|
||||
aco_option_register(&cfg_info, "announce_only_user", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 0, FLDSET(struct user_profile, flags), USER_OPT_NOONLYPERSON);
|
||||
aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
|
||||
aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
|
||||
aco_option_register(&cfg_info, "end_marked_any", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKEDANY);
|
||||
aco_option_register(&cfg_info, "talk_detection_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_TALKER_DETECT);
|
||||
aco_option_register(&cfg_info, "dtmf_passthrough", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DTMF_PASS);
|
||||
aco_option_register(&cfg_info, "announce_join_leave", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCE_JOIN_LEAVE);
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
@ -52,8 +49,6 @@ void conf_invalid_event_fn(struct confbridge_user *user)
|
|||
* \brief Mute the user and play MOH if the user requires it.
|
||||
*
|
||||
* \param user Conference user to mute and optionally start MOH on.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void conf_mute_moh_inactive_waitmarked(struct confbridge_user *user)
|
||||
{
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
@ -85,37 +82,39 @@ static void leave_marked(struct confbridge_user *user)
|
|||
|
||||
conf_remove_user_marked(user->conference, user);
|
||||
|
||||
if (user->conference->markedusers == 0) {
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&user->conference->active_list, user_iter, list) {
|
||||
/* Kick ENDMARKED cbu_iters */
|
||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKED) && !user_iter->kicked) {
|
||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
AST_LIST_REMOVE_CURRENT(list);
|
||||
user_iter->conference->activeusers--;
|
||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||
user_iter->conference->waitingusers++;
|
||||
}
|
||||
user_iter->kicked = 1;
|
||||
pbx_builtin_setvar_helper(user_iter->chan, "CONFBRIDGE_RESULT", "ENDMARKED");
|
||||
ast_bridge_remove(user_iter->conference->bridge, user_iter->chan);
|
||||
} else if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
need_prompt = 1;
|
||||
|
||||
/* If all marked users have left, or we're set to kick if any marked user leaves, then boot everyone */
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&user->conference->active_list, user_iter, list) {
|
||||
if (user->conference->markedusers > 0 && !ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY)) {
|
||||
continue;
|
||||
}
|
||||
/* Kick ENDMARKED cbu_iters */
|
||||
if ((ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKED) || ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY)) && !user_iter->kicked) {
|
||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& (!ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER) || ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY))) {
|
||||
AST_LIST_REMOVE_CURRENT(list);
|
||||
user_iter->conference->activeusers--;
|
||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||
user_iter->conference->waitingusers++;
|
||||
} else {
|
||||
/* User is neither wait_marked nor end_marked; however, they
|
||||
* should still hear the prompt.
|
||||
*/
|
||||
need_prompt = 1;
|
||||
}
|
||||
user_iter->kicked = 1;
|
||||
pbx_builtin_setvar_helper(user_iter->chan, "CONFBRIDGE_RESULT", "ENDMARKED");
|
||||
ast_bridge_remove(user_iter->conference->bridge, user_iter->chan);
|
||||
} else if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
need_prompt = 1;
|
||||
|
||||
AST_LIST_REMOVE_CURRENT(list);
|
||||
user_iter->conference->activeusers--;
|
||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||
user_iter->conference->waitingusers++;
|
||||
} else {
|
||||
/* User is neither wait_marked nor end_marked nor end_marked_any; however, they
|
||||
* should still hear the prompt.
|
||||
*/
|
||||
need_prompt = 1;
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
|
||||
switch (user->conference->activeusers) {
|
||||
case 0:
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
|
|
@ -70,6 +70,8 @@ enum user_profile_flags {
|
|||
USER_OPT_ECHO_EVENTS = (1 << 18), /*!< Send events only to the admin(s) */
|
||||
USER_OPT_TEXT_MESSAGING = (1 << 19), /*!< Send text messages to the user */
|
||||
USER_OPT_ANSWER_CHANNEL = (1 << 20), /*!< Sets if the channel should be answered if currently unanswered */
|
||||
USER_OPT_HEAR_OWN_JOIN_SOUND = (1 << 21), /*!< Set if the caller should hear the join sound */
|
||||
USER_OPT_ENDMARKEDANY = (1 << 22), /*!< Set if the user should be kicked after any marked user exits */
|
||||
};
|
||||
|
||||
enum bridge_profile_flags {
|
||||
|
@ -457,8 +459,6 @@ void conf_ended(struct confbridge_conference *conference);
|
|||
* \brief Update the actual mute status of the user and set it on the bridge.
|
||||
*
|
||||
* \param user User to update the mute status.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void conf_update_user_mute(struct confbridge_user *user);
|
||||
|
||||
|
@ -466,8 +466,6 @@ void conf_update_user_mute(struct confbridge_user *user);
|
|||
* \brief Stop MOH for the conference user.
|
||||
*
|
||||
* \param user Conference user to stop MOH on.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void conf_moh_stop(struct confbridge_user *user);
|
||||
|
||||
|
@ -475,8 +473,6 @@ void conf_moh_stop(struct confbridge_user *user);
|
|||
* \brief Start MOH for the conference user.
|
||||
*
|
||||
* \param user Conference user to start MOH on.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
void conf_moh_start(struct confbridge_user *user);
|
||||
|
||||
|
@ -487,8 +483,6 @@ void conf_mute_only_active(struct confbridge_conference *conference);
|
|||
|
||||
/*! \brief Callback to execute any time we transition from zero to one active users
|
||||
* \param conference The conference bridge with a single active user joined
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
void conf_handle_first_join(struct confbridge_conference *conference);
|
||||
|
||||
|
@ -702,7 +696,7 @@ int conf_announce_channel_push(struct ast_channel *ast);
|
|||
* \since 13.22.0
|
||||
* \since 15.5.0
|
||||
*
|
||||
* \param confbridge_name The name to search for
|
||||
* \param conference_name The name to search for
|
||||
*
|
||||
* \return ConfBridge (which must be unreffed) or NULL.
|
||||
*/
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue