Compare commits
457 Commits
master
...
certified/
Author | SHA1 | Date |
---|---|---|
George Joseph | f631eda4fd | |
George Joseph | d0b8705712 | |
Naveen Albert | e4adc962ca | |
Naveen Albert | 082966bdad | |
Shaaah | c6ff7a6a51 | |
Naveen Albert | 3c42b141d7 | |
Sean Bright | 93e40968c3 | |
George Joseph | 9f182e9f58 | |
George Joseph | 267348bee3 | |
George Joseph | df0221b53d | |
George Joseph | eed8288d0c | |
George Joseph | e6c7f1aee0 | |
Sean Bright | d122225279 | |
Sebastian Jennen | a47acba99b | |
Shyju Kanaprath | 667b5ee192 | |
Sean Bright | 31fc2877cb | |
George Joseph | bbafb63bb5 | |
George Joseph | 7bd9f2b5a5 | |
romryz | 335b925fac | |
Naveen Albert | 59df4892ad | |
George Joseph | cb057a6381 | |
George Joseph | a433ed0d5a | |
Naveen Albert | 54b804fc3b | |
Ben Ford | bbfaff33cf | |
cmaj | 63b5a03538 | |
Joshua C. Colp | 167d393c0f | |
Mike Bradeen | 0d17ee6930 | |
George Joseph | 0f3f0f3529 | |
Naveen Albert | 64f9df5e59 | |
Sean Bright | 103a6dc987 | |
Brad Smith | 921b1d9cbc | |
Brad Smith | 57ea2912e2 | |
Sean Bright | 0fdaf7fc80 | |
Sean Bright | 2aaf28c95f | |
Naveen Albert | d50d981543 | |
Naveen Albert | 8eb6a329d6 | |
Sean Bright | f541f8d8c4 | |
Mike Bradeen | f5d622413a | |
Naveen Albert | f17910ded5 | |
Naveen Albert | 2b587d1d99 | |
PeterHolik | 416d9dfb85 | |
PeterHolik | 83d6f0f48b | |
Naveen Albert | d075a08d7e | |
George Joseph | 4715c1b11c | |
Naveen Albert | f22f49e77a | |
Maximilian Fridrich | 3e069f3274 | |
Naveen Albert | 83a0cb51e5 | |
Naveen Albert | b9ed57092f | |
George Joseph | b074c97f00 | |
Gitea | 5e168ebcd8 | |
Mike Bradeen | 5de9d317c7 | |
George Joseph | 00921cecda | |
Ben Ford | ce4f512bb7 | |
Naveen Albert | fcf36a8766 | |
Naveen Albert | bc53a2a087 | |
Sean Bright | 91c733bc69 | |
Naveen Albert | 8f5581b0d0 | |
George Joseph | 0005aa2995 | |
Maximilian Fridrich | dcf58ee88f | |
George Joseph | 44f1522907 | |
Sean Bright | 0b6e3bc59b | |
Matthew Fredrickson | bfac3945f6 | |
Sean Bright | a2f0d99d9d | |
Sean Bright | 4327ec2907 | |
Sean Bright | 2293edffd0 | |
Sean Bright | 6556a92393 | |
Naveen Albert | 22e34193ee | |
Sean Bright | 3859b630a7 | |
Sean Bright | 0dcf03e844 | |
Naveen Albert | c222343ec6 | |
Sean Bright | f8212d4594 | |
Sean Bright | ff012323e8 | |
Sean Bright | 9e1a60727e | |
Sean Bright | f2961f048d | |
Sean Bright | 978d09fc35 | |
George Joseph | d10d4d9ddd | |
Naveen Albert | 12b353eae0 | |
Matthew Fredrickson | 275f7911b5 | |
Naveen Albert | 1f19227eab | |
Naveen Albert | a0fc8d1b5f | |
Sean Bright | d2afb10eed | |
George Joseph | d819a6bccb | |
Sean Bright | a83c761c95 | |
Sean Bright | 26918d05f4 | |
George Joseph | cd77953172 | |
Sean Bright | 0e126b3841 | |
Naveen Albert | 4b9a4483fc | |
Holger Hans Peter Freyther | 56733c73b4 | |
Holger Hans Peter Freyther | 157389bc59 | |
Brad Smith | e7943dd4d9 | |
Brad Smith | 65d38c8104 | |
Naveen Albert | 5046620fa3 | |
Naveen Albert | 2191a0d33f | |
Mark Murawski | 2ed8daa3cb | |
Naveen Albert | cf9d7fa9f6 | |
Sean Bright | fb937d1d89 | |
George Joseph | f301d4559e | |
Naveen Albert | 8d9d0d3738 | |
Mike Bradeen | c3e2bff36e | |
Sean Bright | deeb1acffe | |
Sean Bright | d4e4942cf5 | |
George Joseph | b619b64137 | |
Sean Bright | 9d329da346 | |
Samuel Olaechea | ebc78a83be | |
George Joseph | 9efc4bdfbc | |
sungtae kim | f89e56c178 | |
George Joseph | be1e83a6ac | |
George Joseph | 289aa1840e | |
George Joseph | 0c1c6e9ada | |
Mike Bradeen | 79220e3f0c | |
Holger Hans Peter Freyther | 1171dcee02 | |
Sean Bright | acb2348f90 | |
George Joseph | 20398e8e95 | |
George Joseph | e9abf11a26 | |
Joshua C. Colp | ef7b0f4c3b | |
George Joseph | b52e07ee1b | |
George Joseph | a9d4175e1d | |
Mike Bradeen | 933490b758 | |
Naveen Albert | 945babf25c | |
Bastian Triller | e6d5b8d8cf | |
Mike Bradeen | 8c934fb7ed | |
Naveen Albert | c04923fcda | |
Eduardo | 11d87713eb | |
George Joseph | 7e2243f9e1 | |
George Joseph | 07cf37531a | |
Tinet-mucw | edc674a6ca | |
Mike Bradeen | 248b92563c | |
Naveen Albert | 71215561d8 | |
George Joseph | 4493d2b2fc | |
Naveen Albert | 833ee80789 | |
Vitezslav Novy | 5179f1af24 | |
George Joseph | 06da7b342e | |
Mike Bradeen | dd817c2708 | |
Sean Bright | 3fafd7c0b7 | |
Mike Bradeen | b53e946b59 | |
George Joseph | 9e2433f73f | |
George Joseph | c929146c61 | |
Maximilian Fridrich | 98ffcfebda | |
Jaco Kroon | a4cb63e231 | |
Joshua C. Colp | abe4f62554 | |
George Joseph | a64718c32c | |
George Joseph | b7dae87d1d | |
George Joseph | c06f938851 | |
Bastian Triller | 0c0b99c5a1 | |
George Joseph | ddbc56505e | |
Naveen Albert | 4542ffe5d5 | |
Mike Bradeen | 36b749ddf8 | |
George Joseph | cb9223cdb9 | |
Naveen Albert | 5077301de6 | |
zhengsh | afe461419e | |
George Joseph | 715594767d | |
Maximilian Fridrich | fcdffe0074 | |
Naveen Albert | 52283301e8 | |
Matthew Fredrickson | 9a28531362 | |
Matthew Fredrickson | c8f2698ec6 | |
George Joseph | d9494ca392 | |
Jason D. McCormick | fc57dca50d | |
MikeNaso | b286d1cca2 | |
Sean Bright | 29eb4fe620 | |
George Joseph | 64b29be702 | |
Naveen Albert | 8be503b663 | |
George Joseph | caf51592e7 | |
Joshua C. Colp | 07d798875d | |
Maximilian Fridrich | 8824b845c4 | |
Naveen Albert | c16cc93a4b | |
Joshua C. Colp | 5ebe1b61bd | |
Naveen Albert | 5516763e57 | |
Holger Hans Peter Freyther | a10de8bc72 | |
Sean Bright | b5762cd54e | |
phoneben | c0b8adfe4d | |
Sean Bright | df87ada8ac | |
Sean Bright | b8aee4a2c6 | |
Joshua C. Colp | 879d4ff56a | |
George Joseph | 12f231c717 | |
George Joseph | 74c809d71f | |
George Joseph | 10b3c2dc45 | |
Sean Bright | d26a00ef34 | |
Sean Bright | e120694711 | |
Mike Bradeen | b8db3dda7e | |
Mike Bradeen | 9b5c29d943 | |
zhengsh | 25a766f49d | |
George Joseph | 0f9de8a3f0 | |
George Joseph | cd7e513087 | |
Naveen Albert | d87abb2ec9 | |
Sean Bright | d902e8e817 | |
George Joseph | 640ead0071 | |
Stanislav Abramenkov | d596c0248a | |
George Joseph | 31663fed53 | |
Mike Bradeen | 2711fba4b9 | |
Mike Bradeen | 9c889911ad | |
Mike Bradeen | 3acdffc17e | |
George Joseph | 4ff17f44a4 | |
George Joseph | caf0fd357c | |
George Joseph | 80d042cb01 | |
George Joseph | b008fd5919 | |
George Joseph | 44c0e1b756 | |
George Joseph | 91503078ff | |
George Joseph | de40ea0f79 | |
George Joseph | 07a3784745 | |
George Joseph | 426b9c568c | |
Olaf Titz | af2ced484e | |
Sean Bright | 4a1edb9a8c | |
Ben Ford | ad120e5d0b | |
Nathan Bruning | 6084bbfe0e | |
Sean Bright | b7eae29fb9 | |
Sean Bright | 97b901a29b | |
Sean Bright | 21d3c4ae58 | |
zhou_jiajian | f7e1f1fbbd | |
Sean Bright | ea63148b23 | |
George Joseph | 25bc5544d8 | |
Sean Bright | ebc007322b | |
Jaco Kroon | 4a637d6d11 | |
Jiajian Zhou | 8e3f9dcc7b | |
George Joseph | 83781c23b9 | |
George Joseph | 6c8b23a688 | |
George Joseph | b3c2a9cd44 | |
Sean Bright | fe15631d38 | |
alex2grad | a8ea16cdf8 | |
Sean Bright | ada3dc2adb | |
Miguel Angel Nubla | 95d339ac7a | |
Naveen Albert | edf488c76e | |
Naveen Albert | 86a11d5b19 | |
Ben Ford | e72b277828 | |
Naveen Albert | 87f44dc0f9 | |
Joe Searle | ec64828c8f | |
Niklas Larsson | df774619fb | |
George Joseph | a83f35c58e | |
Mike Bradeen | f19a6cf11e | |
George Joseph | 7bbeda3908 | |
George Joseph | 2a75114b6d | |
Naveen Albert | d3227a614a | |
Naveen Albert | 22c9d52289 | |
Maximilian Fridrich | 1a7dada804 | |
Naveen Albert | 22b599d86f | |
George Joseph | 15439d08bd | |
Naveen Albert | cfae64a70d | |
George Joseph | 7193e95676 | |
George Joseph | af0535f302 | |
George Joseph | e72b35e7fb | |
Jaco Kroon | f178bf78b7 | |
Sean Bright | 2d18fb6e9d | |
zhengsh | 8abb294063 | |
Sean Bright | 19973b73fd | |
Joshua C. Colp | f0e2d45089 | |
Gitea | c0e32d8245 | |
Joshua C. Colp | 851ec44714 | |
George Joseph | 9d99f6aaf1 | |
Henning Westerholt | 37a41a2a20 | |
Mike Bradeen | 8d3d8fcdcd | |
Sean Bright | 5c6d5ea38f | |
George Joseph | 9097fc7515 | |
George Joseph | fbde0a71f0 | |
George Joseph | aced9046ca | |
George Joseph | b7eeef6314 | |
Maximilian Fridrich | f3cc1e7fbd | |
George Joseph | 3707140472 | |
Joshua C. Colp | a614604f32 | |
Naveen Albert | 82d7bb49dd | |
George Joseph | ef644c3e93 | |
George Joseph | 313cd9dd84 | |
George Joseph | 987315a5fa | |
George Joseph | f8dc014819 | |
Joshua C. Colp | 6c53e5e870 | |
Joshua Colp | de15852ef0 | |
Naveen Albert | 66a7cff57e | |
Naveen Albert | b33f92cbb5 | |
The_Blode | de9aaf7e97 | |
Henning Westerholt | 1c5720b802 | |
Naveen Albert | d0f39250ee | |
Naveen Albert | bad5bda08c | |
George Joseph | a0fd95ef52 | |
Mike Bradeen | df554a447c | |
Jaco Kroon | 019dc51139 | |
Sean Bright | 6dab013e49 | |
Sean Bright | 34ff836db5 | |
Sean Bright | 1ba3b34f77 | |
Mike Bradeen | ffe346b2de | |
Mike Bradeen | fa635a872e | |
Mike Bradeen | 8d2ffc8aa5 | |
Sean Bright | a3ec3efa02 | |
Naveen Albert | f9fd76677f | |
Naveen Albert | 3556ca239a | |
Naveen Albert | 090ec448cf | |
Fabrice Fontaine | cb0220dec2 | |
Mike Bradeen | 405211eff7 | |
Jaco Kroon | 3fd0b65bae | |
George Joseph | bbec5d1a99 | |
Holger Hans Peter Freyther | 8f088aa0f7 | |
Fabrice Fontaine | 030b7b9009 | |
Sean Bright | 46bdd5e3be | |
Asterisk Development Team | 93813c9dca | |
George Joseph | ceda5a9859 | |
Sean Bright | e5c5cd6e25 | |
Naveen Albert | ede67a99be | |
Sean Bright | 827222d607 | |
Nick French | 200dc7d0e8 | |
Mike Bradeen | 5c11d7adea | |
cmaj | 5b0e3444c3 | |
Mike Bradeen | 2308afed8e | |
Mike Bradeen | 98742388b6 | |
Mike Bradeen | 37e558f6ef | |
Sean Bright | aeb16aa7d8 | |
Sean Bright | aef0c0ce0e | |
Mike Bradeen | 58636a6ea6 | |
Sean Bright | 96d9ad51ac | |
Naveen Albert | 88b2c741ca | |
Mike Bradeen | 70856e865f | |
Naveen Albert | 8a45cd7af4 | |
sungtae kim | f99849f8d5 | |
Sean Bright | 56051d1ac5 | |
Naveen Albert | a1da8042d1 | |
Sean Bright | ef16eaee36 | |
George Joseph | 2f5aece0c9 | |
Alexei Gradinari | e86d5d7fda | |
Igor Goncharovsky | 3526441e41 | |
George Joseph | 4710f37ef6 | |
George Joseph | 62ca063fca | |
Naveen Albert | d33bd6d67a | |
Naveen Albert | e06fe8e344 | |
Naveen Albert | 68e345286b | |
Naveen Albert | 3b3fef2347 | |
Naveen Albert | 7b8f7428da | |
George Joseph | 24102ba236 | |
Boris P. Korzun | edc90c96ac | |
Holger Hans Peter Freyther | 3d9b9a2b16 | |
George Joseph | d454801c2d | |
Naveen Albert | cc8d9b947b | |
Naveen Albert | c7598ee947 | |
Ben Ford | 881faf544f | |
Naveen Albert | 20d4775d0a | |
Naveen Albert | cbb1fd2cb9 | |
Igor Goncharovsky | 115a1b4f0a | |
Peter Fern | 58404b5c22 | |
Naveen Albert | 36bea9ad33 | |
Asterisk Development Team | fefc236e7c | |
Alexandre Fournier | 01b3962201 | |
Joshua C. Colp | b6855755ce | |
Naveen Albert | 2f9cdfbc50 | |
Michael Kuron | 5c114dcb4a | |
Michael Kuron | fee9012fe1 | |
Joshua C. Colp | 564349ff5d | |
Naveen Albert | b9c031c1f8 | |
Marcel Wagner | 58534b309f | |
Naveen Albert | 531eacd6c9 | |
Naveen Albert | b365ea8601 | |
Naveen Albert | 0d6003fa9a | |
Marcel Wagner | b83af13f65 | |
Naveen Albert | 80e6205bb0 | |
Naveen Albert | 406143ae61 | |
Naveen Albert | 83eb113e0f | |
Naveen Albert | b90e57758b | |
Naveen Albert | 52c7d3ed07 | |
Naveen Albert | a4bcdce1db | |
Naveen Albert | 691178c48e | |
Ben Ford | d476994768 | |
George Joseph | 7684c9e907 | |
Mike Bradeen | 81f10e847e | |
Mike Bradeen | eb1d7ab53c | |
Naveen Albert | c7df5ee7c1 | |
Naveen Albert | 5ede4e217a | |
Maximilian Fridrich | 60b81eabe0 | |
Naveen Albert | 2efa290d3c | |
Jaco Kroon | ce2153fc5a | |
Naveen Albert | 002afc3f2a | |
Naveen Albert | 1e77b8c473 | |
Joshua C. Colp | 61922d2934 | |
Naveen Albert | 6e59b01e1a | |
Naveen Albert | 49cfdbbdff | |
Naveen Albert | 8142b313c3 | |
George Joseph | 0c1c623dee | |
Naveen Albert | dfe2f38642 | |
George Joseph | f723b465e5 | |
Mike Bradeen | 50e2921a48 | |
Naveen Albert | afd86b47c1 | |
Igor Goncharovsky | 096529d33f | |
Naveen Albert | ca8900b0f6 | |
Henning Westerholt | 12445040d3 | |
Naveen Albert | 40b52322e5 | |
Naveen Albert | c32b39d123 | |
Frederic LE FOLL | 50a4495799 | |
Naveen Albert | 180ca32565 | |
Naveen Albert | 9258d8212a | |
Naveen Albert | 407216a0a5 | |
Philip Prindeville | d0bea5a725 | |
Mike Bradeen | 907d7e7d7d | |
Naveen Albert | b331caca30 | |
Naveen Albert | e0e7f35730 | |
Naveen Albert | 98fc05f13b | |
Philip Prindeville | ef74ecacc7 | |
Philip Prindeville | 5e2485b5c0 | |
George Joseph | 2a500b325a | |
Maximilian Fridrich | 0d2e140123 | |
Asterisk Development Team | 7f80830ced | |
Holger Hans Peter Freyther | 62881c668b | |
Naveen Albert | 8afb313a43 | |
Naveen Albert | 7335b0cffe | |
Naveen Albert | 407167cc28 | |
Naveen Albert | a5ec60e6c6 | |
Naveen Albert | 1e29607b5c | |
Naveen Albert | 8c791f9a65 | |
Philip Prindeville | 3e7ce90f9c | |
Naveen Albert | 1ed4518328 | |
Maximilian Fridrich | 5bbad0d27c | |
Naveen Albert | 8aae0b9f08 | |
Jaco Kroon | 278c5726ca | |
Naveen Albert | ab1dbfef75 | |
George Joseph | e25b690d10 | |
George Joseph | e33f2dcc0f | |
Philip Prindeville | 026dc08529 | |
Asterisk Development Team | f01ed3eea4 | |
Mike Bradeen | 7a44296ca9 | |
George Joseph | 8cbea1c7ef | |
sungtae kim | 80bc844fd6 | |
Ben Ford | 881a3f2306 | |
Philip Prindeville | 3e054c9ebc | |
Philip Prindeville | 736cdf84f4 | |
Philip Prindeville | 2d7656cb50 | |
Philip Prindeville | 5809d879b0 | |
Philip Prindeville | 2c4c44ca64 | |
Philip Prindeville | b9df2c481b | |
Philip Prindeville | d13afaf302 | |
Naveen Albert | 2dac2bf8dc | |
Naveen Albert | c487425620 | |
Naveen Albert | 205c7c8d21 | |
Naveen Albert | 2de016b181 | |
George Joseph | 05f42806cc | |
George Joseph | c799db6a21 | |
George Joseph | 4ffc5561c4 | |
George Joseph | 2d5a6498dd | |
Joshua C. Colp | f3de933b16 | |
Naveen Albert | c7612521be | |
Joshua C. Colp | a0713a9f70 | |
Naveen Albert | 754346a4a9 | |
Mike Bradeen | 46776c77c4 | |
Sean Bright | 583e017f34 | |
Alexei Gradinari | 12c4c1bf5f | |
Sean Bright | 155c796203 | |
Naveen Albert | 3fa66c92b5 | |
Mike Bradeen | adffb975dc | |
Mike Bradeen | 4fc9e06db1 | |
Naveen Albert | e2e049e473 | |
George Joseph | 8a8416e365 | |
Naveen Albert | ff044c222b | |
Naveen Albert | dc7ec11c26 | |
George Joseph | 30d7a212b0 | |
Naveen Albert | f4a020a45b | |
Naveen Albert | c654486547 | |
Naveen Albert | 5feebc0857 | |
Naveen Albert | 165368bf0b | |
Naveen Albert | 2d8f2696b2 | |
Naveen Albert | 4af881506e | |
Naveen Albert | 83912496ab | |
Naveen Albert | c771e2dd7a | |
Sergey V. Lobanov | f645157a4b | |
Naveen Albert | a9223f210e | |
Naveen Albert | ce18196280 | |
George Joseph | f8000daff5 |
|
@ -0,0 +1,87 @@
|
||||||
|
name: Bug
|
||||||
|
description: File a bug report
|
||||||
|
title: "[bug]: "
|
||||||
|
labels: ["bug", "triage"]
|
||||||
|
#assignees:
|
||||||
|
# - octocat
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for creating a report! The issue has entered the triage process. That means the issue will wait in this status until a Bug Marshal has an opportunity to review the issue. Once the issue has been reviewed you will receive comments regarding the next steps towards resolution. Please note that log messages and other files should not be sent to the Sangoma Asterisk Team unless explicitly asked for. All files should be placed on this issue in a sanitized fashion as needed.
|
||||||
|
|
||||||
|
A good first step is for you to review the Asterisk Issue Guidelines if you haven't already. The guidelines detail what is expected from an Asterisk issue report.
|
||||||
|
|
||||||
|
Then, if you are submitting a patch, please review the Patch Contribution Process.
|
||||||
|
|
||||||
|
Please note that once your issue enters an open state it has been accepted. As Asterisk is an open source project there is no guarantee or timeframe on when your issue will be looked into. If you need expedient resolution you will need to find and pay a suitable developer. Asking for an update on your issue will not yield any progress on it and will not result in a response. All updates are posted to the issue when they occur.
|
||||||
|
|
||||||
|
Please note that by submitting data, code, or documentation to Sangoma through GitHub, you accept the Terms of Use present at
|
||||||
|
https://www.asterisk.org/terms-of-use/.
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
- type: dropdown
|
||||||
|
id: severity
|
||||||
|
attributes:
|
||||||
|
label: Severity
|
||||||
|
options:
|
||||||
|
- Trivial
|
||||||
|
- Minor
|
||||||
|
- Major
|
||||||
|
- Critical
|
||||||
|
- Blocker
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: versions
|
||||||
|
attributes:
|
||||||
|
label: Versions
|
||||||
|
description: Enter one or more versions separated by commas.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: components
|
||||||
|
attributes:
|
||||||
|
label: Components/Modules
|
||||||
|
description: Enter one or more components or modules separated by commas.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: Operating Environment
|
||||||
|
description: OS, Disribution, Version, etc.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: frequency
|
||||||
|
attributes:
|
||||||
|
label: Frequency of Occurrence
|
||||||
|
options:
|
||||||
|
- "Never"
|
||||||
|
- "One Time"
|
||||||
|
- "Occasional"
|
||||||
|
- "Frequent"
|
||||||
|
- "Constant"
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Issue Description
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Relevant log output
|
||||||
|
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||||
|
render: shell
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
[Asterisk Issue Guidelines](https://wiki.asterisk.org/wiki/display/AST/Asterisk+Issue+Guidelines)
|
||||||
|
- type: checkboxes
|
||||||
|
id: guidelines
|
||||||
|
attributes:
|
||||||
|
label: Asterisk Issue Guidelines
|
||||||
|
options:
|
||||||
|
- label: Yes, I have read the Asterisk Issue Guidelines
|
||||||
|
required: true
|
|
@ -0,0 +1,11 @@
|
||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Asterisk Community Support
|
||||||
|
url: https://community.asterisk.org
|
||||||
|
about: Please ask and answer questions here.
|
||||||
|
- name: Feature Requests (Without Code)
|
||||||
|
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.
|
|
@ -0,0 +1,27 @@
|
||||||
|
name: Improvement
|
||||||
|
description: Submit an improvement to existing functionality
|
||||||
|
title: "[improvement]: "
|
||||||
|
labels: ["improvement", "triage"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for creating a report! The issue has entered the triage process. That means the issue will wait in this status until a Bug Marshal has an opportunity to review the issue. Once the issue has been reviewed you will receive comments regarding the next steps towards resolution. Please note that log messages and other files should not be sent to the Sangoma Asterisk Team unless explicitly asked for. All files should be placed on this issue in a sanitized fashion as needed.
|
||||||
|
|
||||||
|
A good first step is for you to review the Asterisk Issue Guidelines if you haven't already. The guidelines detail what is expected from an Asterisk issue report.
|
||||||
|
|
||||||
|
Then, if you are submitting a patch, please review the Patch Contribution Process.
|
||||||
|
|
||||||
|
Please note that once your issue enters an open state it has been accepted. As Asterisk is an open source project there is no guarantee or timeframe on when your issue will be looked into. If you need expedient resolution you will need to find and pay a suitable developer. Asking for an update on your issue will not yield any progress on it and will not result in a response. All updates are posted to the issue when they occur.
|
||||||
|
|
||||||
|
Please note that by submitting data, code, or documentation to Sangoma through GitHub, you accept the Terms of Use present at
|
||||||
|
https://www.asterisk.org/terms-of-use/.
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Improvement Description
|
||||||
|
description: Describe the improvement in as much detail as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
name: New Feature Submission
|
||||||
|
description: Submit a New Feature
|
||||||
|
title: "[new-feature]: "
|
||||||
|
labels: ["new-feature", "triage"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for creating a report! The issue has entered the triage process. That means the issue will wait in this status until a Bug Marshal has an opportunity to review the issue. Once the issue has been reviewed you will receive comments regarding the next steps towards resolution. Please note that log messages and other files should not be sent to the Sangoma Asterisk Team unless explicitly asked for. All files should be placed on this issue in a sanitized fashion as needed.
|
||||||
|
|
||||||
|
A good first step is for you to review the Asterisk Issue Guidelines if you haven't already. The guidelines detail what is expected from an Asterisk issue report.
|
||||||
|
|
||||||
|
Then, if you are submitting a patch, please review the Patch Contribution Process.
|
||||||
|
|
||||||
|
Please note that once your issue enters an open state it has been accepted. As Asterisk is an open source project there is no guarantee or timeframe on when your issue will be looked into. If you need expedient resolution you will need to find and pay a suitable developer. Asking for an update on your issue will not yield any progress on it and will not result in a response. All updates are posted to the issue when they occur.
|
||||||
|
|
||||||
|
Please note that by submitting data, code, or documentation to Sangoma through GitHub, you accept the Terms of Use present at
|
||||||
|
https://www.asterisk.org/terms-of-use/.
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Feature Description
|
||||||
|
description: Describe the new feature in as much detail as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
name: CherryPickTest
|
||||||
|
run-name: "Cherry-Pick Tests for PR ${{github.event.number}}"
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [ labeled ]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{github.workflow}}-${{github.event.number}}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
PR_NUMBER: ${{ github.event.number }}
|
||||||
|
MODULES_BLACKLIST: ${{ vars.GATETEST_MODULES_BLACKLIST }} ${{ vars.UNITTEST_MODULES_BLACKLIST }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
IdentifyBranches:
|
||||||
|
name: IdentifyBranches
|
||||||
|
if: ${{ github.event.label.name == vars.CHERRY_PICK_TEST_LABEL }}
|
||||||
|
outputs:
|
||||||
|
branches: ${{ steps.getbranches.outputs.branches }}
|
||||||
|
branch_count: ${{ steps.getbranches.outputs.branch_count }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Remove Trigger Label, Add InProgress Label
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{vars.CHERRY_PICK_TEST_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.CHERRY_PICK_TESTING_IN_PROGRESS}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
|
||||||
|
- 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: Check Branch Count
|
||||||
|
if: ${{ steps.getbranches.outputs.branch_count > 0 }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--add-label ${{vars.CHERRY_PICK_TESTING_IN_PROGRESS}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
|
||||||
|
CherryPickUnitTestMatrix:
|
||||||
|
needs: [ IdentifyBranches ]
|
||||||
|
if: ${{ needs.IdentifyBranches.outputs.branch_count > 0 && ( success() || failure() ) }}
|
||||||
|
continue-on-error: false
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
branch: ${{ fromJSON(needs.IdentifyBranches.outputs.branches) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Run Unit Tests for branch ${{matrix.branch}}
|
||||||
|
uses: asterisk/asterisk-ci-actions/AsteriskUnitComposite@main
|
||||||
|
with:
|
||||||
|
asterisk_repo: ${{github.repository}}
|
||||||
|
pr_number: ${{env.PR_NUMBER}}
|
||||||
|
base_branch: ${{matrix.branch}}
|
||||||
|
is_cherry_pick: true
|
||||||
|
modules_blacklist: ${{env.MODULES_BLACKLIST}}
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
unittest_command: ${{vars.UNITTEST_COMMAND}}
|
||||||
|
|
||||||
|
CherryPickUnitTests:
|
||||||
|
needs: [ IdentifyBranches, CherryPickUnitTestMatrix ]
|
||||||
|
if: ${{ needs.IdentifyBranches.outputs.branch_count > 0 && ( success() || failure() ) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check unit test matrix status
|
||||||
|
env:
|
||||||
|
RESULT: ${{needs.CherryPickUnitTestMatrix.result}}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
case $RESULT in
|
||||||
|
success)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--add-label ${{vars.CHERRY_PICK_CHECKS_PASSED_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
echo "::notice::All tests passed"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
skipped)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{vars.CHERRY_PICK_TESTING_IN_PROGRESS}} \
|
||||||
|
--add-label ${{vars.CHERRY_PICK_CHECKS_FAILED_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
echo "::notice::Unit tests were skipped because of an earlier failure"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{vars.CHERRY_PICK_TESTING_IN_PROGRESS}} \
|
||||||
|
--add-label ${{vars.CHERRY_PICK_CHECKS_FAILED_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
echo "::error::One or more tests failed ($RESULT)"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
CherryPickGateTestMatrix:
|
||||||
|
needs: [ IdentifyBranches, CherryPickUnitTests ]
|
||||||
|
if: ${{ success() }}
|
||||||
|
continue-on-error: false
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
branch: ${{ fromJSON(needs.IdentifyBranches.outputs.branches) }}
|
||||||
|
group: ${{ fromJSON(vars.GATETEST_LIST) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Run Gate Tests for ${{ matrix.group }}-${{matrix.branch}}
|
||||||
|
uses: asterisk/asterisk-ci-actions/AsteriskGateComposite@main
|
||||||
|
with:
|
||||||
|
test_type: Gate
|
||||||
|
asterisk_repo: ${{github.repository}}
|
||||||
|
pr_number: ${{env.PR_NUMBER}}
|
||||||
|
base_branch: ${{matrix.branch}}
|
||||||
|
is_cherry_pick: true
|
||||||
|
modules_blacklist: ${{env.MODULES_BLACKLIST}}
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
testsuite_repo: ${{vars.TESTSUITE_REPO}}
|
||||||
|
gatetest_group: ${{matrix.group}}
|
||||||
|
gatetest_command: ${{ toJSON(fromJSON(vars.GATETEST_COMMANDS)[matrix.group]) }}
|
||||||
|
|
||||||
|
CherryPickGateTests:
|
||||||
|
needs: [ IdentifyBranches, CherryPickGateTestMatrix ]
|
||||||
|
if: ${{ success() || failure() }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check test matrix status
|
||||||
|
env:
|
||||||
|
RESULT: ${{needs.CherryPickGateTestMatrix.result}}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{vars.CHERRY_PICK_TESTING_IN_PROGRESS}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
case $RESULT in
|
||||||
|
success)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--add-label ${{vars.CHERRY_PICK_GATES_PASSED_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
echo "::notice::All Testsuite tests passed"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
skipped)
|
||||||
|
echo "::error::Testsuite tests were skipped because of an earlier failure"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--add-label ${{vars.CHERRY_PICK_GATES_FAILED_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
echo "::error::One or more Testsuite tests failed ($RESULT)"
|
||||||
|
exit 1
|
||||||
|
esac
|
|
@ -0,0 +1,123 @@
|
||||||
|
name: CreateDocs
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
branches:
|
||||||
|
description: "JSON array of branches: ['18','20'] (no spaces)"
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
schedule:
|
||||||
|
# Times are UTC
|
||||||
|
- cron: '0 04 * * *'
|
||||||
|
|
||||||
|
env:
|
||||||
|
ASTERISK_REPO: ${{ github.repository }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
DEFAULT_BRANCHES: ${{ vars.WIKIDOC_BRANCHES }}
|
||||||
|
INPUT_BRANCHES: ${{ inputs.branches }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
CreateDocsDebug:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
manual_branches: ${{ steps.setup.outputs.manual_branches }}
|
||||||
|
steps:
|
||||||
|
- name: setup
|
||||||
|
run: |
|
||||||
|
MANUAL_BRANCHES="$INPUT_BRANCHES"
|
||||||
|
[ -z "$MANUAL_BRANCHES" ] && MANUAL_BRANCHES="$DEFAULT_BRANCHES" || :
|
||||||
|
echo "manual_branches=${MANUAL_BRANCHES}"
|
||||||
|
echo "manual_branches=${MANUAL_BRANCHES}" >>${GITHUB_OUTPUT}
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
- name: DumpEnvironment
|
||||||
|
uses: asterisk/asterisk-ci-actions/DumpEnvironmentAction@main
|
||||||
|
with:
|
||||||
|
action-inputs: ${{toJSON(inputs)}}
|
||||||
|
action-vars: ${{ toJSON(steps.setup.outputs) }}
|
||||||
|
|
||||||
|
CreateDocsScheduledMatrix:
|
||||||
|
needs: [ CreateDocsDebug ]
|
||||||
|
if: ${{github.event_name == 'schedule' && fromJSON(vars.WIKIDOCS_ENABLE) == true }}
|
||||||
|
continue-on-error: false
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
branch: ${{ fromJSON(vars.WIKIDOC_BRANCHES) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: CreateDocs for ${{matrix.branch}}
|
||||||
|
uses: asterisk/asterisk-ci-actions/CreateAsteriskDocsComposite@main
|
||||||
|
with:
|
||||||
|
asterisk_repo: ${{env.ASTERISK_REPO}}
|
||||||
|
base_branch: ${{matrix.branch}}
|
||||||
|
docs_dir: docs_dir/${{matrix.branch}}
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
|
||||||
|
CreateDocsScheduled:
|
||||||
|
needs: [ CreateDocsScheduledMatrix ]
|
||||||
|
if: ${{ success() || failure() }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check CreateDocsScheduledMatrix status
|
||||||
|
env:
|
||||||
|
RESULT: ${{needs.CreateDocsScheduledMatrix.result}}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
case $RESULT in
|
||||||
|
success)
|
||||||
|
echo "::notice::Docs created"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
skipped)
|
||||||
|
echo "::notice::Skipped"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "::error::One or CreateDocs failed ($RESULT)"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
CreateDocsManualMatrix:
|
||||||
|
needs: [ CreateDocsDebug ]
|
||||||
|
if: ${{github.event_name == 'workflow_dispatch'}}
|
||||||
|
continue-on-error: false
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
branch: ${{ fromJSON(vars.WIKIDOC_MANUAL_BRANCHES) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: CreateDocs for ${{matrix.branch}}
|
||||||
|
uses: asterisk/asterisk-ci-actions/CreateAsteriskDocsComposite@main
|
||||||
|
with:
|
||||||
|
asterisk_repo: ${{env.ASTERISK_REPO}}
|
||||||
|
base_branch: ${{matrix.branch}}
|
||||||
|
docs_dir: docs_dir/${{matrix.branch}}
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
|
||||||
|
CreateDocsManual:
|
||||||
|
needs: [ CreateDocsManualMatrix ]
|
||||||
|
if: ${{ success() || failure() }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check CreateDocsManualMatrix status
|
||||||
|
env:
|
||||||
|
RESULT: ${{needs.CreateDocsManualMatrix.result}}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
case $RESULT in
|
||||||
|
success)
|
||||||
|
echo "::notice::Docs created"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
skipped)
|
||||||
|
echo "::notice::Skipped"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "::error::One or CreateDocs failed ($RESULT)"
|
||||||
|
exit 1
|
||||||
|
esac
|
|
@ -0,0 +1,15 @@
|
||||||
|
name: Issue Opened
|
||||||
|
run-name: "Issue ${{github.event.number}} ${{github.event.action}} by ${{github.actor}}"
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: opened
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
triage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: initial labeling
|
||||||
|
uses: andymckay/labeler@master
|
||||||
|
with:
|
||||||
|
add-labels: "triage"
|
||||||
|
ignore-if-labeled: true
|
|
@ -0,0 +1,187 @@
|
||||||
|
name: MergeApproved
|
||||||
|
run-name: "Merge Approved for PR ${{github.event.number}}"
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [labeled]
|
||||||
|
|
||||||
|
env:
|
||||||
|
PR_NUMBER: ${{ github.event.number }}
|
||||||
|
BASE_BRANCH: ${{github.event.pull_request.base.ref}}
|
||||||
|
MODULES_BLACKLIST: ${{ vars.GATETEST_MODULES_BLACKLIST }} ${{ vars.UNITTEST_MODULES_BLACKLIST }}
|
||||||
|
FORCE: ${{ endsWith(github.event.label.name, '-force') }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
IdentifyBranches:
|
||||||
|
if: contains(fromJSON(vars.MERGE_APPROVED_LABELS), github.event.label.name)
|
||||||
|
outputs:
|
||||||
|
branches: ${{ steps.getbranches.outputs.branches }}
|
||||||
|
all_branches: ${{ steps.checkbranches.outputs.all_branches }}
|
||||||
|
branch_count: ${{ steps.getbranches.outputs.branch_count }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Clean up labels
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{github.event.label.name}} \
|
||||||
|
--remove-label ${{vars.PRE_MERGE_CHECKS_PASSED_LABEL}} \
|
||||||
|
--remove-label ${{vars.PRE_MERGE_CHECKS_FAILED_LABEL}} \
|
||||||
|
--remove-label ${{vars.PRE_MERGE_GATES_PASSED_LABEL}} \
|
||||||
|
--remove-label ${{vars.PRE_MERGE_GATES_FAILED_LABEL}} \
|
||||||
|
--remove-label ${{vars.PRE_MERGE_TESTING_IN_PROGRESS}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
|
||||||
|
- 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: Check Branch Count
|
||||||
|
id: checkbranches
|
||||||
|
env:
|
||||||
|
BRANCH_COUNT: ${{ steps.getbranches.outputs.branch_count }}
|
||||||
|
BRANCHES: ${{ steps.getbranches.outputs.branches }}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--add-label ${{vars.PRE_MERGE_TESTING_IN_PROGRESS}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
all_branches=$(echo "$BRANCHES" | jq -c "[ \"$BASE_BRANCH\" ] + .")
|
||||||
|
echo "all_branches=${all_branches}" >>${GITHUB_OUTPUT}
|
||||||
|
|
||||||
|
- name: Pre Check Cherry-Picks
|
||||||
|
if: ${{ steps.getbranches.outputs.branch_count > 0 }}
|
||||||
|
uses: asterisk/asterisk-ci-actions/CherryPick@main
|
||||||
|
with:
|
||||||
|
repo: ${{github.repository}}
|
||||||
|
pr_number: ${{env.PR_NUMBER}}
|
||||||
|
branches: ${{steps.getbranches.outputs.branches}}
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
push: false
|
||||||
|
|
||||||
|
PreMergeUnitTestMatrix:
|
||||||
|
needs: [ IdentifyBranches ]
|
||||||
|
if: success()
|
||||||
|
continue-on-error: false
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
branch: ${{ fromJSON(needs.IdentifyBranches.outputs.all_branches) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Run Unit Tests for branch ${{matrix.branch}}
|
||||||
|
uses: asterisk/asterisk-ci-actions/AsteriskUnitComposite@main
|
||||||
|
with:
|
||||||
|
asterisk_repo: ${{github.repository}}
|
||||||
|
pr_number: ${{env.PR_NUMBER}}
|
||||||
|
base_branch: ${{matrix.branch}}
|
||||||
|
is_cherry_pick: true
|
||||||
|
modules_blacklist: ${{env.MODULES_BLACKLIST}}
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
unittest_command: ${{vars.UNITTEST_COMMAND}}
|
||||||
|
|
||||||
|
PreMergeUnitTests:
|
||||||
|
needs: [ IdentifyBranches, PreMergeUnitTestMatrix ]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check unit test matrix status
|
||||||
|
env:
|
||||||
|
RESULT: ${{needs.PreMergeUnitTestMatrix.result}}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
case $RESULT in
|
||||||
|
success)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{vars.PRE_MERGE_TESTING_IN_PROGRESS}} \
|
||||||
|
--add-label ${{vars.PRE_MERGE_CHECKS_PASSED_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
echo "::notice::All tests passed"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
skipped)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{vars.PRE_MERGE_TESTING_IN_PROGRESS}} \
|
||||||
|
--add-label ${{vars.PRE_MERGE_CHECKS_FAILED_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
echo "::notice::Unit tests were skipped because of an earlier failure"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{vars.PRE_MERGE_TESTING_IN_PROGRESS}} \
|
||||||
|
--add-label ${{vars.PRE_MERGE_CHECKS_FAILED_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
echo "::error::One or more tests failed ($RESULT)"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
MergeAndCherryPick:
|
||||||
|
needs: [ IdentifyBranches, PreMergeUnitTests ]
|
||||||
|
if: success()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Start Merge
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--add-label ${{vars.MERGE_IN_PROGRESS_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
|
||||||
|
- name: Get Token needed to push cherry-picks
|
||||||
|
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: Merge and Cherry Pick to ${{needs.IdentifyBranches.outputs.branches}}
|
||||||
|
id: mergecp
|
||||||
|
uses: asterisk/asterisk-ci-actions/MergeAndCherryPickComposite@main
|
||||||
|
with:
|
||||||
|
repo: ${{github.repository}}
|
||||||
|
pr_number: ${{env.PR_NUMBER}}
|
||||||
|
branches: ${{needs.IdentifyBranches.outputs.branches}}
|
||||||
|
force: ${{env.FORCE}}
|
||||||
|
github_token: ${{steps.get_workflow_token.outputs.token}}
|
||||||
|
|
||||||
|
- name: Merge Cleanup
|
||||||
|
if: always()
|
||||||
|
env:
|
||||||
|
RESULT: ${{ steps.mergecp.outcome }}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
BRANCH_COUNT: ${{ needs.IdentifyBranches.outputs.branch_count }}
|
||||||
|
BRANCHES: ${{ needs.IdentifyBranches.outputs.branches }}
|
||||||
|
|
||||||
|
run: |
|
||||||
|
case $RESULT in
|
||||||
|
success)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{vars.MERGE_IN_PROGRESS_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
if [ $BRANCH_COUNT -eq 0 ] ; then
|
||||||
|
gh pr comment --repo ${{github.repository}} \
|
||||||
|
-b "Successfully merged to branch $BASE_BRANCH." \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
else
|
||||||
|
gh pr comment --repo ${{github.repository}} \
|
||||||
|
-b "Successfully merged to branch $BASE_BRANCH and cherry-picked to $BRANCHES" \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
failure)
|
||||||
|
gh pr edit --repo ${{github.repository}} \
|
||||||
|
--remove-label ${{vars.MERGE_IN_PROGRESS_LABEL}} \
|
||||||
|
--add-label ${{vars.MERGE_FAILED_LABEL}} \
|
||||||
|
${{env.PR_NUMBER}} || :
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
esac
|
|
@ -0,0 +1,28 @@
|
||||||
|
name: Nightly Admin
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '30 1 * * *'
|
||||||
|
|
||||||
|
env:
|
||||||
|
ASTERISK_REPO: ${{ github.repository }}
|
||||||
|
PR_NUMBER: 0
|
||||||
|
PR_COMMIT: ''
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
MODULES_BLACKLIST: ${{ vars.GATETEST_MODULES_BLACKLIST }} ${{ vars.UNITTEST_MODULES_BLACKLIST }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
CloseStaleIssues:
|
||||||
|
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
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
name: NightlyTests
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
schedule:
|
||||||
|
- cron: '0 2 * * *'
|
||||||
|
|
||||||
|
env:
|
||||||
|
ASTERISK_REPO: ${{ github.repository }}
|
||||||
|
PR_NUMBER: 0
|
||||||
|
PR_COMMIT: ''
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
MODULES_BLACKLIST: ${{ vars.GATETEST_MODULES_BLACKLIST }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
AsteriskNightly:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
branch: ${{ fromJSON(vars.NIGHTLYTEST_BRANCHES) }}
|
||||||
|
group: ${{ fromJSON(vars.NIGHTLYTEST_LIST) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Run Nightly Tests for ${{ matrix.group }}/${{ matrix.branch }}
|
||||||
|
uses: asterisk/asterisk-ci-actions/AsteriskGateComposite@main
|
||||||
|
with:
|
||||||
|
test_type: Nightly
|
||||||
|
asterisk_repo: ${{env.ASTERISK_REPO}}
|
||||||
|
pr_number: ${{env.PR_NUMBER}}
|
||||||
|
base_branch: ${{matrix.branch}}
|
||||||
|
modules_blacklist: ${{env.MODULES_BLACKLIST}}
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
testsuite_repo: ${{vars.TESTSUITE_REPO}}
|
||||||
|
gatetest_group: ${{matrix.group}}
|
||||||
|
gatetest_command: ${{ toJSON(fromJSON(vars.GATETEST_COMMANDS)[matrix.group]) }}
|
||||||
|
|
||||||
|
AsteriskNightlyTests:
|
||||||
|
if: ${{ always() }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: AsteriskNightly
|
||||||
|
steps:
|
||||||
|
- name: Check test matrix status
|
||||||
|
env:
|
||||||
|
RESULT: ${{needs.AsteriskNightly.result}}
|
||||||
|
run: |
|
||||||
|
case $RESULT in
|
||||||
|
success)
|
||||||
|
echo "::notice::All Testsuite tests passed"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
skipped)
|
||||||
|
echo "::error::Testsuite tests were skipped because of an earlier failure"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "::error::One or more Testsuite tests failed"
|
||||||
|
exit 1
|
||||||
|
esac
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: PRMerged
|
||||||
|
run-name: "PR ${{github.event.number || inputs.pr_number}} ${{github.event.action || 'MANUAL POST MERGE'}} by ${{ github.actor }}"
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [closed]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
pr_number:
|
||||||
|
description: 'PR number'
|
||||||
|
required: true
|
||||||
|
type: number
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{github.workflow}}-${{github.event.number || inputs.pr_number}}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
REPO: ${{github.repository}}
|
||||||
|
PR_NUMBER: ${{github.event.number || inputs.pr_number}}
|
||||||
|
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
CloseIssues:
|
||||||
|
if: github.event.pull_request.merged == true
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: wow-actions/auto-close-fixed-issues@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
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
|
||||||
|
script: |
|
||||||
|
let rs = JSON.parse(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;
|
|
@ -0,0 +1,114 @@
|
||||||
|
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
|
|
@ -0,0 +1,99 @@
|
||||||
|
name: Releaser
|
||||||
|
run-name: ${{ github.actor }} is creating ${{vars.PRODUCT_NAME}} release ${{inputs.new_version}}
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
new_version:
|
||||||
|
description: |
|
||||||
|
New Version:
|
||||||
|
Examples:
|
||||||
|
20.4.0-rc1, 20.4.0-rc2, 20.4.0, 20.4.1
|
||||||
|
certified-20.4-cert1-rc1, certified-20.4-cert1
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
is_security:
|
||||||
|
description: |
|
||||||
|
Security?
|
||||||
|
(No prev RCs)
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
advisories:
|
||||||
|
description: |
|
||||||
|
Comma separated list of advisories.
|
||||||
|
NO SPACES
|
||||||
|
Example: GHSA-4xjp-22g4-9fxm,GHSA-4xjp-22g4-zzzz
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
is_hotfix:
|
||||||
|
description: |
|
||||||
|
Hotfix?
|
||||||
|
(A patch release but not security. No prev RCs)
|
||||||
|
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?
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
create_github_release:
|
||||||
|
description: |
|
||||||
|
Create the GitHub release?
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
push_tarballs:
|
||||||
|
description: |
|
||||||
|
Push tarballs to downloads server?
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
send_email:
|
||||||
|
description: |
|
||||||
|
Send announcement emails?
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ReleaseAsterisk:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Run Releaser
|
||||||
|
uses: asterisk/asterisk-ci-actions/ReleaserComposite@main
|
||||||
|
with:
|
||||||
|
product: ${{vars.PRODUCT_NAME}}
|
||||||
|
is_security: ${{inputs.is_security}}
|
||||||
|
advisories: ${{inputs.advisories}}
|
||||||
|
is_hotfix: ${{inputs.is_hotfix}}
|
||||||
|
new_version: ${{inputs.new_version}}
|
||||||
|
force_cherry_pick: ${{inputs.force_cherry_pick}}
|
||||||
|
push_release_branches: ${{inputs.push_release_branches}}
|
||||||
|
create_github_release: ${{inputs.create_github_release}}
|
||||||
|
push_tarballs: ${{inputs.push_tarballs}}
|
||||||
|
send_email: ${{inputs.send_email}}
|
||||||
|
repo: ${{github.repository}}
|
||||||
|
mail_list_ga: ${{vars.MAIL_LIST_GA}}
|
||||||
|
mail_list_rc: ${{vars.MAIL_LIST_RC}}
|
||||||
|
mail_list_cert_ga: ${{vars.MAIL_LIST_CERT_GA}}
|
||||||
|
mail_list_cert_rc: ${{vars.MAIL_LIST_CERT_RC}}
|
||||||
|
mail_list_sec: ${{vars.MAIL_LIST_SEC_ADV}}
|
||||||
|
sec_adv_url_base: ${{vars.SEC_ADV_URL_BASE}}
|
||||||
|
gpg_private_key: ${{secrets.ASTDEV_GPG_PRIV_KEY}}
|
||||||
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
application_id: ${{secrets.ASTERISK_ORG_ACCESS_APP_ID}}
|
||||||
|
application_private_key: ${{secrets.ASTERISK_ORG_ACCESS_APP_PRIV_KEY}}
|
||||||
|
asteriskteamsa_username: ${{secrets.ASTERISKTEAMSA_GMAIL_ACCT}}
|
||||||
|
asteriskteamsa_token: ${{secrets.ASTERISKTEAMSA_GMAIL_TOKEN}}
|
||||||
|
deploy_ssh_priv_key: ${{secrets.DOWNLOADS_DEPLOY_SSH_PRIV_KEY}}
|
||||||
|
deploy_ssh_username: ${{secrets.DOWNLOADS_DEPLOY_SSH_USERNAME}}
|
||||||
|
deploy_host: ${{vars.DEPLOY_HOST}}
|
||||||
|
deploy_dir: ${{vars.DEPLOY_DIR}}
|
11
.gitreview
11
.gitreview
|
@ -1,11 +0,0 @@
|
||||||
[gerrit]
|
|
||||||
defaultbranch=master
|
|
||||||
basebranch=master
|
|
||||||
#
|
|
||||||
# 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
|
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:
|
the official Asterisk Bug Tracker at:
|
||||||
|
|
||||||
https://issues.asterisk.org/jira
|
https://github.com/asterisk/asterisk/issues/
|
||||||
|
|
||||||
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
|
learn how you can contribute by acting as a bug marshal
|
||||||
please see:
|
please see:
|
||||||
|
|
||||||
http://www.asterisk.org/developers/bug-guidelines
|
https://docs.asterisk.org/Asterisk-Community/Asterisk-Issue-Guidelines/
|
||||||
|
|
||||||
If you would like to submit a feature request, please
|
If you would like to submit a feature request, please
|
||||||
resist the temptation to post it to the bug tracker.
|
resist the temptation to post it to the bug tracker.
|
||||||
Feature requests should be posted to the asterisk-dev
|
Feature requests should be posted to the asterisk-dev
|
||||||
mailing list, located at:
|
mailing list, located at:
|
||||||
|
|
||||||
http://lists.digium.com
|
http://lists.digium.com
|
||||||
|
|
||||||
Thank you!
|
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
|
should contact our licensing department to determine the necessary
|
||||||
steps you must take. For more information on this policy, please read:
|
steps you must take. For more information on this policy, please read:
|
||||||
|
|
||||||
https://www.sangoma.com/wp-content/uploads/Sangoma-Trademark-Policy.pdf
|
https://www.sangoma.com/wp-content/uploads/Sangoma-Trademark-Policy-1.pdf
|
||||||
|
|
||||||
If you have any questions regarding our licensing policy, please
|
If you have any questions regarding our licensing policy, please
|
||||||
contact us:
|
contact us:
|
||||||
|
|
15
Makefile
15
Makefile
|
@ -377,7 +377,7 @@ $(MOD_SUBDIRS_MENUSELECT_TREE):
|
||||||
+@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) moduleinfo
|
+@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) moduleinfo
|
||||||
+@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) makeopts
|
+@$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) makeopts
|
||||||
|
|
||||||
$(SUBDIRS): makeopts .lastclean main/version.c include/asterisk/build.h include/asterisk/buildopts.h defaults.h
|
$(SUBDIRS): makeopts .lastclean main/version.c include/asterisk/build.h defaults.h
|
||||||
|
|
||||||
ifeq ($(findstring $(OSARCH), mingw32 cygwin ),)
|
ifeq ($(findstring $(OSARCH), mingw32 cygwin ),)
|
||||||
main: third-party
|
main: third-party
|
||||||
|
@ -403,7 +403,7 @@ defaults.h: makeopts .lastclean build_tools/make_defaults_h
|
||||||
@cmp -s $@.tmp $@ || mv $@.tmp $@
|
@cmp -s $@.tmp $@ || mv $@.tmp $@
|
||||||
@rm -f $@.tmp
|
@rm -f $@.tmp
|
||||||
|
|
||||||
main/version.c: FORCE menuselect.makeopts .lastclean
|
main/version.c: FORCE include/asterisk/buildopts.h menuselect.makeopts .lastclean
|
||||||
@build_tools/make_version_c > $@.tmp
|
@build_tools/make_version_c > $@.tmp
|
||||||
@cmp -s $@.tmp $@ || mv $@.tmp $@
|
@cmp -s $@.tmp $@ || mv $@.tmp $@
|
||||||
@rm -f $@.tmp
|
@rm -f $@.tmp
|
||||||
|
@ -545,7 +545,7 @@ INSTALLDIRS="$(ASTLIBDIR)" "$(ASTMODDIR)" "$(ASTSBINDIR)" "$(ASTCACHEDIR)" "$(AS
|
||||||
"$(ASTDATADIR)/firmware/iax" "$(ASTDATADIR)/images" "$(ASTDATADIR)/keys" \
|
"$(ASTDATADIR)/firmware/iax" "$(ASTDATADIR)/images" "$(ASTDATADIR)/keys" \
|
||||||
"$(ASTDATADIR)/phoneprov" "$(ASTDATADIR)/rest-api" "$(ASTDATADIR)/static-http" \
|
"$(ASTDATADIR)/phoneprov" "$(ASTDATADIR)/rest-api" "$(ASTDATADIR)/static-http" \
|
||||||
"$(ASTDATADIR)/sounds" "$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)" \
|
"$(ASTDATADIR)/sounds" "$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)" \
|
||||||
"$(ASTDATADIR)/third-party" "${ASTDATADIR}/keys/stir_shaken"
|
"$(ASTDATADIR)/third-party" "${ASTDATADIR}/keys/stir_shaken" "${ASTDATADIR}/keys/stir_shaken/cache"
|
||||||
|
|
||||||
installdirs:
|
installdirs:
|
||||||
@for i in $(INSTALLDIRS); do \
|
@for i in $(INSTALLDIRS); do \
|
||||||
|
@ -561,9 +561,9 @@ bininstall: _all installdirs $(SUBDIRS_INSTALL) main-bininstall
|
||||||
$(INSTALL) -m 755 contrib/scripts/astversion "$(DESTDIR)$(ASTSBINDIR)/"
|
$(INSTALL) -m 755 contrib/scripts/astversion "$(DESTDIR)$(ASTSBINDIR)/"
|
||||||
$(INSTALL) -m 755 contrib/scripts/astgenkey "$(DESTDIR)$(ASTSBINDIR)/"
|
$(INSTALL) -m 755 contrib/scripts/astgenkey "$(DESTDIR)$(ASTSBINDIR)/"
|
||||||
$(INSTALL) -m 755 contrib/scripts/autosupport "$(DESTDIR)$(ASTSBINDIR)/"
|
$(INSTALL) -m 755 contrib/scripts/autosupport "$(DESTDIR)$(ASTSBINDIR)/"
|
||||||
if [ ! -f /sbin/launchd ]; then \
|
ifneq ($(HAVE_SBIN_LAUNCHD),1)
|
||||||
./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk"; \
|
./build_tools/install_subst contrib/scripts/safe_asterisk "$(DESTDIR)$(ASTSBINDIR)/safe_asterisk";
|
||||||
fi
|
endif
|
||||||
|
|
||||||
ifneq ($(DISABLE_XMLDOC),yes)
|
ifneq ($(DISABLE_XMLDOC),yes)
|
||||||
$(INSTALL) -m 644 doc/core-*.xml "$(DESTDIR)$(ASTDATADIR)/documentation"
|
$(INSTALL) -m 644 doc/core-*.xml "$(DESTDIR)$(ASTDATADIR)/documentation"
|
||||||
|
@ -1119,7 +1119,8 @@ ifeq ($(PYTHON),:)
|
||||||
else
|
else
|
||||||
@$(INSTALL) -d doc/rest-api
|
@$(INSTALL) -d doc/rest-api
|
||||||
$(PYTHON) rest-api-templates/make_ari_stubs.py \
|
$(PYTHON) rest-api-templates/make_ari_stubs.py \
|
||||||
rest-api/resources.json .
|
--resources rest-api/resources.json --source-dir $(ASTTOPDIR) \
|
||||||
|
--dest-dir $(ASTTOPDIR)/doc/rest-api --docs-prefix ../
|
||||||
endif
|
endif
|
||||||
|
|
||||||
check-alembic: makeopts
|
check-alembic: makeopts
|
||||||
|
|
|
@ -213,10 +213,10 @@ endif
|
||||||
# extern const size_t _binary_abc_def_xml_size;
|
# extern const size_t _binary_abc_def_xml_size;
|
||||||
%.o: %.xml
|
%.o: %.xml
|
||||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
$(CMD_PREFIX) $(CC) -g -Wl,-znoexecstack -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||||
|
|
||||||
%.o: %.xslt
|
%.o: %.xslt
|
||||||
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
$(ECHO_PREFIX) echo " [LD] $^ -> $@"
|
||||||
$(CMD_PREFIX) $(CC) -g -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
$(CMD_PREFIX) $(CC) -g -Wl,-znoexecstack -nostartfiles -nodefaultlibs -nostdlib -r -Wl,-b,binary -o $@ $^
|
||||||
|
|
||||||
dist-clean:: clean
|
dist-clean:: clean
|
||||||
|
|
|
@ -377,9 +377,8 @@ is set to no.
|
||||||
|
|
||||||
In Asterisk 12 and later, live_dangerously defaults 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/
|
||||||
[voip-security-webinar]: https://www.asterisk.org/security/webinar/
|
[blog-sip-security]: https://web.archive.org/web/20171030134647/http://blogs.digium.com/2009/03/28/sip-security/
|
||||||
[blog-sip-security]: http://blogs.digium.com/2009/03/28/sip-security/
|
|
||||||
[Strong Password Generator]: https://www.strongpasswordgenerator.com
|
[Strong Password Generator]: https://www.strongpasswordgenerator.com
|
||||||
[Filtering Data]: #filtering-data
|
[Filtering Data]: #filtering-data
|
||||||
[Proper Device Naming]: #proper-device-naming
|
[Proper Device Naming]: #proper-device-naming
|
||||||
|
@ -387,4 +386,4 @@ In Asterisk 12 and later, live_dangerously defaults to no.
|
||||||
[Reducing Pattern Match Typos]: #reducing-pattern-match-typos
|
[Reducing Pattern Match Typos]: #reducing-pattern-match-typos
|
||||||
[Manager Class Authorizations]: #manager-class-authorizations
|
[Manager Class Authorizations]: #manager-class-authorizations
|
||||||
[Avoid Privilege Escalations]: #avoid-privilege-escalations
|
[Avoid Privilege Escalations]: #avoid-privilege-escalations
|
||||||
[Important Security Considerations]: https://wiki.asterisk.org/wiki/display/AST/Important+Security+Considerations
|
[Important Security Considerations]: https://docs.asterisk.org/Deployment/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.
|
vast amount of support for traditional PSTN telephony, as well.
|
||||||
|
|
||||||
For more information on the project itself, please visit the Asterisk
|
For more information on the project itself, please visit the Asterisk
|
||||||
[home page] and the official [wiki]. In addition you'll find lots
|
[home page] and the official [documentation]. In addition you'll find lots
|
||||||
of information compiled by the Asterisk community at [voip-info.org].
|
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
|
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:
|
Supported telephony hardware includes:
|
||||||
* All Analog and Digital Interface cards from [Sangoma]
|
* All Analog and Digital Interface cards from [Sangoma]
|
||||||
* QuickNet Internet PhoneJack and LineJack (http://www.quicknet.net)
|
* QuickNet Internet PhoneJack and LineJack
|
||||||
* any full duplex sound card supported by ALSA, OSS, or PortAudio
|
* any full duplex sound card supported by ALSA, OSS, or PortAudio
|
||||||
* any ISDN card supported by mISDN on Linux
|
* any ISDN card supported by mISDN on Linux
|
||||||
* The Xorcom Astribank channel bank
|
* The Xorcom Astribank channel bank
|
||||||
|
@ -258,7 +258,7 @@ Asterisk is a trademark of Sangoma Technologies Corporation
|
||||||
|
|
||||||
[home page]: https://www.asterisk.org
|
[home page]: https://www.asterisk.org
|
||||||
[support]: https://www.asterisk.org/support
|
[support]: https://www.asterisk.org/support
|
||||||
[wiki]: https://wiki.asterisk.org/
|
[documentation]: https://docs.asterisk.org/
|
||||||
[mailing list]: http://lists.digium.com/mailman/listinfo/asterisk-users
|
[mailing list]: http://lists.digium.com/mailman/listinfo/asterisk-users
|
||||||
[chan_dahdi.conf]: configs/samples/chan_dahdi.conf.sample
|
[chan_dahdi.conf]: configs/samples/chan_dahdi.conf.sample
|
||||||
[voip-info.org]: http://www.voip-info.org/wiki-Asterisk
|
[voip-info.org]: http://www.voip-info.org/wiki-Asterisk
|
||||||
|
@ -269,4 +269,4 @@ Asterisk is a trademark of Sangoma Technologies Corporation
|
||||||
[CHANGES]: CHANGES
|
[CHANGES]: CHANGES
|
||||||
[configs]: configs
|
[configs]: configs
|
||||||
[doc]: doc
|
[doc]: doc
|
||||||
[Important Security Considerations]: https://wiki.asterisk.org/wiki/display/AST/Important+Security+Considerations
|
[Important Security Considerations]: https://docs.asterisk.org/Deployment/Important-Security-Considerations/
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# 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.
|
2986
UPGRADE.txt
2986
UPGRADE.txt
File diff suppressed because it is too large
Load Diff
|
@ -192,11 +192,13 @@ EXTERN int ooQ931Decode
|
||||||
screening indicators ;-) */
|
screening indicators ;-) */
|
||||||
if(ie->discriminator == Q931CallingPartyNumberIE)
|
if(ie->discriminator == Q931CallingPartyNumberIE)
|
||||||
{
|
{
|
||||||
|
int numoffset=1;
|
||||||
OOTRACEDBGB1(" CallingPartyNumber IE = {\n");
|
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);
|
memcpy(number, ie->data+numoffset,ie->length-numoffset);
|
||||||
number[ie->length-numoffset]='\0';
|
number[ie->length-numoffset]='\0';
|
||||||
OOTRACEDBGB2(" %s\n", number);
|
OOTRACEDBGB2(" %s\n", number);
|
||||||
|
@ -204,7 +206,7 @@ EXTERN int ooQ931Decode
|
||||||
ooCallSetCallingPartyNumber(call, number);
|
ooCallSetCallingPartyNumber(call, number);
|
||||||
}
|
}
|
||||||
else{
|
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);
|
call->callType, call->callToken);
|
||||||
}
|
}
|
||||||
OOTRACEDBGB1(" }\n");
|
OOTRACEDBGB1(" }\n");
|
||||||
|
@ -214,7 +216,8 @@ EXTERN int ooQ931Decode
|
||||||
if(ie->discriminator == Q931CalledPartyNumberIE)
|
if(ie->discriminator == Q931CalledPartyNumberIE)
|
||||||
{
|
{
|
||||||
OOTRACEDBGB1(" CalledPartyNumber IE = {\n");
|
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);
|
memcpy(number, ie->data+1,ie->length-1);
|
||||||
number[ie->length-1]='\0';
|
number[ie->length-1]='\0';
|
||||||
|
@ -223,7 +226,7 @@ EXTERN int ooQ931Decode
|
||||||
ooCallSetCalledPartyNumber(call, number);
|
ooCallSetCalledPartyNumber(call, number);
|
||||||
}
|
}
|
||||||
else{
|
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);
|
call->callType, call->callToken);
|
||||||
}
|
}
|
||||||
OOTRACEDBGB1(" }\n");
|
OOTRACEDBGB1(" }\n");
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
<depend>res_adsi</depend>
|
<depend>res_adsi</depend>
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>deprecated</support_level>
|
<support_level>deprecated</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
@ -92,6 +93,12 @@
|
||||||
<para>Is the maximum duration of a word to accept.</para>
|
<para>Is the maximum duration of a word to accept.</para>
|
||||||
<para>If exceeded, then the result is detection as a MACHINE</para>
|
<para>If exceeded, then the result is detection as a MACHINE</para>
|
||||||
</parameter>
|
</parameter>
|
||||||
|
<parameter name="audioFile" required="false">
|
||||||
|
<para>Is an audio file to play to the caller while AMD is in progress.</para>
|
||||||
|
<para>By default, no audio file is played.</para>
|
||||||
|
<para>If an audio file is configured in amd.conf, then that file will be used
|
||||||
|
if one is not specified here. That file may be overridden by this argument.</para>
|
||||||
|
</parameter>
|
||||||
</syntax>
|
</syntax>
|
||||||
<description>
|
<description>
|
||||||
<para>This application attempts to detect answering machines at the beginning
|
<para>This application attempts to detect answering machines at the beginning
|
||||||
|
@ -155,6 +162,9 @@ static int dfltBetweenWordsSilence = 50;
|
||||||
static int dfltMaximumNumberOfWords = 2;
|
static int dfltMaximumNumberOfWords = 2;
|
||||||
static int dfltSilenceThreshold = 256;
|
static int dfltSilenceThreshold = 256;
|
||||||
static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
|
static int dfltMaximumWordLength = 5000; /* Setting this to a large default so it is not used unless specify it in the configs or command line */
|
||||||
|
static char *dfltAudioFile = NULL;
|
||||||
|
|
||||||
|
static ast_mutex_t config_lock;
|
||||||
|
|
||||||
/* Set to the lowest ms value provided in amd.conf or application parameters */
|
/* Set to the lowest ms value provided in amd.conf or application parameters */
|
||||||
static int dfltMaxWaitTimeForFrame = 50;
|
static int dfltMaxWaitTimeForFrame = 50;
|
||||||
|
@ -179,7 +189,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
||||||
char amdCause[256] = "", amdStatus[256] = "";
|
char amdCause[256] = "", amdStatus[256] = "";
|
||||||
char *parse = ast_strdupa(data);
|
char *parse = ast_strdupa(data);
|
||||||
|
|
||||||
/* Lets set the initial values of the variables that will control the algorithm.
|
/* Let's set the initial values of the variables that will control the algorithm.
|
||||||
The initial values are the default ones. If they are passed as arguments
|
The initial values are the default ones. If they are passed as arguments
|
||||||
when invoking the application, then the default values will be overwritten
|
when invoking the application, then the default values will be overwritten
|
||||||
by the ones passed as parameters. */
|
by the ones passed as parameters. */
|
||||||
|
@ -193,6 +203,7 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
||||||
int silenceThreshold = dfltSilenceThreshold;
|
int silenceThreshold = dfltSilenceThreshold;
|
||||||
int maximumWordLength = dfltMaximumWordLength;
|
int maximumWordLength = dfltMaximumWordLength;
|
||||||
int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
|
int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
|
||||||
|
const char *audioFile = NULL;
|
||||||
|
|
||||||
AST_DECLARE_APP_ARGS(args,
|
AST_DECLARE_APP_ARGS(args,
|
||||||
AST_APP_ARG(argInitialSilence);
|
AST_APP_ARG(argInitialSilence);
|
||||||
|
@ -204,8 +215,15 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
||||||
AST_APP_ARG(argMaximumNumberOfWords);
|
AST_APP_ARG(argMaximumNumberOfWords);
|
||||||
AST_APP_ARG(argSilenceThreshold);
|
AST_APP_ARG(argSilenceThreshold);
|
||||||
AST_APP_ARG(argMaximumWordLength);
|
AST_APP_ARG(argMaximumWordLength);
|
||||||
|
AST_APP_ARG(audioFile);
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ast_mutex_lock(&config_lock);
|
||||||
|
if (!ast_strlen_zero(dfltAudioFile)) {
|
||||||
|
audioFile = ast_strdupa(dfltAudioFile);
|
||||||
|
}
|
||||||
|
ast_mutex_unlock(&config_lock);
|
||||||
|
|
||||||
ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
|
ast_verb(3, "AMD: %s %s %s (Fmt: %s)\n", ast_channel_name(chan),
|
||||||
S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
|
S_COR(ast_channel_caller(chan)->ani.number.valid, ast_channel_caller(chan)->ani.number.str, "(N/A)"),
|
||||||
S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
|
S_COR(ast_channel_redirecting(chan)->from.number.valid, ast_channel_redirecting(chan)->from.number.str, "(N/A)"),
|
||||||
|
@ -233,6 +251,9 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
||||||
silenceThreshold = atoi(args.argSilenceThreshold);
|
silenceThreshold = atoi(args.argSilenceThreshold);
|
||||||
if (!ast_strlen_zero(args.argMaximumWordLength))
|
if (!ast_strlen_zero(args.argMaximumWordLength))
|
||||||
maximumWordLength = atoi(args.argMaximumWordLength);
|
maximumWordLength = atoi(args.argMaximumWordLength);
|
||||||
|
if (!ast_strlen_zero(args.audioFile)) {
|
||||||
|
audioFile = args.audioFile;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ast_debug(1, "AMD using the default parameters.\n");
|
ast_debug(1, "AMD using the default parameters.\n");
|
||||||
}
|
}
|
||||||
|
@ -280,6 +301,11 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
||||||
/* Set our start time so we can tie the loop to real world time and not RTP updates */
|
/* Set our start time so we can tie the loop to real world time and not RTP updates */
|
||||||
amd_tvstart = ast_tvnow();
|
amd_tvstart = ast_tvnow();
|
||||||
|
|
||||||
|
/* Optional audio file to play to caller while AMD is doing its thing. */
|
||||||
|
if (!ast_strlen_zero(audioFile)) {
|
||||||
|
ast_streamfile(chan, audioFile, ast_channel_language(chan));
|
||||||
|
}
|
||||||
|
|
||||||
/* Now we go into a loop waiting for frames from the channel */
|
/* Now we go into a loop waiting for frames from the channel */
|
||||||
while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
|
while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
|
||||||
int ms = 0;
|
int ms = 0;
|
||||||
|
@ -462,10 +488,14 @@ static void isAnsweringMachine(struct ast_channel *chan, const char *data)
|
||||||
/* Free the DSP used to detect silence */
|
/* Free the DSP used to detect silence */
|
||||||
ast_dsp_free(silenceDetector);
|
ast_dsp_free(silenceDetector);
|
||||||
|
|
||||||
|
/* If we were playing something to pass the time, stop it now. */
|
||||||
|
if (!ast_strlen_zero(audioFile)) {
|
||||||
|
ast_stopstream(chan);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int amd_exec(struct ast_channel *chan, const char *data)
|
static int amd_exec(struct ast_channel *chan, const char *data)
|
||||||
{
|
{
|
||||||
isAnsweringMachine(chan, data);
|
isAnsweringMachine(chan, data);
|
||||||
|
@ -516,7 +546,16 @@ static int load_config(int reload)
|
||||||
dfltMaximumNumberOfWords = atoi(var->value);
|
dfltMaximumNumberOfWords = atoi(var->value);
|
||||||
} else if (!strcasecmp(var->name, "maximum_word_length")) {
|
} else if (!strcasecmp(var->name, "maximum_word_length")) {
|
||||||
dfltMaximumWordLength = atoi(var->value);
|
dfltMaximumWordLength = atoi(var->value);
|
||||||
|
} else if (!strcasecmp(var->name, "playback_file")) {
|
||||||
|
ast_mutex_lock(&config_lock);
|
||||||
|
if (dfltAudioFile) {
|
||||||
|
ast_free(dfltAudioFile);
|
||||||
|
dfltAudioFile = NULL;
|
||||||
|
}
|
||||||
|
if (!ast_strlen_zero(var->value)) {
|
||||||
|
dfltAudioFile = ast_strdup(var->value);
|
||||||
|
}
|
||||||
|
ast_mutex_unlock(&config_lock);
|
||||||
} else {
|
} else {
|
||||||
ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
|
ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
|
||||||
app, cat, var->name, var->lineno);
|
app, cat, var->name, var->lineno);
|
||||||
|
@ -529,7 +568,7 @@ static int load_config(int reload)
|
||||||
|
|
||||||
ast_config_destroy(cfg);
|
ast_config_destroy(cfg);
|
||||||
|
|
||||||
ast_verb(3, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
|
ast_verb(5, "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
|
||||||
"totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
|
"totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] maximumWordLength [%d]\n",
|
||||||
dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
|
dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
|
||||||
dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
|
dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold, dfltMaximumWordLength);
|
||||||
|
@ -539,6 +578,12 @@ static int load_config(int reload)
|
||||||
|
|
||||||
static int unload_module(void)
|
static int unload_module(void)
|
||||||
{
|
{
|
||||||
|
ast_mutex_lock(&config_lock);
|
||||||
|
if (dfltAudioFile) {
|
||||||
|
ast_free(dfltAudioFile);
|
||||||
|
}
|
||||||
|
ast_mutex_unlock(&config_lock);
|
||||||
|
ast_mutex_destroy(&config_lock);
|
||||||
return ast_unregister_application(app);
|
return ast_unregister_application(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,6 +599,7 @@ static int unload_module(void)
|
||||||
*/
|
*/
|
||||||
static int load_module(void)
|
static int load_module(void)
|
||||||
{
|
{
|
||||||
|
ast_mutex_init(&config_lock);
|
||||||
if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
|
if (load_config(0) || ast_register_application_xml(app, amd_exec)) {
|
||||||
return AST_MODULE_LOAD_DECLINE;
|
return AST_MODULE_LOAD_DECLINE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
<depend>res_audiosocket</depend>
|
<depend>res_audiosocket</depend>
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@
|
||||||
</syntax>
|
</syntax>
|
||||||
<description>
|
<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>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://wiki.asterisk.org/wiki/display/AST/AudioSocket</para>
|
<para>Protocol is specified at https://docs.asterisk.org/Configuration/Channel-Drivers/AudioSocket/</para>
|
||||||
<para>This application does not automatically answer and should generally be preceeded by an application such as Answer() or Progress().</para>
|
<para>This application does not automatically answer and should generally be preceeded by an application such as Answer() or Progress().</para>
|
||||||
</description>
|
</description>
|
||||||
</application>
|
</application>
|
||||||
|
@ -180,7 +181,7 @@ static int audiosocket_run(struct ast_channel *chan, const char *id, int svc)
|
||||||
chanName = ast_channel_name(chan);
|
chanName = ast_channel_name(chan);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
ms = -1;
|
||||||
targetChan = ast_waitfor_nandfds(&chan, 1, &svc, 1, NULL, &outfd, &ms);
|
targetChan = ast_waitfor_nandfds(&chan, 1, &svc, 1, NULL, &outfd, &ms);
|
||||||
if (targetChan) {
|
if (targetChan) {
|
||||||
f = ast_read(chan);
|
f = ast_read(chan);
|
||||||
|
|
|
@ -95,8 +95,17 @@ static const char app[] = "Authenticate";
|
||||||
maxdigits have been entered (without requiring the user to press the <literal>#</literal> key).
|
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>
|
Defaults to 0 - no limit - wait for the user press the <literal>#</literal> key.</para>
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="prompt" required="false">
|
<parameter name="prompt" required="false" argsep="&">
|
||||||
<para>Override the agent-pass prompt file.</para>
|
<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>
|
</parameter>
|
||||||
</syntax>
|
</syntax>
|
||||||
<description>
|
<description>
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,9 @@
|
||||||
<para>Automatically exit the bridge and return to the PBX after
|
<para>Automatically exit the bridge and return to the PBX after
|
||||||
<emphasis>duration</emphasis> seconds.</para>
|
<emphasis>duration</emphasis> seconds.</para>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="n">
|
||||||
|
<para>Do not automatically answer the channel.</para>
|
||||||
|
</option>
|
||||||
</optionlist>
|
</optionlist>
|
||||||
</parameter>
|
</parameter>
|
||||||
</syntax>
|
</syntax>
|
||||||
|
@ -108,7 +111,7 @@
|
||||||
The channel will then wait in the holding bridge until some event occurs
|
The channel will then wait in the holding bridge until some event occurs
|
||||||
which removes it from the holding bridge.</para>
|
which removes it from the holding bridge.</para>
|
||||||
<note><para>This application will answer calls which haven't already
|
<note><para>This application will answer calls which haven't already
|
||||||
been answered.</para></note>
|
been answered, unless the n option is provided.</para></note>
|
||||||
</description>
|
</description>
|
||||||
</application>
|
</application>
|
||||||
***/
|
***/
|
||||||
|
@ -186,6 +189,7 @@ enum bridgewait_flags {
|
||||||
MUXFLAG_MOHCLASS = (1 << 0),
|
MUXFLAG_MOHCLASS = (1 << 0),
|
||||||
MUXFLAG_ENTERTAINMENT = (1 << 1),
|
MUXFLAG_ENTERTAINMENT = (1 << 1),
|
||||||
MUXFLAG_TIMEOUT = (1 << 2),
|
MUXFLAG_TIMEOUT = (1 << 2),
|
||||||
|
MUXFLAG_NOANSWER = (1 << 3),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum bridgewait_args {
|
enum bridgewait_args {
|
||||||
|
@ -199,6 +203,7 @@ AST_APP_OPTIONS(bridgewait_opts, {
|
||||||
AST_APP_OPTION_ARG('e', MUXFLAG_ENTERTAINMENT, OPT_ARG_ENTERTAINMENT),
|
AST_APP_OPTION_ARG('e', MUXFLAG_ENTERTAINMENT, OPT_ARG_ENTERTAINMENT),
|
||||||
AST_APP_OPTION_ARG('m', MUXFLAG_MOHCLASS, OPT_ARG_MOHCLASS),
|
AST_APP_OPTION_ARG('m', MUXFLAG_MOHCLASS, OPT_ARG_MOHCLASS),
|
||||||
AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
|
AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
|
||||||
|
AST_APP_OPTION('n', MUXFLAG_NOANSWER),
|
||||||
});
|
});
|
||||||
|
|
||||||
static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
|
||||||
|
@ -458,7 +463,7 @@ static int bridgewait_exec(struct ast_channel *chan, const char *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Answer the channel if needed */
|
/* Answer the channel if needed */
|
||||||
if (ast_channel_state(chan) != AST_STATE_UP) {
|
if (ast_channel_state(chan) != AST_STATE_UP && !ast_test_flag(&flags, MUXFLAG_NOANSWER)) {
|
||||||
ast_answer(chan);
|
ast_answer(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,620 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
|
<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");
|
|
@ -28,6 +28,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
@ -117,6 +118,7 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
||||||
struct ast_str *tmp_availcause = ast_str_alloca(2048);
|
struct ast_str *tmp_availcause = ast_str_alloca(2048);
|
||||||
struct ast_channel *tempchan;
|
struct ast_channel *tempchan;
|
||||||
struct ast_custom_function *cdr_prop_func = ast_custom_function_find("CDR_PROP");
|
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_DECLARE_APP_ARGS(args,
|
||||||
AST_APP_ARG(reqchans);
|
AST_APP_ARG(reqchans);
|
||||||
AST_APP_ARG(options);
|
AST_APP_ARG(options);
|
||||||
|
@ -126,6 +128,10 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
||||||
|
|
||||||
AST_STANDARD_APP_ARGS(args, info);
|
AST_STANDARD_APP_ARGS(args, info);
|
||||||
|
|
||||||
|
ao2_lock(chan);
|
||||||
|
caps = ao2_bump(ast_channel_nativeformats(chan));
|
||||||
|
ao2_unlock(chan);
|
||||||
|
|
||||||
if (args.options) {
|
if (args.options) {
|
||||||
if (strchr(args.options, 'a')) {
|
if (strchr(args.options, 'a')) {
|
||||||
option_all_avail = 1;
|
option_all_avail = 1;
|
||||||
|
@ -174,10 +180,11 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
||||||
snprintf(trychan, sizeof(trychan), "%s/%s", tech, number);
|
snprintf(trychan, sizeof(trychan), "%s/%s", tech, number);
|
||||||
status = inuse = ast_device_state(trychan);
|
status = inuse = ast_device_state(trychan);
|
||||||
}
|
}
|
||||||
ast_str_append(&tmp_availstat, 0, "%s%d",
|
ast_str_append(&tmp_availstat, 0, "%s%d", ast_str_strlen(tmp_availstat) ? "&" : "", status);
|
||||||
ast_str_strlen(tmp_availstat) ? "&" : "", status);
|
|
||||||
if ((inuse <= (int) AST_DEVICE_NOT_INUSE)
|
if ((inuse <= (int) AST_DEVICE_NOT_INUSE)
|
||||||
&& (tempchan = ast_request(tech, ast_channel_nativeformats(chan), NULL, chan, number, &status))) {
|
&& (tempchan = ast_request(tech, caps, NULL, chan, number, &status))) {
|
||||||
|
|
||||||
ast_str_append(&tmp_availchan, 0, "%s%s",
|
ast_str_append(&tmp_availchan, 0, "%s%s",
|
||||||
ast_str_strlen(tmp_availchan) ? "&" : "", ast_channel_name(tempchan));
|
ast_str_strlen(tmp_availchan) ? "&" : "", ast_channel_name(tempchan));
|
||||||
|
|
||||||
|
@ -199,8 +206,11 @@ static int chanavail_exec(struct ast_channel *chan, const char *data)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ao2_cleanup(caps);
|
||||||
|
|
||||||
pbx_builtin_setvar_helper(chan, "AVAILCHAN", ast_str_buffer(tmp_availchan));
|
pbx_builtin_setvar_helper(chan, "AVAILCHAN", ast_str_buffer(tmp_availchan));
|
||||||
/* Store the originally used channel too */
|
/* Store the originally used channel too */
|
||||||
pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", ast_str_buffer(tmp_availorig));
|
pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", ast_str_buffer(tmp_availorig));
|
||||||
|
|
|
@ -245,6 +245,11 @@
|
||||||
</enum>
|
</enum>
|
||||||
</enumlist>
|
</enumlist>
|
||||||
</option>
|
</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">
|
<option name="e">
|
||||||
<argument name="ext" required="true" />
|
<argument name="ext" required="true" />
|
||||||
<para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
|
<para>Enable <emphasis>enforced</emphasis> mode, so the spying channel can
|
||||||
|
@ -393,6 +398,7 @@ enum {
|
||||||
OPTION_EXITONHANGUP = (1 << 18), /* Hang up when the spied-on channel hangs up. */
|
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_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_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 {
|
enum {
|
||||||
|
@ -411,6 +417,7 @@ AST_APP_OPTIONS(spy_opts, {
|
||||||
AST_APP_OPTION('B', OPTION_BARGE),
|
AST_APP_OPTION('B', OPTION_BARGE),
|
||||||
AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
|
AST_APP_OPTION_ARG('c', OPTION_DTMF_CYCLE, OPT_ARG_CYCLE),
|
||||||
AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
|
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_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
|
||||||
AST_APP_OPTION('E', OPTION_EXITONHANGUP),
|
AST_APP_OPTION('E', OPTION_EXITONHANGUP),
|
||||||
AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
|
AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
|
||||||
|
@ -471,6 +478,56 @@ static int spy_generate(struct ast_channel *chan, void *data, int len, int sampl
|
||||||
if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
|
if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
|
||||||
/* Option 'o' was set, so don't mix channel audio */
|
/* 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);
|
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 {
|
} else {
|
||||||
f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
|
f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, ast_format_slin);
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,6 +393,37 @@
|
||||||
ConfbridgeListRoomsComplete.</para>
|
ConfbridgeListRoomsComplete.</para>
|
||||||
</description>
|
</description>
|
||||||
</manager>
|
</manager>
|
||||||
|
<managerEvent language="en_US" name="ConfbridgeListRooms">
|
||||||
|
<managerEventInstance class="EVENT_FLAG_REPORTING">
|
||||||
|
<synopsis>Raised as part of the ConfbridgeListRooms action response list.</synopsis>
|
||||||
|
<syntax>
|
||||||
|
<parameter name="Conference">
|
||||||
|
<para>The name of the Confbridge conference.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Parties">
|
||||||
|
<para>Number of users in the conference.</para>
|
||||||
|
<para>This includes both active and waiting users.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Marked">
|
||||||
|
<para>Number of marked users in the conference.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Locked">
|
||||||
|
<para>Is the conference locked?</para>
|
||||||
|
<enumlist>
|
||||||
|
<enum name="Yes"/>
|
||||||
|
<enum name="No"/>
|
||||||
|
</enumlist>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Muted">
|
||||||
|
<para>Is the conference muted?</para>
|
||||||
|
<enumlist>
|
||||||
|
<enum name="Yes"/>
|
||||||
|
<enum name="No"/>
|
||||||
|
</enumlist>
|
||||||
|
</parameter>
|
||||||
|
</syntax>
|
||||||
|
</managerEventInstance>
|
||||||
|
</managerEvent>
|
||||||
<manager name="ConfbridgeMute" language="en_US">
|
<manager name="ConfbridgeMute" language="en_US">
|
||||||
<synopsis>
|
<synopsis>
|
||||||
Mute a Confbridge user.
|
Mute a Confbridge user.
|
||||||
|
@ -3004,7 +3035,7 @@ static int action_playback(struct ast_bridge_channel *bridge_channel, const char
|
||||||
char *file_copy = ast_strdupa(playback_file);
|
char *file_copy = ast_strdupa(playback_file);
|
||||||
char *file = NULL;
|
char *file = NULL;
|
||||||
|
|
||||||
while ((file = strsep(&file_copy, "&"))) {
|
while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||||
if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
|
if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
|
||||||
ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
|
ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -3028,7 +3059,7 @@ static int action_playback_and_continue(struct confbridge_conference *conference
|
||||||
char *file_copy = ast_strdupa(playback_file);
|
char *file_copy = ast_strdupa(playback_file);
|
||||||
char *file = NULL;
|
char *file = NULL;
|
||||||
|
|
||||||
while ((file = strsep(&file_copy, "&"))) {
|
while ((file = ast_strsep(&file_copy, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||||
if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
|
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);
|
ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -3991,6 +4022,7 @@ static int action_confbridgelist_item(struct mansession *s, const char *id_text,
|
||||||
"MarkedUser: %s\r\n"
|
"MarkedUser: %s\r\n"
|
||||||
"WaitMarked: %s\r\n"
|
"WaitMarked: %s\r\n"
|
||||||
"EndMarked: %s\r\n"
|
"EndMarked: %s\r\n"
|
||||||
|
"EndMarkedAny: %s\r\n"
|
||||||
"Waiting: %s\r\n"
|
"Waiting: %s\r\n"
|
||||||
"Muted: %s\r\n"
|
"Muted: %s\r\n"
|
||||||
"Talking: %s\r\n"
|
"Talking: %s\r\n"
|
||||||
|
@ -4003,6 +4035,7 @@ static int action_confbridgelist_item(struct mansession *s, const char *id_text,
|
||||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)),
|
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)),
|
||||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)),
|
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)),
|
||||||
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)),
|
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)),
|
||||||
|
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKEDANY)),
|
||||||
AST_YESNO(waiting),
|
AST_YESNO(waiting),
|
||||||
AST_YESNO(user->muted),
|
AST_YESNO(user->muted),
|
||||||
AST_YESNO(user->talking),
|
AST_YESNO(user->talking),
|
||||||
|
|
134
apps/app_dial.c
134
apps/app_dial.c
|
@ -88,9 +88,12 @@
|
||||||
</argument>
|
</argument>
|
||||||
<xi:include xpointer="xpointer(/docs/info[@name='Dial_Resource'])" />
|
<xi:include xpointer="xpointer(/docs/info[@name='Dial_Resource'])" />
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="timeout" required="false">
|
<parameter name="timeout" required="false" argsep="^">
|
||||||
<para>Specifies the number of seconds we attempt to dial the specified devices.</para>
|
<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 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>
|
||||||
<parameter name="options" required="false">
|
<parameter name="options" required="false">
|
||||||
<optionlist>
|
<optionlist>
|
||||||
|
@ -242,6 +245,10 @@
|
||||||
<para>Asterisk will ignore any connected line update requests or any redirecting party
|
<para>Asterisk will ignore any connected line update requests or any redirecting party
|
||||||
update requests it may receive on this dial attempt.</para>
|
update requests it may receive on this dial attempt.</para>
|
||||||
</option>
|
</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">
|
<option name="k">
|
||||||
<para>Allow the called party to enable parking of the call by sending
|
<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>
|
the DTMF sequence defined for call parking in <filename>features.conf</filename>.</para>
|
||||||
|
@ -750,6 +757,7 @@ enum {
|
||||||
#define OPT_RING_WITH_EARLY_MEDIA (1LLU << 43)
|
#define OPT_RING_WITH_EARLY_MEDIA (1LLU << 43)
|
||||||
#define OPT_HANGUPCAUSE (1LLU << 44)
|
#define OPT_HANGUPCAUSE (1LLU << 44)
|
||||||
#define OPT_HEARPULSING (1LLU << 45)
|
#define OPT_HEARPULSING (1LLU << 45)
|
||||||
|
#define OPT_TOPOLOGY_PRESERVE (1LLU << 46)
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
OPT_ARG_ANNOUNCE = 0,
|
OPT_ARG_ANNOUNCE = 0,
|
||||||
|
@ -795,6 +803,7 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
|
||||||
AST_APP_OPTION('H', OPT_CALLER_HANGUP),
|
AST_APP_OPTION('H', OPT_CALLER_HANGUP),
|
||||||
AST_APP_OPTION('i', OPT_IGNORE_FORWARDING),
|
AST_APP_OPTION('i', OPT_IGNORE_FORWARDING),
|
||||||
AST_APP_OPTION('I', OPT_IGNORE_CONNECTEDLINE),
|
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_CALLEE_PARK),
|
||||||
AST_APP_OPTION('K', OPT_CALLER_PARK),
|
AST_APP_OPTION('K', OPT_CALLER_PARK),
|
||||||
AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
|
AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
|
||||||
|
@ -855,6 +864,16 @@ struct chanlist {
|
||||||
|
|
||||||
AST_LIST_HEAD_NOLOCK(dial_head, 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 int detect_disconnect(struct ast_channel *chan, char code, struct ast_str **featurecode);
|
||||||
|
|
||||||
static void chanlist_free(struct chanlist *outgoing)
|
static void chanlist_free(struct chanlist *outgoing)
|
||||||
|
@ -1238,7 +1257,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,
|
static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
struct dial_head *out_chans, int *to, struct ast_flags64 *peerflags,
|
struct dial_head *out_chans, int *to_answer, int *to_progress, struct ast_flags64 *peerflags,
|
||||||
char *opt_args[],
|
char *opt_args[],
|
||||||
struct privacy_args *pa,
|
struct privacy_args *pa,
|
||||||
const struct cause_args *num_in, int *result, char *dtmf_progress,
|
const struct cause_args *num_in, int *result, char *dtmf_progress,
|
||||||
|
@ -1251,7 +1270,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
{
|
{
|
||||||
struct cause_args num = *num_in;
|
struct cause_args num = *num_in;
|
||||||
int prestart = num.busy + num.congestion + num.nochan;
|
int prestart = num.busy + num.congestion + num.nochan;
|
||||||
int orig = *to;
|
int orig_answer_to = *to_answer;
|
||||||
|
int progress_to_dup = *to_progress;
|
||||||
|
int orig_progress_to = *to_progress;
|
||||||
struct ast_channel *peer = NULL;
|
struct ast_channel *peer = NULL;
|
||||||
struct chanlist *outgoing = AST_LIST_FIRST(out_chans);
|
struct chanlist *outgoing = AST_LIST_FIRST(out_chans);
|
||||||
/* single is set if only one destination is enabled */
|
/* single is set if only one destination is enabled */
|
||||||
|
@ -1279,7 +1300,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
* there is no point in continuing. The bridge
|
* there is no point in continuing. The bridge
|
||||||
* will just fail if it gets that far.
|
* will just fail if it gets that far.
|
||||||
*/
|
*/
|
||||||
*to = -1;
|
*to_answer = -1;
|
||||||
strcpy(pa->status, "CONGESTION");
|
strcpy(pa->status, "CONGESTION");
|
||||||
ast_channel_publish_dial(in, outgoing->chan, NULL, pa->status);
|
ast_channel_publish_dial(in, outgoing->chan, NULL, pa->status);
|
||||||
SCOPE_EXIT_RTN_VALUE(NULL, "%s: can't be made compat with %s\n",
|
SCOPE_EXIT_RTN_VALUE(NULL, "%s: can't be made compat with %s\n",
|
||||||
|
@ -1295,7 +1316,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);
|
is_cc_recall = ast_cc_is_recall(in, &cc_recall_core_id, NULL);
|
||||||
|
|
||||||
while ((*to = ast_remaining_ms(start, orig)) && !peer) {
|
while ((*to_answer = ast_remaining_ms(start, orig_answer_to)) && (*to_progress = ast_remaining_ms(start, progress_to_dup)) && !peer) {
|
||||||
struct chanlist *o;
|
struct chanlist *o;
|
||||||
int pos = 0; /* how many channels do we handle */
|
int pos = 0; /* how many channels do we handle */
|
||||||
int numlines = prestart;
|
int numlines = prestart;
|
||||||
|
@ -1321,14 +1342,15 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
} else {
|
} 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);
|
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 = 0;
|
*to_answer = 0;
|
||||||
if (is_cc_recall) {
|
if (is_cc_recall) {
|
||||||
ast_cc_failed(cc_recall_core_id, "Everyone is busy/congested for the recall. How sad");
|
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));
|
SCOPE_EXIT_RTN_VALUE(NULL, "%s: No outgoing channels available\n", ast_channel_name(in));
|
||||||
}
|
}
|
||||||
winner = ast_waitfor_n(watchers, pos, to);
|
winner = ast_waitfor_n(watchers, pos, to_answer);
|
||||||
AST_LIST_TRAVERSE(out_chans, o, node) {
|
AST_LIST_TRAVERSE(out_chans, o, node) {
|
||||||
|
int res = 0;
|
||||||
struct ast_frame *f;
|
struct ast_frame *f;
|
||||||
struct ast_channel *c = o->chan;
|
struct ast_channel *c = o->chan;
|
||||||
|
|
||||||
|
@ -1404,7 +1426,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
ast_channel_unlock(in);
|
ast_channel_unlock(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
do_forward(o, &num, peerflags, single, caller_entertained, &orig,
|
do_forward(o, &num, peerflags, single, caller_entertained, &orig_answer_to,
|
||||||
forced_clid, stored_clid);
|
forced_clid, stored_clid);
|
||||||
|
|
||||||
if (o->chan) {
|
if (o->chan) {
|
||||||
|
@ -1542,6 +1564,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
* fine for ringing frames to get sent through.
|
* fine for ringing frames to get sent through.
|
||||||
*/
|
*/
|
||||||
++num_ringing;
|
++num_ringing;
|
||||||
|
*to_progress = -1;
|
||||||
|
progress_to_dup = -1;
|
||||||
if (ignore_cc || cc_frame_received || num_ringing == numlines) {
|
if (ignore_cc || cc_frame_received || num_ringing == numlines) {
|
||||||
ast_verb(3, "%s is ringing\n", ast_channel_name(c));
|
ast_verb(3, "%s is ringing\n", ast_channel_name(c));
|
||||||
/* Setup early media if appropriate */
|
/* Setup early media if appropriate */
|
||||||
|
@ -1585,6 +1609,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
ast_indicate(in, AST_CONTROL_PROGRESS);
|
ast_indicate(in, AST_CONTROL_PROGRESS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*to_progress = -1;
|
||||||
|
progress_to_dup = -1;
|
||||||
if (!sent_progress) {
|
if (!sent_progress) {
|
||||||
struct timeval now, then;
|
struct timeval now, then;
|
||||||
int64_t diff;
|
int64_t diff;
|
||||||
|
@ -1607,7 +1633,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
"Sending MF '%s' to %s as result of "
|
"Sending MF '%s' to %s as result of "
|
||||||
"receiving a PROGRESS message.\n",
|
"receiving a PROGRESS message.\n",
|
||||||
mf_progress, hearpulsing ? "parties" : "called party");
|
mf_progress, hearpulsing ? "parties" : "called party");
|
||||||
ast_mf_stream(c, (hearpulsing ? NULL : in),
|
res |= ast_mf_stream(c, (hearpulsing ? NULL : in),
|
||||||
(hearpulsing ? in : NULL), mf_progress, 50, 55, 120, 65, 0);
|
(hearpulsing ? in : NULL), mf_progress, 50, 55, 120, 65, 0);
|
||||||
}
|
}
|
||||||
if (!ast_strlen_zero(sf_progress)) {
|
if (!ast_strlen_zero(sf_progress)) {
|
||||||
|
@ -1615,7 +1641,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
"Sending SF '%s' to %s as result of "
|
"Sending SF '%s' to %s as result of "
|
||||||
"receiving a PROGRESS message.\n",
|
"receiving a PROGRESS message.\n",
|
||||||
sf_progress, (hearpulsing ? "parties" : "called party"));
|
sf_progress, (hearpulsing ? "parties" : "called party"));
|
||||||
ast_sf_stream(c, (hearpulsing ? NULL : in),
|
res |= ast_sf_stream(c, (hearpulsing ? NULL : in),
|
||||||
(hearpulsing ? in : NULL), sf_progress, 0, 0);
|
(hearpulsing ? in : NULL), sf_progress, 0, 0);
|
||||||
}
|
}
|
||||||
if (!ast_strlen_zero(dtmf_progress)) {
|
if (!ast_strlen_zero(dtmf_progress)) {
|
||||||
|
@ -1623,7 +1649,11 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
"Sending DTMF '%s' to the called party as result of "
|
"Sending DTMF '%s' to the called party as result of "
|
||||||
"receiving a PROGRESS message.\n",
|
"receiving a PROGRESS message.\n",
|
||||||
dtmf_progress);
|
dtmf_progress);
|
||||||
ast_dtmf_stream(c, in, dtmf_progress, 250, 0);
|
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_channel_publish_dial(in, c, NULL, "PROGRESS");
|
ast_channel_publish_dial(in, c, NULL, "PROGRESS");
|
||||||
|
@ -1637,7 +1667,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
"Sending MF '%s' to %s as result of "
|
"Sending MF '%s' to %s as result of "
|
||||||
"receiving a WINK message.\n",
|
"receiving a WINK message.\n",
|
||||||
mf_wink, (hearpulsing ? "parties" : "called party"));
|
mf_wink, (hearpulsing ? "parties" : "called party"));
|
||||||
ast_mf_stream(c, (hearpulsing ? NULL : in),
|
res |= ast_mf_stream(c, (hearpulsing ? NULL : in),
|
||||||
(hearpulsing ? in : NULL), mf_wink, 50, 55, 120, 65, 0);
|
(hearpulsing ? in : NULL), mf_wink, 50, 55, 120, 65, 0);
|
||||||
}
|
}
|
||||||
if (!ast_strlen_zero(sf_wink)) {
|
if (!ast_strlen_zero(sf_wink)) {
|
||||||
|
@ -1645,9 +1675,13 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
"Sending SF '%s' to %s as result of "
|
"Sending SF '%s' to %s as result of "
|
||||||
"receiving a WINK message.\n",
|
"receiving a WINK message.\n",
|
||||||
sf_wink, (hearpulsing ? "parties" : "called party"));
|
sf_wink, (hearpulsing ? "parties" : "called party"));
|
||||||
ast_sf_stream(c, (hearpulsing ? NULL : in),
|
res |= ast_sf_stream(c, (hearpulsing ? NULL : in),
|
||||||
(hearpulsing ? in : NULL), sf_wink, 0, 0);
|
(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);
|
ast_indicate(in, AST_CONTROL_WINK);
|
||||||
break;
|
break;
|
||||||
|
@ -1762,9 +1796,13 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
case AST_FRAME_VIDEO:
|
case AST_FRAME_VIDEO:
|
||||||
case AST_FRAME_VOICE:
|
case AST_FRAME_VOICE:
|
||||||
case AST_FRAME_IMAGE:
|
case AST_FRAME_IMAGE:
|
||||||
|
case AST_FRAME_DTMF_BEGIN:
|
||||||
|
case AST_FRAME_DTMF_END:
|
||||||
if (caller_entertained) {
|
if (caller_entertained) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
*to_progress = -1;
|
||||||
|
progress_to_dup = -1;
|
||||||
/* Fall through */
|
/* Fall through */
|
||||||
case AST_FRAME_TEXT:
|
case AST_FRAME_TEXT:
|
||||||
if (single && ast_write(in, f)) {
|
if (single && ast_write(in, f)) {
|
||||||
|
@ -1793,7 +1831,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
#endif
|
#endif
|
||||||
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
|
if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
|
||||||
/* Got hung up */
|
/* Got hung up */
|
||||||
*to = -1;
|
*to_answer = -1;
|
||||||
strcpy(pa->status, "CANCEL");
|
strcpy(pa->status, "CANCEL");
|
||||||
pa->canceled = 1;
|
pa->canceled = 1;
|
||||||
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
||||||
|
@ -1817,7 +1855,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
|
context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
|
||||||
if (onedigit_goto(in, context, (char) f->subclass.integer, 1)) {
|
if (onedigit_goto(in, context, (char) f->subclass.integer, 1)) {
|
||||||
ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
|
ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
|
||||||
*to = 0;
|
*to_answer = 0;
|
||||||
*result = f->subclass.integer;
|
*result = f->subclass.integer;
|
||||||
strcpy(pa->status, "CANCEL");
|
strcpy(pa->status, "CANCEL");
|
||||||
pa->canceled = 1;
|
pa->canceled = 1;
|
||||||
|
@ -1836,7 +1874,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
|
||||||
if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) &&
|
if (ast_test_flag64(peerflags, OPT_CALLER_HANGUP) &&
|
||||||
detect_disconnect(in, f->subclass.integer, &featurecode)) {
|
detect_disconnect(in, f->subclass.integer, &featurecode)) {
|
||||||
ast_verb(3, "User requested call disconnect.\n");
|
ast_verb(3, "User requested call disconnect.\n");
|
||||||
*to = 0;
|
*to_answer = 0;
|
||||||
strcpy(pa->status, "CANCEL");
|
strcpy(pa->status, "CANCEL");
|
||||||
pa->canceled = 1;
|
pa->canceled = 1;
|
||||||
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
publish_dial_end_event(in, out_chans, NULL, pa->status);
|
||||||
|
@ -1947,9 +1985,15 @@ skip_frame:;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*to || ast_check_hangup(in)) {
|
wait_over:
|
||||||
ast_verb(3, "Nobody picked up in %d ms\n", orig);
|
if (!*to_answer || ast_check_hangup(in)) {
|
||||||
|
ast_verb(3, "Nobody picked up in %d ms\n", orig_answer_to);
|
||||||
publish_dial_end_event(in, out_chans, NULL, "NOANSWER");
|
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) {
|
if (is_cc_recall) {
|
||||||
|
@ -2321,7 +2365,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
||||||
struct chanlist *outgoing;
|
struct chanlist *outgoing;
|
||||||
struct chanlist *tmp;
|
struct chanlist *tmp;
|
||||||
struct ast_channel *peer = NULL;
|
struct ast_channel *peer = NULL;
|
||||||
int to; /* timeout */
|
int to_answer, to_progress; /* timeouts */
|
||||||
struct cause_args num = { chan, 0, 0, 0 };
|
struct cause_args num = { chan, 0, 0, 0 };
|
||||||
int cause, hanguptreecause = -1;
|
int cause, hanguptreecause = -1;
|
||||||
|
|
||||||
|
@ -2377,6 +2421,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
||||||
*/
|
*/
|
||||||
struct ast_party_caller caller;
|
struct ast_party_caller caller;
|
||||||
int max_forwards;
|
int max_forwards;
|
||||||
|
struct ast_datastore *topology_ds = NULL;
|
||||||
SCOPE_ENTER(1, "%s: Data: %s\n", ast_channel_name(chan), data);
|
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). */
|
/* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
|
||||||
|
@ -2678,7 +2723,21 @@ 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));
|
ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(chan));
|
||||||
|
|
||||||
topology = ast_stream_topology_clone(ast_channel_get_stream_topology(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));
|
||||||
|
}
|
||||||
|
|
||||||
ast_channel_unlock(chan);
|
ast_channel_unlock(chan);
|
||||||
|
|
||||||
|
@ -2920,14 +2979,31 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
||||||
AST_LIST_TRAVERSE_SAFE_END;
|
AST_LIST_TRAVERSE_SAFE_END;
|
||||||
|
|
||||||
if (ast_strlen_zero(args.timeout)) {
|
if (ast_strlen_zero(args.timeout)) {
|
||||||
to = -1;
|
to_answer = -1;
|
||||||
|
to_progress = -1;
|
||||||
} else {
|
} else {
|
||||||
to = atoi(args.timeout);
|
char *anstimeout = strsep(&args.timeout, "^");
|
||||||
if (to > 0)
|
if (!ast_strlen_zero(anstimeout)) {
|
||||||
to *= 1000;
|
to_answer = atoi(anstimeout);
|
||||||
else {
|
if (to_answer > 0) {
|
||||||
ast_log(LOG_WARNING, "Invalid timeout specified: '%s'. Setting timeout to infinite\n", args.timeout);
|
to_answer *= 1000;
|
||||||
to = -1;
|
} 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2967,7 +3043,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,
|
peer = wait_for_answer(chan, &out_chans, &to_answer, &to_progress, peerflags, opt_args, &pa, &num, &result,
|
||||||
dtmf_progress, mf_progress, mf_wink, sf_progress, sf_wink,
|
dtmf_progress, mf_progress, mf_wink, sf_progress, sf_wink,
|
||||||
(ast_test_flag64(&opts, OPT_HEARPULSING) ? 1 : 0),
|
(ast_test_flag64(&opts, OPT_HEARPULSING) ? 1 : 0),
|
||||||
ignore_cc, &forced_clid, &stored_clid, &config);
|
ignore_cc, &forced_clid, &stored_clid, &config);
|
||||||
|
@ -2975,7 +3051,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
|
||||||
if (!peer) {
|
if (!peer) {
|
||||||
if (result) {
|
if (result) {
|
||||||
res = result;
|
res = result;
|
||||||
} else if (to) { /* Musta gotten hung up */
|
} else if (to_answer) { /* Musta gotten hung up */
|
||||||
res = -1;
|
res = -1;
|
||||||
} else { /* Nobody answered, next please? */
|
} else { /* Nobody answered, next please? */
|
||||||
res = 0;
|
res = 0;
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "asterisk/say.h"
|
#include "asterisk/say.h"
|
||||||
#include "asterisk/app.h"
|
#include "asterisk/app.h"
|
||||||
#include "asterisk/utils.h"
|
#include "asterisk/utils.h"
|
||||||
|
#include "asterisk/adsi.h"
|
||||||
|
|
||||||
/*** DOCUMENTATION
|
/*** DOCUMENTATION
|
||||||
<application name="Directory" language="en_US">
|
<application name="Directory" language="en_US">
|
||||||
|
@ -103,23 +104,36 @@
|
||||||
receiver to their ear while entering DTMF.</para>
|
receiver to their ear while entering DTMF.</para>
|
||||||
<argument name="n" required="true" />
|
<argument name="n" required="true" />
|
||||||
</option>
|
</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>
|
</optionlist>
|
||||||
<note><para>Only one of the <replaceable>f</replaceable>, <replaceable>l</replaceable>, or <replaceable>b</replaceable>
|
<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
|
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
|
if <replaceable>b</replaceable> was specified. The number
|
||||||
of characters for the user to type defaults to <literal>3</literal>.</para></note>
|
of characters for the user to type defaults to <literal>3</literal>.</para></note>
|
||||||
|
|
||||||
</parameter>
|
</parameter>
|
||||||
</syntax>
|
</syntax>
|
||||||
<description>
|
<description>
|
||||||
<para>This application will present the calling channel with a directory of extensions from which they can search
|
<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
|
by name. The list of names and corresponding extensions is retrieved from the
|
||||||
voicemail configuration file, <filename>voicemail.conf</filename>.</para>
|
voicemail configuration file, <filename>voicemail.conf</filename>, or from the specified filename.</para>
|
||||||
<para>This application will immediately exit if one of the following DTMF digits are
|
<para>This application will immediately exit if one of the following DTMF digits are
|
||||||
received and the extension to jump to exists:</para>
|
received and the extension to jump to exists:</para>
|
||||||
<para><literal>0</literal> - Jump to the 'o' extension, if it 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><literal>*</literal> - Jump to the 'a' extension, if it exists.</para>
|
||||||
<para>This application will set the following channel variable before completion:</para>
|
<para>This application will set the following channel variables before completion:</para>
|
||||||
<variablelist>
|
<variablelist>
|
||||||
<variable name="DIRECTORY_RESULT">
|
<variable name="DIRECTORY_RESULT">
|
||||||
<para>Reason Directory application exited.</para>
|
<para>Reason Directory application exited.</para>
|
||||||
|
@ -131,6 +145,10 @@
|
||||||
<value name="USEREXIT">User exited with '#' during selection</value>
|
<value name="USEREXIT">User exited with '#' during selection</value>
|
||||||
<value name="FAILED">The application failed</value>
|
<value name="FAILED">The application failed</value>
|
||||||
</variable>
|
</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>
|
</variablelist>
|
||||||
</description>
|
</description>
|
||||||
</application>
|
</application>
|
||||||
|
@ -153,6 +171,9 @@ enum {
|
||||||
OPT_PAUSE = (1 << 5),
|
OPT_PAUSE = (1 << 5),
|
||||||
OPT_NOANSWER = (1 << 6),
|
OPT_NOANSWER = (1 << 6),
|
||||||
OPT_ALIAS = (1 << 7),
|
OPT_ALIAS = (1 << 7),
|
||||||
|
OPT_CONFIG_FILE = (1 << 8),
|
||||||
|
OPT_SKIP = (1 << 9),
|
||||||
|
OPT_ADSI = (1 << 10),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -160,8 +181,9 @@ enum {
|
||||||
OPT_ARG_LASTNAME = 1,
|
OPT_ARG_LASTNAME = 1,
|
||||||
OPT_ARG_EITHER = 2,
|
OPT_ARG_EITHER = 2,
|
||||||
OPT_ARG_PAUSE = 3,
|
OPT_ARG_PAUSE = 3,
|
||||||
|
OPT_ARG_FILENAME = 4,
|
||||||
/* This *must* be the last value in this enum! */
|
/* This *must* be the last value in this enum! */
|
||||||
OPT_ARG_ARRAY_SIZE = 4,
|
OPT_ARG_ARRAY_SIZE = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct directory_item {
|
struct directory_item {
|
||||||
|
@ -183,8 +205,74 @@ AST_APP_OPTIONS(directory_app_options, {
|
||||||
AST_APP_OPTION('m', OPT_SELECTFROMMENU),
|
AST_APP_OPTION('m', OPT_SELECTFROMMENU),
|
||||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||||
AST_APP_OPTION('a', OPT_ALIAS),
|
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)
|
static int compare(const char *text, const char *template)
|
||||||
{
|
{
|
||||||
char digit;
|
char digit;
|
||||||
|
@ -318,6 +406,9 @@ static int select_entry(struct ast_channel *chan, const char *dialcontext, const
|
||||||
if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
|
if (ast_test_flag(flags, OPT_FROMVOICEMAIL)) {
|
||||||
/* We still want to set the exten though */
|
/* We still want to set the exten though */
|
||||||
ast_channel_exten_set(chan, item->exten);
|
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)) {
|
} else if (ast_goto_if_exists(chan, S_OR(dialcontext, item->context), item->exten, 1)) {
|
||||||
ast_log(LOG_WARNING,
|
ast_log(LOG_WARNING,
|
||||||
"Can't find extension '%s' in context '%s'. "
|
"Can't find extension '%s' in context '%s'. "
|
||||||
|
@ -356,6 +447,10 @@ static int select_item_seq(struct ast_channel *chan, struct directory_item **ite
|
||||||
for (ptr = items, i = 0; i < count; i++, ptr++) {
|
for (ptr = items, i = 0; i < count; i++, ptr++) {
|
||||||
item = *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--) {
|
for (loop = 3 ; loop > 0; loop--) {
|
||||||
if (!res)
|
if (!res)
|
||||||
res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
|
res = play_mailbox_owner(chan, item->context, item->exten, item->name, flags);
|
||||||
|
@ -458,7 +553,7 @@ static int select_item_menu(struct ast_channel *chan, struct directory_item **it
|
||||||
|
|
||||||
AST_THREADSTORAGE(commonbuf);
|
AST_THREADSTORAGE(commonbuf);
|
||||||
|
|
||||||
static struct ast_config *realtime_directory(char *context)
|
static struct ast_config *realtime_directory(char *context, const char *filename)
|
||||||
{
|
{
|
||||||
struct ast_config *cfg;
|
struct ast_config *cfg;
|
||||||
struct ast_config *rtdata = NULL;
|
struct ast_config *rtdata = NULL;
|
||||||
|
@ -475,14 +570,14 @@ static struct ast_config *realtime_directory(char *context)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load flat file config. */
|
/* Load flat file config. */
|
||||||
cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
|
cfg = ast_config_load(filename, config_flags);
|
||||||
|
|
||||||
if (!cfg) {
|
if (!cfg) {
|
||||||
/* Loading config failed. */
|
/* Loading config failed. */
|
||||||
ast_log(LOG_WARNING, "Loading config failed.\n");
|
ast_log(LOG_WARNING, "Loading config failed.\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
|
||||||
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", VOICEMAIL_CONFIG);
|
ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", filename);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,7 +962,9 @@ 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))
|
if (args.options && ast_app_parse_options(directory_app_options, &flags, opts, args.options))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!(cfg = realtime_directory(args.vmcontext))) {
|
cfg = realtime_directory(args.vmcontext, S_OR(opts[OPT_ARG_FILENAME], VOICEMAIL_CONFIG));
|
||||||
|
|
||||||
|
if (!cfg) {
|
||||||
ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
|
ast_log(LOG_ERROR, "Unable to read the configuration data!\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -913,6 +1010,18 @@ static int directory_exec(struct ast_channel *chan, const char *data)
|
||||||
}
|
}
|
||||||
digits[7] = digit + '0';
|
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_channel_state(chan) != AST_STATE_UP) {
|
||||||
if (!ast_test_flag(&flags, OPT_NOANSWER)) {
|
if (!ast_test_flag(&flags, OPT_NOANSWER)) {
|
||||||
/* Otherwise answer unless we're supposed to read while on-hook */
|
/* Otherwise answer unless we're supposed to read while on-hook */
|
||||||
|
@ -920,6 +1029,9 @@ static int directory_exec(struct ast_channel *chan, const char *data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
if (ast_test_flag(&flags, OPT_ADSI) && adsi_search_input(chan)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (!ast_strlen_zero(dirintro) && !res) {
|
if (!ast_strlen_zero(dirintro) && !res) {
|
||||||
res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
|
res = ast_stream_and_wait(chan, dirintro, AST_DIGIT_ANY);
|
||||||
} else if (!res) {
|
} else if (!res) {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -836,8 +836,9 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ast_verb(3, "Skip playback of caller name / norecording\n");
|
ast_debug(1, "Taking call with no prompt\n");
|
||||||
tmpuser->state = 2;
|
ast_frfree(f);
|
||||||
|
return tmpuser->ochan;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AST_CONTROL_BUSY:
|
case AST_CONTROL_BUSY:
|
||||||
|
@ -964,11 +965,6 @@ static struct ast_channel *wait_for_winner(struct findme_user_listptr *findme_us
|
||||||
break;
|
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) {
|
if (tmpuser && tmpuser->state == 3 && f->frametype == AST_FRAME_DTMF) {
|
||||||
int cmp_len;
|
int cmp_len;
|
||||||
|
|
||||||
|
@ -1072,6 +1068,7 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
|
||||||
ast_copy_string(num, nm->number, sizeof(num));
|
ast_copy_string(num, nm->number, sizeof(num));
|
||||||
for (number = num; number; number = rest) {
|
for (number = num; number; number = rest) {
|
||||||
struct ast_channel *outbound;
|
struct ast_channel *outbound;
|
||||||
|
struct ast_format_cap *caps;
|
||||||
|
|
||||||
rest = strchr(number, '&');
|
rest = strchr(number, '&');
|
||||||
if (rest) {
|
if (rest) {
|
||||||
|
@ -1101,8 +1098,15 @@ static struct ast_channel *findmeexec(struct fm_args *tpargs, struct ast_channel
|
||||||
? "/n" : "/m");
|
? "/n" : "/m");
|
||||||
}
|
}
|
||||||
|
|
||||||
outbound = ast_request("Local", ast_channel_nativeformats(caller), NULL, caller,
|
/* Capture nativeformats reference in case it gets changed */
|
||||||
tmpuser->dialarg, &dg);
|
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);
|
||||||
|
|
||||||
if (!outbound) {
|
if (!outbound) {
|
||||||
ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n",
|
ast_log(LOG_WARNING, "Unable to allocate a channel for Local/%s cause: %s\n",
|
||||||
tmpuser->dialarg, ast_cause2str(dg));
|
tmpuser->dialarg, ast_cause2str(dg));
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
<depend>res_adsi</depend>
|
<depend>res_adsi</depend>
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>deprecated</support_level>
|
<support_level>deprecated</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,391 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
|
<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");
|
|
@ -37,6 +37,7 @@
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
<depend>jack</depend>
|
<depend>jack</depend>
|
||||||
<depend>resample</depend>
|
<depend>resample</depend>
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -262,7 +262,7 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
|
||||||
char *save_macro_priority;
|
char *save_macro_priority;
|
||||||
char *save_macro_offset;
|
char *save_macro_offset;
|
||||||
int save_in_subroutine;
|
int save_in_subroutine;
|
||||||
struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
|
struct ast_datastore *macro_store;
|
||||||
int had_infinite_include_error = 0;
|
int had_infinite_include_error = 0;
|
||||||
static int deprecation_notice = 0;
|
static int deprecation_notice = 0;
|
||||||
|
|
||||||
|
@ -277,6 +277,10 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
|
||||||
ast_log(LOG_WARNING, "Dialplan should be updated to use Gosub instead.\n");
|
ast_log(LOG_WARNING, "Dialplan should be updated to use Gosub instead.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast_channel_lock(chan);
|
||||||
|
|
||||||
|
macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (macro_store) {
|
if (macro_store) {
|
||||||
break;
|
break;
|
||||||
|
@ -291,7 +295,6 @@ static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
/* does the user want a deeper rabbit hole? */
|
/* does the user want a deeper rabbit hole? */
|
||||||
ast_channel_lock(chan);
|
|
||||||
if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
|
if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
|
||||||
sscanf(s, "%30d", &maxdepth);
|
sscanf(s, "%30d", &maxdepth);
|
||||||
}
|
}
|
||||||
|
|
|
@ -639,6 +639,82 @@
|
||||||
</syntax>
|
</syntax>
|
||||||
</managerEventInstance>
|
</managerEventInstance>
|
||||||
</managerEvent>
|
</managerEvent>
|
||||||
|
<managerEvent language="en_US" name="MeetmeList">
|
||||||
|
<managerEventInstance class="EVENT_FLAG_CALL">
|
||||||
|
<synopsis>Raised in response to a MeetmeList command.</synopsis>
|
||||||
|
<syntax>
|
||||||
|
<parameter name="Conference">
|
||||||
|
<para>Conference ID.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="UserNumber">
|
||||||
|
<para>User ID.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="CallerIDNum">
|
||||||
|
<para>Caller ID number.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="CallerIDName">
|
||||||
|
<para>Caller ID name.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="ConnectedLineNum">
|
||||||
|
<para>Connected Line number.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="ConnectedLineName">
|
||||||
|
<para>Connected Line name.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Channel">
|
||||||
|
<para>Channel name</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Admin">
|
||||||
|
<para>Whether or not the user is an admin.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Role">
|
||||||
|
<para>User role. Can be "Listen only", "Talk only", or "Talk and listen".</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="MarkedUser">
|
||||||
|
<para>Whether or not the user is a marked user.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Muted">
|
||||||
|
<para>Whether or not the user is currently muted.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Talking">
|
||||||
|
<para>Whether or not the user is currently talking.</para>
|
||||||
|
</parameter>
|
||||||
|
</syntax>
|
||||||
|
<see-also>
|
||||||
|
<ref type="manager">MeetmeList</ref>
|
||||||
|
<ref type="application">MeetMe</ref>
|
||||||
|
</see-also>
|
||||||
|
</managerEventInstance>
|
||||||
|
</managerEvent>
|
||||||
|
<managerEvent language="en_US" name="MeetmeListRooms">
|
||||||
|
<managerEventInstance class="EVENT_FLAG_CALL">
|
||||||
|
<synopsis>Raised in response to a MeetmeListRooms command.</synopsis>
|
||||||
|
<syntax>
|
||||||
|
<parameter name="Conference">
|
||||||
|
<para>Conference ID.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Parties">
|
||||||
|
<para>Number of parties in the conference.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Marked">
|
||||||
|
<para>Number of marked users in the conference.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Activity">
|
||||||
|
<para>Total duration of conference in HH:MM:SS format.</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Creation">
|
||||||
|
<para>How the conference was created: "Dyanmic" or "Static".</para>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="Locked">
|
||||||
|
<para>Whether or not the conference is locked.</para>
|
||||||
|
</parameter>
|
||||||
|
</syntax>
|
||||||
|
<see-also>
|
||||||
|
<ref type="manager">MeetmeListRooms</ref>
|
||||||
|
<ref type="application">MeetMe</ref>
|
||||||
|
</see-also>
|
||||||
|
</managerEventInstance>
|
||||||
|
</managerEvent>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#define CONFIG_FILE_NAME "meetme.conf"
|
#define CONFIG_FILE_NAME "meetme.conf"
|
||||||
|
@ -6073,7 +6149,7 @@ struct run_station_args {
|
||||||
|
|
||||||
static void answer_trunk_chan(struct ast_channel *chan)
|
static void answer_trunk_chan(struct ast_channel *chan)
|
||||||
{
|
{
|
||||||
ast_answer(chan);
|
ast_raw_answer(chan);
|
||||||
ast_indicate(chan, -1);
|
ast_indicate(chan, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6918,8 +6994,18 @@ static void *dial_trunk(void *data)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
/* Wait for dial to end, while servicing the channel */
|
||||||
|
while (ast_waitfor(trunk_ref->chan, 100)) {
|
||||||
unsigned int done = 0;
|
unsigned int done = 0;
|
||||||
|
struct ast_frame *fr = ast_read(trunk_ref->chan);
|
||||||
|
|
||||||
|
if (!fr) {
|
||||||
|
ast_debug(1, "Channel %s did not return a frame, must have hung up\n", ast_channel_name(trunk_ref->chan));
|
||||||
|
done = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ast_frfree(fr); /* Ignore while dialing */
|
||||||
|
|
||||||
switch ((dial_res = ast_dial_state(dial))) {
|
switch ((dial_res = ast_dial_state(dial))) {
|
||||||
case AST_DIAL_RESULT_ANSWERED:
|
case AST_DIAL_RESULT_ANSWERED:
|
||||||
trunk_ref->trunk->chan = ast_dial_answered(dial);
|
trunk_ref->trunk->chan = ast_dial_answered(dial);
|
||||||
|
@ -6956,8 +7042,6 @@ static void *dial_trunk(void *data)
|
||||||
last_state = current_state;
|
last_state = current_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* avoid tight loop... sleep for 1/10th second */
|
|
||||||
ast_safe_sleep(trunk_ref->chan, 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!trunk_ref->trunk->chan) {
|
if (!trunk_ref->trunk->chan) {
|
||||||
|
@ -7116,8 +7200,10 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
|
||||||
sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
|
sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
|
||||||
/* Create a thread to dial the trunk and dump it into the conference.
|
/* Create a thread to dial the trunk and dump it into the conference.
|
||||||
* However, we want to wait until the trunk has been dialed and the
|
* However, we want to wait until the trunk has been dialed and the
|
||||||
* conference is created before continuing on here. */
|
* conference is created before continuing on here.
|
||||||
ast_autoservice_start(chan);
|
* Don't autoservice the channel or we'll have multiple threads
|
||||||
|
* handling it. dial_trunk services the channel.
|
||||||
|
*/
|
||||||
ast_mutex_init(&cond_lock);
|
ast_mutex_init(&cond_lock);
|
||||||
ast_cond_init(&cond, NULL);
|
ast_cond_init(&cond, NULL);
|
||||||
ast_mutex_lock(&cond_lock);
|
ast_mutex_lock(&cond_lock);
|
||||||
|
@ -7126,7 +7212,7 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
|
||||||
ast_mutex_unlock(&cond_lock);
|
ast_mutex_unlock(&cond_lock);
|
||||||
ast_mutex_destroy(&cond_lock);
|
ast_mutex_destroy(&cond_lock);
|
||||||
ast_cond_destroy(&cond);
|
ast_cond_destroy(&cond);
|
||||||
ast_autoservice_stop(chan);
|
|
||||||
if (!trunk_ref->trunk->chan) {
|
if (!trunk_ref->trunk->chan) {
|
||||||
ast_debug(1, "Trunk didn't get created. chan: %lx\n", (unsigned long) trunk_ref->trunk->chan);
|
ast_debug(1, "Trunk didn't get created. chan: %lx\n", (unsigned long) trunk_ref->trunk->chan);
|
||||||
pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
|
pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
@ -235,6 +236,7 @@ static const char sendmf_name[] = "SendMF";
|
||||||
* \param buflen Size of buffer
|
* \param buflen Size of buffer
|
||||||
* \param timeout ms to wait for all digits before giving up
|
* \param timeout ms to wait for all digits before giving up
|
||||||
* \param features Any additional DSP features to use
|
* \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 override Start over if we receive additional KPs
|
||||||
* \param no_kp Don't include KP in the output
|
* \param no_kp Don't include KP in the output
|
||||||
* \param no_st Don't include start digits in the output
|
* \param no_st Don't include start digits in the output
|
||||||
|
|
|
@ -148,6 +148,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,16 @@
|
||||||
<para>Play a periodic beep while this call is being recorded.</para>
|
<para>Play a periodic beep while this call is being recorded.</para>
|
||||||
<argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
|
<argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
|
||||||
</option>
|
</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">
|
<option name="v">
|
||||||
<para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
|
<para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
|
||||||
(range <literal>-4</literal> to <literal>4</literal>)</para>
|
(range <literal>-4</literal> to <literal>4</literal>)</para>
|
||||||
|
@ -407,6 +417,8 @@ enum mixmonitor_flags {
|
||||||
MUXFLAG_BEEP_STOP = (1 << 13),
|
MUXFLAG_BEEP_STOP = (1 << 13),
|
||||||
MUXFLAG_DEPRECATED_RWSYNC = (1 << 14),
|
MUXFLAG_DEPRECATED_RWSYNC = (1 << 14),
|
||||||
MUXFLAG_NO_RWSYNC = (1 << 15),
|
MUXFLAG_NO_RWSYNC = (1 << 15),
|
||||||
|
MUXFLAG_AUTO_DELETE = (1 << 16),
|
||||||
|
MUXFLAG_REAL_CALLERID = (1 << 17),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mixmonitor_args {
|
enum mixmonitor_args {
|
||||||
|
@ -427,6 +439,8 @@ AST_APP_OPTIONS(mixmonitor_opts, {
|
||||||
AST_APP_OPTION('a', MUXFLAG_APPEND),
|
AST_APP_OPTION('a', MUXFLAG_APPEND),
|
||||||
AST_APP_OPTION('b', MUXFLAG_BRIDGED),
|
AST_APP_OPTION('b', MUXFLAG_BRIDGED),
|
||||||
AST_APP_OPTION_ARG('B', MUXFLAG_BEEP, OPT_ARG_BEEP_INTERVAL),
|
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_START),
|
||||||
AST_APP_OPTION('P', MUXFLAG_BEEP_STOP),
|
AST_APP_OPTION('P', MUXFLAG_BEEP_STOP),
|
||||||
AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
|
AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
|
||||||
|
@ -860,6 +874,19 @@ static void *mixmonitor_thread(void *obj)
|
||||||
ast_debug(3, "No recipients to forward monitor to, moving on.\n");
|
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);
|
mixmonitor_free(mixmonitor);
|
||||||
|
|
||||||
ast_module_unref(ast_module_info->self);
|
ast_module_unref(ast_module_info->self);
|
||||||
|
@ -1015,20 +1042,37 @@ static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
|
||||||
|
|
||||||
if (!ast_strlen_zero(recipients)) {
|
if (!ast_strlen_zero(recipients)) {
|
||||||
char callerid[256];
|
char callerid[256];
|
||||||
struct ast_party_connected_line *connected;
|
|
||||||
|
|
||||||
ast_channel_lock(chan);
|
ast_channel_lock(chan);
|
||||||
|
|
||||||
/* We use the connected line of the invoking channel for caller ID. */
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
connected = ast_channel_connected(chan);
|
if (ast_test_flag(mixmonitor, MUXFLAG_REAL_CALLERID)) {
|
||||||
ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
|
struct ast_party_caller *caller;
|
||||||
connected->id.name.str, connected->id.number.valid,
|
caller = ast_channel_caller(chan);
|
||||||
connected->id.number.str);
|
ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
|
||||||
ast_callerid_merge(callerid, sizeof(callerid),
|
caller->id.name.str, caller->id.number.valid,
|
||||||
S_COR(connected->id.name.valid, connected->id.name.str, NULL),
|
caller->id.number.str);
|
||||||
S_COR(connected->id.number.valid, connected->id.number.str, NULL),
|
ast_callerid_merge(callerid, sizeof(callerid),
|
||||||
"Unknown");
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
|
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_macrocontext, ast_channel_macrocontext(chan));
|
||||||
|
@ -1403,6 +1447,50 @@ static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_
|
||||||
return CLI_SUCCESS;
|
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 */
|
/*! \brief Mute / unmute a MixMonitor channel */
|
||||||
static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
|
static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
|
||||||
{
|
{
|
||||||
|
@ -1411,7 +1499,8 @@ static int manager_mute_mixmonitor(struct mansession *s, const struct message *m
|
||||||
const char *id = astman_get_header(m, "ActionID");
|
const char *id = astman_get_header(m, "ActionID");
|
||||||
const char *state = astman_get_header(m, "State");
|
const char *state = astman_get_header(m, "State");
|
||||||
const char *direction = astman_get_header(m,"Direction");
|
const char *direction = astman_get_header(m,"Direction");
|
||||||
int clearmute = 1;
|
const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
|
||||||
|
int clearmute = 1, mutedcount = 0;
|
||||||
enum ast_audiohook_flags flag;
|
enum ast_audiohook_flags flag;
|
||||||
RAII_VAR(struct stasis_message *, stasis_message, NULL, ao2_cleanup);
|
RAII_VAR(struct stasis_message *, stasis_message, NULL, ao2_cleanup);
|
||||||
RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
|
RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
|
||||||
|
@ -1450,15 +1539,28 @@ static int manager_mute_mixmonitor(struct mansession *s, const struct message *m
|
||||||
return AMI_SUCCESS;
|
return AMI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
|
if (ast_strlen_zero(mixmonitor_id)) {
|
||||||
ast_channel_unref(c);
|
mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
|
||||||
astman_send_error(s, m, "Cannot set mute flag");
|
if (mutedcount < 0) {
|
||||||
return AMI_SUCCESS;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
stasis_message_blob = ast_json_pack("{s: s, s: b}",
|
|
||||||
|
stasis_message_blob = ast_json_pack("{s: s, s: b, s: s, s: i}",
|
||||||
"direction", direction,
|
"direction", direction,
|
||||||
"state", ast_true(state));
|
"state", ast_true(state),
|
||||||
|
"mixmonitorid", mixmonitor_id,
|
||||||
|
"count", mutedcount);
|
||||||
|
|
||||||
stasis_message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(c),
|
stasis_message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(c),
|
||||||
ast_channel_mixmonitor_mute_type(), stasis_message_blob);
|
ast_channel_mixmonitor_mute_type(), stasis_message_blob);
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
@ -101,7 +102,7 @@ static int mp3play(const char *filename, unsigned int sampling_rate, int fd)
|
||||||
/* Execute mpg123, but buffer if it's a net connection */
|
/* Execute mpg123, but buffer if it's a net connection */
|
||||||
if (!strncasecmp(filename, "http://", 7) && strstr(filename, ".m3u")) {
|
if (!strncasecmp(filename, "http://", 7) && strstr(filename, ".m3u")) {
|
||||||
char buffer_size_str[8];
|
char buffer_size_str[8];
|
||||||
snprintf(buffer_size_str, 8, "%u", (int) 0.5*2*sampling_rate/1000); // 0.5 seconds for a live stream
|
snprintf(buffer_size_str, 8, "%u", (int) 0.5*2*sampling_rate/1000); /* 0.5 seconds for a live stream */
|
||||||
/* Most commonly installed in /usr/local/bin */
|
/* Most commonly installed in /usr/local/bin */
|
||||||
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
|
||||||
/* But many places has it in /usr/bin */
|
/* But many places has it in /usr/bin */
|
||||||
|
@ -111,7 +112,7 @@ static int mp3play(const char *filename, unsigned int sampling_rate, int fd)
|
||||||
}
|
}
|
||||||
else if (!strncasecmp(filename, "http://", 7)) {
|
else if (!strncasecmp(filename, "http://", 7)) {
|
||||||
char buffer_size_str[8];
|
char buffer_size_str[8];
|
||||||
snprintf(buffer_size_str, 8, "%u", 6*2*sampling_rate/1000); // 6 seconds for a remote MP3 file
|
snprintf(buffer_size_str, 8, "%u", 6*2*sampling_rate/1000); /* 6 seconds for a remote MP3 file */
|
||||||
/* Most commonly installed in /usr/local/bin */
|
/* Most commonly installed in /usr/local/bin */
|
||||||
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
execl(LOCAL_MPG_123, "mpg123", "-e", "s16", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
|
||||||
/* But many places has it in /usr/bin */
|
/* But many places has it in /usr/bin */
|
||||||
|
|
|
@ -48,6 +48,13 @@
|
||||||
</synopsis>
|
</synopsis>
|
||||||
<syntax>
|
<syntax>
|
||||||
<parameter name="filenames" required="true" argsep="&">
|
<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="filename" required="true" />
|
||||||
<argument name="filename2" multiple="true" />
|
<argument name="filename2" multiple="true" />
|
||||||
</parameter>
|
</parameter>
|
||||||
|
@ -492,7 +499,7 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
||||||
char *front;
|
char *front;
|
||||||
|
|
||||||
ast_stopstream(chan);
|
ast_stopstream(chan);
|
||||||
while (!res && (front = strsep(&back, "&"))) {
|
while (!res && (front = ast_strsep(&back, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||||
if (option_say)
|
if (option_say)
|
||||||
res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
|
res = say_full(chan, front, "", ast_channel_language(chan), NULL, -1, -1);
|
||||||
else if (option_mix){
|
else if (option_mix){
|
||||||
|
@ -507,8 +514,7 @@ static int playback_exec(struct ast_channel *chan, const char *data)
|
||||||
if (!res) {
|
if (!res) {
|
||||||
res = ast_waitstream(chan, "");
|
res = ast_waitstream(chan, "");
|
||||||
ast_stopstream(chan);
|
ast_stopstream(chan);
|
||||||
}
|
} else {
|
||||||
if (res) {
|
|
||||||
if (!ast_check_hangup(chan)) {
|
if (!ast_check_hangup(chan)) {
|
||||||
ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
|
ast_log(LOG_WARNING, "Playback failed on %s for %s\n", ast_channel_name(chan), (char *)data);
|
||||||
}
|
}
|
||||||
|
|
258
apps/app_queue.c
258
apps/app_queue.c
|
@ -161,7 +161,10 @@
|
||||||
<para>Continue in the dialplan if the callee hangs up.</para>
|
<para>Continue in the dialplan if the callee hangs up.</para>
|
||||||
</option>
|
</option>
|
||||||
<option name="d">
|
<option name="d">
|
||||||
<para>data-quality (modem) call (minimum delay).</para>
|
<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>
|
||||||
</option>
|
</option>
|
||||||
<option name="F" argsep="^">
|
<option name="F" argsep="^">
|
||||||
<argument name="context" required="false" />
|
<argument name="context" required="false" />
|
||||||
|
@ -171,12 +174,6 @@
|
||||||
to the specified destination and <emphasis>start</emphasis> execution at that location.</para>
|
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
|
<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>
|
prefixed with one or two underbars ('_').</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>
|
<para>NOTE: Using this option from a Macro() or GoSub() might not make sense as there would be no return points.</para>
|
||||||
</option>
|
</option>
|
||||||
<option name="h">
|
<option name="h">
|
||||||
|
@ -244,11 +241,18 @@
|
||||||
<para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
|
<para><replaceable>URL</replaceable> will be sent to the called party if the channel supports it.</para>
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="announceoverride" argsep="&">
|
<parameter name="announceoverride" argsep="&">
|
||||||
<argument name="filename" required="true">
|
<para>Announcement file(s) to play to agent before bridging
|
||||||
<para>Announcement file(s) to play to agent before bridging call, overriding the announcement(s)
|
call, overriding the announcement(s) configured in
|
||||||
configured in <filename>queues.conf</filename>, if any.</para>
|
<filename>queues.conf</filename>, if any.</para>
|
||||||
</argument>
|
<para>Ampersand separated list of filenames. If the filename
|
||||||
<argument name="filename2" multiple="true" />
|
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" />
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="timeout">
|
<parameter name="timeout">
|
||||||
<para>Will cause the queue to fail out after a specified number of
|
<para>Will cause the queue to fail out after a specified number of
|
||||||
|
@ -1062,6 +1066,9 @@
|
||||||
<parameter name="Priority" required="true">
|
<parameter name="Priority" required="true">
|
||||||
<para>Priority value for change for caller on queue.</para>
|
<para>Priority value for change for caller on queue.</para>
|
||||||
</parameter>
|
</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>
|
</syntax>
|
||||||
<description>
|
<description>
|
||||||
</description>
|
</description>
|
||||||
|
@ -1277,7 +1284,7 @@
|
||||||
</syntax>
|
</syntax>
|
||||||
<see-also>
|
<see-also>
|
||||||
<ref type="application">PauseQueueMember</ref>
|
<ref type="application">PauseQueueMember</ref>
|
||||||
<ref type="application">UnPauseQueueMember</ref>
|
<ref type="application">UnpauseQueueMember</ref>
|
||||||
</see-also>
|
</see-also>
|
||||||
</managerEventInstance>
|
</managerEventInstance>
|
||||||
</managerEvent>
|
</managerEvent>
|
||||||
|
@ -1621,9 +1628,15 @@ static int negative_penalty_invalid;
|
||||||
/*! \brief queues.conf [general] option */
|
/*! \brief queues.conf [general] option */
|
||||||
static int log_membername_as_agent;
|
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 */
|
/*! \brief name of the ringinuse field in the realtime database */
|
||||||
static char *realtime_ringinuse_field;
|
static char *realtime_ringinuse_field;
|
||||||
|
|
||||||
|
/*! \brief does realtime backend support reason_paused */
|
||||||
|
static int realtime_reason_paused;
|
||||||
|
|
||||||
enum queue_result {
|
enum queue_result {
|
||||||
QUEUE_UNKNOWN = 0,
|
QUEUE_UNKNOWN = 0,
|
||||||
QUEUE_TIMEOUT = 1,
|
QUEUE_TIMEOUT = 1,
|
||||||
|
@ -1856,6 +1869,7 @@ struct call_queue {
|
||||||
int announcepositionlimit; /*!< How many positions we announce? */
|
int announcepositionlimit; /*!< How many positions we announce? */
|
||||||
int announcefrequency; /*!< How often to announce their position */
|
int announcefrequency; /*!< How often to announce their position */
|
||||||
int minannouncefrequency; /*!< The minimum number of seconds between position announcements (def. 15) */
|
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 periodicannouncefrequency; /*!< How often to play periodic announcement */
|
||||||
int numperiodicannounce; /*!< The number of periodic announcements configured */
|
int numperiodicannounce; /*!< The number of periodic announcements configured */
|
||||||
int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
|
int randomperiodicannounce; /*!< Are periodic announcments randomly chosen */
|
||||||
|
@ -2106,8 +2120,10 @@ 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
|
/* 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
|
* 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. */
|
* that even when the queue_ent leaves the call_queue this ref must remain. */
|
||||||
queue_ref(q);
|
if (!new->parent) {
|
||||||
new->parent = q;
|
queue_ref(q);
|
||||||
|
new->parent = q;
|
||||||
|
}
|
||||||
new->pos = ++(*pos);
|
new->pos = ++(*pos);
|
||||||
new->opos = *pos;
|
new->opos = *pos;
|
||||||
}
|
}
|
||||||
|
@ -2956,7 +2972,11 @@ static void init_queue(struct call_queue *q)
|
||||||
q->timeout = DEFAULT_TIMEOUT;
|
q->timeout = DEFAULT_TIMEOUT;
|
||||||
q->maxlen = 0;
|
q->maxlen = 0;
|
||||||
|
|
||||||
|
ast_string_field_set(q, announce, "");
|
||||||
ast_string_field_set(q, context, "");
|
ast_string_field_set(q, context, "");
|
||||||
|
ast_string_field_set(q, membermacro, "");
|
||||||
|
ast_string_field_set(q, membergosub, "");
|
||||||
|
ast_string_field_set(q, defaultrule, "");
|
||||||
|
|
||||||
q->announcefrequency = 0;
|
q->announcefrequency = 0;
|
||||||
q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
|
q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
|
||||||
|
@ -2983,9 +3003,13 @@ static void init_queue(struct call_queue *q)
|
||||||
q->weight = 0;
|
q->weight = 0;
|
||||||
q->timeoutrestart = 0;
|
q->timeoutrestart = 0;
|
||||||
q->periodicannouncefrequency = 0;
|
q->periodicannouncefrequency = 0;
|
||||||
|
q->periodicannouncestartdelay = -1;
|
||||||
q->randomperiodicannounce = 0;
|
q->randomperiodicannounce = 0;
|
||||||
q->numperiodicannounce = 0;
|
q->numperiodicannounce = 0;
|
||||||
|
q->relativeperiodicannounce = 0;
|
||||||
q->autopause = QUEUE_AUTOPAUSE_OFF;
|
q->autopause = QUEUE_AUTOPAUSE_OFF;
|
||||||
|
q->autopausebusy = 0;
|
||||||
|
q->autopauseunavail = 0;
|
||||||
q->timeoutpriority = TIMEOUT_PRIORITY_APP;
|
q->timeoutpriority = TIMEOUT_PRIORITY_APP;
|
||||||
q->autopausedelay = 0;
|
q->autopausedelay = 0;
|
||||||
if (!q->members) {
|
if (!q->members) {
|
||||||
|
@ -3010,6 +3034,7 @@ static void init_queue(struct call_queue *q)
|
||||||
ast_string_field_set(q, sound_minute, "queue-minute");
|
ast_string_field_set(q, sound_minute, "queue-minute");
|
||||||
ast_string_field_set(q, sound_seconds, "queue-seconds");
|
ast_string_field_set(q, sound_seconds, "queue-seconds");
|
||||||
ast_string_field_set(q, sound_thanks, "queue-thankyou");
|
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");
|
ast_string_field_set(q, sound_reporthold, "queue-reporthold");
|
||||||
|
|
||||||
if (!q->sound_periodicannounce[0]) {
|
if (!q->sound_periodicannounce[0]) {
|
||||||
|
@ -3437,6 +3462,8 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
|
||||||
ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
|
ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
|
||||||
q->numperiodicannounce = 1;
|
q->numperiodicannounce = 1;
|
||||||
}
|
}
|
||||||
|
} else if (!strcasecmp(param, "periodic-announce-startdelay")) {
|
||||||
|
q->periodicannouncestartdelay = atoi(val);
|
||||||
} else if (!strcasecmp(param, "periodic-announce-frequency")) {
|
} else if (!strcasecmp(param, "periodic-announce-frequency")) {
|
||||||
q->periodicannouncefrequency = atoi(val);
|
q->periodicannouncefrequency = atoi(val);
|
||||||
} else if (!strcasecmp(param, "relative-periodic-announce")) {
|
} else if (!strcasecmp(param, "relative-periodic-announce")) {
|
||||||
|
@ -3587,6 +3614,7 @@ 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 *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
|
||||||
const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
|
const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
|
||||||
const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
|
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)) {
|
if (ast_strlen_zero(rt_uniqueid)) {
|
||||||
ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
|
ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
|
||||||
|
@ -3653,6 +3681,9 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
|
||||||
m->penalty = penalty;
|
m->penalty = penalty;
|
||||||
m->ringinuse = ringinuse;
|
m->ringinuse = ringinuse;
|
||||||
m->wrapuptime = wrapuptime;
|
m->wrapuptime = wrapuptime;
|
||||||
|
if (realtime_reason_paused) {
|
||||||
|
ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
|
||||||
|
}
|
||||||
found = 1;
|
found = 1;
|
||||||
ao2_ref(m, -1);
|
ao2_ref(m, -1);
|
||||||
break;
|
break;
|
||||||
|
@ -3667,6 +3698,9 @@ static void rt_handle_member_record(struct call_queue *q, char *category, struct
|
||||||
m->dead = 0;
|
m->dead = 0;
|
||||||
m->realtime = 1;
|
m->realtime = 1;
|
||||||
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
|
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) {
|
if (!log_membername_as_agent) {
|
||||||
ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
|
ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
|
||||||
} else {
|
} else {
|
||||||
|
@ -4560,6 +4594,56 @@ static int compare_weight(struct call_queue *rq, struct member *member)
|
||||||
return found;
|
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 */
|
/*! \brief common hangup actions */
|
||||||
static void do_hang(struct callattempt *o)
|
static void do_hang(struct callattempt *o)
|
||||||
{
|
{
|
||||||
|
@ -4624,6 +4708,12 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
|
||||||
return 0;
|
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) {
|
if (!memberp->ringinuse) {
|
||||||
struct member *mem;
|
struct member *mem;
|
||||||
|
|
||||||
|
@ -7161,7 +7251,7 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
|
||||||
if (!res2 && announce) {
|
if (!res2 && announce) {
|
||||||
char *front;
|
char *front;
|
||||||
char *announcefiles = ast_strdupa(announce);
|
char *announcefiles = ast_strdupa(announce);
|
||||||
while ((front = strsep(&announcefiles, "&"))) {
|
while ((front = ast_strsep(&announcefiles, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||||
if (play_file(peer, front) < 0) {
|
if (play_file(peer, front) < 0) {
|
||||||
ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", front, ast_channel_name(peer));
|
ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", front, ast_channel_name(peer));
|
||||||
}
|
}
|
||||||
|
@ -7637,10 +7727,10 @@ static int add_to_queue(const char *queuename, const char *interface, const char
|
||||||
* \retval RES_OKAY change priority
|
* \retval RES_OKAY change priority
|
||||||
* \retval RES_NOT_CALLER queue exists but no caller
|
* \retval RES_NOT_CALLER queue exists but no caller
|
||||||
*/
|
*/
|
||||||
static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority)
|
static int change_priority_caller_on_queue(const char *queuename, const char *caller, int priority, int immediate)
|
||||||
{
|
{
|
||||||
struct call_queue *q;
|
struct call_queue *q;
|
||||||
struct queue_ent *qe;
|
struct queue_ent *current, *prev = NULL, *caller_qe = NULL;
|
||||||
int res = RES_NOSUCHQUEUE;
|
int res = RES_NOSUCHQUEUE;
|
||||||
|
|
||||||
/*! \note Ensure the appropriate realtime queue is loaded. Note that this
|
/*! \note Ensure the appropriate realtime queue is loaded. Note that this
|
||||||
|
@ -7651,14 +7741,57 @@ static int change_priority_caller_on_queue(const char *queuename, const char *ca
|
||||||
|
|
||||||
ao2_lock(q);
|
ao2_lock(q);
|
||||||
res = RES_NOT_CALLER;
|
res = RES_NOT_CALLER;
|
||||||
for (qe = q->head; qe; qe = qe->next) {
|
for (current = q->head; current; current = current->next) {
|
||||||
if (strcmp(ast_channel_name(qe->chan), caller) == 0) {
|
if (strcmp(ast_channel_name(current->chan), caller) == 0) {
|
||||||
ast_debug(1, "%s Caller new priority %d in queue %s\n",
|
ast_debug(1, "%s Caller new priority %d in queue %s\n",
|
||||||
caller, priority, queuename);
|
caller, priority, queuename);
|
||||||
qe->prio = priority;
|
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
|
||||||
|
*/
|
||||||
|
}
|
||||||
res = RES_OKAY;
|
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);
|
ao2_unlock(q);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -7740,10 +7873,17 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con
|
||||||
(paused ? "" : "un"), (paused ? "" : "un"), q->name, mem->interface);
|
(paused ? "" : "un"), (paused ? "" : "un"), q->name, mem->interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mem->realtime) {
|
if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid)) {
|
||||||
if (update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0")) {
|
if (realtime_reason_paused) {
|
||||||
ast_log(LOG_WARNING, "Failed %spause update of realtime queue member %s:%s\n",
|
if (ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, "reason_paused", S_OR(reason, ""), "paused", paused ? "1" : "0", SENTINEL) < 0) {
|
||||||
(paused ? "" : "un"), q->name, mem->interface);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7754,6 +7894,9 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con
|
||||||
if (paused && !ast_strlen_zero(reason)) {
|
if (paused && !ast_strlen_zero(reason)) {
|
||||||
ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
|
ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused));
|
||||||
} else {
|
} 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';
|
mem->reason_paused[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7772,10 +7915,22 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con
|
||||||
"Queue:%s_avail", q->name);
|
"Queue:%s_avail", q->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"),
|
if (!paused && !ast_strlen_zero(reason)) {
|
||||||
"%s", S_OR(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);
|
||||||
|
|
||||||
publish_queue_member_pause(q, mem);
|
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)
|
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused)
|
||||||
|
@ -8172,7 +8327,7 @@ static int pqm_exec(struct ast_channel *chan, const char *data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief UnPauseQueueMember application */
|
/*! \brief UnpauseQueueMember application */
|
||||||
static int upqm_exec(struct ast_channel *chan, const char *data)
|
static int upqm_exec(struct ast_channel *chan, const char *data)
|
||||||
{
|
{
|
||||||
char *parse;
|
char *parse;
|
||||||
|
@ -8193,7 +8348,7 @@ static int upqm_exec(struct ast_channel *chan, const char *data)
|
||||||
AST_STANDARD_APP_ARGS(args, parse);
|
AST_STANDARD_APP_ARGS(args, parse);
|
||||||
|
|
||||||
if (ast_strlen_zero(args.interface)) {
|
if (ast_strlen_zero(args.interface)) {
|
||||||
ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
|
ast_log(LOG_WARNING, "Missing interface argument to UnpauseQueueMember ([queuename],interface[,options[,reason]])\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8614,6 +8769,11 @@ static int queue_exec(struct ast_channel *chan, const char *data)
|
||||||
}
|
}
|
||||||
ast_assert(qe.parent != NULL);
|
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",
|
ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
|
||||||
S_OR(args.url, ""),
|
S_OR(args.url, ""),
|
||||||
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
|
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
|
||||||
|
@ -9498,6 +9658,7 @@ static void queue_reset_global_params(void)
|
||||||
shared_lastcall = 0;
|
shared_lastcall = 0;
|
||||||
negative_penalty_invalid = 0;
|
negative_penalty_invalid = 0;
|
||||||
log_membername_as_agent = 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 */
|
/*! Set the global queue parameters as defined in the "general" section of queues.conf */
|
||||||
|
@ -9523,6 +9684,9 @@ static void queue_set_global_params(struct ast_config *cfg)
|
||||||
if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
|
if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
|
||||||
log_membername_as_agent = ast_true(general_val);
|
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
|
/*! \brief reload information pertaining to a single member
|
||||||
|
@ -10824,12 +10988,13 @@ 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)
|
static int manager_change_priority_caller_on_queue(struct mansession *s, const struct message *m)
|
||||||
{
|
{
|
||||||
const char *queuename, *caller, *priority_s;
|
const char *queuename, *caller, *priority_s, *immediate_s;
|
||||||
int priority = 0;
|
int priority = 0, immediate = 0;
|
||||||
|
|
||||||
queuename = astman_get_header(m, "Queue");
|
queuename = astman_get_header(m, "Queue");
|
||||||
caller = astman_get_header(m, "Caller");
|
caller = astman_get_header(m, "Caller");
|
||||||
priority_s = astman_get_header(m, "Priority");
|
priority_s = astman_get_header(m, "Priority");
|
||||||
|
immediate_s = astman_get_header(m, "Immediate");
|
||||||
|
|
||||||
if (ast_strlen_zero(queuename)) {
|
if (ast_strlen_zero(queuename)) {
|
||||||
astman_send_error(s, m, "'Queue' not specified.");
|
astman_send_error(s, m, "'Queue' not specified.");
|
||||||
|
@ -10849,7 +11014,11 @@ static int manager_change_priority_caller_on_queue(struct mansession *s, const s
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (change_priority_caller_on_queue(queuename, caller, priority)) {
|
if (!ast_strlen_zero(immediate_s)) {
|
||||||
|
immediate = ast_true(immediate_s);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
|
||||||
case RES_OKAY:
|
case RES_OKAY:
|
||||||
astman_send_ack(s, m, "Priority change for caller on queue");
|
astman_send_ack(s, m, "Priority change for caller on queue");
|
||||||
break;
|
break;
|
||||||
|
@ -10910,7 +11079,7 @@ static char *handle_queue_add_member(struct ast_cli_entry *e, int cmd, struct as
|
||||||
case CLI_INIT:
|
case CLI_INIT:
|
||||||
e->command = "queue add member";
|
e->command = "queue add member";
|
||||||
e->usage =
|
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";
|
" 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;
|
return NULL;
|
||||||
case CLI_GENERATE:
|
case CLI_GENERATE:
|
||||||
|
@ -11093,21 +11262,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)
|
static char *handle_queue_change_priority_caller(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||||
{
|
{
|
||||||
const char *queuename, *caller;
|
const char *queuename, *caller;
|
||||||
int priority;
|
int priority, immediate = 0;
|
||||||
char *res = CLI_FAILURE;
|
char *res = CLI_FAILURE;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CLI_INIT:
|
case CLI_INIT:
|
||||||
e->command = "queue priority caller";
|
e->command = "queue priority caller";
|
||||||
e->usage =
|
e->usage =
|
||||||
"Usage: queue priority caller <channel> on <queue> to <priority>\n"
|
"Usage: queue priority caller <channel> on <queue> to <priority> [immediate]\n"
|
||||||
" Change the priority of a channel on a queue.\n";
|
" Change the priority of a channel on a queue, optionally applying the change in relation to existing callers.\n";
|
||||||
return NULL;
|
return NULL;
|
||||||
case CLI_GENERATE:
|
case CLI_GENERATE:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a->argc != 8) {
|
if (a->argc < 8) {
|
||||||
return CLI_SHOWUSAGE;
|
return CLI_SHOWUSAGE;
|
||||||
} else if (strcmp(a->argv[4], "on")) {
|
} else if (strcmp(a->argv[4], "on")) {
|
||||||
return CLI_SHOWUSAGE;
|
return CLI_SHOWUSAGE;
|
||||||
|
@ -11116,12 +11285,17 @@ static char *handle_queue_change_priority_caller(struct ast_cli_entry *e, int cm
|
||||||
} else if (sscanf(a->argv[7], "%30d", &priority) != 1) {
|
} else if (sscanf(a->argv[7], "%30d", &priority) != 1) {
|
||||||
ast_log (LOG_ERROR, "<priority> parameter must be an integer.\n");
|
ast_log (LOG_ERROR, "<priority> parameter must be an integer.\n");
|
||||||
return CLI_SHOWUSAGE;
|
return CLI_SHOWUSAGE;
|
||||||
|
} else if (a->argc == 9) {
|
||||||
|
if (strcmp(a->argv[8], "immediate")) {
|
||||||
|
return CLI_SHOWUSAGE;
|
||||||
|
}
|
||||||
|
immediate = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
caller = a->argv[3];
|
caller = a->argv[3];
|
||||||
queuename = a->argv[5];
|
queuename = a->argv[5];
|
||||||
|
|
||||||
switch (change_priority_caller_on_queue(queuename, caller, priority)) {
|
switch (change_priority_caller_on_queue(queuename, caller, priority, immediate)) {
|
||||||
case RES_OKAY:
|
case RES_OKAY:
|
||||||
res = CLI_SUCCESS;
|
res = CLI_SUCCESS;
|
||||||
break;
|
break;
|
||||||
|
@ -11693,11 +11867,13 @@ static int load_module(void)
|
||||||
return AST_MODULE_LOAD_DECLINE;
|
return AST_MODULE_LOAD_DECLINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
|
ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, "reason_paused", RQ_CHAR, 80, SENTINEL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This section is used to determine which name for 'ringinuse' to use in realtime members
|
* This section is used to determine which name for 'ringinuse' to use in realtime members
|
||||||
* Necessary for supporting older setups.
|
* 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);
|
member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name LIKE", "%", SENTINEL);
|
||||||
if (!member_config) {
|
if (!member_config) {
|
||||||
|
@ -11715,6 +11891,10 @@ static int load_module(void)
|
||||||
ast_log(LOG_NOTICE, "No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
|
ast_log(LOG_NOTICE, "No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
|
||||||
realtime_ringinuse_field = "ringinuse";
|
realtime_ringinuse_field = "ringinuse";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ast_variable_retrieve(member_config, NULL, "reason_paused")) {
|
||||||
|
realtime_reason_paused = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast_config_destroy(member_config);
|
ast_config_destroy(member_config);
|
||||||
|
|
||||||
|
|
|
@ -49,9 +49,15 @@
|
||||||
name.</para>
|
name.</para>
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="filenames" argsep="&">
|
<parameter name="filenames" argsep="&">
|
||||||
<argument name="filename" required="true">
|
<para>Ampersand separated list of filenames to play before
|
||||||
<para>file(s) to play before reading digits or tone with option i</para>
|
reading digits or tone with option <literal>i</literal>. If
|
||||||
</argument>
|
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" />
|
<argument name="filename2" multiple="true" />
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="maxdigits">
|
<parameter name="maxdigits">
|
||||||
|
@ -85,6 +91,13 @@
|
||||||
and you will need to rely on duration and max digits
|
and you will need to rely on duration and max digits
|
||||||
for ending input.</para>
|
for ending input.</para>
|
||||||
</option>
|
</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>
|
</optionlist>
|
||||||
</parameter>
|
</parameter>
|
||||||
<parameter name="attempts">
|
<parameter name="attempts">
|
||||||
|
@ -125,6 +138,7 @@ enum read_option_flags {
|
||||||
OPT_INDICATION = (1 << 1),
|
OPT_INDICATION = (1 << 1),
|
||||||
OPT_NOANSWER = (1 << 2),
|
OPT_NOANSWER = (1 << 2),
|
||||||
OPT_TERMINATOR = (1 << 3),
|
OPT_TERMINATOR = (1 << 3),
|
||||||
|
OPT_KEEP_TERMINATOR = (1 << 4),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -138,6 +152,7 @@ AST_APP_OPTIONS(read_app_options, {
|
||||||
AST_APP_OPTION('i', OPT_INDICATION),
|
AST_APP_OPTION('i', OPT_INDICATION),
|
||||||
AST_APP_OPTION('n', OPT_NOANSWER),
|
AST_APP_OPTION('n', OPT_NOANSWER),
|
||||||
AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
|
AST_APP_OPTION_ARG('t', OPT_TERMINATOR, OPT_ARG_TERMINATOR),
|
||||||
|
AST_APP_OPTION('e', OPT_KEEP_TERMINATOR),
|
||||||
});
|
});
|
||||||
|
|
||||||
static char *app = "Read";
|
static char *app = "Read";
|
||||||
|
@ -261,12 +276,20 @@ static int read_exec(struct ast_channel *chan, const char *data)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
|
res = ast_app_getdata_terminator(chan, arglist.filename, tmp, maxdigits, to, terminator);
|
||||||
if (res == AST_GETDATA_COMPLETE || res == AST_GETDATA_EMPTY_END_TERMINATED)
|
if (res == AST_GETDATA_COMPLETE) {
|
||||||
status = "OK";
|
status = "OK";
|
||||||
else if (res == AST_GETDATA_TIMEOUT)
|
} 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) {
|
||||||
status = "TIMEOUT";
|
status = "TIMEOUT";
|
||||||
else if (res == AST_GETDATA_INTERRUPTED)
|
} else if (res == AST_GETDATA_INTERRUPTED) {
|
||||||
status = "INTERRUPTED";
|
status = "INTERRUPTED";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (res > -1) {
|
if (res > -1) {
|
||||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,15 @@
|
||||||
<parameter name="channel" required="false">
|
<parameter name="channel" required="false">
|
||||||
<para>Channel where digits will be played</para>
|
<para>Channel where digits will be played</para>
|
||||||
</parameter>
|
</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>
|
</syntax>
|
||||||
<description>
|
<description>
|
||||||
<para>It will send all digits or terminate if it encounters an error.</para>
|
<para>It will send all digits or terminate if it encounters an error.</para>
|
||||||
|
@ -88,8 +97,38 @@
|
||||||
<para>Plays a dtmf digit on the specified channel.</para>
|
<para>Plays a dtmf digit on the specified channel.</para>
|
||||||
</description>
|
</description>
|
||||||
</manager>
|
</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 const char senddtmf_name[] = "SendDTMF";
|
||||||
|
|
||||||
static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
||||||
|
@ -100,11 +139,14 @@ static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
||||||
struct ast_channel *chan_found = NULL;
|
struct ast_channel *chan_found = NULL;
|
||||||
struct ast_channel *chan_dest = chan;
|
struct ast_channel *chan_dest = chan;
|
||||||
struct ast_channel *chan_autoservice = NULL;
|
struct ast_channel *chan_autoservice = NULL;
|
||||||
|
char *opt_args[OPT_ARG_ARRAY_SIZE];
|
||||||
|
struct ast_flags flags = {0};
|
||||||
AST_DECLARE_APP_ARGS(args,
|
AST_DECLARE_APP_ARGS(args,
|
||||||
AST_APP_ARG(digits);
|
AST_APP_ARG(digits);
|
||||||
AST_APP_ARG(dinterval);
|
AST_APP_ARG(dinterval);
|
||||||
AST_APP_ARG(duration);
|
AST_APP_ARG(duration);
|
||||||
AST_APP_ARG(channel);
|
AST_APP_ARG(channel);
|
||||||
|
AST_APP_ARG(options);
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ast_strlen_zero(vdata)) {
|
if (ast_strlen_zero(vdata)) {
|
||||||
|
@ -136,6 +178,12 @@ static int senddtmf_exec(struct ast_channel *chan, const char *vdata)
|
||||||
chan_autoservice = chan;
|
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,
|
res = ast_dtmf_stream(chan_dest, chan_autoservice, args.digits,
|
||||||
dinterval <= 0 ? 250 : dinterval, duration);
|
dinterval <= 0 ? 250 : dinterval, duration);
|
||||||
if (chan_found) {
|
if (chan_found) {
|
||||||
|
@ -187,12 +235,41 @@ static int manager_play_dtmf(struct mansession *s, const struct message *m)
|
||||||
return 0;
|
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)
|
static int unload_module(void)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
res = ast_unregister_application(senddtmf_name);
|
res = ast_unregister_application(senddtmf_name);
|
||||||
res |= ast_manager_unregister("PlayDTMF");
|
res |= ast_manager_unregister("PlayDTMF");
|
||||||
|
res |= ast_manager_unregister("SendFlash");
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -202,6 +279,7 @@ static int load_module(void)
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
res = ast_manager_register_xml("PlayDTMF", EVENT_FLAG_CALL, manager_play_dtmf);
|
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);
|
res |= ast_register_application_xml(senddtmf_name, senddtmf_exec);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -139,8 +139,6 @@
|
||||||
</example>
|
</example>
|
||||||
</description>
|
</description>
|
||||||
<see-also>
|
<see-also>
|
||||||
<ref type="application">SendImage</ref>
|
|
||||||
<ref type="application">SendURL</ref>
|
|
||||||
<ref type="application">ReceiveText</ref>
|
<ref type="application">ReceiveText</ref>
|
||||||
</see-also>
|
</see-also>
|
||||||
</application>
|
</application>
|
||||||
|
@ -182,8 +180,6 @@
|
||||||
</description>
|
</description>
|
||||||
<see-also>
|
<see-also>
|
||||||
<ref type="application">SendText</ref>
|
<ref type="application">SendText</ref>
|
||||||
<ref type="application">SendImage</ref>
|
|
||||||
<ref type="application">SendURL</ref>
|
|
||||||
</see-also>
|
</see-also>
|
||||||
</application>
|
</application>
|
||||||
***/
|
***/
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,472 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
|
<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.
|
* at the top of the source tree.
|
||||||
*
|
*
|
||||||
* Please follow coding guidelines
|
* Please follow coding guidelines
|
||||||
* https://wiki.asterisk.org/wiki/display/AST/Coding+Guidelines
|
* https://docs.asterisk.org/Development/Policies-and-Procedures/Coding-Guidelines/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
@ -371,7 +371,8 @@ static void play_files_helper(struct ast_channel *chan, const char *prompts)
|
||||||
char *prompt, *rest = ast_strdupa(prompts);
|
char *prompt, *rest = ast_strdupa(prompts);
|
||||||
|
|
||||||
ast_stopstream(chan);
|
ast_stopstream(chan);
|
||||||
while ((prompt = strsep(&rest, "&")) && !ast_stream_and_wait(chan, prompt, "")) {
|
while ((prompt = ast_strsep(&rest, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))
|
||||||
|
&& !ast_stream_and_wait(chan, prompt, "")) {
|
||||||
ast_stopstream(chan);
|
ast_stopstream(chan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,17 @@
|
||||||
Play a sound file and wait for speech to be recognized.
|
Play a sound file and wait for speech to be recognized.
|
||||||
</synopsis>
|
</synopsis>
|
||||||
<syntax>
|
<syntax>
|
||||||
<parameter name="sound_file" required="true" />
|
<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="timeout">
|
<parameter name="timeout">
|
||||||
<para>Timeout integer in seconds. Note the timeout will only start
|
<para>Timeout integer in seconds. Note the timeout will only start
|
||||||
once the sound file has stopped playing.</para>
|
once the sound file has stopped playing.</para>
|
||||||
|
@ -95,6 +105,9 @@
|
||||||
<option name="n">
|
<option name="n">
|
||||||
<para>Don't answer the channel if it has not already been answered.</para>
|
<para>Don't answer the channel if it has not already been answered.</para>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="p">
|
||||||
|
<para>Return partial results when backend is terminated by timeout.</para>
|
||||||
|
</option>
|
||||||
</optionlist>
|
</optionlist>
|
||||||
</parameter>
|
</parameter>
|
||||||
</syntax>
|
</syntax>
|
||||||
|
@ -690,10 +703,12 @@ static int speech_streamfile(struct ast_channel *chan, const char *filename, con
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
SB_OPT_NOANSWER = (1 << 0),
|
SB_OPT_NOANSWER = (1 << 0),
|
||||||
|
SB_OPT_PARTIALRESULTS = (1 << 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
AST_APP_OPTIONS(speech_background_options, BEGIN_OPTIONS
|
AST_APP_OPTIONS(speech_background_options, BEGIN_OPTIONS
|
||||||
AST_APP_OPTION('n', SB_OPT_NOANSWER),
|
AST_APP_OPTION('n', SB_OPT_NOANSWER),
|
||||||
|
AST_APP_OPTION('p', SB_OPT_PARTIALRESULTS),
|
||||||
END_OPTIONS );
|
END_OPTIONS );
|
||||||
|
|
||||||
/*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
|
/*! \brief SpeechBackground(Sound File,Timeout) Dialplan Application */
|
||||||
|
@ -776,7 +791,10 @@ static int speech_background(struct ast_channel *chan, const char *data)
|
||||||
/* Okay it's streaming so go into a loop grabbing frames! */
|
/* Okay it's streaming so go into a loop grabbing frames! */
|
||||||
while (done == 0) {
|
while (done == 0) {
|
||||||
/* If the filename is null and stream is not running, start up a new sound file */
|
/* 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 = strsep(&filename_tmp, "&"))) {
|
if (!quieted
|
||||||
|
&& ast_channel_streamid(chan) == -1
|
||||||
|
&& ast_channel_timingfunc(chan) == NULL
|
||||||
|
&& (filename = ast_strsep(&filename_tmp, '&', AST_STRSEP_STRIP | AST_STRSEP_TRIM))) {
|
||||||
/* Discard old stream information */
|
/* Discard old stream information */
|
||||||
ast_stopstream(chan);
|
ast_stopstream(chan);
|
||||||
/* Start new stream */
|
/* Start new stream */
|
||||||
|
@ -920,7 +938,10 @@ static int speech_background(struct ast_channel *chan, const char *data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ast_strlen_zero(dtmf)) {
|
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)) {
|
||||||
/* We sort of make a results entry */
|
/* We sort of make a results entry */
|
||||||
speech->results = ast_calloc(1, sizeof(*speech->results));
|
speech->results = ast_calloc(1, sizeof(*speech->results));
|
||||||
if (speech->results != NULL) {
|
if (speech->results != NULL) {
|
||||||
|
|
|
@ -1076,7 +1076,7 @@ static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_
|
||||||
ast_channel_name(chan), app_gosub, sub_args,
|
ast_channel_name(chan), app_gosub, sub_args,
|
||||||
S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
|
S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
|
||||||
} else {
|
} else {
|
||||||
ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
|
ast_log(LOG_WARNING, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
|
||||||
ast_channel_name(chan), app_gosub, sub_args);
|
ast_channel_name(chan), app_gosub, sub_args);
|
||||||
balance_stack(chan);
|
balance_stack(chan);
|
||||||
pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
|
pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,9 @@
|
||||||
<configOption name="end_marked">
|
<configOption name="end_marked">
|
||||||
<synopsis>Kick the user from the conference when the last marked user leaves</synopsis>
|
<synopsis>Kick the user from the conference when the last marked user leaves</synopsis>
|
||||||
</configOption>
|
</configOption>
|
||||||
|
<configOption name="end_marked_any">
|
||||||
|
<synopsis>Kick the user from the conference when any marked user leaves</synopsis>
|
||||||
|
</configOption>
|
||||||
<configOption name="talk_detection_events">
|
<configOption name="talk_detection_events">
|
||||||
<synopsis>Set whether or not notifications of when a user begins and ends talking should be sent out as events over AMI</synopsis>
|
<synopsis>Set whether or not notifications of when a user begins and ends talking should be sent out as events over AMI</synopsis>
|
||||||
</configOption>
|
</configOption>
|
||||||
|
@ -1440,10 +1443,7 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
|
||||||
|
|
||||||
/* if adding any of the actions failed, bail */
|
/* if adding any of the actions failed, bail */
|
||||||
if (res) {
|
if (res) {
|
||||||
struct conf_menu_action *menu_action;
|
conf_menu_entry_destroy(menu_entry);
|
||||||
while ((menu_action = AST_LIST_REMOVE_HEAD(&menu_entry->actions, action))) {
|
|
||||||
ast_free(menu_action);
|
|
||||||
}
|
|
||||||
ast_free(menu_entry);
|
ast_free(menu_entry);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -1452,6 +1452,7 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
|
||||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&menu->entries, cur, entry) {
|
||||||
if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
|
if (!strcasecmp(cur->dtmf, menu_entry->dtmf)) {
|
||||||
AST_LIST_REMOVE_CURRENT(entry);
|
AST_LIST_REMOVE_CURRENT(entry);
|
||||||
|
conf_menu_entry_destroy(cur);
|
||||||
ast_free(cur);
|
ast_free(cur);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1583,9 +1584,12 @@ static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, in
|
||||||
ast_cli(a->fd,"Wait Marked: %s\n",
|
ast_cli(a->fd,"Wait Marked: %s\n",
|
||||||
u_profile.flags & USER_OPT_WAITMARKED ?
|
u_profile.flags & USER_OPT_WAITMARKED ?
|
||||||
"enabled" : "disabled");
|
"enabled" : "disabled");
|
||||||
ast_cli(a->fd,"END Marked: %s\n",
|
ast_cli(a->fd,"END Marked (All): %s\n",
|
||||||
u_profile.flags & USER_OPT_ENDMARKED ?
|
u_profile.flags & USER_OPT_ENDMARKED ?
|
||||||
"enabled" : "disabled");
|
"enabled" : "disabled");
|
||||||
|
ast_cli(a->fd,"END Marked (Any): %s\n",
|
||||||
|
u_profile.flags & USER_OPT_ENDMARKEDANY ?
|
||||||
|
"enabled" : "disabled");
|
||||||
ast_cli(a->fd,"Drop_silence: %s\n",
|
ast_cli(a->fd,"Drop_silence: %s\n",
|
||||||
u_profile.flags & USER_OPT_DROP_SILENCE ?
|
u_profile.flags & USER_OPT_DROP_SILENCE ?
|
||||||
"enabled" : "disabled");
|
"enabled" : "disabled");
|
||||||
|
@ -2216,6 +2220,30 @@ 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;
|
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)
|
static int bridge_template_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||||
{
|
{
|
||||||
struct bridge_profile *b_profile = obj;
|
struct bridge_profile *b_profile = obj;
|
||||||
|
@ -2409,6 +2437,7 @@ int conf_load_config(void)
|
||||||
aco_option_register(&cfg_info, "announce_only_user", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 0, FLDSET(struct user_profile, flags), USER_OPT_NOONLYPERSON);
|
aco_option_register(&cfg_info, "announce_only_user", ACO_EXACT, user_types, "yes", OPT_BOOLFLAG_T, 0, FLDSET(struct user_profile, flags), USER_OPT_NOONLYPERSON);
|
||||||
aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
|
aco_option_register(&cfg_info, "wait_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_WAITMARKED);
|
||||||
aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
|
aco_option_register(&cfg_info, "end_marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKED);
|
||||||
|
aco_option_register(&cfg_info, "end_marked_any", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ENDMARKEDANY);
|
||||||
aco_option_register(&cfg_info, "talk_detection_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_TALKER_DETECT);
|
aco_option_register(&cfg_info, "talk_detection_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_TALKER_DETECT);
|
||||||
aco_option_register(&cfg_info, "dtmf_passthrough", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DTMF_PASS);
|
aco_option_register(&cfg_info, "dtmf_passthrough", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_DTMF_PASS);
|
||||||
aco_option_register(&cfg_info, "announce_join_leave", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCE_JOIN_LEAVE);
|
aco_option_register(&cfg_info, "announce_join_leave", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ANNOUNCE_JOIN_LEAVE);
|
||||||
|
@ -2432,10 +2461,9 @@ int conf_load_config(void)
|
||||||
/* Bridge options */
|
/* Bridge options */
|
||||||
aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
|
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(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);
|
||||||
/* "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_custom(&cfg_info, "internal_sample_rate", ACO_EXACT, bridge_types, "auto", sample_rate_handler, 0);
|
||||||
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(&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(&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, "maximum_sample_rate", ACO_EXACT, bridge_types, "none", sample_rate_handler, 0);
|
||||||
aco_option_register_custom(&cfg_info, "mixing_interval", ACO_EXACT, bridge_types, "20", mix_interval_handler, 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(&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);
|
aco_option_register_custom(&cfg_info, "video_mode", ACO_EXACT, bridge_types, NULL, video_mode_handler, 0);
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
* This program is free software, distributed under the terms of
|
* This program is free software, distributed under the terms of
|
||||||
* the GNU General Public License Version 2. See the LICENSE file
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
* at the top of the source tree.
|
* at the top of the source tree.
|
||||||
*
|
|
||||||
* Please follow coding guidelines
|
|
||||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
* This program is free software, distributed under the terms of
|
* This program is free software, distributed under the terms of
|
||||||
* the GNU General Public License Version 2. See the LICENSE file
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
* at the top of the source tree.
|
* at the top of the source tree.
|
||||||
*
|
|
||||||
* Please follow coding guidelines
|
|
||||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
* This program is free software, distributed under the terms of
|
* This program is free software, distributed under the terms of
|
||||||
* the GNU General Public License Version 2. See the LICENSE file
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
* at the top of the source tree.
|
* at the top of the source tree.
|
||||||
*
|
|
||||||
* Please follow coding guidelines
|
|
||||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
* This program is free software, distributed under the terms of
|
* This program is free software, distributed under the terms of
|
||||||
* the GNU General Public License Version 2. See the LICENSE file
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
* at the top of the source tree.
|
* at the top of the source tree.
|
||||||
*
|
|
||||||
* Please follow coding guidelines
|
|
||||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
* This program is free software, distributed under the terms of
|
* This program is free software, distributed under the terms of
|
||||||
* the GNU General Public License Version 2. See the LICENSE file
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
* at the top of the source tree.
|
* at the top of the source tree.
|
||||||
*
|
|
||||||
* Please follow coding guidelines
|
|
||||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
@ -85,37 +82,39 @@ static void leave_marked(struct confbridge_user *user)
|
||||||
|
|
||||||
conf_remove_user_marked(user->conference, user);
|
conf_remove_user_marked(user->conference, user);
|
||||||
|
|
||||||
if (user->conference->markedusers == 0) {
|
/* If all marked users have left, or we're set to kick if any marked user leaves, then boot everyone */
|
||||||
AST_LIST_TRAVERSE_SAFE_BEGIN(&user->conference->active_list, user_iter, list) {
|
AST_LIST_TRAVERSE_SAFE_BEGIN(&user->conference->active_list, user_iter, list) {
|
||||||
/* Kick ENDMARKED cbu_iters */
|
if (user->conference->markedusers > 0 && !ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY)) {
|
||||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKED) && !user_iter->kicked) {
|
continue;
|
||||||
if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
}
|
||||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
/* Kick ENDMARKED cbu_iters */
|
||||||
AST_LIST_REMOVE_CURRENT(list);
|
if ((ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKED) || ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY)) && !user_iter->kicked) {
|
||||||
user_iter->conference->activeusers--;
|
if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
&& (!ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER) || ast_test_flag(&user_iter->u_profile, USER_OPT_ENDMARKEDANY))) {
|
||||||
user_iter->conference->waitingusers++;
|
|
||||||
}
|
|
||||||
user_iter->kicked = 1;
|
|
||||||
pbx_builtin_setvar_helper(user_iter->chan, "CONFBRIDGE_RESULT", "ENDMARKED");
|
|
||||||
ast_bridge_remove(user_iter->conference->bridge, user_iter->chan);
|
|
||||||
} else if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
|
||||||
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
|
||||||
need_prompt = 1;
|
|
||||||
|
|
||||||
AST_LIST_REMOVE_CURRENT(list);
|
AST_LIST_REMOVE_CURRENT(list);
|
||||||
user_iter->conference->activeusers--;
|
user_iter->conference->activeusers--;
|
||||||
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||||
user_iter->conference->waitingusers++;
|
user_iter->conference->waitingusers++;
|
||||||
} else {
|
|
||||||
/* User is neither wait_marked nor end_marked; however, they
|
|
||||||
* should still hear the prompt.
|
|
||||||
*/
|
|
||||||
need_prompt = 1;
|
|
||||||
}
|
}
|
||||||
|
user_iter->kicked = 1;
|
||||||
|
pbx_builtin_setvar_helper(user_iter->chan, "CONFBRIDGE_RESULT", "ENDMARKED");
|
||||||
|
ast_bridge_remove(user_iter->conference->bridge, user_iter->chan);
|
||||||
|
} else if (ast_test_flag(&user_iter->u_profile, USER_OPT_WAITMARKED)
|
||||||
|
&& !ast_test_flag(&user_iter->u_profile, USER_OPT_MARKEDUSER)) {
|
||||||
|
need_prompt = 1;
|
||||||
|
|
||||||
|
AST_LIST_REMOVE_CURRENT(list);
|
||||||
|
user_iter->conference->activeusers--;
|
||||||
|
AST_LIST_INSERT_TAIL(&user_iter->conference->waiting_list, user_iter, list);
|
||||||
|
user_iter->conference->waitingusers++;
|
||||||
|
} else {
|
||||||
|
/* User is neither wait_marked nor end_marked nor end_marked_any; however, they
|
||||||
|
* should still hear the prompt.
|
||||||
|
*/
|
||||||
|
need_prompt = 1;
|
||||||
}
|
}
|
||||||
AST_LIST_TRAVERSE_SAFE_END;
|
|
||||||
}
|
}
|
||||||
|
AST_LIST_TRAVERSE_SAFE_END;
|
||||||
|
|
||||||
switch (user->conference->activeusers) {
|
switch (user->conference->activeusers) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
* This program is free software, distributed under the terms of
|
* This program is free software, distributed under the terms of
|
||||||
* the GNU General Public License Version 2. See the LICENSE file
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
* at the top of the source tree.
|
* at the top of the source tree.
|
||||||
*
|
|
||||||
* Please follow coding guidelines
|
|
||||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
* This program is free software, distributed under the terms of
|
* This program is free software, distributed under the terms of
|
||||||
* the GNU General Public License Version 2. See the LICENSE file
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
* at the top of the source tree.
|
* at the top of the source tree.
|
||||||
*
|
|
||||||
* Please follow coding guidelines
|
|
||||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
|
|
@ -14,9 +14,6 @@
|
||||||
* This program is free software, distributed under the terms of
|
* This program is free software, distributed under the terms of
|
||||||
* the GNU General Public License Version 2. See the LICENSE file
|
* the GNU General Public License Version 2. See the LICENSE file
|
||||||
* at the top of the source tree.
|
* at the top of the source tree.
|
||||||
*
|
|
||||||
* Please follow coding guidelines
|
|
||||||
* http://svn.digium.com/view/asterisk/trunk/doc/CODING-GUIDELINES
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \file
|
/*! \file
|
||||||
|
@ -25,7 +22,7 @@
|
||||||
*
|
*
|
||||||
* \author\verbatim Terry Wilson <twilson@digium.com> \endverbatim
|
* \author\verbatim Terry Wilson <twilson@digium.com> \endverbatim
|
||||||
*
|
*
|
||||||
* See https://wiki.asterisk.org/wiki/display/AST/Confbridge+state+changes for
|
* See https://docs.asterisk.org/Development/Reference-Information/Other-Reference-Information/Confbridge-state-changes/ for
|
||||||
* a more complete description of how conference states work.
|
* a more complete description of how conference states work.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ enum user_profile_flags {
|
||||||
USER_OPT_TEXT_MESSAGING = (1 << 19), /*!< Send text messages to the user */
|
USER_OPT_TEXT_MESSAGING = (1 << 19), /*!< Send text messages to the user */
|
||||||
USER_OPT_ANSWER_CHANNEL = (1 << 20), /*!< Sets if the channel should be answered if currently unanswered */
|
USER_OPT_ANSWER_CHANNEL = (1 << 20), /*!< Sets if the channel should be answered if currently unanswered */
|
||||||
USER_OPT_HEAR_OWN_JOIN_SOUND = (1 << 21), /*!< Set if the caller should hear the join sound */
|
USER_OPT_HEAR_OWN_JOIN_SOUND = (1 << 21), /*!< Set if the caller should hear the join sound */
|
||||||
|
USER_OPT_ENDMARKEDANY = (1 << 22), /*!< Set if the user should be kicked after any marked user exits */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum bridge_profile_flags {
|
enum bridge_profile_flags {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
|
# https://www.gnu.org/software/autoconf-archive/ax_pthread.html
|
||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
#
|
#
|
||||||
# SYNOPSIS
|
# SYNOPSIS
|
||||||
|
@ -14,20 +14,24 @@
|
||||||
# flags that are needed. (The user can also force certain compiler
|
# flags that are needed. (The user can also force certain compiler
|
||||||
# flags/libs to be tested by setting these environment variables.)
|
# flags/libs to be tested by setting these environment variables.)
|
||||||
#
|
#
|
||||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
|
||||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
# needed for multi-threaded programs (defaults to the value of CC
|
||||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
# respectively CXX otherwise). (This is necessary on e.g. AIX to use the
|
||||||
|
# special cc_r/CC_r compiler alias.)
|
||||||
#
|
#
|
||||||
# NOTE: You are assumed to not only compile your program with these flags,
|
# 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
|
# but also to link with them as well. For example, you might link with
|
||||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
# $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
|
# If you are only building threaded programs, you may wish to use these
|
||||||
# variables in your default LIBS, CFLAGS, and CC:
|
# variables in your default LIBS, CFLAGS, and CC:
|
||||||
#
|
#
|
||||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||||
|
# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
|
||||||
# CC="$PTHREAD_CC"
|
# CC="$PTHREAD_CC"
|
||||||
|
# CXX="$PTHREAD_CXX"
|
||||||
#
|
#
|
||||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||||
# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
|
# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
|
||||||
|
@ -55,6 +59,7 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||||
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
|
# 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
|
# 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
|
# under the terms of the GNU General Public License as published by the
|
||||||
|
@ -67,7 +72,7 @@
|
||||||
# Public License for more details.
|
# Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
# with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||||
# gives unlimited permission to copy, distribute and modify the configure
|
# gives unlimited permission to copy, distribute and modify the configure
|
||||||
|
@ -82,7 +87,7 @@
|
||||||
# modified version of the Autoconf Macro, you may extend this special
|
# modified version of the Autoconf Macro, you may extend this special
|
||||||
# exception to the GPL to apply to your modified version as well.
|
# exception to the GPL to apply to your modified version as well.
|
||||||
|
|
||||||
#serial 23
|
#serial 31
|
||||||
|
|
||||||
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
||||||
AC_DEFUN([AX_PTHREAD], [
|
AC_DEFUN([AX_PTHREAD], [
|
||||||
|
@ -104,6 +109,7 @@ if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
|
||||||
ax_pthread_save_CFLAGS="$CFLAGS"
|
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||||
ax_pthread_save_LIBS="$LIBS"
|
ax_pthread_save_LIBS="$LIBS"
|
||||||
AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
|
AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
|
||||||
|
AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
|
||||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||||
LIBS="$PTHREAD_LIBS $LIBS"
|
LIBS="$PTHREAD_LIBS $LIBS"
|
||||||
AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
|
AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
|
||||||
|
@ -123,10 +129,12 @@ fi
|
||||||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||||
# libraries is broken (non-POSIX).
|
# libraries is broken (non-POSIX).
|
||||||
|
|
||||||
# Create a list of thread flags to try. Items starting with a "-" are
|
# Create a list of thread flags to try. Items with a "," contain both
|
||||||
# C compiler flags, and other items are library names, except for "none"
|
# C compiler flags (before ",") and linker flags (after ","). Other items
|
||||||
# which indicates that we try without any flags at all, and "pthread-config"
|
# starting with a "-" are C compiler flags, and remaining items are
|
||||||
# which is a program returning the flags for the Pth emulation library.
|
# 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"
|
ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||||
|
|
||||||
|
@ -194,14 +202,47 @@ case $host_os in
|
||||||
# that too in a future libc.) So we'll check first for the
|
# that too in a future libc.) So we'll check first for the
|
||||||
# standard Solaris way of linking pthreads (-mt -lpthread).
|
# standard Solaris way of linking pthreads (-mt -lpthread).
|
||||||
|
|
||||||
ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
|
ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags"
|
||||||
;;
|
;;
|
||||||
esac
|
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)
|
# 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"],
|
AS_IF([test "x$GCC" = "xyes"],
|
||||||
[ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
|
[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"])
|
||||||
|
|
||||||
|
|
||||||
# The presence of a feature test macro requesting re-entrant function
|
# The presence of a feature test macro requesting re-entrant function
|
||||||
# definitions is, on some systems, a strong hint that pthreads support is
|
# definitions is, on some systems, a strong hint that pthreads support is
|
||||||
|
@ -224,101 +265,6 @@ AS_IF([test "x$ax_pthread_check_macro" = "x--"],
|
||||||
[ax_pthread_check_cond=0],
|
[ax_pthread_check_cond=0],
|
||||||
[ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
|
[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
|
if test "x$ax_pthread_ok" = "xno"; then
|
||||||
for ax_pthread_try_flag in $ax_pthread_flags; do
|
for ax_pthread_try_flag in $ax_pthread_flags; do
|
||||||
|
@ -328,10 +274,10 @@ for ax_pthread_try_flag in $ax_pthread_flags; do
|
||||||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-mt,pthread)
|
*,*)
|
||||||
AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
|
PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"`
|
||||||
PTHREAD_CFLAGS="-mt"
|
PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"`
|
||||||
PTHREAD_LIBS="-lpthread"
|
AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"])
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-*)
|
-*)
|
||||||
|
@ -371,7 +317,13 @@ for ax_pthread_try_flag in $ax_pthread_flags; do
|
||||||
# if $ax_pthread_check_cond
|
# if $ax_pthread_check_cond
|
||||||
# error "$ax_pthread_check_macro must be defined"
|
# error "$ax_pthread_check_macro must be defined"
|
||||||
# endif
|
# endif
|
||||||
static void routine(void *a) { a = 0; }
|
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 *start_routine(void *a) { return a; }],
|
static void *start_routine(void *a) { return a; }],
|
||||||
[pthread_t th; pthread_attr_t attr;
|
[pthread_t th; pthread_attr_t attr;
|
||||||
pthread_create(&th, 0, start_routine, 0);
|
pthread_create(&th, 0, start_routine, 0);
|
||||||
|
@ -393,6 +345,80 @@ for ax_pthread_try_flag in $ax_pthread_flags; do
|
||||||
done
|
done
|
||||||
fi
|
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:
|
# Various other checks:
|
||||||
if test "x$ax_pthread_ok" = "xyes"; then
|
if test "x$ax_pthread_ok" = "xyes"; then
|
||||||
ax_pthread_save_CFLAGS="$CFLAGS"
|
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||||
|
@ -438,7 +464,8 @@ if test "x$ax_pthread_ok" = "xyes"; then
|
||||||
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
|
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
|
||||||
[ax_cv_PTHREAD_PRIO_INHERIT],
|
[ax_cv_PTHREAD_PRIO_INHERIT],
|
||||||
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
|
||||||
[[int i = PTHREAD_PRIO_INHERIT;]])],
|
[[int i = PTHREAD_PRIO_INHERIT;
|
||||||
|
return i;]])],
|
||||||
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
|
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
|
||||||
[ax_cv_PTHREAD_PRIO_INHERIT=no])
|
[ax_cv_PTHREAD_PRIO_INHERIT=no])
|
||||||
])
|
])
|
||||||
|
@ -460,18 +487,28 @@ if test "x$ax_pthread_ok" = "xyes"; then
|
||||||
[#handle absolute path differently from PATH based program lookup
|
[#handle absolute path differently from PATH based program lookup
|
||||||
AS_CASE(["x$CC"],
|
AS_CASE(["x$CC"],
|
||||||
[x/*],
|
[x/*],
|
||||||
[AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
|
[
|
||||||
[AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
|
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])])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
])
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
|
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
|
||||||
|
test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
|
||||||
|
|
||||||
AC_SUBST([PTHREAD_LIBS])
|
AC_SUBST([PTHREAD_LIBS])
|
||||||
AC_SUBST([PTHREAD_CFLAGS])
|
AC_SUBST([PTHREAD_CFLAGS])
|
||||||
AC_SUBST([PTHREAD_CC])
|
AC_SUBST([PTHREAD_CC])
|
||||||
|
AC_SUBST([PTHREAD_CXX])
|
||||||
|
|
||||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||||
if test "x$ax_pthread_ok" = "xyes"; then
|
if test "x$ax_pthread_ok" = "xyes"; then
|
||||||
|
|
|
@ -9,9 +9,9 @@ check_for_app() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# OpenBSD: pkg_add autoconf%2.63 automake%1.9 metaauto
|
# OpenBSD: pkg_add autoconf%2.69 automake%1.16 metaauto
|
||||||
test -n "$AUTOCONF_VERSION" || export AUTOCONF_VERSION=2.63
|
test -n "$AUTOCONF_VERSION" || export AUTOCONF_VERSION=2.69
|
||||||
test -n "$AUTOMAKE_VERSION" || export AUTOMAKE_VERSION=1.9
|
test -n "$AUTOMAKE_VERSION" || export AUTOMAKE_VERSION=1.16
|
||||||
|
|
||||||
check_for_app autoconf
|
check_for_app autoconf
|
||||||
check_for_app autoheader
|
check_for_app autoheader
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include "asterisk/mixmonitor.h"
|
#include "asterisk/mixmonitor.h"
|
||||||
#include "asterisk/audiohook.h"
|
#include "asterisk/audiohook.h"
|
||||||
#include "asterisk/causes.h"
|
#include "asterisk/causes.h"
|
||||||
|
#include "asterisk/beep.h"
|
||||||
|
|
||||||
enum set_touch_variables_res {
|
enum set_touch_variables_res {
|
||||||
SET_TOUCH_SUCCESS,
|
SET_TOUCH_SUCCESS,
|
||||||
|
@ -78,12 +79,13 @@ 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, int is_mixmonitor, char **touch_format, char **touch_monitor, char **touch_monitor_prefix)
|
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, char **touch_monitor_beep)
|
||||||
{
|
{
|
||||||
enum set_touch_variables_res res = SET_TOUCH_UNSET;
|
enum set_touch_variables_res res = SET_TOUCH_UNSET;
|
||||||
const char *var_format;
|
const char *var_format;
|
||||||
const char *var_monitor;
|
const char *var_monitor;
|
||||||
const char *var_prefix;
|
const char *var_prefix;
|
||||||
|
const char *var_beep;
|
||||||
|
|
||||||
SCOPED_CHANNELLOCK(lock, chan);
|
SCOPED_CHANNELLOCK(lock, chan);
|
||||||
|
|
||||||
|
@ -91,14 +93,17 @@ static enum set_touch_variables_res set_touch_variables(struct ast_channel *chan
|
||||||
var_format = "TOUCH_MIXMONITOR_FORMAT";
|
var_format = "TOUCH_MIXMONITOR_FORMAT";
|
||||||
var_monitor = "TOUCH_MIXMONITOR";
|
var_monitor = "TOUCH_MIXMONITOR";
|
||||||
var_prefix = "TOUCH_MIXMONITOR_PREFIX";
|
var_prefix = "TOUCH_MIXMONITOR_PREFIX";
|
||||||
|
var_beep = "TOUCH_MIXMONITOR_BEEP";
|
||||||
} else {
|
} else {
|
||||||
var_format = "TOUCH_MONITOR_FORMAT";
|
var_format = "TOUCH_MONITOR_FORMAT";
|
||||||
var_monitor = "TOUCH_MONITOR";
|
var_monitor = "TOUCH_MONITOR";
|
||||||
var_prefix = "TOUCH_MONITOR_PREFIX";
|
var_prefix = "TOUCH_MONITOR_PREFIX";
|
||||||
|
var_beep = "TOUCH_MONITOR_BEEP";
|
||||||
}
|
}
|
||||||
set_touch_variable(&res, chan, var_format, touch_format);
|
set_touch_variable(&res, chan, var_format, touch_format);
|
||||||
set_touch_variable(&res, chan, var_monitor, touch_monitor);
|
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_prefix, touch_monitor_prefix);
|
||||||
|
set_touch_variable(&res, chan, var_beep, touch_monitor_beep);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -141,20 +146,22 @@ static void start_automonitor(struct ast_bridge_channel *bridge_channel, struct
|
||||||
char *touch_filename;
|
char *touch_filename;
|
||||||
size_t len;
|
size_t len;
|
||||||
int x;
|
int x;
|
||||||
|
char beep_id[64] = "";
|
||||||
enum set_touch_variables_res set_touch_res;
|
enum set_touch_variables_res set_touch_res;
|
||||||
|
|
||||||
RAII_VAR(char *, touch_format, NULL, ast_free);
|
RAII_VAR(char *, touch_format, NULL, ast_free);
|
||||||
RAII_VAR(char *, touch_monitor, 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_prefix, NULL, ast_free);
|
||||||
|
RAII_VAR(char *, touch_monitor_beep, NULL, ast_free);
|
||||||
|
|
||||||
set_touch_res = set_touch_variables(bridge_channel->chan, 0, &touch_format,
|
set_touch_res = set_touch_variables(bridge_channel->chan, 0, &touch_format,
|
||||||
&touch_monitor, &touch_monitor_prefix);
|
&touch_monitor, &touch_monitor_prefix, &touch_monitor_beep);
|
||||||
switch (set_touch_res) {
|
switch (set_touch_res) {
|
||||||
case SET_TOUCH_SUCCESS:
|
case SET_TOUCH_SUCCESS:
|
||||||
break;
|
break;
|
||||||
case SET_TOUCH_UNSET:
|
case SET_TOUCH_UNSET:
|
||||||
set_touch_res = set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor,
|
set_touch_res = set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor,
|
||||||
&touch_monitor_prefix);
|
&touch_monitor_prefix, &touch_monitor_beep);
|
||||||
if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
|
if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -195,7 +202,28 @@ static void start_automonitor(struct ast_bridge_channel *bridge_channel, struct
|
||||||
|
|
||||||
ast_verb(4, "AutoMonitor used to record call. Filename: %s\n", touch_filename);
|
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)) {
|
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 > 0) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_beep_start(peer_chan, interval, beep_id, sizeof(beep_id))) {
|
||||||
|
ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT, beep_id)) {
|
||||||
ast_verb(4, "AutoMonitor feature was tried by '%s' but monitor failed to start.\n",
|
ast_verb(4, "AutoMonitor feature was tried by '%s' but monitor failed to start.\n",
|
||||||
ast_channel_name(bridge_channel->chan));
|
ast_channel_name(bridge_channel->chan));
|
||||||
return;
|
return;
|
||||||
|
@ -322,7 +350,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)
|
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;
|
char *touch_filename, mix_options[32] = "b";
|
||||||
size_t len;
|
size_t len;
|
||||||
int x;
|
int x;
|
||||||
enum set_touch_variables_res set_touch_res;
|
enum set_touch_variables_res set_touch_res;
|
||||||
|
@ -330,15 +358,16 @@ static void start_automixmonitor(struct ast_bridge_channel *bridge_channel, stru
|
||||||
RAII_VAR(char *, touch_format, NULL, ast_free);
|
RAII_VAR(char *, touch_format, NULL, ast_free);
|
||||||
RAII_VAR(char *, touch_monitor, 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_prefix, NULL, ast_free);
|
||||||
|
RAII_VAR(char *, touch_monitor_beep, NULL, ast_free);
|
||||||
|
|
||||||
set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format,
|
set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format,
|
||||||
&touch_monitor, &touch_monitor_prefix);
|
&touch_monitor, &touch_monitor_prefix, &touch_monitor_beep);
|
||||||
switch (set_touch_res) {
|
switch (set_touch_res) {
|
||||||
case SET_TOUCH_SUCCESS:
|
case SET_TOUCH_SUCCESS:
|
||||||
break;
|
break;
|
||||||
case SET_TOUCH_UNSET:
|
case SET_TOUCH_UNSET:
|
||||||
set_touch_res = set_touch_variables(peer_chan, 1, &touch_format, &touch_monitor,
|
set_touch_res = set_touch_variables(peer_chan, 1, &touch_format, &touch_monitor,
|
||||||
&touch_monitor_prefix);
|
&touch_monitor_prefix, &touch_monitor_beep);
|
||||||
if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
|
if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -381,7 +410,22 @@ static void start_automixmonitor(struct ast_bridge_channel *bridge_channel, stru
|
||||||
|
|
||||||
ast_verb(4, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);
|
ast_verb(4, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);
|
||||||
|
|
||||||
if (ast_start_mixmonitor(peer_chan, touch_filename, "b")) {
|
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)) {
|
||||||
ast_verb(4, "AutoMixMonitor feature was tried by '%s' but MixMonitor failed to start.\n",
|
ast_verb(4, "AutoMixMonitor feature was tried by '%s' but MixMonitor failed to start.\n",
|
||||||
ast_channel_name(bridge_channel->chan));
|
ast_channel_name(bridge_channel->chan));
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,14 @@ static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chann
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ast_channel_request_stream_topology_change(c1, new_top, &simple_bridge);
|
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_stream_topology_free(new_top);
|
ast_stream_topology_free(new_top);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
</member>
|
</member>
|
||||||
<member name="DETECT_DEADLOCKS" displayname="Detect Deadlocks">
|
<member name="DETECT_DEADLOCKS" displayname="Detect Deadlocks">
|
||||||
<depend>DEBUG_THREADS</depend>
|
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
</member>
|
</member>
|
||||||
<member name="DUMP_SCHEDULER" displayname="Dump Scheduler Contents for Debugging">
|
<member name="DUMP_SCHEDULER" displayname="Dump Scheduler Contents for Debugging">
|
||||||
|
|
|
@ -128,4 +128,8 @@
|
||||||
<defaultenabled>yes</defaultenabled>
|
<defaultenabled>yes</defaultenabled>
|
||||||
<depend>native_arch</depend>
|
<depend>native_arch</depend>
|
||||||
</member>
|
</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>
|
</category>
|
||||||
|
|
|
@ -58,7 +58,7 @@ if [[ -z ${cache_dir} ]] ; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
version=$(${ASTTOPDIR}/build_tools/make_version ${ASTTOPDIR})
|
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}"
|
echo "${module_name}: Couldn't parse version ${version}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -172,7 +172,7 @@ if [[ -f ${DESTDIR}${ASTMODDIR}/${module_name}.manifest.xml ]] ; then
|
||||||
|
|
||||||
cs=$(${MD5} ${f} | cut -b1-32)
|
cs=$(${MD5} ${f} | cut -b1-32)
|
||||||
if [[ "${cs}" != "${sum}" ]] ; then
|
if [[ "${cs}" != "${sum}" ]] ; then
|
||||||
echo Checksum mismatch: ${f}
|
echo "Checksum mismatch: ${f}"
|
||||||
need_install=1
|
need_install=1
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
@ -194,8 +194,8 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
need_download=1
|
need_download=1
|
||||||
if [[ -f ${cache_dir}/${full_name}.manifest.xml ]] ; then
|
if [[ -f ${cache_dir}/${full_name}-${major_version}.manifest.xml ]] ; then
|
||||||
cpv=$(${XMLSTARLET} sel -t -v "/package/@version" ${cache_dir}/${full_name}.manifest.xml)
|
cpv=$(${XMLSTARLET} sel -t -v "/package/@version" ${cache_dir}/${full_name}-${major_version}.manifest.xml)
|
||||||
cpvi=$(version_convert ${cpv})
|
cpvi=$(version_convert ${cpv})
|
||||||
echo "${full_name}: Cached package version ${cpv} (${cpvi})"
|
echo "${full_name}: Cached package version ${cpv} (${cpvi})"
|
||||||
if [[ ${cpvi} == ${rpvi} && ( -f ${cache_dir}/${tarball} ) ]] ; then
|
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}"
|
echo "${full_name}: Unable to fetch ${remote_url}/${tarball}"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
cp ${tmpdir}/${variant_manifest} ${cache_dir}/${full_name}.manifest.xml
|
cp ${tmpdir}/${variant_manifest} ${cache_dir}/${full_name}-${major_version}.manifest.xml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tar -xzf ${cache_dir}/${tarball} -C ${cache_dir}
|
tar -xzf ${cache_dir}/${tarball} -C ${cache_dir}
|
||||||
|
|
|
@ -18,42 +18,79 @@ then
|
||||||
# gets added to BUILDOPTS.
|
# gets added to BUILDOPTS.
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TMP=`${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
|
ADD_CFLAGS_TO_BUILDOPTS=false
|
||||||
for x in ${TMP}; do
|
MENUSELECT_CFLAGS=$(${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts)
|
||||||
if test "${x}" = "AO2_DEBUG" \
|
echo "$MENUSELECT_CFLAGS" | grep -q -e "ADD_CFLAGS_TO_BUILDOPTS_H" && ADD_CFLAGS_TO_BUILDOPTS=true
|
||||||
-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"
|
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
|
done
|
||||||
|
|
||||||
BUILDSUM=`echo ${BUILDOPTS} | ${MD5} | cut -c1-32`
|
# 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)
|
||||||
|
|
||||||
echo "#define AST_BUILDOPT_SUM \"${BUILDSUM}\""
|
echo "#define AST_BUILDOPT_SUM \"${BUILDSUM}\""
|
||||||
echo "#define AST_BUILDOPTS \"${BUILDOPTS}\""
|
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,220 +1,67 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
AWK=${AWK:-awk}
|
|
||||||
GIT=${GIT:-git}
|
GIT=${GIT:-git}
|
||||||
GREP=${GREP:-grep}
|
|
||||||
SED=${SED:-sed}
|
SED=${SED:-sed}
|
||||||
|
AWK=${AWK:-awk}
|
||||||
|
|
||||||
if [ -f ${1}/.version ]; then
|
if [ -f ${1}/.version ]; then
|
||||||
cat ${1}/.version
|
cat ${1}/.version
|
||||||
elif [ -d ${1}/.svn ]; then
|
exit 0
|
||||||
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
|
|
||||||
|
|
||||||
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"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ! -d ${1}/.git ]; then
|
||||||
|
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,6 +2,11 @@
|
||||||
|
|
||||||
GREP=${GREP:-grep}
|
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
|
if test ! -f .flavor ; then
|
||||||
EXTRA=""
|
EXTRA=""
|
||||||
elif test ! -f .version ; then
|
elif test ! -f .version ; then
|
||||||
|
@ -18,14 +23,11 @@ then
|
||||||
BUILDOPTS="AST_DEVMODE"
|
BUILDOPTS="AST_DEVMODE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TMP=`${GREP} -e "^MENUSELECT_CFLAGS" menuselect.makeopts | sed 's/MENUSELECT_CFLAGS\=//g' | sed 's/-D//g'`
|
BUILDOPTS=$(sed -n -r -e 's/#define\s+AST_BUILDOPTS\s+"([^"]+)"/\1/gp' \
|
||||||
for x in ${TMP}; do
|
include/asterisk/buildopts.h )
|
||||||
if test "x${BUILDOPTS}" != "x" ; then
|
|
||||||
BUILDOPTS="${BUILDOPTS}, ${x}"
|
BUILDOPTS_ALL=$(sed -n -r -e 's/#define\s+AST_BUILDOPTS_ALL\s+"([^"]+)"/\1/gp' \
|
||||||
else
|
include/asterisk/buildopts.h )
|
||||||
BUILDOPTS="${x}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
cat << END
|
cat << END
|
||||||
/*
|
/*
|
||||||
|
@ -43,6 +45,8 @@ static const char asterisk_version_num[] = "${ASTERISKVERSIONNUM}";
|
||||||
|
|
||||||
static const char asterisk_build_opts[] = "${BUILDOPTS}";
|
static const char asterisk_build_opts[] = "${BUILDOPTS}";
|
||||||
|
|
||||||
|
static const char asterisk_build_opts_all[] = "${BUILDOPTS_ALL}";
|
||||||
|
|
||||||
const char *ast_get_version(void)
|
const char *ast_get_version(void)
|
||||||
{
|
{
|
||||||
return asterisk_version;
|
return asterisk_version;
|
||||||
|
@ -58,4 +62,9 @@ const char *ast_get_build_opts(void)
|
||||||
return asterisk_build_opts;
|
return asterisk_build_opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *ast_get_build_opts_all(void)
|
||||||
|
{
|
||||||
|
return asterisk_build_opts_all;
|
||||||
|
}
|
||||||
|
|
||||||
END
|
END
|
||||||
|
|
|
@ -135,12 +135,18 @@ if [ "${for_wiki}" -eq "1" ] || [ "${validate}" -eq "1" ]; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
make_absolute() {
|
||||||
|
case "$1" in
|
||||||
|
/*) echo "$1" ;;
|
||||||
|
*) echo "$source_tree/$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
if [ "${command}" = "print_dependencies" ] ; then
|
if [ "${command}" = "print_dependencies" ] ; then
|
||||||
for subdir in ${mod_subdirs} ; do
|
for subdir in ${mod_subdirs} ; do
|
||||||
subpath="${source_tree}/${subdir}"
|
subpath=$(make_absolute "$subdir")
|
||||||
# We WANT word splitting in the following line.
|
${FIND} "${subpath}" \( -name '*.c' -o -name '*.cc' -o -name '*.xml' \) \
|
||||||
# shellcheck disable=SC2046
|
-exec ${GREP} -l -E '(language="en_US"|appdocsxml.dtd)' '{}' \;
|
||||||
${GREP} -l -E '(language="en_US"|appdocsxml.dtd)' $(${FIND} "${subpath}" -name '*.c' -or -name '*.cc' -or -name '*.xml') || :
|
|
||||||
done
|
done
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
@ -186,7 +192,7 @@ printf "Building Documentation For: "
|
||||||
|
|
||||||
for subdir in ${mod_subdirs} ; do
|
for subdir in ${mod_subdirs} ; do
|
||||||
printf "%s " "${subdir}"
|
printf "%s " "${subdir}"
|
||||||
subdir_path="${source_tree}/${subdir}"
|
subdir_path=$(make_absolute "$subdir")
|
||||||
for i in $(${FIND} "${subdir_path}" -name '*.c' -or -name '*.cc'); do
|
for i in $(${FIND} "${subdir_path}" -name '*.c' -or -name '*.cc'); do
|
||||||
if [ "${with_moduleinfo}" -eq "1" ] ; then
|
if [ "${with_moduleinfo}" -eq "1" ] ; then
|
||||||
MODULEINFO=$(${AWK} -f "${source_tree}/build_tools/get_moduleinfo" "${i}")
|
MODULEINFO=$(${AWK} -f "${source_tree}/build_tools/get_moduleinfo" "${i}")
|
||||||
|
|
|
@ -29,6 +29,7 @@ URIPARSER=@PBX_URIPARSER@
|
||||||
KQUEUE=@PBX_KQUEUE@
|
KQUEUE=@PBX_KQUEUE@
|
||||||
LDAP=@PBX_LDAP@
|
LDAP=@PBX_LDAP@
|
||||||
LIBEDIT=@PBX_LIBEDIT@
|
LIBEDIT=@PBX_LIBEDIT@
|
||||||
|
LIBJWT=@PBX_LIBJWT@
|
||||||
LIBXML2=@PBX_LIBXML2@
|
LIBXML2=@PBX_LIBXML2@
|
||||||
LIBXSLT=@PBX_LIBXSLT@
|
LIBXSLT=@PBX_LIBXSLT@
|
||||||
XMLSTARLET=@PBX_XMLSTARLET@
|
XMLSTARLET=@PBX_XMLSTARLET@
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
<depend>beanstalk</depend>
|
<depend>beanstalk</depend>
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
<depend>res_odbc</depend>
|
<depend>res_odbc</depend>
|
||||||
<depend>generic_odbc</depend>
|
<depend>generic_odbc</depend>
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
<depend>pgsql</depend>
|
<depend>pgsql</depend>
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
/*** MODULEINFO
|
/*** MODULEINFO
|
||||||
<depend>radius</depend>
|
<depend>radius</depend>
|
||||||
|
<defaultenabled>no</defaultenabled>
|
||||||
<support_level>extended</support_level>
|
<support_level>extended</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue