Compare commits
514 Commits
Author | SHA1 | Date |
---|---|---|
George Joseph | 7b5fff3b20 | |
George Joseph | 334f4b01bb | |
George Joseph | b9fdfaf0cb | |
George Joseph | 2ce533ca84 | |
George Joseph | ece20bf69b | |
George Joseph | a8b01ed8ec | |
George Joseph | 1379e048da | |
George Joseph | c10c1ca4e2 | |
George Joseph | d6e764c496 | |
George Joseph | 64d67349b9 | |
George Joseph | e292c66b1a | |
George Joseph | fb1eee2fef | |
George Joseph | cf116ea187 | |
Sean Bright | 70c551e3bb | |
George Joseph | fcbeaba5ea | |
George Joseph | 898014ab7f | |
George Joseph | f45fd46190 | |
George Joseph | a5c4f3e567 | |
George Joseph | fcaa1ba181 | |
George Joseph | 29570120f2 | |
George Joseph | 1d6de5d77b | |
George Joseph | 4a250c8834 | |
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 |
|
@ -77,7 +77,7 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
[Asterisk Issue Guidelines](https://docs.asterisk.org/Asterisk-Community/Asterisk-Issue-Guidelines/)
|
||||
[Asterisk Issue Guidelines](https://wiki.asterisk.org/wiki/display/AST/Asterisk+Issue+Guidelines)
|
||||
- type: checkboxes
|
||||
id: guidelines
|
||||
attributes:
|
||||
|
|
|
@ -3,9 +3,6 @@ contact_links:
|
|||
- name: Asterisk Community Support
|
||||
url: https://community.asterisk.org
|
||||
about: Please ask and answer questions here.
|
||||
- name: Feature Requests (Without Code)
|
||||
- name: Feature Requests
|
||||
url: https://github.com/asterisk/asterisk-feature-requests/issues
|
||||
about: Please submit feature requests (without code) here.
|
||||
- name: Improvement Requests (Without Code)
|
||||
url: https://github.com/asterisk/asterisk-feature-requests/issues
|
||||
about: Please submit improvement requests (without code) here.
|
||||
about: Please submit feature requests here.
|
||||
|
|
|
@ -131,7 +131,7 @@ jobs:
|
|||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
testsuite_repo: ${{vars.TESTSUITE_REPO}}
|
||||
gatetest_group: ${{matrix.group}}
|
||||
gatetest_command: ${{ toJSON(fromJSON(vars.GATETEST_COMMANDS)[matrix.group]) }}
|
||||
gatetest_commands: ${{vars.GATETEST_COMMANDS}}
|
||||
|
||||
CherryPickGateTests:
|
||||
needs: [ IdentifyBranches, CherryPickGateTestMatrix ]
|
||||
|
|
|
@ -123,6 +123,9 @@ jobs:
|
|||
MergeAndCherryPick:
|
||||
needs: [ IdentifyBranches, PreMergeUnitTests ]
|
||||
if: success()
|
||||
concurrency:
|
||||
group: MergeAndCherryPick
|
||||
cancel-in-progress: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Start Merge
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
name: NightlyAdmin
|
||||
name: Nightly Admin
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
ASTERISK_REPO: ${{ github.repository }}
|
||||
|
@ -14,6 +13,16 @@ env:
|
|||
|
||||
jobs:
|
||||
CloseStaleIssues:
|
||||
uses: asterisk/asterisk-ci-actions/.github/workflows/CloseStaleIssuesAndPRs.yml@main
|
||||
secrets:
|
||||
ASTERISKTEAM_PAT: ${{ secrets.ASTERISKTEAM_PAT }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close Stale Issues
|
||||
uses: actions/stale@v7
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale because it has been open 7 days with no activity. Remove stale label or comment or this will be closed in 14 days.'
|
||||
stale-issue-label: stale
|
||||
close-issue-message: 'This issue was closed because it has been stalled for 14 days with no activity.'
|
||||
days-before-stale: 7
|
||||
days-before-close: 14
|
||||
days-before-pr-close: -1
|
||||
only-labels: triage,feedback-required
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
testsuite_repo: ${{vars.TESTSUITE_REPO}}
|
||||
gatetest_group: ${{matrix.group}}
|
||||
gatetest_command: ${{ toJSON(fromJSON(vars.GATETEST_COMMANDS)[matrix.group]) }}
|
||||
gatetest_commands: ${{vars.GATETEST_COMMANDS}}
|
||||
|
||||
AsteriskNightlyTests:
|
||||
if: ${{ always() }}
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
name: PROpenedOrUpdated
|
||||
run-name: "PR ${{github.event.number}} ${{github.event.action}} by ${{ github.actor }}"
|
||||
on:
|
||||
# workflow_dispatch:
|
||||
pull_request_target:
|
||||
# types: [opened, reopened, synchronize]
|
||||
types: [labeled]
|
||||
|
||||
env:
|
||||
ASTERISK_REPO: ${{github.repository}}
|
||||
PR_NUMBER: ${{github.event.number}}
|
||||
PR_COMMIT: ${{github.event.pull_request.head.sha}}
|
||||
BRANCH: ${{github.event.pull_request.base.ref}}
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
MODULES_BLACKLIST: ${{vars.GATETEST_MODULES_BLACKLIST}} ${{vars.UNITTEST_MODULES_BLACKLIST}}
|
||||
|
||||
jobs:
|
||||
|
||||
PRTestSetup:
|
||||
if: ${{ github.event.label.name == vars.PR_ACCEPTANCE_TEST_LABEL }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Job Start Delay
|
||||
env:
|
||||
JOB_START_DELAY_SEC: ${{vars.PR_JOB_START_DELAY_SEC}}
|
||||
run: |
|
||||
# Give the user a chance to add their "cherry-pick-to" comments
|
||||
sleep ${JOB_START_DELAY_SEC:-60}
|
||||
|
||||
- name: Get Token needed to add reviewers
|
||||
if: github.event.action == 'opened'
|
||||
id: get_workflow_token
|
||||
uses: peter-murray/workflow-application-token-action@v2
|
||||
with:
|
||||
application_id: ${{secrets.ASTERISK_ORG_ACCESS_APP_ID}}
|
||||
application_private_key: ${{secrets.ASTERISK_ORG_ACCESS_APP_PRIV_KEY}}
|
||||
organization: asterisk
|
||||
|
||||
- name: Get cherry-pick branches
|
||||
uses: asterisk/asterisk-ci-actions/GetCherryPickBranchesFromPR@main
|
||||
id: getbranches
|
||||
with:
|
||||
repo: ${{github.repository}}
|
||||
pr_number: ${{env.PR_NUMBER}}
|
||||
cherry_pick_regex: ${{vars.CHERRY_PICK_REGEX}}
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
- name: Add cherry-pick reminder
|
||||
env:
|
||||
GITHUB_TOKEN: ${{steps.get_workflow_token.outputs.token}}
|
||||
GH_TOKEN: ${{steps.get_workflow_token.outputs.token}}
|
||||
CHERRY_PICK_REMINDER: ${{vars.CHERRY_PICK_REMINDER}}
|
||||
BRANCHES_OUTPUT: ${{toJSON(steps.getbranches.outputs)}}
|
||||
BRANCH_COUNT: ${{steps.getbranches.outputs.branch_count}}
|
||||
FORCED_NONE: ${{steps.getbranches.outputs.forced_none}}
|
||||
run: |
|
||||
# If the user already added "cherry-pick-to" comments
|
||||
# we don't need to remind them.
|
||||
( $FORCED_NONE || [ $BRANCH_COUNT -gt 0 ] ) && { echo "No reminder needed." ; exit 0 ; }
|
||||
IFS=$'; \n'
|
||||
# If there's already a reminder comment, don't add another one.
|
||||
ADD_COMMENT=true
|
||||
# This query will FAIL if it finds the comment.
|
||||
gh pr view --repo ${{github.repository}} --json comments \
|
||||
--jq '.comments[].body | select(. | startswith("<!--CPR-->")) | halt_error(1)' \
|
||||
${{env.PR_NUMBER}} >/dev/null 2>&1 || ADD_COMMENT=false
|
||||
if $ADD_COMMENT ; then
|
||||
echo "Adding CPR comment"
|
||||
gh pr comment --repo ${{github.repository}} \
|
||||
-b "${CHERRY_PICK_REMINDER}" ${{env.PR_NUMBER}}
|
||||
else
|
||||
echo "CPR comment already present"
|
||||
fi
|
||||
|
||||
- name: Add reviewers
|
||||
if: github.event.action == 'opened'
|
||||
env:
|
||||
GH_TOKEN: ${{steps.get_workflow_token.outputs.token}}
|
||||
REVIEWERS: ${{vars.PR_REVIEWERS}}
|
||||
run: |
|
||||
IFS=$'; \n'
|
||||
for r in $REVIEWERS ; do
|
||||
echo "Adding reviewer $r"
|
||||
gh pr edit --repo ${{github.repository}} ${PR_NUMBER} --add-reviewer $r || :
|
||||
done
|
||||
|
||||
- name: Set Labels
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh pr edit --repo ${{github.repository}} \
|
||||
--remove-label ${{vars.TEST_CHECKS_PASSED_LABEL}} \
|
||||
--remove-label ${{vars.TEST_CHECKS_FAILED_LABEL}} \
|
||||
--remove-label ${{vars.TEST_GATES_PASSED_LABEL}} \
|
||||
--remove-label ${{vars.TEST_GATES_FAILED_LABEL}} \
|
||||
--remove-label ${{vars.CHERRY_PICK_CHECKS_PASSED_LABEL}} \
|
||||
--remove-label ${{vars.CHERRY_PICK_CHECKS_FAILED_LABEL}} \
|
||||
--remove-label ${{vars.CHERRY_PICK_GATES_PASSED_LABEL}} \
|
||||
--remove-label ${{vars.CHERRY_PICK_GATES_FAILED_LABEL}} \
|
||||
--remove-label ${{vars.PR_ACCEPTANCE_TEST_LABEL}} \
|
||||
--add-label ${{vars.TESTING_IN_PROGRESS}} \
|
||||
${{env.PR_NUMBER}} || :
|
||||
|
||||
PRUnitTest:
|
||||
needs: PRTestSetup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run Unit Tests
|
||||
id: run_unit_tests
|
||||
uses: asterisk/asterisk-ci-actions/AsteriskUnitComposite@main
|
||||
with:
|
||||
asterisk_repo: ${{env.ASTERISK_REPO}}
|
||||
pr_number: ${{env.PR_NUMBER}}
|
||||
base_branch: ${{env.BRANCH}}
|
||||
modules_blacklist: ${{env.MODULES_BLACKLIST}}
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
unittest_command: ${{vars.UNITTEST_COMMAND}}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Post Unit Test
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CONCLUSION: ${{ steps.run_unit_tests.conclusion }}
|
||||
OUTCOME: ${{ steps.run_unit_tests.outcome }}
|
||||
run: |
|
||||
if [ "$OUTCOME" == "success" ] ; then
|
||||
gh pr edit --repo ${{github.repository}} \
|
||||
--add-label ${{vars.TEST_CHECKS_PASSED_LABEL}} \
|
||||
${{env.PR_NUMBER}} || :
|
||||
exit 0
|
||||
fi
|
||||
gh pr edit --repo ${{github.repository}} \
|
||||
--remove-label ${{vars.TESTING_IN_PROGRESS}} \
|
||||
--add-label ${{vars.TEST_CHECKS_FAILED_LABEL}} \
|
||||
${{env.PR_NUMBER}} || :
|
||||
exit 1
|
||||
|
||||
PRGateTestMatrix:
|
||||
needs: PRUnitTest
|
||||
continue-on-error: false
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
group: ${{ fromJSON(vars.GATETEST_LIST) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: runtest
|
||||
name: Run Gate Tests for ${{ matrix.group }}
|
||||
uses: asterisk/asterisk-ci-actions/AsteriskGateComposite@main
|
||||
with:
|
||||
test_type: Gate
|
||||
asterisk_repo: ${{env.ASTERISK_REPO}}
|
||||
pr_number: ${{env.PR_NUMBER}}
|
||||
base_branch: ${{env.BRANCH}}
|
||||
modules_blacklist: ${{env.MODULES_BLACKLIST}}
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
testsuite_repo: ${{vars.TESTSUITE_REPO}}
|
||||
gatetest_group: ${{matrix.group}}
|
||||
gatetest_commands: ${{vars.GATETEST_COMMANDS}}
|
||||
|
||||
PRPRGateTests:
|
||||
if: ${{ always() && github.event.label.name == vars.PR_ACCEPTANCE_TEST_LABEL }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: PRGateTestMatrix
|
||||
steps:
|
||||
- name: Check gate test matrix status
|
||||
env:
|
||||
RESULT: ${{ needs.PRGateTestMatrix.result }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
echo "all results: ${{ toJSON(needs.*.result) }}"
|
||||
echo "composite result: $RESULT"
|
||||
|
||||
gh pr edit --repo ${{github.repository}} \
|
||||
--remove-label ${{vars.TESTING_IN_PROGRESS}} \
|
||||
${{env.PR_NUMBER}} || :
|
||||
|
||||
case $RESULT in
|
||||
success)
|
||||
gh pr edit --repo ${{github.repository}} \
|
||||
--add-label ${{vars.TEST_GATES_PASSED_LABEL}} \
|
||||
${{env.PR_NUMBER}} || :
|
||||
echo "::notice::All Testsuite tests passed"
|
||||
exit 0
|
||||
;;
|
||||
skipped)
|
||||
gh pr edit --repo ${{github.repository}} \
|
||||
--add-label ${{vars.TEST_CHECKS_FAILED_LABEL}} \
|
||||
${{env.PR_NUMBER}} || :
|
||||
echo "::error::Testsuite tests were skipped because of an earlier failure"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
gh pr edit --repo ${{github.repository}} \
|
||||
--add-label ${{vars.TEST_GATES_FAILED_LABEL}} \
|
||||
${{env.PR_NUMBER}} || :
|
||||
echo "::error::One or more Testsuite tests failed ($RESULT)"
|
||||
exit 1
|
||||
esac
|
|
@ -1,149 +0,0 @@
|
|||
name: PRSubmitActions
|
||||
run-name: "PRSubmitActions: Test ${{github.event.action}}"
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [PRSubmitTests]
|
||||
types:
|
||||
- requested
|
||||
- completed
|
||||
env:
|
||||
ACTION: ${{ github.event.action }}
|
||||
CONCLUSION: ${{ github.event.workflow_run.conclusion }}
|
||||
REPO: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
PRSubmitActions:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get PR Number
|
||||
id: getpr
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
retries: 5
|
||||
script: |
|
||||
let search = `repo:${context.repo.owner}/${context.repo.repo} ${context.payload.workflow_run.head_sha}`;
|
||||
let prs = await github.rest.search.issuesAndPullRequests({
|
||||
q: search,
|
||||
});
|
||||
if (prs.data.total_count == 0) {
|
||||
core.setFailed(`Unable to get PR for ${context.payload.workflow_run.head_sha}`);
|
||||
return;
|
||||
}
|
||||
let pr_number = prs.data.items[0].number;
|
||||
core.setOutput('pr_number', pr_number);
|
||||
return;
|
||||
|
||||
- name: Set Label
|
||||
id: setlabel
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{ steps.getpr.outputs.PR_NUMBER }}
|
||||
LABEL_TIP: ${{ vars.PR_SUBMIT_TESTING_IN_PROGRESS }}
|
||||
LABEL_PASS: ${{ vars.PR_SUBMIT_TESTS_PASSED }}
|
||||
LABEL_FAIL: ${{ vars.PR_SUBMIT_TESTS_FAILED }}
|
||||
with:
|
||||
retries: 5
|
||||
script: |
|
||||
let label;
|
||||
if (process.env.ACTION === 'requested') {
|
||||
label = process.env.LABEL_TIP;
|
||||
} else {
|
||||
if ( process.env.CONCLUSION === 'success' ) {
|
||||
label = process.env.LABEL_PASS;
|
||||
} else {
|
||||
label = process.env.LABEL_FAIL;
|
||||
}
|
||||
}
|
||||
core.info(`Setting label ${label}`);
|
||||
github.rest.issues.setLabels({
|
||||
issue_number: process.env.PR_NUMBER,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: [ label ]
|
||||
});
|
||||
return;
|
||||
|
||||
- name: Get cherry-pick branches
|
||||
if: github.event.action == 'completed'
|
||||
id: getbranches
|
||||
uses: asterisk/asterisk-ci-actions/GetCherryPickBranchesFromPR@main
|
||||
with:
|
||||
repo: ${{env.REPO}}
|
||||
pr_number: ${{steps.getpr.outputs.PR_NUMBER}}
|
||||
cherry_pick_regex: ${{vars.CHERRY_PICK_REGEX}}
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
- name: Add cherry-pick reminder
|
||||
if: github.event.action == 'completed'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{steps.getpr.outputs.PR_NUMBER}}
|
||||
CHERRY_PICK_REMINDER: ${{vars.CHERRY_PICK_REMINDER}}
|
||||
BRANCHES_OUTPUT: ${{toJSON(steps.getbranches.outputs)}}
|
||||
BRANCH_COUNT: ${{steps.getbranches.outputs.branch_count}}
|
||||
FORCED_NONE: ${{steps.getbranches.outputs.forced_none}}
|
||||
with:
|
||||
retries: 5
|
||||
script: |
|
||||
if (process.env.FORCED_NONE === 'true' ||
|
||||
process.env.BRANCH_COUNT > 0) {
|
||||
core.info("No cherry-pick reminder needed.");
|
||||
return;
|
||||
}
|
||||
let comments = await github.rest.issues.listComments({
|
||||
issue_number: process.env.PR_NUMBER,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
let found = false;
|
||||
for (const c of comments.data) {
|
||||
if (c.body.startsWith("<!--CPR-->")) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
core.info("Cherry-pick reminder already exists.");
|
||||
return;
|
||||
}
|
||||
core.info("Adding cherry-pick reminder.");
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: process.env.PR_NUMBER,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: process.env.CHERRY_PICK_REMINDER
|
||||
})
|
||||
return;
|
||||
|
||||
- name: Add reviewers
|
||||
if: github.event.action == 'completed'
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER: ${{steps.getpr.outputs.PR_NUMBER}}
|
||||
REVIEWERS: ${{vars.PR_REVIEWERS}}
|
||||
with:
|
||||
retries: 5
|
||||
github-token: ${{ secrets.ASTERISKTEAM_PAT }}
|
||||
script: |
|
||||
let rs = JSON.parse(process.env.REVIEWERS.length ? process.env.REVIEWERS : '[]');
|
||||
let users = [];
|
||||
let teams = [];
|
||||
for (const r of rs) {
|
||||
if (r.indexOf("/") > 0) {
|
||||
teams.push(r.split('/')[1]);
|
||||
} else {
|
||||
users.push(r);
|
||||
}
|
||||
}
|
||||
if (teams.length > 0 || users.length > 0) {
|
||||
core.info(`Adding user reviewers ${users}`);
|
||||
core.info(`Adding team reviewers ${teams}`);
|
||||
await github.rest.pulls.requestReviewers({
|
||||
pull_number: process.env.PR_NUMBER,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
reviewers: users,
|
||||
team_reviewers: teams
|
||||
});
|
||||
}
|
||||
return;
|
|
@ -1,114 +0,0 @@
|
|||
name: PRSubmitTests
|
||||
run-name: "PR ${{github.event.number}} ${{github.event.action}} by ${{ github.actor }}"
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
concurrency:
|
||||
group: ${{github.workflow}}-${{github.event.number}}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
ASTERISK_REPO: ${{github.repository}}
|
||||
PR_NUMBER: ${{github.event.number}}
|
||||
PR_COMMIT: ${{github.event.pull_request.head.sha}}
|
||||
BRANCH: ${{github.event.pull_request.base.ref}}
|
||||
|
||||
jobs:
|
||||
#
|
||||
# Pull requests created from forked respositories don't have access to
|
||||
# the "Action Variables" ('vars' context) so we need to retrieve control
|
||||
# data from an action.
|
||||
#
|
||||
PRSGetControlData:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
control_data: ${{ steps.setvars.outputs.control_data }}
|
||||
steps:
|
||||
- id: setvars
|
||||
uses: asterisk/asterisk-ci-actions/GetRepoControlData@main
|
||||
with:
|
||||
repo: ${{ github.event.repository.name}}
|
||||
- name: DumpEnvironment
|
||||
uses: asterisk/asterisk-ci-actions/DumpEnvironmentAction@main
|
||||
with:
|
||||
action-inputs: ${{toJSON(inputs)}}
|
||||
action-vars: ${{ toJSON(steps.setvars.outputs) }}
|
||||
|
||||
PRSUnitTests:
|
||||
needs: PRSGetControlData
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
UNITTEST_COMMAND: ${{ fromJSON(needs.PRSGetControlData.outputs.control_data).UNITTEST_COMMAND }}
|
||||
steps:
|
||||
- name: Run Unit Tests
|
||||
uses: asterisk/asterisk-ci-actions/AsteriskUnitComposite@main
|
||||
with:
|
||||
asterisk_repo: ${{env.ASTERISK_REPO}}
|
||||
pr_number: ${{env.PR_NUMBER}}
|
||||
base_branch: ${{env.BRANCH}}
|
||||
unittest_command: ${{env.UNITTEST_COMMAND}}
|
||||
|
||||
PRSGateTestMatrix:
|
||||
runs-on: ubuntu-latest
|
||||
needs: PRSGetControlData
|
||||
continue-on-error: false
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
group: ${{ fromJSON(fromJSON(needs.PRSGetControlData.outputs.control_data).GATETEST_LIST) }}
|
||||
env:
|
||||
TESTSUITE_REPO: "${{ fromJSON(needs.PRSGetControlData.outputs.control_data).TESTSUITE_REPO }}"
|
||||
GATETEST_COMMANDS: "${{ fromJSON(needs.PRSGetControlData.outputs.control_data).GATETEST_COMMANDS }}"
|
||||
GATETEST_COMMAND: "${{ toJSON(fromJSON(fromJSON(needs.PRSGetControlData.outputs.control_data).GATETEST_COMMANDS)[matrix.group]) }}"
|
||||
steps:
|
||||
- id: runtest
|
||||
name: Run Gate Tests for ${{ matrix.group }}
|
||||
uses: asterisk/asterisk-ci-actions/AsteriskGateComposite@main
|
||||
with:
|
||||
test_type: Gate
|
||||
asterisk_repo: ${{env.ASTERISK_REPO}}
|
||||
pr_number: ${{env.PR_NUMBER}}
|
||||
base_branch: ${{env.BRANCH}}
|
||||
testsuite_repo: ${{env.TESTSUITE_REPO}}
|
||||
gatetest_group: ${{matrix.group}}
|
||||
gatetest_command: ${{env.GATETEST_COMMAND}}
|
||||
|
||||
PRSTestResults:
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
needs: [PRSUnitTests,PRSGateTestMatrix]
|
||||
steps:
|
||||
- name: Check test matrix status
|
||||
env:
|
||||
RESULT_UNIT: ${{ needs.PRSUnitTests.result }}
|
||||
RESULT_GATE: ${{ needs.PRSGateTestMatrix.result }}
|
||||
run: |
|
||||
declare -i rc=0
|
||||
echo "all results: ${{ toJSON(needs.*.result) }}"
|
||||
case $RESULT_UNIT in
|
||||
success)
|
||||
echo "::notice::Unit tests passed"
|
||||
;;
|
||||
skipped)
|
||||
echo "::error::Unit tests were skipped because of an earlier failure"
|
||||
rc+=1
|
||||
;;
|
||||
*)
|
||||
echo "::error::One or more unit tests failed ($RESULT_UNIT)"
|
||||
rc+=1
|
||||
esac
|
||||
case $RESULT_GATE in
|
||||
success)
|
||||
echo "::notice::Gate tests passed"
|
||||
;;
|
||||
skipped)
|
||||
echo "::error::Gate tests were skipped because of an earlier failure"
|
||||
rc+=1
|
||||
;;
|
||||
*)
|
||||
echo "::error::One or more gate tests failed ($RESULT_GATE)"
|
||||
rc+=1
|
||||
esac
|
||||
echo "::notice::Final result code: $rc"
|
||||
exit $rc
|
|
@ -1,3 +1,4 @@
|
|||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: Releaser
|
||||
run-name: ${{ github.actor }} is creating ${{vars.PRODUCT_NAME}} release ${{inputs.new_version}}
|
||||
on:
|
||||
|
@ -11,6 +12,13 @@ on:
|
|||
certified-20.4-cert1-rc1, certified-20.4-cert1
|
||||
required: true
|
||||
type: string
|
||||
# start_version:
|
||||
# description: |
|
||||
# Last Version:
|
||||
# Only use when you KNOW that the automated
|
||||
# process won't get it right.
|
||||
# required: false
|
||||
# type: string
|
||||
is_security:
|
||||
description: |
|
||||
Security?
|
||||
|
@ -32,12 +40,6 @@ on:
|
|||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
force_cherry_pick:
|
||||
description: |
|
||||
Force cherry-pick for non-RC1 releases? USE WITH CAUTION!
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
push_release_branches:
|
||||
description: |
|
||||
Push release branches live?
|
||||
|
@ -75,7 +77,7 @@ jobs:
|
|||
advisories: ${{inputs.advisories}}
|
||||
is_hotfix: ${{inputs.is_hotfix}}
|
||||
new_version: ${{inputs.new_version}}
|
||||
force_cherry_pick: ${{inputs.force_cherry_pick}}
|
||||
# start_version: ${{inputs.start_version}}
|
||||
push_release_branches: ${{inputs.push_release_branches}}
|
||||
create_github_release: ${{inputs.create_github_release}}
|
||||
push_tarballs: ${{inputs.push_tarballs}}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
[gerrit]
|
||||
defaultbranch=19
|
||||
basebranch=19
|
||||
#
|
||||
# Intentional padding to ensure it is possible to point a commit
|
||||
# to an alternative gerrit server/repository without breaking
|
||||
# cherry-pick between branches.
|
||||
#
|
||||
host=gerrit.asterisk.org
|
||||
port=29418
|
||||
project=asterisk.git
|
10
BUGS
10
BUGS
|
@ -1,22 +1,22 @@
|
|||
Asterisk Bug Tracking Information
|
||||
=================================
|
||||
|
||||
To learn about and report Asterisk bugs, please visit
|
||||
To learn about and report Asterisk bugs, please visit
|
||||
the official Asterisk Bug Tracker at:
|
||||
|
||||
https://github.com/asterisk/asterisk/issues/
|
||||
https://issues.asterisk.org/jira
|
||||
|
||||
For more information on using the bug tracker, or to
|
||||
For more information on using the bug tracker, or to
|
||||
learn how you can contribute by acting as a bug marshal
|
||||
please see:
|
||||
|
||||
https://docs.asterisk.org/Asterisk-Community/Asterisk-Issue-Guidelines/
|
||||
http://www.asterisk.org/developers/bug-guidelines
|
||||
|
||||
If you would like to submit a feature request, please
|
||||
resist the temptation to post it to the bug tracker.
|
||||
Feature requests should be posted to the asterisk-dev
|
||||
mailing list, located at:
|
||||
|
||||
http://lists.digium.com
|
||||
http://lists.digium.com
|
||||
|
||||
Thank you!
|
||||
|
|
2
LICENSE
2
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:
|
||||
|
||||
https://www.sangoma.com/wp-content/uploads/Sangoma-Trademark-Policy-1.pdf
|
||||
https://www.sangoma.com/wp-content/uploads/Sangoma-Trademark-Policy.pdf
|
||||
|
||||
If you have any questions regarding our licensing policy, please
|
||||
contact us:
|
||||
|
|
6
Makefile
6
Makefile
|
@ -377,7 +377,7 @@ $(MOD_SUBDIRS_MENUSELECT_TREE):
|
|||
+@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) moduleinfo
|
||||
+@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) makeopts
|
||||
|
||||
$(SUBDIRS): makeopts .lastclean main/version.c include/asterisk/build.h defaults.h
|
||||
$(SUBDIRS): makeopts .lastclean main/version.c include/asterisk/build.h include/asterisk/buildopts.h defaults.h
|
||||
|
||||
ifeq ($(findstring $(OSARCH), mingw32 cygwin ),)
|
||||
main: third-party
|
||||
|
@ -403,7 +403,7 @@ defaults.h: makeopts .lastclean build_tools/make_defaults_h
|
|||
@cmp -s $@.tmp $@ || mv $@.tmp $@
|
||||
@rm -f $@.tmp
|
||||
|
||||
main/version.c: FORCE include/asterisk/buildopts.h menuselect.makeopts .lastclean
|
||||
main/version.c: FORCE menuselect.makeopts .lastclean
|
||||
@build_tools/make_version_c > $@.tmp
|
||||
@cmp -s $@.tmp $@ || mv $@.tmp $@
|
||||
@rm -f $@.tmp
|
||||
|
@ -545,7 +545,7 @@ INSTALLDIRS="$(ASTLIBDIR)" "$(ASTMODDIR)" "$(ASTSBINDIR)" "$(ASTCACHEDIR)" "$(AS
|
|||
"$(ASTDATADIR)/firmware/iax" "$(ASTDATADIR)/images" "$(ASTDATADIR)/keys" \
|
||||
"$(ASTDATADIR)/phoneprov" "$(ASTDATADIR)/rest-api" "$(ASTDATADIR)/static-http" \
|
||||
"$(ASTDATADIR)/sounds" "$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)" \
|
||||
"$(ASTDATADIR)/third-party" "${ASTDATADIR}/keys/stir_shaken" "${ASTDATADIR}/keys/stir_shaken/cache"
|
||||
"$(ASTDATADIR)/third-party" "${ASTDATADIR}/keys/stir_shaken"
|
||||
|
||||
installdirs:
|
||||
@for i in $(INSTALLDIRS); do \
|
||||
|
|
|
@ -66,7 +66,7 @@ OPTIMIZE?=-O3
|
|||
|
||||
ifneq ($(findstring darwin,$(OSARCH)),)
|
||||
ifeq ($(shell if test `/usr/bin/sw_vers -productVersion | cut -c4` -gt 5; then echo 6; else echo 0; fi),6)
|
||||
# Snow Leopard/Lion has an issue with this optimization flag on large files
|
||||
# Snow Leopard/Lion has an issue with this optimization flag on large files (like chan_sip)
|
||||
OPTIMIZE+=-fno-inline-functions
|
||||
endif
|
||||
endif
|
||||
|
@ -213,10 +213,10 @@ endif
|
|||
# extern const size_t _binary_abc_def_xml_size;
|
||||
%.o: %.xml
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CC) -g -Wl,-znoexecstack -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
|
||||
%.o: %.xslt
|
||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||
$(CMD_PREFIX) $(CC) -g -Wl,-znoexecstack -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||
|
||||
dist-clean:: clean
|
||||
|
|
|
@ -52,28 +52,28 @@ request.
|
|||
```INI
|
||||
[incoming]
|
||||
exten => _X.,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _X.,n,Dial(PJSIP/${EXTEN})
|
||||
exten => _X.,n,Dial(SIP/${EXTEN})
|
||||
exten => _X.,n,Hangup()
|
||||
```
|
||||
|
||||
This dialplan may be utilized to accept calls to extensions, which then dial a
|
||||
numbered device name configured in one of the channel configuration files (such
|
||||
as pjsip.conf, iax.conf, etc...) (see [Proper Device Naming] for more information
|
||||
as sip.conf, iax.conf, etc...) (see [Proper Device Naming] for more information
|
||||
on why this approach is flawed).
|
||||
|
||||
The example we've given above looks harmless enough until you take into
|
||||
consideration that several channel technologies accept characters that could
|
||||
be utilized in a clever attack. For example, instead of just sending a request
|
||||
to dial extension 500 (which in our example above would create the string
|
||||
PJSIP/500 and is then used by the Dial() application to place a call), someone
|
||||
could potentially send a string like "500&PJSIP/itsp/14165551212".
|
||||
SIP/500 and is then used by the Dial() application to place a call), someone
|
||||
could potentially send a string like "500&SIP/itsp/14165551212".
|
||||
|
||||
The string "500&PJSIP/itsp/14165551212" would then be contained within the
|
||||
The string "500&SIP/itsp/14165551212" would then be contained within the
|
||||
${EXTEN} channel variable, which is then utilized by the Dial() application in
|
||||
our example, thereby giving you the dialplan line of:
|
||||
|
||||
```INI
|
||||
exten => _X.,n,Dial(PJSIP/500&PJSIP/itsp/14165551212)
|
||||
exten => _X.,n,Dial(SIP/500&SIP/itsp/14165551212)
|
||||
```
|
||||
|
||||
Our example above has now provided someone with a method to place calls out of
|
||||
|
@ -83,10 +83,10 @@ the FILTER() dialplan function.
|
|||
|
||||
The CALLERID(num) and CALLERID(name) values are other commonly used values that
|
||||
are sources of data potentially supplied by outside sources. If you use these
|
||||
values as parameters to the System() or MixMonitor() applications or the SHELL()
|
||||
dialplan function, you can allow injection of arbitrary operating system command
|
||||
execution. The FILTER() dialplan function is available to remove dangerous
|
||||
characters from untrusted strings to block the command injection.
|
||||
values as parameters to the System(), MixMonitor(), or Monitor() applications
|
||||
or the SHELL() dialplan function, you can allow injection of arbitrary operating
|
||||
system command execution. The FILTER() dialplan function is available to remove
|
||||
dangerous characters from untrusted strings to block the command injection.
|
||||
|
||||
|
||||
### Strict Pattern Matching
|
||||
|
@ -98,7 +98,7 @@ to only accept three digit extensions, we could change our pattern match to
|
|||
be:
|
||||
|
||||
```INI
|
||||
exten => _XXX,n,Dial(PJSIP/${EXTEN})
|
||||
exten => _XXX,n,Dial(SIP/${EXTEN})
|
||||
```
|
||||
|
||||
In this way, we have minimized our impact because we're not allowing anything
|
||||
|
@ -124,7 +124,7 @@ we will accept to just numbers. Our example would then change to something like:
|
|||
```INI
|
||||
[incoming]
|
||||
exten => _X.,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _X.,n,Dial(PJSIP/${FILTER(0-9,${EXTEN})})
|
||||
exten => _X.,n,Dial(SIP/${FILTER(0-9,${EXTEN})})
|
||||
exten => _X.,n,Hangup()
|
||||
```
|
||||
|
||||
|
@ -141,7 +141,7 @@ necessary, and to handle error checking in a separate location.
|
|||
[incoming]
|
||||
exten => _X.,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _X.,n,Set(SAFE_EXTEN=${FILTER(0-9,${EXTEN})})
|
||||
exten => _X.,n,Dial(PJSIP/${SAFE_EXTEN})
|
||||
exten => _X.,n,Dial(SIP/${SAFE_EXTEN})
|
||||
exten => _X.,n,Hangup()
|
||||
```
|
||||
|
||||
|
@ -155,7 +155,7 @@ passed back by FILTER(), and to fail the call if things do not match.
|
|||
exten => _X.,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _X.,n,Set(SAFE_EXTEN=${FILTER(0-9,${EXTEN})})
|
||||
exten => _X.,n,GotoIf($[${EXTEN} != ${SAFE_EXTEN}]?error,1)
|
||||
exten => _X.,n,Dial(PJSIP/${SAFE_EXTEN})
|
||||
exten => _X.,n,Dial(SIP/${SAFE_EXTEN})
|
||||
exten => _X.,n,Hangup()
|
||||
|
||||
exten => error,1,Verbose(2,Values of EXTEN and SAFE_EXTEN did not match.)
|
||||
|
@ -170,7 +170,7 @@ we're expecting to get a SIP URI for dialing.
|
|||
```INI
|
||||
[incoming]
|
||||
exten => _[0-9a-zA-Z].,1,Verbose(2,Incoming call to extension ${EXTEN})
|
||||
exten => _[0-9a-zA-Z].,n,Dial(PJSIP/${FILTER(.@0-9a-zA-Z,${EXTEN})
|
||||
exten => _[0-9a-zA-Z].,n,Dial(SIP/${FILTER(.@0-9a-zA-Z,${EXTEN})
|
||||
exten => _[0-9a-zA-Z].,n,Hangup()
|
||||
```
|
||||
|
||||
|
@ -201,14 +201,13 @@ It can also be a security hazard to name your devices with a number, as this can
|
|||
open you up to brute force attacks. Many of the current exploits deal with
|
||||
device configurations which utilize a number, and even worse, a password that
|
||||
matches the devices name. For example, take a look at this poorly created device
|
||||
in pjsip.conf:
|
||||
in sip.conf:
|
||||
|
||||
```INI
|
||||
[1000]
|
||||
type=auth
|
||||
auth_type=userpass
|
||||
password=1000
|
||||
username=1000
|
||||
type=friend
|
||||
context=international_dialing
|
||||
secret=1000
|
||||
```
|
||||
|
||||
As implied by the context, we've permitted a device named 1000 with a password
|
||||
|
@ -224,10 +223,9 @@ Passwords). The following example would be more secure:
|
|||
|
||||
```INI
|
||||
[0004f2040001]
|
||||
type=auth
|
||||
auth_type=userpass
|
||||
password=aE3%B8*$jk^G
|
||||
username=0004f2040001
|
||||
type=friend
|
||||
context=international_dialing
|
||||
secret=aE3%B8*$jk^G
|
||||
```
|
||||
|
||||
Then in your dialplan, you would reference the device via the MAC address of the
|
||||
|
@ -325,7 +323,7 @@ the Originate manager command:
|
|||
|
||||
```
|
||||
Action: Originate
|
||||
Channel: PJSIP/foo
|
||||
Channel: SIP/foo
|
||||
Exten: s
|
||||
Context: default
|
||||
Priority: 1
|
||||
|
@ -342,7 +340,7 @@ circumvent these checks. For example, take the following dialplan:
|
|||
```INI
|
||||
exten => s,1,Verbose(Incoming call)
|
||||
same => n,MixMonitor(foo.wav,,${EXEC_COMMAND})
|
||||
same => n,Dial(PJSIP/bar)
|
||||
same => n,Dial(SIP/bar)
|
||||
same => n,Hangup()
|
||||
```
|
||||
|
||||
|
@ -379,8 +377,9 @@ is set to no.
|
|||
|
||||
In Asterisk 12 and later, live_dangerously defaults to no.
|
||||
|
||||
[voip-security-webinar]: https://docs.asterisk.org/Deployment/Important-Security-Considerations/Asterisk-Security-Webinars/
|
||||
[blog-sip-security]: https://web.archive.org/web/20171030134647/http://blogs.digium.com/2009/03/28/sip-security/
|
||||
|
||||
[voip-security-webinar]: https://www.asterisk.org/security/webinar/
|
||||
[blog-sip-security]: http://blogs.digium.com/2009/03/28/sip-security/
|
||||
[Strong Password Generator]: https://www.strongpasswordgenerator.com
|
||||
[Filtering Data]: #filtering-data
|
||||
[Proper Device Naming]: #proper-device-naming
|
||||
|
@ -388,4 +387,4 @@ In Asterisk 12 and later, live_dangerously defaults to no.
|
|||
[Reducing Pattern Match Typos]: #reducing-pattern-match-typos
|
||||
[Manager Class Authorizations]: #manager-class-authorizations
|
||||
[Avoid Privilege Escalations]: #avoid-privilege-escalations
|
||||
[Important Security Considerations]: https://docs.asterisk.org/Deployment/Important-Security-Considerations/
|
||||
[Important Security Considerations]: https://wiki.asterisk.org/wiki/display/AST/Important+Security+Considerations
|
||||
|
|
|
@ -20,7 +20,7 @@ more telephony interfaces than just Internet telephony. Asterisk also has a
|
|||
vast amount of support for traditional PSTN telephony, as well.
|
||||
|
||||
For more information on the project itself, please visit the Asterisk
|
||||
[home page] and the official [documentation]. In addition you'll find lots
|
||||
[home page] and the official [wiki]. In addition you'll find lots
|
||||
of information compiled by the Asterisk community at [voip-info.org].
|
||||
|
||||
There is a book on Asterisk published by O'Reilly under the Creative Commons
|
||||
|
@ -48,7 +48,7 @@ ANY special hardware, not even a sound card) to install and run Asterisk.
|
|||
|
||||
Supported telephony hardware includes:
|
||||
* All Analog and Digital Interface cards from [Sangoma]
|
||||
* QuickNet Internet PhoneJack and LineJack
|
||||
* QuickNet Internet PhoneJack and LineJack (http://www.quicknet.net)
|
||||
* any full duplex sound card supported by ALSA, OSS, or PortAudio
|
||||
* any ISDN card supported by mISDN on Linux
|
||||
* The Xorcom Astribank channel bank
|
||||
|
@ -258,7 +258,7 @@ Asterisk is a trademark of Sangoma Technologies Corporation
|
|||
|
||||
[home page]: https://www.asterisk.org
|
||||
[support]: https://www.asterisk.org/support
|
||||
[documentation]: https://docs.asterisk.org/
|
||||
[wiki]: https://wiki.asterisk.org/
|
||||
[mailing list]: http://lists.digium.com/mailman/listinfo/asterisk-users
|
||||
[chan_dahdi.conf]: configs/samples/chan_dahdi.conf.sample
|
||||
[voip-info.org]: http://www.voip-info.org/wiki-Asterisk
|
||||
|
@ -269,4 +269,4 @@ Asterisk is a trademark of Sangoma Technologies Corporation
|
|||
[CHANGES]: CHANGES
|
||||
[configs]: configs
|
||||
[doc]: doc
|
||||
[Important Security Considerations]: https://docs.asterisk.org/Deployment/Important-Security-Considerations/
|
||||
[Important Security Considerations]: https://wiki.asterisk.org/wiki/display/AST/Important+Security+Considerations
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
The Asterisk project maintains a [documentation page](https://docs.asterisk.org/About-the-Project/Asterisk-Versions/) of releases. Each version is listed with its release date, security fix only date, and end of life date. Consult this wiki page to see if the version of Asterisk you are reporting a security vulnerability against is still supported.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
To report a vulnerability use the "Report a vulnerability" button under the "Security" tab of this project.
|
File diff suppressed because it is too large
Load Diff
|
@ -3248,7 +3248,7 @@ static char *handle_cli_ooh323_show_peer(struct ast_cli_entry *e, int cmd, struc
|
|||
if (peer->t38support == T38_DISABLED) {
|
||||
ast_cli(a->fd, "%s\n", "disabled");
|
||||
} else if (peer->t38support == T38_FAXGW) {
|
||||
ast_cli(a->fd, "%s\n", "faxgw compatible");
|
||||
ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible");
|
||||
}
|
||||
if (peer->faxdetect == (FAXDETECT_CNG | FAXDETECT_T38)) {
|
||||
ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "Yes");
|
||||
|
@ -3386,7 +3386,7 @@ static char *handle_cli_ooh323_show_user(struct ast_cli_entry *e, int cmd, struc
|
|||
if (user->t38support == T38_DISABLED) {
|
||||
ast_cli(a->fd, "%s\n", "disabled");
|
||||
} else if (user->t38support == T38_FAXGW) {
|
||||
ast_cli(a->fd, "%s\n", "faxgw compatible");
|
||||
ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible");
|
||||
}
|
||||
if (user->faxdetect == (FAXDETECT_CNG | FAXDETECT_T38)) {
|
||||
ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "Yes");
|
||||
|
@ -3633,7 +3633,7 @@ static char *handle_cli_ooh323_show_config(struct ast_cli_entry *e, int cmd, str
|
|||
if (gT38Support == T38_DISABLED) {
|
||||
ast_cli(a->fd, "%s\n", "disabled");
|
||||
} else if (gT38Support == T38_FAXGW) {
|
||||
ast_cli(a->fd, "%s\n", "faxgw compatible");
|
||||
ast_cli(a->fd, "%s\n", "faxgw/chan_sip compatible");
|
||||
}
|
||||
if (gFAXdetect == (FAXDETECT_CNG | FAXDETECT_T38)) {
|
||||
ast_cli(a->fd,"%-20s%s\n", "FAX Detect:", "Yes");
|
||||
|
@ -5047,7 +5047,7 @@ struct ast_frame *ooh323_rtp_read(struct ast_channel *ast, struct ooh323_pvt *p)
|
|||
p->faxdetected = 1;
|
||||
ooRequestChangeMode(p->callToken, 1);
|
||||
} else if ((dfr->subclass.integer == 'f') && !p->faxdetected) {
|
||||
const char *target_context = ast_channel_context(p->owner);
|
||||
const char *target_context = S_OR(ast_channel_macrocontext(p->owner), ast_channel_context(p->owner));
|
||||
if ((strcmp(ast_channel_exten(p->owner), "fax")) &&
|
||||
(ast_exists_extension(p->owner, target_context, "fax", 1,
|
||||
S_COR(ast_channel_caller(p->owner)->id.number.valid, ast_channel_caller(p->owner)->id.number.str, NULL)))) {
|
||||
|
@ -5123,7 +5123,7 @@ void onModeChanged(ooCallData *call, int t38mode) {
|
|||
if ((p->faxdetect & FAXDETECT_T38) && !p->faxdetected) {
|
||||
const char *target_context;
|
||||
ast_debug(1, "* Detected T.38 Request\n");
|
||||
target_context = ast_channel_context(p->owner);
|
||||
target_context = S_OR(ast_channel_macrocontext(p->owner), ast_channel_context(p->owner));
|
||||
if ((strcmp(ast_channel_exten(p->owner), "fax")) &&
|
||||
(ast_exists_extension(p->owner, target_context, "fax", 1,
|
||||
S_COR(ast_channel_caller(p->owner)->id.number.valid, ast_channel_caller(p->owner)->id.number.str, NULL)))) {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -84,7 +84,7 @@ struct mysql_conn {
|
|||
AST_RWLIST_ENTRY(mysql_conn) list;
|
||||
ast_mutex_t lock;
|
||||
MYSQL handle;
|
||||
char host[MAXHOSTNAMELEN];
|
||||
char host[50];
|
||||
char name[50];
|
||||
char user[50];
|
||||
char pass[50];
|
||||
|
|
|
@ -51,7 +51,8 @@
|
|||
# going to http://www.mpg123.de/cgi-bin/sitexplorer.cgi?/mpg123/
|
||||
# Be sure to download mpg123-0.59r.tar.gz because it is known to
|
||||
# work with Asterisk and hopefully isn't the release with that
|
||||
# awful security problem. If you're using Fedora Core 3 do make
|
||||
# awful security problem. If you're using Fedora Core 3 with
|
||||
# Alsa like me, make linux-alsa isn't going to work. Do make
|
||||
# linux-devel and you're peachy keen.
|
||||
#
|
||||
# - You won't get nifty STDERR debug messages if you're using a
|
||||
|
|
|
@ -58,6 +58,6 @@ app_voicemail_imap.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION) -DIMAP_STORAGE
|
|||
app_while.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
|
||||
|
||||
ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
|
||||
LIBS+= -lres_ael_share.so -lres_speech.so
|
||||
LIBS+= -lres_ael_share.so -lres_monitor.so -lres_speech.so
|
||||
LIBS+= -lres_smdi.so
|
||||
endif
|
||||
|
|
|
@ -567,7 +567,7 @@ static int load_config(int reload)
|
|||
|
||||
ast_config_destroy(cfg);
|
||||
|
||||
ast_verb(5, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
|
||||
ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
|
||||
"totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
|
||||
dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
|
||||
dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
</syntax>
|
||||
<description>
|
||||
<para>Connects to the given TCP service, then transmits channel audio over that socket. In turn, audio is received from the socket and sent to the channel. Only audio frames will be transmitted.</para>
|
||||
<para>Protocol is specified at https://docs.asterisk.org/Configuration/Channel-Drivers/AudioSocket/</para>
|
||||
<para>Protocol is specified at https://wiki.asterisk.org/wiki/display/AST/AudioSocket</para>
|
||||
<para>This application does not automatically answer and should generally be preceeded by an application such as Answer() or Progress().</para>
|
||||
</description>
|
||||
</application>
|
||||
|
@ -180,7 +180,7 @@ static int audiosocket_run(struct ast_channel *chan, const char *id, int svc)
|
|||
chanName = ast_channel_name(chan);
|
||||
|
||||
while (1) {
|
||||
ms = -1;
|
||||
|
||||
targetChan = ast_waitfor_nandfds(&chan, 1, &svc, 1, NULL, &outfd, &ms);
|
||||
if (targetChan) {
|
||||
f = ast_read(chan);
|
||||
|
|
|
@ -95,17 +95,8 @@ static const char app[] = "Authenticate";
|
|||
maxdigits have been entered (without requiring the user to press the <literal>#</literal> key).
|
||||
Defaults to 0 - no limit - wait for the user press the <literal>#</literal> key.</para>
|
||||
</parameter>
|
||||
<parameter name="prompt" required="false" argsep="&">
|
||||
<para>Override the "agent-pass" sound file. Can be
|
||||
an ampersand separated list of filenames. If the filename
|
||||
is a relative filename (it does not begin with a slash), it
|
||||
will be searched for in the Asterisk sounds directory. If the
|
||||
filename is able to be parsed as a URL, Asterisk will
|
||||
download the file and then begin playback on it. To include a
|
||||
literal <literal>&</literal> in the URL you can enclose
|
||||
the URL in single quotes.</para>
|
||||
<argument name="prompt" required="true" />
|
||||
<argument name="prompt2" multiple="true" />
|
||||
<parameter name="prompt" required="false">
|
||||
<para>Override the agent-pass prompt file.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
|
|
|
@ -1,619 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2022, Naveen Albert
|
||||
*
|
||||
* 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 Channel audio broadcasting
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/audiohook.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/options.h"
|
||||
#include "asterisk/autochan.h"
|
||||
#include "asterisk/format_cache.h"
|
||||
#include "asterisk/cli.h" /* use ESS macro */
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Broadcast" language="en_US">
|
||||
<synopsis>
|
||||
Transmit or receive audio to or from multiple channels simultaneously
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="b">
|
||||
<para>In addition to broadcasting to target channels, also
|
||||
broadcast to any channels to which target channels are bridged.</para>
|
||||
</option>
|
||||
<option name="l">
|
||||
<para>Allow usage of a long queue to store audio frames.</para>
|
||||
<note><para>This may introduce some delay in the received audio feed, but will improve the audio quality.</para></note>
|
||||
</option>
|
||||
<option name="o">
|
||||
<para>Do not mix streams when combining audio from target channels (only applies with s option).</para>
|
||||
</option>
|
||||
<option name="r">
|
||||
<para>Feed frames to barge channels in "reverse" by injecting them into the primary channel's read queue instead.</para>
|
||||
<para>This option is required for barge to work in a n-party bridge (but not for 2-party bridges). Alternately, you
|
||||
can add an intermediate channel by using a non-optimized Local channel, so that the target channel is bridged with
|
||||
a single channel that is connected to the bridge, but it is recommended this option be used instead.</para>
|
||||
<para>Note that this option will always feed injected audio to the other party, regardless of whether the target
|
||||
channel is bridged or not.</para>
|
||||
</option>
|
||||
<option name="s">
|
||||
<para>Rather than broadcast audio to a bunch of channels, receive the combined audio from the target channels.</para>
|
||||
</option>
|
||||
<option name="w">
|
||||
<para>Broadcast audio received on this channel to other channels.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
<parameter name="channels" required="true" argsep=",">
|
||||
<para>List of channels for broadcast targets.</para>
|
||||
<para>Channel names must be the full channel names, not merely device names.</para>
|
||||
<para>Broadcasting will continue until the broadcasting channel hangs up or all target channels have hung up.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This application can be used to broadcast audio to multiple channels at once.
|
||||
Any audio received on this channel will be transmitted to all of the specified channels and, optionally, their bridged peers.</para>
|
||||
<para>It can also be used to aggregate audio from multiple channels at once.
|
||||
Any audio on any of the specified channels, and optionally their bridged peers, will be transmitted to this channel.</para>
|
||||
<para>Execution of the application continues until either the broadcasting channel hangs up
|
||||
or all specified channels have hung up.</para>
|
||||
<para>This application is used for one-to-many and many-to-one audio applications where
|
||||
bridge mixing cannot be done synchronously on all the involved channels.
|
||||
This is primarily useful for injecting the same audio stream into multiple channels at once,
|
||||
or doing the reverse, combining the audio from multiple channels into a single stream.
|
||||
This contrasts with using a separate injection channel for each target channel and/or
|
||||
using a conference bridge.</para>
|
||||
<para>The channel running the Broadcast application must do so synchronously. The specified channels,
|
||||
however, may be doing other things.</para>
|
||||
<example title="Broadcast received audio to three channels and their bridged peers">
|
||||
same => n,Broadcast(wb,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Broadcast received audio to three channels, only">
|
||||
same => n,Broadcast(w,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Combine audio from three channels and their bridged peers to us">
|
||||
same => n,Broadcast(s,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Combine audio from three channels to us">
|
||||
same => n,Broadcast(so,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<example title="Two-way audio with a bunch of channels">
|
||||
same => n,Broadcast(wbso,DAHDI/1,DAHDI/3,PJSIP/doorphone)
|
||||
</example>
|
||||
<para>Note that in the last example above, this is NOT the same as a conference bridge.
|
||||
The specified channels are not audible to each other, only to the channel running the
|
||||
Broadcast application. The two-way audio is only between the broadcasting channel and
|
||||
each of the specified channels, individually.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ChanSpy</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static const char app_broadcast[] = "Broadcast";
|
||||
|
||||
enum {
|
||||
OPTION_READONLY = (1 << 0), /* Don't mix the two channels */
|
||||
OPTION_BARGE = (1 << 1), /* Barge mode (whisper to both channels) */
|
||||
OPTION_LONG_QUEUE = (1 << 2), /* Allow usage of a long queue to store audio frames. */
|
||||
OPTION_WHISPER = (1 << 3),
|
||||
OPTION_SPY = (1 << 4),
|
||||
OPTION_REVERSE_FEED = (1 << 5),
|
||||
OPTION_ANSWER_WARN = (1 << 6), /* Internal flag, not set by user */
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(spy_opts, {
|
||||
AST_APP_OPTION('b', OPTION_BARGE),
|
||||
AST_APP_OPTION('l', OPTION_LONG_QUEUE),
|
||||
AST_APP_OPTION('o', OPTION_READONLY),
|
||||
AST_APP_OPTION('r', OPTION_REVERSE_FEED),
|
||||
AST_APP_OPTION('s', OPTION_SPY),
|
||||
AST_APP_OPTION('w', OPTION_WHISPER),
|
||||
});
|
||||
|
||||
struct multi_autochan {
|
||||
char *name;
|
||||
struct ast_autochan *autochan;
|
||||
struct ast_autochan *bridge_autochan;
|
||||
struct ast_audiohook whisper_audiohook;
|
||||
struct ast_audiohook bridge_whisper_audiohook;
|
||||
struct ast_audiohook spy_audiohook;
|
||||
unsigned int connected:1;
|
||||
unsigned int bridge_connected:1;
|
||||
unsigned int spying:1;
|
||||
AST_LIST_ENTRY(multi_autochan) entry; /*!< Next record */
|
||||
};
|
||||
|
||||
AST_RWLIST_HEAD(multi_autochan_list, multi_autochan);
|
||||
|
||||
struct multi_spy {
|
||||
struct multi_autochan_list *chanlist;
|
||||
unsigned int readonly:1;
|
||||
};
|
||||
|
||||
static void *spy_alloc(struct ast_channel *chan, void *data)
|
||||
{
|
||||
return data; /* just store the data pointer in the channel structure */
|
||||
}
|
||||
|
||||
static void spy_release(struct ast_channel *chan, void *data)
|
||||
{
|
||||
return; /* nothing to do */
|
||||
}
|
||||
|
||||
static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
|
||||
{
|
||||
struct multi_spy *multispy = data;
|
||||
struct multi_autochan_list *chanlist = multispy->chanlist;
|
||||
struct multi_autochan *mac;
|
||||
struct ast_frame *f;
|
||||
short *data1, *data2;
|
||||
int res, i;
|
||||
|
||||
/* All the frames we get are slin, so they will all have the same number of samples. */
|
||||
static const int num_samples = 160;
|
||||
short combine_buf[num_samples];
|
||||
struct ast_frame wf = {
|
||||
.frametype = AST_FRAME_VOICE,
|
||||
.offset = 0,
|
||||
.subclass.format = ast_format_slin,
|
||||
.datalen = num_samples * 2,
|
||||
.samples = num_samples,
|
||||
.src = __FUNCTION__,
|
||||
};
|
||||
|
||||
memset(&combine_buf, 0, sizeof(combine_buf));
|
||||
wf.data.ptr = combine_buf;
|
||||
|
||||
AST_RWLIST_WRLOCK(chanlist);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(chanlist, mac, entry) {
|
||||
ast_audiohook_lock(&mac->spy_audiohook);
|
||||
if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_audiohook_unlock(&mac->spy_audiohook); /* Channel is already gone more than likely, the broadcasting channel will clean this up. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (multispy->readonly) { /* Option 'o' was set, so don't mix channel audio */
|
||||
f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);
|
||||
} else {
|
||||
f = ast_audiohook_read_frame(&mac->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
|
||||
}
|
||||
ast_audiohook_unlock(&mac->spy_audiohook);
|
||||
|
||||
if (!f) {
|
||||
continue; /* No frame? No problem. */
|
||||
}
|
||||
|
||||
/* Mix the samples. */
|
||||
for (i = 0, data1 = combine_buf, data2 = f->data.ptr; i < num_samples; i++, data1++, data2++) {
|
||||
ast_slinear_saturated_add(data1, data2);
|
||||
}
|
||||
ast_frfree(f);
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(chanlist);
|
||||
|
||||
res = ast_write(chan, &wf);
|
||||
ast_frfree(&wf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct ast_generator spygen = {
|
||||
.alloc = spy_alloc,
|
||||
.release = spy_release,
|
||||
.generate = spy_generate,
|
||||
};
|
||||
|
||||
static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook, struct ast_flags *flags)
|
||||
{
|
||||
int res;
|
||||
|
||||
ast_autochan_channel_lock(autochan);
|
||||
ast_debug(1, "Attaching spy channel %s to %s\n", spychan_name, ast_channel_name(autochan->chan));
|
||||
|
||||
if (ast_test_flag(flags, OPTION_READONLY)) {
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_MUTE_WRITE);
|
||||
} else {
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
|
||||
}
|
||||
if (ast_test_flag(flags, OPTION_LONG_QUEUE)) {
|
||||
ast_debug(2, "Using a long queue to store audio frames in spy audiohook\n");
|
||||
} else {
|
||||
ast_set_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE);
|
||||
}
|
||||
res = ast_audiohook_attach(autochan->chan, audiohook);
|
||||
ast_autochan_channel_unlock(autochan);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int attach_barge(struct ast_autochan *spyee_autochan, struct ast_autochan **spyee_bridge_autochan,
|
||||
struct ast_audiohook *bridge_whisper_audiohook, const char *spyer_name, const char *name, struct ast_flags *flags)
|
||||
{
|
||||
int retval = 0;
|
||||
struct ast_autochan *internal_bridge_autochan;
|
||||
struct ast_channel *spyee_chan;
|
||||
RAII_VAR(struct ast_channel *, bridged, NULL, ast_channel_cleanup);
|
||||
|
||||
ast_autochan_channel_lock(spyee_autochan);
|
||||
spyee_chan = ast_channel_ref(spyee_autochan->chan);
|
||||
ast_autochan_channel_unlock(spyee_autochan);
|
||||
|
||||
/* Note that ast_channel_bridge_peer only returns non-NULL for 2-party bridges, not n-party bridges (e.g. ConfBridge) */
|
||||
bridged = ast_channel_bridge_peer(spyee_chan);
|
||||
ast_channel_unref(spyee_chan);
|
||||
if (!bridged) {
|
||||
ast_debug(9, "Channel %s is not yet bridged, unable to setup barge\n", ast_channel_name(spyee_chan));
|
||||
/* If we're bridged, but it's not a 2-party bridge, then we probably should have used OPTION_REVERSE_FEED. */
|
||||
if (ast_test_flag(flags, OPTION_ANSWER_WARN) && ast_channel_is_bridged(spyee_chan)) {
|
||||
ast_clear_flag(flags, OPTION_ANSWER_WARN); /* Don't warn more than once. */
|
||||
ast_log(LOG_WARNING, "Barge failed: channel is bridged, but not to a 2-party bridge. Use the 'r' option.\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Broadcast", 0);
|
||||
internal_bridge_autochan = ast_autochan_setup(bridged);
|
||||
if (!internal_bridge_autochan) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook, flags)) {
|
||||
ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name);
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
*spyee_bridge_autochan = internal_bridge_autochan;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void multi_autochan_free(struct multi_autochan *mac)
|
||||
{
|
||||
if (mac->connected) {
|
||||
if (mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_debug(2, "Whisper audiohook no longer running\n");
|
||||
}
|
||||
ast_audiohook_lock(&mac->whisper_audiohook);
|
||||
ast_audiohook_detach(&mac->whisper_audiohook);
|
||||
ast_audiohook_unlock(&mac->whisper_audiohook);
|
||||
ast_audiohook_destroy(&mac->whisper_audiohook);
|
||||
}
|
||||
if (mac->bridge_connected) {
|
||||
if (mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_debug(2, "Whisper (bridged) audiohook no longer running\n");
|
||||
}
|
||||
ast_audiohook_lock(&mac->bridge_whisper_audiohook);
|
||||
ast_audiohook_detach(&mac->bridge_whisper_audiohook);
|
||||
ast_audiohook_unlock(&mac->bridge_whisper_audiohook);
|
||||
ast_audiohook_destroy(&mac->bridge_whisper_audiohook);
|
||||
}
|
||||
if (mac->spying) {
|
||||
if (mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
|
||||
ast_debug(2, "Spy audiohook no longer running\n");
|
||||
}
|
||||
ast_audiohook_lock(&mac->spy_audiohook);
|
||||
ast_audiohook_detach(&mac->spy_audiohook);
|
||||
ast_audiohook_unlock(&mac->spy_audiohook);
|
||||
ast_audiohook_destroy(&mac->spy_audiohook);
|
||||
}
|
||||
if (mac->name) {
|
||||
int total = mac->connected + mac->bridge_connected + mac->spying;
|
||||
ast_debug(1, "Removing channel %s from target list (%d hook%s)\n", mac->name, total, ESS(total));
|
||||
ast_free(mac->name);
|
||||
}
|
||||
if (mac->autochan) {
|
||||
ast_autochan_destroy(mac->autochan);
|
||||
}
|
||||
if (mac->bridge_autochan) {
|
||||
ast_autochan_destroy(mac->bridge_autochan);
|
||||
}
|
||||
ast_free(mac);
|
||||
}
|
||||
|
||||
static int do_broadcast(struct ast_channel *chan, struct ast_flags *flags, const char *channels)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_frame *f;
|
||||
struct ast_silence_generator *silgen = NULL;
|
||||
struct multi_spy multispy;
|
||||
struct multi_autochan_list chanlist;
|
||||
struct multi_autochan *mac;
|
||||
int numchans = 0;
|
||||
int readonly = ast_test_flag(flags, OPTION_READONLY) ? 1 : 0;
|
||||
char *next, *chansdup = ast_strdupa(channels);
|
||||
|
||||
AST_RWLIST_HEAD_INIT(&chanlist);
|
||||
ast_channel_set_flag(chan, AST_FLAG_SPYING);
|
||||
|
||||
ast_set_flag(flags, OPTION_ANSWER_WARN); /* Initialize answer warn to 1 */
|
||||
|
||||
/* Hey, look ma, no list lock needed! Sometimes, it's nice to not have to share... */
|
||||
|
||||
/* Build a list of targets */
|
||||
while ((next = strsep(&chansdup, ","))) {
|
||||
struct ast_channel *ochan;
|
||||
if (ast_strlen_zero(next)) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(next, ast_channel_name(chan))) {
|
||||
ast_log(LOG_WARNING, "Refusing to broadcast to ourself: %s\n", next);
|
||||
continue;
|
||||
}
|
||||
ochan = ast_channel_get_by_name(next);
|
||||
if (!ochan) {
|
||||
ast_log(LOG_WARNING, "No such channel: %s\n", next);
|
||||
continue;
|
||||
}
|
||||
/* Append to end of list. */
|
||||
if (!(mac = ast_calloc(1, sizeof(*mac)))) {
|
||||
ast_log(LOG_WARNING, "Multi autochan allocation failure\n");
|
||||
continue;
|
||||
}
|
||||
mac->name = ast_strdup(next);
|
||||
mac->autochan = ast_autochan_setup(ochan);
|
||||
if (!mac->name || !mac->autochan) {
|
||||
multi_autochan_free(mac);
|
||||
continue;
|
||||
}
|
||||
if (ast_test_flag(flags, OPTION_WHISPER)) {
|
||||
mac->connected = 1;
|
||||
ast_audiohook_init(&mac->whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Broadcast", 0);
|
||||
/* Inject audio from our channel to this target. */
|
||||
if (start_spying(mac->autochan, next, &mac->whisper_audiohook, flags)) {
|
||||
ast_log(LOG_WARNING, "Unable to attach whisper audiohook to %s\n", next);
|
||||
multi_autochan_free(mac);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (ast_test_flag(flags, OPTION_SPY)) {
|
||||
mac->spying = 1;
|
||||
ast_audiohook_init(&mac->spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "Broadcast", 0);
|
||||
if (start_spying(mac->autochan, next, &mac->spy_audiohook, flags)) {
|
||||
ast_log(LOG_WARNING, "Unable to attach spy audiohook to %s\n", next);
|
||||
multi_autochan_free(mac);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
AST_RWLIST_INSERT_TAIL(&chanlist, mac, entry);
|
||||
numchans++;
|
||||
ochan = ast_channel_unref(ochan);
|
||||
}
|
||||
|
||||
ast_verb(4, "Broadcasting to %d channel%s on %s\n", numchans, ESS(numchans), ast_channel_name(chan));
|
||||
ast_debug(1, "Broadcasting: (TX->1) whisper=%d, (TX->2) barge=%d, (RX<-%d) spy=%d (%s)\n",
|
||||
ast_test_flag(flags, OPTION_WHISPER) ? 1 : 0,
|
||||
ast_test_flag(flags, OPTION_BARGE) ? 1 : 0,
|
||||
readonly ? 1 : 2,
|
||||
ast_test_flag(flags, OPTION_SPY) ? 1 : 0,
|
||||
readonly ? "single" : "both");
|
||||
|
||||
if (ast_test_flag(flags, OPTION_SPY)) {
|
||||
multispy.chanlist = &chanlist;
|
||||
multispy.readonly = readonly;
|
||||
ast_activate_generator(chan, &spygen, &multispy);
|
||||
} else {
|
||||
/* We're not expecting to read any audio, just broadcast audio to a bunch of other channels. */
|
||||
silgen = ast_channel_start_silence_generator(chan);
|
||||
}
|
||||
|
||||
while (numchans && ast_waitfor(chan, -1) > 0) {
|
||||
int fres = 0;
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
ast_debug(1, "Channel %s must have hung up\n", ast_channel_name(chan));
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if (f->frametype != AST_FRAME_VOICE) { /* Ignore any non-voice frames */
|
||||
ast_frfree(f);
|
||||
continue;
|
||||
}
|
||||
/* Write the frame to all our targets. */
|
||||
AST_RWLIST_WRLOCK(&chanlist);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {
|
||||
/* Note that if no media is received, execution is suspended, but assuming continuous or
|
||||
* or frequent audio on the broadcasting channel, we'll quickly enough detect hung up targets.
|
||||
* This isn't really an issue, just something that might be confusing at first, but this is
|
||||
* due to the limitation with audiohooks of using the channel for timing. */
|
||||
if ((ast_test_flag(flags, OPTION_WHISPER) && mac->whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
|
||||
|| (ast_test_flag(flags, OPTION_SPY) && mac->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)
|
||||
|| (mac->bridge_connected && ast_test_flag(flags, OPTION_BARGE) && mac->bridge_whisper_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING)) {
|
||||
/* Even if we're spying only and not actually broadcasting audio, we need to detect channel hangup. */
|
||||
AST_RWLIST_REMOVE_CURRENT(entry);
|
||||
ast_debug(2, "Looks like %s has hung up\n", mac->name);
|
||||
multi_autochan_free(mac);
|
||||
numchans--;
|
||||
ast_debug(2, "%d channel%s remaining in broadcast on %s\n", numchans, ESS(numchans), ast_channel_name(chan));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_WHISPER)) {
|
||||
ast_audiohook_lock(&mac->whisper_audiohook);
|
||||
fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
|
||||
ast_audiohook_unlock(&mac->whisper_audiohook);
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_BARGE)) {
|
||||
/* This hook lets us inject audio into the channel that the spyee is currently
|
||||
* bridged with. If the spyee isn't bridged with anything yet, nothing will
|
||||
* be attached and we'll need to continue attempting to attach the barge
|
||||
* audio hook.
|
||||
* The exception to this is if we are emulating barge by doing it "directly",
|
||||
* that is injecting the frames onto this channel's read queue, rather than
|
||||
* its bridged peer's write queue, then skip this. We only do one or the other. */
|
||||
if (!ast_test_flag(flags, OPTION_REVERSE_FEED) && !mac->bridge_connected && !attach_barge(mac->autochan, &mac->bridge_autochan,
|
||||
&mac->bridge_whisper_audiohook, ast_channel_name(chan), mac->name, flags)) {
|
||||
ast_debug(2, "Attached barge channel for %s\n", mac->name);
|
||||
mac->bridge_connected = 1;
|
||||
}
|
||||
|
||||
if (mac->bridge_connected) {
|
||||
ast_audiohook_lock(&mac->bridge_whisper_audiohook);
|
||||
fres |= ast_audiohook_write_frame(&mac->bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
|
||||
ast_audiohook_unlock(&mac->bridge_whisper_audiohook);
|
||||
} else if (ast_test_flag(flags, OPTION_REVERSE_FEED)) {
|
||||
/* So, this is really clever...
|
||||
* If we're connected to an n-party bridge instead of a 2-party bridge,
|
||||
* attach_barge will ALWAYS fail because we're connected to a bridge, not
|
||||
* a single peer channel.
|
||||
* Recall that the objective is for injected audio to be audible to both
|
||||
* sides of the channel. So really, the typical way of doing this by
|
||||
* directly injecting frames separately onto both channels is kind of
|
||||
* bizarre to begin with, when you think about it.
|
||||
*
|
||||
* In other words, this is how ChanSpy and this module by default work:
|
||||
* We have audio F to inject onto channels A and B, which are <= bridged =>:
|
||||
* READ <- A -> WRITE <==> READ <- B -> WRITE
|
||||
* F --^ F --^
|
||||
*
|
||||
* So that makes the same audio audible to both channels A and B, but
|
||||
* in kind of a roundabout way. What if the bridged peer changes at
|
||||
* some point, for example?
|
||||
*
|
||||
* While that method works for 2-party bridges, it doesn't work at all
|
||||
* for an n-party bridge, so we do the thing that seems obvious to begin with:
|
||||
* dump the frames onto THIS channel's read queue, and the channels will
|
||||
* make their way into the bridge like any other audio from this channel,
|
||||
* and everything just works perfectly, no matter what kind of bridging
|
||||
* scenario is being used. At that point, we don't even care if we're
|
||||
* bridged or not, and really, why should we?
|
||||
*
|
||||
* In other words, we do this:
|
||||
* READ <- A -> WRITE <==> READ <- B -> WRITE
|
||||
* F --^ F --^
|
||||
*/
|
||||
ast_audiohook_lock(&mac->whisper_audiohook);
|
||||
fres |= ast_audiohook_write_frame(&mac->whisper_audiohook, AST_AUDIOHOOK_DIRECTION_READ, f);
|
||||
ast_audiohook_unlock(&mac->whisper_audiohook);
|
||||
}
|
||||
}
|
||||
if (fres) {
|
||||
ast_log(LOG_WARNING, "Failed to write to audiohook for %s\n", mac->name);
|
||||
fres = 0;
|
||||
}
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&chanlist);
|
||||
ast_frfree(f);
|
||||
}
|
||||
|
||||
if (!numchans) {
|
||||
ast_debug(1, "Exiting due to all target channels having left the broadcast\n");
|
||||
}
|
||||
|
||||
if (ast_test_flag(flags, OPTION_SPY)) {
|
||||
ast_deactivate_generator(chan);
|
||||
} else {
|
||||
ast_channel_stop_silence_generator(chan, silgen);
|
||||
}
|
||||
|
||||
/* Cleanup any remaining targets */
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&chanlist, mac, entry) {
|
||||
AST_RWLIST_REMOVE_CURRENT(entry);
|
||||
multi_autochan_free(mac);
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
|
||||
ast_channel_clear_flag(chan, AST_FLAG_SPYING);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int broadcast_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
struct ast_flags flags;
|
||||
struct ast_format *write_format;
|
||||
int res = -1;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(options);
|
||||
AST_APP_ARG(channels); /* Channel list last, so we can have multiple */
|
||||
);
|
||||
char *parse = NULL;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Broadcast requires at least one channel\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(args.channels)) {
|
||||
ast_log(LOG_WARNING, "Must specify at least one channel for broadcast\n");
|
||||
return -1;
|
||||
}
|
||||
if (args.options) {
|
||||
ast_app_parse_options(spy_opts, &flags, NULL, args.options);
|
||||
} else {
|
||||
ast_clear_flag(&flags, AST_FLAGS_ALL);
|
||||
}
|
||||
|
||||
if (!ast_test_flag(&flags, OPTION_BARGE) && !ast_test_flag(&flags, OPTION_SPY) && !ast_test_flag(&flags, OPTION_WHISPER)) {
|
||||
ast_log(LOG_WARNING, "At least one of the b, s, or w option must be specified (provided options have no effect)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_format = ao2_bump(ast_channel_writeformat(chan));
|
||||
if (ast_set_write_format(chan, ast_format_slin) < 0) {
|
||||
ast_log(LOG_ERROR, "Failed to set write format to slin.\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
res = do_broadcast(chan, &flags, args.channels);
|
||||
|
||||
/* Restore previous write format */
|
||||
if (ast_set_write_format(chan, write_format)) {
|
||||
ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", ast_channel_name(chan));
|
||||
}
|
||||
|
||||
cleanup:
|
||||
ao2_ref(write_format, -1);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
return ast_unregister_application(app_broadcast);
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return ast_register_application_xml(app_broadcast, broadcast_exec);
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Audio Broadcasting");
|
|
@ -38,6 +38,26 @@
|
|||
#include "asterisk/stasis_message_router.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="NoCDR" language="en_US">
|
||||
<synopsis>
|
||||
Tell Asterisk to not maintain a CDR for this channel.
|
||||
</synopsis>
|
||||
<syntax />
|
||||
<description>
|
||||
<para>This application will tell Asterisk not to maintain a CDR for
|
||||
the current channel. This does <emphasis>NOT</emphasis> mean that
|
||||
information is not tracked; rather, if the channel is hung up no
|
||||
CDRs will be created for that channel.</para>
|
||||
<para>If a subsequent call to ResetCDR occurs, all non-finalized
|
||||
CDRs created for the channel will be enabled.</para>
|
||||
<note><para>This application is deprecated. Please use the CDR_PROP
|
||||
function to disable CDRs on a channel.</para></note>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ResetCDR</ref>
|
||||
<ref type="function">CDR_PROP</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="ResetCDR" language="en_US">
|
||||
<synopsis>
|
||||
Resets the Call Data Record.
|
||||
|
@ -48,6 +68,10 @@
|
|||
<option name="v">
|
||||
<para>Save the CDR variables during the reset.</para>
|
||||
</option>
|
||||
<option name="e">
|
||||
<para>Enable the CDRs for this channel only (negate
|
||||
effects of NoCDR).</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
|
@ -60,14 +84,21 @@
|
|||
current time.</para>
|
||||
<para>3. All variables are wiped from the CDR. Note that this step
|
||||
can be prevented with the <literal>v</literal> option.</para>
|
||||
<para>On the other hand, if the <literal>e</literal> option is
|
||||
specified, the effects of the NoCDR application will be lifted. CDRs
|
||||
will be re-enabled for this channel.</para>
|
||||
<note><para>The <literal>e</literal> option is deprecated. Please
|
||||
use the CDR_PROP function instead.</para></note>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ForkCDR</ref>
|
||||
<ref type="application">NoCDR</ref>
|
||||
<ref type="function">CDR_PROP</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static const char nocdr_app[] = "NoCDR";
|
||||
static const char resetcdr_app[] = "ResetCDR";
|
||||
|
||||
enum reset_cdr_options {
|
||||
|
@ -78,6 +109,7 @@ enum reset_cdr_options {
|
|||
|
||||
AST_APP_OPTIONS(resetcdr_opts, {
|
||||
AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
|
||||
AST_APP_OPTION('e', AST_CDR_FLAG_DISABLE_ALL),
|
||||
});
|
||||
|
||||
STASIS_MESSAGE_TYPE_DEFN_LOCAL(appcdr_message_type);
|
||||
|
@ -86,6 +118,10 @@ STASIS_MESSAGE_TYPE_DEFN_LOCAL(appcdr_message_type);
|
|||
struct app_cdr_message_payload {
|
||||
/*! The name of the channel to be manipulated */
|
||||
const char *channel_name;
|
||||
/*! Disable the CDR for this channel */
|
||||
unsigned int disable:1;
|
||||
/*! Re-enable the CDR for this channel */
|
||||
unsigned int reenable:1;
|
||||
/*! Reset the CDR */
|
||||
unsigned int reset:1;
|
||||
/*! If reseting the CDR, keep the variables */
|
||||
|
@ -105,9 +141,24 @@ static void appcdr_callback(void *data, struct stasis_subscription *sub, struct
|
|||
return;
|
||||
}
|
||||
|
||||
if (payload->disable) {
|
||||
if (ast_cdr_set_property(payload->channel_name, AST_CDR_FLAG_DISABLE_ALL)) {
|
||||
ast_log(AST_LOG_WARNING, "Failed to disable CDRs on channel %s\n",
|
||||
payload->channel_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (payload->reenable) {
|
||||
if (ast_cdr_clear_property(payload->channel_name, AST_CDR_FLAG_DISABLE_ALL)) {
|
||||
ast_log(AST_LOG_WARNING, "Failed to enable CDRs on channel %s\n",
|
||||
payload->channel_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (payload->reset) {
|
||||
if (ast_cdr_reset(payload->channel_name, payload->keep_variables)) {
|
||||
ast_log(AST_LOG_WARNING, "Failed to reset CDRs on channel %s\n", payload->channel_name);
|
||||
ast_log(AST_LOG_WARNING, "Failed to reset CDRs on channel %s\n",
|
||||
payload->channel_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +204,10 @@ static int resetcdr_exec(struct ast_channel *chan, const char *data)
|
|||
payload->channel_name = ast_channel_name(chan);
|
||||
payload->reset = 1;
|
||||
|
||||
if (ast_test_flag(&flags, AST_CDR_FLAG_DISABLE_ALL)) {
|
||||
payload->reenable = 1;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
|
||||
payload->keep_variables = 1;
|
||||
}
|
||||
|
@ -160,6 +215,21 @@ static int resetcdr_exec(struct ast_channel *chan, const char *data)
|
|||
return publish_app_cdr_message(chan, payload);
|
||||
}
|
||||
|
||||
static int nocdr_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
RAII_VAR(struct app_cdr_message_payload *, payload,
|
||||
ao2_alloc(sizeof(*payload), NULL), ao2_cleanup);
|
||||
|
||||
if (!payload) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
payload->channel_name = ast_channel_name(chan);
|
||||
payload->disable = 1;
|
||||
|
||||
return publish_app_cdr_message(chan, payload);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
RAII_VAR(struct stasis_message_router *, router, ast_cdr_message_router(), ao2_cleanup);
|
||||
|
@ -168,6 +238,7 @@ static int unload_module(void)
|
|||
stasis_message_router_remove(router, appcdr_message_type());
|
||||
}
|
||||
STASIS_MESSAGE_TYPE_CLEANUP(appcdr_message_type);
|
||||
ast_unregister_application(nocdr_app);
|
||||
ast_unregister_application(resetcdr_app);
|
||||
return 0;
|
||||
}
|
||||
|
@ -182,8 +253,10 @@ static int load_module(void)
|
|||
}
|
||||
|
||||
res |= STASIS_MESSAGE_TYPE_INIT(appcdr_message_type);
|
||||
res |= ast_register_application_xml(nocdr_app, nocdr_exec);
|
||||
res |= ast_register_application_xml(resetcdr_app, resetcdr_exec);
|
||||
res |= stasis_message_router_add(router, appcdr_message_type(), appcdr_callback, NULL);
|
||||
res |= stasis_message_router_add(router, appcdr_message_type(),
|
||||
appcdr_callback, NULL);
|
||||
|
||||
if (res) {
|
||||
unload_module();
|
||||
|
|
|
@ -117,7 +117,6 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
|||
struct ast_str *tmp_availcause = ast_str_alloca(2048);
|
||||
struct ast_channel *tempchan;
|
||||
struct ast_custom_function *cdr_prop_func = ast_custom_function_find("CDR_PROP");
|
||||
struct ast_format_cap *caps = NULL;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(reqchans);
|
||||
AST_APP_ARG(options);
|
||||
|
@ -127,10 +126,6 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
|||
|
||||
AST_STANDARD_APP_ARGS(args, info);
|
||||
|
||||
ao2_lock(chan);
|
||||
caps = ao2_bump(ast_channel_nativeformats(chan));
|
||||
ao2_unlock(chan);
|
||||
|
||||
if (args.options) {
|
||||
if (strchr(args.options, 'a')) {
|
||||
option_all_avail = 1;
|
||||
|
@ -179,11 +174,10 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
|||
snprintf(trychan, sizeof(trychan), "%s/%s", tech, number);
|
||||
status = inuse = ast_device_state(trychan);
|
||||
}
|
||||
ast_str_append(&tmp_availstat, 0, "%s%d", ast_str_strlen(tmp_availstat) ? "&" : "", status);
|
||||
|
||||
ast_str_append(&tmp_availstat, 0, "%s%d",
|
||||
ast_str_strlen(tmp_availstat) ? "&" : "", status);
|
||||
if ((inuse <= (int) AST_DEVICE_NOT_INUSE)
|
||||
&& (tempchan = ast_request(tech, caps, NULL, chan, number, &status))) {
|
||||
|
||||
&& (tempchan = ast_request(tech, ast_channel_nativeformats(chan), NULL, chan, number, &status))) {
|
||||
ast_str_append(&tmp_availchan, 0, "%s%s",
|
||||
ast_str_strlen(tmp_availchan) ? "&" : "", ast_channel_name(tempchan));
|
||||
|
||||
|
@ -205,11 +199,8 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ao2_cleanup(caps);
|
||||
|
||||
pbx_builtin_setvar_helper(chan, "AVAILCHAN", ast_str_buffer(tmp_availchan));
|
||||
/* Store the originally used channel too */
|
||||
pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", ast_str_buffer(tmp_availorig));
|
||||
|
|
|
@ -245,11 +245,6 @@
|
|||
</enum>
|
||||
</enumlist>
|
||||
</option>
|
||||
<option name="D">
|
||||
<para>Interleave the audio coming from the channel and the audio coming to the channel in
|
||||
the output audio as a dual channel stream, rather than mix it. Does nothing if 'o'
|
||||
is also set.</para>
|
||||
</option>
|
||||
<option name="e">
|
||||
<argument name="ext" required="true" />
|
||||
<para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
|
||||
|
@ -398,7 +393,6 @@ enum {
|
|||
OPTION_EXITONHANGUP = (1 << 18), /* Hang up when the spied-on channel hangs up. */
|
||||
OPTION_UNIQUEID = (1 << 19), /* The chanprefix is a channel uniqueid or fully specified channel name. */
|
||||
OPTION_LONG_QUEUE = (1 << 20), /* Allow usage of a long queue to store audio frames. */
|
||||
OPTION_INTERLEAVED = (1 << 21), /* Interleave the Read and Write frames in the output frame. */
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -417,7 +411,6 @@ AST_APP_OPTIONS(spy_opts, {
|
|||
AST_APP_OPTION('B', OPTION_BARGE),
|
||||
AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
|
||||
AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
|
||||
AST_APP_OPTION('D', OPTION_INTERLEAVED),
|
||||
AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
|
||||
AST_APP_OPTION('E', OPTION_EXITONHANGUP),
|
||||
AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
|
||||
|
@ -478,56 +471,6 @@ static int spy_generate(struct ast_channel *chan, void *data, int len, int sampl
|
|||
if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
|
||||
/* Option 'o' was set, so don't mix channel audio */
|
||||
f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, ast_format_slin);
|
||||
} else if (ast_test_flag(&csth->flags, OPTION_INTERLEAVED)) {
|
||||
/* Option 'D' was set, so mix the spy frame as an interleaved dual channel frame. */
|
||||
int i;
|
||||
struct ast_frame *fr_read = NULL;
|
||||
struct ast_frame *fr_write = NULL;
|
||||
short read_buf[samples];
|
||||
short write_buf[samples];
|
||||
short stereo_buf[samples * 2];
|
||||
struct ast_frame stereo_frame = {
|
||||
.frametype = AST_FRAME_VOICE,
|
||||
.datalen = sizeof(stereo_buf),
|
||||
.samples = samples,
|
||||
};
|
||||
|
||||
f = ast_audiohook_read_frame_all(&csth->spy_audiohook, samples, ast_format_slin, &fr_read, &fr_write);
|
||||
if (f) {
|
||||
ast_frame_free(f, 0);
|
||||
f = NULL;
|
||||
}
|
||||
|
||||
if (fr_read) {
|
||||
memcpy(read_buf, fr_read->data.ptr, sizeof(read_buf));
|
||||
} else {
|
||||
/* silent out the output frame if we can't read the input */
|
||||
memset(read_buf, 0, sizeof(read_buf));
|
||||
}
|
||||
|
||||
if (fr_write) {
|
||||
memcpy(write_buf, fr_write->data.ptr, sizeof(write_buf));
|
||||
} else {
|
||||
memset(write_buf, 0, sizeof(write_buf));
|
||||
}
|
||||
|
||||
for (i = 0; i < samples; i++) {
|
||||
stereo_buf[i*2] = read_buf[i];
|
||||
stereo_buf[i*2+1] = write_buf[i];
|
||||
}
|
||||
|
||||
stereo_frame.data.ptr = stereo_buf;
|
||||
stereo_frame.subclass.format = ast_format_cache_get_slin_by_rate(samples);
|
||||
|
||||
f = ast_frdup(&stereo_frame);
|
||||
|
||||
if (fr_read) {
|
||||
ast_frame_free(fr_read, 0);
|
||||
}
|
||||
if (fr_write) {
|
||||
ast_frame_free(fr_write, 0);
|
||||
}
|
||||
|
||||
} else {
|
||||
f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
|
||||
}
|
||||
|
@ -964,6 +907,8 @@ static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
|
|||
ast_channel_lock(chan);
|
||||
if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
|
||||
ast_copy_string(exitcontext, c, sizeof(exitcontext));
|
||||
} else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
|
||||
ast_copy_string(exitcontext, ast_channel_macrocontext(chan), sizeof(exitcontext));
|
||||
} else {
|
||||
ast_copy_string(exitcontext, ast_channel_context(chan), sizeof(exitcontext));
|
||||
}
|
||||
|
|
|
@ -3035,7 +3035,7 @@ static int action_playback(struct ast_bridge_channel *bridge_channel, const char
|
|||
char *file_copy = ast_strdupa(playback_file);
|
||||
char *file = NULL;
|
||||
|
||||
while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||
while ((file = strsep(&file_copy, "&"))) {
|
||||
if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
|
||||
ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
|
||||
return -1;
|
||||
|
@ -3059,7 +3059,7 @@ static int action_playback_and_continue(struct confbridge_conference *conference
|
|||
char *file_copy = ast_strdupa(playback_file);
|
||||
char *file = NULL;
|
||||
|
||||
while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||
while ((file = strsep(&file_copy, "&"))) {
|
||||
if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
|
||||
ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
|
||||
return -1;
|
||||
|
|
302
apps/app_dial.c
302
apps/app_dial.c
|
@ -88,12 +88,9 @@
|
|||
</argument>
|
||||
<xi:include xpointer="xpointer(/docs/info[@name='Dial_Resource'])" />
|
||||
</parameter>
|
||||
<parameter name="timeout" required="false" argsep="^">
|
||||
<parameter name="timeout" required="false">
|
||||
<para>Specifies the number of seconds we attempt to dial the specified devices.</para>
|
||||
<para>If not specified, this defaults to 136 years.</para>
|
||||
<para>If a second argument is specified, this controls the number of seconds we attempt to dial the specified devices
|
||||
without receiving early media or ringing. If neither progress, ringing, nor voice frames have been received when this
|
||||
timeout expires, the call will be treated as a CHANUNAVAIL. This can be used to skip destinations that may not be responsive.</para>
|
||||
</parameter>
|
||||
<parameter name="options" required="false">
|
||||
<optionlist>
|
||||
|
@ -211,7 +208,7 @@
|
|||
and <emphasis>start</emphasis> execution at that location.</para>
|
||||
<para>NOTE: Any channel variables you want the called channel to inherit from the caller channel must be
|
||||
prefixed with one or two underbars ('_').</para>
|
||||
<para>NOTE: Using this option from a GoSub() might not make sense as there would be no return points.</para>
|
||||
<para>NOTE: Using this option from a Macro() or GoSub() might not make sense as there would be no return points.</para>
|
||||
</option>
|
||||
<option name="g">
|
||||
<para>Proceed with dialplan execution at the next priority in the current extension if the
|
||||
|
@ -245,10 +242,6 @@
|
|||
<para>Asterisk will ignore any connected line update requests or any redirecting party
|
||||
update requests it may receive on this dial attempt.</para>
|
||||
</option>
|
||||
<option name="j">
|
||||
<para>Use the initial stream topology of the caller for outgoing channels, even if the caller topology has changed.</para>
|
||||
<para>NOTE: For this option to work, it has to be present in all invocations of Dial that the caller channel goes through.</para>
|
||||
</option>
|
||||
<option name="k">
|
||||
<para>Allow the called party to enable parking of the call by sending
|
||||
the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
|
||||
|
@ -304,6 +297,47 @@
|
|||
channel answers. A specific music on hold <replaceable>class</replaceable>
|
||||
(as defined in <filename>musiconhold.conf</filename>) can be specified.</para>
|
||||
</option>
|
||||
<option name="M" argsep="^">
|
||||
<argument name="macro" required="true">
|
||||
<para>Name of the macro that should be executed.</para>
|
||||
</argument>
|
||||
<argument name="arg" multiple="true">
|
||||
<para>Macro arguments</para>
|
||||
</argument>
|
||||
<para>Execute the specified <replaceable>macro</replaceable> for the <emphasis>called</emphasis> channel
|
||||
before connecting to the calling channel. Arguments can be specified to the Macro
|
||||
using <literal>^</literal> as a delimiter. The macro can set the variable
|
||||
<variable>MACRO_RESULT</variable> to specify the following actions after the macro is
|
||||
finished executing:</para>
|
||||
<variablelist>
|
||||
<variable name="MACRO_RESULT">
|
||||
<para>If set, this action will be taken after the macro finished executing.</para>
|
||||
<value name="ABORT">
|
||||
Hangup both legs of the call
|
||||
</value>
|
||||
<value name="CONGESTION">
|
||||
Behave as if line congestion was encountered
|
||||
</value>
|
||||
<value name="BUSY">
|
||||
Behave as if a busy signal was encountered
|
||||
</value>
|
||||
<value name="CONTINUE">
|
||||
Hangup the called party and allow the calling party to continue dialplan execution at the next priority
|
||||
</value>
|
||||
<value name="GOTO:[[<context>^]<exten>^]<priority>">
|
||||
Transfer the call to the specified destination.
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<para>NOTE: You cannot use any additional action post answer options in conjunction
|
||||
with this option. Also, pbx services are run on the peer (called) channel,
|
||||
so you will not be able to set timeouts via the <literal>TIMEOUT()</literal> function in this macro.</para>
|
||||
<para>WARNING: Be aware of the limitations that macros have, specifically with regards to use of
|
||||
the <literal>WaitExten</literal> application. For more information, see the documentation for
|
||||
<literal>Macro()</literal>.</para>
|
||||
<para>NOTE: Macros are deprecated, GoSub should be used instead,
|
||||
see the <literal>U</literal> option.</para>
|
||||
</option>
|
||||
<option name="n">
|
||||
<argument name="delete">
|
||||
<para>With <replaceable>delete</replaceable> either not specified or set to <literal>0</literal>,
|
||||
|
@ -367,6 +401,8 @@
|
|||
to send no cause. See the <filename>causes.h</filename> file for the
|
||||
full list of valid causes and names.
|
||||
</para>
|
||||
<para>NOTE: chan_sip does not support setting the cause on a CANCEL to anything
|
||||
other than ANSWERED_ELSEWHERE.</para>
|
||||
</option>
|
||||
<option name="r">
|
||||
<para>Default: Indicate ringing to the calling party, even if the called party isn't actually ringing. Pass no audio to the calling
|
||||
|
@ -621,6 +657,7 @@
|
|||
<ref type="application">RetryDial</ref>
|
||||
<ref type="application">SendDTMF</ref>
|
||||
<ref type="application">Gosub</ref>
|
||||
<ref type="application">Macro</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="RetryDial" language="en_US">
|
||||
|
@ -675,6 +712,7 @@ enum {
|
|||
OPT_ORIGINAL_CLID = (1 << 8),
|
||||
OPT_DURATION_LIMIT = (1 << 9),
|
||||
OPT_MUSICBACK = (1 << 10),
|
||||
OPT_CALLEE_MACRO = (1 << 11),
|
||||
OPT_SCREEN_NOINTRO = (1 << 12),
|
||||
OPT_SCREEN_NOCALLERID = (1 << 13),
|
||||
OPT_IGNORE_CONNECTEDLINE = (1 << 14),
|
||||
|
@ -712,7 +750,6 @@ enum {
|
|||
#define OPT_RING_WITH_EARLY_MEDIA (1LLU << 43)
|
||||
#define OPT_HANGUPCAUSE (1LLU << 44)
|
||||
#define OPT_HEARPULSING (1LLU << 45)
|
||||
#define OPT_TOPOLOGY_PRESERVE (1LLU << 46)
|
||||
|
||||
enum {
|
||||
OPT_ARG_ANNOUNCE = 0,
|
||||
|
@ -720,6 +757,7 @@ enum {
|
|||
OPT_ARG_GOTO,
|
||||
OPT_ARG_DURATION_LIMIT,
|
||||
OPT_ARG_MUSICBACK,
|
||||
OPT_ARG_CALLEE_MACRO,
|
||||
OPT_ARG_RINGBACK,
|
||||
OPT_ARG_CALLEE_GOSUB,
|
||||
OPT_ARG_CALLEE_GO_ON,
|
||||
|
@ -757,11 +795,11 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
|
|||
AST_APP_OPTION('H', OPT_CALLER_HANGUP),
|
||||
AST_APP_OPTION('i', OPT_IGNORE_FORWARDING),
|
||||
AST_APP_OPTION('I', OPT_IGNORE_CONNECTEDLINE),
|
||||
AST_APP_OPTION('j', OPT_TOPOLOGY_PRESERVE),
|
||||
AST_APP_OPTION('k', OPT_CALLEE_PARK),
|
||||
AST_APP_OPTION('K', OPT_CALLER_PARK),
|
||||
AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
|
||||
AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK),
|
||||
AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO),
|
||||
AST_APP_OPTION_ARG('n', OPT_SCREEN_NOINTRO, OPT_ARG_SCREEN_NOINTRO),
|
||||
AST_APP_OPTION('N', OPT_SCREEN_NOCALLERID),
|
||||
AST_APP_OPTION_ARG('o', OPT_ORIGINAL_CLID, OPT_ARG_ORIGINAL_CLID),
|
||||
|
@ -787,7 +825,7 @@ END_OPTIONS );
|
|||
#define CAN_EARLY_BRIDGE(flags,chan,peer) (!ast_test_flag64(flags, OPT_CALLEE_HANGUP | \
|
||||
OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \
|
||||
OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | \
|
||||
OPT_CALLER_PARK | OPT_ANNOUNCE | OPT_CALLEE_GOSUB) && \
|
||||
OPT_CALLER_PARK | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB) && \
|
||||
!ast_channel_audiohooks(chan) && !ast_channel_audiohooks(peer) && \
|
||||
ast_framehook_list_is_empty(ast_channel_framehooks(chan)) && ast_framehook_list_is_empty(ast_channel_framehooks(peer)))
|
||||
|
||||
|
@ -817,16 +855,6 @@ struct chanlist {
|
|||
|
||||
AST_LIST_HEAD_NOLOCK(dial_head, chanlist);
|
||||
|
||||
static void topology_ds_destroy(void *data) {
|
||||
struct ast_stream_topology *top = data;
|
||||
ast_stream_topology_free(top);
|
||||
}
|
||||
|
||||
static const struct ast_datastore_info topology_ds_info = {
|
||||
.type = "app_dial_topology_preserve",
|
||||
.destroy = topology_ds_destroy,
|
||||
};
|
||||
|
||||
static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str **featurecode);
|
||||
|
||||
static void chanlist_free(struct chanlist *outgoing)
|
||||
|
@ -899,6 +927,10 @@ static int onedigit_goto(struct ast_channel *chan, const char *context, char ext
|
|||
} else {
|
||||
if (!ast_goto_if_exists(chan, ast_channel_context(chan), rexten, pri))
|
||||
return 1;
|
||||
else if (!ast_strlen_zero(ast_channel_macrocontext(chan))) {
|
||||
if (!ast_goto_if_exists(chan, ast_channel_macrocontext(chan), rexten, pri))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -910,8 +942,8 @@ static const char *get_cid_name(char *name, int namelen, struct ast_channel *cha
|
|||
const char *exten;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
context = ast_strdupa(ast_channel_context(chan));
|
||||
exten = ast_strdupa(ast_channel_exten(chan));
|
||||
context = ast_strdupa(S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan)));
|
||||
exten = ast_strdupa(S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)));
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : "";
|
||||
|
@ -1046,7 +1078,7 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
|
|||
ast_party_number_init(&ast_channel_redirecting(c)->from.number);
|
||||
ast_channel_redirecting(c)->from.number.valid = 1;
|
||||
ast_channel_redirecting(c)->from.number.str =
|
||||
ast_strdup(ast_channel_exten(in));
|
||||
ast_strdup(S_OR(ast_channel_macroexten(in), ast_channel_exten(in)));
|
||||
}
|
||||
|
||||
ast_channel_dialed(c)->transit_network_select = ast_channel_dialed(in)->transit_network_select;
|
||||
|
@ -1095,12 +1127,17 @@ static void do_forward(struct chanlist *o, struct cause_args *num,
|
|||
* Redirecting updates to the caller make sense only on single
|
||||
* calls.
|
||||
*
|
||||
* Need to re-evalute if unlocking is still required here as macro is gone
|
||||
* We must unlock c before calling
|
||||
* ast_channel_redirecting_macro, because we put c into
|
||||
* autoservice there. That is pretty much a guaranteed
|
||||
* deadlock. This is why the handling of c's lock may seem a
|
||||
* bit unusual here.
|
||||
*/
|
||||
ast_party_redirecting_init(&redirecting);
|
||||
ast_party_redirecting_copy(&redirecting, ast_channel_redirecting(c));
|
||||
ast_channel_unlock(c);
|
||||
if (ast_channel_redirecting_sub(c, in, &redirecting, 0)) {
|
||||
if (ast_channel_redirecting_sub(c, in, &redirecting, 0) &&
|
||||
ast_channel_redirecting_macro(c, in, &redirecting, 1, 0)) {
|
||||
ast_channel_update_redirecting(in, &redirecting, NULL);
|
||||
}
|
||||
ast_party_redirecting_free(&redirecting);
|
||||
|
@ -1176,7 +1213,8 @@ static void update_connected_line_from_peer(struct ast_channel *chan, struct ast
|
|||
ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(peer));
|
||||
ast_channel_unlock(peer);
|
||||
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
|
||||
if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)) {
|
||||
if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)
|
||||
&& ast_channel_connected_line_macro(peer, chan, &connected_caller, is_caller, 0)) {
|
||||
ast_channel_update_connected_line(chan, &connected_caller, NULL);
|
||||
}
|
||||
ast_party_connected_line_free(&connected_caller);
|
||||
|
@ -1200,7 +1238,7 @@ static void set_duration_var(struct ast_channel *chan, const char *var_base, int
|
|||
}
|
||||
|
||||
static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||
struct dial_head *out_chans, int *to_answer, int *to_progress, struct ast_flags64 *peerflags,
|
||||
struct dial_head *out_chans, int *to, struct ast_flags64 *peerflags,
|
||||
char *opt_args[],
|
||||
struct privacy_args *pa,
|
||||
const struct cause_args *num_in, int *result, char *dtmf_progress,
|
||||
|
@ -1213,9 +1251,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
{
|
||||
struct cause_args num = *num_in;
|
||||
int prestart = num.busy + num.congestion + num.nochan;
|
||||
int orig_answer_to = *to_answer;
|
||||
int progress_to_dup = *to_progress;
|
||||
int orig_progress_to = *to_progress;
|
||||
int orig = *to;
|
||||
struct ast_channel *peer = NULL;
|
||||
struct chanlist *outgoing = AST_LIST_FIRST(out_chans);
|
||||
/* single is set if only one destination is enabled */
|
||||
|
@ -1243,7 +1279,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
* there is no point in continuing. The bridge
|
||||
* will just fail if it gets that far.
|
||||
*/
|
||||
*to_answer = -1;
|
||||
*to = -1;
|
||||
strcpy(pa->status, "CONGESTION");
|
||||
ast_channel_publish_dial(in, outgoing->chan, NULL, pa->status);
|
||||
SCOPE_EXIT_RTN_VALUE(NULL, "%s: can't be made compat with %s\n",
|
||||
|
@ -1259,7 +1295,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
|
||||
is_cc_recall = ast_cc_is_recall(in, &cc_recall_core_id, NULL);
|
||||
|
||||
while ((*to_answer = ast_remaining_ms(start, orig_answer_to)) && (*to_progress = ast_remaining_ms(start, progress_to_dup)) && !peer) {
|
||||
while ((*to = ast_remaining_ms(start, orig)) && !peer) {
|
||||
struct chanlist *o;
|
||||
int pos = 0; /* how many channels do we handle */
|
||||
int numlines = prestart;
|
||||
|
@ -1285,15 +1321,14 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
} else {
|
||||
ast_verb(3, "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, num.busy, num.congestion, num.nochan);
|
||||
}
|
||||
*to_answer = 0;
|
||||
*to = 0;
|
||||
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 outgoing channels available\n", ast_channel_name(in));
|
||||
}
|
||||
winner = ast_waitfor_n(watchers, pos, to_answer);
|
||||
winner = ast_waitfor_n(watchers, pos, to);
|
||||
AST_LIST_TRAVERSE(out_chans, o, node) {
|
||||
int res = 0;
|
||||
struct ast_frame *f;
|
||||
struct ast_channel *c = o->chan;
|
||||
|
||||
|
@ -1311,7 +1346,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
update_connected_line_from_peer(in, c, 1);
|
||||
} else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
|
||||
if (o->pending_connected_update) {
|
||||
if (ast_channel_connected_line_sub(c, in, &o->connected, 0)) {
|
||||
if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
|
||||
ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
|
||||
ast_channel_update_connected_line(in, &o->connected, NULL);
|
||||
}
|
||||
} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
|
||||
|
@ -1368,7 +1404,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_channel_unlock(in);
|
||||
}
|
||||
|
||||
do_forward(o, &num, peerflags, single, caller_entertained, &orig_answer_to,
|
||||
do_forward(o, &num, peerflags, single, caller_entertained, &orig,
|
||||
forced_clid, stored_clid);
|
||||
|
||||
if (o->chan) {
|
||||
|
@ -1409,7 +1445,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
update_connected_line_from_peer(in, c, 1);
|
||||
} else if (!single && !ast_test_flag64(o, OPT_IGNORE_CONNECTEDLINE)) {
|
||||
if (o->pending_connected_update) {
|
||||
if (ast_channel_connected_line_sub(c, in, &o->connected, 0)) {
|
||||
if (ast_channel_connected_line_sub(c, in, &o->connected, 0) &&
|
||||
ast_channel_connected_line_macro(c, in, &o->connected, 1, 0)) {
|
||||
ast_channel_update_connected_line(in, &o->connected, NULL);
|
||||
}
|
||||
} else if (!ast_test_flag64(o, DIAL_CALLERID_ABSENT)) {
|
||||
|
@ -1505,8 +1542,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
* fine for ringing frames to get sent through.
|
||||
*/
|
||||
++num_ringing;
|
||||
*to_progress = -1;
|
||||
progress_to_dup = -1;
|
||||
if (ignore_cc || cc_frame_received || num_ringing == numlines) {
|
||||
ast_verb(3, "%s is ringing\n", ast_channel_name(c));
|
||||
/* Setup early media if appropriate */
|
||||
|
@ -1550,8 +1585,6 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_indicate(in, AST_CONTROL_PROGRESS);
|
||||
}
|
||||
}
|
||||
*to_progress = -1;
|
||||
progress_to_dup = -1;
|
||||
if (!sent_progress) {
|
||||
struct timeval now, then;
|
||||
int64_t diff;
|
||||
|
@ -1574,7 +1607,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
"Sending MF '%s' to %s as result of "
|
||||
"receiving a PROGRESS message.\n",
|
||||
mf_progress, hearpulsing ? "parties" : "called party");
|
||||
res |= ast_mf_stream(c, (hearpulsing ? NULL : in),
|
||||
ast_mf_stream(c, (hearpulsing ? NULL : in),
|
||||
(hearpulsing ? in : NULL), mf_progress, 50, 55, 120, 65, 0);
|
||||
}
|
||||
if (!ast_strlen_zero(sf_progress)) {
|
||||
|
@ -1582,7 +1615,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
"Sending SF '%s' to %s as result of "
|
||||
"receiving a PROGRESS message.\n",
|
||||
sf_progress, (hearpulsing ? "parties" : "called party"));
|
||||
res |= ast_sf_stream(c, (hearpulsing ? NULL : in),
|
||||
ast_sf_stream(c, (hearpulsing ? NULL : in),
|
||||
(hearpulsing ? in : NULL), sf_progress, 0, 0);
|
||||
}
|
||||
if (!ast_strlen_zero(dtmf_progress)) {
|
||||
|
@ -1590,11 +1623,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
"Sending DTMF '%s' to the called party as result of "
|
||||
"receiving a PROGRESS message.\n",
|
||||
dtmf_progress);
|
||||
res |= ast_dtmf_stream(c, in, dtmf_progress, 250, 0);
|
||||
}
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Called channel %s hung up post-progress before all digits could be sent\n", ast_channel_name(c));
|
||||
goto wait_over;
|
||||
ast_dtmf_stream(c, in, dtmf_progress, 250, 0);
|
||||
}
|
||||
}
|
||||
ast_channel_publish_dial(in, c, NULL, "PROGRESS");
|
||||
|
@ -1608,7 +1637,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
"Sending MF '%s' to %s as result of "
|
||||
"receiving a WINK message.\n",
|
||||
mf_wink, (hearpulsing ? "parties" : "called party"));
|
||||
res |= ast_mf_stream(c, (hearpulsing ? NULL : in),
|
||||
ast_mf_stream(c, (hearpulsing ? NULL : in),
|
||||
(hearpulsing ? in : NULL), mf_wink, 50, 55, 120, 65, 0);
|
||||
}
|
||||
if (!ast_strlen_zero(sf_wink)) {
|
||||
|
@ -1616,13 +1645,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
"Sending SF '%s' to %s as result of "
|
||||
"receiving a WINK message.\n",
|
||||
sf_wink, (hearpulsing ? "parties" : "called party"));
|
||||
res |= ast_sf_stream(c, (hearpulsing ? NULL : in),
|
||||
ast_sf_stream(c, (hearpulsing ? NULL : in),
|
||||
(hearpulsing ? in : NULL), sf_wink, 0, 0);
|
||||
}
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "Called channel %s hung up post-wink before all digits could be sent\n", ast_channel_name(c));
|
||||
goto wait_over;
|
||||
}
|
||||
}
|
||||
ast_indicate(in, AST_CONTROL_WINK);
|
||||
break;
|
||||
|
@ -1653,7 +1678,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
o->pending_connected_update = 1;
|
||||
break;
|
||||
}
|
||||
if (ast_channel_connected_line_sub(c, in, f, 1)) {
|
||||
if (ast_channel_connected_line_sub(c, in, f, 1) &&
|
||||
ast_channel_connected_line_macro(c, in, f, 1, 1)) {
|
||||
ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
|
||||
}
|
||||
break;
|
||||
|
@ -1682,7 +1708,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
}
|
||||
ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
|
||||
ast_channel_name(c), ast_channel_name(in));
|
||||
if (ast_channel_redirecting_sub(c, in, f, 1)) {
|
||||
if (ast_channel_redirecting_sub(c, in, f, 1) &&
|
||||
ast_channel_redirecting_macro(c, in, f, 1, 1)) {
|
||||
ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
|
||||
}
|
||||
pa->sentringing = 0;
|
||||
|
@ -1735,13 +1762,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
case AST_FRAME_VIDEO:
|
||||
case AST_FRAME_VOICE:
|
||||
case AST_FRAME_IMAGE:
|
||||
case AST_FRAME_DTMF_BEGIN:
|
||||
case AST_FRAME_DTMF_END:
|
||||
if (caller_entertained) {
|
||||
break;
|
||||
}
|
||||
*to_progress = -1;
|
||||
progress_to_dup = -1;
|
||||
/* Fall through */
|
||||
case AST_FRAME_TEXT:
|
||||
if (single && ast_write(in, f)) {
|
||||
|
@ -1770,7 +1793,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
#endif
|
||||
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
|
||||
/* Got hung up */
|
||||
*to_answer = -1;
|
||||
*to = -1;
|
||||
strcpy(pa->status, "CANCEL");
|
||||
pa->canceled = 1;
|
||||
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
||||
|
@ -1794,7 +1817,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
|
||||
if (onedigit_goto(in, context, (char) f->subclass.integer, 1)) {
|
||||
ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
|
||||
*to_answer = 0;
|
||||
*to = 0;
|
||||
*result = f->subclass.integer;
|
||||
strcpy(pa->status, "CANCEL");
|
||||
pa->canceled = 1;
|
||||
|
@ -1813,7 +1836,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) &&
|
||||
detect_disconnect(in, f->subclass.integer, &featurecode)) {
|
||||
ast_verb(3, "User requested call disconnect.\n");
|
||||
*to_answer = 0;
|
||||
*to = 0;
|
||||
strcpy(pa->status, "CANCEL");
|
||||
pa->canceled = 1;
|
||||
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
||||
|
@ -1894,7 +1917,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(o->chan));
|
||||
break;
|
||||
}
|
||||
if (ast_channel_connected_line_sub(in, o->chan, f, 1)) {
|
||||
if (ast_channel_connected_line_sub(in, o->chan, f, 1) &&
|
||||
ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) {
|
||||
ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
|
||||
}
|
||||
break;
|
||||
|
@ -1903,7 +1927,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
|||
ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
|
||||
break;
|
||||
}
|
||||
if (ast_channel_redirecting_sub(in, o->chan, f, 1)) {
|
||||
if (ast_channel_redirecting_sub(in, o->chan, f, 1) &&
|
||||
ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) {
|
||||
ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
|
||||
}
|
||||
break;
|
||||
|
@ -1922,15 +1947,9 @@ skip_frame:;
|
|||
}
|
||||
}
|
||||
|
||||
wait_over:
|
||||
if (!*to_answer || ast_check_hangup(in)) {
|
||||
ast_verb(3, "Nobody picked up in %d ms\n", orig_answer_to);
|
||||
if (!*to || ast_check_hangup(in)) {
|
||||
ast_verb(3, "Nobody picked up in %d ms\n", orig);
|
||||
publish_dial_end_event(in, out_chans, NULL, "NOANSWER");
|
||||
} else if (!*to_progress) {
|
||||
ast_verb(3, "No early media received in %d ms\n", orig_progress_to);
|
||||
publish_dial_end_event(in, out_chans, NULL, "CHANUNAVAIL");
|
||||
strcpy(pa->status, "CHANUNAVAIL");
|
||||
*to_answer = 0; /* Reset to prevent hangup */
|
||||
}
|
||||
|
||||
if (is_cc_recall) {
|
||||
|
@ -2302,7 +2321,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
struct chanlist *outgoing;
|
||||
struct chanlist *tmp;
|
||||
struct ast_channel *peer = NULL;
|
||||
int to_answer, to_progress; /* timeouts */
|
||||
int to; /* timeout */
|
||||
struct cause_args num = { chan, 0, 0, 0 };
|
||||
int cause, hanguptreecause = -1;
|
||||
|
||||
|
@ -2358,7 +2377,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
*/
|
||||
struct ast_party_caller caller;
|
||||
int max_forwards;
|
||||
struct ast_datastore *topology_ds = NULL;
|
||||
SCOPE_ENTER(1, "%s: Data: %s\n", ast_channel_name(chan), data);
|
||||
|
||||
/* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
|
||||
|
@ -2467,7 +2485,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
if (ast_test_flag64(&opts, OPT_FORCECLID)) {
|
||||
if (ast_strlen_zero(opt_args[OPT_ARG_FORCECLID])) {
|
||||
ast_channel_lock(chan);
|
||||
forced_clid.number.str = ast_strdupa(ast_channel_exten(chan));
|
||||
forced_clid.number.str = ast_strdupa(S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)));
|
||||
ast_channel_unlock(chan);
|
||||
forced_clid_name[0] = '\0';
|
||||
forced_clid.name.str = (char *) get_cid_name(forced_clid_name,
|
||||
|
@ -2544,7 +2562,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
stored_clid.name.valid = 1;
|
||||
}
|
||||
ast_channel_lock(chan);
|
||||
stored_clid.number.str = ast_strdupa(ast_channel_exten(chan));
|
||||
stored_clid.number.str = ast_strdupa(S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan)));
|
||||
stored_clid.number.valid = 1;
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
|
@ -2579,7 +2597,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
/* Set per dial instance flags. These flags are also passed back to RetryDial. */
|
||||
ast_copy_flags64(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID
|
||||
| OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING | OPT_CANCEL_TIMEOUT
|
||||
| OPT_ANNOUNCE | OPT_CALLEE_GOSUB | OPT_FORCECLID);
|
||||
| OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB | OPT_FORCECLID);
|
||||
|
||||
/* PREDIAL: Run gosub on the caller's channel */
|
||||
if (ast_test_flag64(&opts, OPT_PREDIAL_CALLER)
|
||||
|
@ -2660,21 +2678,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
*/
|
||||
ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(chan));
|
||||
|
||||
if (ast_test_flag64(&opts, OPT_TOPOLOGY_PRESERVE)) {
|
||||
topology_ds = ast_channel_datastore_find(chan, &topology_ds_info, NULL);
|
||||
|
||||
if (!topology_ds && (topology_ds = ast_datastore_alloc(&topology_ds_info, NULL))) {
|
||||
topology_ds->data = ast_stream_topology_clone(ast_channel_get_stream_topology(chan));
|
||||
ast_channel_datastore_add(chan, topology_ds);
|
||||
}
|
||||
}
|
||||
|
||||
if (topology_ds) {
|
||||
ao2_ref(topology_ds->data, +1);
|
||||
topology = topology_ds->data;
|
||||
} else {
|
||||
topology = ast_stream_topology_clone(ast_channel_get_stream_topology(chan));
|
||||
}
|
||||
topology = ast_stream_topology_clone(ast_channel_get_stream_topology(chan));
|
||||
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
|
@ -2819,8 +2823,11 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
|
||||
|
||||
/* Inherit context and extension */
|
||||
ast_channel_dialcontext_set(tc, ast_channel_context(chan));
|
||||
ast_channel_exten_set(tc, ast_channel_exten(chan));
|
||||
ast_channel_dialcontext_set(tc, ast_strlen_zero(ast_channel_macrocontext(chan)) ? ast_channel_context(chan) : ast_channel_macrocontext(chan));
|
||||
if (!ast_strlen_zero(ast_channel_macroexten(chan)))
|
||||
ast_channel_exten_set(tc, ast_channel_macroexten(chan));
|
||||
else
|
||||
ast_channel_exten_set(tc, ast_channel_exten(chan));
|
||||
|
||||
ast_channel_stage_snapshot_done(tc);
|
||||
|
||||
|
@ -2913,31 +2920,14 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
|
||||
if (ast_strlen_zero(args.timeout)) {
|
||||
to_answer = -1;
|
||||
to_progress = -1;
|
||||
to = -1;
|
||||
} else {
|
||||
char *anstimeout = strsep(&args.timeout, "^");
|
||||
if (!ast_strlen_zero(anstimeout)) {
|
||||
to_answer = atoi(anstimeout);
|
||||
if (to_answer > 0) {
|
||||
to_answer *= 1000;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Invalid answer timeout specified: '%s'. Setting timeout to infinite\n", args.timeout);
|
||||
to_answer = -1;
|
||||
}
|
||||
} else {
|
||||
to_answer = -1;
|
||||
}
|
||||
if (!ast_strlen_zero(args.timeout)) {
|
||||
to_progress = atoi(args.timeout);
|
||||
if (to_progress > 0) {
|
||||
to_progress *= 1000;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Invalid progress timeout specified: '%s'. Setting timeout to infinite\n", args.timeout);
|
||||
to_progress = -1;
|
||||
}
|
||||
} else {
|
||||
to_progress = -1;
|
||||
to = atoi(args.timeout);
|
||||
if (to > 0)
|
||||
to *= 1000;
|
||||
else {
|
||||
ast_log(LOG_WARNING, "Invalid timeout specified: '%s'. Setting timeout to infinite\n", args.timeout);
|
||||
to = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2977,7 +2967,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
}
|
||||
}
|
||||
|
||||
peer = wait_for_answer(chan, &out_chans, &to_answer, &to_progress, peerflags, opt_args, &pa, &num, &result,
|
||||
peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,
|
||||
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);
|
||||
|
@ -2985,7 +2975,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
if (!peer) {
|
||||
if (result) {
|
||||
res = result;
|
||||
} else if (to_answer) { /* Musta gotten hung up */
|
||||
} else if (to) { /* Musta gotten hung up */
|
||||
res = -1;
|
||||
} else { /* Nobody answered, next please? */
|
||||
res = 0;
|
||||
|
@ -3160,7 +3150,9 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
} else {
|
||||
other_chan = chan;
|
||||
}
|
||||
if (ast_channel_connected_line_sub(active_chan, other_chan, fr, 1)) {
|
||||
if (ast_channel_connected_line_sub(active_chan, other_chan, fr, 1)
|
||||
&& ast_channel_connected_line_macro(active_chan,
|
||||
other_chan, fr, other_chan == chan, 1)) {
|
||||
ast_indicate_data(other_chan, fr->subclass.integer,
|
||||
fr->data.ptr, fr->datalen);
|
||||
}
|
||||
|
@ -3207,6 +3199,66 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
|||
goto done;
|
||||
}
|
||||
|
||||
if (ast_test_flag64(&opts, OPT_CALLEE_MACRO) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_MACRO])) {
|
||||
const char *macro_result_peer;
|
||||
int macro_res;
|
||||
|
||||
/* Set peer->exten and peer->context so that MACRO_EXTEN and MACRO_CONTEXT get set */
|
||||
ast_channel_lock_both(chan, peer);
|
||||
ast_channel_context_set(peer, ast_channel_context(chan));
|
||||
ast_channel_exten_set(peer, ast_channel_exten(chan));
|
||||
ast_channel_unlock(peer);
|
||||
ast_channel_unlock(chan);
|
||||
ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_MACRO]);
|
||||
macro_res = ast_app_exec_macro(chan, peer, opt_args[OPT_ARG_CALLEE_MACRO]);
|
||||
|
||||
ast_channel_lock(peer);
|
||||
|
||||
if (!macro_res && (macro_result_peer = pbx_builtin_getvar_helper(peer, "MACRO_RESULT"))) {
|
||||
char *macro_result = ast_strdupa(macro_result_peer);
|
||||
char *macro_transfer_dest;
|
||||
|
||||
ast_channel_unlock(peer);
|
||||
|
||||
if (!strcasecmp(macro_result, "BUSY")) {
|
||||
ast_copy_string(pa.status, macro_result, sizeof(pa.status));
|
||||
ast_set_flag64(peerflags, OPT_GO_ON);
|
||||
macro_res = -1;
|
||||
} else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) {
|
||||
ast_copy_string(pa.status, macro_result, sizeof(pa.status));
|
||||
ast_set_flag64(peerflags, OPT_GO_ON);
|
||||
macro_res = -1;
|
||||
} else if (!strcasecmp(macro_result, "CONTINUE")) {
|
||||
/* hangup peer and keep chan alive assuming the macro has changed
|
||||
the context / exten / priority or perhaps
|
||||
the next priority in the current exten is desired.
|
||||
*/
|
||||
ast_set_flag64(peerflags, OPT_GO_ON);
|
||||
macro_res = -1;
|
||||
} else if (!strcasecmp(macro_result, "ABORT")) {
|
||||
/* Hangup both ends unless the caller has the g flag */
|
||||
macro_res = -1;
|
||||
} else if (!strncasecmp(macro_result, "GOTO:", 5)) {
|
||||
macro_transfer_dest = macro_result + 5;
|
||||
macro_res = -1;
|
||||
/* perform a transfer to a new extension */
|
||||
if (strchr(macro_transfer_dest, '^')) { /* context^exten^priority*/
|
||||
ast_replace_subargument_delimiter(macro_transfer_dest);
|
||||
}
|
||||
if (!ast_parseable_goto(chan, macro_transfer_dest)) {
|
||||
ast_set_flag64(peerflags, OPT_GO_ON);
|
||||
}
|
||||
}
|
||||
if (macro_res && !dial_end_raised) {
|
||||
ast_channel_publish_dial(chan, peer, NULL, macro_result);
|
||||
dial_end_raised = 1;
|
||||
}
|
||||
} else {
|
||||
ast_channel_unlock(peer);
|
||||
}
|
||||
res = macro_res;
|
||||
}
|
||||
|
||||
if (ast_test_flag64(&opts, OPT_CALLEE_GOSUB) && !ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GOSUB])) {
|
||||
const char *gosub_result_peer;
|
||||
char *gosub_argstart;
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#include "asterisk/say.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/adsi.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Directory" language="en_US">
|
||||
|
@ -104,36 +103,23 @@
|
|||
receiver to their ear while entering DTMF.</para>
|
||||
<argument name="n" required="true" />
|
||||
</option>
|
||||
<option name="c">
|
||||
<para>Load the specified config file instead of voicemail.conf</para>
|
||||
<argument name="filename" required="true" />
|
||||
</option>
|
||||
<option name="s">
|
||||
<para>Skip calling the extension, instead set it in the <variable>DIRECTORY_EXTEN</variable>
|
||||
channel variable.</para>
|
||||
</option>
|
||||
<option name="d">
|
||||
<para>Enable ADSI support for screen phone searching and retrieval
|
||||
of directory results.</para>
|
||||
<para>Additionally, the channel must be ADSI-enabled and you must
|
||||
have an ADSI-compatible (Type III) CPE for this to work.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
<note><para>Only one of the <replaceable>f</replaceable>, <replaceable>l</replaceable>, or <replaceable>b</replaceable>
|
||||
options may be specified. <emphasis>If more than one is specified</emphasis>, then Directory will act as
|
||||
if <replaceable>b</replaceable> was specified. The number
|
||||
of characters for the user to type defaults to <literal>3</literal>.</para></note>
|
||||
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This application will present the calling channel with a directory of extensions from which they can search
|
||||
by name. The list of names and corresponding extensions is retrieved from the
|
||||
voicemail configuration file, <filename>voicemail.conf</filename>, or from the specified filename.</para>
|
||||
voicemail configuration file, <filename>voicemail.conf</filename>.</para>
|
||||
<para>This application will immediately exit if one of the following DTMF digits are
|
||||
received and the extension to jump to exists:</para>
|
||||
<para><literal>0</literal> - Jump to the 'o' extension, if it exists.</para>
|
||||
<para><literal>*</literal> - Jump to the 'a' extension, if it exists.</para>
|
||||
<para>This application will set the following channel variables before completion:</para>
|
||||
<para>This application will set the following channel variable before completion:</para>
|
||||
<variablelist>
|
||||
<variable name="DIRECTORY_RESULT">
|
||||
<para>Reason Directory application exited.</para>
|
||||
|
@ -145,10 +131,6 @@
|
|||
<value name="USEREXIT">User exited with '#' during selection</value>
|
||||
<value name="FAILED">The application failed</value>
|
||||
</variable>
|
||||
<variable name="DIRECTORY_EXTEN">
|
||||
<para>If the skip calling option is set this will be set to the selected extension
|
||||
provided one is selected.</para>
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
</application>
|
||||
|
@ -171,9 +153,6 @@ enum {
|
|||
OPT_PAUSE = (1 << 5),
|
||||
OPT_NOANSWER = (1 << 6),
|
||||
OPT_ALIAS = (1 << 7),
|
||||
OPT_CONFIG_FILE = (1 << 8),
|
||||
OPT_SKIP = (1 << 9),
|
||||
OPT_ADSI = (1 << 10),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -181,9 +160,8 @@ enum {
|
|||
OPT_ARG_LASTNAME = 1,
|
||||
OPT_ARG_EITHER = 2,
|
||||
OPT_ARG_PAUSE = 3,
|
||||
OPT_ARG_FILENAME = 4,
|
||||
/* This *must* be the last value in this enum! */
|
||||
OPT_ARG_ARRAY_SIZE = 5,
|
||||
OPT_ARG_ARRAY_SIZE = 4,
|
||||
};
|
||||
|
||||
struct directory_item {
|
||||
|
@ -205,74 +183,8 @@ AST_APP_OPTIONS(directory_app_options, {
|
|||
AST_APP_OPTION('m', OPT_SELECTFROMMENU),
|
||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||
AST_APP_OPTION('a', OPT_ALIAS),
|
||||
AST_APP_OPTION_ARG('c', OPT_CONFIG_FILE, OPT_ARG_FILENAME),
|
||||
AST_APP_OPTION('s', OPT_SKIP),
|
||||
AST_APP_OPTION('d', OPT_ADSI), /* (Would've used 'a', but that was taken already) */
|
||||
});
|
||||
|
||||
static int adsi_search_input(struct ast_channel *chan)
|
||||
{
|
||||
unsigned char buf[256];
|
||||
int bytes = 0;
|
||||
unsigned char keys[6];
|
||||
|
||||
memset(keys, 0, sizeof(keys));
|
||||
|
||||
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
|
||||
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
|
||||
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
|
||||
bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Query: ***", "");
|
||||
bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Search", "Search", "#", 1);
|
||||
bytes += ast_adsi_set_keys(buf + bytes, keys);
|
||||
bytes += ast_adsi_voice_mode(buf + bytes, 0);
|
||||
|
||||
ast_debug(3, "Sending ADSI search input screen on %s\n", ast_channel_name(chan));
|
||||
|
||||
return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
|
||||
}
|
||||
|
||||
static int adsi_confirm_match(struct ast_channel *chan, int seq, int total, const char *exten, const char *name, int showexten)
|
||||
{
|
||||
unsigned char buf[4096];
|
||||
int alignments[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
|
||||
char *lines[5] = {NULL, NULL, NULL, NULL, NULL};
|
||||
int x, bytes = 0;
|
||||
unsigned char keys[8];
|
||||
char matchbuf[32];
|
||||
|
||||
snprintf(matchbuf, sizeof(matchbuf), "%d of %d", seq + 1, total); /* Make it 1-indexed for user consumption */
|
||||
|
||||
lines[0] = " "; /* Leave the first line empty so the following lines stand out more */
|
||||
lines[1] = matchbuf;
|
||||
lines[2] = (char*) name;
|
||||
|
||||
if (showexten) {
|
||||
/* If say extension option is set, show it for ADSI as well */
|
||||
lines[3] = (char*) exten;
|
||||
}
|
||||
|
||||
/* Don't use ast_adsi_print here, this way we can send it all at once instead of in 2 transmissions */
|
||||
for (x = 0; lines[x]; x++) {
|
||||
bytes += ast_adsi_display(buf + bytes, ADSI_INFO_PAGE, x + 1, alignments[x], 0, lines[x], "");
|
||||
}
|
||||
bytes += ast_adsi_set_line(buf + bytes, ADSI_INFO_PAGE, 1);
|
||||
|
||||
keys[3] = ADSI_KEY_APPS + 3;
|
||||
keys[4] = ADSI_KEY_APPS + 4;
|
||||
keys[5] = ADSI_KEY_APPS + 5;
|
||||
/* You might think we only need to set the keys up the first time, but nope, we've got to do it each time. */
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Dial", "Dial", "1", 0);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Next", "Next", "*", 0);
|
||||
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 0);
|
||||
bytes += ast_adsi_set_keys(buf + bytes, keys);
|
||||
bytes += ast_adsi_voice_mode(buf + bytes, 0);
|
||||
|
||||
ast_debug(3, "Sending ADSI confirmation menu for %s\n", name);
|
||||
|
||||
return ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
|
||||
}
|
||||
|
||||
static int compare(const char *text, const char *template)
|
||||
{
|
||||
char digit;
|
||||
|
@ -355,7 +267,9 @@ static int compare(const char *text, const char *template)
|
|||
|
||||
static int goto_exten(struct ast_channel *chan, const char *dialcontext, char *ext)
|
||||
{
|
||||
if (!ast_goto_if_exists(chan, S_OR(dialcontext, ast_channel_context(chan)), ext, 1)) {
|
||||
if (!ast_goto_if_exists(chan, S_OR(dialcontext, ast_channel_context(chan)), ext, 1) ||
|
||||
(!ast_strlen_zero(ast_channel_macrocontext(chan)) &&
|
||||
!ast_goto_if_exists(chan, ast_channel_macrocontext(chan), ext, 1))) {
|
||||
return 0;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Can't find extension '%s' in current context. "
|
||||
|
@ -404,9 +318,6 @@ static int select_entry(struct ast_channel *chan, const char *dialcontext, const
|
|||
if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
|
||||
/* We still want to set the exten though */
|
||||
ast_channel_exten_set(chan, item->exten);
|
||||
} else if (ast_test_flag(flags, OPT_SKIP)) {
|
||||
/* Skip calling the extension, only set it in the channel variable. */
|
||||
pbx_builtin_setvar_helper(chan, "DIRECTORY_EXTEN", item->exten);
|
||||
} else if (ast_goto_if_exists(chan, S_OR(dialcontext, item->context), item->exten, 1)) {
|
||||
ast_log(LOG_WARNING,
|
||||
"Can't find extension '%s' in context '%s'. "
|
||||
|
@ -445,10 +356,6 @@ static int select_item_seq(struct ast_channel *chan, struct directory_item **ite
|
|||
for (ptr = items, i = 0; i < count; i++, ptr++) {
|
||||
item = *ptr;
|
||||
|
||||
if (ast_test_flag(flags, OPT_ADSI) && adsi_confirm_match(chan, i, count, item->exten, item->name, ast_test_flag(flags, OPT_SAYEXTENSION))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (loop = 3 ; loop > 0; loop--) {
|
||||
if (!res)
|
||||
res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
|
||||
|
@ -551,7 +458,7 @@ static int select_item_menu(struct ast_channel *chan, struct directory_item **it
|
|||
|
||||
AST_THREADSTORAGE(commonbuf);
|
||||
|
||||
static struct ast_config *realtime_directory(char *context, const char *filename)
|
||||
static struct ast_config *realtime_directory(char *context)
|
||||
{
|
||||
struct ast_config *cfg;
|
||||
struct ast_config *rtdata = NULL;
|
||||
|
@ -568,14 +475,14 @@ static struct ast_config *realtime_directory(char *context, const char *filename
|
|||
}
|
||||
|
||||
/* Load flat file config. */
|
||||
cfg = ast_config_load(filename, config_flags);
|
||||
cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
|
||||
|
||||
if (!cfg) {
|
||||
/* Loading config failed. */
|
||||
ast_log(LOG_WARNING, "Loading config failed.\n");
|
||||
return NULL;
|
||||
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
||||
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", filename);
|
||||
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", VOICEMAIL_CONFIG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -960,9 +867,7 @@ static int directory_exec(struct ast_channel *chan, const char *data)
|
|||
if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
|
||||
return -1;
|
||||
|
||||
cfg = realtime_directory(args.vmcontext, S_OR(opts[OPT_ARG_FILENAME], VOICEMAIL_CONFIG));
|
||||
|
||||
if (!cfg) {
|
||||
if (!(cfg = realtime_directory(args.vmcontext))) {
|
||||
ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -1008,18 +913,6 @@ static int directory_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
digits[7] = digit + '0';
|
||||
|
||||
if (ast_test_flag(&flags, OPT_ADSI)) {
|
||||
if (!ast_adsi_available(chan)) {
|
||||
ast_log(LOG_WARNING, "ADSI not available on %s\n", ast_channel_name(chan));
|
||||
ast_clear_flag(&flags, OPT_ADSI);
|
||||
} else {
|
||||
res = ast_adsi_load_session(chan, NULL, 0, 1);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_channel_state(chan) != AST_STATE_UP) {
|
||||
if (!ast_test_flag(&flags, OPT_NOANSWER)) {
|
||||
/* Otherwise answer unless we're supposed to read while on-hook */
|
||||
|
@ -1027,9 +920,6 @@ static int directory_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
}
|
||||
for (;;) {
|
||||
if (ast_test_flag(&flags, OPT_ADSI) && adsi_search_input(chan)) {
|
||||
return -1;
|
||||
}
|
||||
if (!ast_strlen_zero(dirintro) && !res) {
|
||||
res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
|
||||
} else if (!res) {
|
||||
|
|
|
@ -836,9 +836,8 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
|
|||
}
|
||||
}
|
||||
} else {
|
||||
ast_debug(1, "Taking call with no prompt\n");
|
||||
ast_frfree(f);
|
||||
return tmpuser->ochan;
|
||||
ast_verb(3, "Skip playback of caller name / norecording\n");
|
||||
tmpuser->state = 2;
|
||||
}
|
||||
break;
|
||||
case AST_CONTROL_BUSY:
|
||||
|
@ -965,6 +964,11 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!tpargs->enable_callee_prompt && tmpuser) {
|
||||
ast_debug(1, "Taking call with no prompt\n");
|
||||
ast_frfree(f);
|
||||
return tmpuser->ochan;
|
||||
}
|
||||
if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
|
||||
int cmp_len;
|
||||
|
||||
|
@ -1068,7 +1072,6 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
|
|||
ast_copy_string(num, nm->number, sizeof(num));
|
||||
for (number = num; number; number = rest) {
|
||||
struct ast_channel *outbound;
|
||||
struct ast_format_cap *caps;
|
||||
|
||||
rest = strchr(number, '&');
|
||||
if (rest) {
|
||||
|
@ -1098,15 +1101,8 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
|
|||
? "/n" : "/m");
|
||||
}
|
||||
|
||||
/* Capture nativeformats reference in case it gets changed */
|
||||
ast_channel_lock(caller);
|
||||
caps = ao2_bump(ast_channel_nativeformats(caller));
|
||||
ast_channel_unlock(caller);
|
||||
|
||||
outbound = ast_request("Local", caps, NULL, caller, tmpuser->dialarg, &dg);
|
||||
|
||||
ao2_cleanup(caps);
|
||||
|
||||
outbound = ast_request("Local", ast_channel_nativeformats(caller), NULL, caller,
|
||||
tmpuser->dialarg, &dg);
|
||||
if (!outbound) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n",
|
||||
tmpuser->dialarg, ast_cause2str(dg));
|
||||
|
@ -1531,7 +1527,8 @@ static int app_exec(struct ast_channel *chan, const char *data)
|
|||
|
||||
/* Update connected line to caller if available. */
|
||||
if (targs->pending_out_connected_update) {
|
||||
if (ast_channel_connected_line_sub(outbound, caller, &targs->connected_out, 0)) {
|
||||
if (ast_channel_connected_line_sub(outbound, caller, &targs->connected_out, 0) &&
|
||||
ast_channel_connected_line_macro(outbound, caller, &targs->connected_out, 1, 0)) {
|
||||
ast_channel_update_connected_line(caller, &targs->connected_out, NULL);
|
||||
}
|
||||
}
|
||||
|
@ -1556,7 +1553,8 @@ static int app_exec(struct ast_channel *chan, const char *data)
|
|||
|
||||
/* Update connected line to winner if changed. */
|
||||
if (targs->pending_in_connected_update) {
|
||||
if (ast_channel_connected_line_sub(caller, outbound, &targs->connected_in, 0)) {
|
||||
if (ast_channel_connected_line_sub(caller, outbound, &targs->connected_in, 0) &&
|
||||
ast_channel_connected_line_macro(caller, outbound, &targs->connected_in, 0, 0)) {
|
||||
ast_channel_update_connected_line(outbound, &targs->connected_in, NULL);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
</description>
|
||||
<see-also>
|
||||
<ref type="function">CDR</ref>
|
||||
<ref type="function">CDR_PROP</ref>
|
||||
<ref type="application">NoCDR</ref>
|
||||
<ref type="application">ResetCDR</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
|
|
390
apps/app_if.c
390
apps/app_if.c
|
@ -1,390 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright 2022, Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* 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 If Branch Implementation
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/channel.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="If" language="en_US">
|
||||
<synopsis>
|
||||
Start an if branch.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="expr" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Start an If branch. Execution will continue inside the branch
|
||||
if expr is true.</para>
|
||||
<note><para>This application (and related applications) set variables
|
||||
internally during execution.</para></note>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="ElseIf" language="en_US">
|
||||
<synopsis>
|
||||
Start an else if branch.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="expr" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Start an optional ElseIf branch. Execution will continue inside the branch
|
||||
if expr is true and if previous If and ElseIf branches evaluated to false.</para>
|
||||
<para>Please note that execution inside a true If branch will fallthrough into
|
||||
ElseIf unless the If segment is terminated with an ExitIf call. This is only
|
||||
necessary with ElseIf but not with Else.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="Else" language="en_US">
|
||||
<synopsis>
|
||||
Define an optional else branch.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="expr" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Start an Else branch. Execution will jump here if all previous
|
||||
If and ElseIf branches evaluated to false.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="EndIf" language="en_US">
|
||||
<synopsis>
|
||||
End an if branch.
|
||||
</synopsis>
|
||||
<syntax />
|
||||
<description>
|
||||
<para>Ends the branch begun by the preceding <literal>If()</literal> application.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">ExitIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="ExitIf" language="en_US">
|
||||
<synopsis>
|
||||
End an If branch.
|
||||
</synopsis>
|
||||
<syntax />
|
||||
<description>
|
||||
<para>Exits an <literal>If()</literal> branch, whether or not it has completed.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">If</ref>
|
||||
<ref type="application">ElseIf</ref>
|
||||
<ref type="application">Else</ref>
|
||||
<ref type="application">EndIf</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static char *if_app = "If";
|
||||
static char *elseif_app = "ElseIf";
|
||||
static char *else_app = "Else";
|
||||
static char *stop_app = "EndIf";
|
||||
static char *exit_app = "ExitIf";
|
||||
|
||||
#define VAR_SIZE 64
|
||||
|
||||
static const char *get_index(struct ast_channel *chan, const char *prefix, int idx)
|
||||
{
|
||||
char varname[VAR_SIZE];
|
||||
|
||||
snprintf(varname, VAR_SIZE, "%s_%d", prefix, idx);
|
||||
return pbx_builtin_getvar_helper(chan, varname);
|
||||
}
|
||||
|
||||
static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
|
||||
{
|
||||
struct ast_exten *e;
|
||||
struct ast_context *c2;
|
||||
int idx;
|
||||
|
||||
for (e = ast_walk_context_extensions(c, NULL); e; e = ast_walk_context_extensions(c, e)) {
|
||||
if (ast_extension_match(ast_get_extension_name(e), exten)) {
|
||||
int needmatch = ast_get_extension_matchcid(e);
|
||||
if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
|
||||
(!needmatch)) {
|
||||
/* This is the matching extension we want */
|
||||
struct ast_exten *p;
|
||||
for (p = ast_walk_extension_priorities(e, NULL); p; p = ast_walk_extension_priorities(e, p)) {
|
||||
if (priority != ast_get_extension_priority(p))
|
||||
continue;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No match; run through includes */
|
||||
for (idx = 0; idx < ast_context_includes_count(c); idx++) {
|
||||
const struct ast_include *i = ast_context_includes_get(c, idx);
|
||||
|
||||
for (c2 = ast_walk_contexts(NULL); c2; c2 = ast_walk_contexts(c2)) {
|
||||
if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
|
||||
e = find_matching_priority(c2, exten, priority, callerid);
|
||||
if (e)
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_matching_endif(struct ast_channel *chan, const char *otherapp)
|
||||
{
|
||||
struct ast_context *c;
|
||||
int res = -1;
|
||||
|
||||
if (ast_rdlock_contexts()) {
|
||||
ast_log(LOG_ERROR, "Failed to lock contexts list\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (c = ast_walk_contexts(NULL); c; c = ast_walk_contexts(c)) {
|
||||
struct ast_exten *e;
|
||||
|
||||
if (!ast_rdlock_context(c)) {
|
||||
if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
|
||||
/* This is the matching context we want */
|
||||
|
||||
int cur_priority = ast_channel_priority(chan) + 1, level = 1;
|
||||
|
||||
for (e = find_matching_priority(c, ast_channel_exten(chan), cur_priority,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
|
||||
e;
|
||||
e = find_matching_priority(c, ast_channel_exten(chan), ++cur_priority,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
|
||||
if (!strcasecmp(ast_get_extension_app(e), "IF")) {
|
||||
level++;
|
||||
} else if (!strcasecmp(ast_get_extension_app(e), "ENDIF")) {
|
||||
level--;
|
||||
}
|
||||
|
||||
if (!otherapp && level == 0) {
|
||||
res = cur_priority;
|
||||
break;
|
||||
} else if (otherapp && level == 1 && !strcasecmp(ast_get_extension_app(e), otherapp)) {
|
||||
res = cur_priority;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_unlock_context(c);
|
||||
if (res > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_unlock_contexts();
|
||||
return res;
|
||||
}
|
||||
|
||||
static int if_helper(struct ast_channel *chan, const char *data, int end)
|
||||
{
|
||||
int res = 0;
|
||||
const char *if_pri = NULL;
|
||||
char *my_name = NULL;
|
||||
const char *label = NULL;
|
||||
char varname[VAR_SIZE + 3]; /* + IF_ */
|
||||
char end_varname[sizeof(varname) + 4]; /* + END_ + sizeof(varname) */
|
||||
const char *prefix = "IF";
|
||||
size_t size = 0;
|
||||
int used_index_i = -1, x = 0;
|
||||
char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
|
||||
|
||||
if (!chan) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (x = 0 ;; x++) {
|
||||
if (get_index(chan, prefix, x)) {
|
||||
used_index_i = x;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(used_index, sizeof(used_index), "%d", used_index_i);
|
||||
snprintf(new_index, sizeof(new_index), "%d", used_index_i + 1);
|
||||
|
||||
size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
|
||||
my_name = ast_alloca(size);
|
||||
memset(my_name, 0, size);
|
||||
snprintf(my_name, size, "%s_%s_%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (end > 1) {
|
||||
label = used_index;
|
||||
} else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
|
||||
label = new_index;
|
||||
pbx_builtin_setvar_helper(chan, my_name, label);
|
||||
}
|
||||
snprintf(varname, sizeof(varname), "%s_%s", prefix, label);
|
||||
if ((if_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
|
||||
if_pri = ast_strdupa(if_pri);
|
||||
snprintf(end_varname,sizeof(end_varname),"END_%s",varname);
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
if ((end <= 1 && !pbx_checkcondition(ast_strdupa(data))) || (end > 1)) {
|
||||
/* Condition Met (clean up helper vars) */
|
||||
const char *goto_str;
|
||||
int pri, endifpri;
|
||||
pbx_builtin_setvar_helper(chan, varname, NULL);
|
||||
pbx_builtin_setvar_helper(chan, my_name, NULL);
|
||||
snprintf(end_varname,sizeof(end_varname),"END_%s",varname);
|
||||
ast_channel_lock(chan);
|
||||
/* For EndIf, simply go to the next priority.
|
||||
* We do not add 1 to ast_channel_priority because the dialplan will
|
||||
* auto-increment the priority when we return, so just keep the priority as is.
|
||||
* For ExitIf or false If() condition, we need to find the end of the current
|
||||
* If branch (at same indentation) and branch there. */
|
||||
endifpri = end == 2 ? ast_channel_priority(chan) : find_matching_endif(chan, NULL);
|
||||
if ((goto_str = pbx_builtin_getvar_helper(chan, end_varname))) {
|
||||
ast_parseable_goto(chan, goto_str);
|
||||
pbx_builtin_setvar_helper(chan, end_varname, NULL);
|
||||
} else if (end <= 1 && (pri = find_matching_endif(chan, "ElseIf")) > 0 && pri < endifpri) {
|
||||
pri--; /* back up a priority, since it returned the priority after the ElseIf */
|
||||
/* If is false, and ElseIf exists, so jump to ElseIf */
|
||||
ast_verb(3, "Taking conditional false branch, jumping to priority %d\n", pri);
|
||||
ast_channel_priority_set(chan, pri);
|
||||
} else if (end <= 1 && (pri = find_matching_endif(chan, "Else")) > 0 && pri < endifpri) {
|
||||
/* don't need to back up a priority, because we don't actually need to execute Else, just jump to the priority after. Directly executing Else will exit the conditional. */
|
||||
/* If is false, and Else exists, so jump to Else */
|
||||
ast_verb(3, "Taking absolute false branch, jumping to priority %d\n", pri);
|
||||
ast_channel_priority_set(chan, pri);
|
||||
} else {
|
||||
pri = endifpri;
|
||||
if (pri > 0) {
|
||||
ast_verb(3, "Exiting conditional, jumping to priority %d\n", pri);
|
||||
ast_channel_priority_set(chan, pri);
|
||||
} else if (end == 4) { /* Condition added because of end > 0 instead of end == 4 */
|
||||
ast_log(LOG_WARNING, "Couldn't find matching EndIf? (If at %s@%s priority %d)\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
|
||||
}
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (end <= 1 && !if_pri) {
|
||||
char *goto_str;
|
||||
size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
|
||||
goto_str = ast_alloca(size);
|
||||
memset(goto_str, 0, size);
|
||||
snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan));
|
||||
pbx_builtin_setvar_helper(chan, varname, goto_str);
|
||||
} else if (end > 1 && if_pri) {
|
||||
/* END of branch */
|
||||
snprintf(end_varname, sizeof(end_varname), "END_%s", varname);
|
||||
if (!pbx_builtin_getvar_helper(chan, end_varname)) {
|
||||
char *goto_str;
|
||||
size = strlen(ast_channel_context(chan)) + strlen(ast_channel_exten(chan)) + 32;
|
||||
goto_str = ast_alloca(size);
|
||||
memset(goto_str, 0, size);
|
||||
snprintf(goto_str, size, "%s,%s,%d", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan)+1);
|
||||
pbx_builtin_setvar_helper(chan, end_varname, goto_str);
|
||||
}
|
||||
ast_parseable_goto(chan, if_pri);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int if_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 0);
|
||||
}
|
||||
|
||||
static int elseif_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 1);
|
||||
}
|
||||
|
||||
static int end_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 2);
|
||||
}
|
||||
|
||||
static int else_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 3);
|
||||
}
|
||||
|
||||
static int exit_exec(struct ast_channel *chan, const char *data) {
|
||||
return if_helper(chan, data, 4);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(if_app);
|
||||
res |= ast_unregister_application(elseif_app);
|
||||
res |= ast_unregister_application(stop_app);
|
||||
res |= ast_unregister_application(else_app);
|
||||
res |= ast_unregister_application(exit_app);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(if_app, if_exec);
|
||||
res |= ast_register_application_xml(elseif_app, elseif_exec);
|
||||
res |= ast_register_application_xml(stop_app, end_exec);
|
||||
res |= ast_register_application_xml(else_app, else_exec);
|
||||
res |= ast_register_application_xml(exit_app, exit_exec);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "If Branch and Conditional Execution");
|
|
@ -0,0 +1,691 @@
|
|||
/*
|
||||
* 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 Dial plan macro Implementation
|
||||
*
|
||||
* \author Mark Spencer <markster@digium.com>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<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"
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/extconf.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/lock.h"
|
||||
#include "asterisk/app.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Macro" language="en_US">
|
||||
<synopsis>
|
||||
Macro Implementation.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="name" required="true">
|
||||
<para>The name of the macro</para>
|
||||
</parameter>
|
||||
<parameter name="args">
|
||||
<argument name="arg1" required="true" />
|
||||
<argument name="arg2" multiple="true" />
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Executes a macro using the context macro-<replaceable>name</replaceable>,
|
||||
jumping to the <literal>s</literal> extension of that context and executing each step,
|
||||
then returning when the steps end.</para>
|
||||
<para>The calling extension, context, and priority are stored in <variable>MACRO_EXTEN</variable>,
|
||||
<variable>MACRO_CONTEXT</variable> and <variable>MACRO_PRIORITY</variable> respectively. Arguments
|
||||
become <variable>ARG1</variable>, <variable>ARG2</variable>, etc in the macro context.</para>
|
||||
<para>If you Goto out of the Macro context, the Macro will terminate and control will be returned
|
||||
at the location of the Goto.</para>
|
||||
<para>If <variable>MACRO_OFFSET</variable> is set at termination, Macro will attempt to continue
|
||||
at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.</para>
|
||||
<warning><para>Because of the way Macro is implemented (it executes the priorities contained within
|
||||
it via sub-engine), and a fixed per-thread memory stack allowance, macros are limited to 7 levels
|
||||
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 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>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">MacroExit</ref>
|
||||
<ref type="application">Goto</ref>
|
||||
<ref type="application">Gosub</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="MacroIf" language="en_US">
|
||||
<synopsis>
|
||||
Conditional Macro implementation.
|
||||
</synopsis>
|
||||
<syntax argsep="?">
|
||||
<parameter name="expr" required="true" />
|
||||
<parameter name="destination" required="true" argsep=":">
|
||||
<argument name="macroiftrue" required="true">
|
||||
<argument name="macroiftrue" required="true" />
|
||||
<argument name="arg1" multiple="true" />
|
||||
</argument>
|
||||
<argument name="macroiffalse">
|
||||
<argument name="macroiffalse" required="true" />
|
||||
<argument name="arg1" multiple="true" />
|
||||
</argument>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Executes macro defined in <replaceable>macroiftrue</replaceable> if
|
||||
<replaceable>expr</replaceable> is true (otherwise <replaceable>macroiffalse</replaceable>
|
||||
if provided)</para>
|
||||
<para>Arguments and return values as in application Macro()</para>
|
||||
<xi:include xpointer="xpointer(/docs/application[@name='Macro']/description/warning[2])" />
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">GotoIf</ref>
|
||||
<ref type="application">GosubIf</ref>
|
||||
<ref type="function">IF</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="MacroExclusive" language="en_US">
|
||||
<synopsis>
|
||||
Exclusive Macro Implementation.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="name" required="true">
|
||||
<para>The name of the macro</para>
|
||||
</parameter>
|
||||
<parameter name="arg1" />
|
||||
<parameter name="arg2" multiple="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Executes macro defined in the context macro-<replaceable>name</replaceable>.
|
||||
Only one call at a time may run the macro. (we'll wait if another call is busy
|
||||
executing in the Macro)</para>
|
||||
<para>Arguments and return values as in application Macro()</para>
|
||||
<xi:include xpointer="xpointer(/docs/application[@name='Macro']/description/warning[2])" />
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Macro</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="MacroExit" language="en_US">
|
||||
<synopsis>
|
||||
Exit from Macro.
|
||||
</synopsis>
|
||||
<syntax />
|
||||
<description>
|
||||
<para>Causes the currently running macro to exit as if it had
|
||||
ended normally by running out of priorities to execute.
|
||||
If used outside a macro, will likely cause unexpected behavior.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Macro</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
#define MAX_ARGS 80
|
||||
|
||||
/* special result value used to force macro exit */
|
||||
#define MACRO_EXIT_RESULT 1024
|
||||
|
||||
static char *app = "Macro";
|
||||
static char *if_app = "MacroIf";
|
||||
static char *exclusive_app = "MacroExclusive";
|
||||
static char *exit_app = "MacroExit";
|
||||
|
||||
static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
|
||||
|
||||
static const struct ast_datastore_info macro_ds_info = {
|
||||
.type = "MACRO",
|
||||
.chan_fixup = macro_fixup,
|
||||
};
|
||||
|
||||
static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
|
||||
{
|
||||
int i;
|
||||
char varname[10];
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0");
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL);
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL);
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL);
|
||||
pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL);
|
||||
for (i = 1; i < 100; i++) {
|
||||
snprintf(varname, sizeof(varname), "ARG%d", i);
|
||||
while (pbx_builtin_getvar_helper(new_chan, varname)) {
|
||||
/* Kill all levels of arguments */
|
||||
pbx_builtin_setvar_helper(new_chan, varname, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten,
|
||||
int priority, const char *callerid, int iter, int *had_error)
|
||||
{
|
||||
struct ast_exten *e;
|
||||
struct ast_context *c2;
|
||||
int idx;
|
||||
|
||||
if (iter >= AST_PBX_MAX_STACK) {
|
||||
if (!(*had_error)) {
|
||||
*had_error = 1;
|
||||
ast_log(LOG_ERROR, "Potential infinite loop detected, will not recurse further.\n");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
|
||||
if (ast_extension_match(ast_get_extension_name(e), exten)) {
|
||||
int needmatch = ast_get_extension_matchcid(e);
|
||||
if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
|
||||
(!needmatch)) {
|
||||
/* This is the matching extension we want */
|
||||
struct ast_exten *p;
|
||||
for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
|
||||
if (priority != ast_get_extension_priority(p))
|
||||
continue;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No match; run through includes */
|
||||
for (idx = 0; idx < ast_context_includes_count(c); idx++) {
|
||||
const struct ast_include *i = ast_context_includes_get(c, idx);
|
||||
|
||||
for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
|
||||
if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
|
||||
e = find_matching_priority(c2, exten, priority, callerid, iter + 1, had_error);
|
||||
if (e)
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive)
|
||||
{
|
||||
const char *s;
|
||||
char *tmp;
|
||||
char *cur, *rest;
|
||||
char *macro;
|
||||
char fullmacro[80];
|
||||
char varname[80];
|
||||
char runningapp[80], runningdata[1024];
|
||||
char *oldargs[MAX_ARGS + 1] = { NULL, };
|
||||
int argc, x;
|
||||
int res=0;
|
||||
char oldexten[256]="";
|
||||
int oldpriority, gosub_level = 0;
|
||||
char pc[80], depthc[12];
|
||||
char oldcontext[AST_MAX_CONTEXT] = "";
|
||||
const char *inhangupc;
|
||||
int offset, depth = 0, maxdepth = 7;
|
||||
int setmacrocontext=0;
|
||||
int autoloopflag, inhangup = 0;
|
||||
struct ast_str *tmp_subst = NULL;
|
||||
const char *my_macro_exten = NULL;
|
||||
char *save_macro_exten;
|
||||
char *save_macro_context;
|
||||
char *save_macro_priority;
|
||||
char *save_macro_offset;
|
||||
int save_in_subroutine;
|
||||
struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
|
||||
int had_infinite_include_error = 0;
|
||||
static int deprecation_notice = 0;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!deprecation_notice) {
|
||||
deprecation_notice = 1;
|
||||
ast_log(LOG_WARNING, "Macro() is deprecated and will be removed from a future version of Asterisk.\n");
|
||||
ast_log(LOG_WARNING, "Dialplan should be updated to use Gosub instead.\n");
|
||||
}
|
||||
|
||||
do {
|
||||
if (macro_store) {
|
||||
break;
|
||||
}
|
||||
if (!(macro_store = ast_datastore_alloc(¯o_ds_info, NULL))) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
|
||||
break;
|
||||
}
|
||||
/* Just the existence of this datastore is enough. */
|
||||
macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
|
||||
ast_channel_datastore_add(chan, macro_store);
|
||||
} while (0);
|
||||
|
||||
/* does the user want a deeper rabbit hole? */
|
||||
ast_channel_lock(chan);
|
||||
if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
|
||||
sscanf(s, "%30d", &maxdepth);
|
||||
}
|
||||
|
||||
/* Count how many levels deep the rabbit hole goes */
|
||||
if ((s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH"))) {
|
||||
sscanf(s, "%30d", &depth);
|
||||
}
|
||||
|
||||
/* Used for detecting whether to return when a Macro is called from another Macro after hangup */
|
||||
if (strcmp(ast_channel_exten(chan), "h") == 0)
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
|
||||
|
||||
if ((inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP"))) {
|
||||
sscanf(inhangupc, "%30d", &inhangup);
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
if (depth >= maxdepth) {
|
||||
ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
|
||||
return 0;
|
||||
}
|
||||
snprintf(depthc, sizeof(depthc), "%d", depth + 1);
|
||||
|
||||
tmp = ast_strdupa(data);
|
||||
rest = tmp;
|
||||
macro = strsep(&rest, ",");
|
||||
if (ast_strlen_zero(macro)) {
|
||||
ast_log(LOG_WARNING, "Invalid macro name specified\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
|
||||
|
||||
/* first search for the macro */
|
||||
if (!ast_context_find(fullmacro)) {
|
||||
ast_log(LOG_WARNING, "No such context '%s' for macro '%s'. Was called by %s@%s\n",
|
||||
fullmacro, macro, ast_channel_exten(chan), ast_channel_context(chan));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* now search for the right extension */
|
||||
if (ast_exists_extension(chan, fullmacro, "s", 1,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid,
|
||||
ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
/* We have a normal macro */
|
||||
my_macro_exten = "s";
|
||||
} else if (ast_exists_extension(chan, fullmacro, "~~s~~", 1,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid,
|
||||
ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
/* We have an AEL generated macro */
|
||||
my_macro_exten = "~~s~~";
|
||||
}
|
||||
|
||||
/* do we have a valid exten? */
|
||||
if (!my_macro_exten) {
|
||||
ast_log(LOG_WARNING,
|
||||
"Context '%s' for macro '%s' lacks 's' extension, priority 1\n",
|
||||
fullmacro, macro);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we are to run the macro exclusively, take the mutex */
|
||||
if (exclusive) {
|
||||
ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
|
||||
ast_autoservice_start(chan);
|
||||
if (ast_context_lockmacro(fullmacro)) {
|
||||
ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
|
||||
ast_autoservice_stop(chan);
|
||||
return 0;
|
||||
}
|
||||
ast_autoservice_stop(chan);
|
||||
}
|
||||
|
||||
if (!(tmp_subst = ast_str_create(16))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Save old info */
|
||||
ast_channel_lock(chan);
|
||||
oldpriority = ast_channel_priority(chan);
|
||||
ast_copy_string(oldexten, ast_channel_exten(chan), sizeof(oldexten));
|
||||
ast_copy_string(oldcontext, ast_channel_context(chan), sizeof(oldcontext));
|
||||
if (ast_strlen_zero(ast_channel_macrocontext(chan))) {
|
||||
ast_channel_macrocontext_set(chan, ast_channel_context(chan));
|
||||
ast_channel_macroexten_set(chan, ast_channel_exten(chan));
|
||||
ast_channel_macropriority_set(chan, ast_channel_priority(chan));
|
||||
setmacrocontext=1;
|
||||
}
|
||||
argc = 1;
|
||||
/* Save old macro variables */
|
||||
save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
|
||||
|
||||
save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
|
||||
|
||||
save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
|
||||
snprintf(pc, sizeof(pc), "%d", oldpriority);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
|
||||
|
||||
save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
|
||||
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
|
||||
|
||||
save_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
|
||||
ast_set_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
|
||||
|
||||
/* Setup environment for new run */
|
||||
ast_channel_exten_set(chan, my_macro_exten);
|
||||
ast_channel_context_set(chan, fullmacro);
|
||||
ast_channel_priority_set(chan, 1);
|
||||
|
||||
while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
|
||||
const char *argp;
|
||||
/* Save copy of old arguments if we're overwriting some, otherwise
|
||||
let them pass through to the other macro */
|
||||
snprintf(varname, sizeof(varname), "ARG%d", argc);
|
||||
if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
|
||||
oldargs[argc] = ast_strdup(argp);
|
||||
}
|
||||
pbx_builtin_setvar_helper(chan, varname, cur);
|
||||
argc++;
|
||||
}
|
||||
autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
|
||||
ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
while (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
struct ast_context *c;
|
||||
struct ast_exten *e;
|
||||
int foundx;
|
||||
runningapp[0] = '\0';
|
||||
runningdata[0] = '\0';
|
||||
|
||||
/* What application will execute? */
|
||||
if (ast_rdlock_contexts()) {
|
||||
ast_log(LOG_WARNING, "Failed to lock contexts list\n");
|
||||
} else {
|
||||
for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
|
||||
if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
|
||||
if (ast_rdlock_context(c)) {
|
||||
ast_log(LOG_WARNING, "Unable to lock context?\n");
|
||||
} else {
|
||||
e = find_matching_priority(c, ast_channel_exten(chan), ast_channel_priority(chan),
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid,
|
||||
ast_channel_caller(chan)->id.number.str, NULL),
|
||||
0, &had_infinite_include_error);
|
||||
if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
|
||||
ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
|
||||
ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
|
||||
}
|
||||
ast_unlock_context(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_unlock_contexts();
|
||||
|
||||
/* Reset the macro depth, if it was changed in the last iteration */
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
|
||||
|
||||
res = ast_spawn_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
|
||||
&foundx, 1);
|
||||
if (res) {
|
||||
/* Something bad happened, or a hangup has been requested. */
|
||||
if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
|
||||
(res == '*') || (res == '#')) {
|
||||
/* Just return result as to the previous application as if it had been dialed */
|
||||
ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
|
||||
break;
|
||||
}
|
||||
switch(res) {
|
||||
case MACRO_EXIT_RESULT:
|
||||
res = 0;
|
||||
goto out;
|
||||
default:
|
||||
ast_debug(2, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan), macro);
|
||||
ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan), macro);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ast_debug(1, "Executed application: %s\n", runningapp);
|
||||
|
||||
if (!strcasecmp(runningapp, "GOSUB")) {
|
||||
gosub_level++;
|
||||
ast_debug(1, "Incrementing gosub_level\n");
|
||||
} else if (!strcasecmp(runningapp, "GOSUBIF")) {
|
||||
char *cond, *app_arg;
|
||||
char *app2;
|
||||
ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
|
||||
app2 = ast_str_buffer(tmp_subst);
|
||||
cond = strsep(&app2, "?");
|
||||
app_arg = strsep(&app2, ":");
|
||||
if (pbx_checkcondition(cond)) {
|
||||
if (!ast_strlen_zero(app_arg)) {
|
||||
gosub_level++;
|
||||
ast_debug(1, "Incrementing gosub_level\n");
|
||||
}
|
||||
} else {
|
||||
if (!ast_strlen_zero(app2)) {
|
||||
gosub_level++;
|
||||
ast_debug(1, "Incrementing gosub_level\n");
|
||||
}
|
||||
}
|
||||
} else if (!strcasecmp(runningapp, "RETURN")) {
|
||||
gosub_level--;
|
||||
ast_debug(1, "Decrementing gosub_level\n");
|
||||
} else if (!strcasecmp(runningapp, "STACKPOP")) {
|
||||
gosub_level--;
|
||||
ast_debug(1, "Decrementing gosub_level\n");
|
||||
} else if (!strncasecmp(runningapp, "EXEC", 4)) {
|
||||
/* Must evaluate args to find actual app */
|
||||
char *tmp2, *tmp3 = NULL;
|
||||
ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
|
||||
tmp2 = ast_str_buffer(tmp_subst);
|
||||
if (!strcasecmp(runningapp, "EXECIF")) {
|
||||
if ((tmp3 = strchr(tmp2, '|'))) {
|
||||
*tmp3++ = '\0';
|
||||
}
|
||||
if (!pbx_checkcondition(tmp2)) {
|
||||
tmp3 = NULL;
|
||||
}
|
||||
} else {
|
||||
tmp3 = tmp2;
|
||||
}
|
||||
|
||||
if (tmp3) {
|
||||
ast_debug(1, "Last app: %s\n", tmp3);
|
||||
}
|
||||
|
||||
if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
|
||||
gosub_level++;
|
||||
ast_debug(1, "Incrementing gosub_level\n");
|
||||
} else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
|
||||
gosub_level--;
|
||||
ast_debug(1, "Decrementing gosub_level\n");
|
||||
} else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
|
||||
gosub_level--;
|
||||
ast_debug(1, "Decrementing gosub_level\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (gosub_level == 0 && strcasecmp(ast_channel_context(chan), fullmacro)) {
|
||||
ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", ast_channel_name(chan), macro);
|
||||
break;
|
||||
}
|
||||
|
||||
/* don't stop executing extensions when we're in "h" */
|
||||
if (ast_check_hangup(chan) && !inhangup) {
|
||||
ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
|
||||
ast_channel_exten(chan),
|
||||
ast_channel_macroexten(chan),
|
||||
ast_channel_priority(chan));
|
||||
goto out;
|
||||
}
|
||||
ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
|
||||
}
|
||||
out:
|
||||
|
||||
/* Don't let the channel change now. */
|
||||
ast_channel_lock(chan);
|
||||
|
||||
/* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
|
||||
snprintf(depthc, sizeof(depthc), "%d", depth);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
|
||||
ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
|
||||
ast_set2_flag(ast_channel_flags(chan), save_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
|
||||
|
||||
for (x = 1; x < argc; x++) {
|
||||
/* Restore old arguments and delete ours */
|
||||
snprintf(varname, sizeof(varname), "ARG%d", x);
|
||||
pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
|
||||
ast_free(oldargs[x]);
|
||||
}
|
||||
|
||||
/* Restore macro variables */
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
|
||||
ast_free(save_macro_exten);
|
||||
ast_free(save_macro_context);
|
||||
ast_free(save_macro_priority);
|
||||
|
||||
if (setmacrocontext) {
|
||||
ast_channel_macrocontext_set(chan, "");
|
||||
ast_channel_macroexten_set(chan, "");
|
||||
ast_channel_macropriority_set(chan, 0);
|
||||
}
|
||||
|
||||
if (!strcasecmp(ast_channel_context(chan), fullmacro)
|
||||
&& !(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
|
||||
const char *offsets;
|
||||
|
||||
/* If we're leaving the macro normally, restore original information */
|
||||
ast_channel_priority_set(chan, oldpriority);
|
||||
ast_channel_context_set(chan, oldcontext);
|
||||
ast_channel_exten_set(chan, oldexten);
|
||||
if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
|
||||
/* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
|
||||
normally if there is any problem */
|
||||
if (sscanf(offsets, "%30d", &offset) == 1) {
|
||||
if (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan),
|
||||
ast_channel_priority(chan) + offset + 1,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
ast_channel_priority_set(chan, ast_channel_priority(chan) + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
|
||||
ast_free(save_macro_offset);
|
||||
|
||||
/* Unlock the macro */
|
||||
if (exclusive) {
|
||||
ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
|
||||
if (ast_context_unlockmacro(fullmacro)) {
|
||||
ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
ast_free(tmp_subst);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int macro_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
return _macro_exec(chan, data, 0);
|
||||
}
|
||||
|
||||
static int macroexclusive_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
return _macro_exec(chan, data, 1);
|
||||
}
|
||||
|
||||
static int macroif_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *expr = NULL, *label_a = NULL, *label_b = NULL;
|
||||
int res = 0;
|
||||
|
||||
expr = ast_strdupa(data);
|
||||
|
||||
if ((label_a = strchr(expr, '?'))) {
|
||||
*label_a = '\0';
|
||||
label_a++;
|
||||
if ((label_b = strchr(label_a, ':'))) {
|
||||
*label_b = '\0';
|
||||
label_b++;
|
||||
}
|
||||
if (pbx_checkcondition(expr))
|
||||
res = macro_exec(chan, label_a);
|
||||
else if (label_b)
|
||||
res = macro_exec(chan, label_b);
|
||||
} else
|
||||
ast_log(LOG_WARNING, "Invalid Syntax.\n");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int macro_exit_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
return MACRO_EXIT_RESULT;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(if_app);
|
||||
res |= ast_unregister_application(exit_app);
|
||||
res |= ast_unregister_application(app);
|
||||
res |= ast_unregister_application(exclusive_app);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(exit_app, macro_exit_exec);
|
||||
res |= ast_register_application_xml(if_app, macroif_exec);
|
||||
res |= ast_register_application_xml(exclusive_app, macroexclusive_exec);
|
||||
res |= ast_register_application_xml(app, macro_exec);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_DEPRECATED(ASTERISK_GPL_KEY, "Extension Macros");
|
2623
apps/app_meetme.c
2623
apps/app_meetme.c
File diff suppressed because it is too large
Load Diff
|
@ -235,7 +235,6 @@ static const char sendmf_name[] = "SendMF";
|
|||
* \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 laxkp Receive digits even if KP not received
|
||||
* \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
|
||||
|
|
|
@ -1878,10 +1878,11 @@ static int leave_voicemail(struct ast_channel *chan, char *username, struct leav
|
|||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
|
||||
"Unknown");
|
||||
snprintf(logbuf, sizeof(logbuf),
|
||||
/* "Mailbox:domain:exten:priority:callerchan:callerid:origdate:origtime:duration:durationstatus:accountcode" */
|
||||
"%s:%s:%s:%d:%s:%s:%s:%s:%d:%s:%s\n",
|
||||
/* "Mailbox:domain:macrocontext:exten:priority:callerchan:callerid:origdate:origtime:duration:durationstatus:accountcode" */
|
||||
"%s:%s:%s:%s:%d:%s:%s:%s:%s:%d:%s:%s\n",
|
||||
username,
|
||||
ast_channel_context(chan),
|
||||
ast_channel_macrocontext(chan),
|
||||
ast_channel_exten(chan),
|
||||
ast_channel_priority(chan),
|
||||
ast_channel_name(chan),
|
||||
|
@ -2139,6 +2140,8 @@ static int minivm_greet_exec(struct ast_channel *chan, const char *data)
|
|||
struct ast_flags flags = { 0 };
|
||||
char *opts[OPT_ARG_ARRAY_SIZE];
|
||||
int res = 0;
|
||||
int ausemacro = 0;
|
||||
int ousemacro = 0;
|
||||
int ouseexten = 0;
|
||||
char tmp[PATH_MAX];
|
||||
char dest[PATH_MAX];
|
||||
|
@ -2209,7 +2212,7 @@ static int minivm_greet_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
ast_debug(2, "Preparing to play message ...\n");
|
||||
|
||||
/* Check current context for special extensions */
|
||||
/* Check current or macro-calling context for special extensions */
|
||||
if (ast_test_flag(vmu, MVM_OPERATOR)) {
|
||||
if (!ast_strlen_zero(vmu->exit)) {
|
||||
if (ast_exists_extension(chan, vmu->exit, "o", 1,
|
||||
|
@ -2222,6 +2225,12 @@ static int minivm_greet_exec(struct ast_channel *chan, const char *data)
|
|||
strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
|
||||
ouseexten = 1;
|
||||
}
|
||||
else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
|
||||
&& ast_exists_extension(chan, ast_channel_macrocontext(chan), "o", 1,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
|
||||
ousemacro = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(vmu->exit)) {
|
||||
|
@ -2232,6 +2241,11 @@ static int minivm_greet_exec(struct ast_channel *chan, const char *data)
|
|||
} else if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
|
||||
} else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
|
||||
&& ast_exists_extension(chan, ast_channel_macrocontext(chan), "a", 1,
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
|
||||
strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
|
||||
ausemacro = 1;
|
||||
}
|
||||
|
||||
res = 0; /* Reset */
|
||||
|
@ -2272,15 +2286,19 @@ static int minivm_greet_exec(struct ast_channel *chan, const char *data)
|
|||
ast_channel_exten_set(chan, "a");
|
||||
if (!ast_strlen_zero(vmu->exit)) {
|
||||
ast_channel_context_set(chan, vmu->exit);
|
||||
} else if (ausemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
|
||||
ast_channel_context_set(chan, ast_channel_macrocontext(chan));
|
||||
}
|
||||
ast_channel_priority_set(chan, 0);
|
||||
pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "USEREXIT");
|
||||
res = 0;
|
||||
} else if (res == '0') { /* Check for a '0' here */
|
||||
if(ouseexten) {
|
||||
if(ouseexten || ousemacro) {
|
||||
ast_channel_exten_set(chan, "o");
|
||||
if (!ast_strlen_zero(vmu->exit)) {
|
||||
ast_channel_context_set(chan, vmu->exit);
|
||||
} else if (ousemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
|
||||
ast_channel_context_set(chan, ast_channel_macrocontext(chan));
|
||||
}
|
||||
ast_play_and_wait(chan, "transfer");
|
||||
ast_channel_priority_set(chan, 0);
|
||||
|
|
|
@ -90,16 +90,6 @@
|
|||
<para>Play a periodic beep while this call is being recorded.</para>
|
||||
<argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
|
||||
</option>
|
||||
<option name="c">
|
||||
<para>Use the real Caller ID from the channel for the voicemail Caller ID.</para>
|
||||
<para>By default, the Connected Line is used. If you want the channel caller's
|
||||
real number, you may need to specify this option.</para>
|
||||
</option>
|
||||
<option name="d">
|
||||
<para>Delete the recording file as soon as MixMonitor is done with it.</para>
|
||||
<para>For example, if you use the m option to dispatch the recording to a voicemail box,
|
||||
you can specify this option to delete the original copy of it afterwards.</para>
|
||||
</option>
|
||||
<option name="v">
|
||||
<para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
|
||||
(range <literal>-4</literal> to <literal>4</literal>)</para>
|
||||
|
@ -183,7 +173,10 @@
|
|||
function <variable>FILTER()</variable>.</para></warning>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Monitor</ref>
|
||||
<ref type="application">StopMixMonitor</ref>
|
||||
<ref type="application">PauseMonitor</ref>
|
||||
<ref type="application">UnpauseMonitor</ref>
|
||||
<ref type="function">AUDIOHOOK_INHERIT</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
|
@ -386,6 +379,7 @@ struct mixmonitor {
|
|||
/* the below string fields describe data used for creating voicemails from the recording */
|
||||
AST_DECLARE_STRING_FIELDS(
|
||||
AST_STRING_FIELD(call_context);
|
||||
AST_STRING_FIELD(call_macrocontext);
|
||||
AST_STRING_FIELD(call_extension);
|
||||
AST_STRING_FIELD(call_callerchan);
|
||||
AST_STRING_FIELD(call_callerid);
|
||||
|
@ -413,8 +407,6 @@ enum mixmonitor_flags {
|
|||
MUXFLAG_BEEP_STOP = (1 << 13),
|
||||
MUXFLAG_DEPRECATED_RWSYNC = (1 << 14),
|
||||
MUXFLAG_NO_RWSYNC = (1 << 15),
|
||||
MUXFLAG_AUTO_DELETE = (1 << 16),
|
||||
MUXFLAG_REAL_CALLERID = (1 << 17),
|
||||
};
|
||||
|
||||
enum mixmonitor_args {
|
||||
|
@ -435,8 +427,6 @@ AST_APP_OPTIONS(mixmonitor_opts, {
|
|||
AST_APP_OPTION('a', MUXFLAG_APPEND),
|
||||
AST_APP_OPTION('b', MUXFLAG_BRIDGED),
|
||||
AST_APP_OPTION_ARG('B', MUXFLAG_BEEP, OPT_ARG_BEEP_INTERVAL),
|
||||
AST_APP_OPTION('c', MUXFLAG_REAL_CALLERID),
|
||||
AST_APP_OPTION('d', MUXFLAG_AUTO_DELETE),
|
||||
AST_APP_OPTION('p', MUXFLAG_BEEP_START),
|
||||
AST_APP_OPTION('P', MUXFLAG_BEEP_STOP),
|
||||
AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
|
||||
|
@ -656,6 +646,7 @@ static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, co
|
|||
ast_string_field_set(&recording_data, recording_file, filename);
|
||||
ast_string_field_set(&recording_data, recording_ext, ext);
|
||||
ast_string_field_set(&recording_data, call_context, mixmonitor->call_context);
|
||||
ast_string_field_set(&recording_data, call_macrocontext, mixmonitor->call_macrocontext);
|
||||
ast_string_field_set(&recording_data, call_extension, mixmonitor->call_extension);
|
||||
ast_string_field_set(&recording_data, call_callerchan, mixmonitor->call_callerchan);
|
||||
ast_string_field_set(&recording_data, call_callerid, mixmonitor->call_callerid);
|
||||
|
@ -869,19 +860,6 @@ static void *mixmonitor_thread(void *obj)
|
|||
ast_debug(3, "No recipients to forward monitor to, moving on.\n");
|
||||
}
|
||||
|
||||
if (ast_test_flag(mixmonitor, MUXFLAG_AUTO_DELETE)) {
|
||||
ast_debug(3, "Deleting our copies of recording files\n");
|
||||
if (!ast_strlen_zero(fs_ext)) {
|
||||
ast_filedelete(mixmonitor->filename, fs_ext);
|
||||
}
|
||||
if (!ast_strlen_zero(fs_read_ext)) {
|
||||
ast_filedelete(mixmonitor->filename_read, fs_ext);
|
||||
}
|
||||
if (!ast_strlen_zero(fs_write_ext)) {
|
||||
ast_filedelete(mixmonitor->filename_write, fs_ext);
|
||||
}
|
||||
}
|
||||
|
||||
mixmonitor_free(mixmonitor);
|
||||
|
||||
ast_module_unref(ast_module_info->self);
|
||||
|
@ -1037,39 +1015,23 @@ static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
|
|||
|
||||
if (!ast_strlen_zero(recipients)) {
|
||||
char callerid[256];
|
||||
struct ast_party_connected_line *connected;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
|
||||
/* We use the connected line of the invoking channel for caller ID,
|
||||
* unless we've been told to use the Caller ID.
|
||||
* The initial use for this relied on Connected Line to get the
|
||||
* actual number for recording with Digium phones,
|
||||
* but in generic use the Caller ID is likely what people want.
|
||||
*/
|
||||
/* We use the connected line of the invoking channel for caller ID. */
|
||||
|
||||
if (ast_test_flag(mixmonitor, MUXFLAG_REAL_CALLERID)) {
|
||||
struct ast_party_caller *caller;
|
||||
caller = ast_channel_caller(chan);
|
||||
ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
|
||||
caller->id.name.str, caller->id.number.valid,
|
||||
caller->id.number.str);
|
||||
ast_callerid_merge(callerid, sizeof(callerid),
|
||||
S_COR(caller->id.name.valid, caller->id.name.str, NULL),
|
||||
S_COR(caller->id.number.valid, caller->id.number.str, NULL),
|
||||
"Unknown");
|
||||
} else {
|
||||
struct ast_party_connected_line *connected;
|
||||
connected = ast_channel_connected(chan);
|
||||
ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
|
||||
connected->id.name.str, connected->id.number.valid,
|
||||
connected->id.number.str);
|
||||
ast_callerid_merge(callerid, sizeof(callerid),
|
||||
S_COR(connected->id.name.valid, connected->id.name.str, NULL),
|
||||
S_COR(connected->id.number.valid, connected->id.number.str, NULL),
|
||||
"Unknown");
|
||||
}
|
||||
connected = ast_channel_connected(chan);
|
||||
ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
|
||||
connected->id.name.str, connected->id.number.valid,
|
||||
connected->id.number.str);
|
||||
ast_callerid_merge(callerid, sizeof(callerid),
|
||||
S_COR(connected->id.name.valid, connected->id.name.str, NULL),
|
||||
S_COR(connected->id.number.valid, connected->id.number.str, NULL),
|
||||
"Unknown");
|
||||
|
||||
ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
|
||||
ast_string_field_set(mixmonitor, call_macrocontext, ast_channel_macrocontext(chan));
|
||||
ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
|
||||
ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
|
||||
ast_string_field_set(mixmonitor, call_callerid, callerid);
|
||||
|
@ -1441,50 +1403,6 @@ static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_
|
|||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
/*! \brief Mute / unmute an individual MixMonitor by id */
|
||||
static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data,
|
||||
enum ast_audiohook_flags flag, int clearmute)
|
||||
{
|
||||
struct ast_datastore *datastore = NULL;
|
||||
char *parse = "";
|
||||
struct mixmonitor_ds *mixmonitor_ds;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(mixmonid);
|
||||
);
|
||||
|
||||
if (!ast_strlen_zero(data)) {
|
||||
parse = ast_strdupa(data);
|
||||
}
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
ast_channel_lock(chan);
|
||||
|
||||
datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info,
|
||||
S_OR(args.mixmonid, NULL));
|
||||
if (!datastore) {
|
||||
ast_channel_unlock(chan);
|
||||
return -1;
|
||||
}
|
||||
mixmonitor_ds = datastore->data;
|
||||
|
||||
ast_mutex_lock(&mixmonitor_ds->lock);
|
||||
|
||||
if (mixmonitor_ds->audiohook) {
|
||||
if (clearmute) {
|
||||
ast_clear_flag(mixmonitor_ds->audiohook, flag);
|
||||
} else {
|
||||
ast_set_flag(mixmonitor_ds->audiohook, flag);
|
||||
}
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&mixmonitor_ds->lock);
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Mute / unmute a MixMonitor channel */
|
||||
static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
|
||||
{
|
||||
|
@ -1493,8 +1411,7 @@ static int manager_mute_mixmonitor(struct mansession *s, const struct message *m
|
|||
const char *id = astman_get_header(m, "ActionID");
|
||||
const char *state = astman_get_header(m, "State");
|
||||
const char *direction = astman_get_header(m,"Direction");
|
||||
const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
|
||||
int clearmute = 1, mutedcount = 0;
|
||||
int clearmute = 1;
|
||||
enum ast_audiohook_flags flag;
|
||||
RAII_VAR(struct stasis_message *, stasis_message, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
|
||||
|
@ -1533,28 +1450,15 @@ static int manager_mute_mixmonitor(struct mansession *s, const struct message *m
|
|||
return AMI_SUCCESS;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(mixmonitor_id)) {
|
||||
mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
|
||||
if (mutedcount < 0) {
|
||||
ast_channel_unref(c);
|
||||
astman_send_error(s, m, "Cannot set mute flag");
|
||||
return AMI_SUCCESS;
|
||||
}
|
||||
} else {
|
||||
if (mute_mixmonitor_instance(c, mixmonitor_id, flag, clearmute)) {
|
||||
ast_channel_unref(c);
|
||||
astman_send_error(s, m, "Cannot set mute flag");
|
||||
return AMI_SUCCESS;
|
||||
}
|
||||
mutedcount = 1;
|
||||
if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
|
||||
ast_channel_unref(c);
|
||||
astman_send_error(s, m, "Cannot set mute flag");
|
||||
return AMI_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
stasis_message_blob = ast_json_pack("{s: s, s: b, s: s, s: i}",
|
||||
stasis_message_blob = ast_json_pack("{s: s, s: b}",
|
||||
"direction", direction,
|
||||
"state", ast_true(state),
|
||||
"mixmonitorid", mixmonitor_id,
|
||||
"count", mutedcount);
|
||||
"state", ast_true(state));
|
||||
|
||||
stasis_message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(c),
|
||||
ast_channel_mixmonitor_mute_type(), stasis_message_blob);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -48,13 +48,6 @@
|
|||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="filenames" required="true" argsep="&">
|
||||
<para>Ampersand separated list of filenames. If the filename
|
||||
is a relative filename (it does not begin with a slash), it
|
||||
will be searched for in the Asterisk sounds directory. If the
|
||||
filename is able to be parsed as a URL, Asterisk will
|
||||
download the file and then begin playback on it. To include a
|
||||
literal <literal>&</literal> in the URL you can enclose
|
||||
the URL in single quotes.</para>
|
||||
<argument name="filename" required="true" />
|
||||
<argument name="filename2" multiple="true" />
|
||||
</parameter>
|
||||
|
@ -499,7 +492,7 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
|||
char *front;
|
||||
|
||||
ast_stopstream(chan);
|
||||
while (!res && (front = ast_strsep(&back, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||
while (!res && (front = strsep(&back, "&"))) {
|
||||
if (option_say)
|
||||
res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
|
||||
else if (option_mix){
|
||||
|
@ -514,7 +507,8 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
|||
if (!res) {
|
||||
res = ast_waitstream(chan, "");
|
||||
ast_stopstream(chan);
|
||||
} else {
|
||||
}
|
||||
if (res) {
|
||||
if (!ast_check_hangup(chan)) {
|
||||
ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
|
||||
}
|
||||
|
|
390
apps/app_queue.c
390
apps/app_queue.c
|
@ -41,6 +41,7 @@
|
|||
* - Position announcement
|
||||
* - Abandoned/completed call counters
|
||||
* - Failout timer passed as optional app parameter
|
||||
* - Optional monitoring of calls, started when call is answered
|
||||
*
|
||||
* Patch Version 1.07 2003-12-24 01
|
||||
*
|
||||
|
@ -62,6 +63,7 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<use type="module">res_monitor</use>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
|
@ -86,6 +88,7 @@
|
|||
#include "asterisk/cli.h"
|
||||
#include "asterisk/manager.h"
|
||||
#include "asterisk/config.h"
|
||||
#include "asterisk/monitor.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/causes.h"
|
||||
#include "asterisk/astdb.h"
|
||||
|
@ -158,10 +161,7 @@
|
|||
<para>Continue in the dialplan if the callee hangs up.</para>
|
||||
</option>
|
||||
<option name="d">
|
||||
<para>Data-quality (modem) call (minimum delay).</para>
|
||||
<para>This option only applies to DAHDI channels. By default,
|
||||
DTMF is verified by muting audio TX/RX to verify the tone
|
||||
is still present. This option disables that behavior.</para>
|
||||
<para>data-quality (modem) call (minimum delay).</para>
|
||||
</option>
|
||||
<option name="F" argsep="^">
|
||||
<argument name="context" required="false" />
|
||||
|
@ -171,7 +171,13 @@
|
|||
to the specified destination and <emphasis>start</emphasis> execution at that location.</para>
|
||||
<para>NOTE: Any channel variables you want the called channel to inherit from the caller channel must be
|
||||
prefixed with one or two underbars ('_').</para>
|
||||
<para>NOTE: Using this option from a GoSub() might not make sense as there would be no return points.</para>
|
||||
</option>
|
||||
<option name="F">
|
||||
<para>When the caller hangs up, transfer the <emphasis>called member</emphasis> to the next priority of
|
||||
the current extension and <emphasis>start</emphasis> execution at that location.</para>
|
||||
<para>NOTE: Any channel variables you want the called channel to inherit from the caller channel must be
|
||||
prefixed with one or two underbars ('_').</para>
|
||||
<para>NOTE: Using this option from a Macro() or GoSub() might not make sense as there would be no return points.</para>
|
||||
</option>
|
||||
<option name="h">
|
||||
<para>Allow <emphasis>callee</emphasis> to hang up by pressing <literal>*</literal>.</para>
|
||||
|
@ -216,6 +222,14 @@
|
|||
<option name="T">
|
||||
<para>Allow the <emphasis>calling</emphasis> user to transfer the call.</para>
|
||||
</option>
|
||||
<option name="w">
|
||||
<para>Allow the <emphasis>called</emphasis> user to write the conversation to
|
||||
disk via Monitor.</para>
|
||||
</option>
|
||||
<option name="W">
|
||||
<para>Allow the <emphasis>calling</emphasis> user to write the conversation to
|
||||
disk via Monitor.</para>
|
||||
</option>
|
||||
<option name="x">
|
||||
<para>Allow the <emphasis>called</emphasis> user to write the conversation
|
||||
to disk via MixMonitor.</para>
|
||||
|
@ -230,18 +244,11 @@
|
|||
<para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
|
||||
</parameter>
|
||||
<parameter name="announceoverride" argsep="&">
|
||||
<para>Announcement file(s) to play to agent before bridging
|
||||
call, overriding the announcement(s) configured in
|
||||
<filename>queues.conf</filename>, if any.</para>
|
||||
<para>Ampersand separated list of filenames. If the filename
|
||||
is a relative filename (it does not begin with a slash), it
|
||||
will be searched for in the Asterisk sounds directory. If the
|
||||
filename is able to be parsed as a URL, Asterisk will
|
||||
download the file and then begin playback on it. To include a
|
||||
literal <literal>&</literal> in the URL you can enclose
|
||||
the URL in single quotes.</para>
|
||||
<argument name="announceoverride" required="true" />
|
||||
<argument name="announceoverride2" multiple="true" />
|
||||
<argument name="filename" required="true">
|
||||
<para>Announcement file(s) to play to agent before bridging call, overriding the announcement(s)
|
||||
configured in <filename>queues.conf</filename>, if any.</para>
|
||||
</argument>
|
||||
<argument name="filename2" multiple="true" />
|
||||
</parameter>
|
||||
<parameter name="timeout">
|
||||
<para>Will cause the queue to fail out after a specified number of
|
||||
|
@ -252,6 +259,10 @@
|
|||
<para>Will setup an AGI script to be executed on the calling party's channel once they are
|
||||
connected to a queue member.</para>
|
||||
</parameter>
|
||||
<parameter name="macro">
|
||||
<para>Will run a macro on the called party's channel (the queue member) once the parties are connected.</para>
|
||||
<para>NOTE: Macros are deprecated, GoSub should be used instead.</para>
|
||||
</parameter>
|
||||
<parameter name="gosub">
|
||||
<para>Will run a gosub on the called party's channel (the queue member)
|
||||
once the parties are connected. The subroutine execution starts in the
|
||||
|
@ -1051,9 +1062,6 @@
|
|||
<parameter name="Priority" required="true">
|
||||
<para>Priority value for change for caller on queue.</para>
|
||||
</parameter>
|
||||
<parameter name="Immediate">
|
||||
<para>When set to yes will cause the priority change to be reflected immediately, causing the channel to change position within the queue.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
</description>
|
||||
|
@ -1269,7 +1277,7 @@
|
|||
</syntax>
|
||||
<see-also>
|
||||
<ref type="application">PauseQueueMember</ref>
|
||||
<ref type="application">UnpauseQueueMember</ref>
|
||||
<ref type="application">UnPauseQueueMember</ref>
|
||||
</see-also>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
|
@ -1613,15 +1621,9 @@ static int negative_penalty_invalid;
|
|||
/*! \brief queues.conf [general] option */
|
||||
static int log_membername_as_agent;
|
||||
|
||||
/*! \brief queues.conf [general] option */
|
||||
static int force_longest_waiting_caller;
|
||||
|
||||
/*! \brief name of the ringinuse field in the realtime database */
|
||||
static char *realtime_ringinuse_field;
|
||||
|
||||
/*! \brief does realtime backend support reason_paused */
|
||||
static int realtime_reason_paused;
|
||||
|
||||
enum queue_result {
|
||||
QUEUE_UNKNOWN = 0,
|
||||
QUEUE_TIMEOUT = 1,
|
||||
|
@ -1798,6 +1800,8 @@ struct call_queue {
|
|||
AST_STRING_FIELD(announce);
|
||||
/*! Exit context */
|
||||
AST_STRING_FIELD(context);
|
||||
/*! Macro to run upon member connection */
|
||||
AST_STRING_FIELD(membermacro);
|
||||
/*! Gosub to run upon member connection */
|
||||
AST_STRING_FIELD(membergosub);
|
||||
/*! Default rule to use if none specified in call to Queue() */
|
||||
|
@ -1852,7 +1856,6 @@ struct call_queue {
|
|||
int announcepositionlimit; /*!< How many positions we announce? */
|
||||
int announcefrequency; /*!< How often to announce their position */
|
||||
int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
|
||||
int periodicannouncestartdelay; /*!< How long into the queue should the periodic accouncement start */
|
||||
int periodicannouncefrequency; /*!< How often to play periodic announcement */
|
||||
int numperiodicannounce; /*!< The number of periodic announcements configured */
|
||||
int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
|
||||
|
@ -1865,6 +1868,7 @@ struct call_queue {
|
|||
int servicelevel; /*!< seconds setting for servicelevel*/
|
||||
int callscompletedinsl; /*!< Number of calls answered with servicelevel*/
|
||||
char monfmt[8]; /*!< Format to use when recording calls */
|
||||
int montype; /*!< Monitor type Monitor vs. MixMonitor */
|
||||
int count; /*!< How many entries */
|
||||
int maxlen; /*!< Max number of entries */
|
||||
int wrapuptime; /*!< Wrapup Time */
|
||||
|
@ -2102,10 +2106,8 @@ static inline void insert_entry(struct call_queue *q, struct queue_ent *prev, st
|
|||
/* every queue_ent must have a reference to it's parent call_queue, this
|
||||
* reference does not go away until the end of the queue_ent's life, meaning
|
||||
* that even when the queue_ent leaves the call_queue this ref must remain. */
|
||||
if (!new->parent) {
|
||||
queue_ref(q);
|
||||
new->parent = q;
|
||||
}
|
||||
queue_ref(q);
|
||||
new->parent = q;
|
||||
new->pos = ++(*pos);
|
||||
new->opos = *pos;
|
||||
}
|
||||
|
@ -2954,10 +2956,7 @@ static void init_queue(struct call_queue *q)
|
|||
q->timeout = DEFAULT_TIMEOUT;
|
||||
q->maxlen = 0;
|
||||
|
||||
ast_string_field_set(q, announce, "");
|
||||
ast_string_field_set(q, context, "");
|
||||
ast_string_field_set(q, membergosub, "");
|
||||
ast_string_field_set(q, defaultrule, "");
|
||||
|
||||
q->announcefrequency = 0;
|
||||
q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
|
||||
|
@ -2973,6 +2972,7 @@ static void init_queue(struct call_queue *q)
|
|||
q->setqueuevar = 0;
|
||||
q->setqueueentryvar = 0;
|
||||
q->autofill = autofill_default;
|
||||
q->montype = montype_default;
|
||||
q->monfmt[0] = '\0';
|
||||
q->reportholdtime = 0;
|
||||
q->wrapuptime = 0;
|
||||
|
@ -2983,13 +2983,9 @@ static void init_queue(struct call_queue *q)
|
|||
q->weight = 0;
|
||||
q->timeoutrestart = 0;
|
||||
q->periodicannouncefrequency = 0;
|
||||
q->periodicannouncestartdelay = -1;
|
||||
q->randomperiodicannounce = 0;
|
||||
q->numperiodicannounce = 0;
|
||||
q->relativeperiodicannounce = 0;
|
||||
q->autopause = QUEUE_AUTOPAUSE_OFF;
|
||||
q->autopausebusy = 0;
|
||||
q->autopauseunavail = 0;
|
||||
q->timeoutpriority = TIMEOUT_PRIORITY_APP;
|
||||
q->autopausedelay = 0;
|
||||
if (!q->members) {
|
||||
|
@ -3014,7 +3010,6 @@ static void init_queue(struct call_queue *q)
|
|||
ast_string_field_set(q, sound_minute, "queue-minute");
|
||||
ast_string_field_set(q, sound_seconds, "queue-seconds");
|
||||
ast_string_field_set(q, sound_thanks, "queue-thankyou");
|
||||
ast_string_field_set(q, sound_callerannounce, "");
|
||||
ast_string_field_set(q, sound_reporthold, "queue-reporthold");
|
||||
|
||||
if (!q->sound_periodicannounce[0]) {
|
||||
|
@ -3350,6 +3345,8 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
|
|||
q->setqueueentryvar = ast_true(val);
|
||||
} else if (!strcasecmp(param, "monitor-format")) {
|
||||
ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
|
||||
} else if (!strcasecmp(param, "membermacro")) {
|
||||
ast_string_field_set(q, membermacro, val);
|
||||
} else if (!strcasecmp(param, "membergosub")) {
|
||||
ast_string_field_set(q, membergosub, val);
|
||||
} else if (!strcasecmp(param, "queue-youarenext")) {
|
||||
|
@ -3440,8 +3437,6 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
|
|||
ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
|
||||
q->numperiodicannounce = 1;
|
||||
}
|
||||
} else if (!strcasecmp(param, "periodic-announce-startdelay")) {
|
||||
q->periodicannouncestartdelay = atoi(val);
|
||||
} else if (!strcasecmp(param, "periodic-announce-frequency")) {
|
||||
q->periodicannouncefrequency = atoi(val);
|
||||
} else if (!strcasecmp(param, "relative-periodic-announce")) {
|
||||
|
@ -3461,6 +3456,10 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
|
|||
}
|
||||
} else if (!strcasecmp(param, "autofill")) {
|
||||
q->autofill = ast_true(val);
|
||||
} else if (!strcasecmp(param, "monitor-type")) {
|
||||
if (!strcasecmp(val, "mixmonitor")) {
|
||||
q->montype = 1;
|
||||
}
|
||||
} else if (!strcasecmp(param, "autopause")) {
|
||||
q->autopause = autopause2int(val);
|
||||
} else if (!strcasecmp(param, "autopausedelay")) {
|
||||
|
@ -3588,7 +3587,6 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
|
|||
const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
|
||||
const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
|
||||
const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
|
||||
const char *reason_paused = ast_variable_retrieve(member_config, category, "reason_paused");
|
||||
|
||||
if (ast_strlen_zero(rt_uniqueid)) {
|
||||
ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
|
||||
|
@ -3655,9 +3653,6 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
|
|||
m->penalty = penalty;
|
||||
m->ringinuse = ringinuse;
|
||||
m->wrapuptime = wrapuptime;
|
||||
if (realtime_reason_paused) {
|
||||
ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
|
||||
}
|
||||
found = 1;
|
||||
ao2_ref(m, -1);
|
||||
break;
|
||||
|
@ -3672,9 +3667,6 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
|
|||
m->dead = 0;
|
||||
m->realtime = 1;
|
||||
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
|
||||
if (!ast_strlen_zero(reason_paused)) {
|
||||
ast_copy_string(m->reason_paused, reason_paused, sizeof(m->reason_paused));
|
||||
}
|
||||
if (!log_membername_as_agent) {
|
||||
ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
|
||||
} else {
|
||||
|
@ -4568,56 +4560,6 @@ static int compare_weight(struct call_queue *rq, struct member *member)
|
|||
return found;
|
||||
}
|
||||
|
||||
static int is_longest_waiting_caller(struct queue_ent *caller, struct member *member)
|
||||
{
|
||||
struct call_queue *q;
|
||||
struct member *mem;
|
||||
int is_longest_waiting = 1;
|
||||
struct ao2_iterator queue_iter;
|
||||
struct queue_ent *ch;
|
||||
|
||||
queue_iter = ao2_iterator_init(queues, 0);
|
||||
while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
|
||||
if (q == caller->parent) { /* don't check myself, could deadlock */
|
||||
queue_t_unref(q, "Done with iterator");
|
||||
continue;
|
||||
}
|
||||
ao2_lock(q);
|
||||
/*
|
||||
* If the other queue has equal weight, see if we should let that handle
|
||||
* their call first. If weights are not equal, compare_weights will step in.
|
||||
*/
|
||||
if (q->weight == caller->parent->weight && q->count && q->members) {
|
||||
if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
|
||||
ast_debug(2, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
|
||||
|
||||
/* Does this queue have a caller that's been waiting longer? */
|
||||
ch = q->head;
|
||||
while (ch) {
|
||||
/* If ch->pending, the other call (which may be waiting for a longer period of time),
|
||||
* is already ringing at another agent. Ignore such callers; otherwise, all agents
|
||||
* will be unused until the first caller is picked up.
|
||||
*/
|
||||
if (ch->start < caller->start && !ch->pending) {
|
||||
ast_debug(1, "Queue %s has a call at position %i that's been waiting longer (%li vs %li)\n",
|
||||
q->name, ch->pos, ch->start, caller->start);
|
||||
is_longest_waiting = 0;
|
||||
break;
|
||||
}
|
||||
ch = ch->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
ao2_unlock(q);
|
||||
queue_t_unref(q, "Done with iterator");
|
||||
if (!is_longest_waiting) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ao2_iterator_destroy(&queue_iter);
|
||||
return is_longest_waiting;
|
||||
}
|
||||
|
||||
/*! \brief common hangup actions */
|
||||
static void do_hang(struct callattempt *o)
|
||||
{
|
||||
|
@ -4682,12 +4624,6 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (force_longest_waiting_caller && !is_longest_waiting_caller(qe, memberp)) {
|
||||
ast_debug(1, "Another caller was waiting longer; delaying call to %s:%s\n",
|
||||
qe->parent->name, call->interface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memberp->ringinuse) {
|
||||
struct member *mem;
|
||||
|
||||
|
@ -4751,6 +4687,7 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
|
|||
int status;
|
||||
char tech[256];
|
||||
char *location;
|
||||
const char *macrocontext, *macroexten;
|
||||
struct ast_format_cap *nativeformats;
|
||||
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
|
||||
|
||||
|
@ -4811,8 +4748,8 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
|
|||
ast_channel_set_caller_event(tmp->chan, &caller, NULL);
|
||||
} else if (!ast_strlen_zero(ast_channel_dialed(qe->chan)->number.str)) {
|
||||
ast_set_callerid(tmp->chan, ast_channel_dialed(qe->chan)->number.str, NULL, NULL);
|
||||
} else if (!ast_strlen_zero(ast_channel_exten(qe->chan))) {
|
||||
ast_set_callerid(tmp->chan, ast_channel_exten(qe->chan), NULL, NULL);
|
||||
} else if (!ast_strlen_zero(S_OR(ast_channel_macroexten(qe->chan), ast_channel_exten(qe->chan)))) {
|
||||
ast_set_callerid(tmp->chan, S_OR(ast_channel_macroexten(qe->chan), ast_channel_exten(qe->chan)), NULL, NULL);
|
||||
}
|
||||
tmp->dial_callerid_absent = 1;
|
||||
}
|
||||
|
@ -4832,8 +4769,14 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
|
|||
ast_channel_adsicpe_set(tmp->chan, ast_channel_adsicpe(qe->chan));
|
||||
|
||||
/* Inherit context and extension */
|
||||
ast_channel_dialcontext_set(tmp->chan, ast_channel_context(qe->chan));
|
||||
ast_channel_exten_set(tmp->chan, ast_channel_exten(qe->chan));
|
||||
macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
|
||||
ast_channel_dialcontext_set(tmp->chan, ast_strlen_zero(macrocontext) ? ast_channel_context(qe->chan) : macrocontext);
|
||||
macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
|
||||
if (!ast_strlen_zero(macroexten)) {
|
||||
ast_channel_exten_set(tmp->chan, macroexten);
|
||||
} else {
|
||||
ast_channel_exten_set(tmp->chan, ast_channel_exten(qe->chan));
|
||||
}
|
||||
|
||||
/* Save the original channel name to detect call pickup masquerading in. */
|
||||
tmp->orig_chan_name = ast_strdup(ast_channel_name(tmp->chan));
|
||||
|
@ -5177,7 +5120,8 @@ static void update_connected_line_from_peer(struct ast_channel *chan, struct ast
|
|||
ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(peer));
|
||||
ast_channel_unlock(peer);
|
||||
connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
|
||||
if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)) {
|
||||
if (ast_channel_connected_line_sub(peer, chan, &connected_caller, 0)
|
||||
&& ast_channel_connected_line_macro(peer, chan, &connected_caller, is_caller, 0)) {
|
||||
ast_channel_update_connected_line(chan, &connected_caller, NULL);
|
||||
}
|
||||
ast_party_connected_line_free(&connected_caller);
|
||||
|
@ -5297,7 +5241,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
update_connected_line_from_peer(in, o->chan, 1);
|
||||
} else if (!o->block_connected_update) {
|
||||
if (o->pending_connected_update) {
|
||||
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0)) {
|
||||
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
|
||||
ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
|
||||
ast_channel_update_connected_line(in, &o->connected, NULL);
|
||||
}
|
||||
} else if (!o->dial_callerid_absent) {
|
||||
|
@ -5407,7 +5352,7 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
ast_party_number_init(&ast_channel_redirecting(o->chan)->from.number);
|
||||
ast_channel_redirecting(o->chan)->from.number.valid = 1;
|
||||
ast_channel_redirecting(o->chan)->from.number.str =
|
||||
ast_strdup(ast_channel_exten(in));
|
||||
ast_strdup(S_OR(ast_channel_macroexten(in), ast_channel_exten(in)));
|
||||
}
|
||||
|
||||
ast_channel_dialed(o->chan)->transit_network_select = ast_channel_dialed(in)->transit_network_select;
|
||||
|
@ -5426,13 +5371,17 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
* Redirecting updates to the caller make sense only on single
|
||||
* call at a time strategies.
|
||||
*
|
||||
* Need to re-evaluate if calling unlock is still required as we no longer
|
||||
* use macro.
|
||||
* We must unlock o->chan before calling
|
||||
* ast_channel_redirecting_macro, because we put o->chan into
|
||||
* autoservice there. That is pretty much a guaranteed
|
||||
* deadlock. This is why the handling of o->chan's lock may
|
||||
* seem a bit unusual here.
|
||||
*/
|
||||
ast_party_redirecting_init(&redirecting);
|
||||
ast_party_redirecting_copy(&redirecting, ast_channel_redirecting(o->chan));
|
||||
ast_channel_unlock(o->chan);
|
||||
if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0)) {
|
||||
if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0) &&
|
||||
ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) {
|
||||
ast_channel_update_redirecting(in, &redirecting, NULL);
|
||||
}
|
||||
ast_party_redirecting_free(&redirecting);
|
||||
|
@ -5481,7 +5430,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
update_connected_line_from_peer(in, o->chan, 1);
|
||||
} else if (!o->block_connected_update) {
|
||||
if (o->pending_connected_update) {
|
||||
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0)) {
|
||||
if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
|
||||
ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
|
||||
ast_channel_update_connected_line(in, &o->connected, NULL);
|
||||
}
|
||||
} else if (!o->dial_callerid_absent) {
|
||||
|
@ -5573,7 +5523,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
*/
|
||||
o->dial_callerid_absent = 1;
|
||||
|
||||
if (ast_channel_connected_line_sub(o->chan, in, f, 1)) {
|
||||
if (ast_channel_connected_line_sub(o->chan, in, f, 1) &&
|
||||
ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
|
||||
ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
|
||||
}
|
||||
break;
|
||||
|
@ -5603,7 +5554,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
}
|
||||
ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
|
||||
ochan_name, inchan_name);
|
||||
if (ast_channel_redirecting_sub(o->chan, in, f, 1)) {
|
||||
if (ast_channel_redirecting_sub(o->chan, in, f, 1) &&
|
||||
ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
|
||||
ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
|
||||
}
|
||||
break;
|
||||
|
@ -5684,7 +5636,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(o->chan));
|
||||
break;
|
||||
}
|
||||
if (ast_channel_connected_line_sub(in, o->chan, f, 1)) {
|
||||
if (ast_channel_connected_line_sub(in, o->chan, f, 1) &&
|
||||
ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) {
|
||||
ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
|
||||
}
|
||||
break;
|
||||
|
@ -5693,7 +5646,8 @@ static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callatte
|
|||
ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
|
||||
break;
|
||||
}
|
||||
if (ast_channel_redirecting_sub(in, o->chan, f, 1)) {
|
||||
if (ast_channel_redirecting_sub(in, o->chan, f, 1) &&
|
||||
ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) {
|
||||
ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
|
||||
}
|
||||
break;
|
||||
|
@ -6967,10 +6921,11 @@ static void setup_mixmonitor(struct queue_ent *qe, const char *filename)
|
|||
* \param[in,out] tries the number of times we have tried calling queue members
|
||||
* \param[out] noption set if the call to Queue() has the 'n' option set.
|
||||
* \param[in] agi the agi passed as the fifth parameter to the Queue() application
|
||||
* \param[in] macro the macro passed as the sixth parameter to the Queue() application
|
||||
* \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
|
||||
* \param[in] ringing 1 if the 'r' option is set, otherwise 0
|
||||
*/
|
||||
static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *gosub, int ringing)
|
||||
static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
|
||||
{
|
||||
struct member *cur;
|
||||
struct callattempt *outgoing = NULL; /* the list of calls we are building */
|
||||
|
@ -6979,6 +6934,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
|
|||
char oldcontext[AST_MAX_CONTEXT]="";
|
||||
char queuename[256]="";
|
||||
struct ast_channel *peer;
|
||||
struct ast_channel *which;
|
||||
struct callattempt *lpeer;
|
||||
struct member *member;
|
||||
struct ast_app *application;
|
||||
|
@ -6991,8 +6947,10 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
|
|||
struct ast_bridge_config bridge_config;
|
||||
char nondataquality = 1;
|
||||
char *agiexec = NULL;
|
||||
char *macroexec = NULL;
|
||||
char *gosubexec = NULL;
|
||||
const char *monitorfilename;
|
||||
char tmpid[256];
|
||||
int forwardsallowed = 1;
|
||||
int block_connected_line = 0;
|
||||
struct ao2_iterator memi;
|
||||
|
@ -7001,6 +6959,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
|
|||
time_t starttime;
|
||||
|
||||
memset(&bridge_config, 0, sizeof(bridge_config));
|
||||
tmpid[0] = 0;
|
||||
time(&now);
|
||||
|
||||
/* If we've already exceeded our timeout, then just stop
|
||||
|
@ -7202,7 +7161,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
|
|||
if (!res2 && announce) {
|
||||
char *front;
|
||||
char *announcefiles = ast_strdupa(announce);
|
||||
while ((front = ast_strsep(&announcefiles, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||
while ((front = strsep(&announcefiles, "&"))) {
|
||||
if (play_file(peer, front) < 0) {
|
||||
ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", front, ast_channel_name(peer));
|
||||
}
|
||||
|
@ -7321,7 +7280,32 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
|
|||
|
||||
/* Begin Monitoring */
|
||||
if (*qe->parent->monfmt) {
|
||||
setup_mixmonitor(qe, monitorfilename);
|
||||
if (!qe->parent->montype) {
|
||||
const char *monexec;
|
||||
ast_debug(1, "Starting Monitor as requested.\n");
|
||||
ast_channel_lock(qe->chan);
|
||||
if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) {
|
||||
which = qe->chan;
|
||||
monexec = monexec ? ast_strdupa(monexec) : NULL;
|
||||
} else {
|
||||
which = peer;
|
||||
}
|
||||
ast_channel_unlock(qe->chan);
|
||||
if (monitorfilename) {
|
||||
ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT, NULL);
|
||||
} else if (qe->chan) {
|
||||
ast_monitor_start(which, qe->parent->monfmt, ast_channel_uniqueid(qe->chan), 1, X_REC_IN | X_REC_OUT, NULL);
|
||||
} else {
|
||||
/* Last ditch effort -- no channel, make up something */
|
||||
snprintf(tmpid, sizeof(tmpid), "chan-%lx", (unsigned long)ast_random());
|
||||
ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT, NULL);
|
||||
}
|
||||
if (!ast_strlen_zero(monexec)) {
|
||||
ast_monitor_setjoinfiles(which, 1);
|
||||
}
|
||||
} else {
|
||||
setup_mixmonitor(qe, monitorfilename);
|
||||
}
|
||||
}
|
||||
/* Drop out of the queue at this point, to prepare for next caller */
|
||||
leave_queue(qe);
|
||||
|
@ -7330,6 +7314,21 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
|
|||
ast_channel_sendurl(peer, url);
|
||||
}
|
||||
|
||||
/* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
|
||||
/* use macro from dialplan if passed as a option, otherwise use the default queue macro */
|
||||
if (!ast_strlen_zero(macro)) {
|
||||
macroexec = ast_strdupa(macro);
|
||||
} else {
|
||||
if (qe->parent->membermacro) {
|
||||
macroexec = ast_strdupa(qe->parent->membermacro);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(macroexec)) {
|
||||
ast_debug(1, "app_queue: macro=%s.\n", macroexec);
|
||||
ast_app_exec_macro(qe->chan, peer, macroexec);
|
||||
}
|
||||
|
||||
/* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
|
||||
/* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
|
||||
if (!ast_strlen_zero(gosub)) {
|
||||
|
@ -7638,10 +7637,10 @@ static int add_to_queue(const char *queuename, const char *interface, const char
|
|||
* \retval RES_OKAY change priority
|
||||
* \retval RES_NOT_CALLER queue exists but no caller
|
||||
*/
|
||||
static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority, int immediate)
|
||||
static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority)
|
||||
{
|
||||
struct call_queue *q;
|
||||
struct queue_ent *current, *prev = NULL, *caller_qe = NULL;
|
||||
struct queue_ent *qe;
|
||||
int res = RES_NOSUCHQUEUE;
|
||||
|
||||
/*! \note Ensure the appropriate realtime queue is loaded. Note that this
|
||||
|
@ -7652,57 +7651,14 @@ static int change_priority_caller_on_queue(const char *queuename, const char *ca
|
|||
|
||||
ao2_lock(q);
|
||||
res = RES_NOT_CALLER;
|
||||
for (current = q->head; current; current = current->next) {
|
||||
if (strcmp(ast_channel_name(current->chan), caller) == 0) {
|
||||
for (qe = q->head; qe; qe = qe->next) {
|
||||
if (strcmp(ast_channel_name(qe->chan), caller) == 0) {
|
||||
ast_debug(1, "%s Caller new priority %d in queue %s\n",
|
||||
caller, priority, queuename);
|
||||
current->prio = priority;
|
||||
if (immediate) {
|
||||
/* This caller is being immediately moved in the queue so remove them */
|
||||
if (prev) {
|
||||
prev->next = current->next;
|
||||
} else {
|
||||
q->head = current->next;
|
||||
}
|
||||
caller_qe = current;
|
||||
/* The position for all callers is not recalculated in here as it will
|
||||
* be updated when the moved caller is inserted back into the queue
|
||||
*/
|
||||
}
|
||||
qe->prio = priority;
|
||||
res = RES_OKAY;
|
||||
break;
|
||||
} else if (immediate) {
|
||||
prev = current;
|
||||
}
|
||||
}
|
||||
|
||||
if (caller_qe) {
|
||||
int inserted = 0, pos = 0;
|
||||
|
||||
/* If a caller queue entry exists, we are applying their priority immediately
|
||||
* and have to reinsert them at the correct position.
|
||||
*/
|
||||
prev = NULL;
|
||||
current = q->head;
|
||||
while (current) {
|
||||
if (!inserted && (caller_qe->prio > current->prio)) {
|
||||
insert_entry(q, prev, caller_qe, &pos);
|
||||
inserted = 1;
|
||||
}
|
||||
|
||||
/* We always update the position as it may have changed */
|
||||
current->pos = ++pos;
|
||||
|
||||
/* Move to the next caller in the queue */
|
||||
prev = current;
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if (!inserted) {
|
||||
insert_entry(q, prev, caller_qe, &pos);
|
||||
}
|
||||
}
|
||||
|
||||
ao2_unlock(q);
|
||||
return res;
|
||||
}
|
||||
|
@ -7784,17 +7740,10 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con
|
|||
(paused ? "" : "un"), (paused ? "" : "un"), q->name, mem->interface);
|
||||
}
|
||||
|
||||
if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid)) {
|
||||
if (realtime_reason_paused) {
|
||||
if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "reason_paused", S_OR(reason, ""), "paused", paused ? "1" : "0", SENTINEL) < 0) {
|
||||
ast_log(LOG_WARNING, "Failed update of realtime queue member %s:%s %spause and reason '%s'\n",
|
||||
q->name, mem->interface, (paused ? "" : "un"), S_OR(reason, ""));
|
||||
}
|
||||
} else {
|
||||
if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "paused", paused ? "1" : "0", SENTINEL) < 0) {
|
||||
ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
|
||||
(paused ? "" : "un"), q->name, mem->interface);
|
||||
}
|
||||
if (mem->realtime) {
|
||||
if (update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0")) {
|
||||
ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
|
||||
(paused ? "" : "un"), q->name, mem->interface);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7805,9 +7754,6 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con
|
|||
if (paused && !ast_strlen_zero(reason)) {
|
||||
ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
|
||||
} else {
|
||||
/* We end up filling this in again later (temporarily) but we need it
|
||||
* empty for now so that the intervening code - specifically
|
||||
* dump_queue_members() - has the correct view of things. */
|
||||
mem->reason_paused[0] = '\0';
|
||||
}
|
||||
|
||||
|
@ -7826,22 +7772,10 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con
|
|||
"Queue:%s_avail", q->name);
|
||||
}
|
||||
|
||||
if (!paused && !ast_strlen_zero(reason)) {
|
||||
/* Because we've been unpaused with a 'reason' we need to ensure that
|
||||
* that reason is emitted when the subsequent PauseQueueMember event
|
||||
* is raised. So temporarily set it on the member and clear it out
|
||||
* again right after. */
|
||||
ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
|
||||
}
|
||||
|
||||
ast_queue_log(q->name, "NONE", mem->membername, paused ? "PAUSE" : "UNPAUSE",
|
||||
"%s", mem->reason_paused);
|
||||
ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"),
|
||||
"%s", S_OR(reason, ""));
|
||||
|
||||
publish_queue_member_pause(q, mem);
|
||||
|
||||
if (!paused) {
|
||||
mem->reason_paused[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
|
||||
|
@ -8238,7 +8172,7 @@ static int pqm_exec(struct ast_channel *chan, const char *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief UnpauseQueueMember application */
|
||||
/*! \brief UnPauseQueueMember application */
|
||||
static int upqm_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *parse;
|
||||
|
@ -8259,7 +8193,7 @@ static int upqm_exec(struct ast_channel *chan, const char *data)
|
|||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(args.interface)) {
|
||||
ast_log(LOG_WARNING, "Missing interface argument to UnpauseQueueMember ([queuename],interface[,options[,reason]])\n");
|
||||
ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -8529,6 +8463,7 @@ static int queue_exec(struct ast_channel *chan, const char *data)
|
|||
AST_APP_ARG(announceoverride);
|
||||
AST_APP_ARG(queuetimeoutstr);
|
||||
AST_APP_ARG(agi);
|
||||
AST_APP_ARG(macro);
|
||||
AST_APP_ARG(gosub);
|
||||
AST_APP_ARG(rule);
|
||||
AST_APP_ARG(position);
|
||||
|
@ -8540,7 +8475,7 @@ static int queue_exec(struct ast_channel *chan, const char *data)
|
|||
int max_forwards;
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,gosub[,rule[,position]]]]]]]]\n");
|
||||
ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -8556,13 +8491,14 @@ static int queue_exec(struct ast_channel *chan, const char *data)
|
|||
parse = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, timeout: %s, agi: %s, gosub: %s, rule: %s, position: %s\n",
|
||||
ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, timeout: %s, agi: %s, macro: %s, gosub: %s, rule: %s, position: %s\n",
|
||||
args.queuename,
|
||||
S_OR(args.options, ""),
|
||||
S_OR(args.url, ""),
|
||||
S_OR(args.announceoverride, ""),
|
||||
S_OR(args.queuetimeoutstr, ""),
|
||||
S_OR(args.agi, ""),
|
||||
S_OR(args.macro, ""),
|
||||
S_OR(args.gosub, ""),
|
||||
S_OR(args.rule, ""),
|
||||
S_OR(args.position, ""));
|
||||
|
@ -8678,11 +8614,6 @@ static int queue_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
ast_assert(qe.parent != NULL);
|
||||
|
||||
if (qe.parent->periodicannouncestartdelay >= 0) {
|
||||
qe.last_periodic_announce_time += qe.parent->periodicannouncestartdelay;
|
||||
qe.last_periodic_announce_time -= qe.parent->periodicannouncefrequency;
|
||||
}
|
||||
|
||||
ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
|
||||
S_OR(args.url, ""),
|
||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
|
||||
|
@ -8788,7 +8719,7 @@ check_turns:
|
|||
}
|
||||
|
||||
/* Try calling all queue members for 'timeout' seconds */
|
||||
res = try_calling(&qe, opts, opt_args, args.announceoverride, args.url, &tries, &noption, args.agi, args.gosub, ringing);
|
||||
res = try_calling(&qe, opts, opt_args, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
|
||||
if (res) {
|
||||
goto stop;
|
||||
}
|
||||
|
@ -9567,7 +9498,6 @@ static void queue_reset_global_params(void)
|
|||
shared_lastcall = 0;
|
||||
negative_penalty_invalid = 0;
|
||||
log_membername_as_agent = 0;
|
||||
force_longest_waiting_caller = 0;
|
||||
}
|
||||
|
||||
/*! Set the global queue parameters as defined in the "general" section of queues.conf */
|
||||
|
@ -9593,9 +9523,6 @@ static void queue_set_global_params(struct ast_config *cfg)
|
|||
if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
|
||||
log_membername_as_agent = ast_true(general_val);
|
||||
}
|
||||
if ((general_val = ast_variable_retrieve(cfg, "general", "force_longest_waiting_caller"))) {
|
||||
force_longest_waiting_caller = ast_true(general_val);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief reload information pertaining to a single member
|
||||
|
@ -10897,13 +10824,12 @@ static int manager_queue_member_penalty(struct mansession *s, const struct messa
|
|||
|
||||
static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *queuename, *caller, *priority_s, *immediate_s;
|
||||
int priority = 0, immediate = 0;
|
||||
const char *queuename, *caller, *priority_s;
|
||||
int priority = 0;
|
||||
|
||||
queuename = astman_get_header(m, "Queue");
|
||||
caller = astman_get_header(m, "Caller");
|
||||
priority_s = astman_get_header(m, "Priority");
|
||||
immediate_s = astman_get_header(m, "Immediate");
|
||||
|
||||
if (ast_strlen_zero(queuename)) {
|
||||
astman_send_error(s, m, "'Queue' not specified.");
|
||||
|
@ -10923,11 +10849,7 @@ static int manager_change_priority_caller_on_queue(struct mansession *s, const s
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(immediate_s)) {
|
||||
immediate = ast_true(immediate_s);
|
||||
}
|
||||
|
||||
switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
|
||||
switch (change_priority_caller_on_queue(queuename, caller, priority)) {
|
||||
case RES_OKAY:
|
||||
astman_send_ack(s, m, "Priority change for caller on queue");
|
||||
break;
|
||||
|
@ -10988,7 +10910,7 @@ static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct as
|
|||
case CLI_INIT:
|
||||
e->command = "queue add member";
|
||||
e->usage =
|
||||
"Usage: queue add member <dial string> to <queue> [penalty <penalty> [as <membername> [state_interface <interface>]]]\n"
|
||||
"Usage: queue add member <dial string> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
|
||||
" Add a dial string (Such as a channel,e.g. SIP/6001) to a queue with optionally: a penalty, membername and a state_interface\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
|
@ -11171,21 +11093,21 @@ static char *handle_queue_remove_member(struct ast_cli_entry *e, int cmd, struct
|
|||
static char *handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
const char *queuename, *caller;
|
||||
int priority, immediate = 0;
|
||||
int priority;
|
||||
char *res = CLI_FAILURE;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "queue priority caller";
|
||||
e->usage =
|
||||
"Usage: queue priority caller <channel> on <queue> to <priority> [immediate]\n"
|
||||
" Change the priority of a channel on a queue, optionally applying the change in relation to existing callers.\n";
|
||||
"Usage: queue priority caller <channel> on <queue> to <priority>\n"
|
||||
" Change the priority of a channel on a queue.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc < 8) {
|
||||
if (a->argc != 8) {
|
||||
return CLI_SHOWUSAGE;
|
||||
} else if (strcmp(a->argv[4], "on")) {
|
||||
return CLI_SHOWUSAGE;
|
||||
|
@ -11194,17 +11116,12 @@ static char *handle_queue_change_priority_caller(struct ast_cli_entry *e, int cm
|
|||
} else if (sscanf(a->argv[7], "%30d", &priority) != 1) {
|
||||
ast_log (LOG_ERROR, "<priority> parameter must be an integer.\n");
|
||||
return CLI_SHOWUSAGE;
|
||||
} else if (a->argc == 9) {
|
||||
if (strcmp(a->argv[8], "immediate")) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
immediate = 1;
|
||||
}
|
||||
|
||||
caller = a->argv[3];
|
||||
queuename = a->argv[5];
|
||||
|
||||
switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
|
||||
switch (change_priority_caller_on_queue(queuename, caller, priority)) {
|
||||
case RES_OKAY:
|
||||
res = CLI_SUCCESS;
|
||||
break;
|
||||
|
@ -11776,13 +11693,11 @@ static int load_module(void)
|
|||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, "reason_paused", RQ_CHAR, 80, SENTINEL);
|
||||
ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
|
||||
|
||||
/*
|
||||
* This section is used to determine which name for 'ringinuse' to use in realtime members
|
||||
* Necessary for supporting older setups.
|
||||
*
|
||||
* It also checks if 'reason_paused' exists in the realtime backend
|
||||
*/
|
||||
member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name LIKE", "%", SENTINEL);
|
||||
if (!member_config) {
|
||||
|
@ -11800,10 +11715,6 @@ static int load_module(void)
|
|||
ast_log(LOG_NOTICE, "No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
|
||||
realtime_ringinuse_field = "ringinuse";
|
||||
}
|
||||
|
||||
if (ast_variable_retrieve(member_config, NULL, "reason_paused")) {
|
||||
realtime_reason_paused = 1;
|
||||
}
|
||||
}
|
||||
ast_config_destroy(member_config);
|
||||
|
||||
|
@ -11936,4 +11847,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "True Call Queueing",
|
|||
.unload = unload_module,
|
||||
.reload = reload,
|
||||
.load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
|
||||
.optional_modules = "res_monitor",
|
||||
);
|
||||
|
|
|
@ -49,15 +49,9 @@
|
|||
name.</para>
|
||||
</parameter>
|
||||
<parameter name="filenames" argsep="&">
|
||||
<para>Ampersand separated list of filenames to play before
|
||||
reading digits or tone with option <literal>i</literal>. If
|
||||
the filename is a relative filename (it does not begin with a
|
||||
slash), it will be searched for in the Asterisk sounds
|
||||
directory. If the filename is able to be parsed as a URL,
|
||||
Asterisk will download the file and then begin playback on
|
||||
it. To include a literal <literal>&</literal> in the URL
|
||||
you can enclose the URL in single quotes.</para>
|
||||
<argument name="filename" required="true" />
|
||||
<argument name="filename" required="true">
|
||||
<para>file(s) to play before reading digits or tone with option i</para>
|
||||
</argument>
|
||||
<argument name="filename2" multiple="true" />
|
||||
</parameter>
|
||||
<parameter name="maxdigits">
|
||||
|
@ -91,13 +85,6 @@
|
|||
and you will need to rely on duration and max digits
|
||||
for ending input.</para>
|
||||
</option>
|
||||
<option name="e">
|
||||
<para>to read the terminator as the digit string if the
|
||||
only digit read is the terminator. This is for cases
|
||||
where the terminator is a valid digit, but only by itself.
|
||||
ie; <literal>1234</literal> and <literal>#</literal> are
|
||||
valid, but <literal>1234#</literal> is not.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
<parameter name="attempts">
|
||||
|
@ -138,7 +125,6 @@ enum read_option_flags {
|
|||
OPT_INDICATION = (1 << 1),
|
||||
OPT_NOANSWER = (1 << 2),
|
||||
OPT_TERMINATOR = (1 << 3),
|
||||
OPT_KEEP_TERMINATOR = (1 << 4),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -152,7 +138,6 @@ AST_APP_OPTIONS(read_app_options, {
|
|||
AST_APP_OPTION('i', OPT_INDICATION),
|
||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||
AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
|
||||
AST_APP_OPTION('e', OPT_KEEP_TERMINATOR),
|
||||
});
|
||||
|
||||
static char *app = "Read";
|
||||
|
@ -276,20 +261,12 @@ static int read_exec(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
} else {
|
||||
res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
|
||||
if (res == AST_GETDATA_COMPLETE) {
|
||||
if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
|
||||
status = "OK";
|
||||
} else if (res == AST_GETDATA_EMPTY_END_TERMINATED) {
|
||||
if (ast_test_flag(&flags, OPT_KEEP_TERMINATOR)) {
|
||||
/* if the option is set to do so, read the
|
||||
returned string as the terminator string */
|
||||
ast_copy_string(tmp, terminator, sizeof(tmp));
|
||||
}
|
||||
status = "OK";
|
||||
} else if (res == AST_GETDATA_TIMEOUT) {
|
||||
else if (res == AST_GETDATA_TIMEOUT)
|
||||
status = "TIMEOUT";
|
||||
} else if (res == AST_GETDATA_INTERRUPTED) {
|
||||
else if (res == AST_GETDATA_INTERRUPTED)
|
||||
status = "INTERRUPTED";
|
||||
}
|
||||
}
|
||||
if (res > -1) {
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||
|
|
|
@ -57,15 +57,6 @@
|
|||
<parameter name="channel" required="false">
|
||||
<para>Channel where digits will be played</para>
|
||||
</parameter>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="a">
|
||||
<para>Answer the channel specified by the <literal>channel</literal>
|
||||
parameter if it is not already up. If no <literal>channel</literal>
|
||||
parameter is provided, the current channel will be answered.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>It will send all digits or terminate if it encounters an error.</para>
|
||||
|
@ -97,38 +88,8 @@
|
|||
<para>Plays a dtmf digit on the specified channel.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<manager name="SendFlash" language="en_US">
|
||||
<synopsis>
|
||||
Send a hook flash 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 hook flash to.</para>
|
||||
</parameter>
|
||||
<parameter name="Receive" required="false">
|
||||
<para>Emulate receiving a hook flash on this channel instead of sending it out.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Sends a hook flash on the specified channel.</para>
|
||||
</description>
|
||||
</manager>
|
||||
***/
|
||||
|
||||
enum read_option_flags {
|
||||
OPT_ANSWER = (1 << 0),
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(senddtmf_app_options, {
|
||||
AST_APP_OPTION('a', OPT_ANSWER),
|
||||
});
|
||||
|
||||
enum {
|
||||
/* note: this entry _MUST_ be the last one in the enum */
|
||||
OPT_ARG_ARRAY_SIZE,
|
||||
};
|
||||
|
||||
static const char senddtmf_name[] = "SendDTMF";
|
||||
|
||||
static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
||||
|
@ -139,14 +100,11 @@ static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
|||
struct ast_channel *chan_found = NULL;
|
||||
struct ast_channel *chan_dest = chan;
|
||||
struct ast_channel *chan_autoservice = NULL;
|
||||
char *opt_args[OPT_ARG_ARRAY_SIZE];
|
||||
struct ast_flags flags = {0};
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(digits);
|
||||
AST_APP_ARG(dinterval);
|
||||
AST_APP_ARG(duration);
|
||||
AST_APP_ARG(channel);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(vdata)) {
|
||||
|
@ -178,12 +136,6 @@ static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
|||
chan_autoservice = chan;
|
||||
}
|
||||
}
|
||||
if (!ast_strlen_zero(args.options)) {
|
||||
ast_app_parse_options(senddtmf_app_options, &flags, opt_args, args.options);
|
||||
}
|
||||
if (ast_test_flag(&flags, OPT_ANSWER)) {
|
||||
ast_auto_answer(chan_dest);
|
||||
}
|
||||
res = ast_dtmf_stream(chan_dest, chan_autoservice, args.digits,
|
||||
dinterval <= 0 ? 250 : dinterval, duration);
|
||||
if (chan_found) {
|
||||
|
@ -235,41 +187,12 @@ static int manager_play_dtmf(struct mansession *s, const struct message *m)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int manager_send_flash(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *channel = astman_get_header(m, "Channel");
|
||||
const char *receive_s = astman_get_header(m, "Receive");
|
||||
struct ast_channel *chan;
|
||||
|
||||
if (!(chan = ast_channel_get_by_name(channel))) {
|
||||
astman_send_error(s, m, "Channel not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ast_true(receive_s)) {
|
||||
struct ast_frame f = { AST_FRAME_CONTROL, };
|
||||
f.subclass.integer = AST_CONTROL_FLASH;
|
||||
ast_queue_frame(chan, &f);
|
||||
} else {
|
||||
struct ast_frame f = { AST_FRAME_CONTROL, };
|
||||
f.subclass.integer = AST_CONTROL_FLASH;
|
||||
ast_channel_lock(chan);
|
||||
ast_write(chan, &f);
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
|
||||
chan = ast_channel_unref(chan);
|
||||
astman_send_ack(s, m, "Flash successfully queued");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(senddtmf_name);
|
||||
res |= ast_manager_unregister("PlayDTMF");
|
||||
res |= ast_manager_unregister("SendFlash");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -279,7 +202,6 @@ static int load_module(void)
|
|||
int res;
|
||||
|
||||
res = ast_manager_register_xml("PlayDTMF", EVENT_FLAG_CALL, manager_play_dtmf);
|
||||
res |= ast_manager_register_xml("SendFlash", EVENT_FLAG_CALL, manager_send_flash);
|
||||
res |= ast_register_application_xml(senddtmf_name, senddtmf_exec);
|
||||
|
||||
return res;
|
||||
|
|
|
@ -113,7 +113,8 @@
|
|||
</para>
|
||||
<note><para>The text encoding and transmission method is completely at the
|
||||
discretion of the channel driver. chan_pjsip will use in-dialog SIP MESSAGE
|
||||
messages always.</para></note>
|
||||
messages always. chan_sip will use T.140 via RTP if a text media type was
|
||||
negotiated and in-dialog SIP MESSAGE messages otherwise.</para></note>
|
||||
<para>
|
||||
</para>
|
||||
<para>Examples:
|
||||
|
@ -138,6 +139,8 @@
|
|||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendImage</ref>
|
||||
<ref type="application">SendURL</ref>
|
||||
<ref type="application">ReceiveText</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
|
@ -179,6 +182,8 @@
|
|||
</description>
|
||||
<see-also>
|
||||
<ref type="application">SendText</ref>
|
||||
<ref type="application">SendImage</ref>
|
||||
<ref type="application">SendURL</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
|
|
@ -1,471 +0,0 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2022, 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 Channel signaling applications
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
* \ingroup applications
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>extended</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Signal" language="en_US">
|
||||
<synopsis>
|
||||
Sends a signal to any waiting channels.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="signalname" required="true">
|
||||
<para>Name of signal to send.</para>
|
||||
</parameter>
|
||||
<parameter name="payload" required="false">
|
||||
<para>Payload data to deliver.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Sends a named signal to any channels that may be
|
||||
waiting for one. Acts as a producer in a simple
|
||||
message queue.</para>
|
||||
<variablelist>
|
||||
<variable name="SIGNALSTATUS">
|
||||
<value name="SUCCESS">
|
||||
Signal was successfully sent to at least
|
||||
one listener for processing.
|
||||
</value>
|
||||
<value name="FAILURE">
|
||||
Signal could not be sent or nobody
|
||||
was listening for this signal.
|
||||
</value>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<example title="Send a signal named workdone">
|
||||
same => n,Signal(workdone,Work has completed)
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">WaitForSignal</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="WaitForSignal" language="en_US">
|
||||
<synopsis>
|
||||
Waits for a named signal on a channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="signalname" required="true">
|
||||
<para>Name of signal to send.</para>
|
||||
</parameter>
|
||||
<parameter name="signaltimeout" required="false">
|
||||
<para>Maximum time, in seconds, to wait for signal.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Waits for <replaceable>signaltimeout</replaceable> seconds on the current
|
||||
channel to receive a signal with name <replaceable>signalname</replaceable>.
|
||||
Acts as a consumer in a simple message queue.</para>
|
||||
<para>Result of signal wait will be stored in the following variables:</para>
|
||||
<variablelist>
|
||||
<variable name="WAITFORSIGNALSTATUS">
|
||||
<value name="SIGNALED">
|
||||
Signal was received.
|
||||
</value>
|
||||
<value name="TIMEOUT">
|
||||
Timed out waiting for signal.
|
||||
</value>
|
||||
<value name="HANGUP">
|
||||
Channel hung up before signal was received.
|
||||
</value>
|
||||
</variable>
|
||||
<variable name="WAITFORSIGNALPAYLOAD">
|
||||
<para>Data payload attached to signal, if it exists</para>
|
||||
</variable>
|
||||
</variablelist>
|
||||
<example title="Wait for the workdone signal, indefinitely, and print out payload">
|
||||
same => n,WaitForSignal(workdone)
|
||||
same => n,NoOp(Received: ${WAITFORSIGNALPAYLOAD})
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Signal</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
***/
|
||||
|
||||
static const char * const app = "Signal";
|
||||
static const char * const app2 = "WaitForSignal";
|
||||
|
||||
struct signalitem {
|
||||
ast_mutex_t lock;
|
||||
char name[AST_MAX_CONTEXT];
|
||||
int sig_alert_pipe[2];
|
||||
int watchers;
|
||||
unsigned int signaled:1;
|
||||
char *payload;
|
||||
AST_LIST_ENTRY(signalitem) entry; /*!< Next Signal item */
|
||||
};
|
||||
|
||||
static AST_RWLIST_HEAD_STATIC(signals, signalitem);
|
||||
|
||||
static struct signalitem *alloc_signal(const char *sname)
|
||||
{
|
||||
struct signalitem *s;
|
||||
|
||||
if (!(s = ast_calloc(1, sizeof(*s)))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_mutex_init(&s->lock);
|
||||
ast_copy_string(s->name, sname, sizeof(s->name));
|
||||
|
||||
s->sig_alert_pipe[0] = -1;
|
||||
s->sig_alert_pipe[1] = -1;
|
||||
s->watchers = 0;
|
||||
s->payload = NULL;
|
||||
ast_alertpipe_init(s->sig_alert_pipe);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int dealloc_signal(struct signalitem *s)
|
||||
{
|
||||
if (s->watchers) { /* somebody is still using us... refuse to go away */
|
||||
ast_debug(1, "Signal '%s' is still being used by %d listener(s)\n", s->name, s->watchers);
|
||||
return -1;
|
||||
}
|
||||
ast_alertpipe_close(s->sig_alert_pipe);
|
||||
ast_mutex_destroy(&s->lock);
|
||||
if (s->payload) {
|
||||
ast_free(s->payload);
|
||||
s->payload = NULL;
|
||||
}
|
||||
ast_free(s);
|
||||
s = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_signal(char *sname)
|
||||
{
|
||||
int res = -1;
|
||||
struct signalitem *s;
|
||||
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&signals, s, entry) {
|
||||
if (!strcmp(s->name, sname)) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
res = dealloc_signal(s);
|
||||
ast_debug(1, "Removed signal '%s'\n", sname);
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct signalitem *get_signal(char *sname, int addnew)
|
||||
{
|
||||
struct signalitem *s = NULL;
|
||||
AST_RWLIST_WRLOCK(&signals);
|
||||
AST_LIST_TRAVERSE(&signals, s, entry) {
|
||||
if (!strcasecmp(s->name, sname)) {
|
||||
ast_debug(1, "Using existing signal item '%s'\n", sname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!s) {
|
||||
if (addnew) { /* signal doesn't exist, so create it */
|
||||
s = alloc_signal(sname);
|
||||
/* Totally fail if we fail to find/create an entry */
|
||||
if (s) {
|
||||
ast_debug(1, "Created new signal item '%s'\n", sname);
|
||||
AST_RWLIST_INSERT_HEAD(&signals, s, entry);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Failed to create signal item for '%s'\n", sname);
|
||||
}
|
||||
} else {
|
||||
ast_debug(1, "Signal '%s' doesn't exist, and not creating it\n", sname);
|
||||
}
|
||||
}
|
||||
AST_RWLIST_UNLOCK(&signals);
|
||||
return s;
|
||||
}
|
||||
|
||||
static int wait_for_signal_or_hangup(struct ast_channel *chan, char *signame, int timeout)
|
||||
{
|
||||
struct signalitem *s = NULL;
|
||||
int ms, remaining_time, res = 1, goaway = 0;
|
||||
struct timeval start;
|
||||
struct ast_frame *frame = NULL;
|
||||
|
||||
remaining_time = timeout;
|
||||
start = ast_tvnow();
|
||||
|
||||
s = get_signal(signame, 1);
|
||||
|
||||
ast_mutex_lock(&s->lock);
|
||||
s->watchers = s->watchers + 1; /* we unlock, because a) other people need to use this and */
|
||||
ast_mutex_unlock(&s->lock); /* b) the signal will be available to us as long as watchers > 0 */
|
||||
|
||||
while (timeout == 0 || remaining_time > 0) {
|
||||
int ofd, exception;
|
||||
|
||||
ms = 1000;
|
||||
errno = 0;
|
||||
if (ast_waitfor_nandfds(&chan, 1, &s->sig_alert_pipe[0], 1, &exception, &ofd, &ms)) { /* channel won */
|
||||
if (!(frame = ast_read(chan))) { /* channel hung up */
|
||||
ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
|
||||
res = -1;
|
||||
break;
|
||||
} else {
|
||||
ast_frfree(frame); /* handle frames */
|
||||
}
|
||||
} else if (ofd == s->sig_alert_pipe[0]) { /* fd won */
|
||||
if (ast_alertpipe_read(s->sig_alert_pipe) == AST_ALERT_READ_SUCCESS) {
|
||||
ast_debug(1, "Alert pipe has data for us\n");
|
||||
res = 0;
|
||||
break;
|
||||
} else {
|
||||
ast_debug(1, "Alert pipe does not have data for us\n");
|
||||
}
|
||||
} else { /* nobody won */
|
||||
if (ms && (ofd < 0)) {
|
||||
if (!((errno == 0) || (errno == EINTR))) {
|
||||
ast_log(LOG_WARNING, "Something bad happened while channel '%s' was polling.\n", ast_channel_name(chan));
|
||||
break;
|
||||
}
|
||||
} /* else, nothing happened */
|
||||
}
|
||||
if (timeout) {
|
||||
remaining_time = ast_remaining_ms(start, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/* WRLOCK the list so that if we're going to destroy the signal now, nobody else can grab it before that happens. */
|
||||
AST_RWLIST_WRLOCK(&signals);
|
||||
ast_mutex_lock(&s->lock);
|
||||
if (s->payload) {
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALPAYLOAD", s->payload);
|
||||
}
|
||||
s->watchers = s->watchers - 1;
|
||||
if (s->watchers) { /* folks are still waiting for this, pass it on... */
|
||||
int save_errno = errno;
|
||||
if (ast_alertpipe_write(s->sig_alert_pipe)) {
|
||||
ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));
|
||||
}
|
||||
errno = save_errno;
|
||||
} else { /* nobody else is waiting for this */
|
||||
goaway = 1; /* we were the last guy using this, so mark signal item for destruction */
|
||||
}
|
||||
ast_mutex_unlock(&s->lock);
|
||||
|
||||
if (goaway) {
|
||||
/* remove_signal calls ast_mutex_destroy, so don't call it with the mutex itself locked. */
|
||||
remove_signal(signame);
|
||||
}
|
||||
AST_RWLIST_UNLOCK(&signals);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int send_signal(char *signame, char *payload)
|
||||
{
|
||||
struct signalitem *s;
|
||||
int save_errno = errno;
|
||||
int res = 0;
|
||||
|
||||
s = get_signal(signame, 0); /* if signal doesn't exist already, no point in creating it, because nobody could be waiting for it! */
|
||||
|
||||
if (!s) {
|
||||
return -1; /* this signal didn't exist, so we can't send a signal for it */
|
||||
}
|
||||
|
||||
/* at this point, we know someone is listening, since signals are destroyed when watchers gets down to 0 */
|
||||
ast_mutex_lock(&s->lock);
|
||||
s->signaled = 1;
|
||||
if (payload && *payload) {
|
||||
int len = strlen(payload);
|
||||
if (s->payload) {
|
||||
ast_free(s->payload); /* if there was already a payload, replace it */
|
||||
s->payload = NULL;
|
||||
}
|
||||
s->payload = ast_malloc(len + 1);
|
||||
if (!s->payload) {
|
||||
ast_log(LOG_WARNING, "Failed to allocate signal payload '%s'\n", payload);
|
||||
} else {
|
||||
ast_copy_string(s->payload, payload, len + 1);
|
||||
}
|
||||
}
|
||||
if (ast_alertpipe_write(s->sig_alert_pipe)) {
|
||||
ast_log(LOG_WARNING, "%s: write() failed: %s\n", __FUNCTION__, strerror(errno));
|
||||
s->signaled = 0; /* okay, so we didn't send a signal after all... */
|
||||
res = -1;
|
||||
}
|
||||
errno = save_errno;
|
||||
ast_debug(1, "Sent '%s' signal to %d listeners\n", signame, s->watchers);
|
||||
ast_mutex_unlock(&s->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int waitsignal_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *argcopy;
|
||||
int r = 0, timeoutms = 0;
|
||||
double timeout = 0;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(signame);
|
||||
AST_APP_ARG(sigtimeout);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Signal() requires arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, argcopy);
|
||||
|
||||
if (ast_strlen_zero(args.signame)) {
|
||||
ast_log(LOG_WARNING, "Missing signal name\n");
|
||||
return -1;
|
||||
}
|
||||
if (strlen(args.signame) >= AST_MAX_CONTEXT) {
|
||||
ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);
|
||||
return -1;
|
||||
}
|
||||
if (!ast_strlen_zero(args.sigtimeout)) {
|
||||
if (sscanf(args.sigtimeout, "%30lg", &timeout) != 1 || timeout < 0) {
|
||||
ast_log(LOG_WARNING, "Invalid timeout provided: %s. Defaulting to no timeout.\n", args.sigtimeout);
|
||||
} else {
|
||||
timeoutms = timeout * 1000; /* sec to msec */
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
ast_debug(1, "Waiting for signal '%s' for %d ms\n", args.signame, timeoutms);
|
||||
} else {
|
||||
ast_debug(1, "Waiting for signal '%s', indefinitely\n", args.signame);
|
||||
}
|
||||
|
||||
r = wait_for_signal_or_hangup(chan, args.signame, timeoutms);
|
||||
|
||||
if (r == 1) {
|
||||
ast_verb(3, "Channel '%s' timed out, waiting for signal '%s'\n", ast_channel_name(chan), args.signame);
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "TIMEOUT");
|
||||
} else if (!r) {
|
||||
ast_verb(3, "Received signal '%s' on channel '%s'\n", args.signame, ast_channel_name(chan));
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "SIGNALED");
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "WAITFORSIGNALSTATUS", "HANGUP");
|
||||
ast_verb(3, "Channel '%s' hung up\n", ast_channel_name(chan));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int signal_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *argcopy;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(signame);
|
||||
AST_APP_ARG(payload);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "Signal() requires arguments\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argcopy = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, argcopy);
|
||||
|
||||
if (ast_strlen_zero(args.signame)) {
|
||||
ast_log(LOG_WARNING, "Missing signal name\n");
|
||||
return -1;
|
||||
}
|
||||
if (strlen(args.signame) >= AST_MAX_CONTEXT) {
|
||||
ast_log(LOG_WARNING, "Signal name '%s' is too long\n", args.signame);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (send_signal(args.signame, args.payload)) {
|
||||
pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "FAILURE");
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "SIGNALSTATUS", "SUCCESS");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
struct signalitem *s;
|
||||
int res = 0;
|
||||
|
||||
/* To avoid a locking nightmare, and for logistical reasons, this module
|
||||
* will refuse to unload if watchers > 0. That way we know a signal's
|
||||
* pipe won't disappear while it's being used. */
|
||||
|
||||
AST_RWLIST_WRLOCK(&signals);
|
||||
/* Don't just use AST_RWLIST_REMOVE_HEAD, because if dealloc_signal fails, it should stay in the list. */
|
||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&signals, s, entry) {
|
||||
int mres = dealloc_signal(s);
|
||||
res |= mres;
|
||||
if (!mres) {
|
||||
AST_LIST_REMOVE_CURRENT(entry);
|
||||
}
|
||||
}
|
||||
AST_LIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&signals);
|
||||
|
||||
/* One or more signals still has watchers. */
|
||||
if (res) {
|
||||
ast_log(LOG_WARNING, "One or more signals is currently in use. Unload failed.\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
res |= ast_unregister_application(app);
|
||||
res |= ast_unregister_application(app2);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = ast_register_application_xml(app, signal_exec);
|
||||
res |= ast_register_application_xml(app2, waitsignal_exec);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Channel Signaling Applications");
|
|
@ -16,7 +16,7 @@
|
|||
* at the top of the source tree.
|
||||
*
|
||||
* Please follow coding guidelines
|
||||
* https://docs.asterisk.org/Development/Policies-and-Procedures/Coding-Guidelines/
|
||||
* https://wiki.asterisk.org/wiki/display/AST/Coding+Guidelines
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
|
@ -371,8 +371,7 @@ static void play_files_helper(struct ast_channel *chan, const char *prompts)
|
|||
char *prompt, *rest = ast_strdupa(prompts);
|
||||
|
||||
ast_stopstream(chan);
|
||||
while ((prompt = ast_strsep(&rest, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))
|
||||
&& !ast_stream_and_wait(chan, prompt, "")) {
|
||||
while ((prompt = strsep(&rest, "&")) && !ast_stream_and_wait(chan, prompt, "")) {
|
||||
ast_stopstream(chan);
|
||||
}
|
||||
}
|
||||
|
|
2875
apps/app_sla.c
2875
apps/app_sla.c
File diff suppressed because it is too large
Load Diff
|
@ -85,17 +85,7 @@
|
|||
Play a sound file and wait for speech to be recognized.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="sound_file" required="true" argsep="&">
|
||||
<para>Ampersand separated list of filenames. If the filename
|
||||
is a relative filename (it does not begin with a slash), it
|
||||
will be searched for in the Asterisk sounds directory. If the
|
||||
filename is able to be parsed as a URL, Asterisk will
|
||||
download the file and then begin playback on it. To include a
|
||||
literal <literal>&</literal> in the URL you can enclose
|
||||
the URL in single quotes.</para>
|
||||
<argument name="sound_file" required="true" />
|
||||
<argument name="sound_file2" multiple="true" />
|
||||
</parameter>
|
||||
<parameter name="sound_file" required="true" />
|
||||
<parameter name="timeout">
|
||||
<para>Timeout integer in seconds. Note the timeout will only start
|
||||
once the sound file has stopped playing.</para>
|
||||
|
@ -105,9 +95,6 @@
|
|||
<option name="n">
|
||||
<para>Don't answer the channel if it has not already been answered.</para>
|
||||
</option>
|
||||
<option name="p">
|
||||
<para>Return partial results when backend is terminated by timeout.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
|
@ -703,12 +690,10 @@ static int speech_streamfile(struct ast_channel *chan, const char *filename, con
|
|||
|
||||
enum {
|
||||
SB_OPT_NOANSWER = (1 << 0),
|
||||
SB_OPT_PARTIALRESULTS = (1 << 1),
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(speech_background_options, BEGIN_OPTIONS
|
||||
AST_APP_OPTION('n', SB_OPT_NOANSWER),
|
||||
AST_APP_OPTION('p', SB_OPT_PARTIALRESULTS),
|
||||
END_OPTIONS );
|
||||
|
||||
/*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
|
||||
|
@ -791,10 +776,7 @@ static int speech_background(struct ast_channel *chan, const char *data)
|
|||
/* Okay it's streaming so go into a loop grabbing frames! */
|
||||
while (done == 0) {
|
||||
/* If the filename is null and stream is not running, start up a new sound file */
|
||||
if (!quieted
|
||||
&& ast_channel_streamid(chan) == -1
|
||||
&& ast_channel_timingfunc(chan) == NULL
|
||||
&& (filename = ast_strsep(&filename_tmp, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||
if (!quieted && (ast_channel_streamid(chan) == -1 && ast_channel_timingfunc(chan) == NULL) && (filename = strsep(&filename_tmp, "&"))) {
|
||||
/* Discard old stream information */
|
||||
ast_stopstream(chan);
|
||||
/* Start new stream */
|
||||
|
@ -938,10 +920,7 @@ static int speech_background(struct ast_channel *chan, const char *data)
|
|||
}
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(dtmf) && speech->state == AST_SPEECH_STATE_READY && ast_test_flag(&options, SB_OPT_PARTIALRESULTS)) {
|
||||
/* Copy to speech structure the results, even partial ones, if desired and available */
|
||||
speech->results = ast_speech_results_get(speech);
|
||||
} else if (!ast_strlen_zero(dtmf)) {
|
||||
if (!ast_strlen_zero(dtmf)) {
|
||||
/* We sort of make a results entry */
|
||||
speech->results = ast_calloc(1, sizeof(*speech->results));
|
||||
if (speech->results != NULL) {
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
</description>
|
||||
<see-also>
|
||||
<ref type="application">GosubIf</ref>
|
||||
<ref type="application">Macro</ref>
|
||||
<ref type="application">Goto</ref>
|
||||
<ref type="application">Return</ref>
|
||||
<ref type="application">StackPop</ref>
|
||||
|
@ -92,6 +93,7 @@
|
|||
<see-also>
|
||||
<ref type="application">Gosub</ref>
|
||||
<ref type="application">Return</ref>
|
||||
<ref type="application">MacroIf</ref>
|
||||
<ref type="function">IF</ref>
|
||||
<ref type="application">GotoIf</ref>
|
||||
<ref type="application">Goto</ref>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2220,30 +2220,6 @@ static int user_template_handler(const struct aco_option *opt, struct ast_variab
|
|||
return conf_find_user_profile(NULL, var->value, u_profile) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int sample_rate_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct bridge_profile *b_profile = obj;
|
||||
unsigned int *slot;
|
||||
|
||||
if (!strcasecmp(var->name, "internal_sample_rate")) {
|
||||
slot = &b_profile->internal_sample_rate;
|
||||
if (!strcasecmp(var->value, "auto")) {
|
||||
*slot = 0;
|
||||
return 0;
|
||||
}
|
||||
} else if (!strcasecmp(var->name, "maximum_sample_rate")) {
|
||||
slot = &b_profile->maximum_sample_rate;
|
||||
if (!strcasecmp(var->value, "none")) {
|
||||
*slot = 0;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ast_parse_arg(var->value, PARSE_UINT32 | PARSE_IN_RANGE, slot, 8000, 192000);
|
||||
}
|
||||
|
||||
static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct bridge_profile *b_profile = obj;
|
||||
|
@ -2461,9 +2437,10 @@ int conf_load_config(void)
|
|||
/* Bridge options */
|
||||
aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
|
||||
aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
|
||||
aco_option_register_custom(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "auto", sample_rate_handler, 0);
|
||||
/* "auto" will fail to parse as a uint, but we use PARSE_DEFAULT to set the value to 0 in that case, which is the value that auto resolves to */
|
||||
aco_option_register(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct bridge_profile, internal_sample_rate), 0);
|
||||
aco_option_register(&cfg_info, "binaural_active", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_BINAURAL_ACTIVE);
|
||||
aco_option_register_custom(&cfg_info, "maximum_sample_rate", ACO_EXACT, bridge_types, "none", sample_rate_handler, 0);
|
||||
aco_option_register(&cfg_info, "maximum_sample_rate", ACO_EXACT, bridge_types, "0", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct bridge_profile, maximum_sample_rate), 0);
|
||||
aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 0);
|
||||
aco_option_register(&cfg_info, "record_conference", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_RECORD_CONFERENCE);
|
||||
aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0);
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
*
|
||||
* \author\verbatim Terry Wilson <twilson@digium.com> \endverbatim
|
||||
*
|
||||
* See https://docs.asterisk.org/Development/Reference-Information/Other-Reference-Information/Confbridge-state-changes/ for
|
||||
* See https://wiki.asterisk.org/wiki/display/AST/Confbridge+state+changes for
|
||||
* a more complete description of how conference states work.
|
||||
*/
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
dnl
|
||||
dnl @synopsis AST_CHECK_OSPTK([REQ_VER_MAJOR],[REQ_VER_MINOR],[REQ_VER_BUGFIX])
|
||||
dnl
|
||||
dnl @summary check for existence of OSP Toolkit package
|
||||
dnl
|
||||
dnl This macro check for existence of OSP Toolkit package by checking osp/osp.h
|
||||
dnl header file, OSPPInit function and OSP Toolkit version.
|
||||
dnl
|
||||
AC_DEFUN([AST_CHECK_OSPTK],
|
||||
[
|
||||
# if OSPTK has not been checked and is not excluded
|
||||
if test "x${PBX_OSPTK}" != "x1" -a "${USE_OSPTK}" != "no"; then
|
||||
# if --with-osptk=DIR has been specified, use it.
|
||||
if test "x${OSPTK_DIR}" != "x"; then
|
||||
osptk_cflags="-I${OSPTK_DIR}/include"
|
||||
osptk_ldflags="-L${OSPTK_DIR}/lib"
|
||||
else
|
||||
osptk_cflags=""
|
||||
osptk_ldflags=""
|
||||
fi
|
||||
|
||||
# check for the header
|
||||
osptk_saved_cppflags="${CPPFLAGS}"
|
||||
CPPFLAGS="${CPPFLAGS} ${osptk_cflags}"
|
||||
AC_CHECK_HEADER([osp/osp.h], [osptk_header_found=yes], [osptk_header_found=no])
|
||||
CPPFLAGS="${osptk_saved_cppflags}"
|
||||
|
||||
# check for the library
|
||||
if test "${osptk_header_found}" = "yes"; then
|
||||
osptk_extralibs="-lssl -lcrypto"
|
||||
|
||||
AC_CHECK_LIB([osptk], [OSPPInit], [osptk_library_found=yes], [osptk_library_found=no], ${osptk_ldflags} ${osptk_extralibs})
|
||||
|
||||
# check OSP Toolkit version
|
||||
if test "${osptk_library_found}" = "yes"; then
|
||||
AC_MSG_CHECKING(if OSP Toolkit version is compatible with app_osplookup)
|
||||
|
||||
osptk_saved_cppflags="${CPPFLAGS}"
|
||||
CPPFLAGS="${CPPFLAGS} ${osptk_cflags}"
|
||||
AC_RUN_IFELSE(
|
||||
[AC_LANG_SOURCE([[
|
||||
#include <osp/osp.h>
|
||||
int main(void) {
|
||||
int ver = OSP_CLIENT_TOOLKIT_VERSION_MAJOR * 10000 + OSP_CLIENT_TOOLKIT_VERSION_MINOR * 100 + OSP_CLIENT_TOOLKIT_VERSION_BUGFIX;
|
||||
int req = $1 * 10000 + $2 * 100 + $3;
|
||||
return (ver < req) ? 1 : 0;
|
||||
}
|
||||
]])],
|
||||
[osptk_compatible=yes],
|
||||
[osptk_compatible=no]
|
||||
)
|
||||
CPPFLAGS="${osptk_saved_cppflags}"
|
||||
|
||||
if test "${osptk_compatible}" = "yes"; then
|
||||
AC_MSG_RESULT(yes)
|
||||
PBX_OSPTK=1
|
||||
OSPTK_INCLUDE="${osptk_cflags}"
|
||||
OSPTK_LIB="${osptk_ldflags} -losptk ${osptk_extralibs}"
|
||||
AC_DEFINE_UNQUOTED([HAVE_OSPTK], 1, [Define this to indicate the ${OSPTK_DESCRIP} library])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
])
|
|
@ -1,5 +1,5 @@
|
|||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_pthread.html
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
|
@ -14,24 +14,20 @@
|
|||
# flags that are needed. (The user can also force certain compiler
|
||||
# flags/libs to be tested by setting these environment variables.)
|
||||
#
|
||||
# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
|
||||
# needed for multi-threaded programs (defaults to the value of CC
|
||||
# respectively CXX otherwise). (This is necessary on e.g. AIX to use the
|
||||
# special cc_r/CC_r compiler alias.)
|
||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
||||
#
|
||||
# NOTE: You are assumed to not only compile your program with these flags,
|
||||
# but also to link with them as well. For example, you might link with
|
||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
#
|
||||
# If you are only building threaded programs, you may wish to use these
|
||||
# variables in your default LIBS, CFLAGS, and CC:
|
||||
#
|
||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
|
||||
# CC="$PTHREAD_CC"
|
||||
# CXX="$PTHREAD_CXX"
|
||||
#
|
||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||
# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
|
||||
|
@ -59,7 +55,6 @@
|
|||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
|
||||
# Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
|
@ -72,7 +67,7 @@
|
|||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
|
@ -87,7 +82,7 @@
|
|||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 31
|
||||
#serial 23
|
||||
|
||||
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
||||
AC_DEFUN([AX_PTHREAD], [
|
||||
|
@ -109,7 +104,6 @@ if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
|
|||
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||
ax_pthread_save_LIBS="$LIBS"
|
||||
AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
|
||||
AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
|
||||
|
@ -129,12 +123,10 @@ fi
|
|||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||
# libraries is broken (non-POSIX).
|
||||
|
||||
# Create a list of thread flags to try. Items with a "," contain both
|
||||
# C compiler flags (before ",") and linker flags (after ","). Other items
|
||||
# starting with a "-" are C compiler flags, and remaining items are
|
||||
# library names, except for "none" which indicates that we try without
|
||||
# any flags at all, and "pthread-config" which is a program returning
|
||||
# the flags for the Pth emulation library.
|
||||
# Create a list of thread flags to try. Items starting with a "-" are
|
||||
# C compiler flags, and other items are library names, except for "none"
|
||||
# which indicates that we try without any flags at all, and "pthread-config"
|
||||
# which is a program returning the flags for the Pth emulation library.
|
||||
|
||||
ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||
|
||||
|
@ -202,47 +194,14 @@ case $host_os in
|
|||
# that too in a future libc.) So we'll check first for the
|
||||
# standard Solaris way of linking pthreads (-mt -lpthread).
|
||||
|
||||
ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags"
|
||||
ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Are we compiling with Clang?
|
||||
|
||||
AC_CACHE_CHECK([whether $CC is Clang],
|
||||
[ax_cv_PTHREAD_CLANG],
|
||||
[ax_cv_PTHREAD_CLANG=no
|
||||
# Note that Autoconf sets GCC=yes for Clang as well as GCC
|
||||
if test "x$GCC" = "xyes"; then
|
||||
AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
|
||||
[/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
|
||||
# if defined(__clang__) && defined(__llvm__)
|
||||
AX_PTHREAD_CC_IS_CLANG
|
||||
# endif
|
||||
],
|
||||
[ax_cv_PTHREAD_CLANG=yes])
|
||||
fi
|
||||
])
|
||||
ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
|
||||
|
||||
|
||||
# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
|
||||
|
||||
# Note that for GCC and Clang -pthread generally implies -lpthread,
|
||||
# except when -nostdlib is passed.
|
||||
# This is problematic using libtool to build C++ shared libraries with pthread:
|
||||
# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460
|
||||
# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333
|
||||
# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555
|
||||
# To solve this, first try -pthread together with -lpthread for GCC
|
||||
|
||||
AS_IF([test "x$GCC" = "xyes"],
|
||||
[ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"])
|
||||
|
||||
# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first
|
||||
|
||||
AS_IF([test "x$ax_pthread_clang" = "xyes"],
|
||||
[ax_pthread_flags="-pthread,-lpthread -pthread"])
|
||||
|
||||
[ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
|
||||
|
||||
# The presence of a feature test macro requesting re-entrant function
|
||||
# definitions is, on some systems, a strong hint that pthreads support is
|
||||
|
@ -265,6 +224,101 @@ AS_IF([test "x$ax_pthread_check_macro" = "x--"],
|
|||
[ax_pthread_check_cond=0],
|
||||
[ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
|
||||
|
||||
# Are we compiling with Clang?
|
||||
|
||||
AC_CACHE_CHECK([whether $CC is Clang],
|
||||
[ax_cv_PTHREAD_CLANG],
|
||||
[ax_cv_PTHREAD_CLANG=no
|
||||
# Note that Autoconf sets GCC=yes for Clang as well as GCC
|
||||
if test "x$GCC" = "xyes"; then
|
||||
AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
|
||||
[/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
|
||||
# if defined(__clang__) && defined(__llvm__)
|
||||
AX_PTHREAD_CC_IS_CLANG
|
||||
# endif
|
||||
],
|
||||
[ax_cv_PTHREAD_CLANG=yes])
|
||||
fi
|
||||
])
|
||||
ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
|
||||
|
||||
ax_pthread_clang_warning=no
|
||||
|
||||
# Clang needs special handling, because older versions handle the -pthread
|
||||
# option in a rather... idiosyncratic way
|
||||
|
||||
if test "x$ax_pthread_clang" = "xyes"; then
|
||||
|
||||
# Clang takes -pthread; it has never supported any other flag
|
||||
|
||||
# (Note 1: This will need to be revisited if a system that Clang
|
||||
# supports has POSIX threads in a separate library. This tends not
|
||||
# to be the way of modern systems, but it's conceivable.)
|
||||
|
||||
# (Note 2: On some systems, notably Darwin, -pthread is not needed
|
||||
# to get POSIX threads support; the API is always present and
|
||||
# active. We could reasonably leave PTHREAD_CFLAGS empty. But
|
||||
# -pthread does define _REENTRANT, and while the Darwin headers
|
||||
# ignore this macro, third-party headers might not.)
|
||||
|
||||
PTHREAD_CFLAGS="-pthread"
|
||||
PTHREAD_LIBS=
|
||||
|
||||
ax_pthread_ok=yes
|
||||
|
||||
# However, older versions of Clang make a point of warning the user
|
||||
# that, in an invocation where only linking and no compilation is
|
||||
# taking place, the -pthread option has no effect ("argument unused
|
||||
# during compilation"). They expect -pthread to be passed in only
|
||||
# when source code is being compiled.
|
||||
#
|
||||
# Problem is, this is at odds with the way Automake and most other
|
||||
# C build frameworks function, which is that the same flags used in
|
||||
# compilation (CFLAGS) are also used in linking. Many systems
|
||||
# supported by AX_PTHREAD require exactly this for POSIX threads
|
||||
# support, and in fact it is often not straightforward to specify a
|
||||
# flag that is used only in the compilation phase and not in
|
||||
# linking. Such a scenario is extremely rare in practice.
|
||||
#
|
||||
# Even though use of the -pthread flag in linking would only print
|
||||
# a warning, this can be a nuisance for well-run software projects
|
||||
# that build with -Werror. So if the active version of Clang has
|
||||
# this misfeature, we search for an option to squash it.
|
||||
|
||||
AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
|
||||
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
|
||||
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
|
||||
# Create an alternate version of $ac_link that compiles and
|
||||
# links in two steps (.c -> .o, .o -> exe) instead of one
|
||||
# (.c -> exe), because the warning occurs only in the second
|
||||
# step
|
||||
ax_pthread_save_ac_link="$ac_link"
|
||||
ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
|
||||
ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
|
||||
ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
|
||||
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||
for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
|
||||
AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
|
||||
CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
|
||||
ac_link="$ax_pthread_save_ac_link"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||
[ac_link="$ax_pthread_2step_ac_link"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||
[break])
|
||||
])
|
||||
done
|
||||
ac_link="$ax_pthread_save_ac_link"
|
||||
CFLAGS="$ax_pthread_save_CFLAGS"
|
||||
AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
|
||||
ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
|
||||
])
|
||||
|
||||
case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
|
||||
no | unknown) ;;
|
||||
*) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
|
||||
esac
|
||||
|
||||
fi # $ax_pthread_clang = yes
|
||||
|
||||
if test "x$ax_pthread_ok" = "xno"; then
|
||||
for ax_pthread_try_flag in $ax_pthread_flags; do
|
||||
|
@ -274,10 +328,10 @@ for ax_pthread_try_flag in $ax_pthread_flags; do
|
|||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||
;;
|
||||
|
||||
*,*)
|
||||
PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"`
|
||||
PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"`
|
||||
AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"])
|
||||
-mt,pthread)
|
||||
AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
|
||||
PTHREAD_CFLAGS="-mt"
|
||||
PTHREAD_LIBS="-lpthread"
|
||||
;;
|
||||
|
||||
-*)
|
||||
|
@ -317,13 +371,7 @@ for ax_pthread_try_flag in $ax_pthread_flags; do
|
|||
# if $ax_pthread_check_cond
|
||||
# error "$ax_pthread_check_macro must be defined"
|
||||
# endif
|
||||
static void *some_global = NULL;
|
||||
static void routine(void *a)
|
||||
{
|
||||
/* To avoid any unused-parameter or
|
||||
unused-but-set-parameter warning. */
|
||||
some_global = a;
|
||||
}
|
||||
static void routine(void *a) { a = 0; }
|
||||
static void *start_routine(void *a) { return a; }],
|
||||
[pthread_t th; pthread_attr_t attr;
|
||||
pthread_create(&th, 0, start_routine, 0);
|
||||
|
@ -345,80 +393,6 @@ for ax_pthread_try_flag in $ax_pthread_flags; do
|
|||
done
|
||||
fi
|
||||
|
||||
|
||||
# Clang needs special handling, because older versions handle the -pthread
|
||||
# option in a rather... idiosyncratic way
|
||||
|
||||
if test "x$ax_pthread_clang" = "xyes"; then
|
||||
|
||||
# Clang takes -pthread; it has never supported any other flag
|
||||
|
||||
# (Note 1: This will need to be revisited if a system that Clang
|
||||
# supports has POSIX threads in a separate library. This tends not
|
||||
# to be the way of modern systems, but it's conceivable.)
|
||||
|
||||
# (Note 2: On some systems, notably Darwin, -pthread is not needed
|
||||
# to get POSIX threads support; the API is always present and
|
||||
# active. We could reasonably leave PTHREAD_CFLAGS empty. But
|
||||
# -pthread does define _REENTRANT, and while the Darwin headers
|
||||
# ignore this macro, third-party headers might not.)
|
||||
|
||||
# However, older versions of Clang make a point of warning the user
|
||||
# that, in an invocation where only linking and no compilation is
|
||||
# taking place, the -pthread option has no effect ("argument unused
|
||||
# during compilation"). They expect -pthread to be passed in only
|
||||
# when source code is being compiled.
|
||||
#
|
||||
# Problem is, this is at odds with the way Automake and most other
|
||||
# C build frameworks function, which is that the same flags used in
|
||||
# compilation (CFLAGS) are also used in linking. Many systems
|
||||
# supported by AX_PTHREAD require exactly this for POSIX threads
|
||||
# support, and in fact it is often not straightforward to specify a
|
||||
# flag that is used only in the compilation phase and not in
|
||||
# linking. Such a scenario is extremely rare in practice.
|
||||
#
|
||||
# Even though use of the -pthread flag in linking would only print
|
||||
# a warning, this can be a nuisance for well-run software projects
|
||||
# that build with -Werror. So if the active version of Clang has
|
||||
# this misfeature, we search for an option to squash it.
|
||||
|
||||
AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
|
||||
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
|
||||
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
|
||||
# Create an alternate version of $ac_link that compiles and
|
||||
# links in two steps (.c -> .o, .o -> exe) instead of one
|
||||
# (.c -> exe), because the warning occurs only in the second
|
||||
# step
|
||||
ax_pthread_save_ac_link="$ac_link"
|
||||
ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
|
||||
ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"`
|
||||
ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
|
||||
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||
for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
|
||||
AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
|
||||
CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
|
||||
ac_link="$ax_pthread_save_ac_link"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||
[ac_link="$ax_pthread_2step_ac_link"
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||
[break])
|
||||
])
|
||||
done
|
||||
ac_link="$ax_pthread_save_ac_link"
|
||||
CFLAGS="$ax_pthread_save_CFLAGS"
|
||||
AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
|
||||
ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
|
||||
])
|
||||
|
||||
case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
|
||||
no | unknown) ;;
|
||||
*) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
|
||||
esac
|
||||
|
||||
fi # $ax_pthread_clang = yes
|
||||
|
||||
|
||||
|
||||
# Various other checks:
|
||||
if test "x$ax_pthread_ok" = "xyes"; then
|
||||
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||
|
@ -464,8 +438,7 @@ if test "x$ax_pthread_ok" = "xyes"; then
|
|||
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT],
|
||||
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
|
||||
[[int i = PTHREAD_PRIO_INHERIT;
|
||||
return i;]])],
|
||||
[[int i = PTHREAD_PRIO_INHERIT;]])],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
|
||||
[ax_cv_PTHREAD_PRIO_INHERIT=no])
|
||||
])
|
||||
|
@ -487,28 +460,18 @@ if test "x$ax_pthread_ok" = "xyes"; then
|
|||
[#handle absolute path differently from PATH based program lookup
|
||||
AS_CASE(["x$CC"],
|
||||
[x/*],
|
||||
[
|
||||
AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])
|
||||
AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])])
|
||||
],
|
||||
[
|
||||
AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])
|
||||
AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])])
|
||||
]
|
||||
)
|
||||
])
|
||||
[AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
|
||||
[AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
|
||||
test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
|
||||
|
||||
AC_SUBST([PTHREAD_LIBS])
|
||||
AC_SUBST([PTHREAD_CFLAGS])
|
||||
AC_SUBST([PTHREAD_CC])
|
||||
AC_SUBST([PTHREAD_CXX])
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test "x$ax_pthread_ok" = "xyes"; then
|
||||
|
|
|
@ -9,9 +9,9 @@ check_for_app() {
|
|||
fi
|
||||
}
|
||||
|
||||
# OpenBSD: pkg_add autoconf%2.69 automake%1.16 metaauto
|
||||
test -n "$AUTOCONF_VERSION" || export AUTOCONF_VERSION=2.69
|
||||
test -n "$AUTOMAKE_VERSION" || export AUTOMAKE_VERSION=1.16
|
||||
# OpenBSD: pkg_add autoconf%2.63 automake%1.9 metaauto
|
||||
test -n "$AUTOCONF_VERSION" || export AUTOCONF_VERSION=2.63
|
||||
test -n "$AUTOMAKE_VERSION" || export AUTOMAKE_VERSION=1.9
|
||||
|
||||
check_for_app autoconf
|
||||
check_for_app autoheader
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<use type="module">res_monitor</use>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
|
@ -48,10 +49,10 @@
|
|||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/parking.h"
|
||||
#include "asterisk/features_config.h"
|
||||
#include "asterisk/monitor.h"
|
||||
#include "asterisk/mixmonitor.h"
|
||||
#include "asterisk/audiohook.h"
|
||||
#include "asterisk/causes.h"
|
||||
#include "asterisk/beep.h"
|
||||
|
||||
enum set_touch_variables_res {
|
||||
SET_TOUCH_SUCCESS,
|
||||
|
@ -77,29 +78,224 @@ static void set_touch_variable(enum set_touch_variables_res *res, struct ast_cha
|
|||
}
|
||||
}
|
||||
|
||||
static enum set_touch_variables_res set_touch_variables(struct ast_channel *chan, char **touch_format, char **touch_monitor, char **touch_monitor_prefix, char **touch_monitor_beep)
|
||||
static enum set_touch_variables_res set_touch_variables(struct ast_channel *chan, int is_mixmonitor, char **touch_format, char **touch_monitor, char **touch_monitor_prefix)
|
||||
{
|
||||
enum set_touch_variables_res res = SET_TOUCH_UNSET;
|
||||
const char *var_format;
|
||||
const char *var_monitor;
|
||||
const char *var_prefix;
|
||||
const char *var_beep;
|
||||
|
||||
SCOPED_CHANNELLOCK(lock, chan);
|
||||
|
||||
var_format = "TOUCH_MIXMONITOR_FORMAT";
|
||||
var_monitor = "TOUCH_MIXMONITOR";
|
||||
var_prefix = "TOUCH_MIXMONITOR_PREFIX";
|
||||
var_beep = "TOUCH_MIXMONITOR_BEEP";
|
||||
|
||||
if (is_mixmonitor) {
|
||||
var_format = "TOUCH_MIXMONITOR_FORMAT";
|
||||
var_monitor = "TOUCH_MIXMONITOR";
|
||||
var_prefix = "TOUCH_MIXMONITOR_PREFIX";
|
||||
} else {
|
||||
var_format = "TOUCH_MONITOR_FORMAT";
|
||||
var_monitor = "TOUCH_MONITOR";
|
||||
var_prefix = "TOUCH_MONITOR_PREFIX";
|
||||
}
|
||||
set_touch_variable(&res, chan, var_format, touch_format);
|
||||
set_touch_variable(&res, chan, var_monitor, touch_monitor);
|
||||
set_touch_variable(&res, chan, var_prefix, touch_monitor_prefix);
|
||||
set_touch_variable(&res, chan, var_beep, touch_monitor_beep);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void stop_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *stop_message)
|
||||
{
|
||||
ast_verb(4, "AutoMonitor used to stop recording call.\n");
|
||||
|
||||
ast_channel_lock(peer_chan);
|
||||
if (ast_channel_monitor(peer_chan)) {
|
||||
if (ast_channel_monitor(peer_chan)->stop(peer_chan, 1)) {
|
||||
ast_verb(4, "Cannot stop AutoMonitor for %s\n", ast_channel_name(bridge_channel->chan));
|
||||
if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
|
||||
ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
|
||||
}
|
||||
ast_channel_unlock(peer_chan);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Something else removed the Monitor before we got to it. */
|
||||
ast_channel_unlock(peer_chan);
|
||||
return;
|
||||
}
|
||||
|
||||
ast_channel_unlock(peer_chan);
|
||||
|
||||
if (features_cfg && !(ast_strlen_zero(features_cfg->courtesytone))) {
|
||||
ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
|
||||
ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(stop_message)) {
|
||||
ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
|
||||
ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void start_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *start_message)
|
||||
{
|
||||
char *touch_filename;
|
||||
size_t len;
|
||||
int x;
|
||||
enum set_touch_variables_res set_touch_res;
|
||||
|
||||
RAII_VAR(char *, touch_format, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
|
||||
|
||||
set_touch_res = set_touch_variables(bridge_channel->chan, 0, &touch_format,
|
||||
&touch_monitor, &touch_monitor_prefix);
|
||||
switch (set_touch_res) {
|
||||
case SET_TOUCH_SUCCESS:
|
||||
break;
|
||||
case SET_TOUCH_UNSET:
|
||||
set_touch_res = set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor,
|
||||
&touch_monitor_prefix);
|
||||
if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case SET_TOUCH_ALLOC_FAILURE:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(touch_monitor)) {
|
||||
len = strlen(touch_monitor) + 50;
|
||||
touch_filename = ast_alloca(len);
|
||||
snprintf(touch_filename, len, "%s-%ld-%s",
|
||||
S_OR(touch_monitor_prefix, "auto"),
|
||||
(long) time(NULL),
|
||||
touch_monitor);
|
||||
} else {
|
||||
char *caller_chan_id;
|
||||
char *peer_chan_id;
|
||||
|
||||
caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
|
||||
ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
|
||||
peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
|
||||
ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
|
||||
len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
|
||||
touch_filename = ast_alloca(len);
|
||||
snprintf(touch_filename, len, "%s-%ld-%s-%s",
|
||||
S_OR(touch_monitor_prefix, "auto"),
|
||||
(long) time(NULL),
|
||||
caller_chan_id,
|
||||
peer_chan_id);
|
||||
}
|
||||
|
||||
for (x = 0; x < strlen(touch_filename); x++) {
|
||||
if (touch_filename[x] == '/') {
|
||||
touch_filename[x] = '-';
|
||||
}
|
||||
}
|
||||
|
||||
ast_verb(4, "AutoMonitor used to record call. Filename: %s\n", touch_filename);
|
||||
|
||||
if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT, NULL)) {
|
||||
ast_verb(4, "AutoMonitor feature was tried by '%s' but monitor failed to start.\n",
|
||||
ast_channel_name(bridge_channel->chan));
|
||||
return;
|
||||
}
|
||||
|
||||
ast_monitor_setjoinfiles(peer_chan, 1);
|
||||
|
||||
if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
|
||||
ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
|
||||
ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(start_message)) {
|
||||
ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
|
||||
ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
|
||||
}
|
||||
|
||||
pbx_builtin_setvar_helper(bridge_channel->chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
|
||||
pbx_builtin_setvar_helper(peer_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
|
||||
}
|
||||
|
||||
static int feature_automonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||
{
|
||||
const char *start_message;
|
||||
const char *stop_message;
|
||||
struct ast_bridge_features_automonitor *options = hook_pvt;
|
||||
enum ast_bridge_features_monitor start_stop = options ? options->start_stop : AUTO_MONITOR_TOGGLE;
|
||||
int is_monitoring;
|
||||
|
||||
RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
|
||||
RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);
|
||||
|
||||
ast_channel_lock(bridge_channel->chan);
|
||||
features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
|
||||
ast_channel_unlock(bridge_channel->chan);
|
||||
ast_bridge_channel_lock_bridge(bridge_channel);
|
||||
peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan);
|
||||
ast_bridge_unlock(bridge_channel->bridge);
|
||||
|
||||
if (!peer_chan) {
|
||||
ast_verb(4, "Cannot start AutoMonitor for %s - can not determine peer in bridge.\n",
|
||||
ast_channel_name(bridge_channel->chan));
|
||||
if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
|
||||
ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_channel_lock(bridge_channel->chan);
|
||||
start_message = pbx_builtin_getvar_helper(bridge_channel->chan,
|
||||
"TOUCH_MONITOR_MESSAGE_START");
|
||||
start_message = ast_strdupa(S_OR(start_message, ""));
|
||||
stop_message = pbx_builtin_getvar_helper(bridge_channel->chan,
|
||||
"TOUCH_MONITOR_MESSAGE_STOP");
|
||||
stop_message = ast_strdupa(S_OR(stop_message, ""));
|
||||
ast_channel_unlock(bridge_channel->chan);
|
||||
|
||||
is_monitoring = ast_channel_monitor(peer_chan) != NULL;
|
||||
switch (start_stop) {
|
||||
case AUTO_MONITOR_TOGGLE:
|
||||
if (is_monitoring) {
|
||||
stop_automonitor(bridge_channel, peer_chan, features_cfg, stop_message);
|
||||
} else {
|
||||
start_automonitor(bridge_channel, peer_chan, features_cfg, start_message);
|
||||
}
|
||||
return 0;
|
||||
case AUTO_MONITOR_START:
|
||||
if (!is_monitoring) {
|
||||
start_automonitor(bridge_channel, peer_chan, features_cfg, start_message);
|
||||
return 0;
|
||||
}
|
||||
ast_verb(4, "AutoMonitor already recording call.\n");
|
||||
break;
|
||||
case AUTO_MONITOR_STOP:
|
||||
if (is_monitoring) {
|
||||
stop_automonitor(bridge_channel, peer_chan, features_cfg, stop_message);
|
||||
return 0;
|
||||
}
|
||||
ast_verb(4, "AutoMonitor already stopped on call.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fake start/stop to invoker so will think it did something but
|
||||
* was already in that mode.
|
||||
*/
|
||||
if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
|
||||
ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
|
||||
}
|
||||
if (is_monitoring) {
|
||||
if (!ast_strlen_zero(start_message)) {
|
||||
ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
|
||||
}
|
||||
} else {
|
||||
if (!ast_strlen_zero(stop_message)) {
|
||||
ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stop_automixmonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *stop_message)
|
||||
{
|
||||
|
@ -126,7 +322,7 @@ static void stop_automixmonitor(struct ast_bridge_channel *bridge_channel, struc
|
|||
|
||||
static void start_automixmonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *start_message)
|
||||
{
|
||||
char *touch_filename, mix_options[32] = "b";
|
||||
char *touch_filename;
|
||||
size_t len;
|
||||
int x;
|
||||
enum set_touch_variables_res set_touch_res;
|
||||
|
@ -134,16 +330,15 @@ static void start_automixmonitor(struct ast_bridge_channel *bridge_channel, stru
|
|||
RAII_VAR(char *, touch_format, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
|
||||
RAII_VAR(char *, touch_monitor_beep, NULL, ast_free);
|
||||
|
||||
set_touch_res = set_touch_variables(bridge_channel->chan, &touch_format,
|
||||
&touch_monitor, &touch_monitor_prefix, &touch_monitor_beep);
|
||||
set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format,
|
||||
&touch_monitor, &touch_monitor_prefix);
|
||||
switch (set_touch_res) {
|
||||
case SET_TOUCH_SUCCESS:
|
||||
break;
|
||||
case SET_TOUCH_UNSET:
|
||||
set_touch_res = set_touch_variables(peer_chan, &touch_format, &touch_monitor,
|
||||
&touch_monitor_prefix, &touch_monitor_beep);
|
||||
set_touch_res = set_touch_variables(peer_chan, 1, &touch_format, &touch_monitor,
|
||||
&touch_monitor_prefix);
|
||||
if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
@ -186,22 +381,7 @@ static void start_automixmonitor(struct ast_bridge_channel *bridge_channel, stru
|
|||
|
||||
ast_verb(4, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);
|
||||
|
||||
if (!ast_strlen_zero(touch_monitor_beep)) {
|
||||
unsigned int interval = 15;
|
||||
if (sscanf(touch_monitor_beep, "%30u", &interval) != 1) {
|
||||
ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
|
||||
touch_monitor_beep, interval);
|
||||
}
|
||||
|
||||
if (interval < 5) {
|
||||
interval = 5;
|
||||
ast_log(LOG_WARNING, "Interval '%s' too small for periodic beep. Using minimum of %u\n",
|
||||
touch_monitor_beep, interval);
|
||||
}
|
||||
snprintf(mix_options, sizeof(mix_options), "bB(%d)", interval);
|
||||
}
|
||||
|
||||
if (ast_start_mixmonitor(peer_chan, touch_filename, mix_options)) {
|
||||
if (ast_start_mixmonitor(peer_chan, touch_filename, "b")) {
|
||||
ast_verb(4, "AutoMixMonitor feature was tried by '%s' but MixMonitor failed to start.\n",
|
||||
ast_channel_name(bridge_channel->chan));
|
||||
|
||||
|
@ -323,6 +503,7 @@ static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_
|
|||
static int unload_module(void)
|
||||
{
|
||||
ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_HANGUP);
|
||||
ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMON);
|
||||
ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMIXMON);
|
||||
|
||||
return 0;
|
||||
|
@ -331,6 +512,7 @@ static int unload_module(void)
|
|||
static int load_module(void)
|
||||
{
|
||||
ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL);
|
||||
ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMON, feature_automonitor, NULL);
|
||||
ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL);
|
||||
|
||||
/* This module cannot be unloaded until shutdown */
|
||||
|
@ -343,4 +525,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Built in bridging featur
|
|||
.support_level = AST_MODULE_SUPPORT_CORE,
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.optional_modules = "res_monitor",
|
||||
);
|
||||
|
|
|
@ -181,14 +181,7 @@ static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chann
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!ast_stream_topology_equal(new_top, existing_top)) {
|
||||
ast_channel_request_stream_topology_change(c1, new_top, &simple_bridge);
|
||||
} else {
|
||||
ast_debug(3, "%s: Topologies already match. Current: %s Requested: %s\n",
|
||||
ast_channel_name(c1),
|
||||
ast_str_tmp(256, ast_stream_topology_to_str(existing_top, &STR_TMP)),
|
||||
ast_str_tmp(256, ast_stream_topology_to_str(new_top, &STR_TMP)));
|
||||
}
|
||||
ast_channel_request_stream_topology_change(c1, new_top, &simple_bridge);
|
||||
ast_stream_topology_free(new_top);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<support_level>extended</support_level>
|
||||
</member>
|
||||
<member name="DETECT_DEADLOCKS" displayname="Detect Deadlocks">
|
||||
<depend>DEBUG_THREADS</depend>
|
||||
<support_level>extended</support_level>
|
||||
</member>
|
||||
<member name="DUMP_SCHEDULER" displayname="Dump Scheduler Contents for Debugging">
|
||||
|
|
|
@ -128,8 +128,4 @@
|
|||
<defaultenabled>yes</defaultenabled>
|
||||
<depend>native_arch</depend>
|
||||
</member>
|
||||
<member name="ADD_CFLAGS_TO_BUILDOPTS_H" displayname="Add ALL of the flags on this page to buildopts.h. Useful for IDEs but may cause slightly longer compile times after flags are changed.">
|
||||
<support_level>core</support_level>
|
||||
<defaultenabled>no</defaultenabled>
|
||||
</member>
|
||||
</category>
|
||||
|
|
|
@ -58,7 +58,7 @@ if [[ -z ${cache_dir} ]] ; then
|
|||
fi
|
||||
|
||||
version=$(${ASTTOPDIR}/build_tools/make_version ${ASTTOPDIR})
|
||||
if [[ ! ${version} =~ ^(GIT-)?(certified[/-])?([^.-]+)[.-].* ]] ; then
|
||||
if [[ ! ${version} =~ ^(GIT-)?(certified/)?([^.-]+)[.-].* ]] ; then
|
||||
echo "${module_name}: Couldn't parse version ${version}"
|
||||
exit 1
|
||||
fi
|
||||
|
@ -172,7 +172,7 @@ if [[ -f ${DESTDIR}${ASTMODDIR}/${module_name}.manifest.xml ]] ; then
|
|||
|
||||
cs=$(${MD5} ${f} | cut -b1-32)
|
||||
if [[ "${cs}" != "${sum}" ]] ; then
|
||||
echo "Checksum mismatch: ${f}"
|
||||
echo Checksum mismatch: ${f}
|
||||
need_install=1
|
||||
break
|
||||
fi
|
||||
|
@ -194,8 +194,8 @@ else
|
|||
fi
|
||||
|
||||
need_download=1
|
||||
if [[ -f ${cache_dir}/${full_name}-${major_version}.manifest.xml ]] ; then
|
||||
cpv=$(${XMLSTARLET} sel -t -v "/package/@version" ${cache_dir}/${full_name}-${major_version}.manifest.xml)
|
||||
if [[ -f ${cache_dir}/${full_name}.manifest.xml ]] ; then
|
||||
cpv=$(${XMLSTARLET} sel -t -v "/package/@version" ${cache_dir}/${full_name}.manifest.xml)
|
||||
cpvi=$(version_convert ${cpv})
|
||||
echo "${full_name}: Cached package version ${cpv} (${cpvi})"
|
||||
if [[ ${cpvi} == ${rpvi} && ( -f ${cache_dir}/${tarball} ) ]] ; then
|
||||
|
@ -210,7 +210,7 @@ if [[ ${need_download} = 1 ]] ; then
|
|||
echo "${full_name}: Unable to fetch ${remote_url}/${tarball}"
|
||||
exit 1
|
||||
}
|
||||
cp ${tmpdir}/${variant_manifest} ${cache_dir}/${full_name}-${major_version}.manifest.xml
|
||||
cp ${tmpdir}/${variant_manifest} ${cache_dir}/${full_name}.manifest.xml
|
||||
fi
|
||||
|
||||
tar -xzf ${cache_dir}/${tarball} -C ${cache_dir}
|
||||
|
|
|
@ -18,79 +18,42 @@ then
|
|||
# gets added to BUILDOPTS.
|
||||
fi
|
||||
|
||||
ADD_CFLAGS_TO_BUILDOPTS=false
|
||||
MENUSELECT_CFLAGS=$(${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts)
|
||||
echo "$MENUSELECT_CFLAGS" | grep -q -e "ADD_CFLAGS_TO_BUILDOPTS_H" && ADD_CFLAGS_TO_BUILDOPTS=true
|
||||
TMP=`${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
|
||||
for x in ${TMP}; do
|
||||
if test "${x}" = "AO2_DEBUG" \
|
||||
-o "${x}" = "BETTER_BACKTRACES" \
|
||||
-o "${x}" = "BUILD_NATIVE" \
|
||||
-o "${x}" = "COMPILE_DOUBLE" \
|
||||
-o "${x}" = "DEBUG_CHAOS" \
|
||||
-o "${x}" = "DEBUG_SCHEDULER" \
|
||||
-o "${x}" = "DETECT_DEADLOCKS" \
|
||||
-o "${x}" = "DONT_OPTIMIZE" \
|
||||
-o "${x}" = "DUMP_SCHEDULER" \
|
||||
-o "${x}" = "LOTS_OF_SPANS" \
|
||||
-o "${x}" = "MALLOC_DEBUG" \
|
||||
-o "${x}" = "RADIO_RELAX" \
|
||||
-o "${x}" = "REBUILD_PARSERS" \
|
||||
-o "${x}" = "REF_DEBUG" \
|
||||
-o "${x}" = "USE_HOARD_ALLOCATOR" ; then
|
||||
# These options are only for specific sources and have no effect on public ABI.
|
||||
# Keep them out of buildopts.h so ccache does not invalidate all sources.
|
||||
continue
|
||||
fi
|
||||
|
||||
# Clean up MENUSELECT_CFLAGS by removing the "MENUSELECT_CFLAGS="
|
||||
# at the front, the "ADD_CFLAGS_TO_BUILDOPTS_H" flag, and any "-D"
|
||||
# entries.
|
||||
MENUSELECT_CFLAGS=$( echo "$MENUSELECT_CFLAGS" | \
|
||||
sed -r -e "s/(MENUSELECT_CFLAGS=|ADD_CFLAGS_TO_BUILDOPTS_H|-D)//g")
|
||||
|
||||
# This is a list of flags that don't affect the ABI.
|
||||
# "ADD_CFLAGS_TO_BUILDOPTS_H" is NOT set, we'll filter these
|
||||
# out of the buildopts.h file.
|
||||
#
|
||||
# These used to always be filtered out but if they're not in
|
||||
# buildopts.h, many IDEs will show them as undefined and mark
|
||||
# any code blocks enabled by them as disabled.
|
||||
#
|
||||
# The original reasoning for removing them was that trivial
|
||||
# changes to the buildopts.h file will cause ccache to
|
||||
# invalidate any source files that use it and increase the
|
||||
# compile time. It's not such a huge deal these days but
|
||||
# to preserve backwards behavior the default is still to
|
||||
# remove them.
|
||||
#
|
||||
# The ABI-breaking flags are always included in buildopts.h.
|
||||
|
||||
# This variable is used by sed so it needs to be a valid
|
||||
# regex which will be surrounded by parens.]
|
||||
FILTER_OUT="\
|
||||
AO2_DEBUG|BETTER_BACKTRACES|BUILD_NATIVE|\
|
||||
COMPILE_DOUBLE|DEBUG_CHAOS|DEBUG_SCHEDULER|\
|
||||
DETECT_DEADLOCKS|DONT_OPTIMIZE|DUMP_SCHEDULER|\
|
||||
LOTS_OF_SPANS|MALLOC_DEBUG|RADIO_RELAX|\
|
||||
REBUILD_PARSERS|REF_DEBUG|USE_HOARD_ALLOCATOR"
|
||||
|
||||
# Create buildopts.h
|
||||
|
||||
INCLUDE_CFLAGS="$MENUSELECT_CFLAGS"
|
||||
# Do the filter-out if needed.
|
||||
if ! $ADD_CFLAGS_TO_BUILDOPTS ; then
|
||||
INCLUDE_CFLAGS=$( echo "$MENUSELECT_CFLAGS" | \
|
||||
sed -r -e "s/(${FILTER_OUT})//g")
|
||||
fi
|
||||
|
||||
# Output the defines.
|
||||
for x in ${INCLUDE_CFLAGS}; do
|
||||
echo "#define ${x} 1"
|
||||
if test "${x}" = "LOW_MEMORY" ; then
|
||||
# LOW_MEMORY isn't an ABI affecting option but it is used in many sources
|
||||
# so it gets defined globally but is not included in AST_BUILTOPTS.
|
||||
continue
|
||||
fi
|
||||
if test "x${BUILDOPTS}" != "x" ; then
|
||||
BUILDOPTS="${BUILDOPTS}, ${x}"
|
||||
else
|
||||
BUILDOPTS="${x}"
|
||||
fi
|
||||
done
|
||||
|
||||
# We NEVER include the non-ABI-breaking flags in the
|
||||
# BUILDOPTS or use them to calculate the checksum so
|
||||
# we always filter out any that may exist.
|
||||
# After the filter-out, we also need to convert the
|
||||
# possibly-multi-spaced MENUSELECT_CFLAGS to a nice
|
||||
# comma-separated list.
|
||||
# I.E.
|
||||
# Remove leading spaces.
|
||||
# Convert consecutive interior spaces to a single space.
|
||||
# Remove trailing spaces.
|
||||
# Convert the now-single-spaces in the interior to ", ".
|
||||
BUILDOPTS=$( echo "$MENUSELECT_CFLAGS" | \
|
||||
sed -r -e "s/(${FILTER_OUT}|LOW_MEMORY)//g" -e "s/^\s+//g;s/\s+/ /g;s/\s+$//g;s/\s/, /g" )
|
||||
|
||||
# Calculate the checksum on only the ABI-breaking flags.
|
||||
BUILDSUM=$(echo "${BUILDOPTS}" | ${MD5} | cut -c1-32)
|
||||
BUILDSUM=`echo ${BUILDOPTS} | ${MD5} | cut -c1-32`
|
||||
|
||||
echo "#define AST_BUILDOPT_SUM \"${BUILDSUM}\""
|
||||
echo "#define AST_BUILDOPTS \"${BUILDOPTS}\""
|
||||
|
||||
# However, it'd be nice to see the non-ABI-breaking flags
|
||||
# when you do a "core show settings" so we create a separate
|
||||
# define for them.
|
||||
BUILDOPTS_ALL=$( echo "$MENUSELECT_CFLAGS" | \
|
||||
sed -r -e "s/^\s+//g;s/\s+/ /g;s/\s+$//g;s/\s/, /g" )
|
||||
echo "#define AST_BUILDOPTS_ALL \"${BUILDOPTS_ALL}\""
|
||||
|
|
|
@ -1,67 +1,220 @@
|
|||
#!/bin/sh
|
||||
|
||||
GIT=${GIT:-git}
|
||||
SED=${SED:-sed}
|
||||
AWK=${AWK:-awk}
|
||||
GIT=${GIT:-git}
|
||||
GREP=${GREP:-grep}
|
||||
SED=${SED:-sed}
|
||||
|
||||
|
||||
if [ -f ${1}/.version ]; then
|
||||
cat ${1}/.version
|
||||
exit 0
|
||||
fi
|
||||
elif [ -d ${1}/.svn ]; then
|
||||
PARTS=`LANG=C svn info ${1} | ${GREP} URL | ${AWK} '{print $2;}' | ${SED} -e 's:^.*/svn/asterisk/::' | ${SED} -e 's:/: :g'`
|
||||
BRANCH=0
|
||||
TEAM=0
|
||||
TAG=0
|
||||
FEATURE=0
|
||||
|
||||
if [ ! -d ${1}/.git ]; then
|
||||
REV=`svnversion -c ${1} | cut -d: -f2`
|
||||
|
||||
INTEGRATED=`LANG=C svn pg automerge-propname ${1}`
|
||||
if [ -z "${INTEGRATED}" ] ; then
|
||||
INTEGRATED=svnmerge-integrated
|
||||
fi
|
||||
|
||||
BASE=`LANG=C svn pg ${INTEGRATED} ${1} | cut -d: -f1`
|
||||
|
||||
if [ "${PARTS}" = "trunk" ] ; then
|
||||
echo SVN-trunk-r${REV}
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for PART in $PARTS ; do
|
||||
if [ ${TAG} != 0 ] ; then
|
||||
if [ "${PART}" = "autotag_for_be" ] ; then
|
||||
continue
|
||||
fi
|
||||
if [ "${PART}" = "autotag_for_sx00i" ] ; then
|
||||
continue
|
||||
fi
|
||||
RESULT="${PART}"
|
||||
break
|
||||
fi
|
||||
|
||||
if [ ${BRANCH} != 0 ] ; then
|
||||
RESULT="${RESULT}-${PART}"
|
||||
if [ ${FEATURE} != 0 ] ; then
|
||||
RESULT="${RESULT}-${FEATURE_NAME}"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
|
||||
if [ ${TEAM} != 0 ] ; then
|
||||
if [ -z "${RESULT}" ] ; then
|
||||
RESULT="${PART}"
|
||||
else
|
||||
RESULT="${RESULT}-${PART}"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "certified" ] ; then
|
||||
FEATURE=1
|
||||
FEATURE_NAME="cert"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "branches" ] ; then
|
||||
BRANCH=1
|
||||
RESULT="branch"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "tags" ] ; then
|
||||
TAG=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "team" ] ; then
|
||||
TEAM=1
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${TAG} != 0 ] ; then
|
||||
echo ${RESULT}
|
||||
else
|
||||
echo SVN-${RESULT}-r${REV}${BASE:+-${BASE}}
|
||||
fi
|
||||
elif [ -d ${1}/.git ]; then
|
||||
if [ -z ${GIT} ]; then
|
||||
GIT="git"
|
||||
fi
|
||||
|
||||
if ! command -v ${GIT} >/dev/null 2>&1; then
|
||||
echo "UNKNOWN__and_probably_unsupported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GITCHECK=$(${GIT} describe --always 2>/dev/null || echo gitfail 2>/dev/null)
|
||||
if [ "x${GITCHECK}" = "xgitfail" ]; then
|
||||
echo "UNKNOWN__git_check_fail"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd ${1}
|
||||
|
||||
# If the first log commit messages indicates that this is checked into
|
||||
# subversion, we'll just use the SVN- form of the revision.
|
||||
MODIFIED=""
|
||||
SVN_REV=`${GIT} log --pretty=full -1 | ${SED} -n '/git-svn-id:/ s/.*\@\([^ ]*\) .*/\1/p'`
|
||||
if [ -z "$SVN_REV" ]; then
|
||||
# If MAINLINE_BRANCH is already set in the environment, use it.
|
||||
if [ -z "${MAINLINE_BRANCH}" ] ; then
|
||||
# Try to retrieve MAINLINE_BRANCH from a local .develvars file first.
|
||||
# .develvars is keyed by the branch name so we need to get that first.
|
||||
BRANCH=$(${GIT} symbolic-ref --short HEAD)
|
||||
if [ -f .develvars ] ; then
|
||||
MAINLINE_BRANCH=$(${GIT} config -f .develvars --get branch.${BRANCH}.mainline-branch)
|
||||
fi
|
||||
|
||||
# If we didn't find it, see if this is a well-known development branch.
|
||||
# development/<mainline_branch>/<branchname> or
|
||||
# devel/<mainline_branch>/<branchname>
|
||||
if [ "x${MAINLINE_BRANCH}" = "x" ] ; then
|
||||
MAINLINE_BRANCH=$(echo "${BRANCH}" | ${SED} -n -r -e "s@devel(opment)?/([0-9]+)/.+@\2@p")
|
||||
fi
|
||||
|
||||
# If we didn't find it, get it from .gitreview defaultbranch.
|
||||
if [ "x${MAINLINE_BRANCH}" = "x" ] ; then
|
||||
MAINLINE_BRANCH=$(${GIT} config -f .gitreview --get gerrit.defaultbranch)
|
||||
fi
|
||||
fi
|
||||
|
||||
VERSION=`${GIT} describe --long --always --tags --dirty=M 2> /dev/null`
|
||||
if [ $? -ne 0 ]; then
|
||||
if [ "`${GIT} ls-files -m | wc -l`" != "0" ]; then
|
||||
MODIFIED="M"
|
||||
fi
|
||||
# Some older versions of git do not support all the above
|
||||
# options.
|
||||
VERSION=`${GIT} rev-parse --short --verify HEAD`${MODIFIED}
|
||||
fi
|
||||
echo GIT-${MAINLINE_BRANCH}-${VERSION}
|
||||
else
|
||||
PARTS=`LANG=C ${GIT} log --pretty=full | ${GREP} -F "git-svn-id:" | head -1 | ${AWK} '{print $2;}' | ${SED} -e s:^.*/svn/$2/:: | ${SED} -e 's:/: :g' | ${SED} -e 's/@.*$//g'`
|
||||
BRANCH=0
|
||||
TEAM=0
|
||||
TAG=0
|
||||
FEATURE=0
|
||||
|
||||
if [ "`${GIT} ls-files -m | wc -l`" != "0" ]; then
|
||||
MODIFIED="M"
|
||||
fi
|
||||
|
||||
for PART in $PARTS ; do
|
||||
if [ ${TAG} != 0 ] ; then
|
||||
if [ "${PART}" = "autotag_for_be" ] ; then
|
||||
continue
|
||||
fi
|
||||
if [ "${PART}" = "autotag_for_sx00i" ] ; then
|
||||
continue
|
||||
fi
|
||||
RESULT="${PART}"
|
||||
break
|
||||
fi
|
||||
|
||||
if [ ${BRANCH} != 0 ] ; then
|
||||
RESULT="${RESULT}-${PART}"
|
||||
if [ ${FEATURE} != 0 ] ; then
|
||||
RESULT="${RESULT}-${FEATURE_NAME}"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
|
||||
if [ ${TEAM} != 0 ] ; then
|
||||
if [ -z "${RESULT}" ] ; then
|
||||
RESULT="${PART}"
|
||||
else
|
||||
RESULT="${RESULT}-${PART}"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "certified" ] ; then
|
||||
FEATURE=1
|
||||
FEATURE_NAME="cert"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "branches" ] ; then
|
||||
BRANCH=1
|
||||
RESULT="branch"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "tags" ] ; then
|
||||
TAG=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "team" ] ; then
|
||||
TEAM=1
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "${PART}" = "trunk" ]; then
|
||||
echo SVN-trunk-r${SVN_REV}${MODIFIED}
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${TAG} != 0 ] ; then
|
||||
echo ${RESULT}
|
||||
else
|
||||
echo SVN-${RESULT##-}-r${SVN_REV}${MODIFIED}
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "UNKNOWN__and_probably_unsupported"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z ${GIT} ]; then
|
||||
GIT="git"
|
||||
fi
|
||||
|
||||
if ! command -v ${GIT} >/dev/null 2>&1; then
|
||||
echo "UNKNOWN__and_probably_unsupported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GITCHECK=$(${GIT} describe --always 2>/dev/null || echo gitfail 2>/dev/null)
|
||||
if [ "x${GITCHECK}" = "xgitfail" ]; then
|
||||
echo "UNKNOWN__git_check_fail"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd ${1} || exit 1
|
||||
|
||||
MODIFIED=""
|
||||
|
||||
# If MAINLINE_BRANCH is already set in the environment, use it.
|
||||
if [ -z "${MAINLINE_BRANCH}" ] ; then
|
||||
# Try to retrieve MAINLINE_BRANCH from a local .develvars file first.
|
||||
# .develvars is keyed by the branch name so we need to get that first.
|
||||
BRANCH=$(${GIT} symbolic-ref --short HEAD 2>/dev/null)
|
||||
if [ -f .develvars ] ; then
|
||||
MAINLINE_BRANCH=$(${GIT} config -f .develvars --get branch.${BRANCH}.mainline-branch)
|
||||
fi
|
||||
|
||||
# If we didn't find it, see if this is a well-known development branch.
|
||||
# development/<mainline_branch>/<branchname> or
|
||||
# devel/<mainline_branch>/<branchname>
|
||||
if [ "x${MAINLINE_BRANCH}" = "x" ] ; then
|
||||
MAINLINE_BRANCH=$(echo "${BRANCH}" | ${SED} -n -r -e "s@devel(opment)?/([0-9]+)/.+@\2@p")
|
||||
fi
|
||||
|
||||
# If we didn't find it, get it from configure.ac.
|
||||
if [ "x${MAINLINE_BRANCH}" = "x" ] ; then
|
||||
MAINLINE_BRANCH=$(${AWK} '/AC_INIT/ { print substr($2, 2, length($2) - 3) }' configure.ac)
|
||||
fi
|
||||
fi
|
||||
|
||||
VERSION=`${GIT} describe --long --always --tags --dirty=M 2> /dev/null`
|
||||
if [ $? -ne 0 ]; then
|
||||
if [ "`${GIT} ls-files -m | wc -l`" != "0" ]; then
|
||||
MODIFIED="M"
|
||||
fi
|
||||
# Some older versions of git do not support all the above
|
||||
# options.
|
||||
VERSION=`${GIT} rev-parse --short --verify HEAD`${MODIFIED}
|
||||
fi
|
||||
echo GIT-${MAINLINE_BRANCH}-${VERSION}
|
||||
|
|
|
@ -2,11 +2,6 @@
|
|||
|
||||
GREP=${GREP:-grep}
|
||||
|
||||
if test ! -f include/asterisk/buildopts.h ; then
|
||||
echo "include/asterisk/buildopts.h is missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test ! -f .flavor ; then
|
||||
EXTRA=""
|
||||
elif test ! -f .version ; then
|
||||
|
@ -23,11 +18,14 @@ then
|
|||
BUILDOPTS="AST_DEVMODE"
|
||||
fi
|
||||
|
||||
BUILDOPTS=$(sed -n -r -e 's/#define\s+AST_BUILDOPTS\s+"([^"]+)"/\1/gp' \
|
||||
include/asterisk/buildopts.h )
|
||||
|
||||
BUILDOPTS_ALL=$(sed -n -r -e 's/#define\s+AST_BUILDOPTS_ALL\s+"([^"]+)"/\1/gp' \
|
||||
include/asterisk/buildopts.h )
|
||||
TMP=`${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
|
||||
for x in ${TMP}; do
|
||||
if test "x${BUILDOPTS}" != "x" ; then
|
||||
BUILDOPTS="${BUILDOPTS}, ${x}"
|
||||
else
|
||||
BUILDOPTS="${x}"
|
||||
fi
|
||||
done
|
||||
|
||||
cat << END
|
||||
/*
|
||||
|
@ -45,8 +43,6 @@ static const char asterisk_version_num[] = "${ASTERISKVERSIONNUM}";
|
|||
|
||||
static const char asterisk_build_opts[] = "${BUILDOPTS}";
|
||||
|
||||
static const char asterisk_build_opts_all[] = "${BUILDOPTS_ALL}";
|
||||
|
||||
const char *ast_get_version(void)
|
||||
{
|
||||
return asterisk_version;
|
||||
|
@ -62,9 +58,4 @@ const char *ast_get_build_opts(void)
|
|||
return asterisk_build_opts;
|
||||
}
|
||||
|
||||
const char *ast_get_build_opts_all(void)
|
||||
{
|
||||
return asterisk_build_opts_all;
|
||||
}
|
||||
|
||||
END
|
||||
|
|
|
@ -135,18 +135,12 @@ if [ "${for_wiki}" -eq "1" ] || [ "${validate}" -eq "1" ]; then
|
|||
fi
|
||||
fi
|
||||
|
||||
make_absolute() {
|
||||
case "$1" in
|
||||
/*) echo "$1" ;;
|
||||
*) echo "$source_tree/$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [ "${command}" = "print_dependencies" ] ; then
|
||||
for subdir in ${mod_subdirs} ; do
|
||||
subpath=$(make_absolute "$subdir")
|
||||
${FIND} "${subpath}" \( -name '*.c' -o -name '*.cc' -o -name '*.xml' \) \
|
||||
-exec ${GREP} -l -E '(language="en_US"|appdocsxml.dtd)' '{}' \;
|
||||
subpath="${source_tree}/${subdir}"
|
||||
# We WANT word splitting in the following line.
|
||||
# shellcheck disable=SC2046
|
||||
${GREP} -l -E '(language="en_US"|appdocsxml.dtd)' $(${FIND} "${subpath}" -name '*.c' -or -name '*.cc' -or -name '*.xml') || :
|
||||
done
|
||||
exit
|
||||
fi
|
||||
|
@ -192,7 +186,7 @@ printf "Building Documentation For: "
|
|||
|
||||
for subdir in ${mod_subdirs} ; do
|
||||
printf "%s " "${subdir}"
|
||||
subdir_path=$(make_absolute "$subdir")
|
||||
subdir_path="${source_tree}/${subdir}"
|
||||
for i in $(${FIND} "${subdir_path}" -name '*.c' -or -name '*.cc'); do
|
||||
if [ "${with_moduleinfo}" -eq "1" ] ; then
|
||||
MODULEINFO=$(${AWK} -f "${source_tree}/build_tools/get_moduleinfo" "${i}")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
ALSA=@PBX_ALSA@
|
||||
BLUETOOTH=@PBX_BLUETOOTH@
|
||||
BEANSTALK=@PBX_BEANSTALK@
|
||||
COROSYNC=@PBX_COROSYNC@
|
||||
|
@ -28,7 +29,6 @@ URIPARSER=@PBX_URIPARSER@
|
|||
KQUEUE=@PBX_KQUEUE@
|
||||
LDAP=@PBX_LDAP@
|
||||
LIBEDIT=@PBX_LIBEDIT@
|
||||
LIBJWT=@PBX_LIBJWT@
|
||||
LIBXML2=@PBX_LIBXML2@
|
||||
LIBXSLT=@PBX_LIBXSLT@
|
||||
XMLSTARLET=@PBX_XMLSTARLET@
|
||||
|
@ -42,6 +42,7 @@ NEON29=@PBX_NEON29@
|
|||
OGG=@PBX_OGG@
|
||||
OPUS=@PBX_OPUS@
|
||||
OPUSFILE=@PBX_OPUSFILE@
|
||||
OSPTK=@PBX_OSPTK@
|
||||
PGSQL=@PBX_PGSQL@
|
||||
PJPROJECT=@PBX_PJPROJECT@
|
||||
POPT=@PBX_POPT@
|
||||
|
|
|
@ -19,14 +19,21 @@ all: _all
|
|||
|
||||
include $(ASTTOPDIR)/Makefile.moddir_rules
|
||||
|
||||
ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
|
||||
LIBS+= -lres_monitor.so
|
||||
endif
|
||||
|
||||
$(call MOD_ADD_C,chan_iax2,$(wildcard iax2/*.c))
|
||||
iax2/parser.o: _ASTCFLAGS+=$(call get_menuselect_cflags,MALLOC_DEBUG)
|
||||
|
||||
$(call MOD_ADD_C,chan_sip,$(wildcard sip/*.c))
|
||||
$(call MOD_ADD_C,chan_pjsip,$(wildcard pjsip/*.c))
|
||||
$(call MOD_ADD_C,chan_dahdi,$(wildcard dahdi/*.c) sig_analog.c sig_pri.c sig_ss7.c)
|
||||
|
||||
chan_dahdi.o: _ASTCFLAGS+=$(call get_menuselect_cflags,LOTS_OF_SPANS)
|
||||
chan_mgcp.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
|
||||
chan_unistim.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
|
||||
chan_phone.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
|
||||
chan_sip.o: _ASTCFLAGS+=$(AST_NO_FORMAT_TRUNCATION)
|
||||
|
||||
$(call MOD_ADD_C,console_video.c vgrabbers.c console_board.c)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -35,9 +35,11 @@
|
|||
* - svn co https://www.portaudio.com/repos/portaudio/branches/v19-devel
|
||||
*
|
||||
* \note Since this works with any audio system that libportaudio supports,
|
||||
* including ALSA and OSS, it has come to replace the deprecated chan_alsa and
|
||||
* chan_oss. However, the following features *at least* need to be implemented
|
||||
* here for this to be a full replacement:
|
||||
* including ALSA and OSS, this may someday deprecate chan_alsa and chan_oss.
|
||||
* However, before that can be done, it needs to *at least* have all of the
|
||||
* features that these other channel drivers have. The features implemented
|
||||
* in at least one of the other console channel drivers that are not yet
|
||||
* implemented here are:
|
||||
*
|
||||
* - Set Auto-answer from the dialplan
|
||||
* - transfer CLI command
|
||||
|
@ -152,8 +154,6 @@ static struct console_pvt {
|
|||
struct ast_frame fr;
|
||||
/*! Running = 1, Not running = 0 */
|
||||
unsigned int streamstate:1;
|
||||
/*! Abort stream processing? */
|
||||
unsigned int abort:1;
|
||||
/*! On-hook = 0, Off-hook = 1 */
|
||||
unsigned int hookstate:1;
|
||||
/*! Unmuted = 0, Muted = 1 */
|
||||
|
@ -277,19 +277,18 @@ static void *stream_monitor(void *data)
|
|||
};
|
||||
|
||||
for (;;) {
|
||||
pthread_testcancel();
|
||||
console_pvt_lock(pvt);
|
||||
res = Pa_ReadStream(pvt->stream, buf, sizeof(buf) / sizeof(int16_t));
|
||||
console_pvt_unlock(pvt);
|
||||
pthread_testcancel();
|
||||
|
||||
if (!pvt->owner || pvt->abort) {
|
||||
if (!pvt->owner) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (res == paNoError) {
|
||||
if (res == paNoError)
|
||||
ast_queue_frame(pvt->owner, &f);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Console ReadStream failed: %s\n", Pa_GetErrorText(res));
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -404,9 +403,8 @@ static int stop_stream(struct console_pvt *pvt)
|
|||
if (!pvt->streamstate || pvt->thread == AST_PTHREADT_NULL)
|
||||
return 0;
|
||||
|
||||
pvt->abort = 1;
|
||||
/* Wait for pvt->thread to exit cleanly, to avoid killing it while it's holding a lock. */
|
||||
pthread_kill(pvt->thread, SIGURG); /* Wake it up if needed, but don't cancel it */
|
||||
pthread_cancel(pvt->thread);
|
||||
pthread_kill(pvt->thread, SIGURG);
|
||||
pthread_join(pvt->thread, NULL);
|
||||
|
||||
console_pvt_lock(pvt);
|
||||
|
|
|
@ -261,36 +261,6 @@
|
|||
completely disabled)</para>
|
||||
<para> <literal>voice</literal> Voice mode (returns from FAX mode, reverting the changes that were made)</para>
|
||||
</enum>
|
||||
<enum name="dialmode">
|
||||
<para>R/W Pulse and tone dialing mode of the channel.</para>
|
||||
<para>Disabling tone dialing using this option will not disable the DSP used for DTMF detection.
|
||||
To do that, also set the <literal>digitdetect</literal> option. If digit detection is disabled,
|
||||
DTMF will not be detected, regardless of the <literal>dialmode</literal> setting.
|
||||
The <literal>digitdetect</literal> setting has no impact on pulse dialing detection.</para>
|
||||
<para>If set, overrides the setting in <literal>chan_dahdi.conf</literal> for that channel.</para>
|
||||
<enumlist>
|
||||
<enum name="both" />
|
||||
<enum name="pulse" />
|
||||
<enum name="dtmf" />
|
||||
<enum name="tone" />
|
||||
<enum name="none" />
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="waitfordialtone">
|
||||
<para>W/O Duration in ms for which to wait for dial tone on the current call.</para>
|
||||
<para>This setting is will temporarily override the <literal>waitfordialtone</literal>
|
||||
setting in <literal>chan_dahdi.conf</literal> (typically if that setting is disabled).
|
||||
You must call this in a pre-dial handler when making a call on an analog trunk
|
||||
(e.g. FXS-signalled interface).</para>
|
||||
<para>This allows, for example, being able to barge in on an in-use trunk,
|
||||
if dialed specifically, but allows skipping the trunk when routing calls
|
||||
if dial tone is not present on a channel.</para>
|
||||
<para>This setting will only apply to the current (next) call made on the
|
||||
DAHDI channel, and will not persist for future calls.</para>
|
||||
<para>Please keep in mind that due to the way that chan_dahdi implements dial tone detection,
|
||||
DTMF digits on an in-use channel will temporarily relay to any other channels attempting to use the channel for a call.
|
||||
However, voice transmission will not leak.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<info name="Dial_Resource" language="en_US" tech="DAHDI">
|
||||
|
@ -1057,7 +1027,6 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
|
|||
#endif
|
||||
.chan = {
|
||||
.context = "default",
|
||||
.immediatering = 1,
|
||||
.cid_num = "",
|
||||
.cid_name = "",
|
||||
.cid_tag = "",
|
||||
|
@ -1065,7 +1034,6 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void)
|
|||
.mohsuggest = "",
|
||||
.parkinglot = "",
|
||||
.transfertobusy = 1,
|
||||
.dialmode = 0,
|
||||
|
||||
.ani_info_digits = 2,
|
||||
.ani_wink_time = 1000,
|
||||
|
@ -1479,18 +1447,6 @@ static int my_get_callerid(void *pvt, char *namebuf, char *numbuf, enum analog_e
|
|||
if (num)
|
||||
ast_copy_string(numbuf, num, ANALOG_MAX_CID);
|
||||
|
||||
if (flags & (CID_PRIVATE_NUMBER | CID_UNKNOWN_NUMBER)) {
|
||||
/* If we got a presentation, we must set it on the channel */
|
||||
struct ast_channel *chan = analog_p->ss_astchan;
|
||||
struct ast_party_caller caller;
|
||||
|
||||
ast_party_caller_set_init(&caller, ast_channel_caller(chan));
|
||||
caller.id.name.presentation = caller.id.number.presentation = (flags & CID_PRIVATE_NUMBER) ?
|
||||
AST_PRES_RESTRICTED | AST_PRES_USER_NUMBER_UNSCREENED : AST_PRES_UNAVAILABLE | AST_PRES_USER_NUMBER_UNSCREENED;
|
||||
ast_party_caller_set(ast_channel_caller(chan), &caller, NULL);
|
||||
ast_party_caller_free(&caller);
|
||||
}
|
||||
|
||||
ast_debug(1, "CallerID number: %s, name: %s, flags=%d\n", num, name, flags);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1677,7 +1633,6 @@ static int my_callwait(void *pvt)
|
|||
static int my_send_callerid(void *pvt, int cwcid, struct ast_party_caller *caller)
|
||||
{
|
||||
struct dahdi_pvt *p = pvt;
|
||||
struct analog_pvt *analog_p = p->sig_pvt;
|
||||
|
||||
ast_debug(2, "Starting cid spill\n");
|
||||
|
||||
|
@ -1689,20 +1644,13 @@ static int my_send_callerid(void *pvt, int cwcid, struct ast_party_caller *calle
|
|||
if ((p->cidspill = ast_malloc(MAX_CALLERID_SIZE))) {
|
||||
int pres = ast_party_id_presentation(&caller->id);
|
||||
if (cwcid == 0) {
|
||||
/* Some CPE support additional parameters for on-hook Caller*ID,
|
||||
* such as redirecting reason and call qualifier, so send those
|
||||
* if available.
|
||||
* I don't know of any CPE that supports this for Call Waiting (unfortunately),
|
||||
* so don't send those for call waiting as that will just lengthen the CID spill
|
||||
* for no good reason.
|
||||
*/
|
||||
p->cidlen = ast_callerid_full_generate(p->cidspill,
|
||||
caller->id.name.str,
|
||||
caller->id.number.str,
|
||||
NULL,
|
||||
analog_p->redirecting_reason,
|
||||
-1,
|
||||
pres,
|
||||
analog_p->call_qualifier,
|
||||
0,
|
||||
CID_TYPE_MDMF,
|
||||
AST_LAW(p));
|
||||
} else {
|
||||
|
@ -1829,7 +1777,7 @@ static void my_handle_dtmf(void *pvt, struct ast_channel *ast, enum analog_sub a
|
|||
ast_debug(1, "Disabling FAX tone detection on %s after tone received\n", ast_channel_name(ast));
|
||||
}
|
||||
if (strcmp(ast_channel_exten(ast), "fax")) {
|
||||
const char *target_context = ast_channel_context(ast);
|
||||
const char *target_context = S_OR(ast_channel_macrocontext(ast), ast_channel_context(ast));
|
||||
|
||||
/*
|
||||
* We need to unlock 'ast' here because ast_exists_extension has the
|
||||
|
@ -2043,9 +1991,6 @@ static void my_set_cadence(void *pvt, int *cid_rings, struct ast_channel *ast)
|
|||
ast_log(LOG_WARNING, "Unable to set distinctive ring cadence %d on '%s': %s\n", p->distinctivering, ast_channel_name(ast), strerror(errno));
|
||||
*cid_rings = cidrings[p->distinctivering - 1];
|
||||
} else {
|
||||
if (p->distinctivering > 0) {
|
||||
ast_log(LOG_WARNING, "Cadence %d is not defined, falling back to default ring cadence\n", p->distinctivering);
|
||||
}
|
||||
if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCADENCE, NULL))
|
||||
ast_log(LOG_WARNING, "Unable to reset default ring on '%s': %s\n", ast_channel_name(ast), strerror(errno));
|
||||
*cid_rings = p->sendcalleridafter;
|
||||
|
@ -2119,40 +2064,11 @@ static void my_set_waitingfordt(void *pvt, struct ast_channel *ast)
|
|||
{
|
||||
struct dahdi_pvt *p = pvt;
|
||||
|
||||
/* We reset p->waitfordialtonetemp here, to prevent leaking to future calls,
|
||||
* but we also need to check against this value until we get dialtone
|
||||
* or the timer expires, since waitingfordt is when the timer started,
|
||||
* not when it should expire.
|
||||
*
|
||||
* Critically, we only set p->waitingfordt here if waitfordialtone or waitfordialtonetemp
|
||||
* has already been set, as waitingfordt is what is checked at runtime to determine
|
||||
* if we should be waiting for dial tone. This ensures that if a second call
|
||||
* is initiated concurrently, the first one "consumes" waitfordialtonetemp and resets it,
|
||||
* preventing leaking to other calls while remaining available to check on the first one while dialing.
|
||||
*/
|
||||
p->waitfordialtoneduration = p->waitfordialtonetemp ? p->waitfordialtonetemp : p->waitfordialtone;
|
||||
p->waitfordialtonetemp = 0;
|
||||
|
||||
if (!(p->waitfordialtoneduration && CANPROGRESSDETECT(p))) {
|
||||
return;
|
||||
if (p->waitfordialtone && CANPROGRESSDETECT(p) && p->dsp) {
|
||||
ast_debug(1, "Defer dialing for %dms or dialtone\n", p->waitfordialtone);
|
||||
gettimeofday(&p->waitingfordt, NULL);
|
||||
ast_setstate(ast, AST_STATE_OFFHOOK);
|
||||
}
|
||||
|
||||
/* Because the DSP is allocated when the channel is created,
|
||||
* if we requested waitfordialtone later (in a predial handler),
|
||||
* we need to create it now */
|
||||
if (!p->dsp) {
|
||||
p->dsp = ast_dsp_new();
|
||||
if (!p->dsp) {
|
||||
ast_log(LOG_ERROR, "Unable to allocate DSP\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
p->dsp_features |= DSP_FEATURE_WAITDIALTONE;
|
||||
ast_dsp_set_features(p->dsp, p->dsp_features);
|
||||
|
||||
ast_debug(1, "Defer dialing for %dms or dialtone\n", p->waitfordialtoneduration);
|
||||
gettimeofday(&p->waitingfordt, NULL);
|
||||
ast_setstate(ast, AST_STATE_OFFHOOK);
|
||||
}
|
||||
|
||||
static int my_check_waitingfordt(void *pvt)
|
||||
|
@ -5274,18 +5190,6 @@ static int has_voicemail(struct dahdi_pvt *p)
|
|||
int new_msgs;
|
||||
RAII_VAR(struct stasis_message *, mwi_message, NULL, ao2_cleanup);
|
||||
|
||||
/* A manual MWI disposition has been requested, use that instead
|
||||
* if this is for sending the new MWI indication. */
|
||||
if (p->mwioverride_active) {
|
||||
/* We don't clear p->mwioverride_active automatically,
|
||||
* because otherwise do_monitor would just change it back to the way it was.
|
||||
* We need to keep the override active until explicitly disabled by the user,
|
||||
* so that we can keep returning the correct answer in subsequent calls to do_monitor. */
|
||||
ast_debug(6, "MWI manual override active on channel %d: pretending that it should be %s\n",
|
||||
p->channel, p->mwioverride_disposition ? "active" : "inactive");
|
||||
return p->mwioverride_disposition;
|
||||
}
|
||||
|
||||
mwi_message = stasis_cache_get(ast_mwi_state_cache(), ast_mwi_state_type(), p->mailbox);
|
||||
if (mwi_message) {
|
||||
struct ast_mwi_state *mwi_state = stasis_message_data(mwi_message);
|
||||
|
@ -6628,36 +6532,6 @@ hangup_out:
|
|||
ast_free(p->cidspill);
|
||||
p->cidspill = NULL;
|
||||
|
||||
if (p->reoriginate && p->sig == SIG_FXOKS && dahdi_analog_lib_handles(p->sig, p->radio, 0)) {
|
||||
/* Automatic reorigination: if all calls towards a user have hung up,
|
||||
* give dial tone again, so user doesn't need to cycle the hook state manually. */
|
||||
if (my_is_off_hook(p) && !p->owner) {
|
||||
/* 2 important criteria: channel must be off-hook, with no calls remaining (no owner) */
|
||||
ast_debug(1, "Queuing reorigination for channel %d\n", p->channel);
|
||||
my_play_tone(p, SUB_REAL, -1); /* Stop any congestion tone that may be present. */
|
||||
/* Must wait for the loop disconnect to end.
|
||||
* Sadly, these definitions are in dahdi/kernel.h, not dahdi/user.h
|
||||
* Calling usleep on an active DAHDI channel is a no-no, but this is okay.
|
||||
*/
|
||||
usleep(800000); /* DAHDI_KEWLTIME + DAHDI_AFTERKEWLTIME */
|
||||
/* If the line is still off-hook and ownerless, actually queue the reorigination.
|
||||
* do_monitor will actually go ahead and do it. */
|
||||
if (!p->owner && my_is_off_hook(p)) {
|
||||
p->doreoriginate = 1; /* Tell do_monitor to reoriginate this channel */
|
||||
/* Note, my_off_hook will fail if called before the loop disconnect has finished
|
||||
* (important for FXOKS signaled channels). This is because DAHDI will reject
|
||||
* DAHDI_OFFHOOK while the channel is in TXSTATE_KEWL or TXSTATE_AFTERKEWL,
|
||||
* so we have to wait for that to finish (see comment above).
|
||||
* do_monitor itself cannot block, so make the blocking usleep call
|
||||
* here in the channel thread instead.
|
||||
*/
|
||||
my_off_hook(p); /* Now, go ahead and take the channel back off hook (sig_analog put it on hook) */
|
||||
} else {
|
||||
ast_debug(1, "Channel %d is no longer eligible for reorigination (went back on hook or became in use)\n", p->channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ast_mutex_unlock(&p->lock);
|
||||
ast_verb(3, "Hungup '%s'\n", ast_channel_name(ast));
|
||||
|
||||
|
@ -6782,14 +6656,6 @@ static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, i
|
|||
}
|
||||
|
||||
switch (option) {
|
||||
case AST_OPTION_TDD:
|
||||
cp = (char *) data;
|
||||
if (p->mate) {
|
||||
*cp = 2;
|
||||
} else {
|
||||
*cp = p->tdd ? 1 : 0;
|
||||
}
|
||||
break;
|
||||
case AST_OPTION_DIGIT_DETECT:
|
||||
cp = (char *) data;
|
||||
*cp = p->ignoredtmf ? 0 : 1;
|
||||
|
@ -7143,32 +7009,6 @@ static int dahdi_func_read(struct ast_channel *chan, const char *function, char
|
|||
}
|
||||
ast_mutex_unlock(&p->lock);
|
||||
#endif /* defined(HAVE_PRI) */
|
||||
} else if (!strcasecmp(data, "dialmode")) {
|
||||
struct analog_pvt *analog_p;
|
||||
ast_mutex_lock(&p->lock);
|
||||
analog_p = p->sig_pvt;
|
||||
/* Hardcode p->radio and p->oprmode as 0 since we're using this to check for analogness, not the handler */
|
||||
if (dahdi_analog_lib_handles(p->sig, 0, 0) && analog_p) {
|
||||
switch (analog_p->dialmode) {
|
||||
case ANALOG_DIALMODE_BOTH:
|
||||
ast_copy_string(buf, "both", len);
|
||||
break;
|
||||
case ANALOG_DIALMODE_PULSE:
|
||||
ast_copy_string(buf, "pulse", len);
|
||||
break;
|
||||
case ANALOG_DIALMODE_DTMF:
|
||||
ast_copy_string(buf, "dtmf", len);
|
||||
break;
|
||||
case ANALOG_DIALMODE_NONE:
|
||||
ast_copy_string(buf, "none", len);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "%s only supported on analog channels\n", data);
|
||||
*buf = '\0';
|
||||
res = -1;
|
||||
}
|
||||
ast_mutex_unlock(&p->lock);
|
||||
} else {
|
||||
*buf = '\0';
|
||||
res = -1;
|
||||
|
@ -7274,45 +7114,6 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char
|
|||
ast_log(LOG_WARNING, "Unsupported value '%s' provided for '%s' item.\n", value, data);
|
||||
res = -1;
|
||||
}
|
||||
} else if (!strcasecmp(data, "dialmode")) {
|
||||
struct analog_pvt *analog_p;
|
||||
|
||||
ast_mutex_lock(&p->lock);
|
||||
analog_p = p->sig_pvt;
|
||||
if (!dahdi_analog_lib_handles(p->sig, 0, 0) || !analog_p) {
|
||||
ast_log(LOG_WARNING, "%s only supported on analog channels\n", data);
|
||||
ast_mutex_unlock(&p->lock);
|
||||
return -1;
|
||||
}
|
||||
/* analog pvt is used for pulse dialing, so update both */
|
||||
if (!strcasecmp(value, "pulse")) {
|
||||
p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_PULSE;
|
||||
} else if (!strcasecmp(value, "dtmf") || !strcasecmp(value, "tone")) {
|
||||
p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_DTMF;
|
||||
} else if (!strcasecmp(value, "none")) {
|
||||
p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_NONE;
|
||||
} else if (!strcasecmp(value, "both")) {
|
||||
p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_BOTH;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "'%s' is an invalid setting for %s\n", value, data);
|
||||
res = -1;
|
||||
}
|
||||
ast_mutex_unlock(&p->lock);
|
||||
} else if (!strcasecmp(data, "waitfordialtone")) {
|
||||
if (ast_strlen_zero(value)) {
|
||||
ast_log(LOG_WARNING, "waitfordialtone requires a duration in ms\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_mutex_lock(&p->lock);
|
||||
if (!CANPROGRESSDETECT(p)) {
|
||||
ast_log(LOG_WARNING, "%s only supported on analog trunks\n", data);
|
||||
ast_mutex_unlock(&p->lock);
|
||||
return -1;
|
||||
}
|
||||
/* Only set the temp waitfordialtone setting, not the permanent one. */
|
||||
p->waitfordialtonetemp = atoi(value);
|
||||
ast_mutex_unlock(&p->lock);
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
|
@ -7632,7 +7433,7 @@ static void dahdi_handle_dtmf(struct ast_channel *ast, int idx, struct ast_frame
|
|||
ast_debug(1, "Disabling FAX tone detection on %s after tone received\n", ast_channel_name(ast));
|
||||
}
|
||||
if (strcmp(ast_channel_exten(ast), "fax")) {
|
||||
const char *target_context = ast_channel_context(ast);
|
||||
const char *target_context = S_OR(ast_channel_macrocontext(ast), ast_channel_context(ast));
|
||||
|
||||
/*
|
||||
* We need to unlock 'ast' here because ast_exists_extension has the
|
||||
|
@ -9152,9 +8953,9 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
|
|||
/* DSP clears us of being pulse */
|
||||
p->pulsedial = 0;
|
||||
} else if (p->waitingfordt.tv_sec) {
|
||||
if (ast_tvdiff_ms(ast_tvnow(), p->waitingfordt) >= p->waitfordialtoneduration) {
|
||||
if (ast_tvdiff_ms(ast_tvnow(), p->waitingfordt) >= p->waitfordialtone ) {
|
||||
p->waitingfordt.tv_sec = 0;
|
||||
ast_log(LOG_NOTICE, "Never saw dialtone on channel %d\n", p->channel);
|
||||
ast_log(LOG_WARNING, "Never saw dialtone on channel %d\n", p->channel);
|
||||
ast_frfree(f);
|
||||
f = NULL;
|
||||
} else if (f->frametype == AST_FRAME_VOICE) {
|
||||
|
@ -9196,13 +8997,6 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast)
|
|||
} else {
|
||||
dahdi_handle_dtmf(ast, idx, &f);
|
||||
}
|
||||
if (!(p->dialmode == ANALOG_DIALMODE_BOTH || p->dialmode == ANALOG_DIALMODE_DTMF)) {
|
||||
if (f->frametype == AST_FRAME_DTMF_END) { /* only show this message when the key is let go of */
|
||||
ast_debug(1, "Dropping DTMF digit '%c' because tone dialing is disabled\n", f->subclass.integer);
|
||||
}
|
||||
f->frametype = AST_FRAME_NULL;
|
||||
f->subclass.integer = 0;
|
||||
}
|
||||
break;
|
||||
case AST_FRAME_VOICE:
|
||||
if (p->cidspill || p->cid_suppress_expire) {
|
||||
|
@ -11960,7 +11754,7 @@ static void *do_monitor(void *data)
|
|||
&& (last->sig & __DAHDI_SIG_FXO)
|
||||
&& !analog_p->fxsoffhookstate
|
||||
&& !last->owner
|
||||
&& (!ast_strlen_zero(last->mailbox) || last->mwioverride_active)
|
||||
&& !ast_strlen_zero(last->mailbox)
|
||||
&& !analog_p->subs[SUB_REAL].owner /* could be a recall ring from a flash hook hold */
|
||||
&& (thispass - analog_p->onhooktime > 3)) {
|
||||
res = has_voicemail(last);
|
||||
|
@ -12108,26 +11902,6 @@ static void *do_monitor(void *data)
|
|||
else
|
||||
doomed = handle_init_event(i, res);
|
||||
}
|
||||
if (i->doreoriginate && res == DAHDI_EVENT_HOOKCOMPLETE) {
|
||||
/* Actually automatically reoriginate this FXS line, if directed to.
|
||||
* We should get a DAHDI_EVENT_HOOKCOMPLETE from the loop disconnect
|
||||
* doing its thing (one reason why this is for FXOKS only: FXOLS
|
||||
* hangups don't give us any DAHDI events to piggyback off of)*/
|
||||
i->doreoriginate = 0;
|
||||
/* Double check the channel is still off-hook. There's only about a millisecond
|
||||
* between when doreoriginate is set high and we see that here, but just to be safe. */
|
||||
if (!my_is_off_hook(i)) {
|
||||
ast_debug(1, "Woah! Went back on hook before reoriginate could happen on channel %d\n", i->channel);
|
||||
} else {
|
||||
ast_verb(3, "Automatic reorigination on channel %d\n", i->channel);
|
||||
res = DAHDI_EVENT_RINGOFFHOOK; /* Pretend that the physical channel just went off hook */
|
||||
if (dahdi_analog_lib_handles(i->sig, i->radio, i->oprmode)) {
|
||||
doomed = (struct dahdi_pvt *) analog_handle_init_event(i->sig_pvt, dahdievent_to_analogevent(res));
|
||||
} else {
|
||||
doomed = handle_init_event(i, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_mutex_lock(&iflock);
|
||||
}
|
||||
}
|
||||
|
@ -13005,9 +12779,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
|
|||
}
|
||||
#endif
|
||||
tmp->immediate = conf->chan.immediate;
|
||||
tmp->immediatering = conf->chan.immediatering;
|
||||
tmp->transfertobusy = conf->chan.transfertobusy;
|
||||
tmp->dialmode = conf->chan.dialmode;
|
||||
if (chan_sig & __DAHDI_SIG_FXS) {
|
||||
tmp->mwimonitor_fsk = conf->chan.mwimonitor_fsk;
|
||||
tmp->mwimonitor_neon = conf->chan.mwimonitor_neon;
|
||||
|
@ -13034,8 +12806,6 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
|
|||
tmp->usedistinctiveringdetection = usedistinctiveringdetection;
|
||||
tmp->callwaitingcallerid = conf->chan.callwaitingcallerid;
|
||||
tmp->threewaycalling = conf->chan.threewaycalling;
|
||||
tmp->threewaysilenthold = conf->chan.threewaysilenthold;
|
||||
tmp->calledsubscriberheld = conf->chan.calledsubscriberheld; /* Not used in chan_dahdi.c, just analog pvt, but must exist on the DAHDI pvt anyways */
|
||||
tmp->adsi = conf->chan.adsi;
|
||||
tmp->use_smdi = conf->chan.use_smdi;
|
||||
tmp->permhidecallerid = conf->chan.hidecallerid;
|
||||
|
@ -13224,7 +12994,6 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
|
|||
tmp->ani_wink_time = conf->chan.ani_wink_time;
|
||||
tmp->ani_timeout = conf->chan.ani_timeout;
|
||||
tmp->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch;
|
||||
tmp->reoriginate = conf->chan.reoriginate;
|
||||
tmp->sendcalleridafter = conf->chan.sendcalleridafter;
|
||||
ast_cc_copy_config_params(tmp->cc_params, conf->chan.cc_params);
|
||||
|
||||
|
@ -13334,20 +13103,16 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
|
|||
analog_p->ani_wink_time = conf->chan.ani_wink_time;
|
||||
analog_p->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch;
|
||||
analog_p->permcallwaiting = conf->chan.callwaiting; /* permcallwaiting possibly modified in analog_config_complete */
|
||||
analog_p->calledsubscriberheld = conf->chan.calledsubscriberheld; /* Only actually used in analog pvt, not DAHDI pvt */
|
||||
analog_p->callreturn = conf->chan.callreturn;
|
||||
analog_p->cancallforward = conf->chan.cancallforward;
|
||||
analog_p->canpark = conf->chan.canpark;
|
||||
analog_p->dahditrcallerid = conf->chan.dahditrcallerid;
|
||||
analog_p->immediate = conf->chan.immediate;
|
||||
analog_p->immediatering = conf->chan.immediatering;
|
||||
analog_p->permhidecallerid = conf->chan.hidecallerid; /* hidecallerid is the config setting, not permhidecallerid (~permcallwaiting above) */
|
||||
/* It's not necessary to set analog_p->hidecallerid here, sig_analog will set hidecallerid=permhidecaller before each call */
|
||||
analog_p->permhidecallerid = conf->chan.permhidecallerid;
|
||||
analog_p->pulse = conf->chan.pulse;
|
||||
analog_p->threewaycalling = conf->chan.threewaycalling;
|
||||
analog_p->transfer = conf->chan.transfer;
|
||||
analog_p->transfertobusy = conf->chan.transfertobusy;
|
||||
analog_p->dialmode = conf->chan.dialmode;
|
||||
analog_p->use_callerid = tmp->use_callerid;
|
||||
analog_p->usedistinctiveringdetection = tmp->usedistinctiveringdetection;
|
||||
analog_p->use_smdi = tmp->use_smdi;
|
||||
|
@ -16612,75 +16377,6 @@ static char *dahdi_set_dnd(struct ast_cli_entry *e, int cmd, struct ast_cli_args
|
|||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static char *dahdi_set_mwi(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
int channel;
|
||||
int on;
|
||||
int override = 1;
|
||||
struct dahdi_pvt *dahdi_chan = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "dahdi set mwi";
|
||||
e->usage =
|
||||
"Usage: dahdi set mwi <chan#> <on|off|reset>\n"
|
||||
" Sets/unsets MWI (Message Waiting Indicator) manually on a channel.\n"
|
||||
" This may be used regardless of whether the channel is assigned any mailboxes.\n"
|
||||
" When active, this setting will override the voicemail status to set MWI.\n"
|
||||
" Once cleared, the voicemail status will resume control of MWI.\n"
|
||||
" Changes are queued for when the channel is idle and persist until cleared.\n"
|
||||
" <chan num> is the channel number\n"
|
||||
" <on|off|reset> Enable, disable, or reset Message Waiting Indicator override?\n"
|
||||
;
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc != 5)
|
||||
return CLI_SHOWUSAGE;
|
||||
|
||||
if ((channel = atoi(a->argv[3])) <= 0) {
|
||||
ast_cli(a->fd, "Expected channel number, got '%s'\n", a->argv[3]);
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
if (ast_true(a->argv[4])) {
|
||||
on = 1;
|
||||
} else if (ast_false(a->argv[4])) {
|
||||
on = 0;
|
||||
} else if (!strcmp(a->argv[4], "reset")) {
|
||||
override = 0;
|
||||
} else {
|
||||
ast_cli(a->fd, "Expected 'on' or 'off' or 'reset', got '%s'\n", a->argv[4]);
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
ast_mutex_lock(&iflock);
|
||||
for (dahdi_chan = iflist; dahdi_chan; dahdi_chan = dahdi_chan->next) {
|
||||
if (dahdi_chan->channel != channel)
|
||||
continue;
|
||||
|
||||
/* Found the channel. Actually set it */
|
||||
if (override) {
|
||||
dahdi_chan->mwioverride_disposition = on;
|
||||
ast_cli(a->fd, "MWI '%s' queued for channel %d\n", on ? "enable" : "disable", channel);
|
||||
}
|
||||
dahdi_chan->mwioverride_active = override;
|
||||
/* The do_monitor thread will take care of actually sending the MWI
|
||||
* at an appropriate time for the channel. */
|
||||
break;
|
||||
}
|
||||
ast_mutex_unlock(&iflock);
|
||||
|
||||
if (!dahdi_chan) {
|
||||
ast_cli(a->fd, "Unable to find given channel %d\n", channel);
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry dahdi_cli[] = {
|
||||
AST_CLI_DEFINE(handle_dahdi_show_cadences, "List cadences"),
|
||||
AST_CLI_DEFINE(dahdi_show_channels, "Show active DAHDI channels"),
|
||||
|
@ -16693,7 +16389,6 @@ static struct ast_cli_entry dahdi_cli[] = {
|
|||
AST_CLI_DEFINE(dahdi_set_hwgain, "Set hardware gain on a channel"),
|
||||
AST_CLI_DEFINE(dahdi_set_swgain, "Set software gain on a channel"),
|
||||
AST_CLI_DEFINE(dahdi_set_dnd, "Sets/resets DND (Do Not Disturb) mode on a channel"),
|
||||
AST_CLI_DEFINE(dahdi_set_mwi, "Sets/unsets MWI (Message Waiting Indicator) manually on a channel"),
|
||||
};
|
||||
|
||||
#define TRANSFER 0
|
||||
|
@ -18455,8 +18150,6 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
confp->chan.cid_start = CID_START_RING;
|
||||
} else if (!strcasecmp(v->name, "threewaycalling")) {
|
||||
confp->chan.threewaycalling = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "threewaysilenthold")) {
|
||||
confp->chan.threewaysilenthold = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "cancallforward")) {
|
||||
confp->chan.cancallforward = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "relaxdtmf")) {
|
||||
|
@ -18499,8 +18192,6 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
confp->chan.busycount = atoi(v->value);
|
||||
} else if (!strcasecmp(v->name, "busypattern")) {
|
||||
parse_busy_pattern(v, &confp->chan.busy_cadence);
|
||||
} else if (!strcasecmp(v->name, "calledsubscriberheld")) {
|
||||
confp->chan.calledsubscriberheld = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "callprogress")) {
|
||||
confp->chan.callprogress &= ~CALLPROGRESS_PROGRESS;
|
||||
if (ast_true(v->value))
|
||||
|
@ -18590,30 +18281,18 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
} else if (!strcasecmp(v->name, "group")) {
|
||||
confp->chan.group = ast_get_group(v->value);
|
||||
} else if (!strcasecmp(v->name, "callgroup")) {
|
||||
if (!((confp->chan.sig == SIG_FXOKS) || (confp->chan.sig == SIG_FXOGS) || (confp->chan.sig == SIG_FXOLS))) {
|
||||
ast_log(LOG_WARNING, "Only FXO signalled channels may belong to a call group\n");
|
||||
}
|
||||
if (!strcasecmp(v->value, "none"))
|
||||
confp->chan.callgroup = 0;
|
||||
else
|
||||
confp->chan.callgroup = ast_get_group(v->value);
|
||||
} else if (!strcasecmp(v->name, "pickupgroup")) {
|
||||
if (!((confp->chan.sig == SIG_FXOKS) || (confp->chan.sig == SIG_FXOGS) || (confp->chan.sig == SIG_FXOLS))) {
|
||||
ast_log(LOG_WARNING, "Only FXO signalled channels may belong to a pickup group\n");
|
||||
}
|
||||
if (!strcasecmp(v->value, "none"))
|
||||
confp->chan.pickupgroup = 0;
|
||||
else
|
||||
confp->chan.pickupgroup = ast_get_group(v->value);
|
||||
} else if (!strcasecmp(v->name, "namedcallgroup")) {
|
||||
if (!((confp->chan.sig == SIG_FXOKS) || (confp->chan.sig == SIG_FXOGS) || (confp->chan.sig == SIG_FXOLS))) {
|
||||
ast_log(LOG_WARNING, "Only FXO signalled channels may belong to a named call group\n");
|
||||
}
|
||||
confp->chan.named_callgroups = ast_get_namedgroups(v->value);
|
||||
} else if (!strcasecmp(v->name, "namedpickupgroup")) {
|
||||
if (!((confp->chan.sig == SIG_FXOKS) || (confp->chan.sig == SIG_FXOGS) || (confp->chan.sig == SIG_FXOLS))) {
|
||||
ast_log(LOG_WARNING, "Only FXO signalled channels may belong to a named pickup group\n");
|
||||
}
|
||||
confp->chan.named_pickupgroups = ast_get_namedgroups(v->value);
|
||||
} else if (!strcasecmp(v->name, "setvar")) {
|
||||
if (v->value) {
|
||||
|
@ -18633,20 +18312,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
}
|
||||
} else if (!strcasecmp(v->name, "immediate")) {
|
||||
confp->chan.immediate = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "immediatering")) {
|
||||
confp->chan.immediatering = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "transfertobusy")) {
|
||||
confp->chan.transfertobusy = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "dialmode")) {
|
||||
if (!strcasecmp(v->value, "pulse")) {
|
||||
confp->chan.dialmode = ANALOG_DIALMODE_PULSE;
|
||||
} else if (!strcasecmp(v->value, "dtmf") || !strcasecmp(v->value, "tone")) {
|
||||
confp->chan.dialmode = ANALOG_DIALMODE_DTMF;
|
||||
} else if (!strcasecmp(v->value, "none")) {
|
||||
confp->chan.dialmode = ANALOG_DIALMODE_NONE;
|
||||
} else {
|
||||
confp->chan.dialmode = ANALOG_DIALMODE_BOTH;
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "mwimonitor")) {
|
||||
confp->chan.mwimonitor_neon = 0;
|
||||
confp->chan.mwimonitor_fsk = 0;
|
||||
|
@ -18745,8 +18412,6 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
|
|||
confp->chan.ani_timeout = atoi(v->value);
|
||||
} else if (!strcasecmp(v->name, "hanguponpolarityswitch")) {
|
||||
confp->chan.hanguponpolarityswitch = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "autoreoriginate")) {
|
||||
confp->chan.reoriginate = ast_true(v->value);
|
||||
} else if (!strcasecmp(v->name, "sendcalleridafter")) {
|
||||
confp->chan.sendcalleridafter = atoi(v->value);
|
||||
} else if (!strcasecmp(v->name, "mwimonitornotify")) {
|
||||
|
|
|
@ -146,7 +146,6 @@ struct dahdi_pvt {
|
|||
* \note Set to a couple of nonzero values but it is only tested like a boolean.
|
||||
*/
|
||||
int radio;
|
||||
int dialmode; /*!< Dialing Modes Allowed (Pulse/Tone) */
|
||||
int outsigmod; /*!< Outbound Signalling style (modifier) */
|
||||
int oprmode; /*!< "Operator Services" mode */
|
||||
struct dahdi_pvt *oprpeer; /*!< "Operator Services" peer tech_pvt ptr */
|
||||
|
@ -204,13 +203,6 @@ struct dahdi_pvt {
|
|||
* \note Set from the "busydetect" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int busydetect:1;
|
||||
/*!
|
||||
* \brief TRUE if Called Subscriber held is enabled.
|
||||
* This allows a single incoming call to hold a DAHDI channel up,
|
||||
* allowing a recipient to hang up an extension and pick up another
|
||||
* phone on the same line without disconnecting the call.
|
||||
*/
|
||||
unsigned int calledsubscriberheld:1;
|
||||
/*!
|
||||
* \brief TRUE if call return is enabled.
|
||||
* (*69, if your dialplan doesn't catch this first)
|
||||
|
@ -283,14 +275,6 @@ struct dahdi_pvt {
|
|||
* \note Set from the "hanguponpolarityswitch" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int hanguponpolarityswitch:1;
|
||||
/*!
|
||||
* \brief TRUE if FXS (FXO-signalled) channel should reoriginate for user to make a new call.
|
||||
*/
|
||||
unsigned int reoriginate:1;
|
||||
/*!
|
||||
* \brief Internal flag for if we should actually process a reorigination.
|
||||
*/
|
||||
unsigned int doreoriginate:1;
|
||||
/*! \brief TRUE if DTMF detection needs to be done by hardware. */
|
||||
unsigned int hardwaredtmf:1;
|
||||
/*!
|
||||
|
@ -314,12 +298,6 @@ struct dahdi_pvt {
|
|||
* \note Set from the "immediate" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int immediate:1;
|
||||
/*!
|
||||
* \brief TRUE if audible ringback should be provided
|
||||
* when immediate = yes.
|
||||
* \note Set from the "immediatering" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int immediatering:1;
|
||||
/*! \brief TRUE if in an alarm condition. */
|
||||
unsigned int inalarm:1;
|
||||
/*! \brief TRUE if TDD in MATE mode */
|
||||
|
@ -366,11 +344,6 @@ struct dahdi_pvt {
|
|||
* \note Set from the "threewaycalling" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int threewaycalling:1;
|
||||
/*!
|
||||
* \brief TRUE if a three way dial tone should time out to silence
|
||||
* \note Set from the "threewaysilenthold" value read in from chan_dahdi.conf
|
||||
*/
|
||||
unsigned int threewaysilenthold:1;
|
||||
/*!
|
||||
* \brief TRUE if call transfer is enabled
|
||||
* \note For FXS ports (either direct analog or over T1/E1):
|
||||
|
@ -431,10 +404,6 @@ struct dahdi_pvt {
|
|||
unsigned int mwimonitoractive:1;
|
||||
/*! \brief TRUE if a MWI message sending thread is active */
|
||||
unsigned int mwisendactive:1;
|
||||
/*! \brief TRUE if a manual MWI override is active for a channel */
|
||||
unsigned int mwioverride_active:1;
|
||||
/*! \brief Manual MWI disposition (on/off) */
|
||||
unsigned int mwioverride_disposition:1;
|
||||
/*!
|
||||
* \brief TRUE if channel is out of reset and ready
|
||||
* \note Used by SS7. Otherwise set but not used.
|
||||
|
@ -654,14 +623,6 @@ struct dahdi_pvt {
|
|||
* \note Set from the "waitfordialtone" value read in from chan_dahdi.conf
|
||||
*/
|
||||
int waitfordialtone;
|
||||
/*!
|
||||
* \brief Transient variable. Same as waitfordialtone, but temporarily set for a specific call, rather than permanently for the channel.
|
||||
*/
|
||||
int waitfordialtonetemp;
|
||||
/*!
|
||||
* \brief Transient variable. Stored off waitfordialtone duration at runtime.
|
||||
*/
|
||||
int waitfordialtoneduration;
|
||||
/*!
|
||||
* \brief Number of frames to watch for dialtone in incoming calls
|
||||
* \note Set from the "dialtone_detect" value read in from chan_dahdi.conf
|
||||
|
|
|
@ -398,47 +398,6 @@ static int (*iax2_regfunk)(const char *username, int onoff) = NULL;
|
|||
#define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */
|
||||
#define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */
|
||||
|
||||
/*! \brief Name of effective auth method */
|
||||
static const char *auth_method_labels[] = {
|
||||
[0] = "none",
|
||||
[IAX_AUTH_PLAINTEXT] = "plaintext",
|
||||
[IAX_AUTH_MD5] = "MD5",
|
||||
[IAX_AUTH_RSA] = "RSA",
|
||||
};
|
||||
|
||||
/* Max length is length of |RSA|MD5|plaintext (18 + 1 for NUL = 19) */
|
||||
#define AUTH_METHOD_NAMES_BUFSIZE 19
|
||||
|
||||
/*!
|
||||
* \brief Get names of all auth methods
|
||||
* \param Bit field of auth methods
|
||||
* \param[out] buf Buffer into which to write the names. Must be of size AUTH_METHOD_NAMES_BUFSIZE.
|
||||
* \return Auth methods name
|
||||
*/
|
||||
static char *auth_method_names(int authmethods, char *restrict buf)
|
||||
{
|
||||
char *pos = buf;
|
||||
|
||||
*pos = '\0';
|
||||
|
||||
if (authmethods & IAX_AUTH_RSA) {
|
||||
pos += sprintf(pos, "|RSA");
|
||||
}
|
||||
if (authmethods & IAX_AUTH_MD5) {
|
||||
pos += sprintf(pos, "|MD5");
|
||||
}
|
||||
if (authmethods & IAX_AUTH_PLAINTEXT) {
|
||||
pos += sprintf(pos, "|plaintext");
|
||||
}
|
||||
|
||||
if (pos == buf) { /* No auth methods */
|
||||
strcpy(buf, "none");
|
||||
return buf;
|
||||
}
|
||||
|
||||
return buf + 1; /* Skip leading | */
|
||||
}
|
||||
|
||||
/* if a pvt has encryption setup done and is running on the call */
|
||||
#define IAX_CALLENCRYPTED(pvt) \
|
||||
(ast_test_flag64(pvt, IAX_ENCRYPTED) && ast_test_flag64(pvt, IAX_KEYPOPULATED))
|
||||
|
@ -866,8 +825,6 @@ struct chan_iax2_pvt {
|
|||
int authrej;
|
||||
/*! permitted authentication methods */
|
||||
int authmethods;
|
||||
/*! effective authentication method */
|
||||
int eff_auth_method;
|
||||
/*! permitted encryption methods */
|
||||
int encmethods;
|
||||
/*! Encryption AES-128 Key */
|
||||
|
@ -4201,19 +4158,9 @@ static void __get_from_jb(const void *p)
|
|||
now.tv_usec += 1000;
|
||||
|
||||
ms = ast_tvdiff_ms(now, pvt->rxcore);
|
||||
if (ms >= (next = jb_next(pvt->jb))) {
|
||||
voicefmt = ast_format_compatibility_bitfield2format(pvt->voiceformat);
|
||||
if (!voicefmt) {
|
||||
/* pvt->voiceformat won't be set if we haven't received any voice frames yet.
|
||||
* In this case, fall back to using the format negotiated during call setup,
|
||||
* so we don't stall the jitterbuffer completely. */
|
||||
voicefmt = ast_format_compatibility_bitfield2format(pvt->peerformat);
|
||||
}
|
||||
if (!voicefmt) {
|
||||
/* Really shouldn't happen, but if it does, should be looked into */
|
||||
ast_log(LOG_WARNING, "No voice format and no peer format available on %s, backlogging frame\n", ast_channel_name(pvt->owner));
|
||||
goto cleanup; /* Don't crash if there's no voice format */
|
||||
}
|
||||
|
||||
voicefmt = ast_format_compatibility_bitfield2format(pvt->voiceformat);
|
||||
if (voicefmt && ms >= (next = jb_next(pvt->jb))) {
|
||||
ret = jb_get(pvt->jb, &frame, ms, ast_format_get_default_ms(voicefmt));
|
||||
switch(ret) {
|
||||
case JB_OK:
|
||||
|
@ -4255,7 +4202,6 @@ static void __get_from_jb(const void *p)
|
|||
break;
|
||||
}
|
||||
}
|
||||
cleanup:
|
||||
if (pvt)
|
||||
update_jbsched(pvt);
|
||||
ast_mutex_unlock(&iaxsl[callno]);
|
||||
|
@ -8232,7 +8178,7 @@ static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
|
|||
user = user_unref(user);
|
||||
}
|
||||
if (ast_test_flag64(p, IAX_FORCE_ENCRYPT) && !p->encmethods) {
|
||||
ast_log(LOG_WARNING, "Call Terminated, incoming call is unencrypted while force encrypt is enabled.\n");
|
||||
ast_log(LOG_NOTICE, "Call Terminated, Incoming call is unencrypted while force encrypt is enabled.\n");
|
||||
return res;
|
||||
}
|
||||
if (!ast_test_flag(&p->state, IAX_STATE_AUTHENTICATED))
|
||||
|
@ -8258,17 +8204,12 @@ static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
|
|||
key = ast_key_get(keyn, AST_KEY_PUBLIC);
|
||||
if (key && !ast_check_signature(key, p->challenge, rsasecret)) {
|
||||
res = 0;
|
||||
p->eff_auth_method = IAX_AUTH_RSA;
|
||||
break;
|
||||
} else if (!key) {
|
||||
ast_log(LOG_WARNING, "Requested inkey '%s' for RSA authentication does not exist\n", keyn);
|
||||
}
|
||||
} else if (!key)
|
||||
ast_log(LOG_WARNING, "requested inkey '%s' for RSA authentication does not exist\n", keyn);
|
||||
keyn = strsep(&stringp, ":");
|
||||
}
|
||||
ast_free(tmpkey);
|
||||
if (res && authdebug) {
|
||||
ast_log(LOG_WARNING, "No RSA public keys on file matched incoming call\n");
|
||||
}
|
||||
} else if (p->authmethods & IAX_AUTH_MD5) {
|
||||
struct MD5Context md5;
|
||||
unsigned char digest[16];
|
||||
|
@ -8285,19 +8226,12 @@ static int authenticate_verify(struct chan_iax2_pvt *p, struct iax_ies *ies)
|
|||
sprintf(requeststr + (x << 1), "%02hhx", digest[x]); /* safe */
|
||||
if (!strcasecmp(requeststr, md5secret)) {
|
||||
res = 0;
|
||||
p->eff_auth_method = IAX_AUTH_MD5;
|
||||
break;
|
||||
} else if (authdebug) {
|
||||
ast_log(LOG_WARNING, "MD5 secret mismatch\n");
|
||||
}
|
||||
}
|
||||
} else if (p->authmethods & IAX_AUTH_PLAINTEXT) {
|
||||
if (!strcmp(secret, p->secret)) {
|
||||
if (!strcmp(secret, p->secret))
|
||||
res = 0;
|
||||
p->eff_auth_method = IAX_AUTH_PLAINTEXT;
|
||||
} else if (authdebug) {
|
||||
ast_log(LOG_WARNING, "Plaintext secret mismatch\n");
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -8472,25 +8406,22 @@ static int authenticate(const char *challenge, const char *secret, const char *k
|
|||
if (!ast_strlen_zero(keyn)) {
|
||||
if (!(authmethods & IAX_AUTH_RSA)) {
|
||||
if (ast_strlen_zero(secret)) {
|
||||
ast_log(LOG_WARNING, "Asked to authenticate to %s with an RSA key, but they don't allow RSA authentication\n", ast_sockaddr_stringify_addr(addr));
|
||||
ast_log(LOG_NOTICE, "Asked to authenticate to %s with an RSA key, but they don't allow RSA authentication\n", ast_sockaddr_stringify_addr(addr));
|
||||
}
|
||||
} else if (ast_strlen_zero(challenge)) {
|
||||
ast_log(LOG_WARNING, "No challenge provided for RSA authentication to %s\n", ast_sockaddr_stringify_addr(addr));
|
||||
ast_log(LOG_NOTICE, "No challenge provided for RSA authentication to %s\n", ast_sockaddr_stringify_addr(addr));
|
||||
} else {
|
||||
char sig[256];
|
||||
struct ast_key *key;
|
||||
key = ast_key_get(keyn, AST_KEY_PRIVATE);
|
||||
if (!key) {
|
||||
ast_log(LOG_WARNING, "Unable to find private key '%s'\n", keyn);
|
||||
ast_log(LOG_NOTICE, "Unable to find private key '%s'\n", keyn);
|
||||
} else {
|
||||
if (ast_sign(key, (char*)challenge, sig)) {
|
||||
ast_log(LOG_WARNING, "Unable to sign challenge with key\n");
|
||||
ast_log(LOG_NOTICE, "Unable to sign challenge with key\n");
|
||||
res = -1;
|
||||
} else {
|
||||
iax_ie_append_str(ied, IAX_IE_RSA_RESULT, sig);
|
||||
if (pvt) {
|
||||
pvt->eff_auth_method = IAX_AUTH_RSA;
|
||||
}
|
||||
res = 0;
|
||||
}
|
||||
}
|
||||
|
@ -8523,15 +8454,11 @@ static int authenticate(const char *challenge, const char *secret, const char *k
|
|||
sprintf(digres + (x << 1), "%02hhx", digest[x]); /* safe */
|
||||
if (pvt) {
|
||||
build_encryption_keys(digest, pvt);
|
||||
pvt->eff_auth_method = IAX_AUTH_MD5;
|
||||
}
|
||||
iax_ie_append_str(ied, IAX_IE_MD5_RESULT, digres);
|
||||
res = 0;
|
||||
} else if (authmethods & IAX_AUTH_PLAINTEXT) {
|
||||
iax_ie_append_str(ied, IAX_IE_PASSWORD, secret);
|
||||
if (pvt) {
|
||||
pvt->eff_auth_method = IAX_AUTH_PLAINTEXT;
|
||||
}
|
||||
res = 0;
|
||||
} else
|
||||
ast_log(LOG_WARNING, "No way to send secret to peer '%s' (their methods: %d)\n", ast_sockaddr_stringify_addr(addr), authmethods);
|
||||
|
@ -11373,7 +11300,7 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
}
|
||||
if (authenticate_verify(iaxs[fr->callno], &ies)) {
|
||||
if (authdebug)
|
||||
ast_log(LOG_WARNING, "Host %s failed to authenticate as %s\n", ast_sockaddr_stringify(&addr),
|
||||
ast_log(LOG_NOTICE, "Host %s failed to authenticate as %s\n", ast_sockaddr_stringify(&addr),
|
||||
iaxs[fr->callno]->username);
|
||||
memset(&ied0, 0, sizeof(ied0));
|
||||
auth_fail(fr->callno, IAX_COMMAND_REJECT);
|
||||
|
@ -11386,7 +11313,7 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
exists = 0;
|
||||
if (strcmp(iaxs[fr->callno]->exten, "TBD") && !exists) {
|
||||
if (authdebug)
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, request '%s@%s' does not exist\n",
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iaxs[fr->callno]->exten,
|
||||
iaxs[fr->callno]->context);
|
||||
|
@ -11441,12 +11368,12 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
if (!format) {
|
||||
if (authdebug) {
|
||||
if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peerformat, &peer_form_buf),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->capability, &cap_buf));
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, requested/capability '%s'/'%s' incompatible with our capability '%s'.\n",
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability '%s'/'%s' incompatible with our capability '%s'.\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peerformat, &peer_form_buf),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peercapability, &peer_buf),
|
||||
|
@ -11499,12 +11426,12 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
iax2_getformatname_multiple(iaxs[fr->callno]->peercapability & iaxs[fr->callno]->capability, &cap_buf));
|
||||
if (authdebug) {
|
||||
if (ast_test_flag64(iaxs[fr->callno], IAX_CODEC_NOCAP)) {
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested '%s' incompatible with our capability '%s'.\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peerformat, &peer_form_buf),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->capability, &cap_buf));
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Rejected connect attempt from %s, requested/capability '%s'/'%s' incompatible with our capability '%s'.\n",
|
||||
ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability '%s'/'%s' incompatible with our capability '%s'.\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peerformat, &peer_form_buf),
|
||||
iax2_getformatname_multiple(iaxs[fr->callno]->peercapability, &peer_buf),
|
||||
|
@ -11528,12 +11455,8 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
iax_ie_append_versioned_uint64(&ied1, IAX_IE_FORMAT2, 0, format);
|
||||
send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_ACCEPT, 0, ied1.buf, ied1.pos, -1);
|
||||
if (strcmp(iaxs[fr->callno]->exten, "TBD")) {
|
||||
char authmethodnames[AUTH_METHOD_NAMES_BUFSIZE];
|
||||
ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_STARTED);
|
||||
ast_verb(3, "Accepting AUTHENTICATED call from %s:\n"
|
||||
"%srequested auth methods = (%s),\n"
|
||||
"%sactual auth method = %s,\n"
|
||||
"%sencrypted = %s,\n"
|
||||
"%srequested format = %s,\n"
|
||||
"%srequested prefs = %s,\n"
|
||||
"%sactual format = %s,\n"
|
||||
|
@ -11541,12 +11464,6 @@ static int socket_process_helper(struct iax2_thread *thread)
|
|||
"%spriority = %s\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
VERBOSE_PREFIX_4,
|
||||
auth_method_names(iaxs[fr->callno]->authmethods, authmethodnames),
|
||||
VERBOSE_PREFIX_4,
|
||||
auth_method_labels[iaxs[fr->callno]->eff_auth_method],
|
||||
VERBOSE_PREFIX_4,
|
||||
IAX_CALLENCRYPTED(iaxs[fr->callno]) ? "yes" : "no",
|
||||
VERBOSE_PREFIX_4,
|
||||
iax2_getformatname(iaxs[fr->callno]->peerformat),
|
||||
VERBOSE_PREFIX_4,
|
||||
caller_pref_buf,
|
||||
|
@ -11615,7 +11532,7 @@ immediatedial:
|
|||
ast_string_field_set(iaxs[fr->callno], exten, ies.called_number ? ies.called_number : "s");
|
||||
if (!ast_exists_extension(NULL, iaxs[fr->callno]->context, iaxs[fr->callno]->exten, 1, iaxs[fr->callno]->cid_num)) {
|
||||
if (authdebug)
|
||||
ast_log(LOG_WARNING, "Rejected dial attempt from %s, request '%s@%s' does not exist\n",
|
||||
ast_log(LOG_NOTICE, "Rejected dial attempt from %s, request '%s@%s' does not exist\n",
|
||||
ast_sockaddr_stringify(&addr),
|
||||
iaxs[fr->callno]->exten,
|
||||
iaxs[fr->callno]->context);
|
||||
|
@ -14395,7 +14312,7 @@ static struct iax2_dpcache *find_cache(struct ast_channel *chan, const char *dat
|
|||
ast_log(LOG_WARNING, "Timeout waiting for %s exten %s\n", data, exten);
|
||||
}
|
||||
|
||||
if (chan && ast_check_hangup(chan)) {
|
||||
if (ast_check_hangup(chan)) {
|
||||
doabort = 1;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* 8-bit raw data
|
||||
*
|
||||
* Source: DialTone.ulaw
|
||||
*
|
||||
* Copyright (C) 1999, Mark Spencer
|
||||
*
|
||||
* Distributed under the terms of the GNU General Public License
|
||||
*
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
* \brief
|
||||
* 8-bit raw data
|
||||
*/
|
||||
|
||||
static unsigned char DialTone[] = {
|
||||
0xff, 0xab, 0x9d, 0x96, 0x91, 0x90, 0x91, 0x96, 0x9c, 0xaa,
|
||||
0xd9, 0x2f, 0x1f, 0x19, 0x15, 0x14, 0x15, 0x19, 0x1f, 0x2c,
|
||||
0x4e, 0xb9, 0xa7, 0x9f, 0x9c, 0x9b, 0x9c, 0x9f, 0xa7, 0xb3,
|
||||
0xcf, 0x47, 0x34, 0x2d, 0x2a, 0x2a, 0x2c, 0x31, 0x3a, 0x47,
|
||||
0x5f, 0xe4, 0xd8, 0xd9, 0xe9, 0x64, 0x4f, 0x49, 0x46, 0x49,
|
||||
0x58, 0xde, 0xc2, 0xb5, 0xad, 0xa8, 0xa6, 0xa6, 0xa9, 0xaf,
|
||||
0xbf, 0x56, 0x32, 0x26, 0x1e, 0x1b, 0x19, 0x1a, 0x1d, 0x24,
|
||||
0x33, 0xdd, 0xad, 0x9f, 0x98, 0x94, 0x92, 0x93, 0x97, 0x9e,
|
||||
0xac, 0xf8, 0x2c, 0x1d, 0x16, 0x11, 0xf, 0x10, 0x15, 0x1b,
|
||||
0x29, 0x55, 0xae, 0x9e, 0x97, 0x92, 0x90, 0x91, 0x95, 0x9c,
|
||||
0xa8, 0xca, 0x35, 0x22, 0x1a, 0x16, 0x15, 0x16, 0x19, 0x1f,
|
||||
0x2b, 0x47, 0xbe, 0xab, 0xa2, 0x9e, 0x9d, 0x9e, 0xa2, 0xa9,
|
||||
0xb4, 0xcc, 0x4f, 0x3a, 0x32, 0x2f, 0x2f, 0x32, 0x39, 0x41,
|
||||
0x4f, 0x67, 0xf5, 0xf5, 0x67, 0x51, 0x46, 0x3e, 0x3b, 0x3b,
|
||||
0x3f, 0x4d, 0xe2, 0xbe, 0xb0, 0xa9, 0xa4, 0xa1, 0xa1, 0xa5,
|
||||
0xab, 0xba, 0x64, 0x33, 0x25, 0x1d, 0x19, 0x17, 0x18, 0x1b,
|
||||
0x20, 0x2e, 0x72, 0xae, 0x9f, 0x98, 0x94, 0x91, 0x92, 0x96,
|
||||
0x9c, 0xa9, 0xd4, 0x2f, 0x1e, 0x17, 0x11, 0xf, 0x10, 0x14,
|
||||
0x1a, 0x26, 0x48, 0xb2, 0x9f, 0x98, 0x93, 0x91, 0x92, 0x95,
|
||||
0x9b, 0xa7, 0xc1, 0x3a, 0x25, 0x1c, 0x18, 0x16, 0x17, 0x1a,
|
||||
0x1f, 0x2b, 0x42, 0xc6, 0xae, 0xa6, 0xa0, 0x9f, 0xa0, 0xa5,
|
||||
0xab, 0xb6, 0xcb, 0x5a, 0x40, 0x39, 0x36, 0x37, 0x3b, 0x43,
|
||||
0x4e, 0x60, 0x7b, 0x7c, 0x60, 0x4e, 0x41, 0x3a, 0x34, 0x32,
|
||||
0x33, 0x39, 0x45, 0xed, 0xbd, 0xae, 0xa6, 0xa0, 0x9e, 0x9e,
|
||||
0xa0, 0xa8, 0xb4, 0xef, 0x34, 0x24, 0x1c, 0x18, 0x16, 0x16,
|
||||
0x19, 0x1e, 0x2b, 0x54, 0xb1, 0x9f, 0x98, 0x93, 0x91, 0x91,
|
||||
0x95, 0x9b, 0xa6, 0xc6, 0x33, 0x1f, 0x17, 0x12, 0xf, 0x10,
|
||||
0x13, 0x1a, 0x24, 0x3e, 0xb8, 0xa2, 0x99, 0x94, 0x92, 0x92,
|
||||
0x96, 0x9b, 0xa6, 0xbc, 0x40, 0x29, 0x1e, 0x1a, 0x18, 0x19,
|
||||
0x1b, 0x20, 0x2b, 0x3f, 0xcf, 0xb3, 0xa9, 0xa5, 0xa3, 0xa4,
|
||||
0xa8, 0xae, 0xb9, 0xcc, 0x67, 0x49, 0x40, 0x3f, 0x42, 0x4a,
|
||||
0x59, 0x79, 0xe5, 0xe4, 0x7a, 0x54, 0x43, 0x39, 0x31, 0x2d,
|
||||
0x2c, 0x2d, 0x32, 0x3e, 0x71, 0xbd, 0xad, 0xa4, 0x9e, 0x9c,
|
||||
0x9c, 0x9e, 0xa4, 0xaf, 0xd7, 0x36, 0x24, 0x1c, 0x17, 0x15,
|
||||
0x15, 0x17, 0x1d, 0x28, 0x47, 0xb5, 0xa1, 0x98, 0x93, 0x90,
|
||||
0x90, 0x93, 0x99, 0xa4, 0xbd, 0x38, 0x21, 0x18, 0x13, 0x10,
|
||||
0x10, 0x13, 0x19, 0x22, 0x39, 0xbe, 0xa5, 0x9b, 0x96, 0x93,
|
||||
0x93, 0x96, 0x9b, 0xa5, 0xb9, 0x4a, 0x2c, 0x20, 0x1c, 0x1a,
|
||||
0x1a, 0x1d, 0x22, 0x2c, 0x3d, 0xdc, 0xb8, 0xad, 0xa9, 0xa8,
|
||||
0xa9, 0xac, 0xb2, 0xbd, 0xce, 0x78, 0x54, 0x4d, 0x4f, 0x5a,
|
||||
0xff, 0xda, 0xcf, 0xcd, 0xd4, 0xf8, 0x4e, 0x3d, 0x32, 0x2c,
|
||||
0x29, 0x28, 0x29, 0x2d, 0x38, 0x5c, 0xbd, 0xac, 0xa2, 0x9d,
|
||||
0x9a, 0x9a, 0x9c, 0xa0, 0xac, 0xca, 0x39, 0x25, 0x1b, 0x16,
|
||||
0x13, 0x13, 0x16, 0x1b, 0x25, 0x3e, 0xb9, 0xa2, 0x99, 0x93,
|
||||
0x90, 0x90, 0x93, 0x98, 0xa1, 0xb8, 0x3d, 0x24, 0x19, 0x13,
|
||||
0x10, 0x10, 0x13, 0x18, 0x21, 0x35, 0xc7, 0xa8, 0x9d, 0x97,
|
||||
0x95, 0x95, 0x97, 0x9c, 0xa4, 0xb6, 0x57, 0x2f, 0x24, 0x1e,
|
||||
0x1c, 0x1c, 0x1e, 0x24, 0x2d, 0x3d, 0xf1, 0xbe, 0xb2, 0xad,
|
||||
0xac, 0xad, 0xb1, 0xb9, 0xc3, 0xd4, 0xfa, 0x64, 0x65, 0xf9,
|
||||
0xd9, 0xca, 0xc2, 0xbf, 0xc0, 0xc9, 0xe7, 0x4c, 0x39, 0x2e,
|
||||
0x28, 0x24, 0x23, 0x25, 0x29, 0x33, 0x4f, 0xbf, 0xab, 0xa0,
|
||||
0x9b, 0x99, 0x98, 0x9a, 0x9e, 0xa9, 0xc0, 0x3c, 0x26, 0x1b,
|
||||
0x16, 0x12, 0x12, 0x14, 0x19, 0x22, 0x38, 0xbe, 0xa4, 0x9a,
|
||||
0x93, 0x90, 0x8f, 0x92, 0x97, 0x9f, 0xb3, 0x46, 0x26, 0x1b,
|
||||
0x15, 0x11, 0x11, 0x13, 0x18, 0x1f, 0x31, 0xd4, 0xab, 0x9e,
|
||||
0x99, 0x96, 0x96, 0x98, 0x9c, 0xa4, 0xb4, 0x6f, 0x34, 0x28,
|
||||
0x20, 0x1e, 0x1e, 0x20, 0x26, 0x2e, 0x3d, 0x6d, 0xc5, 0xb9,
|
||||
0xb3, 0xb2, 0xb4, 0xba, 0xc1, 0xcd, 0xe0, 0xfc, 0xfb, 0xe0,
|
||||
0xce, 0xc3, 0xbb, 0xb7, 0xb6, 0xb9, 0xc0, 0xda, 0x4b, 0x36,
|
||||
0x2b, 0x25, 0x20, 0x1f, 0x20, 0x26, 0x2e, 0x46, 0xc2, 0xab,
|
||||
0x9f, 0x9a, 0x97, 0x96, 0x98, 0x9c, 0xa5, 0xba, 0x41, 0x27,
|
||||
0x1b, 0x15, 0x12, 0x11, 0x13, 0x18, 0x1f, 0x32, 0xc8, 0xa6,
|
||||
0x9a, 0x94, 0x90, 0x8f, 0x91, 0x97, 0x9e, 0xaf, 0x54, 0x29,
|
||||
0x1c, 0x16, 0x12, 0x11, 0x14, 0x18, 0x1f, 0x2e, 0xf2, 0xae,
|
||||
0xa0, 0x9b, 0x98, 0x97, 0x99, 0x9d, 0xa5, 0xb3, 0xe4, 0x3a,
|
||||
0x2b, 0x25, 0x21, 0x21, 0x24, 0x29, 0x30, 0x3e, 0x62, 0xcd,
|
||||
0xbf, 0xbb, 0xbb, 0xbe, 0xc6, 0xd1, 0xe7, 0x76, 0x75, 0xe7,
|
||||
0xcf, 0xc1, 0xb9, 0xb2, 0xaf, 0xaf, 0xb2, 0xba, 0xcf, 0x4c,
|
||||
0x34, 0x29, 0x22, 0x1e, 0x1d, 0x1e, 0x22, 0x2b, 0x3e, 0xc7,
|
||||
0xab, 0x9f, 0x99, 0x96, 0x95, 0x96, 0x9a, 0xa2, 0xb5, 0x4a,
|
||||
0x28, 0x1c, 0x15, 0x11, 0x10, 0x12, 0x17, 0x1e, 0x2e, 0xd5,
|
||||
0xa9, 0x9b, 0x95, 0x90, 0x8f, 0x91, 0x96, 0x9d, 0xac, 0x78,
|
||||
0x2c, 0x1e, 0x17, 0x13, 0x12, 0x14, 0x18, 0x1f, 0x2d, 0x5d,
|
||||
0xb3, 0xa4, 0x9d, 0x9a, 0x99, 0x9b, 0x9e, 0xa6, 0xb2, 0xd6,
|
||||
0x3f, 0x2f, 0x29, 0x26, 0x26, 0x28, 0x2d, 0x35, 0x42, 0x5e,
|
||||
0xd8, 0xca, 0xc6, 0xc9, 0xcf, 0xe4, 0x69, 0x59, 0x58, 0x64,
|
||||
0xdf, 0xc7, 0xba, 0xb1, 0xac, 0xaa, 0xaa, 0xad, 0xb4, 0xc7,
|
||||
0x4f, 0x33, 0x27, 0x1f, 0x1c, 0x1b, 0x1c, 0x1f, 0x27, 0x39,
|
||||
0xce, 0xac, 0x9f, 0x99, 0x95, 0x94, 0x95, 0x99, 0x9f, 0xaf,
|
||||
0x59, 0x2a, 0x1c, 0x16, 0x11, 0x10, 0x11, 0x16, 0x1d, 0x2b,
|
||||
0xff, 0xab, 0x9d, 0x96, 0x91, 0x90, 0x91, 0x96, 0x9c, 0xaa,
|
||||
0xd9, 0x2f, 0x1f, 0x19, 0x15, 0x14, 0x15, 0x19, 0x1f, 0x2c,
|
||||
0x4e, 0xb9, 0xa7, 0x9f, 0x9c, 0x9b, 0x9c, 0x9f, 0xa7, 0xb3,
|
||||
0xcf, 0x47, 0x34, 0x2d, 0x2a, 0x2a, 0x2c, 0x31, 0x3a, 0x47,
|
||||
0x5f, 0xe4, 0xd8, 0xd9, 0xe9, 0x64, 0x4f, 0x49, 0x46, 0x49,
|
||||
0x58, 0xde, 0xc2, 0xb5, 0xad, 0xa8, 0xa6, 0xa6, 0xa9, 0xaf,
|
||||
0xbf, 0x56, 0x32, 0x26, 0x1e, 0x1b, 0x19, 0x1a, 0x1d, 0x24,
|
||||
0x33, 0xdd, 0xad, 0x9f, 0x98, 0x94, 0x92, 0x93, 0x97, 0x9e,
|
||||
0xac, 0xf8, 0x2c, 0x1d, 0x16, 0x11, 0xf, 0x10, 0x15, 0x1b,
|
||||
0x29, 0x55, 0xae, 0x9e, 0x97, 0x92, 0x90, 0x91, 0x95, 0x9c,
|
||||
0xa8, 0xca, 0x35, 0x22, 0x1a, 0x16, 0x15, 0x16, 0x19, 0x1f,
|
||||
0x2b, 0x47, 0xbe, 0xab, 0xa2, 0x9e, 0x9d, 0x9e, 0xa2, 0xa9,
|
||||
0xb4, 0xcc, 0x4f, 0x3a, 0x32, 0x2f, 0x2f, 0x32, 0x39, 0x41,
|
||||
0x4f, 0x67, 0xf5, 0xf5, 0x67, 0x51, 0x46, 0x3e, 0x3b, 0x3b,
|
||||
0x3f, 0x4d, 0xe2, 0xbe, 0xb0, 0xa9, 0xa4, 0xa1, 0xa1, 0xa5,
|
||||
0xab, 0xba, 0x64, 0x33, 0x25, 0x1d, 0x19, 0x17, 0x18, 0x1b,
|
||||
0x20, 0x2e, 0x72, 0xae, 0x9f, 0x98, 0x94, 0x91, 0x92, 0x96,
|
||||
0x9c, 0xa9, 0xd4, 0x2f, 0x1e, 0x17, 0x11, 0xf, 0x10, 0x14,
|
||||
0x1a, 0x26, 0x48, 0xb2, 0x9f, 0x98, 0x93, 0x91, 0x92, 0x95,
|
||||
0x9b, 0xa7, 0xc1, 0x3a, 0x25, 0x1c, 0x18, 0x16, 0x17, 0x1a,
|
||||
0x1f, 0x2b, 0x42, 0xc6, 0xae, 0xa6, 0xa0, 0x9f, 0xa0, 0xa5,
|
||||
0xab, 0xb6, 0xcb, 0x5a, 0x40, 0x39, 0x36, 0x37, 0x3b, 0x43,
|
||||
0x4e, 0x60, 0x7b, 0x7c, 0x60, 0x4e, 0x41, 0x3a, 0x34, 0x32,
|
||||
0x33, 0x39, 0x45, 0xed, 0xbd, 0xae, 0xa6, 0xa0, 0x9e, 0x9e,
|
||||
0xa0, 0xa8, 0xb4, 0xef, 0x34, 0x24, 0x1c, 0x18, 0x16, 0x16,
|
||||
0x19, 0x1e, 0x2b, 0x54, 0xb1, 0x9f, 0x98, 0x93, 0x91, 0x91,
|
||||
0x95, 0x9b, 0xa6, 0xc6, 0x33, 0x1f, 0x17, 0x12, 0xf, 0x10,
|
||||
0x13, 0x1a, 0x24, 0x3e, 0xb8, 0xa2, 0x99, 0x94, 0x92, 0x92,
|
||||
0x96, 0x9b, 0xa6, 0xbc, 0x40, 0x29, 0x1e, 0x1a, 0x18, 0x19,
|
||||
0x1b, 0x20, 0x2b, 0x3f, 0xcf, 0xb3, 0xa9, 0xa5, 0xa3, 0xa4,
|
||||
0xa8, 0xae, 0xb9, 0xcc, 0x67, 0x49, 0x40, 0x3f, 0x42, 0x4a,
|
||||
0x59, 0x79, 0xe5, 0xe4, 0x7a, 0x54, 0x43, 0x39, 0x31, 0x2d,
|
||||
0x2c, 0x2d, 0x32, 0x3e, 0x71, 0xbd, 0xad, 0xa4, 0x9e, 0x9c,
|
||||
0x9c, 0x9e, 0xa4, 0xaf, 0xd7, 0x36, 0x24, 0x1c, 0x17, 0x15,
|
||||
0x15, 0x17, 0x1d, 0x28, 0x47, 0xb5, 0xa1, 0x98, 0x93, 0x90,
|
||||
0x90, 0x93, 0x99, 0xa4, 0xbd, 0x38, 0x21, 0x18, 0x13, 0x10,
|
||||
0x10, 0x13, 0x19, 0x22, 0x39, 0xbe, 0xa5, 0x9b, 0x96, 0x93,
|
||||
0x93, 0x96, 0x9b, 0xa5, 0xb9, 0x4a, 0x2c, 0x20, 0x1c, 0x1a,
|
||||
0x1a, 0x1d, 0x22, 0x2c, 0x3d, 0xdc, 0xb8, 0xad, 0xa9, 0xa8,
|
||||
0xa9, 0xac, 0xb2, 0xbd, 0xce, 0x78, 0x54, 0x4d, 0x4f, 0x5a,
|
||||
0xff, 0xda, 0xcf, 0xcd, 0xd4, 0xf8, 0x4e, 0x3d, 0x32, 0x2c,
|
||||
0x29, 0x28, 0x29, 0x2d, 0x38, 0x5c, 0xbd, 0xac, 0xa2, 0x9d,
|
||||
0x9a, 0x9a, 0x9c, 0xa0, 0xac, 0xca, 0x39, 0x25, 0x1b, 0x16,
|
||||
0x13, 0x13, 0x16, 0x1b, 0x25, 0x3e, 0xb9, 0xa2, 0x99, 0x93,
|
||||
0x90, 0x90, 0x93, 0x98, 0xa1, 0xb8, 0x3d, 0x24, 0x19, 0x13,
|
||||
0x10, 0x10, 0x13, 0x18, 0x21, 0x35, 0xc7, 0xa8, 0x9d, 0x97,
|
||||
0x95, 0x95, 0x97, 0x9c, 0xa4, 0xb6, 0x57, 0x2f, 0x24, 0x1e,
|
||||
0x1c, 0x1c, 0x1e, 0x24, 0x2d, 0x3d, 0xf1, 0xbe, 0xb2, 0xad,
|
||||
0xac, 0xad, 0xb1, 0xb9, 0xc3, 0xd4, 0xfa, 0x64, 0x65, 0xf9,
|
||||
0xd9, 0xca, 0xc2, 0xbf, 0xc0, 0xc9, 0xe7, 0x4c, 0x39, 0x2e,
|
||||
0x28, 0x24, 0x23, 0x25, 0x29, 0x33, 0x4f, 0xbf, 0xab, 0xa0,
|
||||
0x9b, 0x99, 0x98, 0x9a, 0x9e, 0xa9, 0xc0, 0x3c, 0x26, 0x1b,
|
||||
0x16, 0x12, 0x12, 0x14, 0x19, 0x22, 0x38, 0xbe, 0xa4, 0x9a,
|
||||
0x93, 0x90, 0x8f, 0x92, 0x97, 0x9f, 0xb3, 0x46, 0x26, 0x1b,
|
||||
0x15, 0x11, 0x11, 0x13, 0x18, 0x1f, 0x31, 0xd4, 0xab, 0x9e,
|
||||
0x99, 0x96, 0x96, 0x98, 0x9c, 0xa4, 0xb4, 0x6f, 0x34, 0x28,
|
||||
0x20, 0x1e, 0x1e, 0x20, 0x26, 0x2e, 0x3d, 0x6d, 0xc5, 0xb9,
|
||||
0xb3, 0xb2, 0xb4, 0xba, 0xc1, 0xcd, 0xe0, 0xfc, 0xfb, 0xe0,
|
||||
0xce, 0xc3, 0xbb, 0xb7, 0xb6, 0xb9, 0xc0, 0xda, 0x4b, 0x36,
|
||||
0x2b, 0x25, 0x20, 0x1f, 0x20, 0x26, 0x2e, 0x46, 0xc2, 0xab,
|
||||
0x9f, 0x9a, 0x97, 0x96, 0x98, 0x9c, 0xa5, 0xba, 0x41, 0x27,
|
||||
0x1b, 0x15, 0x12, 0x11, 0x13, 0x18, 0x1f, 0x32, 0xc8, 0xa6,
|
||||
0x9a, 0x94, 0x90, 0x8f, 0x91, 0x97, 0x9e, 0xaf, 0x54, 0x29,
|
||||
0x1c, 0x16, 0x12, 0x11, 0x14, 0x18, 0x1f, 0x2e, 0xf2, 0xae,
|
||||
0xa0, 0x9b, 0x98, 0x97, 0x99, 0x9d, 0xa5, 0xb3, 0xe4, 0x3a,
|
||||
0x2b, 0x25, 0x21, 0x21, 0x24, 0x29, 0x30, 0x3e, 0x62, 0xcd,
|
||||
0xbf, 0xbb, 0xbb, 0xbe, 0xc6, 0xd1, 0xe7, 0x76, 0x75, 0xe7,
|
||||
0xcf, 0xc1, 0xb9, 0xb2, 0xaf, 0xaf, 0xb2, 0xba, 0xcf, 0x4c,
|
||||
0x34, 0x29, 0x22, 0x1e, 0x1d, 0x1e, 0x22, 0x2b, 0x3e, 0xc7,
|
||||
0xab, 0x9f, 0x99, 0x96, 0x95, 0x96, 0x9a, 0xa2, 0xb5, 0x4a,
|
||||
0x28, 0x1c, 0x15, 0x11, 0x10, 0x12, 0x17, 0x1e, 0x2e, 0xd5,
|
||||
0xa9, 0x9b, 0x95, 0x90, 0x8f, 0x91, 0x96, 0x9d, 0xac, 0x78,
|
||||
0x2c, 0x1e, 0x17, 0x13, 0x12, 0x14, 0x18, 0x1f, 0x2d, 0x5d,
|
||||
0xb3, 0xa4, 0x9d, 0x9a, 0x99, 0x9b, 0x9e, 0xa6, 0xb2, 0xd6,
|
||||
0x3f, 0x2f, 0x29, 0x26, 0x26, 0x28, 0x2d, 0x35, 0x42, 0x5e,
|
||||
0xd8, 0xca, 0xc6, 0xc9, 0xcf, 0xe4, 0x69, 0x59, 0x58, 0x64,
|
||||
0xdf, 0xc7, 0xba, 0xb1, 0xac, 0xaa, 0xaa, 0xad, 0xb4, 0xc7,
|
||||
0x4f, 0x33, 0x27, 0x1f, 0x1c, 0x1b, 0x1c, 0x1f, 0x27, 0x39,
|
||||
0xce, 0xac, 0x9f, 0x99, 0x95, 0x94, 0x95, 0x99, 0x9f, 0xaf,
|
||||
0x59, 0x2a, 0x1c, 0x16, 0x11, 0x10, 0x11, 0x16, 0x1d, 0x2b,
|
||||
0xff, 0xab, 0x9d, 0x96, 0x91, 0x90, 0x91, 0x96, 0x9c, 0xaa,
|
||||
0xd9, 0x2f, 0x1f, 0x19, 0x15, 0x14, 0x15, 0x19, 0x1f, 0x2c,
|
||||
0x4e, 0xb9, 0xa7, 0x9f, 0x9c, 0x9b, 0x9c, 0x9f, 0xa7, 0xb3,
|
||||
0xcf, 0x47, 0x34, 0x2d, 0x2a, 0x2a, 0x2c, 0x31, 0x3a, 0x47,
|
||||
0x5f, 0xe4, 0xd8, 0xd9, 0xe9, 0x64, 0x4f, 0x49, 0x46, 0x49,
|
||||
0x58, 0xde, 0xc2, 0xb5, 0xad, 0xa8, 0xa6, 0xa6, 0xa9, 0xaf,
|
||||
0xbf, 0x56, 0x32, 0x26, 0x1e, 0x1b, 0x19, 0x1a, 0x1d, 0x24,
|
||||
0x33, 0xdd, 0xad, 0x9f, 0x98, 0x94, 0x92, 0x93, 0x97, 0x9e,
|
||||
0xac, 0xf8, 0x2c, 0x1d, 0x16, 0x11, 0xf, 0x10, 0x15, 0x1b,
|
||||
0x29, 0x55, 0xae, 0x9e, 0x97, 0x92, 0x90, 0x91, 0x95, 0x9c,
|
||||
0xa8, 0xca, 0x35, 0x22, 0x1a, 0x16, 0x15, 0x16, 0x19, 0x1f,
|
||||
0x2b, 0x47, 0xbe, 0xab, 0xa2, 0x9e, 0x9d, 0x9e, 0xa2, 0xa9,
|
||||
0xb4, 0xcc, 0x4f, 0x3a, 0x32, 0x2f, 0x2f, 0x32, 0x39, 0x41,
|
||||
0x4f, 0x67, 0xf5, 0xf5, 0x67, 0x51, 0x46, 0x3e, 0x3b, 0x3b,
|
||||
0x3f, 0x4d, 0xe2, 0xbe, 0xb0, 0xa9, 0xa4, 0xa1, 0xa1, 0xa5,
|
||||
0xab, 0xba, 0x64, 0x33, 0x25, 0x1d, 0x19, 0x17, 0x18, 0x1b,
|
||||
0x20, 0x2e, 0x72, 0xae, 0x9f, 0x98, 0x94, 0x91, 0x92, 0x96,
|
||||
0x9c, 0xa9, 0xd4, 0x2f, 0x1e, 0x17, 0x11, 0xf, 0x10, 0x14,
|
||||
0x1a, 0x26, 0x48, 0xb2, 0x9f, 0x98, 0x93, 0x91, 0x92, 0x95,
|
||||
0x9b, 0xa7, 0xc1, 0x3a, 0x25, 0x1c, 0x18, 0x16, 0x17, 0x1a,
|
||||
0x1f, 0x2b, 0x42, 0xc6, 0xae, 0xa6, 0xa0, 0x9f, 0xa0, 0xa5,
|
||||
0xab, 0xb6, 0xcb, 0x5a, 0x40, 0x39, 0x36, 0x37, 0x3b, 0x43,
|
||||
0x4e, 0x60, 0x7b, 0x7c, 0x60, 0x4e, 0x41, 0x3a, 0x34, 0x32,
|
||||
0x33, 0x39, 0x45, 0xed, 0xbd, 0xae, 0xa6, 0xa0, 0x9e, 0x9e,
|
||||
0xa0, 0xa8, 0xb4, 0xef, 0x34, 0x24, 0x1c, 0x18, 0x16, 0x16,
|
||||
0x19, 0x1e, 0x2b, 0x54, 0xb1, 0x9f, 0x98, 0x93, 0x91, 0x91,
|
||||
0x95, 0x9b, 0xa6, 0xc6, 0x33, 0x1f, 0x17, 0x12, 0xf, 0x10,
|
||||
0x13, 0x1a, 0x24, 0x3e, 0xb8, 0xa2, 0x99, 0x94, 0x92, 0x92,
|
||||
0x96, 0x9b, 0xa6, 0xbc, 0x40, 0x29, 0x1e, 0x1a, 0x18, 0x19,
|
||||
0x1b, 0x20, 0x2b, 0x3f, 0xcf, 0xb3, 0xa9, 0xa5, 0xa3, 0xa4,
|
||||
0xa8, 0xae, 0xb9, 0xcc, 0x67, 0x49, 0x40, 0x3f, 0x42, 0x4a,
|
||||
0x59, 0x79, 0xe5, 0xe4, 0x7a, 0x54, 0x43, 0x39, 0x31, 0x2d,
|
||||
0x2c, 0x2d, 0x32, 0x3e, 0x71, 0xbd, 0xad, 0xa4, 0x9e, 0x9c,
|
||||
0x9c, 0x9e, 0xa4, 0xaf, 0xd7, 0x36, 0x24, 0x1c, 0x17, 0x15,
|
||||
0x15, 0x17, 0x1d, 0x28, 0x47, 0xb5, 0xa1, 0x98, 0x93, 0x90,
|
||||
0x90, 0x93, 0x99, 0xa4, 0xbd, 0x38, 0x21, 0x18, 0x13, 0x10,
|
||||
0x10, 0x13, 0x19, 0x22, 0x39, 0xbe, 0xa5, 0x9b, 0x96, 0x93,
|
||||
0x93, 0x96, 0x9b, 0xa5, 0xb9, 0x4a, 0x2c, 0x20, 0x1c, 0x1a,
|
||||
0x1a, 0x1d, 0x22, 0x2c, 0x3d, 0xdc, 0xb8, 0xad, 0xa9, 0xa8,
|
||||
0xa9, 0xac, 0xb2, 0xbd, 0xce, 0x78, 0x54, 0x4d, 0x4f, 0x5a,
|
||||
0xff, 0xda, 0xcf, 0xcd, 0xd4, 0xf8, 0x4e, 0x3d, 0x32, 0x2c,
|
||||
0x29, 0x28, 0x29, 0x2d, 0x38, 0x5c, 0xbd, 0xac, 0xa2, 0x9d,
|
||||
0x9a, 0x9a, 0x9c, 0xa0, 0xac, 0xca, 0x39, 0x25, 0x1b, 0x16,
|
||||
0x13, 0x13, 0x16, 0x1b, 0x25, 0x3e, 0xb9, 0xa2, 0x99, 0x93,
|
||||
0x90, 0x90, 0x93, 0x98, 0xa1, 0xb8, 0x3d, 0x24, 0x19, 0x13,
|
||||
0x10, 0x10, 0x13, 0x18, 0x21, 0x35, 0xc7, 0xa8, 0x9d, 0x97,
|
||||
0x95, 0x95, 0x97, 0x9c, 0xa4, 0xb6, 0x57, 0x2f, 0x24, 0x1e,
|
||||
0x1c, 0x1c, 0x1e, 0x24, 0x2d, 0x3d, 0xf1, 0xbe, 0xb2, 0xad,
|
||||
0xac, 0xad, 0xb1, 0xb9, 0xc3, 0xd4, 0xfa, 0x64, 0x65, 0xf9,
|
||||
0xd9, 0xca, 0xc2, 0xbf, 0xc0, 0xc9, 0xe7, 0x4c, 0x39, 0x2e,
|
||||
0x28, 0x24, 0x23, 0x25, 0x29, 0x33, 0x4f, 0xbf, 0xab, 0xa0,
|
||||
0x9b, 0x99, 0x98, 0x9a, 0x9e, 0xa9, 0xc0, 0x3c, 0x26, 0x1b,
|
||||
0x16, 0x12, 0x12, 0x14, 0x19, 0x22, 0x38, 0xbe, 0xa4, 0x9a,
|
||||
0x93, 0x90, 0x8f, 0x92, 0x97, 0x9f, 0xb3, 0x46, 0x26, 0x1b,
|
||||
0x15, 0x11, 0x11, 0x13, 0x18, 0x1f, 0x31, 0xd4, 0xab, 0x9e,
|
||||
0x99, 0x96, 0x96, 0x98, 0x9c, 0xa4, 0xb4, 0x6f, 0x34, 0x28,
|
||||
0x20, 0x1e, 0x1e, 0x20, 0x26, 0x2e, 0x3d, 0x6d, 0xc5, 0xb9,
|
||||
0xb3, 0xb2, 0xb4, 0xba, 0xc1, 0xcd, 0xe0, 0xfc, 0xfb, 0xe0,
|
||||
0xce, 0xc3, 0xbb, 0xb7, 0xb6, 0xb9, 0xc0, 0xda, 0x4b, 0x36,
|
||||
0x2b, 0x25, 0x20, 0x1f, 0x20, 0x26, 0x2e, 0x46, 0xc2, 0xab,
|
||||
0x9f, 0x9a, 0x97, 0x96, 0x98, 0x9c, 0xa5, 0xba, 0x41, 0x27,
|
||||
0x1b, 0x15, 0x12, 0x11, 0x13, 0x18, 0x1f, 0x32, 0xc8, 0xa6,
|
||||
0x9a, 0x94, 0x90, 0x8f, 0x91, 0x97, 0x9e, 0xaf, 0x54, 0x29,
|
||||
0x1c, 0x16, 0x12, 0x11, 0x14, 0x18, 0x1f, 0x2e, 0xf2, 0xae,
|
||||
0xa0, 0x9b, 0x98, 0x97, 0x99, 0x9d, 0xa5, 0xb3, 0xe4, 0x3a,
|
||||
0x2b, 0x25, 0x21, 0x21, 0x24, 0x29, 0x30, 0x3e, 0x62, 0xcd,
|
||||
0xbf, 0xbb, 0xbb, 0xbe, 0xc6, 0xd1, 0xe7, 0x76, 0x75, 0xe7,
|
||||
0xcf, 0xc1, 0xb9, 0xb2, 0xaf, 0xaf, 0xb2, 0xba, 0xcf, 0x4c,
|
||||
0x34, 0x29, 0x22, 0x1e, 0x1d, 0x1e, 0x22, 0x2b, 0x3e, 0xc7,
|
||||
0xab, 0x9f, 0x99, 0x96, 0x95, 0x96, 0x9a, 0xa2, 0xb5, 0x4a,
|
||||
0x28, 0x1c, 0x15, 0x11, 0x10, 0x12, 0x17, 0x1e, 0x2e, 0xd5,
|
||||
0xa9, 0x9b, 0x95, 0x90, 0x8f, 0x91, 0x96, 0x9d, 0xac, 0x78,
|
||||
0x2c, 0x1e, 0x17, 0x13, 0x12, 0x14, 0x18, 0x1f, 0x2d, 0x5d,
|
||||
0xb3, 0xa4, 0x9d, 0x9a, 0x99, 0x9b, 0x9e, 0xa6, 0xb2, 0xd6,
|
||||
0x3f, 0x2f, 0x29, 0x26, 0x26, 0x28, 0x2d, 0x35, 0x42, 0x5e,
|
||||
0xd8, 0xca, 0xc6, 0xc9, 0xcf, 0xe4, 0x69, 0x59, 0x58, 0x64,
|
||||
0xdf, 0xc7, 0xba, 0xb1, 0xac, 0xaa, 0xaa, 0xad, 0xb4, 0xc7,
|
||||
0x4f, 0x33, 0x27, 0x1f, 0x1c, 0x1b, 0x1c, 0x1f, 0x27, 0x39,
|
||||
0xce, 0xac, 0x9f, 0x99, 0x95, 0x94, 0x95, 0x99, 0x9f, 0xaf,
|
||||
0x59, 0x2a, 0x1c, 0x16, 0x11, 0x10, 0x11, 0x16, 0x1d, 0x2b };
|
|
@ -167,14 +167,6 @@ static struct ast_sip_session_supplement chan_pjsip_ack_supplement = {
|
|||
.incoming_request = chan_pjsip_incoming_ack,
|
||||
};
|
||||
|
||||
static int chan_pjsip_incoming_prack(struct ast_sip_session *session, struct pjsip_rx_data *rdata);
|
||||
|
||||
static struct ast_sip_session_supplement chan_pjsip_prack_supplement = {
|
||||
.method = "PRACK",
|
||||
.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,
|
||||
.incoming_request = chan_pjsip_incoming_prack,
|
||||
};
|
||||
|
||||
/*! \brief Function called by RTP engine to get local audio RTP peer */
|
||||
static enum ast_rtp_glue_result chan_pjsip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
|
||||
{
|
||||
|
@ -784,7 +776,7 @@ static struct ast_frame *chan_pjsip_cng_tone_detected(struct ast_channel *ast, s
|
|||
return f;
|
||||
}
|
||||
|
||||
target_context = ast_channel_context(ast);
|
||||
target_context = S_OR(ast_channel_macrocontext(ast), ast_channel_context(ast));
|
||||
|
||||
/*
|
||||
* We need to unlock the channel here because ast_exists_extension has the
|
||||
|
@ -1572,22 +1564,13 @@ static int send_topology_change_refresh(void *data)
|
|||
{
|
||||
struct topology_change_refresh_data *refresh_data = data;
|
||||
struct ast_sip_session *session = refresh_data->session;
|
||||
enum ast_channel_state state = ast_channel_state(session->channel);
|
||||
enum ast_sip_session_refresh_method method = AST_SIP_SESSION_REFRESH_METHOD_INVITE;
|
||||
int ret;
|
||||
SCOPE_ENTER(3, "%s: %s\n", ast_sip_session_get_name(session),
|
||||
ast_str_tmp(256, ast_stream_topology_to_str(refresh_data->media_state->topology, &STR_TMP)));
|
||||
|
||||
/* See RFC 6337, especially section 3.2: If the early media SDP was sent reliably, we are allowed
|
||||
* to send UPDATEs. Only relevant for AST_STATE_RINGING and AST_STATE_RING - if the channel is UP,
|
||||
* re-INVITES can be sent.
|
||||
*/
|
||||
if (session->early_confirmed && (state == AST_STATE_RINGING || state == AST_STATE_RING)) {
|
||||
method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
|
||||
}
|
||||
|
||||
ret = ast_sip_session_refresh(session, NULL, NULL, on_topology_change_response,
|
||||
method, 1, refresh_data->media_state);
|
||||
AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1, refresh_data->media_state);
|
||||
refresh_data->media_state = NULL;
|
||||
topology_change_refresh_data_free(refresh_data);
|
||||
|
||||
|
@ -2530,15 +2513,6 @@ static int hangup(void *data)
|
|||
if (session) {
|
||||
int cause = h_data->cause;
|
||||
|
||||
if (channel->session->active_media_state &&
|
||||
channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]) {
|
||||
struct ast_sip_session_media *media =
|
||||
channel->session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
|
||||
if (media->rtp) {
|
||||
ast_rtp_instance_set_stats_vars(ast, media->rtp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* It's possible that session_terminate might cause the session to be destroyed
|
||||
* immediately so we need to keep a reference to it so we can NULL session->channel
|
||||
|
@ -2898,6 +2872,97 @@ static int chan_pjsip_sendtext(struct ast_channel *ast, const char *text)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief Convert SIP hangup causes to Asterisk hangup causes */
|
||||
static int hangup_sip2cause(int cause)
|
||||
{
|
||||
/* Possible values taken from causes.h */
|
||||
|
||||
switch(cause) {
|
||||
case 401: /* Unauthorized */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 403: /* Not found */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 404: /* Not found */
|
||||
return AST_CAUSE_UNALLOCATED;
|
||||
case 405: /* Method not allowed */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 407: /* Proxy authentication required */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 408: /* No reaction */
|
||||
return AST_CAUSE_NO_USER_RESPONSE;
|
||||
case 409: /* Conflict */
|
||||
return AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
|
||||
case 410: /* Gone */
|
||||
return AST_CAUSE_NUMBER_CHANGED;
|
||||
case 411: /* Length required */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 413: /* Request entity too large */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 414: /* Request URI too large */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 415: /* Unsupported media type */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 420: /* Bad extension */
|
||||
return AST_CAUSE_NO_ROUTE_DESTINATION;
|
||||
case 480: /* No answer */
|
||||
return AST_CAUSE_NO_ANSWER;
|
||||
case 481: /* No answer */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 482: /* Loop detected */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 483: /* Too many hops */
|
||||
return AST_CAUSE_NO_ANSWER;
|
||||
case 484: /* Address incomplete */
|
||||
return AST_CAUSE_INVALID_NUMBER_FORMAT;
|
||||
case 485: /* Ambiguous */
|
||||
return AST_CAUSE_UNALLOCATED;
|
||||
case 486: /* Busy everywhere */
|
||||
return AST_CAUSE_BUSY;
|
||||
case 487: /* Request terminated */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 488: /* No codecs approved */
|
||||
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
|
||||
case 491: /* Request pending */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 493: /* Undecipherable */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 500: /* Server internal failure */
|
||||
return AST_CAUSE_FAILURE;
|
||||
case 501: /* Call rejected */
|
||||
return AST_CAUSE_FACILITY_REJECTED;
|
||||
case 502:
|
||||
return AST_CAUSE_DESTINATION_OUT_OF_ORDER;
|
||||
case 503: /* Service unavailable */
|
||||
return AST_CAUSE_CONGESTION;
|
||||
case 504: /* Gateway timeout */
|
||||
return AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
|
||||
case 505: /* SIP version not supported */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
case 600: /* Busy everywhere */
|
||||
return AST_CAUSE_USER_BUSY;
|
||||
case 603: /* Decline */
|
||||
return AST_CAUSE_CALL_REJECTED;
|
||||
case 604: /* Does not exist anywhere */
|
||||
return AST_CAUSE_UNALLOCATED;
|
||||
case 606: /* Not acceptable */
|
||||
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
|
||||
default:
|
||||
if (cause < 500 && cause >= 400) {
|
||||
/* 4xx class error that is unknown - someting wrong with our request */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
} else if (cause < 600 && cause >= 500) {
|
||||
/* 5xx class error - problem in the remote end */
|
||||
return AST_CAUSE_CONGESTION;
|
||||
} else if (cause < 700 && cause >= 600) {
|
||||
/* 6xx - global errors in the 4xx class */
|
||||
return AST_CAUSE_INTERWORKING;
|
||||
}
|
||||
return AST_CAUSE_NORMAL;
|
||||
}
|
||||
/* Never reached */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chan_pjsip_session_begin(struct ast_sip_session *session)
|
||||
{
|
||||
RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
|
||||
|
@ -2928,21 +2993,11 @@ static void chan_pjsip_session_end(struct ast_sip_session *session)
|
|||
SCOPE_EXIT_RTN("No channel\n");
|
||||
}
|
||||
|
||||
|
||||
if (session->active_media_state &&
|
||||
session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO]) {
|
||||
struct ast_sip_session_media *media =
|
||||
session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
|
||||
if (media->rtp) {
|
||||
ast_rtp_instance_set_stats_vars(session->channel, media->rtp);
|
||||
}
|
||||
}
|
||||
|
||||
chan_pjsip_remove_hold(ast_channel_uniqueid(session->channel));
|
||||
|
||||
ast_set_hangupsource(session->channel, ast_channel_name(session->channel), 0);
|
||||
if (!ast_channel_hangupcause(session->channel) && session->inv_session) {
|
||||
int cause = ast_sip_hangup_sip2cause(session->inv_session->cause);
|
||||
int cause = hangup_sip2cause(session->inv_session->cause);
|
||||
|
||||
ast_queue_hangup_with_cause(session->channel, cause);
|
||||
} else {
|
||||
|
@ -3136,7 +3191,7 @@ static void chan_pjsip_incoming_response_update_cause(struct ast_sip_session *se
|
|||
snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "SIP %d %.*s", status.code,
|
||||
(int) pj_strlen(&status.reason), pj_strbuf(&status.reason));
|
||||
|
||||
cause_code->ast_cause = ast_sip_hangup_sip2cause(status.code);
|
||||
cause_code->ast_cause = hangup_sip2cause(status.code);
|
||||
ast_queue_control_data(session->channel, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
|
||||
ast_channel_hangupcause_hash_set(session->channel, cause_code, data_size);
|
||||
|
||||
|
@ -3158,7 +3213,6 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct
|
|||
pjsip_rdata_sdp_info *sdp = pjsip_rdata_get_sdp_info(rdata);
|
||||
if (sdp && sdp->body.ptr) {
|
||||
ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
|
||||
session->early_confirmed = pjsip_100rel_is_reliable(rdata) == PJ_TRUE;
|
||||
ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
|
||||
} else {
|
||||
ast_trace(-1, "%s: Queueing RINGING\n", ast_sip_session_get_name(session));
|
||||
|
@ -3179,7 +3233,6 @@ static void chan_pjsip_incoming_response(struct ast_sip_session *session, struct
|
|||
ast_trace(-1, "%s: Queueing PROGRESS\n", ast_sip_session_get_name(session));
|
||||
ast_trace(1, "%s Method: %.*s Status: %d Queueing PROGRESS with SDP\n", ast_sip_session_get_name(session),
|
||||
(int)rdata->msg_info.cseq->method.name.slen, rdata->msg_info.cseq->method.name.ptr, status.code);
|
||||
session->early_confirmed = pjsip_100rel_is_reliable(rdata) == PJ_TRUE;
|
||||
ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
|
||||
}
|
||||
} else {
|
||||
|
@ -3214,18 +3267,6 @@ static int chan_pjsip_incoming_ack(struct ast_sip_session *session, struct pjsip
|
|||
SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session));
|
||||
}
|
||||
|
||||
static int chan_pjsip_incoming_prack(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
|
||||
{
|
||||
SCOPE_ENTER(3, "%s\n", ast_sip_session_get_name(session));
|
||||
|
||||
if (pj_strcmp2(&rdata->msg_info.msg->line.req.method.name, "PRACK") == 0 &&
|
||||
pjmedia_sdp_neg_get_state(session->inv_session->neg) == PJMEDIA_SDP_NEG_STATE_DONE) {
|
||||
|
||||
session->early_confirmed = 1;
|
||||
}
|
||||
SCOPE_EXIT_RTN_VALUE(0, "%s\n", ast_sip_session_get_name(session));
|
||||
}
|
||||
|
||||
static int update_devstate(void *obj, void *arg, int flags)
|
||||
{
|
||||
ast_devstate_changed(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE,
|
||||
|
@ -3266,8 +3307,6 @@ static struct ast_custom_function session_refresh_function = {
|
|||
.write = pjsip_acf_session_refresh_write,
|
||||
};
|
||||
|
||||
static char *app_pjsip_hangup = "PJSIPHangup";
|
||||
|
||||
/*!
|
||||
* \brief Load the module
|
||||
*
|
||||
|
@ -3325,13 +3364,6 @@ static int load_module(void)
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (ast_register_application_xml(app_pjsip_hangup, pjsip_app_hangup)) {
|
||||
ast_log(LOG_WARNING, "Unable to register PJSIPHangup dialplan application\n");
|
||||
goto end;
|
||||
}
|
||||
ast_manager_register_xml(app_pjsip_hangup, EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, pjsip_action_hangup);
|
||||
|
||||
|
||||
ast_sip_register_service(&refer_callback_module);
|
||||
|
||||
ast_sip_session_register_supplement(&chan_pjsip_supplement);
|
||||
|
@ -3347,7 +3379,6 @@ static int load_module(void)
|
|||
ast_sip_session_register_supplement(&call_pickup_supplement);
|
||||
ast_sip_session_register_supplement(&pbx_start_supplement);
|
||||
ast_sip_session_register_supplement(&chan_pjsip_ack_supplement);
|
||||
ast_sip_session_register_supplement(&chan_pjsip_prack_supplement);
|
||||
|
||||
if (pjsip_channel_cli_register()) {
|
||||
ast_log(LOG_ERROR, "Unable to register PJSIP Channel CLI\n");
|
||||
|
@ -3367,7 +3398,6 @@ end:
|
|||
ao2_cleanup(pjsip_uids_onhold);
|
||||
pjsip_uids_onhold = NULL;
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_prack_supplement);
|
||||
ast_sip_session_unregister_supplement(&pbx_start_supplement);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_supplement_response);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_supplement);
|
||||
|
@ -3379,9 +3409,6 @@ end:
|
|||
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
|
||||
ast_custom_function_unregister(&chan_pjsip_parse_uri_function);
|
||||
ast_custom_function_unregister(&session_refresh_function);
|
||||
ast_unregister_application(app_pjsip_hangup);
|
||||
ast_manager_unregister(app_pjsip_hangup);
|
||||
|
||||
ast_channel_unregister(&chan_pjsip_tech);
|
||||
ast_rtp_glue_unregister(&chan_pjsip_rtp_glue);
|
||||
|
||||
|
@ -3400,7 +3427,6 @@ static int unload_module(void)
|
|||
ast_sip_session_unregister_supplement(&chan_pjsip_supplement);
|
||||
ast_sip_session_unregister_supplement(&pbx_start_supplement);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_ack_supplement);
|
||||
ast_sip_session_unregister_supplement(&chan_pjsip_prack_supplement);
|
||||
ast_sip_session_unregister_supplement(&call_pickup_supplement);
|
||||
|
||||
ast_sip_unregister_service(&refer_callback_module);
|
||||
|
@ -3411,8 +3437,6 @@ static int unload_module(void)
|
|||
ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
|
||||
ast_custom_function_unregister(&chan_pjsip_parse_uri_function);
|
||||
ast_custom_function_unregister(&session_refresh_function);
|
||||
ast_unregister_application(app_pjsip_hangup);
|
||||
ast_manager_unregister(app_pjsip_hangup);
|
||||
|
||||
ast_channel_unregister(&chan_pjsip_tech);
|
||||
ao2_ref(chan_pjsip_tech.capabilities, -1);
|
||||
|
|
|
@ -129,8 +129,7 @@ static struct ast_format *derive_format_from_cap(struct ast_format_cap *cap)
|
|||
* assignments. Signed linear @ 8kHz does not map, so if that is our
|
||||
* only capability, we force μ-law instead.
|
||||
*/
|
||||
ao2_ref(fmt, -1);
|
||||
fmt = ao2_bump(ast_format_ulaw);
|
||||
fmt = ast_format_ulaw;
|
||||
}
|
||||
|
||||
return fmt;
|
||||
|
@ -212,7 +211,7 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo
|
|||
}
|
||||
|
||||
chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids,
|
||||
requestor, 0, "MulticastRTP/%s-%p", args.destination, instance);
|
||||
requestor, 0, "MulticastRTP/%p", instance);
|
||||
if (!chan) {
|
||||
ast_rtp_instance_destroy(instance);
|
||||
goto failure;
|
||||
|
@ -250,7 +249,6 @@ failure:
|
|||
enum {
|
||||
OPT_RTP_CODEC = (1 << 0),
|
||||
OPT_RTP_ENGINE = (1 << 1),
|
||||
OPT_RTP_GLUE = (1 << 2),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -265,14 +263,8 @@ AST_APP_OPTIONS(unicast_rtp_options, BEGIN_OPTIONS
|
|||
AST_APP_OPTION_ARG('c', OPT_RTP_CODEC, OPT_ARG_RTP_CODEC),
|
||||
/*! Set the RTP engine to use for unicast RTP */
|
||||
AST_APP_OPTION_ARG('e', OPT_RTP_ENGINE, OPT_ARG_RTP_ENGINE),
|
||||
/*! Provide RTP glue for the channel */
|
||||
AST_APP_OPTION('g', OPT_RTP_GLUE),
|
||||
END_OPTIONS );
|
||||
|
||||
static const struct ast_datastore_info chan_rtp_datastore_info = {
|
||||
.type = "CHAN_RTP_GLUE",
|
||||
};
|
||||
|
||||
/*! \brief Function called when we should prepare to call the unicast destination */
|
||||
static struct ast_channel *unicast_rtp_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
|
||||
{
|
||||
|
@ -380,13 +372,6 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
|
|||
|
||||
ast_channel_tech_set(chan, &unicast_rtp_tech);
|
||||
|
||||
if (ast_test_flag(&opts, OPT_RTP_GLUE)) {
|
||||
struct ast_datastore *datastore;
|
||||
if ((datastore = ast_datastore_alloc(&chan_rtp_datastore_info, NULL))) {
|
||||
ast_channel_datastore_add(chan, datastore);
|
||||
}
|
||||
}
|
||||
|
||||
ast_format_cap_append(caps, fmt, 0);
|
||||
ast_channel_nativeformats_set(chan, caps);
|
||||
ast_channel_set_writeformat(chan, fmt);
|
||||
|
@ -396,9 +381,9 @@ static struct ast_channel *unicast_rtp_request(const char *type, struct ast_form
|
|||
|
||||
ast_channel_tech_pvt_set(chan, instance);
|
||||
|
||||
ast_rtp_instance_get_local_address(instance, &local_address);
|
||||
pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_ADDRESS",
|
||||
ast_sockaddr_stringify_addr(&local_address));
|
||||
ast_rtp_instance_get_local_address(instance, &local_address);
|
||||
pbx_builtin_setvar_helper(chan, "UNICASTRTP_LOCAL_PORT",
|
||||
ast_sockaddr_stringify_port(&local_address));
|
||||
|
||||
|
@ -416,61 +401,6 @@ failure:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*! \brief Function called by RTP engine to get peer capabilities */
|
||||
static void chan_rtp_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
|
||||
{
|
||||
SCOPE_ENTER(1, "%s Native formats %s\n", ast_channel_name(chan),
|
||||
ast_str_tmp(AST_FORMAT_CAP_NAMES_LEN, ast_format_cap_get_names(ast_channel_nativeformats(chan), &STR_TMP)));
|
||||
ast_format_cap_append_from_cap(result, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_UNKNOWN);
|
||||
SCOPE_EXIT_RTN();
|
||||
}
|
||||
|
||||
/*! \brief Function called by RTP engine to change where the remote party should send media.
|
||||
*
|
||||
* chan_rtp is not able to actually update the peer, so this function has no effect.
|
||||
* */
|
||||
static int chan_rtp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *tpeer, const struct ast_format_cap *cap, int nat_active)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! \brief Function called by RTP engine to get local audio RTP peer */
|
||||
static enum ast_rtp_glue_result chan_rtp_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
|
||||
{
|
||||
return AST_RTP_GLUE_RESULT_FORBID;
|
||||
}
|
||||
|
||||
/*! \brief Function called by RTP engine to get local audio RTP peer */
|
||||
static enum ast_rtp_glue_result chan_rtp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
|
||||
{
|
||||
struct ast_rtp_instance *rtp_instance = ast_channel_tech_pvt(chan);
|
||||
struct ast_datastore *datastore;
|
||||
|
||||
if (!rtp_instance) {
|
||||
return AST_RTP_GLUE_RESULT_FORBID;
|
||||
}
|
||||
|
||||
if ((datastore = ast_channel_datastore_find(chan, &chan_rtp_datastore_info, NULL))) {
|
||||
ao2_ref(datastore, -1);
|
||||
|
||||
*instance = rtp_instance;
|
||||
ao2_ref(*instance, +1);
|
||||
|
||||
return AST_RTP_GLUE_RESULT_LOCAL;
|
||||
}
|
||||
|
||||
return AST_RTP_GLUE_RESULT_FORBID;
|
||||
}
|
||||
|
||||
/*! \brief Local glue for interacting with the RTP engine core */
|
||||
static struct ast_rtp_glue unicast_rtp_glue = {
|
||||
.type = "UnicastRTP",
|
||||
.get_rtp_info = chan_rtp_get_rtp_peer,
|
||||
.get_vrtp_info = chan_rtp_get_vrtp_peer,
|
||||
.get_codec = chan_rtp_get_codec,
|
||||
.update_peer = chan_rtp_set_rtp_peer,
|
||||
};
|
||||
|
||||
/*! \brief Function called when our module is unloaded */
|
||||
static int unload_module(void)
|
||||
{
|
||||
|
@ -482,8 +412,6 @@ static int unload_module(void)
|
|||
ao2_cleanup(unicast_rtp_tech.capabilities);
|
||||
unicast_rtp_tech.capabilities = NULL;
|
||||
|
||||
ast_rtp_glue_unregister(&unicast_rtp_glue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -493,9 +421,6 @@ static int load_module(void)
|
|||
if (!(multicast_rtp_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
ast_rtp_glue_register(&unicast_rtp_glue);
|
||||
|
||||
ast_format_cap_append_by_type(multicast_rtp_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN);
|
||||
if (ast_channel_register(&multicast_rtp_tech)) {
|
||||
ast_log(LOG_ERROR, "Unable to register channel class 'MulticastRTP'\n");
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -99,20 +99,14 @@ if the formats are equivalent. This will save some unnecessary format
|
|||
conversion.
|
||||
|
||||
|
||||
In order to handle video you need to add the following to the endpoint in
|
||||
pjsip.conf
|
||||
In order to handle video you need to add to sip.conf (and presumably
|
||||
iax.conf too) the following:
|
||||
|
||||
[general](+)
|
||||
videosupport=yes
|
||||
allow=h263 ; this or other video formats
|
||||
allow=h263p ; this or other video formats
|
||||
|
||||
(Presumably, iax.conf would require):
|
||||
|
||||
[general](+)
|
||||
videosupport=yes
|
||||
allow=h263 ; this or other video formats
|
||||
allow=h263p ; this or other video formats
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
|
@ -424,7 +424,7 @@ static void dump_ies(unsigned char *iedata, int len)
|
|||
|
||||
if (len < 2)
|
||||
return;
|
||||
while(len >= 2) {
|
||||
while(len > 2) {
|
||||
ie = iedata[0];
|
||||
ielen = iedata[1];
|
||||
if (ielen + 2> len) {
|
||||
|
@ -1063,7 +1063,7 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
|
|||
if (len == (int)sizeof(unsigned int)) {
|
||||
ies->calling_ani2 = ntohl(get_unaligned_uint32(data + 2));
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "Expected callingani2 to be %zu bytes but was %d\n", sizeof(unsigned int), len);
|
||||
snprintf(tmp, (int)sizeof(tmp), "callingani2 was %d long: %s\n", len, data + 2);
|
||||
errorf(tmp);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -343,7 +343,6 @@ static int cli_channelstats_print_body(void *obj, void *arg, int flags)
|
|||
struct ast_sip_session *session;
|
||||
struct ast_sip_session_media *media;
|
||||
struct ast_rtp_instance_stats stats;
|
||||
struct ast_stream *stream;
|
||||
char *print_name = NULL;
|
||||
char *print_time = alloca(32);
|
||||
char codec_in_use[7];
|
||||
|
@ -360,29 +359,16 @@ static int cli_channelstats_print_body(void *obj, void *arg, int flags)
|
|||
|
||||
cpvt = ast_channel_tech_pvt(channel);
|
||||
session = cpvt ? cpvt->session : NULL;
|
||||
|
||||
if (!session
|
||||
|| !session->active_media_state
|
||||
|| !session->active_media_state->topology) {
|
||||
if (!session) {
|
||||
ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
|
||||
ast_channel_unlock(channel);
|
||||
ao2_cleanup(channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream = ast_stream_topology_get_first_stream_by_type(
|
||||
session->active_media_state->topology, AST_MEDIA_TYPE_AUDIO);
|
||||
|
||||
if (!stream) {
|
||||
ast_str_append(&context->output_buffer, 0, " %s no audio streams\n", snapshot->base->name);
|
||||
ast_channel_unlock(channel);
|
||||
ao2_cleanup(channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
media = session->active_media_state->default_session[AST_MEDIA_TYPE_AUDIO];
|
||||
if (!media || media->type != AST_MEDIA_TYPE_AUDIO || !media->rtp) {
|
||||
ast_str_append(&context->output_buffer, 0, " %s corrupted default audio session\n", snapshot->base->name);
|
||||
if (!media || !media->rtp) {
|
||||
ast_str_append(&context->output_buffer, 0, " %s not valid\n", snapshot->base->name);
|
||||
ast_channel_unlock(channel);
|
||||
ao2_cleanup(channel);
|
||||
return 0;
|
||||
|
|
|
@ -29,6 +29,508 @@
|
|||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<function name="PJSIP_DIAL_CONTACTS" language="en_US">
|
||||
<synopsis>
|
||||
Return a dial string for dialing all contacts on an AOR.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="endpoint" required="true">
|
||||
<para>Name of the endpoint</para>
|
||||
</parameter>
|
||||
<parameter name="aor" required="false">
|
||||
<para>Name of an AOR to use, if not specified the configured AORs on the endpoint are used</para>
|
||||
</parameter>
|
||||
<parameter name="request_user" required="false">
|
||||
<para>Optional request user to use in the request URI</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Returns a properly formatted dial string for dialing all contacts on an AOR.</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_MEDIA_OFFER" language="en_US">
|
||||
<synopsis>
|
||||
Media and codec offerings to be set on an outbound SIP channel prior to dialing.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="media" required="true">
|
||||
<para>types of media offered</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the codecs offered based upon the media choice.</para>
|
||||
<para>When written, sets the codecs to offer when an outbound dial attempt is made,
|
||||
or when a session refresh is sent using <replaceable>PJSIP_SEND_SESSION_REFRESH</replaceable>.
|
||||
</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">PJSIP_SEND_SESSION_REFRESH</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="PJSIP_DTMF_MODE" language="en_US">
|
||||
<since>
|
||||
<version>13.18.0</version>
|
||||
<version>14.7.0</version>
|
||||
<version>15.1.0</version>
|
||||
<version>16.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Get or change the DTMF mode for a SIP call.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the current DTMF mode</para>
|
||||
<para>When written, sets the current DTMF mode</para>
|
||||
<para>This function uses the same DTMF mode naming as the dtmf_mode configuration option</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_MOH_PASSTHROUGH" language="en_US">
|
||||
<synopsis>
|
||||
Get or change the on-hold behavior for a SIP call.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the current moh passthrough mode</para>
|
||||
<para>When written, sets the current moh passthrough mode</para>
|
||||
<para>If <replaceable>yes</replaceable>, on-hold re-INVITEs are sent. If <replaceable>no</replaceable>, music on hold is generated.</para>
|
||||
<para>This function can be used to override the moh_passthrough configuration option</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_SEND_SESSION_REFRESH" language="en_US">
|
||||
<since>
|
||||
<version>13.12.0</version>
|
||||
<version>14.1.0</version>
|
||||
<version>15.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="update_type" required="false">
|
||||
<para>The type of update to send. Default is <literal>invite</literal>.</para>
|
||||
<enumlist>
|
||||
<enum name="invite">
|
||||
<para>Send the session refresh as a re-INVITE.</para>
|
||||
</enum>
|
||||
<enum name="update">
|
||||
<para>Send the session refresh as an UPDATE.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This function will cause the PJSIP stack to immediately refresh
|
||||
the media session for the channel. This will be done using either a
|
||||
re-INVITE (default) or an UPDATE request.
|
||||
</para>
|
||||
<para>This is most useful when combined with the <replaceable>PJSIP_MEDIA_OFFER</replaceable>
|
||||
dialplan function, as it allows the formats in use on a channel to be
|
||||
re-negotiated after call setup.</para>
|
||||
<warning>
|
||||
<para>The formats the endpoint supports are <emphasis>not</emphasis>
|
||||
checked or enforced by this function. Using this function to offer
|
||||
formats not supported by the endpoint <emphasis>may</emphasis> result
|
||||
in a loss of media.</para>
|
||||
</warning>
|
||||
<example title="Re-negotiate format to g722">
|
||||
; Within some existing extension on an answered channel
|
||||
same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722)
|
||||
same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite)
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">PJSIP_MEDIA_OFFER</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="PJSIP_PARSE_URI" language="en_US">
|
||||
<since>
|
||||
<version>13.24.0</version>
|
||||
<version>16.1.0</version>
|
||||
<version>17.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Parse an uri and return a type part of the URI.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="uri" required="true">
|
||||
<para>URI to parse</para>
|
||||
</parameter>
|
||||
<parameter name="type" required="true">
|
||||
<para>The <literal>type</literal> parameter specifies which URI part to read</para>
|
||||
<enumlist>
|
||||
<enum name="display">
|
||||
<para>Display name.</para>
|
||||
</enum>
|
||||
<enum name="scheme">
|
||||
<para>URI scheme.</para>
|
||||
</enum>
|
||||
<enum name="user">
|
||||
<para>User part.</para>
|
||||
</enum>
|
||||
<enum name="passwd">
|
||||
<para>Password part.</para>
|
||||
</enum>
|
||||
<enum name="host">
|
||||
<para>Host part.</para>
|
||||
</enum>
|
||||
<enum name="port">
|
||||
<para>Port number, or zero.</para>
|
||||
</enum>
|
||||
<enum name="user_param">
|
||||
<para>User parameter.</para>
|
||||
</enum>
|
||||
<enum name="method_param">
|
||||
<para>Method parameter.</para>
|
||||
</enum>
|
||||
<enum name="transport_param">
|
||||
<para>Transport parameter.</para>
|
||||
</enum>
|
||||
<enum name="ttl_param">
|
||||
<para>TTL param, or -1.</para>
|
||||
</enum>
|
||||
<enum name="lr_param">
|
||||
<para>Loose routing param, or zero.</para>
|
||||
</enum>
|
||||
<enum name="maddr_param">
|
||||
<para>Maddr param.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Parse an URI and return a specified part of the URI.</para>
|
||||
</description>
|
||||
</function>
|
||||
<info name="CHANNEL" language="en_US" tech="PJSIP">
|
||||
<enumlist>
|
||||
<enum name="rtp">
|
||||
<para>R/O Retrieve media related information.</para>
|
||||
<parameter name="type" required="true">
|
||||
<para>When <replaceable>rtp</replaceable> is specified, the
|
||||
<literal>type</literal> parameter must be provided. It specifies
|
||||
which RTP parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="src">
|
||||
<para>Retrieve the local address for RTP.</para>
|
||||
</enum>
|
||||
<enum name="dest">
|
||||
<para>Retrieve the remote address for RTP.</para>
|
||||
</enum>
|
||||
<enum name="direct">
|
||||
<para>If direct media is enabled, this address is the remote address
|
||||
used for RTP.</para>
|
||||
</enum>
|
||||
<enum name="secure">
|
||||
<para>Whether or not the media stream is encrypted.</para>
|
||||
<enumlist>
|
||||
<enum name="0">
|
||||
<para>The media stream is not encrypted.</para>
|
||||
</enum>
|
||||
<enum name="1">
|
||||
<para>The media stream is encrypted.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="hold">
|
||||
<para>Whether or not the media stream is currently restricted
|
||||
due to a call hold.</para>
|
||||
<enumlist>
|
||||
<enum name="0">
|
||||
<para>The media stream is not held.</para>
|
||||
</enum>
|
||||
<enum name="1">
|
||||
<para>The media stream is held.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="media_type" required="false">
|
||||
<para>When <replaceable>rtp</replaceable> is specified, the
|
||||
<literal>media_type</literal> parameter may be provided. It specifies
|
||||
which media stream the chosen RTP parameter should be retrieved
|
||||
from.</para>
|
||||
<enumlist>
|
||||
<enum name="audio">
|
||||
<para>Retrieve information from the audio media stream.</para>
|
||||
<note><para>If not specified, <literal>audio</literal> is used
|
||||
by default.</para></note>
|
||||
</enum>
|
||||
<enum name="video">
|
||||
<para>Retrieve information from the video media stream.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
<enum name="rtcp">
|
||||
<para>R/O Retrieve RTCP statistics.</para>
|
||||
<parameter name="statistic" required="true">
|
||||
<para>When <replaceable>rtcp</replaceable> is specified, the
|
||||
<literal>statistic</literal> parameter must be provided. It specifies
|
||||
which RTCP statistic parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="all">
|
||||
<para>Retrieve a summary of all RTCP statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="ssrc">
|
||||
<para>Our Synchronization Source identifier</para>
|
||||
</enum>
|
||||
<enum name="themssrc">
|
||||
<para>Their Synchronization Source identifier</para>
|
||||
</enum>
|
||||
<enum name="lp">
|
||||
<para>Our lost packet count</para>
|
||||
</enum>
|
||||
<enum name="rxjitter">
|
||||
<para>Received packet jitter</para>
|
||||
</enum>
|
||||
<enum name="rxcount">
|
||||
<para>Received packet count</para>
|
||||
</enum>
|
||||
<enum name="txjitter">
|
||||
<para>Transmitted packet jitter</para>
|
||||
</enum>
|
||||
<enum name="txcount">
|
||||
<para>Transmitted packet count</para>
|
||||
</enum>
|
||||
<enum name="rlp">
|
||||
<para>Remote lost packet count</para>
|
||||
</enum>
|
||||
<enum name="rtt">
|
||||
<para>Round trip time</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_jitter">
|
||||
<para>Retrieve a summary of all RTCP Jitter statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrxjitter">
|
||||
<para>Our minimum jitter</para>
|
||||
</enum>
|
||||
<enum name="maxrxjitter">
|
||||
<para>Our max jitter</para>
|
||||
</enum>
|
||||
<enum name="avgrxjitter">
|
||||
<para>Our average jitter</para>
|
||||
</enum>
|
||||
<enum name="stdevrxjitter">
|
||||
<para>Our jitter standard deviation</para>
|
||||
</enum>
|
||||
<enum name="reported_minjitter">
|
||||
<para>Their minimum jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_maxjitter">
|
||||
<para>Their max jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_avgjitter">
|
||||
<para>Their average jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevjitter">
|
||||
<para>Their jitter standard deviation</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_loss">
|
||||
<para>Retrieve a summary of all RTCP packet loss statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrxlost">
|
||||
<para>Our minimum lost packets</para>
|
||||
</enum>
|
||||
<enum name="maxrxlost">
|
||||
<para>Our max lost packets</para>
|
||||
</enum>
|
||||
<enum name="avgrxlost">
|
||||
<para>Our average lost packets</para>
|
||||
</enum>
|
||||
<enum name="stdevrxlost">
|
||||
<para>Our lost packets standard deviation</para>
|
||||
</enum>
|
||||
<enum name="reported_minlost">
|
||||
<para>Their minimum lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_maxlost">
|
||||
<para>Their max lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_avglost">
|
||||
<para>Their average lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevlost">
|
||||
<para>Their lost packets standard deviation</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_rtt">
|
||||
<para>Retrieve a summary of all RTCP round trip time information.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrtt">
|
||||
<para>Minimum round trip time</para>
|
||||
</enum>
|
||||
<enum name="maxrtt">
|
||||
<para>Maximum round trip time</para>
|
||||
</enum>
|
||||
<enum name="avgrtt">
|
||||
<para>Average round trip time</para>
|
||||
</enum>
|
||||
<enum name="stdevrtt">
|
||||
<para>Standard deviation round trip time</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="txcount"><para>Transmitted packet count</para></enum>
|
||||
<enum name="rxcount"><para>Received packet count</para></enum>
|
||||
<enum name="txjitter"><para>Transmitted packet jitter</para></enum>
|
||||
<enum name="rxjitter"><para>Received packet jitter</para></enum>
|
||||
<enum name="remote_maxjitter"><para>Their max jitter</para></enum>
|
||||
<enum name="remote_minjitter"><para>Their minimum jitter</para></enum>
|
||||
<enum name="remote_normdevjitter"><para>Their average jitter</para></enum>
|
||||
<enum name="remote_stdevjitter"><para>Their jitter standard deviation</para></enum>
|
||||
<enum name="local_maxjitter"><para>Our max jitter</para></enum>
|
||||
<enum name="local_minjitter"><para>Our minimum jitter</para></enum>
|
||||
<enum name="local_normdevjitter"><para>Our average jitter</para></enum>
|
||||
<enum name="local_stdevjitter"><para>Our jitter standard deviation</para></enum>
|
||||
<enum name="txploss"><para>Transmitted packet loss</para></enum>
|
||||
<enum name="rxploss"><para>Received packet loss</para></enum>
|
||||
<enum name="remote_maxrxploss"><para>Their max lost packets</para></enum>
|
||||
<enum name="remote_minrxploss"><para>Their minimum lost packets</para></enum>
|
||||
<enum name="remote_normdevrxploss"><para>Their average lost packets</para></enum>
|
||||
<enum name="remote_stdevrxploss"><para>Their lost packets standard deviation</para></enum>
|
||||
<enum name="local_maxrxploss"><para>Our max lost packets</para></enum>
|
||||
<enum name="local_minrxploss"><para>Our minimum lost packets</para></enum>
|
||||
<enum name="local_normdevrxploss"><para>Our average lost packets</para></enum>
|
||||
<enum name="local_stdevrxploss"><para>Our lost packets standard deviation</para></enum>
|
||||
<enum name="rtt"><para>Round trip time</para></enum>
|
||||
<enum name="maxrtt"><para>Maximum round trip time</para></enum>
|
||||
<enum name="minrtt"><para>Minimum round trip time</para></enum>
|
||||
<enum name="normdevrtt"><para>Average round trip time</para></enum>
|
||||
<enum name="stdevrtt"><para>Standard deviation round trip time</para></enum>
|
||||
<enum name="local_ssrc"><para>Our Synchronization Source identifier</para></enum>
|
||||
<enum name="remote_ssrc"><para>Their Synchronization Source identifier</para></enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="media_type" required="false">
|
||||
<para>When <replaceable>rtcp</replaceable> is specified, the
|
||||
<literal>media_type</literal> parameter may be provided. It specifies
|
||||
which media stream the chosen RTCP parameter should be retrieved
|
||||
from.</para>
|
||||
<enumlist>
|
||||
<enum name="audio">
|
||||
<para>Retrieve information from the audio media stream.</para>
|
||||
<note><para>If not specified, <literal>audio</literal> is used
|
||||
by default.</para></note>
|
||||
</enum>
|
||||
<enum name="video">
|
||||
<para>Retrieve information from the video media stream.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
<enum name="endpoint">
|
||||
<para>R/O The name of the endpoint associated with this channel.
|
||||
Use the <replaceable>PJSIP_ENDPOINT</replaceable> function to obtain
|
||||
further endpoint related information.</para>
|
||||
</enum>
|
||||
<enum name="contact">
|
||||
<para>R/O The name of the contact associated with this channel.
|
||||
Use the <replaceable>PJSIP_CONTACT</replaceable> function to obtain
|
||||
further contact related information. Note this may not be present and if so
|
||||
is only available on outgoing legs.</para>
|
||||
</enum>
|
||||
<enum name="aor">
|
||||
<para>R/O The name of the AOR associated with this channel.
|
||||
Use the <replaceable>PJSIP_AOR</replaceable> function to obtain
|
||||
further AOR related information. Note this may not be present and if so
|
||||
is only available on outgoing legs.</para>
|
||||
</enum>
|
||||
<enum name="pjsip">
|
||||
<para>R/O Obtain information about the current PJSIP channel and its
|
||||
session.</para>
|
||||
<parameter name="type" required="true">
|
||||
<para>When <replaceable>pjsip</replaceable> is specified, the
|
||||
<literal>type</literal> parameter must be provided. It specifies
|
||||
which signalling parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="call-id">
|
||||
<para>The SIP call-id.</para>
|
||||
</enum>
|
||||
<enum name="secure">
|
||||
<para>Whether or not the signalling uses a secure transport.</para>
|
||||
<enumlist>
|
||||
<enum name="0"><para>The signalling uses a non-secure transport.</para></enum>
|
||||
<enum name="1"><para>The signalling uses a secure transport.</para></enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="target_uri">
|
||||
<para>The contact URI where requests are sent.</para>
|
||||
</enum>
|
||||
<enum name="local_uri">
|
||||
<para>The local URI.</para>
|
||||
</enum>
|
||||
<enum name="local_tag">
|
||||
<para>Tag in From header</para>
|
||||
</enum>
|
||||
<enum name="remote_uri">
|
||||
<para>The remote URI.</para>
|
||||
</enum>
|
||||
<enum name="remote_tag">
|
||||
<para>Tag in To header</para>
|
||||
</enum>
|
||||
<enum name="request_uri">
|
||||
<para>The request URI of the incoming <literal>INVITE</literal>
|
||||
associated with the creation of this channel.</para>
|
||||
</enum>
|
||||
<enum name="t38state">
|
||||
<para>The current state of any T.38 fax on this channel.</para>
|
||||
<enumlist>
|
||||
<enum name="DISABLED"><para>T.38 faxing is disabled on this channel.</para></enum>
|
||||
<enum name="LOCAL_REINVITE"><para>Asterisk has sent a <literal>re-INVITE</literal> to the remote end to initiate a T.38 fax.</para></enum>
|
||||
<enum name="REMOTE_REINVITE"><para>The remote end has sent a <literal>re-INVITE</literal> to Asterisk to initiate a T.38 fax.</para></enum>
|
||||
<enum name="ENABLED"><para>A T.38 fax session has been enabled.</para></enum>
|
||||
<enum name="REJECTED"><para>A T.38 fax session was attempted but was rejected.</para></enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="local_addr">
|
||||
<para>On inbound calls, the full IP address and port number that
|
||||
the <literal>INVITE</literal> request was received on. On outbound
|
||||
calls, the full IP address and port number that the <literal>INVITE</literal>
|
||||
request was transmitted from.</para>
|
||||
</enum>
|
||||
<enum name="remote_addr">
|
||||
<para>On inbound calls, the full IP address and port number that
|
||||
the <literal>INVITE</literal> request was received from. On outbound
|
||||
calls, the full IP address and port number that the <literal>INVITE</literal>
|
||||
request was transmitted to.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<info name="CHANNEL_EXAMPLES" language="en_US" tech="PJSIP">
|
||||
<example title="PJSIP specific CHANNEL examples">
|
||||
; Log the current Call-ID
|
||||
same => n,Log(NOTICE, ${CHANNEL(pjsip,call-id)})
|
||||
|
||||
; Log the destination address of the audio stream
|
||||
same => n,Log(NOTICE, ${CHANNEL(rtp,dest)})
|
||||
|
||||
; Store the round-trip time associated with a
|
||||
; video stream in the CDR field video-rtt
|
||||
same => n,Set(CDR(video-rtt)=${CHANNEL(rtcp,rtt,video)})
|
||||
</example>
|
||||
</info>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <pjsip.h>
|
||||
|
@ -39,7 +541,6 @@
|
|||
#include "asterisk/module.h"
|
||||
#include "asterisk/acl.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/conversions.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/stream.h"
|
||||
#include "asterisk/format.h"
|
||||
|
@ -177,8 +678,6 @@ static int channel_read_rtcp(struct ast_channel *chan, const char *type, const c
|
|||
stat_field = AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT;
|
||||
} else if (!strcasecmp(type, "all_loss")) {
|
||||
stat_field = AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS;
|
||||
} else if (!strcasecmp(type, "all_mes")) {
|
||||
stat_field = AST_RTP_INSTANCE_STAT_FIELD_QUALITY_MES;
|
||||
}
|
||||
|
||||
if (!ast_rtp_instance_get_quality(media->rtp, stat_field, buf, buflen)) {
|
||||
|
@ -225,16 +724,6 @@ static int channel_read_rtcp(struct ast_channel *chan, const char *type, const c
|
|||
{ "stdevrtt", DBL, { .d8 = &stats.stdevrtt, }, },
|
||||
{ "local_ssrc", INT, { .i4 = &stats.local_ssrc, }, },
|
||||
{ "remote_ssrc", INT, { .i4 = &stats.remote_ssrc, }, },
|
||||
{ "txmes", DBL, { .d8 = &stats.txmes, }, },
|
||||
{ "rxmes", DBL, { .d8 = &stats.rxmes, }, },
|
||||
{ "remote_maxmes", DBL, { .d8 = &stats.remote_maxmes, }, },
|
||||
{ "remote_minmes", DBL, { .d8 = &stats.remote_minmes, }, },
|
||||
{ "remote_normdevmes", DBL, { .d8 = &stats.remote_normdevmes, }, },
|
||||
{ "remote_stdevmes", DBL, { .d8 = &stats.remote_stdevmes, }, },
|
||||
{ "local_maxmes", DBL, { .d8 = &stats.local_maxmes, }, },
|
||||
{ "local_minmes", DBL, { .d8 = &stats.local_minmes, }, },
|
||||
{ "local_normdevmes", DBL, { .d8 = &stats.local_normdevmes, }, },
|
||||
{ "local_stdevmes", DBL, { .d8 = &stats.local_stdevmes, }, },
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
|
@ -1228,121 +1717,3 @@ int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, c
|
|||
|
||||
return ast_sip_push_task_wait_serializer(channel->session->serializer, refresh_write_cb, &rdata);
|
||||
}
|
||||
|
||||
struct hangup_data {
|
||||
struct ast_sip_session *session;
|
||||
int response_code;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Serializer task to hangup channel
|
||||
*/
|
||||
static int pjsip_hangup(void *obj)
|
||||
{
|
||||
struct hangup_data *hdata = obj;
|
||||
pjsip_tx_data *packet = NULL;
|
||||
|
||||
if ((hdata->session->inv_session->state != PJSIP_INV_STATE_DISCONNECTED) &&
|
||||
(pjsip_inv_answer(hdata->session->inv_session, hdata->response_code, NULL, NULL, &packet) == PJ_SUCCESS)) {
|
||||
ast_sip_session_send_response(hdata->session, packet);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Callback that validates the response code
|
||||
*/
|
||||
static int response_code_validator(const char *channel_name,
|
||||
const char *response) {
|
||||
int response_code;
|
||||
|
||||
int rc = ast_str_to_int(response, &response_code);
|
||||
if (rc != 0) {
|
||||
response_code = ast_sip_str2rc(response);
|
||||
if (response_code < 0) {
|
||||
ast_log(LOG_WARNING, "%s: Unrecognized response code parameter '%s'."
|
||||
" Defaulting to 603 DECLINE\n",
|
||||
channel_name, response);
|
||||
return PJSIP_SC_DECLINE;
|
||||
}
|
||||
}
|
||||
|
||||
if (response_code < 400 || response_code > 699) {
|
||||
ast_log(LOG_WARNING, "%s: Response code %d is out of range 400 -> 699."
|
||||
" Defaulting to 603 DECLINE\n",
|
||||
channel_name, response_code);
|
||||
return PJSIP_SC_DECLINE;
|
||||
}
|
||||
return response_code;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Called by pjsip_app_hangup and pjsip_action_hangup
|
||||
* to actually perform the hangup
|
||||
*/
|
||||
static void pjsip_app_hangup_handler(struct ast_channel *chan, int response_code)
|
||||
{
|
||||
struct ast_sip_channel_pvt *channel;
|
||||
struct hangup_data hdata = { NULL, -1 };
|
||||
const char *tag = ast_channel_name(chan);
|
||||
|
||||
hdata.response_code = response_code;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) {
|
||||
ast_log(LOG_WARNING, "%s: Not a PJSIP channel\n", tag);
|
||||
ast_channel_unlock(chan);
|
||||
return;
|
||||
}
|
||||
|
||||
channel = ast_channel_tech_pvt(chan);
|
||||
hdata.session = channel->session;
|
||||
|
||||
if (hdata.session->inv_session->role != PJSIP_ROLE_UAS || (
|
||||
hdata.session->inv_session->state != PJSIP_INV_STATE_INCOMING &&
|
||||
hdata.session->inv_session->state != PJSIP_INV_STATE_EARLY)) {
|
||||
ast_log(LOG_WARNING, "%s: Not an incoming channel or invalid state '%s'\n",
|
||||
tag, pjsip_inv_state_name(hdata.session->inv_session->state));
|
||||
ast_channel_unlock(chan);
|
||||
return;
|
||||
}
|
||||
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
if (ast_sip_push_task_wait_serializer(channel->session->serializer,
|
||||
pjsip_hangup, &hdata) != 0) {
|
||||
ast_log(LOG_WARNING, "%s: failed to push hangup task to serializer\n", tag);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief PJSIPHangup Dialplan App
|
||||
*/
|
||||
int pjsip_app_hangup(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
int response_code;
|
||||
const char *tag = ast_channel_name(chan);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "%s: Missing response code parameter\n", tag);
|
||||
return -1;
|
||||
}
|
||||
|
||||
response_code = response_code_validator(tag, data);
|
||||
|
||||
pjsip_app_hangup_handler(chan, response_code);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief PJSIPHangup Manager Action
|
||||
*/
|
||||
int pjsip_action_hangup(struct mansession *s, const struct message *m)
|
||||
{
|
||||
return ast_manager_hangup_helper(s, m,
|
||||
pjsip_app_hangup_handler, response_code_validator);
|
||||
}
|
||||
|
|
|
@ -1,659 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE docs SYSTEM "appdocsxml.dtd">
|
||||
<docs>
|
||||
<application name="PJSIPHangup" language="en_US">
|
||||
<synopsis>
|
||||
Hangup an incoming PJSIP channel with a SIP response code
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Cause" required="true">
|
||||
<para>May be one of...</para>
|
||||
<enumlist>
|
||||
<enum name="Response code"><para>A numeric response code in the range 400 ->699</para></enum>
|
||||
<enum name="Response code name"><para>A response code name from
|
||||
<literal>third-party/pjproject/source/pjsip/include/pjsip/sip_msg.h</literal>
|
||||
such as <literal>USE_IDENTITY_HEADER</literal> or
|
||||
<literal>PJSIP_SC_USE_IDENTITY_HEADER</literal></para></enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>
|
||||
Hangs up an incoming PJSIP channel and returns the
|
||||
specified SIP response code in the final response to the caller.
|
||||
</para>
|
||||
<para>
|
||||
</para>
|
||||
<warning><para>
|
||||
This function must be called BEFORE anything that
|
||||
might cause any other final (non 1XX) response to be sent.
|
||||
For example calling <literal>Answer()</literal> or
|
||||
<literal>Playback</literal> without the
|
||||
<literal>noanswer</literal> option will cause the call
|
||||
to be answered and a final 200 response to be sent.
|
||||
</para></warning>
|
||||
<para>
|
||||
</para>
|
||||
<para>As with the <literal>Hangup</literal> application,
|
||||
the dialplan will terminate after calling this function.</para>
|
||||
<para>
|
||||
</para>
|
||||
<para>The cause code set on the channel will be translated to
|
||||
a standard ISDN cause code using the table defined in
|
||||
ast_sip_hangup_sip2cause() in res_pjsip.c</para>
|
||||
<para>
|
||||
</para>
|
||||
<example title="Terminate call with 437 response code">
|
||||
same = n,PJSIPHangup(437)
|
||||
</example>
|
||||
<example title="Terminate call with 437 response code using the response code name">
|
||||
same = n,PJSIPHangup(UNSUPPORTED_CERTIFICATE)
|
||||
</example>
|
||||
<example title="Terminate call with 437 response code based on condition">
|
||||
same = n,ExecIf($[${SOMEVALUE} = ${SOME_BAD_VALUE}]?PJSIPHangup(437))
|
||||
</example>
|
||||
</description>
|
||||
</application>
|
||||
|
||||
<manager name="PJSIPHangup" language="en_US">
|
||||
<synopsis>
|
||||
Hangup an incoming PJSIP channel with a SIP response code
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Hangup']/syntax/parameter[@name='Channel'])" />
|
||||
<xi:include xpointer="xpointer(/docs/application[@name='PJSIPHangup']/syntax/parameter[@name='Cause'])" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>
|
||||
Hangs up an incoming PJSIP channel and returns the
|
||||
specified SIP response code in the final response to the caller.
|
||||
</para>
|
||||
<para>
|
||||
</para>
|
||||
<warning><para>
|
||||
This function must be called BEFORE anything that
|
||||
might cause any other final (non 1XX) response to be sent.
|
||||
For example calling <literal>Answer()</literal> or
|
||||
<literal>Playback</literal> without the
|
||||
<literal>noanswer</literal> option will cause the call
|
||||
to be answered and a final 200 response to be sent.
|
||||
</para></warning>
|
||||
<para>
|
||||
</para>
|
||||
<para>The cause code set on the channel will be translated to
|
||||
a standard ISDN cause code using the table defined in
|
||||
ast_sip_hangup_sip2cause() in res_pjsip.c</para>
|
||||
<para>
|
||||
</para>
|
||||
<example title="Terminate call with 437 response code">
|
||||
Action: PJSIPHangup
|
||||
ActionID: 12345678
|
||||
Channel: PJSIP/alice-00000002
|
||||
Cause: 437
|
||||
</example>
|
||||
<example title="Terminate call with 437 response code using the response code name">
|
||||
Action: PJSIPHangup
|
||||
ActionID: 12345678
|
||||
Channel: PJSIP/alice-00000002
|
||||
Cause: UNSUPPORTED_CERTIFICATE
|
||||
</example>
|
||||
</description>
|
||||
</manager>
|
||||
|
||||
<function name="PJSIP_DIAL_CONTACTS" language="en_US">
|
||||
<synopsis>
|
||||
Return a dial string for dialing all contacts on an AOR.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="endpoint" required="true">
|
||||
<para>Name of the endpoint</para>
|
||||
</parameter>
|
||||
<parameter name="aor" required="false">
|
||||
<para>Name of an AOR to use, if not specified the configured AORs on the endpoint are used</para>
|
||||
</parameter>
|
||||
<parameter name="request_user" required="false">
|
||||
<para>Optional request user to use in the request URI</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Returns a properly formatted dial string for dialing all contacts on an AOR.</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_MEDIA_OFFER" language="en_US">
|
||||
<synopsis>
|
||||
Media and codec offerings to be set on an outbound SIP channel prior to dialing.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="media" required="true">
|
||||
<para>types of media offered</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the codecs offered based upon the media choice.</para>
|
||||
<para>When written, sets the codecs to offer when an outbound dial attempt is made,
|
||||
or when a session refresh is sent using <replaceable>PJSIP_SEND_SESSION_REFRESH</replaceable>.
|
||||
</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">PJSIP_SEND_SESSION_REFRESH</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="PJSIP_DTMF_MODE" language="en_US">
|
||||
<since>
|
||||
<version>13.18.0</version>
|
||||
<version>14.7.0</version>
|
||||
<version>15.1.0</version>
|
||||
<version>16.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Get or change the DTMF mode for a SIP call.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the current DTMF mode</para>
|
||||
<para>When written, sets the current DTMF mode</para>
|
||||
<para>This function uses the same DTMF mode naming as the dtmf_mode configuration option</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_MOH_PASSTHROUGH" language="en_US">
|
||||
<synopsis>
|
||||
Get or change the on-hold behavior for a SIP call.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>When read, returns the current moh passthrough mode</para>
|
||||
<para>When written, sets the current moh passthrough mode</para>
|
||||
<para>If <replaceable>yes</replaceable>, on-hold re-INVITEs are sent. If <replaceable>no</replaceable>, music on hold is generated.</para>
|
||||
<para>This function can be used to override the moh_passthrough configuration option</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="PJSIP_SEND_SESSION_REFRESH" language="en_US">
|
||||
<since>
|
||||
<version>13.12.0</version>
|
||||
<version>14.1.0</version>
|
||||
<version>15.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="update_type" required="false">
|
||||
<para>The type of update to send. Default is <literal>invite</literal>.</para>
|
||||
<enumlist>
|
||||
<enum name="invite">
|
||||
<para>Send the session refresh as a re-INVITE.</para>
|
||||
</enum>
|
||||
<enum name="update">
|
||||
<para>Send the session refresh as an UPDATE.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>This function will cause the PJSIP stack to immediately refresh
|
||||
the media session for the channel. This will be done using either a
|
||||
re-INVITE (default) or an UPDATE request.
|
||||
</para>
|
||||
<para>This is most useful when combined with the <replaceable>PJSIP_MEDIA_OFFER</replaceable>
|
||||
dialplan function, as it allows the formats in use on a channel to be
|
||||
re-negotiated after call setup.</para>
|
||||
<warning>
|
||||
<para>The formats the endpoint supports are <emphasis>not</emphasis>
|
||||
checked or enforced by this function. Using this function to offer
|
||||
formats not supported by the endpoint <emphasis>may</emphasis> result
|
||||
in a loss of media.</para>
|
||||
</warning>
|
||||
<example title="Re-negotiate format to g722">
|
||||
; Within some existing extension on an answered channel
|
||||
same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722)
|
||||
same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite)
|
||||
</example>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="function">PJSIP_MEDIA_OFFER</ref>
|
||||
</see-also>
|
||||
</function>
|
||||
<function name="PJSIP_PARSE_URI" language="en_US">
|
||||
<since>
|
||||
<version>13.24.0</version>
|
||||
<version>16.1.0</version>
|
||||
<version>17.0.0</version>
|
||||
</since>
|
||||
<synopsis>
|
||||
Parse an uri and return a type part of the URI.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="uri" required="true">
|
||||
<para>URI to parse</para>
|
||||
</parameter>
|
||||
<parameter name="type" required="true">
|
||||
<para>The <literal>type</literal> parameter specifies which URI part to read</para>
|
||||
<enumlist>
|
||||
<enum name="display">
|
||||
<para>Display name.</para>
|
||||
</enum>
|
||||
<enum name="scheme">
|
||||
<para>URI scheme.</para>
|
||||
</enum>
|
||||
<enum name="user">
|
||||
<para>User part.</para>
|
||||
</enum>
|
||||
<enum name="passwd">
|
||||
<para>Password part.</para>
|
||||
</enum>
|
||||
<enum name="host">
|
||||
<para>Host part.</para>
|
||||
</enum>
|
||||
<enum name="port">
|
||||
<para>Port number, or zero.</para>
|
||||
</enum>
|
||||
<enum name="user_param">
|
||||
<para>User parameter.</para>
|
||||
</enum>
|
||||
<enum name="method_param">
|
||||
<para>Method parameter.</para>
|
||||
</enum>
|
||||
<enum name="transport_param">
|
||||
<para>Transport parameter.</para>
|
||||
</enum>
|
||||
<enum name="ttl_param">
|
||||
<para>TTL param, or -1.</para>
|
||||
</enum>
|
||||
<enum name="lr_param">
|
||||
<para>Loose routing param, or zero.</para>
|
||||
</enum>
|
||||
<enum name="maddr_param">
|
||||
<para>Maddr param.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Parse an URI and return a specified part of the URI.</para>
|
||||
</description>
|
||||
</function>
|
||||
|
||||
<info name="CHANNEL" language="en_US" tech="PJSIP">
|
||||
<enumlist>
|
||||
<enum name="rtp">
|
||||
<para>R/O Retrieve media related information.</para>
|
||||
<parameter name="type" required="true">
|
||||
<para>When <replaceable>rtp</replaceable> is specified, the
|
||||
<literal>type</literal> parameter must be provided. It specifies
|
||||
which RTP parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="src">
|
||||
<para>Retrieve the local address for RTP.</para>
|
||||
</enum>
|
||||
<enum name="dest">
|
||||
<para>Retrieve the remote address for RTP.</para>
|
||||
</enum>
|
||||
<enum name="direct">
|
||||
<para>If direct media is enabled, this address is the remote address
|
||||
used for RTP.</para>
|
||||
</enum>
|
||||
<enum name="secure">
|
||||
<para>Whether or not the media stream is encrypted.</para>
|
||||
<enumlist>
|
||||
<enum name="0">
|
||||
<para>The media stream is not encrypted.</para>
|
||||
</enum>
|
||||
<enum name="1">
|
||||
<para>The media stream is encrypted.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="hold">
|
||||
<para>Whether or not the media stream is currently restricted
|
||||
due to a call hold.</para>
|
||||
<enumlist>
|
||||
<enum name="0">
|
||||
<para>The media stream is not held.</para>
|
||||
</enum>
|
||||
<enum name="1">
|
||||
<para>The media stream is held.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="media_type" required="false">
|
||||
<para>When <replaceable>rtp</replaceable> is specified, the
|
||||
<literal>media_type</literal> parameter may be provided. It specifies
|
||||
which media stream the chosen RTP parameter should be retrieved
|
||||
from.</para>
|
||||
<enumlist>
|
||||
<enum name="audio">
|
||||
<para>Retrieve information from the audio media stream.</para>
|
||||
<note><para>If not specified, <literal>audio</literal> is used
|
||||
by default.</para></note>
|
||||
</enum>
|
||||
<enum name="video">
|
||||
<para>Retrieve information from the video media stream.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
<enum name="rtcp">
|
||||
<para>R/O Retrieve RTCP statistics.</para>
|
||||
<parameter name="statistic" required="true">
|
||||
<para>When <replaceable>rtcp</replaceable> is specified, the
|
||||
<literal>statistic</literal> parameter must be provided. It specifies
|
||||
which RTCP statistic parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="all">
|
||||
<para>Retrieve a summary of all RTCP statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="ssrc">
|
||||
<para>Our Synchronization Source identifier</para>
|
||||
</enum>
|
||||
<enum name="themssrc">
|
||||
<para>Their Synchronization Source identifier</para>
|
||||
</enum>
|
||||
<enum name="lp">
|
||||
<para>Our lost packet count</para>
|
||||
</enum>
|
||||
<enum name="rxjitter">
|
||||
<para>Received packet jitter</para>
|
||||
</enum>
|
||||
<enum name="rxcount">
|
||||
<para>Received packet count</para>
|
||||
</enum>
|
||||
<enum name="txjitter">
|
||||
<para>Transmitted packet jitter</para>
|
||||
</enum>
|
||||
<enum name="txcount">
|
||||
<para>Transmitted packet count</para>
|
||||
</enum>
|
||||
<enum name="rlp">
|
||||
<para>Remote lost packet count</para>
|
||||
</enum>
|
||||
<enum name="rtt">
|
||||
<para>Round trip time</para>
|
||||
</enum>
|
||||
<enum name="txmes">
|
||||
<para>Transmitted Media Experience Score</para>
|
||||
</enum>
|
||||
<enum name="rxmes">
|
||||
<para>Received Media Experience Score</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_jitter">
|
||||
<para>Retrieve a summary of all RTCP Jitter statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrxjitter">
|
||||
<para>Our minimum jitter</para>
|
||||
</enum>
|
||||
<enum name="maxrxjitter">
|
||||
<para>Our max jitter</para>
|
||||
</enum>
|
||||
<enum name="avgrxjitter">
|
||||
<para>Our average jitter</para>
|
||||
</enum>
|
||||
<enum name="stdevrxjitter">
|
||||
<para>Our jitter standard deviation</para>
|
||||
</enum>
|
||||
<enum name="reported_minjitter">
|
||||
<para>Their minimum jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_maxjitter">
|
||||
<para>Their max jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_avgjitter">
|
||||
<para>Their average jitter</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevjitter">
|
||||
<para>Their jitter standard deviation</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_loss">
|
||||
<para>Retrieve a summary of all RTCP packet loss statistics.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrxlost">
|
||||
<para>Our minimum lost packets</para>
|
||||
</enum>
|
||||
<enum name="maxrxlost">
|
||||
<para>Our max lost packets</para>
|
||||
</enum>
|
||||
<enum name="avgrxlost">
|
||||
<para>Our average lost packets</para>
|
||||
</enum>
|
||||
<enum name="stdevrxlost">
|
||||
<para>Our lost packets standard deviation</para>
|
||||
</enum>
|
||||
<enum name="reported_minlost">
|
||||
<para>Their minimum lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_maxlost">
|
||||
<para>Their max lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_avglost">
|
||||
<para>Their average lost packets</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevlost">
|
||||
<para>Their lost packets standard deviation</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_rtt">
|
||||
<para>Retrieve a summary of all RTCP round trip time information.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minrtt">
|
||||
<para>Minimum round trip time</para>
|
||||
</enum>
|
||||
<enum name="maxrtt">
|
||||
<para>Maximum round trip time</para>
|
||||
</enum>
|
||||
<enum name="avgrtt">
|
||||
<para>Average round trip time</para>
|
||||
</enum>
|
||||
<enum name="stdevrtt">
|
||||
<para>Standard deviation round trip time</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="all_mes">
|
||||
<para>Retrieve a summary of all RTCP Media Experience Score information.</para>
|
||||
<para>The following data items are returned in a semi-colon
|
||||
delineated list:</para>
|
||||
<enumlist>
|
||||
<enum name="minmes">
|
||||
<para>Minimum MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="maxmes">
|
||||
<para>Maximum MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="avgmes">
|
||||
<para>Average MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="stdevmes">
|
||||
<para>Standard deviation MES based on us analysing received packets.</para>
|
||||
</enum>
|
||||
<enum name="reported_minmes">
|
||||
<para>Minimum MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
<enum name="reported_maxmes">
|
||||
<para>Maximum MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
<enum name="reported_avgmes">
|
||||
<para>Average MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
<enum name="reported_stdevmes">
|
||||
<para>Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="txcount"><para>Transmitted packet count</para></enum>
|
||||
<enum name="rxcount"><para>Received packet count</para></enum>
|
||||
<enum name="txjitter"><para>Transmitted packet jitter</para></enum>
|
||||
<enum name="rxjitter"><para>Received packet jitter</para></enum>
|
||||
<enum name="remote_maxjitter"><para>Their max jitter</para></enum>
|
||||
<enum name="remote_minjitter"><para>Their minimum jitter</para></enum>
|
||||
<enum name="remote_normdevjitter"><para>Their average jitter</para></enum>
|
||||
<enum name="remote_stdevjitter"><para>Their jitter standard deviation</para></enum>
|
||||
<enum name="local_maxjitter"><para>Our max jitter</para></enum>
|
||||
<enum name="local_minjitter"><para>Our minimum jitter</para></enum>
|
||||
<enum name="local_normdevjitter"><para>Our average jitter</para></enum>
|
||||
<enum name="local_stdevjitter"><para>Our jitter standard deviation</para></enum>
|
||||
<enum name="txploss"><para>Transmitted packet loss</para></enum>
|
||||
<enum name="rxploss"><para>Received packet loss</para></enum>
|
||||
<enum name="remote_maxrxploss"><para>Their max lost packets</para></enum>
|
||||
<enum name="remote_minrxploss"><para>Their minimum lost packets</para></enum>
|
||||
<enum name="remote_normdevrxploss"><para>Their average lost packets</para></enum>
|
||||
<enum name="remote_stdevrxploss"><para>Their lost packets standard deviation</para></enum>
|
||||
<enum name="local_maxrxploss"><para>Our max lost packets</para></enum>
|
||||
<enum name="local_minrxploss"><para>Our minimum lost packets</para></enum>
|
||||
<enum name="local_normdevrxploss"><para>Our average lost packets</para></enum>
|
||||
<enum name="local_stdevrxploss"><para>Our lost packets standard deviation</para></enum>
|
||||
<enum name="rtt"><para>Round trip time</para></enum>
|
||||
<enum name="maxrtt"><para>Maximum round trip time</para></enum>
|
||||
<enum name="minrtt"><para>Minimum round trip time</para></enum>
|
||||
<enum name="normdevrtt"><para>Average round trip time</para></enum>
|
||||
<enum name="stdevrtt"><para>Standard deviation round trip time</para></enum>
|
||||
<enum name="local_ssrc"><para>Our Synchronization Source identifier</para></enum>
|
||||
<enum name="remote_ssrc"><para>Their Synchronization Source identifier</para></enum>
|
||||
<enum name="txmes"><para>
|
||||
Current MES based on us analyzing rtt, jitter and loss
|
||||
in the actual received RTP stream received from the remote end.
|
||||
I.E. This is the MES for the incoming audio stream.
|
||||
</para></enum>
|
||||
<enum name="rxmes"><para>
|
||||
Current MES based on rtt and the jitter and loss values in
|
||||
RTCP sender and receiver reports we receive from the
|
||||
remote end. I.E. This is the MES for the outgoing audio stream.
|
||||
</para></enum>
|
||||
<enum name="remote_maxmes"><para>Max MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="remote_minmes"><para>Min MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="remote_normdevmes"><para>Average MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="remote_stdevmes"><para>Standard deviation MES based on data we get in Sender and Receiver Reports sent by the remote end</para></enum>
|
||||
<enum name="local_maxmes"><para>Max MES based on us analyzing the received RTP stream</para></enum>
|
||||
<enum name="local_minmes"><para>Min MES based on us analyzing the received RTP stream</para></enum>
|
||||
<enum name="local_normdevmes"><para>Average MES based on us analyzing the received RTP stream</para></enum>
|
||||
<enum name="local_stdevmes"><para>Standard deviation MES based on us analyzing the received RTP stream</para></enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="media_type" required="false">
|
||||
<para>When <replaceable>rtcp</replaceable> is specified, the
|
||||
<literal>media_type</literal> parameter may be provided. It specifies
|
||||
which media stream the chosen RTCP parameter should be retrieved
|
||||
from.</para>
|
||||
<enumlist>
|
||||
<enum name="audio">
|
||||
<para>Retrieve information from the audio media stream.</para>
|
||||
<note><para>If not specified, <literal>audio</literal> is used
|
||||
by default.</para></note>
|
||||
</enum>
|
||||
<enum name="video">
|
||||
<para>Retrieve information from the video media stream.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
<enum name="endpoint">
|
||||
<para>R/O The name of the endpoint associated with this channel.
|
||||
Use the <replaceable>PJSIP_ENDPOINT</replaceable> function to obtain
|
||||
further endpoint related information.</para>
|
||||
</enum>
|
||||
<enum name="contact">
|
||||
<para>R/O The name of the contact associated with this channel.
|
||||
Use the <replaceable>PJSIP_CONTACT</replaceable> function to obtain
|
||||
further contact related information. Note this may not be present and if so
|
||||
is only available on outgoing legs.</para>
|
||||
</enum>
|
||||
<enum name="aor">
|
||||
<para>R/O The name of the AOR associated with this channel.
|
||||
Use the <replaceable>PJSIP_AOR</replaceable> function to obtain
|
||||
further AOR related information. Note this may not be present and if so
|
||||
is only available on outgoing legs.</para>
|
||||
</enum>
|
||||
<enum name="pjsip">
|
||||
<para>R/O Obtain information about the current PJSIP channel and its
|
||||
session.</para>
|
||||
<parameter name="type" required="true">
|
||||
<para>When <replaceable>pjsip</replaceable> is specified, the
|
||||
<literal>type</literal> parameter must be provided. It specifies
|
||||
which signalling parameter to read.</para>
|
||||
<enumlist>
|
||||
<enum name="call-id">
|
||||
<para>The SIP call-id.</para>
|
||||
</enum>
|
||||
<enum name="secure">
|
||||
<para>Whether or not the signalling uses a secure transport.</para>
|
||||
<enumlist>
|
||||
<enum name="0"><para>The signalling uses a non-secure transport.</para></enum>
|
||||
<enum name="1"><para>The signalling uses a secure transport.</para></enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="target_uri">
|
||||
<para>The contact URI where requests are sent.</para>
|
||||
</enum>
|
||||
<enum name="local_uri">
|
||||
<para>The local URI.</para>
|
||||
</enum>
|
||||
<enum name="local_tag">
|
||||
<para>Tag in From header</para>
|
||||
</enum>
|
||||
<enum name="remote_uri">
|
||||
<para>The remote URI.</para>
|
||||
</enum>
|
||||
<enum name="remote_tag">
|
||||
<para>Tag in To header</para>
|
||||
</enum>
|
||||
<enum name="request_uri">
|
||||
<para>The request URI of the incoming <literal>INVITE</literal>
|
||||
associated with the creation of this channel.</para>
|
||||
</enum>
|
||||
<enum name="t38state">
|
||||
<para>The current state of any T.38 fax on this channel.</para>
|
||||
<enumlist>
|
||||
<enum name="DISABLED"><para>T.38 faxing is disabled on this channel.</para></enum>
|
||||
<enum name="LOCAL_REINVITE"><para>Asterisk has sent a <literal>re-INVITE</literal> to the remote end to initiate a T.38 fax.</para></enum>
|
||||
<enum name="REMOTE_REINVITE"><para>The remote end has sent a <literal>re-INVITE</literal> to Asterisk to initiate a T.38 fax.</para></enum>
|
||||
<enum name="ENABLED"><para>A T.38 fax session has been enabled.</para></enum>
|
||||
<enum name="REJECTED"><para>A T.38 fax session was attempted but was rejected.</para></enum>
|
||||
</enumlist>
|
||||
</enum>
|
||||
<enum name="local_addr">
|
||||
<para>On inbound calls, the full IP address and port number that
|
||||
the <literal>INVITE</literal> request was received on. On outbound
|
||||
calls, the full IP address and port number that the <literal>INVITE</literal>
|
||||
request was transmitted from.</para>
|
||||
</enum>
|
||||
<enum name="remote_addr">
|
||||
<para>On inbound calls, the full IP address and port number that
|
||||
the <literal>INVITE</literal> request was received from. On outbound
|
||||
calls, the full IP address and port number that the <literal>INVITE</literal>
|
||||
request was transmitted to.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
<info name="CHANNEL_EXAMPLES" language="en_US" tech="PJSIP">
|
||||
<example title="PJSIP specific CHANNEL examples">
|
||||
; Log the current Call-ID
|
||||
same => n,Log(NOTICE, ${CHANNEL(pjsip,call-id)})
|
||||
|
||||
; Log the destination address of the audio stream
|
||||
same => n,Log(NOTICE, ${CHANNEL(rtp,dest)})
|
||||
|
||||
; Store the round-trip time associated with a
|
||||
; video stream in the CDR field video-rtt
|
||||
same => n,Set(CDR(video-rtt)=${CHANNEL(rtcp,rtt,video)})
|
||||
</example>
|
||||
</info>
|
||||
</docs>
|
|
@ -148,24 +148,4 @@ int pjsip_acf_dial_contacts_read(struct ast_channel *chan, const char *cmd, char
|
|||
*/
|
||||
int pjsip_acf_parse_uri_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
|
||||
|
||||
/*!
|
||||
* \brief Hang up an incoming PJSIP channel with a SIP response code
|
||||
* \param chan The channel the function is called on
|
||||
* \param data SIP response code or name
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int pjsip_app_hangup(struct ast_channel *chan, const char *data);
|
||||
|
||||
/*!
|
||||
* \brief Manager action to hang up an incoming PJSIP channel with a SIP response code
|
||||
* \param s session
|
||||
* \param m message
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int pjsip_action_hangup(struct mansession *s, const struct message *m);
|
||||
|
||||
#endif /* _PJSIP_DIALPLAN_FUNCTIONS */
|
||||
#endif /* _PJSIP_DIALPLAN_FUNCTIONS */
|
|
@ -805,11 +805,6 @@ int analog_available(struct analog_pvt *p)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* If line is being held, definitely not (don't allow call waitings to an on-hook phone) */
|
||||
if (p->cshactive) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If no owner definitely available */
|
||||
if (!p->owner) {
|
||||
offhook = analog_is_off_hook(p);
|
||||
|
@ -1078,8 +1073,6 @@ int analog_call(struct analog_pvt *p, struct ast_channel *ast, const char *rdest
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/* Name and Number */
|
||||
n = ast_channel_connected(ast)->id.name.valid ? ast_channel_connected(ast)->id.name.str : NULL;
|
||||
l = ast_channel_connected(ast)->id.number.valid ? ast_channel_connected(ast)->id.number.str : NULL;
|
||||
if (l) {
|
||||
|
@ -1094,25 +1087,12 @@ int analog_call(struct analog_pvt *p, struct ast_channel *ast, const char *rdest
|
|||
}
|
||||
|
||||
if (p->use_callerid) {
|
||||
const char *qual_var;
|
||||
|
||||
/* Caller ID Name and Number */
|
||||
p->caller.id.name.str = p->lastcid_name;
|
||||
p->caller.id.number.str = p->lastcid_num;
|
||||
p->caller.id.name.valid = ast_channel_connected(ast)->id.name.valid;
|
||||
p->caller.id.number.valid = ast_channel_connected(ast)->id.number.valid;
|
||||
p->caller.id.name.presentation = ast_channel_connected(ast)->id.name.presentation;
|
||||
p->caller.id.number.presentation = ast_channel_connected(ast)->id.number.presentation;
|
||||
|
||||
/* Redirecting Reason */
|
||||
p->redirecting_reason = ast_channel_redirecting(ast)->from.number.valid ? ast_channel_redirecting(ast)->reason.code : -1;
|
||||
|
||||
/* Call Qualifier */
|
||||
ast_channel_lock(ast);
|
||||
/* XXX In the future, we may want to make this a CALLERID or CHANNEL property and fetch it from there. */
|
||||
qual_var = pbx_builtin_getvar_helper(ast, "CALL_QUALIFIER");
|
||||
p->call_qualifier = ast_true(qual_var) ? 1 : 0;
|
||||
ast_channel_unlock(ast);
|
||||
}
|
||||
|
||||
ast_setstate(ast, AST_STATE_RINGING);
|
||||
|
@ -1305,7 +1285,6 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
|
|||
p->channel, idx, p->subs[ANALOG_SUB_REAL].allocd, p->subs[ANALOG_SUB_CALLWAIT].allocd, p->subs[ANALOG_SUB_THREEWAY].allocd);
|
||||
if (idx > -1) {
|
||||
/* Real channel, do some fixup */
|
||||
p->cshactive = 0;
|
||||
p->subs[idx].owner = NULL;
|
||||
p->polarity = POLARITY_IDLE;
|
||||
analog_set_linear_mode(p, idx, 0);
|
||||
|
@ -1472,8 +1451,6 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
|
|||
ast_channel_setoption(ast,AST_OPTION_TDD,&x,sizeof(char),0);
|
||||
p->callwaitcas = 0;
|
||||
analog_set_callwaiting(p, p->permcallwaiting);
|
||||
/* In theory, the below is not necessary since we set hidecallerid = permhidecaller when calls start,
|
||||
* but this ensures the setting is defaulted properly when channels are idle, too. */
|
||||
p->hidecallerid = p->permhidecallerid;
|
||||
analog_set_dialing(p, 0);
|
||||
analog_update_conf(p);
|
||||
|
@ -1764,7 +1741,10 @@ static void *__analog_ss_thread(void *data)
|
|||
|
||||
ast_debug(1, "%s %d\n", __FUNCTION__, p->channel);
|
||||
|
||||
ast_assert(chan != NULL);
|
||||
if (!chan) {
|
||||
/* What happened to the channel? */
|
||||
goto quit;
|
||||
}
|
||||
|
||||
if ((callid = ast_channel_callid(chan))) {
|
||||
ast_callid_threadassoc_add(callid);
|
||||
|
@ -2153,26 +2133,12 @@ static void *__analog_ss_thread(void *data)
|
|||
case ANALOG_SIG_FXOLS:
|
||||
case ANALOG_SIG_FXOGS:
|
||||
case ANALOG_SIG_FXOKS:
|
||||
/* Set our default presentation.
|
||||
* This is necessary because the presentation for each call is independent
|
||||
* (though the default may be the same).
|
||||
* For example, if hidecallerid=yes and somebody makes a call with *82,
|
||||
* then makes a 3-way call, the presentation for the 2nd call should still
|
||||
* be blocked, unless that also had a *82.
|
||||
* For this reason, setting hidecallerid = permhidecallerid on hangup
|
||||
* is NOT sufficient, as the *82 from the first call could "leak" into
|
||||
* subsequent ones made before a hangup, improperly leaking a number
|
||||
* that should have been hidden.
|
||||
*/
|
||||
p->hidecallerid = p->permhidecallerid;
|
||||
|
||||
/* Read the first digit */
|
||||
timeout = analog_get_firstdigit_timeout(p);
|
||||
/* If starting a threeway call, never timeout on the first digit so someone
|
||||
* can use flash-hook as a "hold" feature...
|
||||
* ...Unless three-way dial tone should time out to silence, in which case the default suffices. */
|
||||
if (!p->threewaysilenthold && p->subs[ANALOG_SUB_THREEWAY].owner) {
|
||||
timeout = INT_MAX;
|
||||
can use flash-hook as a "hold" feature */
|
||||
if (p->subs[ANALOG_SUB_THREEWAY].owner) {
|
||||
timeout = 999999;
|
||||
}
|
||||
while (len < AST_MAX_EXTENSION-1) {
|
||||
int is_exten_parking = 0;
|
||||
|
@ -2224,18 +2190,18 @@ static void *__analog_ss_thread(void *data)
|
|||
res = analog_play_tone(p, idx, -1);
|
||||
ast_channel_lock(chan);
|
||||
ast_channel_exten_set(chan, exten);
|
||||
|
||||
/* Properly set the presentation.
|
||||
* We need to do this here as well, because p->hidecallerid might be set
|
||||
* due to permanent blocking, not star-67/star-82 usage. */
|
||||
if (p->hidecallerid) {
|
||||
ast_channel_caller(chan)->id.number.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
|
||||
ast_channel_caller(chan)->id.name.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
|
||||
} else {
|
||||
ast_channel_caller(chan)->id.number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
|
||||
ast_channel_caller(chan)->id.name.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
|
||||
if (!ast_strlen_zero(p->cid_num)) {
|
||||
if (!p->hidecallerid) {
|
||||
ast_set_callerid(chan, p->cid_num, NULL, p->cid_num);
|
||||
} else {
|
||||
ast_set_callerid(chan, NULL, NULL, p->cid_num);
|
||||
}
|
||||
}
|
||||
if (!ast_strlen_zero(p->cid_name)) {
|
||||
if (!p->hidecallerid) {
|
||||
ast_set_callerid(chan, NULL, p->cid_name, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ast_setstate(chan, AST_STATE_RING);
|
||||
ast_channel_unlock(chan);
|
||||
analog_set_echocanceller(p, 1);
|
||||
|
@ -2253,11 +2219,7 @@ static void *__analog_ss_thread(void *data)
|
|||
}
|
||||
} else if (res == 0) {
|
||||
ast_debug(1, "not enough digits (and no ambiguous match)...\n");
|
||||
if (p->threewaysilenthold) {
|
||||
ast_debug(1, "Nothing dialed at three-way dial tone, timed out to silent hold\n");
|
||||
} else {
|
||||
res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
|
||||
}
|
||||
res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
|
||||
analog_wait_event(p);
|
||||
ast_hangup(chan);
|
||||
goto quit;
|
||||
|
@ -2301,11 +2263,9 @@ static void *__analog_ss_thread(void *data)
|
|||
ast_hangup(chan);
|
||||
goto quit;
|
||||
}
|
||||
/* While the DMS-100 allows dialing as many *67s and *82s in succession as one's heart may desire,
|
||||
* the 5ESS does not, it only allows pure toggling (and only once!). So, it's not incorrect
|
||||
* to prevent people from dialing *67 if that won't actually do anything. */
|
||||
|
||||
} else if (!p->hidecallerid && !strcmp(exten, "*67")) {
|
||||
ast_verb(3, "Blocking Caller*ID on %s\n", ast_channel_name(chan));
|
||||
ast_verb(3, "Disabling Caller*ID on %s\n", ast_channel_name(chan));
|
||||
/* Disable Caller*ID if enabled */
|
||||
p->hidecallerid = 1;
|
||||
ast_channel_caller(chan)->id.number.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
|
||||
|
@ -2392,7 +2352,7 @@ static void *__analog_ss_thread(void *data)
|
|||
len = 0;
|
||||
}
|
||||
} else if (p->hidecallerid && !strcmp(exten, "*82")) {
|
||||
ast_verb(3, "Allowing Caller*ID on %s\n", ast_channel_name(chan));
|
||||
ast_verb(3, "Enabling Caller*ID on %s\n", ast_channel_name(chan));
|
||||
/* Enable Caller*ID if enabled */
|
||||
p->hidecallerid = 0;
|
||||
ast_channel_caller(chan)->id.number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
|
||||
|
@ -2753,7 +2713,6 @@ int analog_ss_thread_start(struct analog_pvt *p, struct ast_channel *chan)
|
|||
{
|
||||
pthread_t threadid;
|
||||
|
||||
p->ss_astchan = chan;
|
||||
return ast_pthread_create_detached(&threadid, NULL, __analog_ss_thread, p);
|
||||
}
|
||||
|
||||
|
@ -2816,13 +2775,9 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
|
|||
analog_set_pulsedial(p, (res & ANALOG_EVENT_PULSEDIGIT) ? 1 : 0);
|
||||
ast_debug(1, "Detected %sdigit '%c'\n", (res & ANALOG_EVENT_PULSEDIGIT) ? "pulse ": "", res & 0xff);
|
||||
analog_confmute(p, 0);
|
||||
if (p->dialmode == ANALOG_DIALMODE_BOTH || p->dialmode == ANALOG_DIALMODE_PULSE) {
|
||||
p->subs[idx].f.frametype = AST_FRAME_DTMF_END;
|
||||
p->subs[idx].f.subclass.integer = res & 0xff;
|
||||
analog_handle_dtmf(p, ast, idx, &f);
|
||||
} else {
|
||||
ast_debug(1, "Dropping pulse digit '%c' because pulse dialing disabled on channel %d\n", res & 0xff, p->channel);
|
||||
}
|
||||
p->subs[idx].f.frametype = AST_FRAME_DTMF_END;
|
||||
p->subs[idx].f.subclass.integer = res & 0xff;
|
||||
analog_handle_dtmf(p, ast, idx, &f);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -2937,34 +2892,6 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
|
|||
analog_get_and_handle_alarms(p);
|
||||
cause_code->ast_cause = AST_CAUSE_NETWORK_OUT_OF_ORDER;
|
||||
case ANALOG_EVENT_ONHOOK:
|
||||
if (p->calledsubscriberheld && (p->sig == ANALOG_SIG_FXOLS || p->sig == ANALOG_SIG_FXOGS || p->sig == ANALOG_SIG_FXOKS) && idx == ANALOG_SUB_REAL) {
|
||||
ast_debug(4, "Channel state on %s is %d\n", ast_channel_name(ast), ast_channel_state(ast));
|
||||
/* Called Subscriber Held: don't let the called party hang up on an incoming call immediately (if it's the only call). */
|
||||
if (p->subs[ANALOG_SUB_CALLWAIT].owner || p->subs[ANALOG_SUB_THREEWAY].owner) {
|
||||
ast_debug(2, "Letting this call hang up normally, since it's not the only call\n");
|
||||
} else if (!p->owner || !p->subs[ANALOG_SUB_REAL].owner || ast_channel_state(ast) != AST_STATE_UP) {
|
||||
ast_debug(2, "Called Subscriber Held does not apply: channel state is %d\n", ast_channel_state(ast));
|
||||
} else if (!p->owner || !p->subs[ANALOG_SUB_REAL].owner || strcmp(ast_channel_appl(p->subs[ANALOG_SUB_REAL].owner), "AppDial")) {
|
||||
/* Called Subscriber held only applies to incoming calls, not outgoing calls.
|
||||
* We can't use p->outgoing because that is always true, for both incoming and outgoing calls, so it's not accurate.
|
||||
* We can check the channel application/data instead.
|
||||
* For incoming calls to the channel, it will look like: AppDial / (Outgoing Line)
|
||||
* We only want this behavior for regular calls anyways (and not, say, Queue),
|
||||
* so this would actually work great. But accessing ast_channel_appl can cause a crash if there are no calls left,
|
||||
* so this check must occur AFTER we confirm the channel state *is* still UP.
|
||||
*/
|
||||
ast_debug(2, "Called Subscriber Held does not apply: not an incoming call\n");
|
||||
} else if (analog_is_off_hook(p)) {
|
||||
ast_log(LOG_WARNING, "Got ONHOOK but channel %d is off hook?\n", p->channel); /* Shouldn't happen */
|
||||
} else {
|
||||
ast_verb(3, "Holding incoming call %s for channel %d\n", ast_channel_name(ast), p->channel);
|
||||
/* Inhibit dahdi_hangup from getting called, and do nothing else now.
|
||||
* When the DAHDI channel goes off hook again, it'll just get reconnected with the incoming call,
|
||||
* to which, as far as its concerned, nothing has happened. */
|
||||
p->cshactive = 1; /* Keep track that this DAHDI channel is currently being held by an incoming call. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
ast_queue_control_data(ast, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
|
||||
ast_channel_hangupcause_hash_set(ast, cause_code, data_size);
|
||||
switch (p->sig) {
|
||||
|
@ -3804,32 +3731,6 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
|
|||
/* Handle an event on a given channel for the monitor thread. */
|
||||
switch (event) {
|
||||
case ANALOG_EVENT_WINKFLASH:
|
||||
case ANALOG_EVENT_RINGBEGIN:
|
||||
switch (i->sig) {
|
||||
case ANALOG_SIG_FXSLS:
|
||||
case ANALOG_SIG_FXSGS:
|
||||
case ANALOG_SIG_FXSKS:
|
||||
if (i->immediate) {
|
||||
if (i->use_callerid || i->usedistinctiveringdetection) {
|
||||
ast_log(LOG_WARNING, "Can't start PBX immediately, must wait for Caller ID / distinctive ring\n");
|
||||
} else {
|
||||
/* If we don't care about Caller ID or Distinctive Ring, then there's
|
||||
* no need to wait for anything before accepting the call, as
|
||||
* waiting will buy us nothing.
|
||||
* So if the channel is configured for immediate, actually start immediately
|
||||
* and get the show on the road as soon as possible. */
|
||||
ast_debug(1, "Disabling ring timeout (previously %d) to begin handling immediately\n", i->ringt_base);
|
||||
analog_set_ringtimeout(i, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Fall through */
|
||||
if (!(ISTRUNK(i) && i->immediate && !i->use_callerid && !i->usedistinctiveringdetection)) {
|
||||
break;
|
||||
}
|
||||
case ANALOG_EVENT_RINGOFFHOOK:
|
||||
if (i->inalarm) {
|
||||
break;
|
||||
|
@ -3841,7 +3742,6 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
|
|||
case ANALOG_SIG_FXOKS:
|
||||
res = analog_off_hook(i);
|
||||
i->fxsoffhookstate = 1;
|
||||
i->cshactive = 0;
|
||||
if (res && (errno == EBUSY)) {
|
||||
break;
|
||||
}
|
||||
|
@ -3853,10 +3753,7 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
|
|||
if (i->immediate) {
|
||||
analog_set_echocanceller(i, 1);
|
||||
/* The channel is immediately up. Start right away */
|
||||
if (i->immediatering) {
|
||||
/* Play fake ringing, if we've been told to... */
|
||||
res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
|
||||
}
|
||||
res = analog_play_tone(i, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
|
||||
chan = analog_new_ast_channel(i, AST_STATE_RING, 1, ANALOG_SUB_REAL, NULL);
|
||||
if (!chan) {
|
||||
ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel);
|
||||
|
|
|
@ -116,13 +116,6 @@ enum analog_dsp_digitmode {
|
|||
ANALOG_DIGITMODE_MF,
|
||||
};
|
||||
|
||||
enum analog_dialmode {
|
||||
ANALOG_DIALMODE_BOTH = 0,
|
||||
ANALOG_DIALMODE_PULSE,
|
||||
ANALOG_DIALMODE_DTMF,
|
||||
ANALOG_DIALMODE_NONE,
|
||||
};
|
||||
|
||||
enum analog_cid_start {
|
||||
ANALOG_CID_START_POLARITY = 1,
|
||||
ANALOG_CID_START_POLARITY_IN,
|
||||
|
@ -289,19 +282,16 @@ struct analog_pvt {
|
|||
unsigned int ani_wink_time:16; /* Safe wait time before we wink to start ANI spill */
|
||||
|
||||
unsigned int answeronpolarityswitch:1;
|
||||
unsigned int calledsubscriberheld:1; /*!< TRUE if a single incoming call can hold an FXS channel */
|
||||
unsigned int callreturn:1;
|
||||
unsigned int cancallforward:1;
|
||||
unsigned int canpark:1;
|
||||
unsigned int dahditrcallerid:1; /*!< should we use the callerid from incoming call on dahdi transfer or not */
|
||||
unsigned int hanguponpolarityswitch:1;
|
||||
unsigned int immediate:1;
|
||||
unsigned int immediatering:1; /*!< TRUE if ringing should be provided for immediate execution */
|
||||
unsigned int permcallwaiting:1; /*!< TRUE if call waiting is enabled. (Configured option) */
|
||||
unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */
|
||||
unsigned int pulse:1;
|
||||
unsigned int threewaycalling:1;
|
||||
unsigned int threewaysilenthold:1; /*!< Whether to time out a three-way dial tone to silence */
|
||||
unsigned int transfer:1;
|
||||
unsigned int transfertobusy:1; /*!< allow flash-transfers to busy channels */
|
||||
unsigned int use_callerid:1; /*!< Whether or not to use caller id on this channel */
|
||||
|
@ -318,7 +308,6 @@ struct analog_pvt {
|
|||
int channel; /*!< Channel Number */
|
||||
|
||||
enum analog_sigtype outsigmod;
|
||||
enum analog_dialmode dialmode; /*!< Which of pulse and/or tone dialing to support */
|
||||
int echotraining;
|
||||
int cid_signalling; /*!< Asterisk callerid type we're using */
|
||||
int polarityonanswerdelay;
|
||||
|
@ -331,7 +320,6 @@ struct analog_pvt {
|
|||
|
||||
/* XXX: All variables after this are internal */
|
||||
unsigned int callwaiting:1; /*!< TRUE if call waiting is enabled. (Active option) */
|
||||
unsigned int cshactive:1; /*!< TRUE if FXS channel is currently held by an incoming call */
|
||||
unsigned int dialednone:1;
|
||||
unsigned int dialing:1; /*!< TRUE if in the process of dialing digits or sending something */
|
||||
unsigned int dnd:1; /*!< TRUE if Do-Not-Disturb is enabled. */
|
||||
|
@ -346,15 +334,12 @@ struct analog_pvt {
|
|||
* gives a positive reply.
|
||||
*/
|
||||
unsigned int callwaitcas:1;
|
||||
unsigned int call_qualifier:1; /*!< Call qualifier delivery */
|
||||
|
||||
char callwait_num[AST_MAX_EXTENSION];
|
||||
char callwait_name[AST_MAX_EXTENSION];
|
||||
char lastcid_num[AST_MAX_EXTENSION];
|
||||
char lastcid_name[AST_MAX_EXTENSION];
|
||||
struct ast_party_caller caller;
|
||||
int redirecting_reason; /*!< Redirecting reason */
|
||||
|
||||
int cidrings; /*!< Which ring to deliver CID on */
|
||||
char echorest[20];
|
||||
int polarity;
|
||||
|
|
|
@ -0,0 +1,927 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* 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 sip config parsing functions and unit tests
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>deprecated</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "include/sip.h"
|
||||
#include "include/config_parser.h"
|
||||
#include "include/sip_utils.h"
|
||||
|
||||
/*! \brief Parse register=> line in sip.conf
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const char *value, int lineno)
|
||||
{
|
||||
int portnum = 0;
|
||||
int domainport = 0;
|
||||
enum ast_transport transport = AST_TRANSPORT_UDP;
|
||||
char buf[256] = "";
|
||||
char *userpart = NULL, *hostpart = NULL;
|
||||
/* register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] */
|
||||
AST_DECLARE_APP_ARGS(pre1,
|
||||
AST_APP_ARG(peer);
|
||||
AST_APP_ARG(userpart);
|
||||
);
|
||||
AST_DECLARE_APP_ARGS(pre2,
|
||||
AST_APP_ARG(transport);
|
||||
AST_APP_ARG(blank);
|
||||
AST_APP_ARG(userpart);
|
||||
);
|
||||
AST_DECLARE_APP_ARGS(user1,
|
||||
AST_APP_ARG(userpart);
|
||||
AST_APP_ARG(secret);
|
||||
AST_APP_ARG(authuser);
|
||||
);
|
||||
AST_DECLARE_APP_ARGS(user2,
|
||||
AST_APP_ARG(user);
|
||||
AST_APP_ARG(domain);
|
||||
);
|
||||
AST_DECLARE_APP_ARGS(user3,
|
||||
AST_APP_ARG(authuser);
|
||||
AST_APP_ARG(domainport);
|
||||
);
|
||||
AST_DECLARE_APP_ARGS(host1,
|
||||
AST_APP_ARG(hostpart);
|
||||
AST_APP_ARG(expiry);
|
||||
);
|
||||
AST_DECLARE_APP_ARGS(host2,
|
||||
AST_APP_ARG(hostpart);
|
||||
AST_APP_ARG(extension);
|
||||
);
|
||||
AST_DECLARE_APP_ARGS(host3,
|
||||
AST_APP_ARG(host);
|
||||
AST_APP_ARG(port);
|
||||
);
|
||||
|
||||
if (!reg) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
reg->expire = -1;
|
||||
reg->timeout = -1;
|
||||
|
||||
if (!value) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_copy_string(buf, value, sizeof(buf));
|
||||
|
||||
/*
|
||||
* register => [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry]
|
||||
* becomes
|
||||
* userpart => [peer?][transport://]user[@domain][:secret[:authuser]]
|
||||
* hostpart => host[:port][/extension][~expiry]
|
||||
*/
|
||||
if ((hostpart = strrchr(buf, '@'))) {
|
||||
*hostpart++ = '\0';
|
||||
userpart = buf;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(userpart) || ast_strlen_zero(hostpart)) {
|
||||
ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* pre1.peer => peer
|
||||
* pre1.userpart => [transport://]user[@domain][:secret[:authuser]]
|
||||
* hostpart => host[:port][/extension][~expiry]
|
||||
*/
|
||||
AST_NONSTANDARD_RAW_ARGS(pre1, userpart, '?');
|
||||
if (ast_strlen_zero(pre1.userpart)) {
|
||||
pre1.userpart = pre1.peer;
|
||||
pre1.peer = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* pre1.peer => peer
|
||||
* pre2.transport = transport
|
||||
* pre2.userpart => user[@domain][:secret[:authuser]]
|
||||
* hostpart => host[:port][/extension][~expiry]
|
||||
*/
|
||||
AST_NONSTANDARD_RAW_ARGS(pre2, pre1.userpart, '/');
|
||||
if (ast_strlen_zero(pre2.userpart)) {
|
||||
pre2.userpart = pre2.transport;
|
||||
pre2.transport = NULL;
|
||||
} else {
|
||||
pre2.transport[strlen(pre2.transport) - 1] = '\0'; /* Remove trailing : */
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(pre2.blank)) {
|
||||
ast_log(LOG_WARNING, "Format for registration is [peer?][transport://]user[@domain][:secret[:authuser]]@host[:port][/extension][~expiry] at line %d\n", lineno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* pre1.peer => peer
|
||||
* pre2.transport = transport
|
||||
* user1.userpart => user[@domain]
|
||||
* user1.secret => secret
|
||||
* user1.authuser => authuser
|
||||
* hostpart => host[:port][/extension][~expiry]
|
||||
*/
|
||||
AST_NONSTANDARD_RAW_ARGS(user1, pre2.userpart, ':');
|
||||
|
||||
/*
|
||||
* pre1.peer => peer
|
||||
* pre2.transport = transport
|
||||
* user1.userpart => user[@domain]
|
||||
* user1.secret => secret
|
||||
* user1.authuser => authuser
|
||||
* host1.hostpart => host[:port][/extension]
|
||||
* host1.expiry => [expiry]
|
||||
*/
|
||||
AST_NONSTANDARD_RAW_ARGS(host1, hostpart, '~');
|
||||
|
||||
/*
|
||||
* pre1.peer => peer
|
||||
* pre2.transport = transport
|
||||
* user1.userpart => user[@domain]
|
||||
* user1.secret => secret
|
||||
* user1.authuser => authuser
|
||||
* host2.hostpart => host[:port]
|
||||
* host2.extension => [extension]
|
||||
* host1.expiry => [expiry]
|
||||
*/
|
||||
AST_NONSTANDARD_RAW_ARGS(host2, host1.hostpart, '/');
|
||||
|
||||
/*
|
||||
* pre1.peer => peer
|
||||
* pre2.transport = transport
|
||||
* user1.userpart => user[@domain]
|
||||
* user1.secret => secret
|
||||
* user1.authuser => authuser
|
||||
* host3.host => host
|
||||
* host3.port => port
|
||||
* host2.extension => extension
|
||||
* host1.expiry => expiry
|
||||
*/
|
||||
AST_NONSTANDARD_RAW_ARGS(host3, host2.hostpart, ':');
|
||||
|
||||
/*
|
||||
* pre1.peer => peer
|
||||
* pre2.transport = transport
|
||||
* user2.user => user
|
||||
* user2.domain => domain
|
||||
* user1.secret => secret
|
||||
* user1.authuser => authuser
|
||||
* host3.host => host
|
||||
* host3.port => port
|
||||
* host2.extension => extension
|
||||
* host1.expiry => expiry
|
||||
*/
|
||||
AST_NONSTANDARD_RAW_ARGS(user2, user1.userpart, '@');
|
||||
|
||||
/*
|
||||
* pre1.peer => peer
|
||||
* pre2.transport = transport
|
||||
* user2.user => user
|
||||
* user2.domain => domain
|
||||
* user1.secret => secret
|
||||
* user3.authuser => authuser
|
||||
* user3.domainport => domainport
|
||||
* host3.host => host
|
||||
* host3.port => port
|
||||
* host2.extension => extension
|
||||
* host1.expiry => expiry
|
||||
*/
|
||||
AST_NONSTANDARD_RAW_ARGS(user3, user1.authuser, ':');
|
||||
|
||||
/* Reordering needed due to fields being [(:secret[:username])|(:regdomainport:secret:username)]
|
||||
but parsing being [secret[:username[:regdomainport]]] */
|
||||
if (user3.argc == 2) {
|
||||
char *reorder = user3.domainport;
|
||||
user3.domainport = user1.secret;
|
||||
user1.secret = user3.authuser;
|
||||
user3.authuser = reorder;
|
||||
}
|
||||
|
||||
if (host3.port) {
|
||||
if (!(portnum = port_str2int(host3.port, 0))) {
|
||||
ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", host3.port, lineno);
|
||||
}
|
||||
}
|
||||
if (user3.domainport) {
|
||||
if (!(domainport = port_str2int(user3.domainport, 0))) {
|
||||
ast_log(LOG_NOTICE, "'%s' is not a valid domain port number on line %d of sip.conf. using default.\n", user3.domainport, lineno);
|
||||
}
|
||||
}
|
||||
|
||||
/* set transport type */
|
||||
if (!pre2.transport) {
|
||||
transport = AST_TRANSPORT_UDP;
|
||||
} else if (!strncasecmp(pre2.transport, "tcp", 3)) {
|
||||
transport = AST_TRANSPORT_TCP;
|
||||
} else if (!strncasecmp(pre2.transport, "tls", 3)) {
|
||||
transport = AST_TRANSPORT_TLS;
|
||||
} else if (!strncasecmp(pre2.transport, "udp", 3)) {
|
||||
transport = AST_TRANSPORT_UDP;
|
||||
} else {
|
||||
transport = AST_TRANSPORT_UDP;
|
||||
ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", pre2.transport, lineno);
|
||||
}
|
||||
|
||||
/* if no portnum specified, set default for transport */
|
||||
if (!portnum) {
|
||||
if (transport == AST_TRANSPORT_TLS) {
|
||||
portnum = STANDARD_TLS_PORT;
|
||||
} else {
|
||||
portnum = STANDARD_SIP_PORT;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy into sip_registry object */
|
||||
ast_string_field_set(reg, callback, ast_strip_quoted(S_OR(host2.extension, "s"), "\"", "\""));
|
||||
ast_string_field_set(reg, username, ast_strip_quoted(S_OR(user2.user, ""), "\"", "\""));
|
||||
ast_string_field_set(reg, hostname, ast_strip_quoted(S_OR(host3.host, ""), "\"", "\""));
|
||||
ast_string_field_set(reg, authuser, ast_strip_quoted(S_OR(user3.authuser, ""), "\"", "\""));
|
||||
ast_string_field_set(reg, secret, ast_strip_quoted(S_OR(user1.secret, ""), "\"", "\""));
|
||||
ast_string_field_set(reg, peername, ast_strip_quoted(S_OR(pre1.peer, ""), "\"", "\""));
|
||||
ast_string_field_set(reg, regdomain, ast_strip_quoted(S_OR(user2.domain, ""), "\"", "\""));
|
||||
|
||||
reg->transport = transport;
|
||||
reg->portno = portnum;
|
||||
reg->regdomainport = domainport;
|
||||
reg->callid_valid = FALSE;
|
||||
reg->ocseq = INITIAL_CSEQ;
|
||||
reg->refresh = reg->expiry = reg->configured_expiry = (host1.expiry ? atoi(ast_strip_quoted(host1.expiry, "\"", "\"")) : default_expiry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
AST_TEST_DEFINE(sip_parse_register_line_test)
|
||||
{
|
||||
int res = AST_TEST_PASS;
|
||||
struct sip_registry *reg;
|
||||
int default_expiry = 120;
|
||||
const char *reg1 = "name@domain";
|
||||
const char *reg2 = "name:pass@domain";
|
||||
const char *reg3 = "name@namedomain:pass:authuser@domain";
|
||||
const char *reg4 = "name@namedomain:pass:authuser@domain/extension";
|
||||
const char *reg5 = "tcp://name@namedomain:pass:authuser@domain/extension";
|
||||
const char *reg6 = "tls://name@namedomain:pass:authuser@domain/extension~111";
|
||||
const char *reg7 = "peer?tcp://name@namedomain:pass:authuser@domain:1234/extension~111";
|
||||
const char *reg8 = "peer?name@namedomain:pass:authuser@domain:1234/extension~111";
|
||||
const char *reg9 = "peer?name:pass:authuser:1234/extension~111";
|
||||
const char *reg10 = "@domin:1234";
|
||||
const char *reg12 = "name@namedomain:4321:pass:authuser@domain";
|
||||
const char *reg13 = "name@namedomain:4321::@domain";
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "sip_parse_register_line_test";
|
||||
info->category = "/channels/chan_sip/";
|
||||
info->summary = "tests sip register line parsing";
|
||||
info->description =
|
||||
"Tests parsing of various register line configurations. "
|
||||
"Verifies output matches expected behavior.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
/* ---Test reg 1, simple config --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg1, 1) ||
|
||||
strcmp(reg->callback, "s") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "") ||
|
||||
strcmp(reg->secret, "") ||
|
||||
strcmp(reg->peername, "") ||
|
||||
reg->transport != AST_TRANSPORT_UDP ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != default_expiry ||
|
||||
reg->expiry != default_expiry ||
|
||||
reg->configured_expiry != default_expiry ||
|
||||
reg->portno != STANDARD_SIP_PORT ||
|
||||
(reg->regdomainport) ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 1: simple config failed\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 2, add secret --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg2, 1) ||
|
||||
strcmp(reg->callback, "s") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "") ||
|
||||
strcmp(reg->secret, "pass") ||
|
||||
strcmp(reg->peername, "") ||
|
||||
reg->transport != AST_TRANSPORT_UDP ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != default_expiry ||
|
||||
reg->expiry != default_expiry ||
|
||||
reg->configured_expiry != default_expiry ||
|
||||
reg->portno != STANDARD_SIP_PORT ||
|
||||
(reg->regdomainport) ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 2: add secret failed\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 3, add userdomain and authuser --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg3, 1) ||
|
||||
strcmp(reg->callback, "s") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "namedomain") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "authuser") ||
|
||||
strcmp(reg->secret, "pass") ||
|
||||
strcmp(reg->peername, "") ||
|
||||
reg->transport != AST_TRANSPORT_UDP ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != default_expiry ||
|
||||
reg->expiry != default_expiry ||
|
||||
reg->configured_expiry != default_expiry ||
|
||||
reg->portno != STANDARD_SIP_PORT ||
|
||||
(reg->regdomainport) ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 3: add userdomain and authuser failed\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 4, add callback extension --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg4, 1) ||
|
||||
strcmp(reg->callback, "extension") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "namedomain") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "authuser") ||
|
||||
strcmp(reg->secret, "pass") ||
|
||||
strcmp(reg->peername, "") ||
|
||||
reg->transport != AST_TRANSPORT_UDP ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != default_expiry ||
|
||||
reg->expiry != default_expiry ||
|
||||
reg->configured_expiry != default_expiry ||
|
||||
reg->portno != STANDARD_SIP_PORT ||
|
||||
(reg->regdomainport) ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 4: add callback extension failed\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 5, add transport --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg5, 1) ||
|
||||
strcmp(reg->callback, "extension") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "namedomain") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "authuser") ||
|
||||
strcmp(reg->secret, "pass") ||
|
||||
strcmp(reg->peername, "") ||
|
||||
reg->transport != AST_TRANSPORT_TCP ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != default_expiry ||
|
||||
reg->expiry != default_expiry ||
|
||||
reg->configured_expiry != default_expiry ||
|
||||
reg->portno != STANDARD_SIP_PORT ||
|
||||
(reg->regdomainport) ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 5: add transport failed\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 6, change to tls transport, add expiry --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg6, 1) ||
|
||||
strcmp(reg->callback, "extension") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "namedomain") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "authuser") ||
|
||||
strcmp(reg->secret, "pass") ||
|
||||
strcmp(reg->peername, "") ||
|
||||
reg->transport != AST_TRANSPORT_TLS ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != 111 ||
|
||||
reg->expiry != 111 ||
|
||||
reg->configured_expiry != 111 ||
|
||||
reg->portno != STANDARD_TLS_PORT ||
|
||||
(reg->regdomainport) ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 6: change to tls transport and add expiry failed\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 7, change transport to tcp, add custom port, and add peer --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg7, 1) ||
|
||||
strcmp(reg->callback, "extension") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "namedomain") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "authuser") ||
|
||||
strcmp(reg->secret, "pass") ||
|
||||
strcmp(reg->peername, "peer") ||
|
||||
reg->transport != AST_TRANSPORT_TCP ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != 111 ||
|
||||
reg->expiry != 111 ||
|
||||
reg->configured_expiry != 111 ||
|
||||
reg->portno != 1234 ||
|
||||
(reg->regdomainport) ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 7, change transport to tcp, add custom port, and add peer failed.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 8, remove transport --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg8, 1) ||
|
||||
strcmp(reg->callback, "extension") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "namedomain") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "authuser") ||
|
||||
strcmp(reg->secret, "pass") ||
|
||||
strcmp(reg->peername, "peer") ||
|
||||
reg->transport != AST_TRANSPORT_UDP ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != 111 ||
|
||||
reg->expiry != 111 ||
|
||||
reg->configured_expiry != 111 ||
|
||||
reg->portno != 1234 ||
|
||||
(reg->regdomainport) ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 8, remove transport failed.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 9, missing domain, expected to fail --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (!sip_parse_register_line(reg, default_expiry, reg9, 1)) {
|
||||
ast_test_status_update(test,
|
||||
"Test 9, missing domain, expected to fail but did not.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 10, missing user, expected to fail --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (!sip_parse_register_line(reg, default_expiry, reg10, 1)) {
|
||||
ast_test_status_update(test,
|
||||
"Test 10, missing user expected to fail but did not\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg 11, no registry object, expected to fail--- */
|
||||
if (!sip_parse_register_line(NULL, default_expiry, reg1, 1)) {
|
||||
ast_test_status_update(test,
|
||||
"Test 11, no registry object, expected to fail but did not.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
/* ---Test reg 12, no registry line, expected to fail --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (!sip_parse_register_line(reg, default_expiry, NULL, 1)) {
|
||||
|
||||
ast_test_status_update(test,
|
||||
"Test 12, NULL register line expected to fail but did not.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg13, add domain port --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg12, 1) ||
|
||||
strcmp(reg->callback, "s") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "namedomain") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "authuser") ||
|
||||
strcmp(reg->secret, "pass") ||
|
||||
strcmp(reg->peername, "") ||
|
||||
reg->transport != AST_TRANSPORT_UDP ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != default_expiry ||
|
||||
reg->expiry != default_expiry ||
|
||||
reg->configured_expiry != default_expiry ||
|
||||
reg->portno != STANDARD_SIP_PORT ||
|
||||
reg->regdomainport != 4321 ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 13, add domain port failed.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
/* ---Test reg14, domain port without secret --- */
|
||||
if (!(reg = ast_calloc_with_stringfields(1, struct sip_registry, 256))) {
|
||||
goto alloc_fail;
|
||||
} else if (
|
||||
sip_parse_register_line(reg, default_expiry, reg13, 1) ||
|
||||
strcmp(reg->callback, "s") ||
|
||||
strcmp(reg->username, "name") ||
|
||||
strcmp(reg->regdomain, "namedomain") ||
|
||||
strcmp(reg->hostname, "domain") ||
|
||||
strcmp(reg->authuser, "") ||
|
||||
strcmp(reg->secret, "") ||
|
||||
strcmp(reg->peername, "") ||
|
||||
reg->transport != AST_TRANSPORT_UDP ||
|
||||
reg->timeout != -1 ||
|
||||
reg->expire != -1 ||
|
||||
reg->refresh != default_expiry ||
|
||||
reg->expiry != default_expiry ||
|
||||
reg->configured_expiry != default_expiry ||
|
||||
reg->portno != STANDARD_SIP_PORT ||
|
||||
reg->regdomainport != 4321 ||
|
||||
reg->callid_valid != FALSE ||
|
||||
reg->ocseq != INITIAL_CSEQ) {
|
||||
|
||||
ast_test_status_update(test, "Test 14, domain port without secret failed.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
ast_string_field_free_memory(reg);
|
||||
ast_free(reg);
|
||||
|
||||
|
||||
return res;
|
||||
|
||||
alloc_fail:
|
||||
ast_test_status_update(test, "Out of memory. \n");
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum ast_transport *transport)
|
||||
{
|
||||
char *port;
|
||||
|
||||
if (ast_strlen_zero(line)) {
|
||||
*hostname = NULL;
|
||||
return -1;
|
||||
}
|
||||
if ((*hostname = strstr(line, "://"))) {
|
||||
*hostname += 3;
|
||||
|
||||
if (!strncasecmp(line, "tcp", 3)) {
|
||||
*transport = AST_TRANSPORT_TCP;
|
||||
} else if (!strncasecmp(line, "tls", 3)) {
|
||||
*transport = AST_TRANSPORT_TLS;
|
||||
} else if (!strncasecmp(line, "udp", 3)) {
|
||||
*transport = AST_TRANSPORT_UDP;
|
||||
} else if (lineno) {
|
||||
ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", line, lineno);
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type in sip config. defaulting to udp.\n", line);
|
||||
}
|
||||
} else {
|
||||
*hostname = line;
|
||||
*transport = AST_TRANSPORT_UDP;
|
||||
}
|
||||
|
||||
if ((line = strrchr(*hostname, '@')))
|
||||
line++;
|
||||
else
|
||||
line = *hostname;
|
||||
|
||||
if (ast_sockaddr_split_hostport(line, hostname, &port, 0) == 0) {
|
||||
if (lineno) {
|
||||
ast_log(LOG_WARNING, "Cannot parse host '%s' on line %d of sip.conf.\n",
|
||||
line, lineno);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Cannot parse host '%s' in sip config.\n", line);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (port) {
|
||||
if (!sscanf(port, "%5d", portnum)) {
|
||||
if (lineno) {
|
||||
ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", port, lineno);
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "'%s' is not a valid port number in sip config. using default.\n", port);
|
||||
}
|
||||
port = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!port) {
|
||||
if (*transport & AST_TRANSPORT_TLS) {
|
||||
*portnum = STANDARD_TLS_PORT;
|
||||
} else {
|
||||
*portnum = STANDARD_SIP_PORT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
AST_TEST_DEFINE(sip_parse_host_line_test)
|
||||
{
|
||||
int res = AST_TEST_PASS;
|
||||
char *host;
|
||||
int port;
|
||||
enum ast_transport transport;
|
||||
char host1[] = "www.blah.com";
|
||||
char host2[] = "tcp://www.blah.com";
|
||||
char host3[] = "tls://10.10.10.10";
|
||||
char host4[] = "tls://10.10.10.10:1234";
|
||||
char host5[] = "10.10.10.10:1234";
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "sip_parse_host_line_test";
|
||||
info->category = "/channels/chan_sip/";
|
||||
info->summary = "tests sip.conf host line parsing";
|
||||
info->description =
|
||||
"Tests parsing of various host line configurations. "
|
||||
"Verifies output matches expected behavior.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
/* test 1, simple host */
|
||||
sip_parse_host(host1, 1, &host, &port, &transport);
|
||||
if (port != STANDARD_SIP_PORT ||
|
||||
ast_strlen_zero(host) || strcmp(host, "www.blah.com") ||
|
||||
transport != AST_TRANSPORT_UDP) {
|
||||
ast_test_status_update(test, "Test 1: simple host failed.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
/* test 2, add tcp transport */
|
||||
sip_parse_host(host2, 1, &host, &port, &transport);
|
||||
if (port != STANDARD_SIP_PORT ||
|
||||
ast_strlen_zero(host) || strcmp(host, "www.blah.com") ||
|
||||
transport != AST_TRANSPORT_TCP) {
|
||||
ast_test_status_update(test, "Test 2: tcp host failed.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
/* test 3, add tls transport */
|
||||
sip_parse_host(host3, 1, &host, &port, &transport);
|
||||
if (port != STANDARD_TLS_PORT ||
|
||||
ast_strlen_zero(host) || strcmp(host, "10.10.10.10") ||
|
||||
transport != AST_TRANSPORT_TLS) {
|
||||
ast_test_status_update(test, "Test 3: tls host failed. \n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
/* test 4, add custom port with tls */
|
||||
sip_parse_host(host4, 1, &host, &port, &transport);
|
||||
if (port != 1234 || ast_strlen_zero(host) ||
|
||||
strcmp(host, "10.10.10.10") ||
|
||||
transport != AST_TRANSPORT_TLS) {
|
||||
ast_test_status_update(test, "Test 4: tls host with custom port failed.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
/* test 5, simple host with custom port */
|
||||
sip_parse_host(host5, 1, &host, &port, &transport);
|
||||
if (port != 1234 || ast_strlen_zero(host) ||
|
||||
strcmp(host, "10.10.10.10") ||
|
||||
transport != AST_TRANSPORT_UDP) {
|
||||
ast_test_status_update(test, "Test 5: simple host with custom port failed.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
/* test 6, expected failure with NULL input */
|
||||
if (!sip_parse_host(NULL, 1, &host, &port, &transport)) {
|
||||
ast_test_status_update(test, "Test 6: expected error on NULL input did not occur.\n");
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! \brief Parse the comma-separated nat= option values */
|
||||
void sip_parse_nat_option(const char *value, struct ast_flags *mask, struct ast_flags *flags)
|
||||
{
|
||||
char *parse, *this;
|
||||
|
||||
if (!(parse = ast_strdupa(value))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Since we need to completely override the general settings if we are being called
|
||||
* later for a peer, always set the flags for all options on the mask */
|
||||
ast_set_flag(&mask[0], SIP_NAT_FORCE_RPORT);
|
||||
ast_set_flag(&mask[1], SIP_PAGE2_SYMMETRICRTP);
|
||||
ast_set_flag(&mask[2], SIP_PAGE3_NAT_AUTO_RPORT);
|
||||
ast_set_flag(&mask[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
|
||||
|
||||
while ((this = strsep(&parse, ","))) {
|
||||
if (ast_false(this)) {
|
||||
ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
|
||||
ast_clear_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
|
||||
ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
|
||||
ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
|
||||
break; /* It doesn't make sense to have no + something else */
|
||||
} else if (!strcasecmp(this, "yes")) {
|
||||
ast_log(LOG_WARNING, "nat=yes is deprecated, use nat=force_rport,comedia instead\n");
|
||||
ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
|
||||
ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
|
||||
ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
|
||||
ast_clear_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
|
||||
break; /* It doesn't make sense to have yes + something else */
|
||||
} else if (!strcasecmp(this, "force_rport") && !ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT)) {
|
||||
ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
|
||||
} else if (!strcasecmp(this, "comedia") && !ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA)) {
|
||||
ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
|
||||
} else if (!strcasecmp(this, "auto_force_rport")) {
|
||||
ast_set_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT);
|
||||
/* In case someone did something dumb like nat=force_rport,auto_force_rport */
|
||||
ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
|
||||
} else if (!strcasecmp(this, "auto_comedia")) {
|
||||
ast_set_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA);
|
||||
/* In case someone did something dumb like nat=comedia,auto_comedia*/
|
||||
ast_clear_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
#define TEST_FORCE_RPORT 1 << 0
|
||||
#define TEST_COMEDIA 1 << 1
|
||||
#define TEST_AUTO_FORCE_RPORT 1 << 2
|
||||
#define TEST_AUTO_COMEDIA 1 << 3
|
||||
static int match_nat_options(int val, struct ast_flags *flags)
|
||||
{
|
||||
if ((!ast_test_flag(&flags[0], SIP_NAT_FORCE_RPORT)) != !(val & TEST_FORCE_RPORT)) {
|
||||
return 0;
|
||||
}
|
||||
if (!ast_test_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP) != !(val & TEST_COMEDIA)) {
|
||||
return 0;
|
||||
}
|
||||
if (!ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_RPORT) != !(val & TEST_AUTO_FORCE_RPORT)) {
|
||||
return 0;
|
||||
}
|
||||
if (!ast_test_flag(&flags[2], SIP_PAGE3_NAT_AUTO_COMEDIA) != !(val & TEST_AUTO_COMEDIA)) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(sip_parse_nat_test)
|
||||
{
|
||||
int i, res = AST_TEST_PASS;
|
||||
struct ast_flags mask[3] = {{0}}, flags[3] = {{0}};
|
||||
struct {
|
||||
const char *str;
|
||||
int i;
|
||||
} options[] = {
|
||||
{ "yes", TEST_FORCE_RPORT | TEST_COMEDIA },
|
||||
{ "no", 0 },
|
||||
{ "force_rport", TEST_FORCE_RPORT },
|
||||
{ "comedia", TEST_COMEDIA },
|
||||
{ "auto_force_rport", TEST_AUTO_FORCE_RPORT },
|
||||
{ "auto_comedia", TEST_AUTO_COMEDIA },
|
||||
{ "force_rport,auto_force_rport", TEST_AUTO_FORCE_RPORT },
|
||||
{ "auto_force_rport,force_rport", TEST_AUTO_FORCE_RPORT },
|
||||
{ "comedia,auto_comedia", TEST_AUTO_COMEDIA },
|
||||
{ "auto_comedia,comedia", TEST_AUTO_COMEDIA },
|
||||
{ "force_rport,comedia", TEST_FORCE_RPORT | TEST_COMEDIA },
|
||||
{ "force_rport,auto_comedia", TEST_FORCE_RPORT | TEST_AUTO_COMEDIA },
|
||||
{ "force_rport,yes,no", TEST_FORCE_RPORT | TEST_COMEDIA },
|
||||
{ "auto_comedia,no,yes", 0 },
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "sip_parse_nat_test";
|
||||
info->category = "/channels/chan_sip/";
|
||||
info->summary = "tests sip.conf nat line parsing";
|
||||
info->description =
|
||||
"Tests parsing of various nat line configurations. "
|
||||
"Verifies output matches expected behavior.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_LEN(options); i++) {
|
||||
sip_parse_nat_option(options[i].str, mask, flags);
|
||||
if (!match_nat_options(options[i].i, flags)) {
|
||||
ast_test_status_update(test, "Failed nat=%s\n", options[i].str);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
memset(flags, 0, sizeof(flags));
|
||||
memset(mask, 0, sizeof(mask));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! \brief SIP test registration */
|
||||
void sip_config_parser_register_tests(void)
|
||||
{
|
||||
AST_TEST_REGISTER(sip_parse_register_line_test);
|
||||
AST_TEST_REGISTER(sip_parse_host_line_test);
|
||||
AST_TEST_REGISTER(sip_parse_nat_test);
|
||||
}
|
||||
|
||||
/*! \brief SIP test registration */
|
||||
void sip_config_parser_unregister_tests(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(sip_parse_register_line_test);
|
||||
AST_TEST_UNREGISTER(sip_parse_host_line_test);
|
||||
AST_TEST_UNREGISTER(sip_parse_nat_test);
|
||||
}
|
|
@ -0,0 +1,515 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* 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 sip channel dialplan functions and unit tests
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>deprecated</support_level>
|
||||
***/
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<info name="CHANNEL" language="en_US" tech="SIP">
|
||||
<enumlist>
|
||||
<enum name="peerip">
|
||||
<para>R/O Get the IP address of the peer.</para>
|
||||
</enum>
|
||||
<enum name="recvip">
|
||||
<para>R/O Get the source IP address of the peer.</para>
|
||||
</enum>
|
||||
<enum name="recvport">
|
||||
<para>R/O Get the source port of the peer.</para>
|
||||
</enum>
|
||||
<enum name="from">
|
||||
<para>R/O Get the URI from the From: header.</para>
|
||||
</enum>
|
||||
<enum name="uri">
|
||||
<para>R/O Get the URI from the Contact: header.</para>
|
||||
</enum>
|
||||
<enum name="ruri">
|
||||
<para>R/O Get the Request-URI from the INVITE header.</para>
|
||||
</enum>
|
||||
<enum name="useragent">
|
||||
<para>R/O Get the useragent.</para>
|
||||
</enum>
|
||||
<enum name="peername">
|
||||
<para>R/O Get the name of the peer.</para>
|
||||
</enum>
|
||||
<enum name="t38passthrough">
|
||||
<para>R/O <literal>1</literal> if T38 is offered or enabled in this channel,
|
||||
otherwise <literal>0</literal></para>
|
||||
</enum>
|
||||
<enum name="rtpqos">
|
||||
<para>R/O Get QOS information about the RTP stream</para>
|
||||
<para> This option takes two additional arguments:</para>
|
||||
<para> Argument 1:</para>
|
||||
<para> <literal>audio</literal> Get data about the audio stream</para>
|
||||
<para> <literal>video</literal> Get data about the video stream</para>
|
||||
<para> <literal>text</literal> Get data about the text stream</para>
|
||||
<para> Argument 2:</para>
|
||||
<para> <literal>local_ssrc</literal> Local SSRC (stream ID)</para>
|
||||
<para> <literal>local_lostpackets</literal> Local lost packets</para>
|
||||
<para> <literal>local_jitter</literal> Local calculated jitter</para>
|
||||
<para> <literal>local_maxjitter</literal> Local calculated jitter (maximum)</para>
|
||||
<para> <literal>local_minjitter</literal> Local calculated jitter (minimum)</para>
|
||||
<para> <literal>local_normdevjitter</literal>Local calculated jitter (normal deviation)</para>
|
||||
<para> <literal>local_stdevjitter</literal> Local calculated jitter (standard deviation)</para>
|
||||
<para> <literal>local_count</literal> Number of received packets</para>
|
||||
<para> <literal>remote_ssrc</literal> Remote SSRC (stream ID)</para>
|
||||
<para> <literal>remote_lostpackets</literal>Remote lost packets</para>
|
||||
<para> <literal>remote_jitter</literal> Remote reported jitter</para>
|
||||
<para> <literal>remote_maxjitter</literal> Remote calculated jitter (maximum)</para>
|
||||
<para> <literal>remote_minjitter</literal> Remote calculated jitter (minimum)</para>
|
||||
<para> <literal>remote_normdevjitter</literal>Remote calculated jitter (normal deviation)</para>
|
||||
<para> <literal>remote_stdevjitter</literal>Remote calculated jitter (standard deviation)</para>
|
||||
<para> <literal>remote_count</literal> Number of transmitted packets</para>
|
||||
<para> <literal>rtt</literal> Round trip time</para>
|
||||
<para> <literal>maxrtt</literal> Round trip time (maximum)</para>
|
||||
<para> <literal>minrtt</literal> Round trip time (minimum)</para>
|
||||
<para> <literal>normdevrtt</literal> Round trip time (normal deviation)</para>
|
||||
<para> <literal>stdevrtt</literal> Round trip time (standard deviation)</para>
|
||||
<para> <literal>all</literal> All statistics (in a form suited to logging,
|
||||
but not for parsing)</para>
|
||||
</enum>
|
||||
<enum name="rtpdest">
|
||||
<para>R/O Get remote RTP destination information.</para>
|
||||
<para> This option takes one additional argument:</para>
|
||||
<para> Argument 1:</para>
|
||||
<para> <literal>audio</literal> Get audio destination</para>
|
||||
<para> <literal>video</literal> Get video destination</para>
|
||||
<para> <literal>text</literal> Get text destination</para>
|
||||
<para> Defaults to <literal>audio</literal> if unspecified.</para>
|
||||
</enum>
|
||||
<enum name="rtpsource">
|
||||
<para>R/O Get source RTP destination information.</para>
|
||||
<para> This option takes one additional argument:</para>
|
||||
<para> Argument 1:</para>
|
||||
<para> <literal>audio</literal> Get audio destination</para>
|
||||
<para> <literal>video</literal> Get video destination</para>
|
||||
<para> <literal>text</literal> Get text destination</para>
|
||||
<para> Defaults to <literal>audio</literal> if unspecified.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</info>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/rtp_engine.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/acl.h"
|
||||
|
||||
#include "include/sip.h"
|
||||
#include "include/globals.h"
|
||||
#include "include/dialog.h"
|
||||
#include "include/dialplan_functions.h"
|
||||
#include "include/sip_utils.h"
|
||||
|
||||
|
||||
int sip_acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen)
|
||||
{
|
||||
struct sip_pvt *p = ast_channel_tech_pvt(chan);
|
||||
char *parse = ast_strdupa(preparse);
|
||||
int res = 0;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(param);
|
||||
AST_APP_ARG(type);
|
||||
AST_APP_ARG(field);
|
||||
);
|
||||
|
||||
/* Check for zero arguments */
|
||||
if (ast_strlen_zero(parse)) {
|
||||
ast_log(LOG_ERROR, "Cannot call %s without arguments\n", funcname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
/* Sanity check */
|
||||
if (!IS_SIP_TECH(ast_channel_tech(chan))) {
|
||||
ast_log(LOG_ERROR, "Cannot call %s on a non-SIP channel\n", funcname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(buf, 0, buflen);
|
||||
|
||||
if (p == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcasecmp(args.param, "peerip")) {
|
||||
ast_copy_string(buf, ast_sockaddr_isnull(&p->sa) ? "" : ast_sockaddr_stringify_addr(&p->sa), buflen);
|
||||
} else if (!strcasecmp(args.param, "recvip")) {
|
||||
ast_copy_string(buf, ast_sockaddr_isnull(&p->recv) ? "" : ast_sockaddr_stringify_addr(&p->recv), buflen);
|
||||
} else if (!strcasecmp(args.param, "recvport")) {
|
||||
ast_copy_string(buf, ast_sockaddr_isnull(&p->recv) ? "" : ast_sockaddr_stringify_port(&p->recv), buflen);
|
||||
} else if (!strcasecmp(args.param, "from")) {
|
||||
ast_copy_string(buf, p->from, buflen);
|
||||
} else if (!strcasecmp(args.param, "uri")) {
|
||||
ast_copy_string(buf, p->uri, buflen);
|
||||
} else if (!strcasecmp(args.param, "ruri")) {
|
||||
if (p->initreq.data) {
|
||||
char *tmpruri = REQ_OFFSET_TO_STR(&p->initreq, rlpart2);
|
||||
ast_copy_string(buf, tmpruri, buflen);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if (!strcasecmp(args.param, "useragent")) {
|
||||
ast_copy_string(buf, p->useragent, buflen);
|
||||
} else if (!strcasecmp(args.param, "peername")) {
|
||||
ast_copy_string(buf, p->peername, buflen);
|
||||
} else if (!strcasecmp(args.param, "t38passthrough")) {
|
||||
ast_copy_string(buf, (p->t38.state == T38_DISABLED) ? "0" : "1", buflen);
|
||||
} else if (!strcasecmp(args.param, "rtpdest")) {
|
||||
struct ast_sockaddr addr;
|
||||
struct ast_rtp_instance *stream;
|
||||
|
||||
if (ast_strlen_zero(args.type))
|
||||
args.type = "audio";
|
||||
|
||||
if (!strcasecmp(args.type, "audio"))
|
||||
stream = p->rtp;
|
||||
else if (!strcasecmp(args.type, "video"))
|
||||
stream = p->vrtp;
|
||||
else if (!strcasecmp(args.type, "text"))
|
||||
stream = p->trtp;
|
||||
else
|
||||
return -1;
|
||||
|
||||
/* Return 0 to suppress a console warning message */
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_rtp_instance_get_remote_address(stream, &addr);
|
||||
snprintf(buf, buflen, "%s", ast_sockaddr_stringify(&addr));
|
||||
} else if (!strcasecmp(args.param, "rtpsource")) {
|
||||
struct ast_sockaddr sa;
|
||||
struct ast_rtp_instance *stream;
|
||||
|
||||
if (ast_strlen_zero(args.type))
|
||||
args.type = "audio";
|
||||
|
||||
if (!strcasecmp(args.type, "audio"))
|
||||
stream = p->rtp;
|
||||
else if (!strcasecmp(args.type, "video"))
|
||||
stream = p->vrtp;
|
||||
else if (!strcasecmp(args.type, "text"))
|
||||
stream = p->trtp;
|
||||
else
|
||||
return -1;
|
||||
|
||||
/* Return 0 to suppress a console warning message */
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_rtp_instance_get_local_address(stream, &sa);
|
||||
|
||||
if (ast_sockaddr_isnull(&sa)) {
|
||||
struct ast_sockaddr dest_sa;
|
||||
ast_rtp_instance_get_remote_address(stream, &dest_sa);
|
||||
ast_ouraddrfor(&dest_sa, &sa);
|
||||
}
|
||||
|
||||
snprintf(buf, buflen, "%s", ast_sockaddr_stringify(&sa));
|
||||
} else if (!strcasecmp(args.param, "rtpqos")) {
|
||||
struct ast_rtp_instance *rtp = NULL;
|
||||
|
||||
if (ast_strlen_zero(args.type)) {
|
||||
args.type = "audio";
|
||||
}
|
||||
|
||||
if (!strcasecmp(args.type, "audio")) {
|
||||
rtp = p->rtp;
|
||||
} else if (!strcasecmp(args.type, "video")) {
|
||||
rtp = p->vrtp;
|
||||
} else if (!strcasecmp(args.type, "text")) {
|
||||
rtp = p->trtp;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(args.field) || !strcasecmp(args.field, "all")) {
|
||||
char quality_buf[AST_MAX_USER_FIELD];
|
||||
|
||||
if (!ast_rtp_instance_get_quality(rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_copy_string(buf, quality_buf, buflen);
|
||||
return res;
|
||||
} else {
|
||||
struct ast_rtp_instance_stats stats;
|
||||
int i;
|
||||
struct {
|
||||
const char *name;
|
||||
enum { INT, DBL } type;
|
||||
union {
|
||||
unsigned int *i4;
|
||||
double *d8;
|
||||
};
|
||||
} lookup[] = {
|
||||
{ "txcount", INT, { .i4 = &stats.txcount, }, },
|
||||
{ "rxcount", INT, { .i4 = &stats.rxcount, }, },
|
||||
{ "txjitter", DBL, { .d8 = &stats.txjitter, }, },
|
||||
{ "rxjitter", DBL, { .d8 = &stats.rxjitter, }, },
|
||||
{ "remote_maxjitter", DBL, { .d8 = &stats.remote_maxjitter, }, },
|
||||
{ "remote_minjitter", DBL, { .d8 = &stats.remote_minjitter, }, },
|
||||
{ "remote_normdevjitter", DBL, { .d8 = &stats.remote_normdevjitter, }, },
|
||||
{ "remote_stdevjitter", DBL, { .d8 = &stats.remote_stdevjitter, }, },
|
||||
{ "local_maxjitter", DBL, { .d8 = &stats.local_maxjitter, }, },
|
||||
{ "local_minjitter", DBL, { .d8 = &stats.local_minjitter, }, },
|
||||
{ "local_normdevjitter", DBL, { .d8 = &stats.local_normdevjitter, }, },
|
||||
{ "local_stdevjitter", DBL, { .d8 = &stats.local_stdevjitter, }, },
|
||||
{ "txploss", INT, { .i4 = &stats.txploss, }, },
|
||||
{ "rxploss", INT, { .i4 = &stats.rxploss, }, },
|
||||
{ "remote_maxrxploss", DBL, { .d8 = &stats.remote_maxrxploss, }, },
|
||||
{ "remote_minrxploss", DBL, { .d8 = &stats.remote_minrxploss, }, },
|
||||
{ "remote_normdevrxploss", DBL, { .d8 = &stats.remote_normdevrxploss, }, },
|
||||
{ "remote_stdevrxploss", DBL, { .d8 = &stats.remote_stdevrxploss, }, },
|
||||
{ "local_maxrxploss", DBL, { .d8 = &stats.local_maxrxploss, }, },
|
||||
{ "local_minrxploss", DBL, { .d8 = &stats.local_minrxploss, }, },
|
||||
{ "local_normdevrxploss", DBL, { .d8 = &stats.local_normdevrxploss, }, },
|
||||
{ "local_stdevrxploss", DBL, { .d8 = &stats.local_stdevrxploss, }, },
|
||||
{ "rtt", DBL, { .d8 = &stats.rtt, }, },
|
||||
{ "maxrtt", DBL, { .d8 = &stats.maxrtt, }, },
|
||||
{ "minrtt", DBL, { .d8 = &stats.minrtt, }, },
|
||||
{ "normdevrtt", DBL, { .d8 = &stats.normdevrtt, }, },
|
||||
{ "stdevrtt", DBL, { .d8 = &stats.stdevrtt, }, },
|
||||
{ "local_ssrc", INT, { .i4 = &stats.local_ssrc, }, },
|
||||
{ "remote_ssrc", INT, { .i4 = &stats.remote_ssrc, }, },
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
if (ast_rtp_instance_get_stats(rtp, &stats, AST_RTP_INSTANCE_STAT_ALL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; !ast_strlen_zero(lookup[i].name); i++) {
|
||||
if (!strcasecmp(args.field, lookup[i].name)) {
|
||||
if (lookup[i].type == INT) {
|
||||
snprintf(buf, buflen, "%u", *lookup[i].i4);
|
||||
} else {
|
||||
snprintf(buf, buflen, "%f", *lookup[i].d8);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname);
|
||||
return -1;
|
||||
}
|
||||
} else if (!strcasecmp(args.param, "secure_signaling")) {
|
||||
snprintf(buf, buflen, "%s", p->socket.type == AST_TRANSPORT_TLS ? "1" : "");
|
||||
} else if (!strcasecmp(args.param, "secure_media")) {
|
||||
snprintf(buf, buflen, "%s", p->srtp ? "1" : "");
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
static int test_sip_rtpqos_1_new(struct ast_rtp_instance *instance, struct ast_sched_context *sched, struct ast_sockaddr *addr, void *data)
|
||||
{
|
||||
/* Needed to pass sanity checks */
|
||||
ast_rtp_instance_set_data(instance, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_sip_rtpqos_1_destroy(struct ast_rtp_instance *instance)
|
||||
{
|
||||
/* Needed to pass sanity checks */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ast_frame *test_sip_rtpqos_1_read(struct ast_rtp_instance *instance, int rtcp)
|
||||
{
|
||||
/* Needed to pass sanity checks */
|
||||
return &ast_null_frame;
|
||||
}
|
||||
|
||||
static int test_sip_rtpqos_1_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
|
||||
{
|
||||
/* Needed to pass sanity checks */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_sip_rtpqos_1_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat)
|
||||
{
|
||||
struct ast_rtp_instance_stats *s = ast_rtp_instance_get_data(instance);
|
||||
memcpy(stats, s, sizeof(*stats));
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(test_sip_rtpqos_1)
|
||||
{
|
||||
int i, res = AST_TEST_PASS;
|
||||
static struct ast_rtp_engine test_engine = {
|
||||
.name = "test",
|
||||
.new = test_sip_rtpqos_1_new,
|
||||
.destroy = test_sip_rtpqos_1_destroy,
|
||||
.read = test_sip_rtpqos_1_read,
|
||||
.write = test_sip_rtpqos_1_write,
|
||||
.get_stat = test_sip_rtpqos_1_get_stat,
|
||||
};
|
||||
struct ast_sockaddr sa = { {0, } };
|
||||
struct ast_rtp_instance_stats mine = { 0, };
|
||||
struct sip_pvt *p = NULL;
|
||||
struct ast_channel *chan = NULL;
|
||||
struct ast_str *varstr = NULL, *buffer = NULL;
|
||||
struct {
|
||||
const char *name;
|
||||
enum { INT, DBL } type;
|
||||
union {
|
||||
unsigned int *i4;
|
||||
double *d8;
|
||||
};
|
||||
} lookup[] = {
|
||||
{ "txcount", INT, { .i4 = &mine.txcount, }, },
|
||||
{ "rxcount", INT, { .i4 = &mine.rxcount, }, },
|
||||
{ "txjitter", DBL, { .d8 = &mine.txjitter, }, },
|
||||
{ "rxjitter", DBL, { .d8 = &mine.rxjitter, }, },
|
||||
{ "remote_maxjitter", DBL, { .d8 = &mine.remote_maxjitter, }, },
|
||||
{ "remote_minjitter", DBL, { .d8 = &mine.remote_minjitter, }, },
|
||||
{ "remote_normdevjitter", DBL, { .d8 = &mine.remote_normdevjitter, }, },
|
||||
{ "remote_stdevjitter", DBL, { .d8 = &mine.remote_stdevjitter, }, },
|
||||
{ "local_maxjitter", DBL, { .d8 = &mine.local_maxjitter, }, },
|
||||
{ "local_minjitter", DBL, { .d8 = &mine.local_minjitter, }, },
|
||||
{ "local_normdevjitter", DBL, { .d8 = &mine.local_normdevjitter, }, },
|
||||
{ "local_stdevjitter", DBL, { .d8 = &mine.local_stdevjitter, }, },
|
||||
{ "txploss", INT, { .i4 = &mine.txploss, }, },
|
||||
{ "rxploss", INT, { .i4 = &mine.rxploss, }, },
|
||||
{ "remote_maxrxploss", DBL, { .d8 = &mine.remote_maxrxploss, }, },
|
||||
{ "remote_minrxploss", DBL, { .d8 = &mine.remote_minrxploss, }, },
|
||||
{ "remote_normdevrxploss", DBL, { .d8 = &mine.remote_normdevrxploss, }, },
|
||||
{ "remote_stdevrxploss", DBL, { .d8 = &mine.remote_stdevrxploss, }, },
|
||||
{ "local_maxrxploss", DBL, { .d8 = &mine.local_maxrxploss, }, },
|
||||
{ "local_minrxploss", DBL, { .d8 = &mine.local_minrxploss, }, },
|
||||
{ "local_normdevrxploss", DBL, { .d8 = &mine.local_normdevrxploss, }, },
|
||||
{ "local_stdevrxploss", DBL, { .d8 = &mine.local_stdevrxploss, }, },
|
||||
{ "rtt", DBL, { .d8 = &mine.rtt, }, },
|
||||
{ "maxrtt", DBL, { .d8 = &mine.maxrtt, }, },
|
||||
{ "minrtt", DBL, { .d8 = &mine.minrtt, }, },
|
||||
{ "normdevrtt", DBL, { .d8 = &mine.normdevrtt, }, },
|
||||
{ "stdevrtt", DBL, { .d8 = &mine.stdevrtt, }, },
|
||||
{ "local_ssrc", INT, { .i4 = &mine.local_ssrc, }, },
|
||||
{ "remote_ssrc", INT, { .i4 = &mine.remote_ssrc, }, },
|
||||
{ NULL, },
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "test_sip_rtpqos";
|
||||
info->category = "/channels/chan_sip/";
|
||||
info->summary = "Test retrieval of SIP RTP QOS stats";
|
||||
info->description =
|
||||
"Verify values in the RTP instance structure can be accessed through the dialplan.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
ast_rtp_engine_register(&test_engine);
|
||||
/* Have to associate this with a SIP pvt and an ast_channel */
|
||||
if (!(p = sip_alloc(0, NULL, 0, SIP_NOTIFY, NULL, 0))) {
|
||||
res = AST_TEST_NOT_RUN;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!(p->rtp = ast_rtp_instance_new("test", sched, &bindaddr, &mine))) {
|
||||
res = AST_TEST_NOT_RUN;
|
||||
goto done;
|
||||
}
|
||||
ast_rtp_instance_set_remote_address(p->rtp, &sa);
|
||||
if (!(chan = ast_dummy_channel_alloc())) {
|
||||
res = AST_TEST_NOT_RUN;
|
||||
goto done;
|
||||
}
|
||||
ast_channel_tech_set(chan, &sip_tech);
|
||||
ast_channel_tech_pvt_set(chan, dialog_ref(p, "Give the owner channel a reference to the dialog"));
|
||||
p->owner = chan;
|
||||
|
||||
varstr = ast_str_create(16);
|
||||
buffer = ast_str_create(16);
|
||||
if (!varstr || !buffer) {
|
||||
res = AST_TEST_NOT_RUN;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Populate "mine" with values, then retrieve them with the CHANNEL dialplan function */
|
||||
for (i = 0; !ast_strlen_zero(lookup[i].name); i++) {
|
||||
ast_str_set(&varstr, 0, "${CHANNEL(rtpqos,audio,%s)}", lookup[i].name);
|
||||
if (lookup[i].type == INT) {
|
||||
int j;
|
||||
char cmpstr[256];
|
||||
for (j = 1; j < 25; j++) {
|
||||
*lookup[i].i4 = j;
|
||||
ast_str_substitute_variables(&buffer, 0, chan, ast_str_buffer(varstr));
|
||||
snprintf(cmpstr, sizeof(cmpstr), "%d", j);
|
||||
if (strcmp(cmpstr, ast_str_buffer(buffer))) {
|
||||
res = AST_TEST_FAIL;
|
||||
ast_test_status_update(test, "%s != %s != %s\n", ast_str_buffer(varstr), cmpstr, ast_str_buffer(buffer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
double j, cmpdbl = 0.0;
|
||||
for (j = 1.0; j < 10.0; j += 0.3) {
|
||||
*lookup[i].d8 = j;
|
||||
ast_str_substitute_variables(&buffer, 0, chan, ast_str_buffer(varstr));
|
||||
if (sscanf(ast_str_buffer(buffer), "%lf", &cmpdbl) != 1 || fabs(j - cmpdbl) > .05) {
|
||||
res = AST_TEST_FAIL;
|
||||
ast_test_status_update(test, "%s != %f != %s\n", ast_str_buffer(varstr), j, ast_str_buffer(buffer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
ast_free(varstr);
|
||||
ast_free(buffer);
|
||||
|
||||
/* This unlink and unref will take care of destroying the channel, RTP instance, and SIP pvt */
|
||||
if (p) {
|
||||
dialog_unlink_all(p);
|
||||
dialog_unref(p, "Destroy test object");
|
||||
}
|
||||
if (chan) {
|
||||
ast_channel_unref(chan);
|
||||
}
|
||||
ast_rtp_engine_unregister(&test_engine);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! \brief SIP test registration */
|
||||
void sip_dialplan_function_register_tests(void)
|
||||
{
|
||||
AST_TEST_REGISTER(test_sip_rtpqos_1);
|
||||
}
|
||||
|
||||
/*! \brief SIP test registration */
|
||||
void sip_dialplan_function_unregister_tests(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(test_sip_rtpqos_1);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* 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 sip.conf parser header file
|
||||
*/
|
||||
|
||||
#include "sip.h"
|
||||
|
||||
#ifndef _SIP_CONF_PARSE_H
|
||||
#define _SIP_CONF_PARSE_H
|
||||
|
||||
/*!
|
||||
* \brief Parse register=> line in sip.conf
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int sip_parse_register_line(struct sip_registry *reg, int default_expiry, const char *value, int lineno);
|
||||
|
||||
/*!
|
||||
* \brief parses a config line for a host with a transport
|
||||
*
|
||||
* An example input would be:
|
||||
* <code>tls://www.google.com:8056</code>
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure
|
||||
*/
|
||||
int sip_parse_host(char *line, int lineno, char **hostname, int *portnum, enum ast_transport *transport);
|
||||
|
||||
/*! \brief Parse the comma-separated nat= option values
|
||||
* \param value The comma-separated value
|
||||
* \param mask An array of ast_flags that will be set by this function
|
||||
* and used as a mask for copying the flags later
|
||||
* \param flags An array of ast_flags that will be set by this function
|
||||
*
|
||||
* \note The nat-related values in both mask and flags are assumed to empty. This function
|
||||
* will treat the first "yes" or "no" value in a list of values as overriding all other values
|
||||
* and will stop parsing. Auto values will override their non-auto counterparts.
|
||||
*/
|
||||
void sip_parse_nat_option(const char *value, struct ast_flags *mask, struct ast_flags *flags);
|
||||
|
||||
/*!
|
||||
* \brief register config parsing tests
|
||||
*/
|
||||
void sip_config_parser_register_tests(void);
|
||||
|
||||
/*!
|
||||
* \brief unregister config parsing tests
|
||||
*/
|
||||
void sip_config_parser_unregister_tests(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* 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 sip dialog management header file
|
||||
*/
|
||||
|
||||
#include "sip.h"
|
||||
|
||||
#ifndef _SIP_DIALOG_H
|
||||
#define _SIP_DIALOG_H
|
||||
|
||||
/*! \brief
|
||||
* when we create or delete references, make sure to use these
|
||||
* functions so we keep track of the refcounts.
|
||||
* To simplify the code, we allow a NULL to be passed to dialog_unref().
|
||||
*/
|
||||
#define dialog_ref(dialog, tag) ao2_t_bump(dialog, tag)
|
||||
#define dialog_unref(dialog, tag) ({ ao2_t_cleanup(dialog, tag); (NULL); })
|
||||
|
||||
struct sip_pvt *__sip_alloc(ast_string_field callid, struct ast_sockaddr *sin,
|
||||
int useglobal_nat, const int intended_method, struct sip_request *req, ast_callid logger_callid,
|
||||
const char *file, int line, const char *func);
|
||||
|
||||
#define sip_alloc(callid, addr, useglobal_nat, intended_method, req, logger_callid) \
|
||||
__sip_alloc(callid, addr, useglobal_nat, intended_method, req, logger_callid, __FILE__, __LINE__, __PRETTY_FUNCTION__)
|
||||
|
||||
/*!
|
||||
* \brief Schedule final destruction of SIP dialog.
|
||||
*
|
||||
* \note This cannot be canceled.
|
||||
*
|
||||
* \details
|
||||
* This function is used to keep a dialog around for a period of time in order
|
||||
* to properly respond to any retransmits.
|
||||
*/
|
||||
void sip_scheddestroy_final(struct sip_pvt *p, int ms);
|
||||
|
||||
/*! \brief Schedule destruction of SIP dialog */
|
||||
void sip_scheddestroy(struct sip_pvt *p, int ms);
|
||||
|
||||
/*! \brief Cancel destruction of SIP dialog. */
|
||||
void sip_cancel_destroy(struct sip_pvt *pvt);
|
||||
|
||||
/*!
|
||||
* \brief Unlink a dialog from the dialogs container, as well as any other places
|
||||
* that it may be currently stored.
|
||||
*
|
||||
* \note A reference to the dialog must be held before calling
|
||||
* this function, and this function does not release that
|
||||
* reference.
|
||||
*
|
||||
* \note The dialog must not be locked when called.
|
||||
*/
|
||||
void dialog_unlink_all(struct sip_pvt *dialog);
|
||||
|
||||
/*! \brief Acknowledges receipt of a packet and stops retransmission
|
||||
* called with p locked*/
|
||||
int __sip_ack(struct sip_pvt *p, uint32_t seqno, int resp, int sipmethod);
|
||||
|
||||
/*! \brief Pretend to ack all packets
|
||||
* called with p locked */
|
||||
void __sip_pretend_ack(struct sip_pvt *p);
|
||||
|
||||
/*! \brief Acks receipt of packet, keep it around (used for provisional responses) */
|
||||
int __sip_semi_ack(struct sip_pvt *p, uint32_t seqno, int resp, int sipmethod);
|
||||
|
||||
#endif /* defined(_SIP_DIALOG_H) */
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* 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 SIP dialplan functions header file
|
||||
*/
|
||||
|
||||
#include "sip.h"
|
||||
|
||||
#ifndef _SIP_DIALPLAN_FUNCTIONS_H
|
||||
#define _SIP_DIALPLAN_FUNCTIONS_H
|
||||
|
||||
/*!
|
||||
* \brief Channel read dialplan function for SIP
|
||||
*/
|
||||
int sip_acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
|
||||
|
||||
/*!
|
||||
* \brief register dialplan function tests
|
||||
*/
|
||||
void sip_dialplan_function_register_tests(void);
|
||||
/*!
|
||||
* \brief unregister dialplan function tests
|
||||
*/
|
||||
void sip_dialplan_function_unregister_tests(void);
|
||||
|
||||
#endif /* !defined(_SIP_DIALPLAN_FUNCTIONS_H) */
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* 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 sip global declaration header file
|
||||
*/
|
||||
|
||||
#include "sip.h"
|
||||
|
||||
#ifndef _SIP_GLOBALS_H
|
||||
#define _SIP_GLOBALS_H
|
||||
|
||||
extern struct ast_sockaddr bindaddr; /*!< UDP: The address we bind to */
|
||||
extern struct ast_sched_context *sched; /*!< The scheduling context */
|
||||
|
||||
/*! \brief Definition of this channel for PBX channel registration */
|
||||
extern struct ast_channel_tech sip_tech;
|
||||
|
||||
/*! \brief This version of the sip channel tech has no send_digit_begin
|
||||
* callback so that the core knows that the channel does not want
|
||||
* DTMF BEGIN frames.
|
||||
* The struct is initialized just before registering the channel driver,
|
||||
* and is for use with channels using SIP INFO DTMF.
|
||||
*/
|
||||
extern struct ast_channel_tech sip_tech_info;
|
||||
|
||||
#endif /* !defined(SIP_GLOBALS_H) */
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2010, Digium, Inc.
|
||||
*
|
||||
* 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 sip request response parser header file
|
||||
*/
|
||||
|
||||
#ifndef _SIP_REQRESP_H
|
||||
#define _SIP_REQRESP_H
|
||||
|
||||
/*! \brief uri parameters */
|
||||
struct uriparams {
|
||||
char *transport;
|
||||
char *user;
|
||||
char *method;
|
||||
char *ttl;
|
||||
char *maddr;
|
||||
int lr;
|
||||
};
|
||||
|
||||
struct contact {
|
||||
AST_LIST_ENTRY(contact) list;
|
||||
char *name;
|
||||
char *user;
|
||||
char *pass;
|
||||
char *hostport;
|
||||
struct uriparams params;
|
||||
char *headers;
|
||||
char *expires;
|
||||
char *q;
|
||||
};
|
||||
|
||||
AST_LIST_HEAD_NOLOCK(contactliststruct, contact);
|
||||
|
||||
/*!
|
||||
* \brief parses a URI in its components.
|
||||
*
|
||||
* \note
|
||||
* - Multiple scheme's can be specified ',' delimited. ex: "sip:,sips:"
|
||||
* - If a component is not requested, do not split around it. This means
|
||||
* that if we don't have domain, we cannot split name:pass.
|
||||
* - It is safe to call with ret_name, pass, hostport pointing all to
|
||||
* the same place.
|
||||
* - If no secret parameter is provided, ret_name will return with both
|
||||
* parts, user:secret.
|
||||
* - If the URI contains a port number, hostport will return with both
|
||||
* parts, host:port.
|
||||
* - This function overwrites the URI string.
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on error.
|
||||
*
|
||||
* \verbatim
|
||||
* general form we are expecting is sip:user:password;user-parameters@host:port;uri-parameters?headers
|
||||
* \endverbatim
|
||||
*/
|
||||
int parse_uri(char *uri, const char *scheme, char **ret_name, char **pass,
|
||||
char **hostport, char **transport);
|
||||
|
||||
/*!
|
||||
* \brief parses a URI in to all of its components and any trailing residue
|
||||
*
|
||||
* \retval 0 on success
|
||||
* \retval -1 on error.
|
||||
*
|
||||
*/
|
||||
int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
|
||||
char **hostport, struct uriparams *params, char **headers,
|
||||
char **residue);
|
||||
|
||||
/*!
|
||||
* \brief Get caller id name from SIP headers, copy into output buffer
|
||||
*
|
||||
* \return input string pointer placed after display-name field if possible
|
||||
*/
|
||||
const char *get_calleridname(const char *input, char *output, size_t outputsize);
|
||||
|
||||
/*!
|
||||
* \brief Get name and number from sip header
|
||||
*
|
||||
* \note name and number point to malloced memory on return and must be
|
||||
* freed. If name or number is not found, they will be returned as NULL.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int get_name_and_number(const char *hdr, char **name, char **number);
|
||||
|
||||
/*! \brief Pick out text in brackets from character string
|
||||
* \return pointer to terminated stripped string
|
||||
* \param tmp input string that will be modified
|
||||
*
|
||||
* Examples:
|
||||
* \verbatim
|
||||
* "foo" <bar> valid input, returns bar
|
||||
* foo returns the whole string
|
||||
* < "foo ... > returns the string between brackets
|
||||
* < "foo... bogus (missing closing bracket), returns the whole string
|
||||
* \endverbatim
|
||||
*/
|
||||
char *get_in_brackets(char *tmp);
|
||||
|
||||
/*! \brief Get text in brackets on a const without copy
|
||||
*
|
||||
* \param src String to search
|
||||
* \param[out] start Set to first character inside left bracket.
|
||||
* \param[out] length Set to lenght of string inside brackets
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
* \retval 1 no brackets so got all
|
||||
*/
|
||||
int get_in_brackets_const(const char *src,const char **start,int *length);
|
||||
|
||||
/*! \brief Get text in brackets and any trailing residue
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
* \retval 1 no brackets so got all
|
||||
*/
|
||||
int get_in_brackets_full(char *tmp, char **out, char **residue);
|
||||
|
||||
/*! \brief Parse the ABNF structure
|
||||
* name-andor-addr = name-addr / addr-spec
|
||||
* into its components and return any trailing message-header parameters
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int parse_name_andor_addr(char *uri, const char *scheme, char **name,
|
||||
char **user, char **pass, char **domain,
|
||||
struct uriparams *params, char **headers,
|
||||
char **residue);
|
||||
|
||||
/*! \brief Parse all contact header contacts
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
* \retval 1 all contacts (star)
|
||||
*/
|
||||
|
||||
int get_comma(char *parse, char **out);
|
||||
|
||||
int parse_contact_header(char *contactheader, struct contactliststruct *contactlist);
|
||||
|
||||
/*!
|
||||
* \brief register request parsing tests
|
||||
*/
|
||||
void sip_request_parser_register_tests(void);
|
||||
|
||||
/*!
|
||||
* \brief unregister request parsing tests
|
||||
*/
|
||||
void sip_request_parser_unregister_tests(void);
|
||||
|
||||
/*!
|
||||
* \brief Parse supported header in incoming packet
|
||||
*
|
||||
* \details This function parses through the options parameters and
|
||||
* builds a bit field representing all the SIP options in that field. When an
|
||||
* item is found that is not supported, it is copied to the unsupported
|
||||
* out buffer.
|
||||
*
|
||||
* \param options list
|
||||
* \param[in,out] unsupported buffer (optional)
|
||||
* \param[in,out] unsupported_len buffer length
|
||||
*
|
||||
* \note Because this function can be called multiple times, it will append
|
||||
* whatever options are specified in \c options to \c unsupported. Callers
|
||||
* of this function should make sure the unsupported buffer is clear before
|
||||
* calling this function.
|
||||
*/
|
||||
unsigned int parse_sip_options(const char *options, char *unsupported, size_t unsupported_len);
|
||||
|
||||
/*!
|
||||
* \brief Compare two URIs as described in RFC 3261 Section 19.1.4
|
||||
*
|
||||
* \param input1 First URI
|
||||
* \param input2 Second URI
|
||||
* \retval 0 URIs match
|
||||
* \retval nonzero URIs do not match or one or both is malformed
|
||||
*/
|
||||
int sip_uri_cmp(const char *input1, const char *input2);
|
||||
|
||||
/*!
|
||||
* \brief initialize request and response parser data
|
||||
*
|
||||
* \retval 0 Success
|
||||
* \retval -1 Failure
|
||||
*/
|
||||
int sip_reqresp_parser_init(void);
|
||||
|
||||
/*!
|
||||
* \brief Free resources used by request and response parser
|
||||
*/
|
||||
void sip_reqresp_parser_exit(void);
|
||||
|
||||
/*!
|
||||
* \brief Parse a Via header
|
||||
*
|
||||
* This function parses the Via header and processes it according to section
|
||||
* 18.2 of RFC 3261 and RFC 3581. Since we don't have a transport layer, we
|
||||
* only care about the maddr and ttl parms. The received and rport params are
|
||||
* not parsed.
|
||||
*
|
||||
* \note This function fails to parse some odd combinations of SWS in parameter
|
||||
* lists.
|
||||
*
|
||||
* \code
|
||||
* VIA syntax. RFC 3261 section 25.1
|
||||
* Via = ( "Via" / "v" ) HCOLON via-parm *(COMMA via-parm)
|
||||
* via-parm = sent-protocol LWS sent-by *( SEMI via-params )
|
||||
* via-params = via-ttl / via-maddr
|
||||
* / via-received / via-branch
|
||||
* / via-extension
|
||||
* via-ttl = "ttl" EQUAL ttl
|
||||
* via-maddr = "maddr" EQUAL host
|
||||
* via-received = "received" EQUAL (IPv4address / IPv6address)
|
||||
* via-branch = "branch" EQUAL token
|
||||
* via-extension = generic-param
|
||||
* sent-protocol = protocol-name SLASH protocol-version
|
||||
* SLASH transport
|
||||
* protocol-name = "SIP" / token
|
||||
* protocol-version = token
|
||||
* transport = "UDP" / "TCP" / "TLS" / "SCTP"
|
||||
* / other-transport
|
||||
* sent-by = host [ COLON port ]
|
||||
* ttl = 1*3DIGIT ; 0 to 255
|
||||
* \endcode
|
||||
*/
|
||||
struct sip_via *parse_via(const char *header);
|
||||
|
||||
/*!
|
||||
* \brief Free parsed Via data.
|
||||
*/
|
||||
void free_via(struct sip_via *v);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* 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 sip_route header file
|
||||
*/
|
||||
|
||||
#ifndef _SIP_ROUTE_H
|
||||
#define _SIP_ROUTE_H
|
||||
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/strings.h"
|
||||
|
||||
/*!
|
||||
* \brief Opaque storage of a sip route hop
|
||||
*/
|
||||
struct sip_route_hop;
|
||||
|
||||
/*!
|
||||
* \internal \brief Internal enum to remember last calculated
|
||||
*/
|
||||
enum sip_route_type {
|
||||
route_loose = 0, /*!< The first hop contains ;lr or does not exist */
|
||||
route_strict, /*!< The first hop exists and does not contain ;lr */
|
||||
route_invalidated, /*!< strict/loose routing needs to be rechecked */
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Structure to store route information
|
||||
*
|
||||
* \note This must be zero-filled on allocation
|
||||
*/
|
||||
struct sip_route {
|
||||
AST_LIST_HEAD_NOLOCK(, sip_route_hop) list;
|
||||
enum sip_route_type type;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Add a new hop to the route
|
||||
*
|
||||
* \param route Route
|
||||
* \param uri Address of this hop
|
||||
* \param len Length of hop not including null terminator
|
||||
* \param inserthead If true then inserted the new route to the top of the list
|
||||
*
|
||||
* \return Pointer to null terminated copy of URI on success
|
||||
* \retval NULL on error
|
||||
*/
|
||||
const char *sip_route_add(struct sip_route *route, const char *uri, size_t len, int inserthead);
|
||||
|
||||
/*!
|
||||
* \brief Add routes from header
|
||||
*
|
||||
* \note This procedure is for headers that require use of \<brackets\>.
|
||||
*/
|
||||
void sip_route_process_header(struct sip_route *route, const char *header, int inserthead);
|
||||
|
||||
/*!
|
||||
* \brief copy route-set
|
||||
*/
|
||||
void sip_route_copy(struct sip_route *dst, const struct sip_route *src);
|
||||
|
||||
/*!
|
||||
* \brief Free all routes in the list
|
||||
*/
|
||||
void sip_route_clear(struct sip_route *route);
|
||||
|
||||
/*!
|
||||
* \brief Verbose dump of all hops for debugging
|
||||
*/
|
||||
void sip_route_dump(const struct sip_route *route);
|
||||
|
||||
/*!
|
||||
* \brief Make the comma separated list of route hops
|
||||
*
|
||||
* \param route Source of route list
|
||||
* \param formatcli Add's space after comma's, print's N/A if list is empty.
|
||||
* \param skip Number of hops to skip
|
||||
*
|
||||
* \return an allocated struct ast_str on success
|
||||
* \retval NULL on failure
|
||||
*/
|
||||
struct ast_str *sip_route_list(const struct sip_route *route, int formatcli, int skip)
|
||||
__attribute__((__malloc__)) __attribute__((__warn_unused_result__));
|
||||
|
||||
/*!
|
||||
* \brief Check if the route is strict
|
||||
*
|
||||
* \note The result is cached in route->type
|
||||
*/
|
||||
int sip_route_is_strict(struct sip_route *route);
|
||||
|
||||
/*!
|
||||
* \brief Get the URI of the route's first hop
|
||||
*/
|
||||
const char *sip_route_first_uri(const struct sip_route *route);
|
||||
|
||||
/*!
|
||||
* \brief Check if route has no URI's
|
||||
*/
|
||||
#define sip_route_empty(route) AST_LIST_EMPTY(&(route)->list)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2011, Digium, Inc.
|
||||
*
|
||||
* Michael L. Young <elgueromexicano@gmail.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 Generate security events in the SIP channel
|
||||
*
|
||||
* \author Michael L. Young <elgueromexicano@gmail.com>
|
||||
*/
|
||||
|
||||
#include "sip.h"
|
||||
|
||||
#ifndef _SIP_SECURITY_EVENTS_H
|
||||
#define _SIP_SECURITY_EVENTS_H
|
||||
|
||||
void sip_report_invalid_peer(const struct sip_pvt *p);
|
||||
void sip_report_failed_acl(const struct sip_pvt *p, const char *aclname);
|
||||
void sip_report_inval_password(const struct sip_pvt *p, const char *responsechallenge, const char *responsehash);
|
||||
void sip_report_auth_success(const struct sip_pvt *p, uint32_t using_password);
|
||||
void sip_report_session_limit(const struct sip_pvt *p);
|
||||
void sip_report_failed_challenge_response(const struct sip_pvt *p, const char *response, const char *expected_response);
|
||||
void sip_report_chal_sent(const struct sip_pvt *p);
|
||||
void sip_report_inval_transport(const struct sip_pvt *p, const char *transport);
|
||||
void sip_digest_parser(char *c, struct digestkeys *keys);
|
||||
int sip_report_security_event(const char *peer, struct ast_sockaddr *addr, const struct sip_pvt *p,
|
||||
const struct sip_request *req, const int res);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue